|
|
@@ -26,50 +26,6 @@ static void rtl83xx_init_stats(struct rtl838x_switch_priv *priv)
|
|
|
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;
|
|
|
@@ -79,7 +35,7 @@ static void rtl83xx_enable_phy_polling(struct rtl838x_switch_priv *priv)
|
|
|
/* 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);
|
|
|
@@ -204,7 +160,7 @@ static int rtl83xx_setup(struct dsa_switch *ds)
|
|
|
*/
|
|
|
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);
|
|
|
}
|
|
|
@@ -250,8 +206,8 @@ static int rtl930x_setup(struct dsa_switch *ds)
|
|
|
|
|
|
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);
|
|
|
@@ -276,7 +232,7 @@ static void rtl83xx_phylink_validate(struct dsa_switch *ds, int port,
|
|
|
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 &&
|
|
|
@@ -313,6 +269,10 @@ static void rtl83xx_phylink_validate(struct dsa_switch *ds, int port,
|
|
|
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);
|
|
|
@@ -345,7 +305,7 @@ static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port,
|
|
|
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))
|
|
|
@@ -364,7 +324,8 @@ static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int 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 */
|
|
|
@@ -378,7 +339,6 @@ static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port,
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
static void rtl83xx_config_interface(int port, phy_interface_t interface)
|
|
|
{
|
|
|
u32 old, int_shift, sds_shift;
|
|
|
@@ -583,8 +543,11 @@ static int rtl83xx_port_enable(struct dsa_switch *ds, int port,
|
|
|
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;
|
|
|
}
|
|
|
@@ -689,7 +652,7 @@ static int rtl83xx_port_bridge_join(struct dsa_switch *ds, int port,
|
|
|
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);
|
|
|
@@ -705,8 +668,8 @@ static int rtl83xx_port_bridge_join(struct dsa_switch *ds, int port,
|
|
|
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);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -727,7 +690,7 @@ static void rtl83xx_port_bridge_leave(struct dsa_switch *ds, int port,
|
|
|
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);
|
|
|
@@ -745,7 +708,7 @@ static void rtl83xx_port_bridge_leave(struct dsa_switch *ds, int 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);
|
|
|
}
|
|
|
}
|
|
|
@@ -897,17 +860,16 @@ static int rtl83xx_vlan_prepare(struct dsa_switch *ds, int port,
|
|
|
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);
|
|
|
|
|
|
@@ -925,7 +887,7 @@ static void rtl83xx_vlan_add(struct dsa_switch *ds, int port,
|
|
|
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) {
|
|
|
@@ -970,10 +932,10 @@ static void rtl83xx_vlan_add(struct dsa_switch *ds, int port,
|
|
|
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);
|
|
|
@@ -1024,59 +986,136 @@ static int rtl83xx_vlan_del(struct dsa_switch *ds, int port,
|
|
|
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);
|
|
|
@@ -1088,41 +1127,29 @@ static int rtl83xx_port_fdb_del(struct dsa_switch *ds, int port,
|
|
|
{
|
|
|
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;
|
|
|
@@ -1137,8 +1164,7 @@ static int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port,
|
|
|
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);
|
|
|
@@ -1150,13 +1176,25 @@ static int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port,
|
|
|
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++) {
|
|
|
@@ -1173,6 +1211,164 @@ static int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port,
|
|
|
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)
|
|
|
@@ -1339,6 +1535,10 @@ const struct dsa_switch_ops rtl83xx_switch_ops = {
|
|
|
.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,
|
|
|
};
|
|
|
@@ -1380,4 +1580,9 @@ const struct dsa_switch_ops rtl930x_switch_ops = {
|
|
|
.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,
|
|
|
+
|
|
|
};
|