[OpenWrt-Devel] [PATCH 3/3][RESEND] b53: implement ARL Table read/write operations

Alexandru Ardelean ardeleanalex at gmail.com
Mon Feb 23 10:55:45 EST 2015


From: Alexandru Ardelean <ardeleanalex at gmail.com>

Read/Write operations for the ARL table.
To use it:
   swconfig dev switch0 set arl "rd XX:XX:XX:XX:XX:XX vid NNNN"
   swconfig dev switch0 get arl

Output should be:
  ARL Operation: Read
    MAC: XX:XX:XX:XX:XX:XX
    VLAN ID: NNNN
    Valid: 1
    Age: 1
    Static: 0
    Port(s): 001

Reading/Writing the ARL table is a bit complex of an operation,
so string parsing is used.
The idea is that this uses swconfig 'set' prepare a r/w operation
and swconfig 'get' to execute an operation.
Not the most elegant approach, but it works fairly well for a
debugging operation using b53 hardware.

There are 3 op codes: rd, wr and cl. 'cl' clears any previous rd/wr.
This parsing is stupid simple, so any spaces, cases and quotes matter.
If a operation failed, the output will be 'failed' and the kernel
log can be consulted. The kernel log can also be consulted for
various messages that may have been printed during a r/w operation.

For 'rd' and 'wr' ops
 - if VLAN not enabled, only the MAC address is required
 - if VLAN is enabled, both MAC and VLAN ID are required

Commands:
 - swconfig dev switch0 set arl cl - clear any prev op; no 'get' call required
 - swconfig dev switch0 set arl "rd <MAC> <VID>" - the call 'get',
     if output is 'failed' check kernel log
 - swconfig dev switch0 set arl "rd <MAC> <VID> \
   [static 0/1] [age 0/1] [valid 0/1] [ports NNN]"

The write operation takes parameters, static, age and valid, which
are bits that can be set/modified in the ARL table.

The ports must field is dependant on the type of MAC.
 - for unicat MACs, ports is a port number
 - for multicast MACs, ports is a bitmask for ports on which to forward
This is the same meaning when reading MAC/VID entries.

Signed-off-by: Alexandru Ardelean <ardeleanalex at gmail.com>
---
 .../generic/files/drivers/net/phy/b53/b53_common.c | 278 +++++++++++++++++++++
 .../generic/files/drivers/net/phy/b53/b53_priv.h   |  20 ++
 .../generic/files/drivers/net/phy/b53/b53_regs.h   |  28 +++
 3 files changed, 326 insertions(+)

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
index 4abc8c3..fd1e78a 100644
--- a/target/linux/generic/files/drivers/net/phy/b53/b53_common.c
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c
@@ -464,6 +464,155 @@ out:
 	b53_write16(dev, B53_MGMT_PAGE, B53_MIRROR_CTRL, port_mirror_ctrl);
 }
 
+static int b53_wait_arl_table_rw_done(struct b53_device *dev)
+{
+	u8 i;
+
+	for (i = 0; i < 10; i++) {
+		u8 arl_rw_ctrl;
+		b53_read8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, &arl_rw_ctrl);
+
+		if (!(arl_rw_ctrl & B53_ARLTBL_DONE))
+			return 0;
+
+		mdelay(1);
+	}
+	return -EINVAL;
+}
+
+static int b53_read_arl_table_entry(struct b53_device *dev,
+			u64 mac_match, u16 vid_match, u32 *arl_entry, u8 *idx)
+{
+	u8 i;
+
+	if (b53_wait_arl_table_rw_done(dev))
+		return -EINVAL;
+
+	for (i = 0; i <= 3; i++) {
+		u64 mv;
+		u32 en;
+		u8 *m = (u8 *)&mv;
+
+		b53_read64(dev, B53_ARLIO_PAGE, B53_ARLTBL_MAC_VID_ENTRY(i), &mv);
+		b53_read32(dev, B53_ARLIO_PAGE, B53_ARLTBL_DATA_ENTRY(i), &en);
+		pr_info("ARL Entry(%u) %08x MAC/VID: %02x:%02x %02x:%02x:%02x:%02x:%02x:%02x\n",
+		        i, en, m[0],m[1],m[2],m[3],m[4],m[5],m[6],m[7]);
+		if (!(en & B53_ARLTBL_VALID))
+			continue;
+		if (B53_ARLTBL_MACADDR(mv) != mac_match)
+			continue;
+		if (dev->enable_vlan && (B53_ARLTBL_VID_GET(mv) != vid_match))
+			continue;
+		*idx = i;
+		*arl_entry = en;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static inline void b53_arl_entry_to_arl_ops(struct b53_arl_ops *ops, u32 arl_entry)
+{
+	ops->age_set = 0;
+	ops->valid_set = 0;
+	ops->static_set = 0;
+	ops->ports_set = 0;
+	ops->valid = !!(arl_entry & B53_ARLTBL_VALID);
+	ops->static_ = !!(arl_entry & B53_ARLTBL_STATIC);
+	ops->age = !!(arl_entry & B53_ARLTBL_AGE);
+	ops->ports = (arl_entry & 0x1ff);
+}
+
+static inline void b53_arl_ops_to_arl_entry(struct b53_arl_ops *ops, u32 *arl_entry)
+{
+	if (ops->valid_set)
+		*arl_entry = (ops->valid) ? (*arl_entry | B53_ARLTBL_VALID) :
+		                            (*arl_entry & ~B53_ARLTBL_VALID);
+	if (ops->static_set)
+		*arl_entry = (ops->static_) ? (*arl_entry | B53_ARLTBL_STATIC) :
+		                              (*arl_entry & ~B53_ARLTBL_STATIC);
+	if (ops->age_set)
+		*arl_entry = (ops->age) ? (*arl_entry | B53_ARLTBL_AGE) :
+		                          (*arl_entry & ~B53_ARLTBL_AGE);
+	if (ops->ports_set) {
+		*arl_entry &= ~0x1fe;
+		*arl_entry |= (ops->ports & 0x1ff);
+	}
+}
+
+static int b53_run_arl_ops(struct b53_device *dev)
+{
+	struct b53_arl_ops lops, *ops = dev->arl_ops;
+	u8 arl_rw_ctrl;
+	u64 u64_mac = b53_mac_array_to_u64(ops->mac);
+	u64 u64_mac_vid;
+	u32 arl_entry = 0;
+	u8 idx = 0, *m;
+	int rc;
+
+	/* A read always comes first */
+	b53_write48(dev, B53_ARLIO_PAGE, B53_MAC_ADDR_IDX, u64_mac);
+	b53_write16(dev, B53_ARLIO_PAGE, B53_VLAN_ID_IDX, ops->vid);
+
+	b53_read8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, &arl_rw_ctrl);
+	arl_rw_ctrl |= (B53_ARLTBL_DONE | B53_ARLTBL_RW);
+	b53_write8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, arl_rw_ctrl);
+
+	rc = b53_read_arl_table_entry(dev, u64_mac, ops->vid, &arl_entry, &idx);
+
+	if (!ops->write) {
+		if (!rc)
+			b53_arl_entry_to_arl_ops(ops, arl_entry);
+		return rc;
+	}
+	/* If this entry is new, reset ARL register & index */
+	if (rc) {
+		arl_entry = 0;
+		idx = 1;
+	}
+
+	/* Now we're writing */
+	b53_arl_ops_to_arl_entry(ops, &arl_entry);
+
+	b53_read8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, &arl_rw_ctrl);
+
+	u64_mac_vid = u64_mac | B53_ARLTBL_VID_SET(ops->vid);
+	m = (u8 *)&u64_mac_vid;
+	b53_write64(dev, B53_ARLIO_PAGE, B53_ARLTBL_MAC_VID_ENTRY(idx), u64_mac_vid);
+	b53_write32(dev, B53_ARLIO_PAGE, B53_ARLTBL_DATA_ENTRY(idx), arl_entry);
+
+	arl_rw_ctrl &= ~B53_ARLTBL_RW;
+	arl_rw_ctrl |= B53_ARLTBL_DONE;
+	b53_write8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, arl_rw_ctrl);
+	pr_info("Writing: ARL Entry(%u) %08x MAC/VID: %02x%02x %02x:%02x:%02x:%02x:%02x:%02x\n",
+                idx, arl_entry, m[0],m[1],m[2],m[3],m[4],m[5],m[6],m[7]);
+
+	if (b53_wait_arl_table_rw_done(dev))
+		return -EINVAL;
+
+	/* Writing done; read result */
+	b53_read8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, &arl_rw_ctrl);
+	arl_rw_ctrl |= (B53_ARLTBL_RW | B53_ARLTBL_DONE);
+	b53_write8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, arl_rw_ctrl);
+
+	u64_mac &= ~B53_ARLTBL_VID_SET(0);
+	if (b53_read_arl_table_entry(dev, u64_mac, ops->vid, &arl_entry, &idx))
+		return -EINVAL;
+
+	b53_arl_entry_to_arl_ops(&lops, arl_entry);
+
+	if (ops->valid_set && (lops.valid != ops->valid))
+		return -EINVAL;
+	if (ops->static_set && (lops.static_ != ops->static_))
+		return -EINVAL;
+	if (ops->age_set && (lops.age != ops->age))
+		return -EINVAL;
+	if (ops->ports_set && (lops.ports != ops->ports))
+		return -EINVAL;
+
+	return 0;
+}
+
 #else
 #define b53_enable_port_capture(x)
 #endif /* CONFIG_B53_HW_DEBUG_FEATURES */
@@ -887,6 +1036,128 @@ out:
 	return rc;
 }
 
+static int b53_global_get_arl(struct switch_dev *dev,
+				const struct switch_attr *attr,
+				struct switch_val *val)
+{
+	struct b53_device *priv = sw_to_b53(dev);
+	struct b53_arl_ops *a = priv->arl_ops;
+	int len = 0;
+
+	/* Run ARL Table R/W */
+	if (!a || b53_run_arl_ops(priv)) {
+		snprintf(priv->buf, B53_BUF_SIZE,
+		         "No read/write ARL operation set before get call\n");
+		return -EINVAL;
+	}
+
+	len += snprintf(priv->buf + len, B53_BUF_SIZE - len,
+	                "ARL Operation: %s\n", a->write ? "Write" :"Read");
+	len += snprintf(priv->buf + len, B53_BUF_SIZE - len,
+	                "  MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+	                a->mac[0],a->mac[1],a->mac[2],
+	                a->mac[3],a->mac[4],a->mac[5]);
+
+	len += snprintf(priv->buf + len, B53_BUF_SIZE - len,
+	                "  VLAN ID: %u\n", a->vid);
+
+	len += snprintf(priv->buf + len, B53_BUF_SIZE - len,
+	                "  Valid%s: %u\n",
+	                (a->write && a->valid_set) ? "(set)" : "",
+	                a->valid);
+
+	len += snprintf(priv->buf + len, B53_BUF_SIZE - len,
+	                "  Age%s: %u\n",
+	                (a->write && a->age_set) ? "(set)" : "",
+	                a->age);
+
+	len += snprintf(priv->buf + len, B53_BUF_SIZE - len,
+	                "  Static%s: %u\n",
+	                (a->write && a->static_set) ? "(set)" : "",
+	                a->static_);
+
+	len += snprintf(priv->buf + len, B53_BUF_SIZE - len,
+	                "  Ports%s: %03x\n",
+	                (a->write && a->ports_set) ? "(set)" : "",
+	                a->ports);
+
+	val->len = len;
+	val->value.s = priv->buf;
+
+	return 0;
+}
+
+static int b53_global_set_arl(struct switch_dev *dev,
+				const struct switch_attr *attr,
+				struct switch_val *val)
+{
+	struct b53_device *priv = sw_to_b53(dev);
+	struct b53_arl_ops *ops;
+	const char *s;
+	int op = -1;
+	u8 mac[ETH_ALEN];
+	u16 vid = 0, u16val = 0;
+
+	if (strstr(val->value.s, "cl"))
+		op = 2;
+	else if (strstr(val->value.s, "wr"))
+		op = 1;
+	else if (strstr(val->value.s, "rd"))
+		op = 0;
+
+	if (op == -1)
+		return -EINVAL;
+
+	if (op == 2 && priv->arl_ops) {
+		devm_kfree(priv->dev, priv->arl_ops);
+		return 0;
+	}
+
+	if (!b53_mac_from_string(val->value.s + 2, mac))
+		return -EINVAL;
+
+	if ((s = strstr(val->value.s, "vid")) &&
+	   (sscanf(s + 4, "%hu", &vid) < 1 || vid > 0xfff))
+		return -EINVAL;
+
+	priv->arl_ops = devm_kzalloc(priv->dev,
+	                             sizeof(struct b53_arl_ops),
+	                             GFP_KERNEL);
+	if (!priv->arl_ops)
+		return -ENOMEM;
+
+	ops = priv->arl_ops;
+
+	ops->write = op;
+	memcpy(ops->mac, mac, ETH_ALEN);
+	ops->vid = vid;
+	ops->ports_set = 0;
+	if (b53_ports_from_string(val->value.s, NULL, &u16val) > 0) {
+		ops->ports = u16val;
+		ops->ports_set = ops->write;
+	}
+
+	s = strstr(val->value.s, "valid");
+	ops->valid_set = ops->write && !!s;
+	ops->valid = 1;
+	if (ops->valid_set && sscanf(s + 6, "%hu", &u16val) == 1)
+		ops->valid = (u16val & 1);
+
+	s = strstr(val->value.s, "age");;
+	ops->age_set = ops->write && !!s;
+	ops->age = 0;
+	if (ops->age_set && sscanf(s + 4, "%hu", &u16val) == 1)
+		ops->age = (u16val & 1);
+
+	s = strstr(val->value.s, "static");
+	ops->static_set = ops->write && !!s;
+	ops->static_ = 1;
+	if (ops->static_set && sscanf(s + 7, "%hu", &u16val) == 1)
+		ops->static_ = (u16val & 1);
+
+	return 0;
+}
+
 #else
 
 #define b53_global_port_capture_cleanup(x)
@@ -1232,6 +1503,13 @@ static struct switch_attr b53_global_ops[] = {
 		.set = b53_global_set_port_capture,
 		.get = b53_global_get_port_capture,
 	},
+	{
+		.type = SWITCH_TYPE_STRING,
+		.name = "arl",
+		.description = "ARL Entry For MAC/VID",
+		.set = b53_global_set_arl,
+		.get = b53_global_get_arl,
+	},
 #endif /* CONFIG_B53_HW_DEBUG_FEATURES */
 };
 
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
index 1edad71..395a76c 100644
--- a/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h
@@ -81,6 +81,25 @@ struct b53_port_capture_filter {
 	u8 mac[ETH_ALEN];
 	unsigned divider:10;
 };
+
+struct b53_arl_ops {
+	unsigned write:1;
+	u8 mac[ETH_ALEN];
+	unsigned vid:12;
+
+	unsigned valid:1;
+	unsigned valid_set:1;
+
+	unsigned age:1;
+	unsigned age_set:1;
+
+	unsigned static_:1;
+	unsigned static_set:1;
+
+	/* unsigned tc:2; not used yet */
+	unsigned ports:9;
+	unsigned ports_set:1;
+};
 #endif
 
 struct b53_device {
@@ -119,6 +138,7 @@ struct b53_device {
 		struct b53_port_capture_filter *in_filter;
 		struct b53_port_capture_filter *out_filter;
 	} port_capture;
+	struct b53_arl_ops *arl_ops;
 #endif
 
 	struct b53_port *ports;
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
index 28361e6..33a763c 100644
--- a/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h
@@ -223,6 +223,34 @@ static inline u64 b53_mac_array_to_u64(const u8 *u8_arr) {
 #define   VTE_UNTAG			(0x1ff << 9)
 
 /*************************************************************************
+ * ARL I/O Page Registers
+ *************************************************************************/
+/* ARL Table Read/Write Register (8 bit) */
+#define B53_ARLTBL_RW_CTRL		0x00
+#define   B53_ARLTBL_RW		BIT(0)
+#define   B53_ARLTBL_DONE		BIT(7)
+
+/* MAC Address Index Register (48 bit) */
+#define B53_MAC_ADDR_IDX		0x02
+
+/* VLAN ID Index Register (16 bit) */
+#define B53_VLAN_ID_IDX			0x08
+
+/* ARL Table MAC/VID Entry N Registers (64 bit) */
+#define B53_ARLTBL_MAC_VID_ENTRY(n)	(0x10 * n)
+#define   B53_ARLTBL_MACADDR(m)		(0xffffffffffff & m)
+#define   B53_ARLTBL_VID_SET(v)		((0xfffull & v) << 48)
+#define   B53_ARLTBL_VID_GET(v)		((v >> 48) & 0xfff)
+
+/* ARL Table Data Entry N Registers (32 bit) */
+#define B53_ARLTBL_DATA_ENTRY(n)	((0x10 * n) + 0x08)
+#define   B53_ARLTBL_DATA_PORT_ID(p)	(0x1ff & p)
+#define   B53_ARLTBL_TC(tc)		((3 & tc) << 11)
+#define   B53_ARLTBL_AGE		BIT(14)
+#define   B53_ARLTBL_STATIC		BIT(15)
+#define   B53_ARLTBL_VALID		BIT(16)
+
+/*************************************************************************
  * Port VLAN Registers
  *************************************************************************/
 
-- 
2.1.2
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel



More information about the openwrt-devel mailing list