diff options
Diffstat (limited to 'target/linux')
-rw-r--r-- | target/linux/ar71xx/files/arch/mips/include/asm/mach-ath79/ag71xx_platform.h | 2 | ||||
-rw-r--r-- | target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_mdio.c | 72 |
2 files changed, 66 insertions, 8 deletions
diff --git a/target/linux/ar71xx/files/arch/mips/include/asm/mach-ath79/ag71xx_platform.h b/target/linux/ar71xx/files/arch/mips/include/asm/mach-ath79/ag71xx_platform.h index e2f6f9b113..656a6ef860 100644 --- a/target/linux/ar71xx/files/arch/mips/include/asm/mach-ath79/ag71xx_platform.h +++ b/target/linux/ar71xx/files/arch/mips/include/asm/mach-ath79/ag71xx_platform.h @@ -53,6 +53,8 @@ struct ag71xx_mdio_platform_data { u8 is_ar7240:1; u8 is_ar9330:1; u8 is_ar934x:1; + unsigned long mdio_clock; + unsigned long ref_clock; }; #endif /* __ASM_MACH_ATH79_PLATFORM_H */ 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 eaaf121485..e123505c7a 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 @@ -105,19 +105,75 @@ void ag71xx_mdio_mii_write(struct ag71xx_mdio *am, int addr, int reg, u16 val) 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; + } + } + + 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); |