swconfig: simplify init code
[openwrt/openwrt.git] / target / linux / generic / files / drivers / net / phy / ar8216.c
index b88e9ec45974e2acaec7cc17ec428ed7a406f5d0..75db1f120b6e3ee52cb45d054a3eca48cb8cd900 100644 (file)
@@ -134,19 +134,6 @@ const struct ar8xxx_mib_desc ar8236_mibs[39] = {
 static DEFINE_MUTEX(ar8xxx_dev_list_lock);
 static LIST_HEAD(ar8xxx_dev_list);
 
-static inline void
-split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
-{
-       regaddr >>= 1;
-       *r1 = regaddr & 0x1e;
-
-       regaddr >>= 5;
-       *r2 = regaddr & 0x7;
-
-       regaddr >>= 3;
-       *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)
@@ -217,8 +204,8 @@ ar8xxx_phy_init(struct ar8xxx_priv *priv)
        ar8xxx_phy_poll_reset(bus);
 }
 
-static u32
-mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum)
+u32
+ar8xxx_mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum)
 {
        struct mii_bus *bus = priv->mii_bus;
        u16 lo, hi;
@@ -229,8 +216,8 @@ mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum)
        return (hi << 16) | lo;
 }
 
-static void
-mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val)
+void
+ar8xxx_mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val)
 {
        struct mii_bus *bus = priv->mii_bus;
        u16 lo, hi;
@@ -260,8 +247,8 @@ ar8xxx_read(struct ar8xxx_priv *priv, int reg)
        mutex_lock(&bus->mdio_lock);
 
        bus->write(bus, 0x18, 0, page);
-       usleep_range(1000, 2000); /* wait for the page switch to propagate */
-       val = mii_read32(priv, 0x10 | r2, r1);
+       wait_for_page_switch();
+       val = ar8xxx_mii_read32(priv, 0x10 | r2, r1);
 
        mutex_unlock(&bus->mdio_lock);
 
@@ -279,8 +266,8 @@ ar8xxx_write(struct ar8xxx_priv *priv, int reg, u32 val)
        mutex_lock(&bus->mdio_lock);
 
        bus->write(bus, 0x18, 0, page);
-       usleep_range(1000, 2000); /* wait for the page switch to propagate */
-       mii_write32(priv, 0x10 | r2, r1, val);
+       wait_for_page_switch();
+       ar8xxx_mii_write32(priv, 0x10 | r2, r1, val);
 
        mutex_unlock(&bus->mdio_lock);
 }
@@ -297,12 +284,12 @@ ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val)
        mutex_lock(&bus->mdio_lock);
 
        bus->write(bus, 0x18, 0, page);
-       usleep_range(1000, 2000); /* wait for the page switch to propagate */
+       wait_for_page_switch();
 
-       ret = mii_read32(priv, 0x10 | r2, r1);
+       ret = ar8xxx_mii_read32(priv, 0x10 | r2, r1);
        ret &= ~mask;
        ret |= val;
-       mii_write32(priv, 0x10 | r2, r1, ret);
+       ar8xxx_mii_write32(priv, 0x10 | r2, r1, ret);
 
        mutex_unlock(&bus->mdio_lock);
 
@@ -332,6 +319,20 @@ ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 data)
        mutex_unlock(&bus->mdio_lock);
 }
 
+u16
+ar8xxx_phy_mmd_read(struct ar8xxx_priv *priv, int phy_addr, u16 addr)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 data;
+
+       mutex_lock(&bus->mdio_lock);
+       bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr);
+       data = bus->read(bus, phy_addr, MII_ATH_MMD_DATA);
+       mutex_unlock(&bus->mdio_lock);
+
+       return data;
+}
+
 static int
 ar8xxx_reg_wait(struct ar8xxx_priv *priv, u32 reg, u32 mask, u32 val,
                unsigned timeout)
@@ -453,6 +454,9 @@ ar8216_read_port_link(struct ar8xxx_priv *priv, int port,
        link->tx_flow = !!(status & AR8216_PORT_STATUS_TXFLOW);
        link->rx_flow = !!(status & AR8216_PORT_STATUS_RXFLOW);
 
+       if (link->aneg && link->duplex && priv->chip->read_port_eee_status)
+               link->eee = priv->chip->read_port_eee_status(priv, port);
+
        speed = (status & AR8216_PORT_STATUS_SPEED) >>
                 AR8216_PORT_STATUS_SPEED_S;
 
@@ -594,9 +598,26 @@ ar8216_atu_flush(struct ar8xxx_priv *priv)
 {
        int ret;
 
-       ret = ar8216_wait_bit(priv, AR8216_REG_ATU, AR8216_ATU_ACTIVE, 0);
+       ret = ar8216_wait_bit(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_ACTIVE, 0);
        if (!ret)
-               ar8xxx_write(priv, AR8216_REG_ATU, AR8216_ATU_OP_FLUSH);
+               ar8xxx_write(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_OP_FLUSH |
+                                                        AR8216_ATU_ACTIVE);
+
+       return ret;
+}
+
+static int
+ar8216_atu_flush_port(struct ar8xxx_priv *priv, int port)
+{
+       u32 t;
+       int ret;
+
+       ret = ar8216_wait_bit(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_ACTIVE, 0);
+       if (!ret) {
+               t = (port << AR8216_ATU_PORT_NUM_S) | AR8216_ATU_OP_FLUSH_PORT;
+               t |= AR8216_ATU_ACTIVE;
+               ar8xxx_write(priv, AR8216_REG_ATU_FUNC0, t);
+       }
 
        return ret;
 }
@@ -696,6 +717,77 @@ ar8216_init_port(struct ar8xxx_priv *priv, int port)
        }
 }
 
+static void
+ar8216_wait_atu_ready(struct ar8xxx_priv *priv, u16 r2, u16 r1)
+{
+       int timeout = 20;
+
+       while (ar8xxx_mii_read32(priv, r2, r1) & AR8216_ATU_ACTIVE && --timeout)
+                udelay(10);
+
+       if (!timeout)
+               pr_err("ar8216: timeout waiting for atu to become ready\n");
+}
+
+static void ar8216_get_arl_entry(struct ar8xxx_priv *priv,
+                                struct arl_entry *a, u32 *status, enum arl_op op)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 r2, page;
+       u16 r1_func0, r1_func1, r1_func2;
+       u32 t, val0, val1, val2;
+       int i;
+
+       split_addr(AR8216_REG_ATU_FUNC0, &r1_func0, &r2, &page);
+       r2 |= 0x10;
+
+       r1_func1 = (AR8216_REG_ATU_FUNC1 >> 1) & 0x1e;
+       r1_func2 = (AR8216_REG_ATU_FUNC2 >> 1) & 0x1e;
+
+       switch (op) {
+       case AR8XXX_ARL_INITIALIZE:
+               /* all ATU registers are on the same page
+               * therefore set page only once
+               */
+               bus->write(bus, 0x18, 0, page);
+               wait_for_page_switch();
+
+               ar8216_wait_atu_ready(priv, r2, r1_func0);
+
+               ar8xxx_mii_write32(priv, r2, r1_func0, AR8216_ATU_OP_GET_NEXT);
+               ar8xxx_mii_write32(priv, r2, r1_func1, 0);
+               ar8xxx_mii_write32(priv, r2, r1_func2, 0);
+               break;
+       case AR8XXX_ARL_GET_NEXT:
+               t = ar8xxx_mii_read32(priv, r2, r1_func0);
+               t |= AR8216_ATU_ACTIVE;
+               ar8xxx_mii_write32(priv, r2, r1_func0, t);
+               ar8216_wait_atu_ready(priv, r2, r1_func0);
+
+               val0 = ar8xxx_mii_read32(priv, r2, r1_func0);
+               val1 = ar8xxx_mii_read32(priv, r2, r1_func1);
+               val2 = ar8xxx_mii_read32(priv, r2, r1_func2);
+
+               *status = (val2 & AR8216_ATU_STATUS) >> AR8216_ATU_STATUS_S;
+               if (!*status)
+                       break;
+
+               i = 0;
+               t = AR8216_ATU_PORT0;
+               while (!(val2 & t) && ++i < priv->dev.ports)
+                       t <<= 1;
+
+               a->port = i;
+               a->mac[0] = (val0 & AR8216_ATU_ADDR5) >> AR8216_ATU_ADDR5_S;
+               a->mac[1] = (val0 & AR8216_ATU_ADDR4) >> AR8216_ATU_ADDR4_S;
+               a->mac[2] = (val1 & AR8216_ATU_ADDR3) >> AR8216_ATU_ADDR3_S;
+               a->mac[3] = (val1 & AR8216_ATU_ADDR2) >> AR8216_ATU_ADDR2_S;
+               a->mac[4] = (val1 & AR8216_ATU_ADDR1) >> AR8216_ATU_ADDR1_S;
+               a->mac[5] = (val1 & AR8216_ATU_ADDR0) >> AR8216_ATU_ADDR0_S;
+               break;
+       }
+}
+
 static void
 ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
 {
@@ -742,12 +834,11 @@ ar8236_init_globals(struct ar8xxx_priv *priv)
                   AR8316_GCTRL_MTU, 9018 + 8 + 2);
 
        /* enable cpu port to receive arp frames */
-       ar8xxx_rmw(priv, AR8216_REG_ATU_CTRL,
-                  AR8236_ATU_CTRL_RES, AR8236_ATU_CTRL_RES);
+       ar8xxx_reg_set(priv, AR8216_REG_ATU_CTRL,
+                  AR8236_ATU_CTRL_RES);
 
        /* enable cpu port to receive multicast and broadcast frames */
-       ar8xxx_rmw(priv, AR8216_REG_FLOOD_MASK,
-                  AR8236_FM_CPU_BROADCAST_EN | AR8236_FM_CPU_BCAST_FWD_EN,
+       ar8xxx_reg_set(priv, AR8216_REG_FLOOD_MASK,
                   AR8236_FM_CPU_BROADCAST_EN | AR8236_FM_CPU_BCAST_FWD_EN);
 
        /* Enable MIB counters */
@@ -958,13 +1049,11 @@ ar8216_set_mirror_regs(struct ar8xxx_priv *priv)
                   AR8216_GLOBAL_CPUPORT_MIRROR_PORT,
                   (0xF << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S));
        for (port = 0; port < AR8216_NUM_PORTS; port++) {
-               ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
-                          AR8216_PORT_CTRL_MIRROR_RX,
-                          0);
+               ar8xxx_reg_clear(priv, AR8216_REG_PORT_CTRL(port),
+                          AR8216_PORT_CTRL_MIRROR_RX);
 
-               ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
-                          AR8216_PORT_CTRL_MIRROR_TX,
-                          0);
+               ar8xxx_reg_clear(priv, AR8216_REG_PORT_CTRL(port),
+                          AR8216_PORT_CTRL_MIRROR_TX);
        }
 
        /* now enable mirroring if necessary */
@@ -979,13 +1068,11 @@ ar8216_set_mirror_regs(struct ar8xxx_priv *priv)
                   (priv->monitor_port << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S));
 
        if (priv->mirror_rx)
-               ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(priv->source_port),
-                          AR8216_PORT_CTRL_MIRROR_RX,
+               ar8xxx_reg_set(priv, AR8216_REG_PORT_CTRL(priv->source_port),
                           AR8216_PORT_CTRL_MIRROR_RX);
 
        if (priv->mirror_tx)
-               ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(priv->source_port),
-                          AR8216_PORT_CTRL_MIRROR_TX,
+               ar8xxx_reg_set(priv, AR8216_REG_PORT_CTRL(priv->source_port),
                           AR8216_PORT_CTRL_MIRROR_TX);
 }
 
@@ -1046,6 +1133,7 @@ int
 ar8xxx_sw_reset_switch(struct switch_dev *dev)
 {
        struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       const struct ar8xxx_chip *chip = priv->chip;
        int i;
 
        mutex_lock(&priv->reg_mutex);
@@ -1057,18 +1145,18 @@ ar8xxx_sw_reset_switch(struct switch_dev *dev)
 
        /* Configure all ports */
        for (i = 0; i < dev->ports; i++)
-               priv->chip->init_port(priv, i);
+               chip->init_port(priv, i);
 
        priv->mirror_rx = false;
        priv->mirror_tx = false;
        priv->source_port = 0;
        priv->monitor_port = 0;
 
-       priv->chip->init_globals(priv);
+       chip->init_globals(priv);
 
        mutex_unlock(&priv->reg_mutex);
 
-       return ar8xxx_sw_hw_apply(dev);
+       return chip->sw_hw_apply(dev);
 }
 
 int
@@ -1199,7 +1287,7 @@ ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev,
        return 0;
 }
 
-static int
+int
 ar8xxx_sw_set_port_reset_mib(struct switch_dev *dev,
                             const struct switch_attr *attr,
                             struct switch_val *val)
@@ -1229,7 +1317,7 @@ unlock:
        return ret;
 }
 
-static int
+int
 ar8xxx_sw_get_port_mib(struct switch_dev *dev,
                       const struct switch_attr *attr,
                       struct switch_val *val)
@@ -1277,7 +1365,112 @@ unlock:
        return ret;
 }
 
-static struct switch_attr ar8xxx_sw_attr_globals[] = {
+int
+ar8xxx_sw_get_arl_table(struct switch_dev *dev,
+                       const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       struct mii_bus *bus = priv->mii_bus;
+       const struct ar8xxx_chip *chip = priv->chip;
+       char *buf = priv->arl_buf;
+       int i, j, k, len = 0;
+       struct arl_entry *a, *a1;
+       u32 status;
+
+       if (!chip->get_arl_entry)
+               return -EOPNOTSUPP;
+
+       mutex_lock(&priv->reg_mutex);
+       mutex_lock(&bus->mdio_lock);
+
+       chip->get_arl_entry(priv, NULL, NULL, AR8XXX_ARL_INITIALIZE);
+
+       for(i = 0; i < AR8XXX_NUM_ARL_RECORDS; ++i) {
+               a = &priv->arl_table[i];
+               duplicate:
+               chip->get_arl_entry(priv, a, &status, AR8XXX_ARL_GET_NEXT);
+
+               if (!status)
+                       break;
+
+               /* avoid duplicates
+                * ARL table can include multiple valid entries
+                * per MAC, just with differing status codes
+                */
+               for (j = 0; j < i; ++j) {
+                       a1 = &priv->arl_table[j];
+                       if (a->port == a1->port && !memcmp(a->mac, a1->mac, sizeof(a->mac)))
+                               goto duplicate;
+               }
+       }
+
+       mutex_unlock(&bus->mdio_lock);
+
+       len += snprintf(buf + len, sizeof(priv->arl_buf) - len,
+                        "address resolution table\n");
+
+       if (i == AR8XXX_NUM_ARL_RECORDS)
+               len += snprintf(buf + len, sizeof(priv->arl_buf) - len,
+                               "Too many entries found, displaying the first %d only!\n",
+                               AR8XXX_NUM_ARL_RECORDS);
+
+       for (j = 0; j < priv->dev.ports; ++j) {
+               for (k = 0; k < i; ++k) {
+                       a = &priv->arl_table[k];
+                       if (a->port != j)
+                               continue;
+                       len += snprintf(buf + len, sizeof(priv->arl_buf) - len,
+                                       "Port %d: MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+                                       j,
+                                       a->mac[5], a->mac[4], a->mac[3],
+                                       a->mac[2], a->mac[1], a->mac[0]);
+               }
+       }
+
+       val->value.s = buf;
+       val->len = len;
+
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8xxx_sw_set_flush_arl_table(struct switch_dev *dev,
+                             const struct switch_attr *attr,
+                             struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int ret;
+
+       mutex_lock(&priv->reg_mutex);
+       ret = priv->chip->atu_flush(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return ret;
+}
+
+int
+ar8xxx_sw_set_flush_port_arl_table(struct switch_dev *dev,
+                                  const struct switch_attr *attr,
+                                  struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int port, ret;
+
+       port = val->port_vlan;
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       mutex_lock(&priv->reg_mutex);
+       ret = priv->chip->atu_flush_port(priv, port);
+       mutex_unlock(&priv->reg_mutex);
+
+       return ret;
+}
+
+static const struct switch_attr ar8xxx_sw_attr_globals[] = {
        {
                .type = SWITCH_TYPE_INT,
                .name = "enable_vlan",
@@ -1324,9 +1517,22 @@ static struct switch_attr ar8xxx_sw_attr_globals[] = {
                .get = ar8xxx_sw_get_mirror_source_port,
                .max = AR8216_NUM_PORTS - 1
        },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "arl_table",
+               .description = "Get ARL table",
+               .set = NULL,
+               .get = ar8xxx_sw_get_arl_table,
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "flush_arl_table",
+               .description = "Flush ARL table",
+               .set = ar8xxx_sw_set_flush_arl_table,
+       },
 };
 
-struct switch_attr ar8xxx_sw_attr_port[2] = {
+const struct switch_attr ar8xxx_sw_attr_port[] = {
        {
                .type = SWITCH_TYPE_NOVAL,
                .name = "reset_mib",
@@ -1340,9 +1546,15 @@ struct switch_attr ar8xxx_sw_attr_port[2] = {
                .set = NULL,
                .get = ar8xxx_sw_get_port_mib,
        },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "flush_arl_table",
+               .description = "Flush port's ARL table entries",
+               .set = ar8xxx_sw_set_flush_port_arl_table,
+       },
 };
 
-struct switch_attr ar8xxx_sw_attr_vlan[1] = {
+const struct switch_attr ar8xxx_sw_attr_vlan[1] = {
        {
                .type = SWITCH_TYPE_INT,
                .name = "vid",
@@ -1392,9 +1604,12 @@ static const struct ar8xxx_chip ar8216_chip = {
        .setup_port = ar8216_setup_port,
        .read_port_status = ar8216_read_port_status,
        .atu_flush = ar8216_atu_flush,
+       .atu_flush_port = ar8216_atu_flush_port,
        .vtu_flush = ar8216_vtu_flush,
        .vtu_load_vlan = ar8216_vtu_load_vlan,
        .set_mirror_regs = ar8216_set_mirror_regs,
+       .get_arl_entry = ar8216_get_arl_entry,
+       .sw_hw_apply = ar8xxx_sw_hw_apply,
 
        .num_mibs = ARRAY_SIZE(ar8216_mibs),
        .mib_decs = ar8216_mibs,
@@ -1418,9 +1633,12 @@ static const struct ar8xxx_chip ar8236_chip = {
        .setup_port = ar8236_setup_port,
        .read_port_status = ar8216_read_port_status,
        .atu_flush = ar8216_atu_flush,
+       .atu_flush_port = ar8216_atu_flush_port,
        .vtu_flush = ar8216_vtu_flush,
        .vtu_load_vlan = ar8216_vtu_load_vlan,
        .set_mirror_regs = ar8216_set_mirror_regs,
+       .get_arl_entry = ar8216_get_arl_entry,
+       .sw_hw_apply = ar8xxx_sw_hw_apply,
 
        .num_mibs = ARRAY_SIZE(ar8236_mibs),
        .mib_decs = ar8236_mibs,
@@ -1444,9 +1662,12 @@ static const struct ar8xxx_chip ar8316_chip = {
        .setup_port = ar8216_setup_port,
        .read_port_status = ar8216_read_port_status,
        .atu_flush = ar8216_atu_flush,
+       .atu_flush_port = ar8216_atu_flush_port,
        .vtu_flush = ar8216_vtu_flush,
        .vtu_load_vlan = ar8216_vtu_load_vlan,
        .set_mirror_regs = ar8216_set_mirror_regs,
+       .get_arl_entry = ar8216_get_arl_entry,
+       .sw_hw_apply = ar8xxx_sw_hw_apply,
 
        .num_mibs = ARRAY_SIZE(ar8236_mibs),
        .mib_decs = ar8236_mibs,
@@ -1690,12 +1911,44 @@ ar8xxx_phy_config_init(struct phy_device *phydev)
        return 0;
 }
 
+static bool
+ar8xxx_check_link_states(struct ar8xxx_priv *priv)
+{
+       bool link_new, changed = false;
+       u32 status;
+       int i;
+
+       mutex_lock(&priv->reg_mutex);
+
+       for (i = 0; i < priv->dev.ports; i++) {
+               status = priv->chip->read_port_status(priv, i);
+               link_new = !!(status & AR8216_PORT_STATUS_LINK_UP);
+               if (link_new == priv->link_up[i])
+                       continue;
+
+               priv->link_up[i] = link_new;
+               changed = true;
+               /* flush ARL entries for this port if it went down*/
+               if (!link_new)
+                       priv->chip->atu_flush_port(priv, i);
+               dev_info(&priv->phy->dev, "Port %d is %s\n",
+                        i, link_new ? "up" : "down");
+       }
+
+       mutex_unlock(&priv->reg_mutex);
+
+       return changed;
+}
+
 static int
 ar8xxx_phy_read_status(struct phy_device *phydev)
 {
        struct ar8xxx_priv *priv = phydev->priv;
        struct switch_port_link link;
-       int ret;
+
+       /* check for switch port link changes */
+       if (phydev->state == PHY_CHANGELINK)
+               ar8xxx_check_link_states(priv);
 
        if (phydev->addr != 0)
                return genphy_read_status(phydev);
@@ -1720,16 +1973,11 @@ ar8xxx_phy_read_status(struct phy_device *phydev)
        }
        phydev->duplex = link.duplex ? DUPLEX_FULL : DUPLEX_HALF;
 
-       /* flush the address translation unit */
-       mutex_lock(&priv->reg_mutex);
-       ret = priv->chip->atu_flush(priv);
-       mutex_unlock(&priv->reg_mutex);
-
        phydev->state = PHY_RUNNING;
        netif_carrier_on(phydev->attached_dev);
        phydev->adjust_link(phydev->attached_dev);
 
-       return ret;
+       return 0;
 }
 
 static int