ar71xx: implement callback in mdio reset
[openwrt.git] / target / linux / ar71xx / files / drivers / net / ethernet / atheros / ag71xx / ag71xx_mdio.c
index eaaf121..71ae825 100644 (file)
@@ -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);
@@ -125,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;
 }
 
@@ -149,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;
@@ -225,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);
 
@@ -242,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",
        }