ar8216: factor out PHY init code into a generic function
[openwrt.git] / target / linux / generic / files / drivers / net / phy / ar8216.c
index fe0ddb9..b2c87db 100644 (file)
@@ -51,6 +51,8 @@ struct ar8xxx_priv;
 #define AR8XXX_CAP_GIGE                        BIT(0)
 #define AR8XXX_CAP_MIB_COUNTERS                BIT(1)
 
+#define AR8XXX_NUM_PHYS        5
+
 enum {
        AR8XXX_VER_AR8216 = 0x01,
        AR8XXX_VER_AR8236 = 0x03,
@@ -73,12 +75,12 @@ struct ar8xxx_chip {
 
        void (*init_globals)(struct ar8xxx_priv *priv);
        void (*init_port)(struct ar8xxx_priv *priv, int port);
-       void (*setup_port)(struct ar8xxx_priv *priv, int port, u32 egress,
-                          u32 ingress, u32 members, u32 pvid);
+       void (*setup_port)(struct ar8xxx_priv *priv, int port, u32 members);
        u32 (*read_port_status)(struct ar8xxx_priv *priv, int port);
        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);
 
        const struct ar8xxx_mib_desc *mib_decs;
        unsigned num_mibs;
@@ -315,6 +317,53 @@ 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 void
+ar8xxx_phy_init(struct ar8xxx_priv *priv, bool support_1000)
+{
+       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++) {
+               /* initialize the port itself */
+               mdiobus_write(bus, i, MII_ADVERTISE,
+                       ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+               if (support_1000)
+                       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)
 {
@@ -722,10 +771,24 @@ ar8216_read_port_status(struct ar8xxx_priv *priv, int port)
 }
 
 static void
-ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress,
-                 u32 members, u32 pvid)
+ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
 {
        u32 header;
+       u32 egress, ingress;
+       u32 pvid;
+
+       if (priv->vlan) {
+               pvid = priv->vlan_id[priv->pvid[port]];
+               if (priv->vlan_tagged & (1 << port))
+                       egress = AR8216_OUT_ADD_VLAN;
+               else
+                       egress = AR8216_OUT_STRIP_VLAN;
+               ingress = AR8216_IN_SECURE;
+       } else {
+               pvid = port;
+               egress = AR8216_OUT_KEEP;
+               ingress = AR8216_IN_PORT_ONLY;
+       }
 
        if (chip_is_ar8216(priv) && priv->vlan && port == AR8216_PORT_CPU)
                header = AR8216_PORT_CTRL_HEADER;
@@ -807,9 +870,24 @@ static const struct ar8xxx_chip ar8216_chip = {
 };
 
 static void
-ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress,
-                 u32 members, u32 pvid)
+ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
 {
+       u32 egress, ingress;
+       u32 pvid;
+
+       if (priv->vlan) {
+               pvid = priv->vlan_id[priv->pvid[port]];
+               if (priv->vlan_tagged & (1 << port))
+                       egress = AR8216_OUT_ADD_VLAN;
+               else
+                       egress = AR8216_OUT_STRIP_VLAN;
+               ingress = AR8216_IN_SECURE;
+       } else {
+               pvid = port;
+               egress = AR8216_OUT_KEEP;
+               ingress = AR8216_IN_PORT_ONLY;
+       }
+
        ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
                   AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE |
                   AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE |
@@ -832,21 +910,10 @@ ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress,
 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 < 5; 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);
+       ar8xxx_phy_init(priv, false);
 
        priv->initialized = true;
        return 0;
@@ -883,9 +950,7 @@ static const struct ar8xxx_chip ar8236_chip = {
 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);
 
@@ -924,17 +989,7 @@ ar8316_hw_init(struct ar8xxx_priv *priv)
                msleep(1000);
        }
 
-       /* Initialize the ports */
-       bus = priv->mii_bus;
-       for (i = 0; i < 5; 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, true);
 
 out:
        priv->initialized = true;
@@ -1565,9 +1620,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);
@@ -1580,19 +1633,7 @@ ar8327_hw_init(struct ar8xxx_priv *priv)
 
        ar8327_leds_init(priv);
 
-       bus = priv->mii_bus;
-       for (i = 0; i < AR8327_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, true);
 
        return 0;
 }
@@ -1720,7 +1761,7 @@ ar8327_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask)
                        mode = AR8327_VTU_FUNC0_EG_MODE_NOT;
                else if (priv->vlan == 0)
                        mode = AR8327_VTU_FUNC0_EG_MODE_KEEP;
-               else if (priv->vlan_tagged & BIT(i))
+               else if ((priv->vlan_tagged & BIT(i)) || (priv->vlan_id[priv->pvid[i]] != vid))
                        mode = AR8327_VTU_FUNC0_EG_MODE_TAG;
                else
                        mode = AR8327_VTU_FUNC0_EG_MODE_UNTAG;
@@ -1731,31 +1772,26 @@ ar8327_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask)
 }
 
 static void
-ar8327_setup_port(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress,
-                 u32 members, u32 pvid)
+ar8327_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
 {
        u32 t;
-       u32 mode;
+       u32 egress, ingress;
+       u32 pvid = priv->vlan_id[priv->pvid[port]];
+
+       if (priv->vlan) {
+               egress = AR8327_PORT_VLAN1_OUT_MODE_UNMOD;
+               ingress = AR8216_IN_SECURE;
+       } else {
+               egress = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH;
+               ingress = AR8216_IN_PORT_ONLY;
+       }
 
        t = pvid << AR8327_PORT_VLAN0_DEF_SVID_S;
        t |= pvid << AR8327_PORT_VLAN0_DEF_CVID_S;
        priv->write(priv, AR8327_REG_PORT_VLAN0(port), t);
 
-       mode = AR8327_PORT_VLAN1_OUT_MODE_UNMOD;
-       switch (egress) {
-       case AR8216_OUT_KEEP:
-               mode = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH;
-               break;
-       case AR8216_OUT_STRIP_VLAN:
-               mode = AR8327_PORT_VLAN1_OUT_MODE_UNTAG;
-               break;
-       case AR8216_OUT_ADD_VLAN:
-               mode = AR8327_PORT_VLAN1_OUT_MODE_TAG;
-               break;
-       }
-
        t = AR8327_PORT_VLAN1_PORT_VLAN_PROP;
-       t |= mode << AR8327_PORT_VLAN1_OUT_MODE_S;
+       t |= egress << AR8327_PORT_VLAN1_OUT_MODE_S;
        priv->write(priv, AR8327_REG_PORT_VLAN1(port), t);
 
        t = members;
@@ -1765,6 +1801,15 @@ ar8327_setup_port(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress,
        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,
        .hw_init = ar8327_hw_init,
@@ -1776,6 +1821,7 @@ 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,
 
        .num_mibs = ARRAY_SIZE(ar8236_mibs),
        .mib_decs = ar8236_mibs,
@@ -1875,6 +1921,30 @@ ar8xxx_sw_get_ports(struct switch_dev *dev, struct switch_val *val)
 }
 
 static int
+ar8327_sw_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       u8 ports = priv->vlan_table[val->port_vlan];
+       int i;
+
+       val->len = 0;
+       for (i = 0; i < dev->ports; i++) {
+               struct switch_port *p;
+
+               if (!(ports & (1 << i)))
+                       continue;
+
+               p = &val->value.ports[val->len++];
+               p->id = i;
+               if ((priv->vlan_tagged & (1 << i)) || (priv->pvid[i] != val->port_vlan))
+                       p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
+               else
+                       p->flags = 0;
+       }
+       return 0;
+}
+
+static int
 ar8xxx_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
 {
        struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
@@ -1905,6 +1975,31 @@ ar8xxx_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
        return 0;
 }
 
+static int
+ar8327_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       u8 *vt = &priv->vlan_table[val->port_vlan];
+       int i;
+
+       *vt = 0;
+       for (i = 0; i < val->len; i++) {
+               struct switch_port *p = &val->value.ports[i];
+
+               if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
+                       if (val->port_vlan == priv->pvid[p->id]) {
+                               priv->vlan_tagged |= (1 << p->id);
+                       }
+               } else {
+                       priv->vlan_tagged &= ~(1 << p->id);
+                       priv->pvid[p->id] = val->port_vlan;
+               }
+
+               *vt |= 1 << p->id;
+       }
+       return 0;
+}
+
 static void
 ar8327_set_mirror_regs(struct ar8xxx_priv *priv)
 {
@@ -2041,24 +2136,7 @@ ar8xxx_sw_hw_apply(struct switch_dev *dev)
 
        /* update the port destination mask registers and tag settings */
        for (i = 0; i < dev->ports; i++) {
-               int egress, ingress;
-               int pvid;
-
-               if (priv->vlan) {
-                       pvid = priv->vlan_id[priv->pvid[i]];
-                       if (priv->vlan_tagged & (1 << i))
-                               egress = AR8216_OUT_ADD_VLAN;
-                       else
-                               egress = AR8216_OUT_STRIP_VLAN;
-                       ingress = AR8216_IN_SECURE;
-               } else {
-                       pvid = i;
-                       egress = AR8216_OUT_KEEP;
-                       ingress = AR8216_IN_PORT_ONLY;
-               }
-
-               priv->chip->setup_port(priv, i, egress, ingress, portmask[i],
-                                      pvid);
+               priv->chip->setup_port(priv, i, portmask[i]);
        }
 
        ar8xxx_set_mirror_regs(priv);
@@ -2464,8 +2542,8 @@ static const struct switch_dev_ops ar8327_sw_ops = {
        },
        .get_port_pvid = ar8xxx_sw_get_pvid,
        .set_port_pvid = ar8xxx_sw_set_pvid,
-       .get_vlan_ports = ar8xxx_sw_get_ports,
-       .set_vlan_ports = ar8xxx_sw_set_ports,
+       .get_vlan_ports = ar8327_sw_get_ports,
+       .set_vlan_ports = ar8327_sw_set_ports,
        .apply_config = ar8xxx_sw_hw_apply,
        .reset_switch = ar8xxx_sw_reset_switch,
        .get_port_link = ar8xxx_sw_get_port_link,
@@ -2800,6 +2878,7 @@ static const u32 ar8xxx_phy_ids[] = {
        0x004dd036, /* AR8337 */
        0x004dd041,
        0x004dd042,
+       0x004dd043, /* AR8236 */
 };
 
 static bool