<div dir="ltr">I just realized that this patchset would have looked nicer if I would have put it in a b53_hwdbg.c [or something similar] file.<br></div><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Feb 23, 2015 at 4:41 PM, Alexandru Ardelean <span dir="ltr"><<a href="mailto:ardeleanalex@gmail.com" target="_blank">ardeleanalex@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">From: Alexandru Ardelean <<a href="mailto:ardeleanalex@gmail.com">ardeleanalex@gmail.com</a>><br>
<br>
This has been implemented on a BCM53128 chip.<br>
May very likely work on BCM53125 which is a 4 port variant.<br>
No idea on what other chips it works, but since this is hidden<br>
behind a config flag, others are free to try it out themselves.<br>
<br>
Read/Write operations for the ARL table.<br>
To use it:<br>
   swconfig dev switch0 set arl "rd XX:XX:XX:XX:XX:XX vid NNNN"<br>
   swconfig dev switch0 get arl<br>
<br>
Output should be:<br>
  ARL Operation: Read<br>
    MAC: XX:XX:XX:XX:XX:XX<br>
    VLAN ID: NNNN<br>
    Valid: 1<br>
    Age: 1<br>
    Static: 0<br>
    Port(s): 001<br>
<br>
Reading/Writing the ARL table is a bit complex of an operation,<br>
so string parsing is used.<br>
The idea is that this uses swconfig 'set' prepare a r/w operation<br>
and swconfig 'get' to execute an operation.<br>
Not the most elegant approach, but it works fairly well for a<br>
debugging operation using b53 hardware.<br>
<br>
There are 3 op codes: rd, wr and cl. 'cl' clears any previous rd/wr.<br>
This parsing is stupid simple, so any spaces, cases and quotes matter.<br>
If a operation failed, the output will be 'failed' and the kernel<br>
log can be consulted. The kernel log can also be consulted for<br>
various messages that may have been printed during a r/w operation.<br>
<br>
For 'rd' and 'wr' ops<br>
 - if VLAN not enabled, only the MAC address is required<br>
 - if VLAN is enabled, both MAC and VLAN ID are required<br>
<br>
Commands:<br>
 - swconfig dev switch0 set arl cl - clear any prev op; no 'get' call required<br>
 - swconfig dev switch0 set arl "rd <MAC> <VID>" - the call 'get',<br>
     if output is 'failed' check kernel log<br>
 - swconfig dev switch0 set arl "rd <MAC> <VID> \<br>
   [static 0/1] [age 0/1] [valid 0/1] [ports NNN]"<br>
<br>
The write operation takes parameters, static, age and valid, which<br>
are bits that can be set/modified in the ARL table.<br>
<br>
The ports must field is dependant on the type of MAC.<br>
 - for unicat MACs, ports is a port number<br>
 - for multicast MACs, ports is a bitmask for ports on which to forward<br>
This is the same meaning when reading MAC/VID entries.<br>
<br>
Signed-off-by: Alexandru Ardelean <<a href="mailto:ardeleanalex@gmail.com">ardeleanalex@gmail.com</a>><br>
---<br>
 .../generic/files/drivers/net/phy/b53/b53_common.c | 278 +++++++++++++++++++++<br>
 .../generic/files/drivers/net/phy/b53/b53_priv.h   |  20 ++<br>
 .../generic/files/drivers/net/phy/b53/b53_regs.h   |  28 +++<br>
 3 files changed, 326 insertions(+)<br>
<br>
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_common.c b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c<br>
index bdd4006..ab7604a 100644<br>
--- a/target/linux/generic/files/drivers/net/phy/b53/b53_common.c<br>
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c<br>
@@ -464,6 +464,155 @@ out:<br>
        b53_write16(dev, B53_MGMT_PAGE, B53_MIRROR_CTRL, port_mirror_ctrl);<br>
 }<br>
<br>
+static int b53_wait_arl_table_rw_done(struct b53_device *dev)<br>
+{<br>
+       u8 i;<br>
+<br>
+       for (i = 0; i < 10; i++) {<br>
+               u8 arl_rw_ctrl;<br>
+               b53_read8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, &arl_rw_ctrl);<br>
+<br>
+               if (!(arl_rw_ctrl & B53_ARLTBL_DONE))<br>
+                       return 0;<br>
+<br>
+               mdelay(1);<br>
+       }<br>
+       return -EINVAL;<br>
+}<br>
+<br>
+static int b53_read_arl_table_entry(struct b53_device *dev,<br>
+                       u64 mac_match, u16 vid_match, u32 *arl_entry, u8 *idx)<br>
+{<br>
+       u8 i;<br>
+<br>
+       if (b53_wait_arl_table_rw_done(dev))<br>
+               return -EINVAL;<br>
+<br>
+       for (i = 0; i <= 3; i++) {<br>
+               u64 mv;<br>
+               u32 en;<br>
+               u8 *m = (u8 *)&mv;<br>
+<br>
+               b53_read64(dev, B53_ARLIO_PAGE, B53_ARLTBL_MAC_VID_ENTRY(i), &mv);<br>
+               b53_read32(dev, B53_ARLIO_PAGE, B53_ARLTBL_DATA_ENTRY(i), &en);<br>
+               pr_info("ARL Entry(%u) %08x MAC/VID: %02x:%02x %02x:%02x:%02x:%02x:%02x:%02x\n",<br>
+                       i, en, m[0],m[1],m[2],m[3],m[4],m[5],m[6],m[7]);<br>
+               if (!(en & B53_ARLTBL_VALID))<br>
+                       continue;<br>
+               if (B53_ARLTBL_MACADDR(mv) != mac_match)<br>
+                       continue;<br>
+               if (dev->enable_vlan && (B53_ARLTBL_VID_GET(mv) != vid_match))<br>
+                       continue;<br>
+               *idx = i;<br>
+               *arl_entry = en;<br>
+               return 0;<br>
+       }<br>
+<br>
+       return -EINVAL;<br>
+}<br>
+<br>
+static inline void b53_arl_entry_to_arl_ops(struct b53_arl_ops *ops, u32 arl_entry)<br>
+{<br>
+       ops->age_set = 0;<br>
+       ops->valid_set = 0;<br>
+       ops->static_set = 0;<br>
+       ops->ports_set = 0;<br>
+       ops->valid = !!(arl_entry & B53_ARLTBL_VALID);<br>
+       ops->static_ = !!(arl_entry & B53_ARLTBL_STATIC);<br>
+       ops->age = !!(arl_entry & B53_ARLTBL_AGE);<br>
+       ops->ports = (arl_entry & 0x1ff);<br>
+}<br>
+<br>
+static inline void b53_arl_ops_to_arl_entry(struct b53_arl_ops *ops, u32 *arl_entry)<br>
+{<br>
+       if (ops->valid_set)<br>
+               *arl_entry = (ops->valid) ? (*arl_entry | B53_ARLTBL_VALID) :<br>
+                                           (*arl_entry & ~B53_ARLTBL_VALID);<br>
+       if (ops->static_set)<br>
+               *arl_entry = (ops->static_) ? (*arl_entry | B53_ARLTBL_STATIC) :<br>
+                                             (*arl_entry & ~B53_ARLTBL_STATIC);<br>
+       if (ops->age_set)<br>
+               *arl_entry = (ops->age) ? (*arl_entry | B53_ARLTBL_AGE) :<br>
+                                         (*arl_entry & ~B53_ARLTBL_AGE);<br>
+       if (ops->ports_set) {<br>
+               *arl_entry &= ~0x1fe;<br>
+               *arl_entry |= (ops->ports & 0x1ff);<br>
+       }<br>
+}<br>
+<br>
+static int b53_run_arl_ops(struct b53_device *dev)<br>
+{<br>
+       struct b53_arl_ops lops, *ops = dev->arl_ops;<br>
+       u8 arl_rw_ctrl;<br>
+       u64 u64_mac = b53_mac_array_to_u64(ops->mac);<br>
+       u64 u64_mac_vid;<br>
+       u32 arl_entry = 0;<br>
+       u8 idx = 0, *m;<br>
+       int rc;<br>
+<br>
+       /* A read always comes first */<br>
+       b53_write48(dev, B53_ARLIO_PAGE, B53_MAC_ADDR_IDX, u64_mac);<br>
+       b53_write16(dev, B53_ARLIO_PAGE, B53_VLAN_ID_IDX, ops->vid);<br>
+<br>
+       b53_read8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, &arl_rw_ctrl);<br>
+       arl_rw_ctrl |= (B53_ARLTBL_DONE | B53_ARLTBL_RW);<br>
+       b53_write8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, arl_rw_ctrl);<br>
+<br>
+       rc = b53_read_arl_table_entry(dev, u64_mac, ops->vid, &arl_entry, &idx);<br>
+<br>
+       if (!ops->write) {<br>
+               if (!rc)<br>
+                       b53_arl_entry_to_arl_ops(ops, arl_entry);<br>
+               return rc;<br>
+       }<br>
+       /* If this entry is new, reset ARL register & index */<br>
+       if (rc) {<br>
+               arl_entry = 0;<br>
+               idx = 1;<br>
+       }<br>
+<br>
+       /* Now we're writing */<br>
+       b53_arl_ops_to_arl_entry(ops, &arl_entry);<br>
+<br>
+       b53_read8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, &arl_rw_ctrl);<br>
+<br>
+       u64_mac_vid = u64_mac | B53_ARLTBL_VID_SET(ops->vid);<br>
+       m = (u8 *)&u64_mac_vid;<br>
+       b53_write64(dev, B53_ARLIO_PAGE, B53_ARLTBL_MAC_VID_ENTRY(idx), u64_mac_vid);<br>
+       b53_write32(dev, B53_ARLIO_PAGE, B53_ARLTBL_DATA_ENTRY(idx), arl_entry);<br>
+<br>
+       arl_rw_ctrl &= ~B53_ARLTBL_RW;<br>
+       arl_rw_ctrl |= B53_ARLTBL_DONE;<br>
+       b53_write8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, arl_rw_ctrl);<br>
+       pr_info("Writing: ARL Entry(%u) %08x MAC/VID: %02x%02x %02x:%02x:%02x:%02x:%02x:%02x\n",<br>
+                idx, arl_entry, m[0],m[1],m[2],m[3],m[4],m[5],m[6],m[7]);<br>
+<br>
+       if (b53_wait_arl_table_rw_done(dev))<br>
+               return -EINVAL;<br>
+<br>
+       /* Writing done; read result */<br>
+       b53_read8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, &arl_rw_ctrl);<br>
+       arl_rw_ctrl |= (B53_ARLTBL_RW | B53_ARLTBL_DONE);<br>
+       b53_write8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, arl_rw_ctrl);<br>
+<br>
+       u64_mac &= ~B53_ARLTBL_VID_SET(0);<br>
+       if (b53_read_arl_table_entry(dev, u64_mac, ops->vid, &arl_entry, &idx))<br>
+               return -EINVAL;<br>
+<br>
+       b53_arl_entry_to_arl_ops(&lops, arl_entry);<br>
+<br>
+       if (ops->valid_set && (lops.valid != ops->valid))<br>
+               return -EINVAL;<br>
+       if (ops->static_set && (lops.static_ != ops->static_))<br>
+               return -EINVAL;<br>
+       if (ops->age_set && (lops.age != ops->age))<br>
+               return -EINVAL;<br>
+       if (ops->ports_set && (lops.ports != ops->ports))<br>
+               return -EINVAL;<br>
+<br>
+       return 0;<br>
+}<br>
+<br>
 #else<br>
 #define b53_enable_port_capture(x)<br>
 #endif /* CONFIG_B53_HW_DEBUG_FEATURES */<br>
@@ -885,6 +1034,128 @@ out:<br>
        return rc;<br>
 }<br>
<br>
+static int b53_global_get_arl(struct switch_dev *dev,<br>
+                               const struct switch_attr *attr,<br>
+                               struct switch_val *val)<br>
+{<br>
+       struct b53_device *priv = sw_to_b53(dev);<br>
+       struct b53_arl_ops *a = priv->arl_ops;<br>
+       int len = 0;<br>
+<br>
+       /* Run ARL Table R/W */<br>
+       if (!a || b53_run_arl_ops(priv)) {<br>
+               snprintf(priv->buf, B53_BUF_SIZE,<br>
+                        "No read/write ARL operation set before get call\n");<br>
+               return -EINVAL;<br>
+       }<br>
+<br>
+       len += snprintf(priv->buf + len, B53_BUF_SIZE - len,<br>
+                       "ARL Operation: %s\n", a->write ? "Write" :"Read");<br>
+       len += snprintf(priv->buf + len, B53_BUF_SIZE - len,<br>
+                       "  MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",<br>
+                       a->mac[0],a->mac[1],a->mac[2],<br>
+                       a->mac[3],a->mac[4],a->mac[5]);<br>
+<br>
+       len += snprintf(priv->buf + len, B53_BUF_SIZE - len,<br>
+                       "  VLAN ID: %u\n", a->vid);<br>
+<br>
+       len += snprintf(priv->buf + len, B53_BUF_SIZE - len,<br>
+                       "  Valid%s: %u\n",<br>
+                       (a->write && a->valid_set) ? "(set)" : "",<br>
+                       a->valid);<br>
+<br>
+       len += snprintf(priv->buf + len, B53_BUF_SIZE - len,<br>
+                       "  Age%s: %u\n",<br>
+                       (a->write && a->age_set) ? "(set)" : "",<br>
+                       a->age);<br>
+<br>
+       len += snprintf(priv->buf + len, B53_BUF_SIZE - len,<br>
+                       "  Static%s: %u\n",<br>
+                       (a->write && a->static_set) ? "(set)" : "",<br>
+                       a->static_);<br>
+<br>
+       len += snprintf(priv->buf + len, B53_BUF_SIZE - len,<br>
+                       "  Ports%s: %03x\n",<br>
+                       (a->write && a->ports_set) ? "(set)" : "",<br>
+                       a->ports);<br>
+<br>
+       val->len = len;<br>
+       val->value.s = priv->buf;<br>
+<br>
+       return 0;<br>
+}<br>
+<br>
+static int b53_global_set_arl(struct switch_dev *dev,<br>
+                               const struct switch_attr *attr,<br>
+                               struct switch_val *val)<br>
+{<br>
+       struct b53_device *priv = sw_to_b53(dev);<br>
+       struct b53_arl_ops *ops;<br>
+       const char *s;<br>
+       int op = -1;<br>
+       u8 mac[ETH_ALEN];<br>
+       u16 vid = 0, u16val = 0;<br>
+<br>
+       if (strstr(val->value.s, "cl"))<br>
+               op = 2;<br>
+       else if (strstr(val->value.s, "wr"))<br>
+               op = 1;<br>
+       else if (strstr(val->value.s, "rd"))<br>
+               op = 0;<br>
+<br>
+       if (op == -1)<br>
+               return -EINVAL;<br>
+<br>
+       if (op == 2 && priv->arl_ops) {<br>
+               devm_kfree(priv->dev, priv->arl_ops);<br>
+               return 0;<br>
+       }<br>
+<br>
+       if (!b53_mac_from_string(val->value.s + 2, mac))<br>
+               return -EINVAL;<br>
+<br>
+       if ((s = strstr(val->value.s, "vid")) &&<br>
+          (sscanf(s + 4, "%hu", &vid) < 1 || vid > 0xfff))<br>
+               return -EINVAL;<br>
+<br>
+       priv->arl_ops = devm_kzalloc(priv->dev,<br>
+                                    sizeof(struct b53_arl_ops),<br>
+                                    GFP_KERNEL);<br>
+       if (!priv->arl_ops)<br>
+               return -ENOMEM;<br>
+<br>
+       ops = priv->arl_ops;<br>
+<br>
+       ops->write = op;<br>
+       memcpy(ops->mac, mac, ETH_ALEN);<br>
+       ops->vid = vid;<br>
+       ops->ports_set = 0;<br>
+       if (b53_ports_from_string(val->value.s, NULL, &u16val) > 0) {<br>
+               ops->ports = u16val;<br>
+               ops->ports_set = ops->write;<br>
+       }<br>
+<br>
+       s = strstr(val->value.s, "valid");<br>
+       ops->valid_set = ops->write && !!s;<br>
+       ops->valid = 1;<br>
+       if (ops->valid_set && sscanf(s + 6, "%hu", &u16val) == 1)<br>
+               ops->valid = (u16val & 1);<br>
+<br>
+       s = strstr(val->value.s, "age");;<br>
+       ops->age_set = ops->write && !!s;<br>
+       ops->age = 0;<br>
+       if (ops->age_set && sscanf(s + 4, "%hu", &u16val) == 1)<br>
+               ops->age = (u16val & 1);<br>
+<br>
+       s = strstr(val->value.s, "static");<br>
+       ops->static_set = ops->write && !!s;<br>
+       ops->static_ = 1;<br>
+       if (ops->static_set && sscanf(s + 7, "%hu", &u16val) == 1)<br>
+               ops->static_ = (u16val & 1);<br>
+<br>
+       return 0;<br>
+}<br>
+<br>
 #else<br>
<br>
 #define b53_global_port_capture_cleanup(x)<br>
@@ -1230,6 +1501,13 @@ static struct switch_attr b53_global_ops[] = {<br>
                .set = b53_global_set_port_capture,<br>
                .get = b53_global_get_port_capture,<br>
        },<br>
+       {<br>
+               .type = SWITCH_TYPE_STRING,<br>
+               .name = "arl",<br>
+               .description = "ARL Entry For MAC/VID",<br>
+               .set = b53_global_set_arl,<br>
+               .get = b53_global_get_arl,<br>
+       },<br>
 #endif /* CONFIG_B53_HW_DEBUG_FEATURES */<br>
 };<br>
<br>
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h<br>
index 1edad71..395a76c 100644<br>
--- a/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h<br>
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h<br>
@@ -81,6 +81,25 @@ struct b53_port_capture_filter {<br>
        u8 mac[ETH_ALEN];<br>
        unsigned divider:10;<br>
 };<br>
+<br>
+struct b53_arl_ops {<br>
+       unsigned write:1;<br>
+       u8 mac[ETH_ALEN];<br>
+       unsigned vid:12;<br>
+<br>
+       unsigned valid:1;<br>
+       unsigned valid_set:1;<br>
+<br>
+       unsigned age:1;<br>
+       unsigned age_set:1;<br>
+<br>
+       unsigned static_:1;<br>
+       unsigned static_set:1;<br>
+<br>
+       /* unsigned tc:2; not used yet */<br>
+       unsigned ports:9;<br>
+       unsigned ports_set:1;<br>
+};<br>
 #endif<br>
<br>
 struct b53_device {<br>
@@ -119,6 +138,7 @@ struct b53_device {<br>
                struct b53_port_capture_filter *in_filter;<br>
                struct b53_port_capture_filter *out_filter;<br>
        } port_capture;<br>
+       struct b53_arl_ops *arl_ops;<br>
 #endif<br>
<br>
        struct b53_port *ports;<br>
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h b/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h<br>
index 28361e6..33a763c 100644<br>
--- a/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h<br>
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h<br>
@@ -223,6 +223,34 @@ static inline u64 b53_mac_array_to_u64(const u8 *u8_arr) {<br>
 #define   VTE_UNTAG                    (0x1ff << 9)<br>
<br>
 /*************************************************************************<br>
+ * ARL I/O Page Registers<br>
+ *************************************************************************/<br>
+/* ARL Table Read/Write Register (8 bit) */<br>
+#define B53_ARLTBL_RW_CTRL             0x00<br>
+#define   B53_ARLTBL_RW                BIT(0)<br>
+#define   B53_ARLTBL_DONE              BIT(7)<br>
+<br>
+/* MAC Address Index Register (48 bit) */<br>
+#define B53_MAC_ADDR_IDX               0x02<br>
+<br>
+/* VLAN ID Index Register (16 bit) */<br>
+#define B53_VLAN_ID_IDX                        0x08<br>
+<br>
+/* ARL Table MAC/VID Entry N Registers (64 bit) */<br>
+#define B53_ARLTBL_MAC_VID_ENTRY(n)    (0x10 * n)<br>
+#define   B53_ARLTBL_MACADDR(m)                (0xffffffffffff & m)<br>
+#define   B53_ARLTBL_VID_SET(v)                ((0xfffull & v) << 48)<br>
+#define   B53_ARLTBL_VID_GET(v)                ((v >> 48) & 0xfff)<br>
+<br>
+/* ARL Table Data Entry N Registers (32 bit) */<br>
+#define B53_ARLTBL_DATA_ENTRY(n)       ((0x10 * n) + 0x08)<br>
+#define   B53_ARLTBL_DATA_PORT_ID(p)   (0x1ff & p)<br>
+#define   B53_ARLTBL_TC(tc)            ((3 & tc) << 11)<br>
+#define   B53_ARLTBL_AGE               BIT(14)<br>
+#define   B53_ARLTBL_STATIC            BIT(15)<br>
+#define   B53_ARLTBL_VALID             BIT(16)<br>
+<br>
+/*************************************************************************<br>
  * Port VLAN Registers<br>
  *************************************************************************/<br>
<span class="HOEnZb"><font color="#888888"><br>
--<br>
2.1.2<br>
<br>
</font></span></blockquote></div><br></div>