On Oct. Friday 23 (43) 11:38 AM, Florian Fainelli wrote:
> Add support for the FDB add, delete, and dump operations. The add and
> delete operations are implemented using directed ARL operations using
> the specified MAC address and consist in a read operation, write and
> readback operation.
> 
> The dump operation consists in using the ARL search and software
> filtering entries which are not for the desired port.
> 
> Signed-off-by: Florian Fainelli <f.faine...@gmail.com>
> ---
>  drivers/net/dsa/bcm_sf2.c      | 236 
> +++++++++++++++++++++++++++++++++++++++++
>  drivers/net/dsa/bcm_sf2.h      |  56 ++++++++++
>  drivers/net/dsa/bcm_sf2_regs.h |  43 ++++++++
>  3 files changed, 335 insertions(+)
> 
> diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
> index 9d56515f4c4d..4f32b8a530bf 100644
> --- a/drivers/net/dsa/bcm_sf2.c
> +++ b/drivers/net/dsa/bcm_sf2.c
> @@ -25,6 +25,8 @@
>  #include <linux/ethtool.h>
>  #include <linux/if_bridge.h>
>  #include <linux/brcmphy.h>
> +#include <linux/etherdevice.h>
> +#include <net/switchdev.h>
>  
>  #include "bcm_sf2.h"
>  #include "bcm_sf2_regs.h"
> @@ -555,6 +557,236 @@ static int bcm_sf2_sw_br_set_stp_state(struct 
> dsa_switch *ds, int port,
>       return 0;
>  }
>  
> +/* Address Resolution Logic routines */
> +static int bcm_sf2_arl_op_wait(struct bcm_sf2_priv *priv)
> +{
> +     unsigned int timeout = 10;
> +     u32 reg;
> +
> +     do {
> +             reg = core_readl(priv, CORE_ARLA_RWCTL);
> +             if (!(reg & ARL_STRTDN))
> +                     return 0;
> +
> +             usleep_range(1000, 2000);
> +     } while (timeout--);
> +
> +     return -ETIMEDOUT;
> +}
> +
> +static int bcm_sf2_arl_rw_op(struct bcm_sf2_priv *priv, unsigned int op)
> +{
> +     u32 cmd;
> +
> +     if (op > ARL_RW)
> +             return -EINVAL;
> +
> +     cmd = core_readl(priv, CORE_ARLA_RWCTL);
> +     cmd &= ~IVL_SVL_SELECT;
> +     cmd |= ARL_STRTDN;
> +     if (op)
> +             cmd |= ARL_RW;
> +     else
> +             cmd &= ~ARL_RW;
> +     core_writel(priv, cmd, CORE_ARLA_RWCTL);
> +
> +     return bcm_sf2_arl_op_wait(priv);
> +}
> +
> +static int bcm_sf2_arl_read(struct bcm_sf2_priv *priv, u64 mac,
> +                         u16 vid, struct bcm_sf2_arl_entry *ent, u8 *idx,
> +                         bool is_valid)
> +{
> +     unsigned int i;
> +     int ret;
> +
> +     ret = bcm_sf2_arl_op_wait(priv);
> +     if (ret)
> +             return ret;
> +
> +     /* Read the 4 bins */
> +     for (i = 0; i < 4; i++) {
> +             u64 mac_vid;
> +             u32 fwd_entry;
> +
> +             mac_vid = core_readq(priv, CORE_ARLA_MACVID_ENTRY(i));
> +             fwd_entry = core_readl(priv, CORE_ARLA_FWD_ENTRY(i));
> +             bcm_sf2_arl_to_entry(ent, mac_vid, fwd_entry);
> +
> +             if (ent->is_valid && is_valid) {
> +                     *idx = i;
> +                     return 0;
> +             }
> +
> +             /* This is the MAC we just deleted */
> +             if (!is_valid && (mac_vid & mac))
> +                     return 0;
> +     }
> +
> +     return -ENOENT;
> +}

What is the purpose of the "vid" parameter in bcm_sf2_arl_read?

> +
> +static int bcm_sf2_arl_op(struct bcm_sf2_priv *priv, int op, int port,
> +                       const unsigned char *addr, u16 vid, bool is_valid)
> +{
> +     struct bcm_sf2_arl_entry ent;
> +     u32 fwd_entry;
> +     u64 mac, mac_vid = 0;
> +     u8 idx = 0;
> +     int ret;
> +
> +     /* Convert the array into a 64-bit MAC */
> +     mac = bcm_sf2_mac_to_u64(addr);
> +
> +     /* Perform a read for the given MAC and VID */
> +     core_writeq(priv, mac, CORE_ARLA_MAC);
> +     core_writel(priv, vid, CORE_ARLA_VID);
> +
> +     /* Issue a read operation for this MAC */
> +     ret = bcm_sf2_arl_rw_op(priv, 1);
> +     if (ret)
> +             return ret;
> +
> +     ret = bcm_sf2_arl_read(priv, mac, vid, &ent, &idx, is_valid);
> +     /* If this is a read, just finish now */
> +     if (op)
> +             return ret;
> +
> +     /* We could not find a matching MAC, so reset to a new entry */
> +     if (ret) {
> +             fwd_entry = 0;
> +             idx = 0;
> +     }
> +
> +     memset(&ent, 0, sizeof(ent));
> +     ent.port = port;
> +     ent.is_valid = is_valid;
> +     ent.vid = vid;
> +     ent.is_static = true;
> +     memcpy(ent.mac, addr, ETH_ALEN);
> +     bcm_sf2_arl_from_entry(&mac_vid, &fwd_entry, &ent);
> +
> +     core_writeq(priv, mac_vid, CORE_ARLA_MACVID_ENTRY(idx));
> +     core_writel(priv, fwd_entry, CORE_ARLA_FWD_ENTRY(idx));
> +
> +     ret = bcm_sf2_arl_rw_op(priv, 0);
> +     if (ret)
> +             return ret;
> +
> +     /* Re-read the entry to check */
> +     return bcm_sf2_arl_read(priv, mac, vid, &ent, &idx, is_valid);
> +}
> +
> +static int bcm_sf2_sw_fdb_prepare(struct dsa_switch *ds, int port,
> +                               const struct switchdev_obj_port_fdb *fdb,
> +                               struct switchdev_trans *trans)
> +{
> +     /* We do not need to do anything specific here yet */
> +     return 0;
> +}
> +
> +static int bcm_sf2_sw_fdb_add(struct dsa_switch *ds, int port,
> +                           const struct switchdev_obj_port_fdb *fdb,
> +                           struct switchdev_trans *trans)
> +{
> +     struct bcm_sf2_priv *priv = ds_to_priv(ds);
> +
> +     return bcm_sf2_arl_op(priv, 0, port, fdb->addr, fdb->vid, true);
> +}
> +
> +static int bcm_sf2_sw_fdb_del(struct dsa_switch *ds, int port,
> +                           const struct switchdev_obj_port_fdb *fdb)
> +{
> +     struct bcm_sf2_priv *priv = ds_to_priv(ds);
> +
> +     return bcm_sf2_arl_op(priv, 0, port, fdb->addr, fdb->vid, false);
> +}

I'm wondering if you are populating the FDB of the invalid VLAN 0 here.

Does your ARL consider that fdb->vid == 0 means "this port's FDB" and
not "FDB of VLAN 0"?

> +
> +static int bcm_sf2_arl_search_wait(struct bcm_sf2_priv *priv)
> +{
> +     unsigned timeout = 1000;
> +     u32 reg;
> +
> +     do {
> +             reg = core_readl(priv, CORE_ARLA_SRCH_CTL);
> +             if (!(reg & ARLA_SRCH_STDN))
> +                     return 0;
> +
> +             if (reg & ARLA_SRCH_VLID)
> +                     return 0;
> +
> +             usleep_range(1000, 2000);
> +     } while (timeout--);
> +
> +     return -ETIMEDOUT;
> +}
> +
> +static void bcm_sf2_arl_search_rd(struct bcm_sf2_priv *priv, u8 idx,
> +                               struct bcm_sf2_arl_entry *ent)
> +{
> +     u64 mac_vid;
> +     u32 fwd_entry;
> +
> +     mac_vid = core_readq(priv, CORE_ARLA_SRCH_RSLT_MACVID(idx));
> +     fwd_entry = core_readl(priv, CORE_ARLA_SRCH_RSLT(idx));
> +     bcm_sf2_arl_to_entry(ent, mac_vid, fwd_entry);
> +}
> +
> +static int bcm_sf2_sw_fdb_copy(struct net_device *dev, int port,
> +                            const struct bcm_sf2_arl_entry *ent,
> +                            struct switchdev_obj_port_fdb *fdb,
> +                            int (*cb)(struct switchdev_obj *obj))
> +{
> +     if (!ent->is_valid)
> +             return 0;
> +
> +     if (port != ent->port)
> +             return 0;
> +
> +     ether_addr_copy(fdb->addr, ent->mac);
> +     fdb->vid = ent->vid;
> +     fdb->ndm_state = ent->is_static ? NUD_NOARP : NUD_REACHABLE;
> +
> +     return cb(&fdb->obj);
> +}
> +
> +static int bcm_sf2_sw_fdb_dump(struct dsa_switch *ds, int port,
> +                            struct switchdev_obj_port_fdb *fdb,
> +                            int (*cb)(struct switchdev_obj *obj))
> +{
> +     struct bcm_sf2_priv *priv = ds_to_priv(ds);
> +     struct net_device *dev = ds->ports[port];
> +     struct bcm_sf2_arl_entry results[2];
> +     unsigned int count = 0;
> +     int ret;
> +
> +     /* Start search operation */
> +     core_writel(priv, ARLA_SRCH_STDN, CORE_ARLA_SRCH_CTL);
> +
> +     do {
> +             ret = bcm_sf2_arl_search_wait(priv);
> +             if (ret)
> +                     return ret;
> +
> +             /* Read both entries, then return their values back */
> +             bcm_sf2_arl_search_rd(priv, 0, &results[0]);
> +             ret = bcm_sf2_sw_fdb_copy(dev, port, &results[0], fdb, cb);
> +             if (ret)
> +                     return ret;
> +
> +             bcm_sf2_arl_search_rd(priv, 1, &results[1]);
> +             ret = bcm_sf2_sw_fdb_copy(dev, port, &results[1], fdb, cb);
> +             if (ret)
> +                     return ret;
> +
> +             if (!results[0].is_valid && !results[1].is_valid)
> +                     break;
> +
> +     } while (count++ < CORE_ARLA_NUM_ENTRIES);
> +
> +     return 0;
> +}
> +
>  static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id)
>  {
>       struct bcm_sf2_priv *priv = dev_id;
> @@ -1076,6 +1308,10 @@ static struct dsa_switch_driver bcm_sf2_switch_driver 
> = {
>       .port_join_bridge       = bcm_sf2_sw_br_join,
>       .port_leave_bridge      = bcm_sf2_sw_br_leave,
>       .port_stp_update        = bcm_sf2_sw_br_set_stp_state,
> +     .port_fdb_prepare       = bcm_sf2_sw_fdb_prepare,
> +     .port_fdb_add           = bcm_sf2_sw_fdb_add,
> +     .port_fdb_del           = bcm_sf2_sw_fdb_del,
> +     .port_fdb_dump          = bcm_sf2_sw_fdb_dump,
>  };
>  
>  static int __init bcm_sf2_init(void)
> diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h
> index 789d7b7737da..cc98abc0aaf3 100644
> --- a/drivers/net/dsa/bcm_sf2.h
> +++ b/drivers/net/dsa/bcm_sf2.h
> @@ -19,6 +19,8 @@
>  #include <linux/mutex.h>
>  #include <linux/mii.h>
>  #include <linux/ethtool.h>
> +#include <linux/types.h>
> +#include <linux/bitops.h>
>  
>  #include <net/dsa.h>
>  
> @@ -50,6 +52,60 @@ struct bcm_sf2_port_status {
>       u32 vlan_ctl_mask;
>  };
>  
> +struct bcm_sf2_arl_entry {
> +     u8 port;
> +     u8 mac[ETH_ALEN];
> +     u16 vid;
> +     u8 is_valid:1;
> +     u8 is_age:1;
> +     u8 is_static:1;
> +};
> +
> +static inline void bcm_sf2_mac_from_u64(u64 src, u8 *dst)
> +{
> +     unsigned int i;
> +
> +     for (i = 0; i < ETH_ALEN; i++)
> +             dst[ETH_ALEN - 1 - i] = (src >> (8 * i)) & 0xff;
> +}
> +
> +static inline u64 bcm_sf2_mac_to_u64(const u8 *src)
> +{
> +     unsigned int i;
> +     u64 dst = 0;
> +
> +     for (i = 0; i < ETH_ALEN; i++)
> +             dst |= (u64)src[ETH_ALEN - 1 - i] << (8 * i);
> +
> +     return dst;
> +}
> +
> +static inline void bcm_sf2_arl_to_entry(struct bcm_sf2_arl_entry *ent,
> +                                     u64 mac_vid, u32 fwd_entry)
> +{
> +     memset(ent, 0, sizeof(*ent));
> +     ent->port = fwd_entry & PORTID_MASK;
> +     ent->is_valid = !!(fwd_entry & ARL_VALID);
> +     ent->is_age = !!(fwd_entry & ARL_AGE);
> +     ent->is_static = !!(fwd_entry & ARL_STATIC);
> +     bcm_sf2_mac_from_u64(mac_vid, ent->mac);
> +     ent->vid = mac_vid >> VID_SHIFT;
> +}
> +
> +static inline void bcm_sf2_arl_from_entry(u64 *mac_vid, u32 *fwd_entry,
> +                                       const struct bcm_sf2_arl_entry *ent)
> +{
> +     *mac_vid = bcm_sf2_mac_to_u64(ent->mac);
> +     *mac_vid |= (u64)(ent->vid & VID_MASK) << VID_SHIFT;
> +     *fwd_entry = ent->port & PORTID_MASK;
> +     if (ent->is_valid)
> +             *fwd_entry |= ARL_VALID;
> +     if (ent->is_static)
> +             *fwd_entry |= ARL_STATIC;
> +     if (ent->is_age)
> +             *fwd_entry |= ARL_AGE;
> +}
> +
>  struct bcm_sf2_priv {
>       /* Base registers, keep those in order with BCM_SF2_REGS_NAME */
>       void __iomem                    *core;
> diff --git a/drivers/net/dsa/bcm_sf2_regs.h b/drivers/net/dsa/bcm_sf2_regs.h
> index fa4e6e78c9ea..97780d43b5c0 100644
> --- a/drivers/net/dsa/bcm_sf2_regs.h
> +++ b/drivers/net/dsa/bcm_sf2_regs.h
> @@ -231,6 +231,49 @@
>  #define CORE_BRCM_HDR_RX_DIS         0x0980
>  #define CORE_BRCM_HDR_TX_DIS         0x0988
>  
> +#define CORE_ARLA_NUM_ENTRIES                1024
> +
> +#define CORE_ARLA_RWCTL                      0x1400
> +#define  ARL_RW                              (1 << 0)
> +#define  IVL_SVL_SELECT                      (1 << 6)
> +#define  ARL_STRTDN                  (1 << 7)
> +
> +#define CORE_ARLA_MAC                        0x1408
> +#define CORE_ARLA_VID                        0x1420
> +#define  ARLA_VIDTAB_INDX_MASK               0x1fff
> +
> +#define CORE_ARLA_MACVID0            0x1440
> +#define  MAC_MASK                    0xffffffffff
> +#define  VID_SHIFT                   48
> +#define  VID_MASK                    0xfff
> +
> +#define CORE_ARLA_FWD_ENTRY0         0x1460
> +#define  PORTID_MASK                 0x1ff
> +#define  ARL_CON_SHIFT                       9
> +#define  ARL_CON_MASK                        0x3
> +#define  ARL_PRI_SHIFT                       11
> +#define  ARL_PRI_MASK                        0x7
> +#define  ARL_AGE                     (1 << 14)
> +#define  ARL_STATIC                  (1 << 15)
> +#define  ARL_VALID                   (1 << 16)
> +
> +#define CORE_ARLA_MACVID_ENTRY(x)    (CORE_ARLA_MACVID0 + ((x) * 0x40))
> +#define CORE_ARLA_FWD_ENTRY(x)               (CORE_ARLA_FWD_ENTRY0 + ((x) * 
> 0x40))
> +
> +#define CORE_ARLA_SRCH_CTL           0x1540
> +#define  ARLA_SRCH_VLID                      (1 << 0)
> +#define  IVL_SVL_SELECT                      (1 << 6)
> +#define  ARLA_SRCH_STDN                      (1 << 7)
> +
> +#define CORE_ARLA_SRCH_ADR           0x1544
> +#define  ARLA_SRCH_ADR_VALID         (1 << 15)
> +
> +#define CORE_ARLA_SRCH_RSLT_0_MACVID 0x1580
> +#define CORE_ARLA_SRCH_RSLT_0                0x15a0
> +
> +#define CORE_ARLA_SRCH_RSLT_MACVID(x)        (CORE_ARLA_SRCH_RSLT_0_MACVID + 
> ((x) * 0x40))
> +#define CORE_ARLA_SRCH_RSLT(x)               (CORE_ARLA_SRCH_RSLT_0 + ((x) * 
> 0x40))
> +
>  #define CORE_MEM_PSM_VDD_CTRL                0x2380
>  #define  P_TXQ_PSM_VDD_SHIFT         2
>  #define  P_TXQ_PSM_VDD_MASK          0x3
> -- 
> 2.1.0
> 
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to