Message ID | 1424702463-10012-3-git-send-email-aa@ocedo.com |
---|---|
State | Rejected |
Headers | show |
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. On Mon, Feb 23, 2015 at 4:41 PM, Alexandru Ardelean <ardeleanalex@gmail.com> wrote: > From: Alexandru Ardelean <ardeleanalex@gmail.com> > > This has been implemented on a BCM53128 chip. > May very likely work on BCM53125 which is a 4 port variant. > No idea on what other chips it works, but since this is hidden > behind a config flag, others are free to try it out themselves. > > 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(+) > > 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 bdd4006..ab7604a 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 */ > @@ -885,6 +1034,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) > @@ -1230,6 +1501,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 > >
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 bdd4006..ab7604a 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 */ @@ -885,6 +1034,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) @@ -1230,6 +1501,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 *************************************************************************/