aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c')
-rw-r--r--target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c302
1 files changed, 215 insertions, 87 deletions
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c
index 9ecd189d91..dfd773c5e6 100644
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c
@@ -102,6 +102,40 @@ static void rtl838x_vlan_fwd_on_inner(int port, bool is_set)
sw_w32_mask(0, BIT(port), RTL838X_VLAN_PORT_FWD);
}
+static u64 rtl838x_l2_hash_seed(u64 mac, u32 vid)
+{
+ return mac << 12 | vid;
+}
+
+/*
+ * Applies the same hash algorithm as the one used currently by the ASIC to the seed
+ * and returns a key into the L2 hash table
+ */
+static u32 rtl838x_l2_hash_key(struct rtl838x_switch_priv *priv, u64 seed)
+{
+ u32 h1, h2, h3, h;
+
+ if (sw_r32(priv->r->l2_ctrl_0) & 1) {
+ h1 = (seed >> 11) & 0x7ff;
+ h1 = ((h1 & 0x1f) << 6) | ((h1 >> 5) & 0x3f);
+
+ h2 = (seed >> 33) & 0x7ff;
+ h2 = ((h2 & 0x3f) << 5) | ((h2 >> 6) & 0x1f);
+
+ h3 = (seed >> 44) & 0x7ff;
+ h3 = ((h3 & 0x7f) << 4) | ((h3 >> 7) & 0xf);
+
+ h = h1 ^ h2 ^ h3 ^ ((seed >> 55) & 0x1ff);
+ h ^= ((seed >> 22) & 0x7ff) ^ (seed & 0x7ff);
+ } else {
+ h = ((seed >> 55) & 0x1ff) ^ ((seed >> 44) & 0x7ff)
+ ^ ((seed >> 33) & 0x7ff) ^ ((seed >> 22) & 0x7ff)
+ ^ ((seed >> 11) & 0x7ff) ^ (seed & 0x7ff);
+ }
+
+ return h;
+}
+
static inline int rtl838x_mac_force_mode_ctrl(int p)
{
return RTL838X_MAC_FORCE_MODE_CTRL + (p << 2);
@@ -132,84 +166,202 @@ inline static int rtl838x_trk_mbr_ctr(int group)
return RTL838X_TRK_MBR_CTR + (group << 2);
}
-static u64 rtl838x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e)
+/*
+ * Fills an L2 entry structure from the SoC registers
+ */
+static void rtl838x_fill_l2_entry(u32 r[], struct rtl838x_l2_entry *e)
+{
+ /* Table contains different entry types, we need to identify the right one:
+ * Check for MC entries, first
+ * In contrast to the RTL93xx SoCs, there is no valid bit, use heuristics to
+ * identify valid entries
+ */
+ e->is_ip_mc = !!(r[0] & BIT(22));
+ e->is_ipv6_mc = !!(r[0] & BIT(21));
+ e->type = L2_INVALID;
+
+ if (!e->is_ip_mc && !e->is_ipv6_mc) {
+ e->mac[0] = (r[1] >> 20);
+ e->mac[1] = (r[1] >> 12);
+ e->mac[2] = (r[1] >> 4);
+ e->mac[3] = (r[1] & 0xf) << 4 | (r[2] >> 28);
+ e->mac[4] = (r[2] >> 20);
+ e->mac[5] = (r[2] >> 12);
+
+ e->rvid = r[2] & 0xfff;
+ e->vid = r[0] & 0xfff;
+
+ /* Is it a unicast entry? check multicast bit */
+ if (!(e->mac[0] & 1)) {
+ e->is_static = !!((r[0] >> 19) & 1);
+ e->port = (r[0] >> 12) & 0x1f;
+ e->block_da = !!(r[1] & BIT(30));
+ e->block_sa = !!(r[1] & BIT(31));
+ e->suspended = !!(r[1] & BIT(29));
+ e->next_hop = !!(r[1] & BIT(28));
+ if (e->next_hop) {
+ pr_info("Found next hop entry, need to read extra data\n");
+ e->nh_vlan_target = !!(r[0] & BIT(9));
+ e->nh_route_id = r[0] & 0x1ff;
+ }
+ e->age = (r[0] >> 17) & 0x3;
+ e->valid = true;
+
+ /* A valid entry has one of mutli-cast, aging, sa/da-blocking,
+ * next-hop or static entry bit set */
+ if (!(r[0] & 0x007c0000) && !(r[1] & 0xd0000000))
+ e->valid = false;
+ else
+ e->type = L2_UNICAST;
+ } else { // L2 multicast
+ pr_info("Got L2 MC entry: %08x %08x %08x\n", r[0], r[1], r[2]);
+ e->valid = true;
+ e->type = L2_MULTICAST;
+ e->mc_portmask_index = (r[0] >> 12) & 0x1ff;
+ }
+ } else { // IPv4 and IPv6 multicast
+ e->valid = true;
+ e->mc_portmask_index = (r[0] >> 12) & 0x1ff;
+ e->mc_gip = r[1];
+ e->mc_sip = r[2];
+ e->rvid = r[0] & 0xfff;
+ }
+ if (e->is_ip_mc)
+ e->type = IP4_MULTICAST;
+ if (e->is_ipv6_mc)
+ e->type = IP6_MULTICAST;
+}
+
+/*
+ * Fills the 3 SoC table registers r[] with the information of in the rtl838x_l2_entry
+ */
+static void rtl838x_fill_l2_row(u32 r[], struct rtl838x_l2_entry *e)
+{
+ u64 mac = ether_addr_to_u64(e->mac);
+
+ if (!e->valid) {
+ r[0] = r[1] = r[2] = 0;
+ return;
+ }
+
+ r[0] = e->is_ip_mc ? BIT(22) : 0;
+ r[0] |= e->is_ipv6_mc ? BIT(21) : 0;
+
+ if (!e->is_ip_mc && !e->is_ipv6_mc) {
+ r[1] = mac >> 20;
+ r[2] = (mac & 0xfffff) << 12;
+
+ /* Is it a unicast entry? check multicast bit */
+ if (!(e->mac[0] & 1)) {
+ r[0] |= e->is_static ? BIT(19) : 0;
+ r[0] |= (e->port & 0x3f) << 12;
+ r[0] |= e->vid;
+ r[1] |= e->block_da ? BIT(30) : 0;
+ r[1] |= e->block_sa ? BIT(31) : 0;
+ r[1] |= e->suspended ? BIT(29) : 0;
+ r[2] |= e->rvid & 0xfff;
+ if (e->next_hop) {
+ r[1] |= BIT(28);
+ r[0] |= e->nh_vlan_target ? BIT(9) : 0;
+ r[0] |= e->nh_route_id &0x1ff;
+ }
+ r[0] |= (e->age & 0x3) << 17;
+ } else { // L2 Multicast
+ r[0] |= (e->mc_portmask_index & 0x1ff) << 12;
+ r[2] |= e->rvid & 0xfff;
+ r[0] |= e->vid & 0xfff;
+ pr_info("FILL MC: %08x %08x %08x\n", r[0], r[1], r[2]);
+ }
+ } else { // IPv4 and IPv6 multicast
+ r[1] = e->mc_gip;
+ r[2] = e->mc_sip;
+ r[0] |= e->rvid;
+ }
+}
+
+/*
+ * Read an L2 UC or MC entry out of a hash bucket of the L2 forwarding table
+ * hash is the id of the bucket and pos is the position of the entry in that bucket
+ * The data read from the SoC is filled into rtl838x_l2_entry
+ */
+static u64 rtl838x_read_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e)
{
u64 entry;
u32 r[3];
+ struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 0); // Access L2 Table 0
+ u32 idx = (0 << 14) | (hash << 2) | pos; // Search SRAM, with hash and at pos in bucket
+ int i;
- /* Search in SRAM, with hash and at position in hash bucket (0-3) */
- u32 idx = (0 << 14) | (hash << 2) | position;
-
- u32 cmd = BIT(16) /* Execute cmd */
- | BIT(15) /* Read */
- | 0 << 13 /* Table type 0b00 */
- | (idx & 0x1fff);
-
- sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL);
- do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & BIT(16));
- r[0] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(0));
- r[1] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(1));
- r[2] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(2));
-
- e->mac[0] = (r[1] >> 20);
- e->mac[1] = (r[1] >> 12);
- e->mac[2] = (r[1] >> 4);
- e->mac[3] = (r[1] & 0xf) << 4 | (r[2] >> 28);
- e->mac[4] = (r[2] >> 20);
- e->mac[5] = (r[2] >> 12);
- e->is_static = !!((r[0] >> 19) & 1);
- e->vid = r[0] & 0xfff;
- e->rvid = r[2] & 0xfff;
- e->port = (r[0] >> 12) & 0x1f;
-
- e->valid = true;
- if (!(r[0] >> 17)) /* Check for invalid entry */
- e->valid = false;
-
- if (e->valid)
- pr_debug("Found in Hash: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]);
+ rtl_table_read(q, idx);
+ for (i= 0; i < 3; i++)
+ r[i] = sw_r32(rtl_table_data(q, i));
+
+ rtl_table_release(q);
+
+ rtl838x_fill_l2_entry(r, e);
+ if (!e->valid)
+ return 0;
entry = (((u64) r[1]) << 32) | (r[2] & 0xfffff000) | (r[0] & 0xfff);
return entry;
}
+static void rtl838x_write_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e)
+{
+ u32 r[3];
+ struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 0);
+ int i;
+
+ u32 idx = (0 << 14) | (hash << 2) | pos; // Access SRAM, with hash and at pos in bucket
+
+ rtl838x_fill_l2_row(r, e);
+
+ for (i= 0; i < 3; i++)
+ sw_w32(r[i], rtl_table_data(q, i));
+
+ rtl_table_write(q, idx);
+ rtl_table_release(q);
+}
+
static u64 rtl838x_read_cam(int idx, struct rtl838x_l2_entry *e)
{
u64 entry;
u32 r[3];
+ struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 1); // Access L2 Table 1
+ int i;
+
+ rtl_table_read(q, idx);
+ for (i= 0; i < 3; i++)
+ r[i] = sw_r32(rtl_table_data(q, i));
+
+ rtl_table_release(q);
- u32 cmd = BIT(16) /* Execute cmd */
- | BIT(15) /* Read */
- | BIT(13) /* Table type 0b01 */
- | (idx & 0x3f);
- sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL);
- do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & BIT(16));
- r[0] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(0));
- r[1] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(1));
- r[2] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(2));
-
- e->mac[0] = (r[1] >> 20);
- e->mac[1] = (r[1] >> 12);
- e->mac[2] = (r[1] >> 4);
- e->mac[3] = (r[1] & 0xf) << 4 | (r[2] >> 28);
- e->mac[4] = (r[2] >> 20);
- e->mac[5] = (r[2] >> 12);
- e->is_static = !!((r[0] >> 19) & 1);
- e->vid = r[0] & 0xfff;
- e->rvid = r[2] & 0xfff;
- e->port = (r[0] >> 12) & 0x1f;
-
- e->valid = true;
- if (!(r[0] >> 17)) /* Check for invalid entry */
- e->valid = false;
-
- if (e->valid)
- pr_debug("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]);
+ rtl838x_fill_l2_entry(r, e);
+ if (!e->valid)
+ return 0;
+ pr_debug("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]);
+
+ // Return MAC with concatenated VID ac concatenated ID
entry = (((u64) r[1]) << 32) | (r[2] & 0xfffff000) | (r[0] & 0xfff);
return entry;
}
+static void rtl838x_write_cam(int idx, struct rtl838x_l2_entry *e)
+{
+ u32 r[3];
+ struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 1); // Access L2 Table 1
+ int i;
+
+ rtl838x_fill_l2_row(r, e);
+
+ for (i= 0; i < 3; i++)
+ sw_w32(r[i], rtl_table_data(q, i));
+
+ rtl_table_write(q, idx);
+ rtl_table_release(q);
+}
+
static u64 rtl838x_read_mcast_pmask(int idx)
{
u32 portmask;
@@ -431,7 +583,9 @@ const struct rtl838x_reg rtl838x_reg = {
.mac_rx_pause_sts = RTL838X_MAC_RX_PAUSE_STS,
.mac_tx_pause_sts = RTL838X_MAC_TX_PAUSE_STS,
.read_l2_entry_using_hash = rtl838x_read_l2_entry_using_hash,
+ .write_l2_entry_using_hash = rtl838x_write_l2_entry_using_hash,
.read_cam = rtl838x_read_cam,
+ .write_cam = rtl838x_write_cam,
.vlan_port_egr_filter = RTL838X_VLAN_PORT_EGR_FLTR,
.vlan_port_igr_filter = RTL838X_VLAN_PORT_IGR_FLTR(0),
.vlan_port_pb = RTL838X_VLAN_PORT_PB_VLAN,
@@ -442,6 +596,8 @@ const struct rtl838x_reg rtl838x_reg = {
.init_eee = rtl838x_init_eee,
.port_eee_set = rtl838x_port_eee_set,
.eee_port_ability = rtl838x_eee_port_ability,
+ .l2_hash_seed = rtl838x_l2_hash_seed,
+ .l2_hash_key = rtl838x_l2_hash_key,
.read_mcast_pmask = rtl838x_read_mcast_pmask,
.write_mcast_pmask = rtl838x_write_mcast_pmask,
};
@@ -659,34 +815,6 @@ void rtl8380_get_version(struct rtl838x_switch_priv *priv)
}
}
-/*
- * Applies the same hash algorithm as the one used currently by the ASIC
- */
-u32 rtl838x_hash(struct rtl838x_switch_priv *priv, u64 seed)
-{
- u32 h1, h2, h3, h;
-
- if (sw_r32(priv->r->l2_ctrl_0) & 1) {
- h1 = (seed >> 11) & 0x7ff;
- h1 = ((h1 & 0x1f) << 6) | ((h1 >> 5) & 0x3f);
-
- h2 = (seed >> 33) & 0x7ff;
- h2 = ((h2 & 0x3f) << 5) | ((h2 >> 6) & 0x1f);
-
- h3 = (seed >> 44) & 0x7ff;
- h3 = ((h3 & 0x7f) << 4) | ((h3 >> 7) & 0xf);
-
- h = h1 ^ h2 ^ h3 ^ ((seed >> 55) & 0x1ff);
- h ^= ((seed >> 22) & 0x7ff) ^ (seed & 0x7ff);
- } else {
- h = ((seed >> 55) & 0x1ff) ^ ((seed >> 44) & 0x7ff)
- ^ ((seed >> 33) & 0x7ff) ^ ((seed >> 22) & 0x7ff)
- ^ ((seed >> 11) & 0x7ff) ^ (seed & 0x7ff);
- }
-
- return h;
-}
-
void rtl838x_vlan_profile_dump(int profile)
{
u32 p;