X-Git-Url: https://git.archive.openwrt.org/?p=openwrt.git;a=blobdiff_plain;f=target%2Flinux%2Far71xx%2Ffiles%2Fdrivers%2Fnet%2Fethernet%2Fatheros%2Fag71xx%2Fag71xx_mdio.c;h=71ae8258b2ef20f7d20ceacec59011ab3babfe23;hp=1b2fe2ee7abc26040a03f35610918003a7bd1eec;hb=67539ac550c360c3290faf36d10f983f510f3769;hpb=8c1c75ba17b6e4337039dd5cf85f6e8f4fe51268 diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_mdio.c b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_mdio.c index 1b2fe2ee7a..71ae8258b2 100644 --- a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_mdio.c +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_mdio.c @@ -47,68 +47,133 @@ static void ag71xx_mdio_dump_regs(struct ag71xx_mdio *am) ag71xx_mdio_rr(am, AG71XX_REG_MII_IND)); } +static int ag71xx_mdio_wait_busy(struct ag71xx_mdio *am) +{ + int i; + + for (i = 0; i < AG71XX_MDIO_RETRY; i++) { + u32 busy; + + udelay(AG71XX_MDIO_DELAY); + + busy = ag71xx_mdio_rr(am, AG71XX_REG_MII_IND); + if (!busy) + return 0; + + udelay(AG71XX_MDIO_DELAY); + } + + pr_err("%s: MDIO operation timed out\n", am->mii_bus->name); + + return -ETIMEDOUT; +} + int ag71xx_mdio_mii_read(struct ag71xx_mdio *am, int addr, int reg) { + int err; int ret; - int i; + + err = ag71xx_mdio_wait_busy(am); + if (err) + return 0xffff; ag71xx_mdio_wr(am, AG71XX_REG_MII_CMD, MII_CMD_WRITE); ag71xx_mdio_wr(am, AG71XX_REG_MII_ADDR, ((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff)); ag71xx_mdio_wr(am, AG71XX_REG_MII_CMD, MII_CMD_READ); - i = AG71XX_MDIO_RETRY; - while (ag71xx_mdio_rr(am, AG71XX_REG_MII_IND) & MII_IND_BUSY) { - if (i-- == 0) { - pr_err("%s: mii_read timed out\n", am->mii_bus->name); - ret = 0xffff; - goto out; - } - udelay(AG71XX_MDIO_DELAY); - } + err = ag71xx_mdio_wait_busy(am); + if (err) + return 0xffff; ret = ag71xx_mdio_rr(am, AG71XX_REG_MII_STATUS) & 0xffff; ag71xx_mdio_wr(am, AG71XX_REG_MII_CMD, MII_CMD_WRITE); DBG("mii_read: addr=%04x, reg=%04x, value=%04x\n", addr, reg, ret); -out: return ret; } void ag71xx_mdio_mii_write(struct ag71xx_mdio *am, int addr, int reg, u16 val) { - int i; - DBG("mii_write: addr=%04x, reg=%04x, value=%04x\n", addr, reg, val); ag71xx_mdio_wr(am, AG71XX_REG_MII_ADDR, ((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff)); ag71xx_mdio_wr(am, AG71XX_REG_MII_CTRL, val); - i = AG71XX_MDIO_RETRY; - while (ag71xx_mdio_rr(am, AG71XX_REG_MII_IND) & MII_IND_BUSY) { - if (i-- == 0) { - pr_err("%s: mii_write timed out\n", am->mii_bus->name); - break; + ag71xx_mdio_wait_busy(am); +} + +static const u32 ar71xx_mdio_div_table[] = { + 4, 4, 6, 8, 10, 14, 20, 28, +}; + +static const u32 ar7240_mdio_div_table[] = { + 2, 2, 4, 6, 8, 12, 18, 26, 32, 40, 48, 56, 62, 70, 78, 96, +}; + +static const u32 ar933x_mdio_div_table[] = { + 4, 4, 6, 8, 10, 14, 20, 28, 34, 42, 50, 58, 66, 74, 82, 98, +}; + +static int ag71xx_mdio_get_divider(struct ag71xx_mdio *am, u32 *div) +{ + unsigned long ref_clock, mdio_clock; + const u32 *table; + int ndivs; + int i; + + ref_clock = am->pdata->ref_clock; + mdio_clock = am->pdata->mdio_clock; + + if (!ref_clock || !mdio_clock) + return -EINVAL; + + if (am->pdata->is_ar9330 || am->pdata->is_ar934x) { + table = ar933x_mdio_div_table; + ndivs = ARRAY_SIZE(ar933x_mdio_div_table); + } else if (am->pdata->is_ar7240) { + table = ar7240_mdio_div_table; + ndivs = ARRAY_SIZE(ar7240_mdio_div_table); + } else { + table = ar71xx_mdio_div_table; + ndivs = ARRAY_SIZE(ar71xx_mdio_div_table); + } + + for (i = 0; i < ndivs; i++) { + unsigned long t; + + t = ref_clock / table[i]; + if (t <= mdio_clock) { + *div = i; + return 0; } - udelay(AG71XX_MDIO_DELAY); } + + dev_err(&am->mii_bus->dev, "no divider found for %lu/%lu\n", + ref_clock, mdio_clock); + return -ENOENT; } static int ag71xx_mdio_reset(struct mii_bus *bus) { struct ag71xx_mdio *am = bus->priv; u32 t; + int err; - if (am->pdata->is_ar7240) - t = MII_CFG_CLK_DIV_6; - else if (am->pdata->builtin_switch && !am->pdata->is_ar934x) - t = MII_CFG_CLK_DIV_10; - else if (!am->pdata->builtin_switch && am->pdata->is_ar934x) - t = MII_CFG_CLK_DIV_58; - else - t = MII_CFG_CLK_DIV_28; + err = ag71xx_mdio_get_divider(am, &t); + if (err) { + /* fallback */ + if (am->pdata->is_ar7240) + t = MII_CFG_CLK_DIV_6; + else if (am->pdata->builtin_switch && !am->pdata->is_ar934x) + t = MII_CFG_CLK_DIV_10; + else if (!am->pdata->builtin_switch && am->pdata->is_ar934x) + t = MII_CFG_CLK_DIV_58; + else + t = MII_CFG_CLK_DIV_28; + } ag71xx_mdio_wr(am, AG71XX_REG_MII_CFG, t | MII_CFG_RESET); udelay(100); @@ -116,6 +181,9 @@ static int ag71xx_mdio_reset(struct mii_bus *bus) ag71xx_mdio_wr(am, AG71XX_REG_MII_CFG, t); udelay(100); + if (am->pdata->reset) + am->pdata->reset(bus); + return 0; } @@ -140,7 +208,7 @@ static int ag71xx_mdio_write(struct mii_bus *bus, int addr, int reg, u16 val) return 0; } -static int __devinit ag71xx_mdio_probe(struct platform_device *pdev) +static int ag71xx_mdio_probe(struct platform_device *pdev) { struct ag71xx_mdio_platform_data *pdata; struct ag71xx_mdio *am; @@ -216,7 +284,7 @@ err_out: return err; } -static int __devexit ag71xx_mdio_remove(struct platform_device *pdev) +static int ag71xx_mdio_remove(struct platform_device *pdev) { struct ag71xx_mdio *am = platform_get_drvdata(pdev); @@ -233,7 +301,7 @@ static int __devexit ag71xx_mdio_remove(struct platform_device *pdev) static struct platform_driver ag71xx_mdio_driver = { .probe = ag71xx_mdio_probe, - .remove = __exit_p(ag71xx_mdio_remove), + .remove = ag71xx_mdio_remove, .driver = { .name = "ag71xx-mdio", }