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

Alexandru Ardelean ardeleanalex at gmail.com
Mon Feb 23 10:25:38 EST 2015


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 at gmail.com>
wrote:

> From: Alexandru Ardelean <ardeleanalex at 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 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 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
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.infradead.org/pipermail/openwrt-devel/attachments/20150223/ca0d0696/attachment.htm>
-------------- next part --------------
_______________________________________________
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