<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>