X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=target%2Flinux%2Frealtek%2Ffiles-5.4%2Fdrivers%2Fnet%2Fdsa%2Frtl83xx%2Frtl930x.c;fp=target%2Flinux%2Frealtek%2Ffiles-5.4%2Fdrivers%2Fnet%2Fdsa%2Frtl83xx%2Frtl930x.c;h=820c78165a3c8677697fb25349cc10c4aff91c3f;hb=cde31976e37513cf2977e6d4b691f561344e61ed;hp=a80a6d89b29edf5699b7de8a252f03db59088c31;hpb=4342d27ec90cd0988fd3e62ccefbe66f2e691372;p=openwrt%2Fstaging%2Fdedeckeh.git 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,