ar8216: factor out mib_func to ar8xxx_chip
[15.05/openwrt.git] / target / linux / generic / files / drivers / net / phy / ar8216.c
index b2c87db..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,10 +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 (*fixup_phys)(struct ar8xxx_priv *priv);
+       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 {
@@ -342,21 +345,44 @@ ar8xxx_phy_poll_reset(struct mii_bus *bus)
         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, bool support_1000)
+ar8xxx_phy_init(struct ar8xxx_priv *priv)
 {
        int i;
        struct mii_bus *bus;
 
-       if (priv->chip->fixup_phys)
-               priv->chip->fixup_phys(priv);
-
        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 (support_1000)
+               if (ar8xxx_has_gige(priv))
                        mdiobus_write(bus, i, MII_CTRL1000, ADVERTISE_1000FULL);
                mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
        }
@@ -507,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));
 
@@ -814,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;
 }
 
@@ -867,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
@@ -907,18 +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)
-{
-       if (priv->initialized)
-               return 0;
-
-       ar8xxx_phy_init(priv, false);
-
-       priv->initialized = true;
-       return 0;
-}
-
 static void
 ar8236_init_globals(struct ar8xxx_priv *priv)
 {
@@ -934,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,
@@ -945,6 +961,7 @@ 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
@@ -989,7 +1006,7 @@ ar8316_hw_init(struct ar8xxx_priv *priv)
                msleep(1000);
        }
 
-       ar8xxx_phy_init(priv, true);
+       ar8xxx_phy_init(priv);
 
 out:
        priv->initialized = true;
@@ -1028,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
@@ -1633,7 +1651,7 @@ ar8327_hw_init(struct ar8xxx_priv *priv)
 
        ar8327_leds_init(priv);
 
-       ar8xxx_phy_init(priv, true);
+       ar8xxx_phy_init(priv);
 
        return 0;
 }
@@ -1801,17 +1819,9 @@ ar8327_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
        priv->write(priv, AR8327_REG_PORT_LOOKUP(port), t);
 }
 
-static void
-ar8327_fixup_phys(struct ar8xxx_priv *priv)
-{
-       int i;
-
-       for (i = 0; i < AR8XXX_NUM_PHYS; i++)
-               ar8327_phy_fixup(priv, i);
-}
-
 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,
@@ -1821,10 +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,
-       .fixup_phys = ar8327_fixup_phys,
+       .phy_fixup = ar8327_phy_fixup,
 
        .num_mibs = ARRAY_SIZE(ar8236_mibs),
        .mib_decs = ar8236_mibs,
+       .mib_func = AR8327_REG_MIB_FUNC
 };
 
 static int
@@ -2788,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;
 
@@ -2964,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);
@@ -3034,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",
@@ -3045,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 },
 };