ar8216: Fix problem with AR8337 MAC swap handling
[openwrt.git] / target / linux / generic / files / drivers / net / phy / ar8327.c
index ea880bc..a6dd7d8 100644 (file)
@@ -124,6 +124,9 @@ ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg)
                break;
        }
 
+       if (cfg->mac06_exchange_en)
+               t |= AR8337_PAD_MAC06_EXCHANGE_EN;
+
        return t;
 }
 
@@ -508,9 +511,6 @@ ar8327_hw_config_pdata(struct ar8xxx_priv *priv,
        data->port6_status = ar8327_get_port_init_status(&pdata->port6_cfg);
 
        t = ar8327_get_pad_cfg(pdata->pad0_cfg);
-       if (chip_is_ar8337(priv))
-               t |= AR8337_PAD_MAC06_EXCHANGE_EN;
-
        ar8xxx_write(priv, AR8327_REG_PAD0_MODE, t);
        t = ar8327_get_pad_cfg(pdata->pad5_cfg);
        ar8xxx_write(priv, AR8327_REG_PAD5_MODE, t);
@@ -759,7 +759,8 @@ ar8327_atu_flush(struct ar8xxx_priv *priv)
                              AR8327_ATU_FUNC_BUSY, 0);
        if (!ret)
                ar8xxx_write(priv, AR8327_REG_ATU_FUNC,
-                           AR8327_ATU_FUNC_OP_FLUSH);
+                            AR8327_ATU_FUNC_OP_FLUSH |
+                            AR8327_ATU_FUNC_BUSY);
 
        return ret;
 }
@@ -970,6 +971,78 @@ ar8327_sw_get_eee(struct switch_dev *dev,
        return 0;
 }
 
+static void
+ar8327_wait_atu_ready(struct ar8xxx_priv *priv, u16 r2, u16 r1)
+{
+       int timeout = 20;
+
+       while (ar8xxx_mii_read32(priv, r2, r1) & AR8327_ATU_FUNC_BUSY && --timeout)
+                udelay(10);
+
+       if (!timeout)
+               pr_err("ar8327: timeout waiting for atu to become ready\n");
+}
+
+static void ar8327_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_data0, r1_data1, r1_data2, r1_func;
+       u32 t, val0, val1, val2;
+       int i;
+
+       split_addr(AR8327_REG_ATU_DATA0, &r1_data0, &r2, &page);
+       r2 |= 0x10;
+
+       r1_data1 = (AR8327_REG_ATU_DATA1 >> 1) & 0x1e;
+       r1_data2 = (AR8327_REG_ATU_DATA2 >> 1) & 0x1e;
+       r1_func  = (AR8327_REG_ATU_FUNC >> 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();
+
+               ar8327_wait_atu_ready(priv, r2, r1_func);
+
+               ar8xxx_mii_write32(priv, r2, r1_data0, 0);
+               ar8xxx_mii_write32(priv, r2, r1_data1, 0);
+               ar8xxx_mii_write32(priv, r2, r1_data2, 0);
+               break;
+       case AR8XXX_ARL_GET_NEXT:
+               ar8xxx_mii_write32(priv, r2, r1_func,
+                                  AR8327_ATU_FUNC_OP_GET_NEXT |
+                                  AR8327_ATU_FUNC_BUSY);
+               ar8327_wait_atu_ready(priv, r2, r1_func);
+
+               val0 = ar8xxx_mii_read32(priv, r2, r1_data0);
+               val1 = ar8xxx_mii_read32(priv, r2, r1_data1);
+               val2 = ar8xxx_mii_read32(priv, r2, r1_data2);
+
+               *status = val2 & AR8327_ATU_STATUS;
+               if (!*status)
+                       break;
+
+               i = 0;
+               t = AR8327_ATU_PORT0;
+               while (!(val1 & t) && ++i < AR8327_NUM_PORTS)
+                       t <<= 1;
+
+               a->port = i;
+               a->mac[0] = (val0 & AR8327_ATU_ADDR0) >> AR8327_ATU_ADDR0_S;
+               a->mac[1] = (val0 & AR8327_ATU_ADDR1) >> AR8327_ATU_ADDR1_S;
+               a->mac[2] = (val0 & AR8327_ATU_ADDR2) >> AR8327_ATU_ADDR2_S;
+               a->mac[3] = (val0 & AR8327_ATU_ADDR3) >> AR8327_ATU_ADDR3_S;
+               a->mac[4] = (val1 & AR8327_ATU_ADDR4) >> AR8327_ATU_ADDR4_S;
+               a->mac[5] = (val1 & AR8327_ATU_ADDR5) >> AR8327_ATU_ADDR5_S;
+               break;
+       }
+}
+
 static int
 ar8327_sw_hw_apply(struct switch_dev *dev)
 {
@@ -1040,6 +1113,13 @@ static const struct switch_attr ar8327_sw_attr_globals[] = {
                .get = ar8xxx_sw_get_mirror_source_port,
                .max = AR8327_NUM_PORTS - 1
        },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "arl_table",
+               .description = "Get ARL table",
+               .set = NULL,
+               .get = ar8xxx_sw_get_arl_table,
+       },
 };
 
 static const struct switch_attr ar8327_sw_attr_port[] = {
@@ -1113,6 +1193,7 @@ const struct ar8xxx_chip ar8327_chip = {
        .vtu_load_vlan = ar8327_vtu_load_vlan,
        .phy_fixup = ar8327_phy_fixup,
        .set_mirror_regs = ar8327_set_mirror_regs,
+       .get_arl_entry = ar8327_get_arl_entry,
        .sw_hw_apply = ar8327_sw_hw_apply,
 
        .num_mibs = ARRAY_SIZE(ar8236_mibs),
@@ -1145,6 +1226,7 @@ const struct ar8xxx_chip ar8337_chip = {
        .vtu_load_vlan = ar8327_vtu_load_vlan,
        .phy_fixup = ar8327_phy_fixup,
        .set_mirror_regs = ar8327_set_mirror_regs,
+       .get_arl_entry = ar8327_get_arl_entry,
        .sw_hw_apply = ar8327_sw_hw_apply,
 
        .num_mibs = ARRAY_SIZE(ar8236_mibs),