ar8216: add reading ARL table for AR8216/AR8236/AR8316
authorFelix Fietkau <nbd@openwrt.org>
Wed, 15 Jul 2015 08:17:23 +0000 (08:17 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Wed, 15 Jul 2015 08:17:23 +0000 (08:17 +0000)
Adds the chip-specific part of reading ARL table for AR8216/AR8236/AR8316.

It's based on the AR8236 datasheet and compile-tested only as I couldn't
find datasheets for AR8216/AR8316 and don't own devices with these chips.

The existing ar8216_atu_flush implementation was used for all three
chip types, therefore I guess they share a common ATU register layout.

More testing would be appreciated.

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
SVN-Revision: 46379

target/linux/generic/files/drivers/net/phy/ar8216.c
target/linux/generic/files/drivers/net/phy/ar8216.h

index e39d540a258e1dfb3817cc47170f6cb83a4dfdf3..595f144ddb394cbb9e6cafe019d66e3390ec1b06 100644 (file)
@@ -598,10 +598,10 @@ 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 |
-                                                  AR8216_ATU_ACTIVE);
+               ar8xxx_write(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_OP_FLUSH |
+                                                        AR8216_ATU_ACTIVE);
 
        return ret;
 }
@@ -701,6 +701,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)
 {
@@ -1349,7 +1420,6 @@ ar8xxx_sw_get_arl_table(struct switch_dev *dev,
        return 0;
 }
 
-
 static const struct switch_attr ar8xxx_sw_attr_globals[] = {
        {
                .type = SWITCH_TYPE_INT,
@@ -1475,6 +1545,7 @@ static const struct ar8xxx_chip ar8216_chip = {
        .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),
@@ -1502,6 +1573,7 @@ static const struct ar8xxx_chip ar8236_chip = {
        .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),
@@ -1529,6 +1601,7 @@ static const struct ar8xxx_chip ar8316_chip = {
        .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),
index 0f53f23f6e7ec140ee2e89eaffe9549dcec34178..934a8b5cc175a3b9ccd6e69f914671ded68b08f5 100644 (file)
@@ -79,7 +79,7 @@
 #define   AR8236_VTUDATA_MEMBER                BITS(0, 7)
 #define   AR8216_VTUDATA_VALID         BIT(11)
 
-#define AR8216_REG_ATU                 0x0050
+#define AR8216_REG_ATU_FUNC0           0x0050
 #define   AR8216_ATU_OP                        BITS(0, 3)
 #define   AR8216_ATU_OP_NOOP           0x0
 #define   AR8216_ATU_OP_FLUSH          0x1
 #define   AR8216_ATU_ACTIVE            BIT(3)
 #define   AR8216_ATU_PORT_NUM          BITS(8, 4)
 #define   AR8216_ATU_FULL_VIO          BIT(12)
-#define   AR8216_ATU_ADDR4             BITS(16, 8)
-#define   AR8216_ATU_ADDR5             BITS(24, 8)
+#define   AR8216_ATU_ADDR5             BITS(16, 8)
+#define   AR8216_ATU_ADDR5_S           16
+#define   AR8216_ATU_ADDR4             BITS(24, 8)
+#define   AR8216_ATU_ADDR4_S           24
 
-#define AR8216_REG_ATU_DATA            0x0054
+#define AR8216_REG_ATU_FUNC1           0x0054
 #define   AR8216_ATU_ADDR3             BITS(0, 8)
+#define   AR8216_ATU_ADDR3_S           0
 #define   AR8216_ATU_ADDR2             BITS(8, 8)
+#define   AR8216_ATU_ADDR2_S           8
 #define   AR8216_ATU_ADDR1             BITS(16, 8)
+#define   AR8216_ATU_ADDR1_S           16
 #define   AR8216_ATU_ADDR0             BITS(24, 8)
+#define   AR8216_ATU_ADDR0_S           24
+
+#define AR8216_REG_ATU_FUNC2           0x0058
+#define   AR8216_ATU_PORTS             BITS(0, 6)
+#define   AR8216_ATU_PORT0             BIT(0)
+#define   AR8216_ATU_PORT1             BIT(1)
+#define   AR8216_ATU_PORT2             BIT(2)
+#define   AR8216_ATU_PORT3             BIT(3)
+#define   AR8216_ATU_PORT4             BIT(4)
+#define   AR8216_ATU_PORT5             BIT(5)
+#define   AR8216_ATU_STATUS            BITS(16, 4)
+#define   AR8216_ATU_STATUS_S          16
 
 #define AR8216_REG_ATU_CTRL            0x005C
 #define   AR8216_ATU_CTRL_AGE_EN       BIT(17)