ar8216: factor out mib_func to ar8xxx_chip
[15.05/openwrt.git] / target / linux / generic / files / drivers / net / phy / ar8216.c
index d57f9e5..effa33c 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/of_device.h>
 #include <linux/leds.h>
 #include <linux/gpio.h>
+#include <linux/version.h>
 
 #include "ar8216.h"
 
@@ -69,6 +70,7 @@ struct ar8xxx_mib_desc {
 
 struct ar8xxx_chip {
        unsigned long caps;
+       bool config_at_probe;
 
        int (*hw_init)(struct ar8xxx_priv *priv);
        void (*cleanup)(struct ar8xxx_priv *priv);
@@ -80,9 +82,11 @@ struct ar8xxx_chip {
        int (*atu_flush)(struct ar8xxx_priv *priv);
        void (*vtu_flush)(struct ar8xxx_priv *priv);
        void (*vtu_load_vlan)(struct ar8xxx_priv *priv, u32 vid, u32 port_mask);
+       void (*phy_fixup)(struct ar8xxx_priv *priv, int phy);
 
        const struct ar8xxx_mib_desc *mib_decs;
        unsigned num_mibs;
+       unsigned mib_func;
 };
 
 enum ar8327_led_pattern {
@@ -316,6 +320,76 @@ split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
        *page = regaddr & 0x1ff;
 }
 
+/* inspired by phy_poll_reset in drivers/net/phy/phy_device.c */
+static int
+ar8xxx_phy_poll_reset(struct mii_bus *bus)
+{
+        unsigned int sleep_msecs = 20;
+        int ret, elapsed, i;
+
+        for (elapsed = sleep_msecs; elapsed <= 600;
+            elapsed += sleep_msecs) {
+                msleep(sleep_msecs);
+                for (i = 0; i < AR8XXX_NUM_PHYS; i++) {
+                        ret = mdiobus_read(bus, i, MII_BMCR);
+                        if (ret < 0)
+                               return ret;
+                        if (ret & BMCR_RESET)
+                               break;
+                        if (i == AR8XXX_NUM_PHYS - 1) {
+                                usleep_range(1000, 2000);
+                                return 0;
+                        }
+                }
+        }
+        return -ETIMEDOUT;
+}
+
+static int
+ar8xxx_phy_check_aneg(struct phy_device *phydev)
+{
+       int ret;
+
+       if (phydev->autoneg != AUTONEG_ENABLE)
+               return 0;
+       /*
+        * BMCR_ANENABLE might have been cleared
+        * by phy_init_hw in certain kernel versions
+        * therefore check for it
+        */
+       ret = phy_read(phydev, MII_BMCR);
+       if (ret < 0)
+               return ret;
+       if (ret & BMCR_ANENABLE)
+               return 0;
+
+       dev_info(&phydev->dev, "ANEG disabled, re-enabling ...\n");
+       ret |= BMCR_ANENABLE | BMCR_ANRESTART;
+       return phy_write(phydev, MII_BMCR, ret);
+}
+
+static void
+ar8xxx_phy_init(struct ar8xxx_priv *priv)
+{
+       int i;
+       struct mii_bus *bus;
+
+       bus = priv->mii_bus;
+       for (i = 0; i < AR8XXX_NUM_PHYS; i++) {
+               if (priv->chip->phy_fixup)
+                       priv->chip->phy_fixup(priv, i);
+
+               /* initialize the port itself */
+               mdiobus_write(bus, i, MII_ADVERTISE,
+                       ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+               if (ar8xxx_has_gige(priv))
+                       mdiobus_write(bus, i, MII_CTRL1000, ADVERTISE_1000FULL);
+               mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
+       }
+
+       ar8xxx_phy_poll_reset(bus);
+}
+
 static u32
 ar8xxx_mii_read(struct ar8xxx_priv *priv, int reg)
 {
@@ -459,16 +533,11 @@ ar8xxx_reg_wait(struct ar8xxx_priv *priv, u32 reg, u32 mask, u32 val,
 static int
 ar8xxx_mib_op(struct ar8xxx_priv *priv, u32 op)
 {
-       unsigned mib_func;
+       unsigned mib_func = priv->chip->mib_func;
        int ret;
 
        lockdep_assert_held(&priv->mib_lock);
 
-       if (chip_is_ar8327(priv) || chip_is_ar8337(priv))
-               mib_func = AR8327_REG_MIB_FUNC;
-       else
-               mib_func = AR8216_REG_MIB_FUNC;
-
        /* Capture the hardware statistics for all ports */
        ar8xxx_rmw(priv, mib_func, AR8216_MIB_FUNC, (op << AR8216_MIB_FUNC_S));
 
@@ -766,6 +835,12 @@ ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
 static int
 ar8216_hw_init(struct ar8xxx_priv *priv)
 {
+       if (priv->initialized)
+               return 0;
+
+       ar8xxx_phy_init(priv);
+
+       priv->initialized = true;
        return 0;
 }
 
@@ -819,6 +894,7 @@ static const struct ar8xxx_chip ar8216_chip = {
 
        .num_mibs = ARRAY_SIZE(ar8216_mibs),
        .mib_decs = ar8216_mibs,
+       .mib_func = AR8216_REG_MIB_FUNC
 };
 
 static void
@@ -859,29 +935,6 @@ ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
                   (members << AR8236_PORT_VLAN2_MEMBER_S));
 }
 
-static int
-ar8236_hw_init(struct ar8xxx_priv *priv)
-{
-       int i;
-       struct mii_bus *bus;
-
-       if (priv->initialized)
-               return 0;
-
-       /* Initialize the PHYs */
-       bus = priv->mii_bus;
-       for (i = 0; i < AR8XXX_NUM_PHYS; i++) {
-               mdiobus_write(bus, i, MII_ADVERTISE,
-                             ADVERTISE_ALL | ADVERTISE_PAUSE_CAP |
-                             ADVERTISE_PAUSE_ASYM);
-               mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
-       }
-       msleep(1000);
-
-       priv->initialized = true;
-       return 0;
-}
-
 static void
 ar8236_init_globals(struct ar8xxx_priv *priv)
 {
@@ -897,7 +950,7 @@ ar8236_init_globals(struct ar8xxx_priv *priv)
 
 static const struct ar8xxx_chip ar8236_chip = {
        .caps = AR8XXX_CAP_MIB_COUNTERS,
-       .hw_init = ar8236_hw_init,
+       .hw_init = ar8216_hw_init,
        .init_globals = ar8236_init_globals,
        .init_port = ar8216_init_port,
        .setup_port = ar8236_setup_port,
@@ -908,14 +961,13 @@ static const struct ar8xxx_chip ar8236_chip = {
 
        .num_mibs = ARRAY_SIZE(ar8236_mibs),
        .mib_decs = ar8236_mibs,
+       .mib_func = AR8216_REG_MIB_FUNC
 };
 
 static int
 ar8316_hw_init(struct ar8xxx_priv *priv)
 {
-       int i;
        u32 val, newval;
-       struct mii_bus *bus;
 
        val = priv->read(priv, AR8316_REG_POSTRIP);
 
@@ -954,17 +1006,7 @@ ar8316_hw_init(struct ar8xxx_priv *priv)
                msleep(1000);
        }
 
-       /* Initialize the ports */
-       bus = priv->mii_bus;
-       for (i = 0; i < AR8XXX_NUM_PHYS; i++) {
-               /* initialize the port itself */
-               mdiobus_write(bus, i, MII_ADVERTISE,
-                       ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
-               mdiobus_write(bus, i, MII_CTRL1000, ADVERTISE_1000FULL);
-               mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
-       }
-
-       msleep(1000);
+       ar8xxx_phy_init(priv);
 
 out:
        priv->initialized = true;
@@ -1003,6 +1045,7 @@ static const struct ar8xxx_chip ar8316_chip = {
 
        .num_mibs = ARRAY_SIZE(ar8236_mibs),
        .mib_decs = ar8236_mibs,
+       .mib_func = AR8216_REG_MIB_FUNC
 };
 
 static u32
@@ -1595,9 +1638,7 @@ ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np)
 static int
 ar8327_hw_init(struct ar8xxx_priv *priv)
 {
-       struct mii_bus *bus;
        int ret;
-       int i;
 
        if (priv->phy->dev.of_node)
                ret = ar8327_hw_config_of(priv, priv->phy->dev.of_node);
@@ -1610,19 +1651,7 @@ ar8327_hw_init(struct ar8xxx_priv *priv)
 
        ar8327_leds_init(priv);
 
-       bus = priv->mii_bus;
-       for (i = 0; i < AR8XXX_NUM_PHYS; i++) {
-               ar8327_phy_fixup(priv, i);
-
-               /* start aneg on the PHY */
-               mdiobus_write(bus, i, MII_ADVERTISE, ADVERTISE_ALL |
-                                                    ADVERTISE_PAUSE_CAP |
-                                                    ADVERTISE_PAUSE_ASYM);
-               mdiobus_write(bus, i, MII_CTRL1000, ADVERTISE_1000FULL);
-               mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
-       }
-
-       msleep(1000);
+       ar8xxx_phy_init(priv);
 
        return 0;
 }
@@ -1792,6 +1821,7 @@ ar8327_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
 
 static const struct ar8xxx_chip ar8327_chip = {
        .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS,
+       .config_at_probe = true,
        .hw_init = ar8327_hw_init,
        .cleanup = ar8327_cleanup,
        .init_globals = ar8327_init_globals,
@@ -1801,9 +1831,11 @@ static const struct ar8xxx_chip ar8327_chip = {
        .atu_flush = ar8327_atu_flush,
        .vtu_flush = ar8327_vtu_flush,
        .vtu_load_vlan = ar8327_vtu_load_vlan,
+       .phy_fixup = ar8327_phy_fixup,
 
        .num_mibs = ARRAY_SIZE(ar8236_mibs),
        .mib_decs = ar8236_mibs,
+       .mib_func = AR8327_REG_MIB_FUNC
 };
 
 static int
@@ -2767,8 +2799,8 @@ ar8xxx_phy_config_init(struct phy_device *phydev)
        if (WARN_ON(!priv))
                return -ENODEV;
 
-       if (chip_is_ar8327(priv) || chip_is_ar8337(priv))
-               return 0;
+       if (priv->chip->config_at_probe)
+               return ar8xxx_phy_check_aneg(phydev);
 
        priv->phy = phydev;
 
@@ -2943,7 +2975,7 @@ found:
                        phydev->advertising = ADVERTISED_100baseT_Full;
                }
 
-               if (chip_is_ar8327(priv) || chip_is_ar8337(priv)) {
+               if (priv->chip->config_at_probe) {
                        priv->phy = phydev;
 
                        ret = ar8xxx_start(priv);
@@ -3013,6 +3045,15 @@ ar8xxx_phy_remove(struct phy_device *phydev)
        ar8xxx_free(priv);
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)
+static int
+ar8xxx_phy_soft_reset(struct phy_device *phydev)
+{
+       /* we don't need an extra reset */
+       return 0;
+}
+#endif
+
 static struct phy_driver ar8xxx_phy_driver = {
        .phy_id         = 0x004d0000,
        .name           = "Atheros AR8216/AR8236/AR8316",
@@ -3024,6 +3065,9 @@ static struct phy_driver ar8xxx_phy_driver = {
        .config_init    = ar8xxx_phy_config_init,
        .config_aneg    = ar8xxx_phy_config_aneg,
        .read_status    = ar8xxx_phy_read_status,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)
+       .soft_reset     = ar8xxx_phy_soft_reset,
+#endif
        .driver         = { .owner = THIS_MODULE },
 };