mutex_unlock(&priv->reg_mutex);
}
-static void rtl83xx_write_cam(int idx, u32 *r)
-{
- u32 cmd = BIT(16) /* Execute cmd */
- | BIT(15) /* Read */
- | BIT(13) /* Table type 0b01 */
- | (idx & 0x3f);
-
- sw_w32(r[0], RTL838X_TBL_ACCESS_L2_DATA(0));
- sw_w32(r[1], RTL838X_TBL_ACCESS_L2_DATA(1));
- sw_w32(r[2], RTL838X_TBL_ACCESS_L2_DATA(2));
-
- sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL);
- do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & BIT(16));
-}
-
-static u64 rtl83xx_hash_key(struct rtl838x_switch_priv *priv, u64 mac, u32 vid)
-{
- switch (priv->family_id) {
- case RTL8380_FAMILY_ID:
- return rtl838x_hash(priv, mac << 12 | vid);
- case RTL8390_FAMILY_ID:
- return rtl839x_hash(priv, mac << 12 | vid);
- case RTL9300_FAMILY_ID:
- return rtl930x_hash(priv, ((u64)vid) << 48 | mac);
- default:
- pr_err("Hash not implemented\n");
- }
- return 0;
-}
-
-static void rtl83xx_write_hash(int idx, u32 *r)
-{
- u32 cmd = BIT(16) /* Execute cmd */
- | 0 << 15 /* Write */
- | 0 << 13 /* Table type 0b00 */
- | (idx & 0x1fff);
-
- sw_w32(0, RTL838X_TBL_ACCESS_L2_DATA(0));
- sw_w32(0, RTL838X_TBL_ACCESS_L2_DATA(1));
- sw_w32(0, RTL838X_TBL_ACCESS_L2_DATA(2));
- sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL);
- do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & BIT(16));
-}
-
static void rtl83xx_enable_phy_polling(struct rtl838x_switch_priv *priv)
{
int i;
/* Enable all ports with a PHY, including the SFP-ports */
for (i = 0; i < priv->cpu_port; i++) {
if (priv->ports[i].phy)
- v |= BIT(i);
+ v |= BIT_ULL(i);
}
pr_debug("%s: %16llx\n", __func__, v);
*/
for (i = 0; i < priv->cpu_port; i++) {
if (priv->ports[i].phy) {
- priv->r->set_port_reg_be(BIT_ULL(priv->cpu_port) | BIT(i),
+ priv->r->set_port_reg_be(BIT_ULL(priv->cpu_port) | BIT_ULL(i),
priv->r->port_iso_ctrl(i));
port_bitmap |= BIT_ULL(i);
}
for (i = 0; i < priv->cpu_port; i++) {
if (priv->ports[i].phy) {
- priv->r->traffic_set(i, BIT(priv->cpu_port) | BIT(i));
- port_bitmap |= 1ULL << i;
+ priv->r->traffic_set(i, BIT_ULL(priv->cpu_port) | BIT_ULL(i));
+ port_bitmap |= BIT_ULL(i);
}
}
priv->r->traffic_set(priv->cpu_port, port_bitmap);
struct rtl838x_switch_priv *priv = ds->priv;
__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
- pr_debug("In %s port %d", __func__, port);
+ pr_debug("In %s port %d, state is %d", __func__, port, state->interface);
if (!phy_interface_mode_is_rgmii(state->interface) &&
state->interface != PHY_INTERFACE_MODE_NA &&
if (port >= 24 && port <= 27 && priv->family_id == RTL8380_FAMILY_ID)
phylink_set(mask, 1000baseX_Full);
+ /* On the RTL839x family of SoCs, ports 48 to 51 are SFP ports */
+ if (port >=48 && port <= 51 && priv->family_id == RTL8390_FAMILY_ID)
+ phylink_set(mask, 1000baseX_Full);
+
phylink_set(mask, 10baseT_Half);
phylink_set(mask, 10baseT_Full);
phylink_set(mask, 100baseT_Half);
link = priv->r->get_port_reg_le(priv->r->mac_link_sts);
if (link & BIT_ULL(port))
state->link = 1;
- pr_debug("%s: link state: %llx\n", __func__, link & BIT_ULL(port));
+ pr_debug("%s: link state port %d: %llx\n", __func__, port, link & BIT_ULL(port));
state->duplex = 0;
if (priv->r->get_port_reg_le(priv->r->mac_link_dup_sts) & BIT_ULL(port))
state->speed = SPEED_1000;
break;
case 3:
- if (port == 24 || port == 26) /* Internal serdes */
+ if (priv->family_id == RTL9300_FAMILY_ID
+ && (port == 24 || port == 26)) /* Internal serdes */
state->speed = SPEED_2500;
else
state->speed = SPEED_100; /* Is in fact 500Mbit */
return 1;
}
-
static void rtl83xx_config_interface(int port, phy_interface_t interface)
{
u32 old, int_shift, sds_shift;
v |= priv->ports[port].pm;
priv->r->traffic_set(port, v);
- sw_w32_mask(0, BIT(port), RTL930X_L2_PORT_SABLK_CTRL);
- sw_w32_mask(0, BIT(port), RTL930X_L2_PORT_DABLK_CTRL);
+ // TODO: Figure out if this is necessary
+ if (priv->family_id == RTL9300_FAMILY_ID) {
+ sw_w32_mask(0, BIT(port), RTL930X_L2_PORT_SABLK_CTRL);
+ sw_w32_mask(0, BIT(port), RTL930X_L2_PORT_DABLK_CTRL);
+ }
return 0;
}
struct net_device *bridge)
{
struct rtl838x_switch_priv *priv = ds->priv;
- u64 port_bitmap = 1ULL << priv->cpu_port, v;
+ u64 port_bitmap = BIT_ULL(priv->cpu_port), v;
int i;
pr_debug("%s %x: %d %llx", __func__, (u32)priv, port, port_bitmap);
if (priv->ports[i].enable)
priv->r->traffic_enable(i, port);
- priv->ports[i].pm |= 1ULL << port;
- port_bitmap |= 1ULL << i;
+ priv->ports[i].pm |= BIT_ULL(port);
+ port_bitmap |= BIT_ULL(i);
}
}
struct net_device *bridge)
{
struct rtl838x_switch_priv *priv = ds->priv;
- u64 port_bitmap = 1ULL << priv->cpu_port, v;
+ u64 port_bitmap = BIT_ULL(priv->cpu_port), v;
int i;
pr_debug("%s %x: %d", __func__, (u32)priv, port);
if (priv->ports[i].enable)
priv->r->traffic_disable(i, port);
- priv->ports[i].pm |= 1ULL << port;
+ priv->ports[i].pm |= BIT_ULL(port);
port_bitmap &= ~BIT_ULL(i);
}
}
struct rtl838x_vlan_info info;
struct rtl838x_switch_priv *priv = ds->priv;
- pr_info("%s: port %d\n", __func__, port);
+ priv->r->vlan_tables_read(0, &info);
- mutex_lock(&priv->reg_mutex);
+ pr_debug("VLAN 0: Tagged ports %llx, untag %llx, profile %d, MC# %d, UC# %d, FID %x\n",
+ info.tagged_ports, info.untagged_ports, info.profile_id,
+ info.hash_mc_fid, info.hash_uc_fid, info.fid);
- priv->r->vlan_profile_dump(1);
priv->r->vlan_tables_read(1, &info);
-
- pr_info("Tagged ports %llx, untag %llx, prof %x, MC# %d, UC# %d, FID %x\n",
+ pr_debug("VLAN 1: Tagged ports %llx, untag %llx, profile %d, MC# %d, UC# %d, FID %x\n",
info.tagged_ports, info.untagged_ports, info.profile_id,
info.hash_mc_fid, info.hash_uc_fid, info.fid);
-
priv->r->vlan_set_untagged(1, info.untagged_ports);
pr_debug("SET: Untagged ports, VLAN %d: %llx\n", 1, info.untagged_ports);
struct rtl838x_switch_priv *priv = ds->priv;
int v;
- pr_info("%s port %d, vid_end %d, vid_end %d, flags %x\n", __func__,
+ pr_debug("%s port %d, vid_end %d, vid_end %d, flags %x\n", __func__,
port, vlan->vid_begin, vlan->vid_end, vlan->flags);
if (vlan->vid_begin > 4095 || vlan->vid_end > 4095) {
info.untagged_ports |= BIT_ULL(port);
priv->r->vlan_set_untagged(v, info.untagged_ports);
- pr_info("Untagged ports, VLAN %d: %llx\n", v, info.untagged_ports);
+ pr_debug("Untagged ports, VLAN %d: %llx\n", v, info.untagged_ports);
priv->r->vlan_set_tagged(v, &info);
- pr_info("Tagged ports, VLAN %d: %llx\n", v, info.tagged_ports);
+ pr_debug("Tagged ports, VLAN %d: %llx\n", v, info.tagged_ports);
}
mutex_unlock(&priv->reg_mutex);
return 0;
}
-static int rtl83xx_port_fdb_add(struct dsa_switch *ds, int port,
- const unsigned char *addr, u16 vid)
+static void dump_l2_entry(struct rtl838x_l2_entry *e)
{
- struct rtl838x_switch_priv *priv = ds->priv;
- u64 mac = ether_addr_to_u64(addr);
- u32 key = rtl83xx_hash_key(priv, mac, vid);
- struct rtl838x_l2_entry e;
- u32 r[3];
+ pr_info("MAC: %02x:%02x:%02x:%02x:%02x:%02x vid: %d, rvid: %d, port: %d, valid: %d\n",
+ e->mac[0], e->mac[1], e->mac[2], e->mac[3], e->mac[4], e->mac[5],
+ e->vid, e->rvid, e->port, e->valid);
+
+ if (e->type != L2_MULTICAST) {
+ pr_info("Type: %d, is_static: %d, is_ip_mc: %d, is_ipv6_mc: %d, block_da: %d\n",
+ e->type, e->is_static, e->is_ip_mc, e->is_ipv6_mc, e->block_da);
+ pr_info(" block_sa: %d, susp: %d, nh: %d, age: %d, is_trunk: %d, trunk: %d\n",
+ e->block_sa, e->suspended, e->next_hop, e->age, e->is_trunk, e->trunk);
+ }
+ if (e->type == L2_MULTICAST)
+ pr_info(" L2_MULTICAST mc_portmask_index: %d\n", e->mc_portmask_index);
+ if (e->is_ip_mc || e->is_ipv6_mc)
+ pr_info(" mc_portmask_index: %d, mc_gip: %d, mc_sip: %d\n",
+ e->mc_portmask_index, e->mc_gip, e->mc_sip);
+ pr_info(" stack_dev: %d\n", e->stack_dev);
+ if (e->next_hop)
+ pr_info(" nh_route_id: %d\n", e->nh_route_id);
+}
+
+static void rtl83xx_setup_l2_uc_entry(struct rtl838x_l2_entry *e, int port, int vid, u64 mac)
+{
+ e->is_ip_mc = e->is_ipv6_mc = false;
+ e->valid = true;
+ e->age = 3;
+ e->port = port,
+ e->vid = vid;
+ u64_to_ether_addr(mac, e->mac);
+}
+
+static void rtl83xx_setup_l2_mc_entry(struct rtl838x_switch_priv *priv,
+ struct rtl838x_l2_entry *e, int vid, u64 mac, int mc_group)
+{
+ e->is_ip_mc = e->is_ipv6_mc = false;
+ e->valid = true;
+ e->mc_portmask_index = mc_group;
+ e->type = L2_MULTICAST;
+ e->rvid = e->vid = vid;
+ pr_debug("%s: vid: %d, rvid: %d\n", __func__, e->vid, e->rvid);
+ u64_to_ether_addr(mac, e->mac);
+}
+
+/*
+ * Uses the seed to identify a hash bucket in the L2 using the derived hash key and then loops
+ * over the entries in the bucket until either a matching entry is found or an empty slot
+ * Returns the filled in rtl838x_l2_entry and the index in the bucket when an entry was found
+ * when an empty slot was found and must exist is false, the index of the slot is returned
+ * when no slots are available returns -1
+ */
+static int rtl83xx_find_l2_hash_entry(struct rtl838x_switch_priv *priv, u64 seed,
+ bool must_exist, struct rtl838x_l2_entry *e)
+{
+ int i, idx = -1;
+ u32 key = priv->r->l2_hash_key(priv, seed);
u64 entry;
- int idx = -1, err = 0, i;
- mutex_lock(&priv->reg_mutex);
- for (i = 0; i < 4; i++) {
- entry = priv->r->read_l2_entry_using_hash(key, i, &e);
- if (!e.valid) {
- idx = (key << 2) | i;
- break;
- }
- if ((entry & 0x0fffffffffffffffULL) == ((mac << 12) | vid)) {
- idx = (key << 2) | i;
+ pr_debug("%s: using key %x, for seed %016llx\n", __func__, key, seed);
+ // Loop over all entries in the hash-bucket and over the second block on 93xx SoCs
+ for (i = 0; i < priv->l2_bucket_size; i++) {
+ entry = priv->r->read_l2_entry_using_hash(key, i, e);
+ pr_debug("valid %d, mac %016llx\n", e->valid, ether_addr_to_u64(&e->mac[0]));
+ if (must_exist && !e->valid)
+ continue;
+ if (!e->valid || ((entry & 0x0fffffffffffffffULL) == seed)) {
+ idx = i > 3 ? ((key >> 14) & 0xffff) | i >> 1 : ((key << 2) | i) & 0xffff;
break;
}
}
- if (idx >= 0) {
- r[0] = 3 << 17 | port << 12; // Aging and port
- r[0] |= vid;
- r[1] = mac >> 16;
- r[2] = (mac & 0xffff) << 12; /* rvid = 0 */
- rtl83xx_write_hash(idx, r);
- goto out;
- }
- /* Hash buckets full, try CAM */
+ return idx;
+}
+
+/*
+ * Uses the seed to identify an entry in the CAM by looping over all its entries
+ * Returns the filled in rtl838x_l2_entry and the index in the CAM when an entry was found
+ * when an empty slot was found the index of the slot is returned
+ * when no slots are available returns -1
+ */
+static int rtl83xx_find_l2_cam_entry(struct rtl838x_switch_priv *priv, u64 seed,
+ bool must_exist, struct rtl838x_l2_entry *e)
+{
+ int i, idx = -1;
+ u64 entry;
+
for (i = 0; i < 64; i++) {
- entry = priv->r->read_cam(i, &e);
- if (!e.valid) {
+ entry = priv->r->read_cam(i, e);
+ if (!must_exist && !e->valid) {
if (idx < 0) /* First empty entry? */
idx = i;
break;
- } else if ((entry & 0x0fffffffffffffffULL) == ((mac << 12) | vid)) {
+ } else if ((entry & 0x0fffffffffffffffULL) == seed) {
pr_debug("Found entry in CAM\n");
idx = i;
break;
}
}
+ return idx;
+}
+
+static int rtl83xx_port_fdb_add(struct dsa_switch *ds, int port,
+ const unsigned char *addr, u16 vid)
+{
+ struct rtl838x_switch_priv *priv = ds->priv;
+ u64 mac = ether_addr_to_u64(addr);
+ struct rtl838x_l2_entry e;
+ int err = 0, idx;
+ u64 seed = priv->r->l2_hash_seed(mac, vid);
+
+ mutex_lock(&priv->reg_mutex);
+
+ idx = rtl83xx_find_l2_hash_entry(priv, seed, false, &e);
+
+ // Found an existing or empty entry
+ if (idx >= 0) {
+ rtl83xx_setup_l2_uc_entry(&e, port, vid, mac);
+ priv->r->write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e);
+ goto out;
+ }
+
+ // Hash buckets full, try CAM
+ rtl83xx_find_l2_cam_entry(priv, seed, false, &e);
+
if (idx >= 0) {
- r[0] = 3 << 17 | port << 12; // Aging
- r[0] |= vid;
- r[1] = mac >> 16;
- r[2] = (mac & 0xffff) << 12; /* rvid = 0 */
- rtl83xx_write_cam(idx, r);
+ rtl83xx_setup_l2_uc_entry(&e, port, vid, mac);
+ priv->r->write_cam(idx, &e);
goto out;
}
+
err = -ENOTSUPP;
out:
mutex_unlock(&priv->reg_mutex);
{
struct rtl838x_switch_priv *priv = ds->priv;
u64 mac = ether_addr_to_u64(addr);
- u32 key = rtl83xx_hash_key(priv, mac, vid);
struct rtl838x_l2_entry e;
- u32 r[3];
- u64 entry;
- int idx = -1, err = 0, i;
+ int err = 0, idx;
+ u64 seed = priv->r->l2_hash_seed(mac, vid);
- pr_debug("In %s, mac %llx, vid: %d, key: %x08x\n", __func__, mac, vid, key);
+ pr_info("In %s, mac %llx, vid: %d\n", __func__, mac, vid);
mutex_lock(&priv->reg_mutex);
- for (i = 0; i < 4; i++) {
- entry = priv->r->read_l2_entry_using_hash(key, i, &e);
- if (!e.valid)
- continue;
- if ((entry & 0x0fffffffffffffffULL) == ((mac << 12) | vid)) {
- idx = (key << 2) | i;
- break;
- }
- }
+ idx = rtl83xx_find_l2_hash_entry(priv, seed, true, &e);
+
+ pr_info("Found entry index %d, key %d and bucket %d\n", idx, idx >> 2, idx & 3);
if (idx >= 0) {
- r[0] = r[1] = r[2] = 0;
- rtl83xx_write_hash(idx, r);
+ e.valid = false;
+ dump_l2_entry(&e);
+ priv->r->write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e);
goto out;
}
/* Check CAM for spillover from hash buckets */
- for (i = 0; i < 64; i++) {
- entry = priv->r->read_cam(i, &e);
- if ((entry & 0x0fffffffffffffffULL) == ((mac << 12) | vid)) {
- idx = i;
- break;
- }
- }
+ rtl83xx_find_l2_cam_entry(priv, seed, true, &e);
+
if (idx >= 0) {
- r[0] = r[1] = r[2] = 0;
- rtl83xx_write_cam(idx, r);
+ e.valid = false;
+ priv->r->write_cam(idx, &e);
goto out;
}
err = -ENOENT;
struct rtl838x_l2_entry e;
struct rtl838x_switch_priv *priv = ds->priv;
int i;
- u32 fid;
- u32 pkey;
+ u32 fid, pkey;
u64 mac;
mutex_lock(&priv->reg_mutex);
continue;
if (e.port == port) {
- fid = (i & 0x3ff) | (e.rvid & ~0x3ff);
+ fid = ((i >> 2) & 0x3ff) | (e.rvid & ~0x3ff);
mac = ether_addr_to_u64(&e.mac[0]);
- pkey = rtl838x_hash(priv, mac << 12 | fid);
+ pkey = priv->r->l2_hash_key(priv, priv->r->l2_hash_seed(mac, fid));
fid = (pkey & 0x3ff) | (fid & ~0x3ff);
- pr_debug("-> mac %016llx, fid: %d\n", mac, fid);
+ pr_info("-> index %d, key %x, bucket %d, dmac %016llx, fid: %x rvid: %x\n",
+ i, i >> 2, i & 0x3, mac, fid, e.rvid);
+ dump_l2_entry(&e);
+ u64 seed = priv->r->l2_hash_seed(mac, e.rvid);
+ u32 key = priv->r->l2_hash_key(priv, seed);
+ pr_info("seed: %016llx, key based on rvid: %08x\n", seed, key);
cb(e.mac, e.vid, e.is_static, data);
}
+ if (e.type == L2_MULTICAST) {
+ u64 portmask = priv->r->read_mcast_pmask(e.mc_portmask_index);
+ if (portmask & BIT_ULL(port)) {
+ dump_l2_entry(&e);
+ pr_info(" PM: %016llx\n", portmask);
+ }
+ }
}
for (i = 0; i < 64; i++) {
return 0;
}
+static int rtl83xx_port_mdb_prepare(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_mdb *mdb)
+{
+ struct rtl838x_switch_priv *priv = ds->priv;
+
+ if (priv->id >= 0x9300)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static int rtl83xx_mc_group_alloc(struct rtl838x_switch_priv *priv, int port)
+{
+ int mc_group = find_first_zero_bit(priv->mc_group_bm, MAX_MC_GROUPS - 1);
+ u64 portmask;
+
+ if (mc_group >= MAX_MC_GROUPS - 1)
+ return -1;
+
+ pr_debug("Using MC group %d\n", mc_group);
+ set_bit(mc_group, priv->mc_group_bm);
+ mc_group++; // We cannot use group 0, as this is used for lookup miss flooding
+ portmask = BIT_ULL(port);
+ priv->r->write_mcast_pmask(mc_group, portmask);
+
+ return mc_group;
+}
+
+static u64 rtl83xx_mc_group_add_port(struct rtl838x_switch_priv *priv, int mc_group, int port)
+{
+ u64 portmask = priv->r->read_mcast_pmask(mc_group);
+
+ portmask |= BIT_ULL(port);
+ priv->r->write_mcast_pmask(mc_group, portmask);
+
+ return portmask;
+}
+
+static u64 rtl83xx_mc_group_del_port(struct rtl838x_switch_priv *priv, int mc_group, int port)
+{
+ u64 portmask = priv->r->read_mcast_pmask(mc_group);
+
+ portmask &= ~BIT_ULL(port);
+ priv->r->write_mcast_pmask(mc_group, portmask);
+ if (!portmask)
+ clear_bit(mc_group, priv->mc_group_bm);
+
+ return portmask;
+}
+
+static void rtl83xx_port_mdb_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_mdb *mdb)
+{
+ struct rtl838x_switch_priv *priv = ds->priv;
+ u64 mac = ether_addr_to_u64(mdb->addr);
+ struct rtl838x_l2_entry e;
+ int err = 0, idx;
+ int vid = mdb->vid;
+ u64 seed = priv->r->l2_hash_seed(mac, vid);
+ int mc_group;
+
+ pr_debug("In %s port %d, mac %llx, vid: %d\n", __func__, port, mac, vid);
+ mutex_lock(&priv->reg_mutex);
+
+ idx = rtl83xx_find_l2_hash_entry(priv, seed, false, &e);
+
+ // Found an existing or empty entry
+ if (idx >= 0) {
+ if (e.valid) {
+ pr_debug("Found an existing entry %016llx, mc_group %d\n",
+ ether_addr_to_u64(e.mac), e.mc_portmask_index);
+ rtl83xx_mc_group_add_port(priv, e.mc_portmask_index, port);
+ } else {
+ pr_debug("New entry for seed %016llx\n", seed);
+ mc_group = rtl83xx_mc_group_alloc(priv, port);
+ if (mc_group < 0) {
+ err = -ENOTSUPP;
+ goto out;
+ }
+ rtl83xx_setup_l2_mc_entry(priv, &e, vid, mac, mc_group);
+ priv->r->write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e);
+ }
+ goto out;
+ }
+
+ // Hash buckets full, try CAM
+ rtl83xx_find_l2_cam_entry(priv, seed, false, &e);
+
+ if (idx >= 0) {
+ if (e.valid) {
+ pr_debug("Found existing CAM entry %016llx, mc_group %d\n",
+ ether_addr_to_u64(e.mac), e.mc_portmask_index);
+ rtl83xx_mc_group_add_port(priv, e.mc_portmask_index, port);
+ } else {
+ pr_debug("New entry\n");
+ mc_group = rtl83xx_mc_group_alloc(priv, port);
+ if (mc_group < 0) {
+ err = -ENOTSUPP;
+ goto out;
+ }
+ rtl83xx_setup_l2_mc_entry(priv, &e, vid, mac, mc_group);
+ priv->r->write_cam(idx, &e);
+ }
+ goto out;
+ }
+
+ err = -ENOTSUPP;
+out:
+ mutex_unlock(&priv->reg_mutex);
+ if (err)
+ dev_err(ds->dev, "failed to add MDB entry\n");
+}
+
+int rtl83xx_port_mdb_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_mdb *mdb)
+{
+ struct rtl838x_switch_priv *priv = ds->priv;
+ u64 mac = ether_addr_to_u64(mdb->addr);
+ struct rtl838x_l2_entry e;
+ int err = 0, idx;
+ int vid = mdb->vid;
+ u64 seed = priv->r->l2_hash_seed(mac, vid);
+ u64 portmask;
+
+ pr_debug("In %s, port %d, mac %llx, vid: %d\n", __func__, port, mac, vid);
+ mutex_lock(&priv->reg_mutex);
+
+ idx = rtl83xx_find_l2_hash_entry(priv, seed, true, &e);
+
+ pr_debug("Found entry index %d, key %d and bucket %d\n", idx, idx >> 2, idx & 3);
+ if (idx >= 0) {
+ portmask = rtl83xx_mc_group_del_port(priv, e.mc_portmask_index, port);
+ if (!portmask) {
+ e.valid = false;
+ // dump_l2_entry(&e);
+ priv->r->write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e);
+ }
+ goto out;
+ }
+
+ /* Check CAM for spillover from hash buckets */
+ rtl83xx_find_l2_cam_entry(priv, seed, true, &e);
+
+ if (idx >= 0) {
+ portmask = rtl83xx_mc_group_del_port(priv, e.mc_portmask_index, port);
+ if (!portmask) {
+ e.valid = false;
+ // dump_l2_entry(&e);
+ priv->r->write_cam(idx, &e);
+ }
+ goto out;
+ }
+ // TODO: Re-enable with a newer kernel: err = -ENOENT;
+out:
+ mutex_unlock(&priv->reg_mutex);
+ return err;
+}
+
static int rtl83xx_port_mirror_add(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror,
bool ingress)
.port_fdb_del = rtl83xx_port_fdb_del,
.port_fdb_dump = rtl83xx_port_fdb_dump,
+ .port_mdb_prepare = rtl83xx_port_mdb_prepare,
+ .port_mdb_add = rtl83xx_port_mdb_add,
+ .port_mdb_del = rtl83xx_port_mdb_del,
+
.port_mirror_add = rtl83xx_port_mirror_add,
.port_mirror_del = rtl83xx_port_mirror_del,
};
.port_fdb_add = rtl83xx_port_fdb_add,
.port_fdb_del = rtl83xx_port_fdb_del,
.port_fdb_dump = rtl83xx_port_fdb_dump,
+
+ .port_mdb_prepare = rtl83xx_port_mdb_prepare,
+ .port_mdb_add = rtl83xx_port_mdb_add,
+ .port_mdb_del = rtl83xx_port_mdb_del,
+
};