diff mbox

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

Message ID 1424706945-9977-1-git-send-email-aa@ocedo.com
State Changes Requested
Delegated to: Jonas Gorski
Headers show

Commit Message

Alexandru Ardelean Feb. 23, 2015, 3:55 p.m. UTC
From: Alexandru Ardelean <ardeleanalex@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@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(+)

Comments

Jonas Gorski March 10, 2015, 12:13 p.m. UTC | #1
On Mon, Feb 23, 2015 at 4:55 PM, Alexandru Ardelean
<ardeleanalex@gmail.com> wrote:
> From: Alexandru Ardelean <ardeleanalex@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.

There seem to be four ARL register table layouts:

(Very) old FE switch chips (5325, 5365):

2 ARL table entries at 0x10 and 0x18 format (64 bit wide);

[63] valid, [62]: static, [61] age, [60:59]: priority, [60:48]: port
id, [47:0] mac.

where port id is either a numeric id in case of single cast, and a
bitmask for multicast addresses. Note there is no VID field.

BCM63XX:

2 ARL table entries at 0x10 and 0x20 (64 + 16 bit wide), format

0x?0: (64 bit)
[60:48]: vid [47:0] mac.
0x?8 (16 bit):
[15]: valid [14]: static, [13]  age [12:10]: priority. [8:0] port id /
forward map.

where port id is either a numeric id in case of single cast, and a
bitmask for multicast addresses. Note there is no VID field.

BCM539x:

4 ARL table entries at 0x10/0x20/0x30/0x40, format:
0x?0: (64 bit)
[60:48]: vid [47:0] mac.
0x?8 (32 bit):
[16]: valid [15]: static, [14]  age [12:10]: priority. [8:0] port id /
forward map.

where port id is either a numeric id in case of single cast, and a
bitmask for multicast addresses. Note there is no VID field.

BCM531xx:

4 ARL table entries at 0x10/0x20/0x30/0x40, format:
0x?0: (64 bit)
[60:48]: vid [47:0] mac.
0x?8 (32 bit):
[16]: valid [15]: static, [14]  age [13:11]: priority. [8:0] port id /
forward map.

At least for BCM53101 the portid /forward map is always a bitmap, but
I'm not sure about BCM53115/53118/53125/53125.


Generally it would be nice to also have the option to just search for
a MAC to find out the ports/vid(s) with the ARL sarch register. But
more to this in a second email.


Jonas
Jonas Gorski March 10, 2015, 12:22 p.m. UTC | #2
On Mon, Feb 23, 2015 at 4:55 PM, Alexandru Ardelean
<ardeleanalex@gmail.com> wrote:
> From: Alexandru Ardelean <ardeleanalex@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.

ARL table access is something very common in switch chips, I think it
would make more sense to come up with an API for swconfig for the
switches to implement that instead of cooking up our own language for
each switch driver.

So generally I am thinking of something like

swconfig dev switch0 arl find <mac> [vid <vid]
swconfig dev switch0 arl delete <mac> [vid <vid>]
swconfig dev switch0 arl add <mac> [vid <vid>] [ports <ports>] [static <0|1>]
swconfig dev switch0 arl add_port <mac> [vid <vid>] port <port> <-
this might be useful for igmpproxy & co, to update multicast
forwarding tables in the switch itself.
swconfig dev switch0 arl del_port <mac> [vid <vid>] port <port>


Jonas
Alexandru Ardelean March 10, 2015, 1:20 p.m. UTC | #3
On Tue, Mar 10, 2015 at 2:22 PM, Jonas Gorski <jogo@openwrt.org> wrote:

> On Mon, Feb 23, 2015 at 4:55 PM, Alexandru Ardelean
> <ardeleanalex@gmail.com> wrote:
> > From: Alexandru Ardelean <ardeleanalex@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.
>
> ARL table access is something very common in switch chips, I think it
> would make more sense to come up with an API for swconfig for the
> switches to implement that instead of cooking up our own language for
> each switch driver.
>
> So generally I am thinking of something like
>
> swconfig dev switch0 arl find <mac> [vid <vid]
> swconfig dev switch0 arl delete <mac> [vid <vid>]
> swconfig dev switch0 arl add <mac> [vid <vid>] [ports <ports>] [static
> <0|1>]
> swconfig dev switch0 arl add_port <mac> [vid <vid>] port <port> <-
> this might be useful for igmpproxy & co, to update multicast
> forwarding tables in the switch itself.
> swconfig dev switch0 arl del_port <mac> [vid <vid>] port <port>
>
>
> Jonas
>

That's actually a pretty good idea.
Will consider it when re-submitting.
diff mbox

Patch

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