aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c')
-rw-r--r--target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c314
1 files changed, 292 insertions, 22 deletions
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c
index a80a6d89b2..820c78165a 100644
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c
@@ -207,12 +207,74 @@ static inline int rtl930x_mac_link_spd_sts(int p)
return RTL930X_MAC_LINK_SPD_STS(p);
}
+static u64 rtl930x_l2_hash_seed(u64 mac, u32 vid)
+{
+ u64 v = vid;
+
+ v <<= 48;
+ v |= mac;
+
+ return v;
+}
+
+/*
+ * Calculate both the block 0 and the block 1 hash by applyingthe same hash
+ * algorithm as the one used currently by the ASIC to the seed, and return
+ * both hashes in the lower and higher word of the return value since only 12 bit of
+ * the hash are significant
+ */
+static u32 rtl930x_l2_hash_key(struct rtl838x_switch_priv *priv, u64 seed)
+{
+ u32 k0, k1, h1, h2, h;
+
+ k0 = (u32) (((seed >> 55) & 0x1f) ^ ((seed >> 44) & 0x7ff)
+ ^ ((seed >> 33) & 0x7ff) ^ ((seed >> 22) & 0x7ff)
+ ^ ((seed >> 11) & 0x7ff) ^ (seed & 0x7ff));
+
+ h1 = (seed >> 11) & 0x7ff;
+ h1 = ((h1 & 0x1f) << 6) | ((h1 >> 5) & 0x3f);
+
+ h2 = (seed >> 33) & 0x7ff;
+ h2 = ((h2 & 0x3f) << 5)| ((h2 >> 6) & 0x3f);
+
+ k1 = (u32) (((seed << 55) & 0x1f) ^ ((seed >> 44) & 0x7ff) ^ h2
+ ^ ((seed >> 22) & 0x7ff) ^ h1
+ ^ (seed & 0x7ff));
+
+ // Algorithm choice for block 0
+ if (sw_r32(RTL930X_L2_CTRL) & BIT(0))
+ h = k1;
+ else
+ h = k0;
+
+ /* Algorithm choice for block 1
+ * Since k0 and k1 are < 2048, adding 2048 will offset the hash into the second
+ * half of hash-space
+ * 2048 is in fact the hash-table size 16384 divided by 4 hashes per bucket
+ * divided by 2 to divide the hash space in 2
+ */
+ if (sw_r32(RTL930X_L2_CTRL) & BIT(1))
+ h |= (k1 + 2048) << 16;
+ else
+ h |= (k0 + 2048) << 16;
+
+ return h;
+}
+
+/*
+ * Fills an L2 entry structure from the SoC registers
+ */
static void rtl930x_fill_l2_entry(u32 r[], struct rtl838x_l2_entry *e)
{
+ pr_debug("In %s valid?\n", __func__);
e->valid = !!(r[2] & BIT(31));
if (!e->valid)
return;
+ pr_debug("In %s is valid\n", __func__);
+ e->is_ip_mc = false;
+ e->is_ipv6_mc = false;
+
// TODO: Is there not a function to copy directly MAC memory?
e->mac[0] = (r[0] >> 24);
e->mac[1] = (r[0] >> 16);
@@ -221,61 +283,164 @@ static void rtl930x_fill_l2_entry(u32 r[], struct rtl838x_l2_entry *e)
e->mac[4] = (r[1] >> 24);
e->mac[5] = (r[1] >> 16);
+ e->next_hop = !!(r[2] & BIT(12));
+ e->rvid = r[1] & 0xfff;
+
/* Is it a unicast entry? check multicast bit */
if (!(e->mac[0] & 1)) {
e->type = L2_UNICAST;
e->is_static = !!(r[2] & BIT(14));
- e->vid = r[2] & 0xfff;
- e->rvid = r[1] & 0xfff;
e->port = (r[2] >> 20) & 0x3ff;
// Check for trunk port
if (r[2] & BIT(30)) {
- e->stackDev = (e->port >> 9) & 1;
+ e->is_trunk = true;
+ e->stack_dev = (e->port >> 9) & 1;
e->trunk = e->port & 0x3f;
} else {
- e->stackDev = (e->port >> 6) & 0xf;
+ e->is_trunk = false;
+ e->stack_dev = (e->port >> 6) & 0xf;
e->port = e->port & 0x3f;
}
e->block_da = !!(r[2] & BIT(15));
e->block_sa = !!(r[2] & BIT(16));
e->suspended = !!(r[2] & BIT(13));
- e->next_hop = !!(r[2] & BIT(12));
e->age = (r[2] >> 17) & 3;
e->valid = true;
-
+ // the UC_VID field in hardware is used for the VID or for the route id
+ if (e->next_hop) {
+ e->nh_route_id = r[2] & 0xfff;
+ e->vid = 0;
+ } else {
+ e->vid = r[2] & 0xfff;
+ e->nh_route_id = 0;
+ }
} else {
e->valid = true;
e->type = L2_MULTICAST;
- e->mc_portmask_index = (r[2]>>6) & 0xfff;
+ e->mc_portmask_index = (r[2] >> 16) & 0x3ff;
}
}
-static u64 rtl930x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e)
+/*
+ * Fills the 3 SoC table registers r[] with the information of in the rtl838x_l2_entry
+ */
+static void rtl930x_fill_l2_row(u32 r[], struct rtl838x_l2_entry *e)
+{
+ u32 port;
+
+ if (!e->valid) {
+ r[0] = r[1] = r[2] = 0;
+ return;
+ }
+
+ r[2] = BIT(31); // Set valid bit
+
+ r[0] = ((u32)e->mac[0]) << 24 | ((u32)e->mac[1]) << 16
+ | ((u32)e->mac[2]) << 8 | ((u32)e->mac[3]);
+ r[1] = ((u32)e->mac[4]) << 24 | ((u32)e->mac[5]) << 16;
+
+ r[2] |= e->next_hop ? BIT(12) : 0;
+
+ if (e->type == L2_UNICAST) {
+ r[2] |= e->is_static ? BIT(14) : 0;
+ r[1] |= e->rvid & 0xfff;
+ r[2] |= (e->port & 0x3ff) << 20;
+ if (e->is_trunk) {
+ r[2] |= BIT(30);
+ port = e->stack_dev << 9 | (e->port & 0x3f);
+ } else {
+ port = (e->stack_dev & 0xf) << 6;
+ port |= e->port & 0x3f;
+ }
+ r[2] |= port << 20;
+ r[2] |= e->block_da ? BIT(15) : 0;
+ r[2] |= e->block_sa ? BIT(17) : 0;
+ r[2] |= e->suspended ? BIT(13) : 0;
+ r[2] |= (e->age & 0x3) << 17;
+ // the UC_VID field in hardware is used for the VID or for the route id
+ if (e->next_hop)
+ r[2] |= e->nh_route_id & 0xfff;
+ else
+ r[2] |= e->vid & 0xfff;
+ } else { // L2_MULTICAST
+ r[2] |= (e->mc_portmask_index & 0x3ff) << 16;
+ r[2] |= e->mc_mac_index & 0x7ff;
+ }
+}
+
+/*
+ * 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 rtl930x_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(RTL9300_TBL_L2, 0);
- u32 idx = (0 << 14) | (hash << 2) | position;
+ u32 idx;
int i;
+ u64 mac;
+ u64 seed;
+
+ pr_debug("%s: hash %08x, pos: %d\n", __func__, hash, pos);
+
+ /* On the RTL93xx, 2 different hash algorithms are used making it a total of
+ * 8 buckets that need to be searched, 4 for each hash-half
+ * Use second hash space when bucket is between 4 and 8 */
+ if (pos >= 4) {
+ pos -= 4;
+ hash >>= 16;
+ } else {
+ hash &= 0xffff;
+ }
+
+ idx = (0 << 14) | (hash << 2) | pos; // Search SRAM, with hash and at pos in bucket
+ pr_debug("%s: NOW hash %08x, pos: %d\n", __func__, hash, pos);
rtl_table_read(q, idx);
- for (i= 0; i < 3; i++)
+ for (i = 0; i < 3; i++)
r[i] = sw_r32(rtl_table_data(q, i));
rtl_table_release(q);
rtl930x_fill_l2_entry(r, e);
+
+ pr_debug("%s: valid: %d, nh: %d\n", __func__, e->valid, e->next_hop);
if (!e->valid)
return 0;
- entry = ((u64)r[0] << 32) | (r[1] & 0xffff0000) | e->vid;
- return entry;
+ mac = ((u64)e->mac[0]) << 40 | ((u64)e->mac[1]) << 32 | ((u64)e->mac[2]) << 24
+ | ((u64)e->mac[3]) << 16 | ((u64)e->mac[4]) << 8 | ((u64)e->mac[5]);
+
+ seed = rtl930x_l2_hash_seed(mac, e->rvid);
+ pr_debug("%s: mac %016llx, seed %016llx\n", __func__, mac, seed);
+ // return vid with concatenated mac as unique id
+ return seed;
+}
+
+static void rtl930x_write_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e)
+{
+ u32 r[3];
+ struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 0);
+ u32 idx = (0 << 14) | (hash << 2) | pos; // Access SRAM, with hash and at pos in bucket
+ int i;
+
+ pr_info("%s: hash %d, pos %d\n", __func__, hash, pos);
+ pr_info("%s: index %d -> mac %02x:%02x:%02x:%02x:%02x:%02x\n", __func__, idx,
+ e->mac[0], e->mac[1], e->mac[2], e->mac[3],e->mac[4],e->mac[5]);
+
+ rtl930x_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 rtl930x_read_cam(int idx, struct rtl838x_l2_entry *e)
{
- u64 entry;
u32 r[3];
struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 1);
int i;
@@ -290,9 +455,113 @@ static u64 rtl930x_read_cam(int idx, struct rtl838x_l2_entry *e)
if (!e->valid)
return 0;
- entry = ((u64)r[0] << 32) | (r[1] & 0xffff0000) | e->vid;
+ // return mac with concatenated vid as unique id
+ return ((u64)r[0] << 28) | ((r[1] & 0xffff0000) >> 4) | e->vid;
+}
- return entry;
+static void rtl930x_write_cam(int idx, struct rtl838x_l2_entry *e)
+{
+ u32 r[3];
+ struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 1); // Access L2 Table 1
+ int i;
+
+ rtl930x_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 void dump_l2_entry(struct rtl838x_l2_entry *e)
+{
+ 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);
+ 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, suspended: %d, next_hop: %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->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(" stac_dev: %d, nh_route_id: %d, port: %d, dev_id\n",
+ e->stack_dev, e->nh_route_id, e->port);
+}
+
+/*
+ * Add an L2 nexthop entry for the L3 routing system in the SoC
+ * Use VID and MAC in rtl838x_l2_entry to identify either a free slot in the L2 hash table
+ * or mark an existing entry as a nexthop by setting it's nexthop bit
+ * Called from the L3 layer
+ * The index in the L2 hash table is filled into nh->l2_id;
+ */
+static int rtl930x_l2_nexthop_add(struct rtl838x_switch_priv *priv, struct rtl838x_nexthop *nh)
+{
+ struct rtl838x_l2_entry e;
+ u64 seed = rtl930x_l2_hash_seed(nh->mac, nh->vid);
+ u32 key = rtl930x_l2_hash_key(priv, seed);
+ int i, idx = -1;
+ u64 entry;
+
+ pr_info("%s searching for %08llx vid %d with key %d, seed: %016llx\n",
+ __func__, nh->mac, nh->vid, key, seed);
+
+ e.type = L2_UNICAST;
+ e.rvid = nh->fid; // Verify its the forwarding ID!!! l2_entry.un.unicast.fid
+ u64_to_ether_addr(nh->mac, &e.mac[0]);
+ e.port = RTL930X_PORT_IGNORE;
+
+ // 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 = rtl930x_read_l2_entry_using_hash(key, i, &e);
+ pr_info("%s i: %d, entry %016llx, seed %016llx\n", __func__, i, entry, seed);
+ if (e.valid && e.next_hop)
+ continue;
+ if (!e.valid || ((entry & 0x0fffffffffffffffULL) == seed)) {
+ idx = i > 3 ? ((key >> 14) & 0xffff) | i >> 1
+ : ((key << 2) | i) & 0xffff;
+ break;
+ }
+ }
+
+ pr_info("%s: found idx %d and i %d\n", __func__, idx, i);
+
+ if (idx < 0) {
+ pr_err("%s: No more L2 forwarding entries available\n", __func__);
+ return -1;
+ }
+
+ // Found an existing or empty entry, make it a nexthop entry
+ pr_info("%s BEFORE -> key %d, pos: %d, index: %d\n", __func__, key, i, idx);
+ dump_l2_entry(&e);
+ nh->l2_id = idx;
+
+ // Found an existing (e->valid is true) or empty entry, make it a nexthop entry
+ if (e.valid) {
+ nh->port = e.port;
+ nh->fid = e.rvid;
+ nh->vid = e.vid;
+ nh->dev_id = e.stack_dev;
+ } else {
+ e.valid = true;
+ e.is_static = false;
+ e.vid = nh->vid;
+ e.rvid = nh->fid;
+ e.port = RTL930X_PORT_IGNORE;
+ u64_to_ether_addr(nh->mac, &e.mac[0]);
+ }
+ e.next_hop = true;
+ // For nexthop entries, the vid field in the table is used to denote the dest mac_id
+ e.nh_route_id = nh->mac_id;
+ pr_info("%s AFTER\n", __func__);
+ dump_l2_entry(&e);
+
+ rtl930x_write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e);
+
+ // _dal_longan_l2_nexthop_add
+ return 0;
}
static u64 rtl930x_read_mcast_pmask(int idx)
@@ -485,7 +754,6 @@ int rtl930x_read_phy(u32 port, u32 page, u32 reg, u32 *val)
u32 v;
int err = 0;
-// pr_info("In %s\n", __func__);
if (port > 63 || page > 4095 || reg > 31)
return -ENOTSUPP;
@@ -531,12 +799,12 @@ int rtl930x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val)
// Set MMD device number and register to write to
sw_w32(devnum << 16 | (regnum & 0xffff), RTL930X_SMI_ACCESS_PHY_CTRL_3);
- v = BIT(2)| BIT(1)| BIT(0); // WRITE | MMD-access | EXEC
+ v = BIT(2) | BIT(1) | BIT(0); // WRITE | MMD-access | EXEC
sw_w32(v, RTL930X_SMI_ACCESS_PHY_CTRL_1);
do {
v = sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_1);
- } while ( v & BIT(0));
+ } while (v & BIT(0));
pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, val, err);
mutex_unlock(&smi_lock);
@@ -559,12 +827,12 @@ int rtl930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val)
// Set MMD device number and register to write to
sw_w32(devnum << 16 | (regnum & 0xffff), RTL930X_SMI_ACCESS_PHY_CTRL_3);
- v = BIT(1)| BIT(0); // MMD-access | EXEC
+ v = BIT(1) | BIT(0); // MMD-access | EXEC
sw_w32(v, RTL930X_SMI_ACCESS_PHY_CTRL_1);
do {
v = sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_1);
- } while ( v & 0x1);
+ } while (v & BIT(0));
// There is no error-checking via BIT 25 of v, as it does not seem to be set correctly
*val = (sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_2) & 0xffff);
pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, *val, err);
@@ -574,7 +842,6 @@ int rtl930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val)
return err;
}
-
/*
* Calculate both the block 0 and the block 1 hash, and return in
* lower and higher word of the return value since only 12 bit of
@@ -617,6 +884,7 @@ u32 rtl930x_hash(struct rtl838x_switch_priv *priv, u64 seed)
return h;
}
+
/*
* Enables or disables the EEE/EEEP capability of a port
*/
@@ -754,7 +1022,9 @@ const struct rtl838x_reg rtl930x_reg = {
.mac_rx_pause_sts = RTL930X_MAC_RX_PAUSE_STS,
.mac_tx_pause_sts = RTL930X_MAC_TX_PAUSE_STS,
.read_l2_entry_using_hash = rtl930x_read_l2_entry_using_hash,
+ .write_l2_entry_using_hash = rtl930x_write_l2_entry_using_hash,
.read_cam = rtl930x_read_cam,
+ .write_cam = rtl930x_write_cam,
.vlan_port_egr_filter = RTL930X_VLAN_PORT_EGR_FLTR,
.vlan_port_igr_filter = RTL930X_VLAN_PORT_IGR_FLTR(0),
.vlan_port_pb = RTL930X_VLAN_PORT_PB_VLAN,