+/* 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;
+}
+
+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);
+}
+
+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;
+}
+