// SPDX-License-Identifier: GPL-2.0-only
#include <asm/mach-rtl838x/mach-rtl83xx.h>
+#include <net/nexthop.h>
+
#include "rtl83xx.h"
extern struct mutex smi_lock;
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");
+ pr_debug("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->vid = e->rvid;
}
e->age = (r[0] >> 17) & 0x3;
e->valid = true;
else
e->type = L2_UNICAST;
} else { // L2 multicast
- pr_info("Got L2 MC entry: %08x %08x %08x\n", r[0], r[1], r[2]);
+ pr_debug("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;
+ e->mc_gip = (r[1] << 20) | (r[2] >> 12);
+ e->rvid = r[2] & 0xfff;
}
if (e->is_ip_mc)
e->type = IP4_MULTICAST;
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->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]);
+ pr_debug("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;
+ r[0] |= (e->mc_portmask_index & 0x1ff) << 12;
+ r[1] = e->mc_gip >> 20;
+ r[2] = e->mc_gip << 12;
+ r[2] |= e->rvid;
}
}
if (!e->valid)
return 0;
- entry = (((u64) r[1]) << 32) | (r[2] & 0xfffff000) | (r[0] & 0xfff);
+ entry = (((u64) r[1]) << 32) | (r[2]); // mac and vid concatenated as hash seed
return entry;
}
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);
+ entry = (((u64) r[1]) << 32) | r[2];
return entry;
}
rtl838x_write_mcast_pmask(UNKNOWN_MC_PMASK, 0x1fffffff);
}
-static inline int rtl838x_vlan_port_egr_filter(int port)
+static void rtl838x_l2_learning_setup(void)
+{
+ /* Set portmask for broadcast traffic and unknown unicast address flooding
+ * to the reserved entry in the portmask table used also for
+ * multicast flooding */
+ sw_w32(UNKNOWN_MC_PMASK << 12 | UNKNOWN_MC_PMASK, RTL838X_L2_FLD_PMSK);
+
+ /* Enable learning constraint system-wide (bit 0), per-port (bit 1)
+ * and per vlan (bit 2) */
+ sw_w32(0x7, RTL838X_L2_LRN_CONSTRT_EN);
+
+ // Limit learning to maximum: 16k entries, after that just flood (bits 0-1)
+ sw_w32((0x3fff << 2) | 0, RTL838X_L2_LRN_CONSTRT);
+
+ // Do not trap ARP packets to CPU_PORT
+ sw_w32(0, RTL838X_SPCL_TRAP_ARP_CTRL);
+}
+
+static void rtl838x_enable_learning(int port, bool enable)
+{
+ // Limit learning to maximum: 32k entries, after that just flood (bits 0-1)
+
+ if (enable) {
+ // flood after 32k entries
+ sw_w32((0x3fff << 2) | 0, RTL838X_L2_PORT_LRN_CONSTRT + (port << 2));
+ } else {
+ // just forward
+ sw_w32(0, RTL838X_L2_PORT_LRN_CONSTRT + (port << 2));
+ }
+}
+
+static void rtl838x_enable_flood(int port, bool enable)
{
- return RTL838X_VLAN_PORT_EGR_FLTR;
+ u32 flood_mask = sw_r32(RTL838X_L2_PORT_LRN_CONSTRT + (port << 2));
+
+ if (enable) {
+ // flood
+ flood_mask &= ~3;
+ flood_mask |= 0;
+ sw_w32(flood_mask, RTL838X_L2_PORT_LRN_CONSTRT + (port << 2));
+ } else {
+ // drop (bit 1)
+ flood_mask &= ~3;
+ flood_mask |= 1;
+ sw_w32(flood_mask, RTL838X_L2_PORT_LRN_CONSTRT + (port << 2));
+ }
}
-static inline int rtl838x_vlan_port_igr_filter(int port)
+static void rtl838x_enable_mcast_flood(int port, bool enable)
{
- return RTL838X_VLAN_PORT_IGR_FLTR(port);
+
+}
+
+static void rtl838x_enable_bcast_flood(int port, bool enable)
+{
+
}
static void rtl838x_stp_get(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[])
rtl_table_release(r);
}
+static void rtl838x_route_read(int idx, struct rtl83xx_route *rt)
+{
+ // Read ROUTING table (2) via register RTL8380_TBL_1
+ struct table_reg *r = rtl_table_get(RTL8380_TBL_1, 2);
+
+ pr_debug("In %s, id %d\n", __func__, idx);
+ rtl_table_read(r, idx);
+
+ // The table has a size of 2 registers
+ rt->nh.gw = sw_r32(rtl_table_data(r, 0));
+ rt->nh.gw <<= 32;
+ rt->nh.gw |= sw_r32(rtl_table_data(r, 1));
+
+ rtl_table_release(r);
+}
+
+static void rtl838x_route_write(int idx, struct rtl83xx_route *rt)
+{
+ // Access ROUTING table (2) via register RTL8380_TBL_1
+ struct table_reg *r = rtl_table_get(RTL8380_TBL_1, 2);
+
+ pr_debug("In %s, id %d, gw: %016llx\n", __func__, idx, rt->nh.gw);
+ sw_w32(rt->nh.gw >> 32, rtl_table_data(r, 0));
+ sw_w32(rt->nh.gw, rtl_table_data(r, 1));
+ rtl_table_write(r, idx);
+
+ rtl_table_release(r);
+}
+
+static int rtl838x_l3_setup(struct rtl838x_switch_priv *priv)
+{
+ // Nothing to be done
+ return 0;
+}
+
+void rtl838x_vlan_port_pvidmode_set(int port, enum pbvlan_type type, enum pbvlan_mode mode)
+{
+ if (type == PBVLAN_TYPE_INNER)
+ sw_w32_mask(0x3, mode, RTL838X_VLAN_PORT_PB_VLAN + (port << 2));
+ else
+ sw_w32_mask(0x3 << 14, mode << 14, RTL838X_VLAN_PORT_PB_VLAN + (port << 2));
+}
+
+void rtl838x_vlan_port_pvid_set(int port, enum pbvlan_type type, int pvid)
+{
+ if (type == PBVLAN_TYPE_INNER)
+ sw_w32_mask(0xfff << 2, pvid << 2, RTL838X_VLAN_PORT_PB_VLAN + (port << 2));
+ else
+ sw_w32_mask(0xfff << 16, pvid << 16, RTL838X_VLAN_PORT_PB_VLAN + (port << 2));
+}
+
+static int rtl838x_set_ageing_time(unsigned long msec)
+{
+ int t = sw_r32(RTL838X_L2_CTRL_1);
+
+ t &= 0x7FFFFF;
+ t = t * 128 / 625; /* Aging time in seconds. 0: L2 aging disabled */
+ pr_debug("L2 AGING time: %d sec\n", t);
+
+ t = (msec * 625 + 127000) / 128000;
+ t = t > 0x7FFFFF ? 0x7FFFFF : t;
+ sw_w32_mask(0x7FFFFF, t, RTL838X_L2_CTRL_1);
+ pr_debug("Dynamic aging for ports: %x\n", sw_r32(RTL838X_L2_PORT_AGING_OUT));
+
+ return 0;
+}
+
+static void rtl838x_set_igr_filter(int port, enum igr_filter state)
+{
+ sw_w32_mask(0x3 << ((port & 0xf)<<1), state << ((port & 0xf)<<1),
+ RTL838X_VLAN_PORT_IGR_FLTR + (((port >> 4) << 2)));
+}
+
+static void rtl838x_set_egr_filter(int port, enum egr_filter state)
+{
+ sw_w32_mask(0x1 << (port % 0x1d), state << (port % 0x1d),
+ RTL838X_VLAN_PORT_EGR_FLTR + (((port / 29) << 2)));
+}
+
+void rtl838x_set_distribution_algorithm(int group, int algoidx, u32 algomsk)
+{
+ algoidx &= 1; // RTL838X only supports 2 concurrent algorithms
+ sw_w32_mask(1 << (group % 8), algoidx << (group % 8),
+ RTL838X_TRK_HASH_IDX_CTRL + ((group >> 3) << 2));
+ sw_w32(algomsk, RTL838X_TRK_HASH_CTRL + (algoidx << 2));
+}
+
+void rtl838x_set_receive_management_action(int port, rma_ctrl_t type, action_type_t action)
+{
+ switch(type) {
+ case BPDU:
+ sw_w32_mask(3 << ((port & 0xf) << 1), (action & 0x3) << ((port & 0xf) << 1),
+ RTL838X_RMA_BPDU_CTRL + ((port >> 4) << 2));
+ break;
+ case PTP:
+ sw_w32_mask(3 << ((port & 0xf) << 1), (action & 0x3) << ((port & 0xf) << 1),
+ RTL838X_RMA_PTP_CTRL + ((port >> 4) << 2));
+ break;
+ case LLTP:
+ sw_w32_mask(3 << ((port & 0xf) << 1), (action & 0x3) << ((port & 0xf) << 1),
+ RTL838X_RMA_LLTP_CTRL + ((port >> 4) << 2));
+ break;
+ default:
+ break;
+ }
+}
+
const struct rtl838x_reg rtl838x_reg = {
.mask_port_reg_be = rtl838x_mask_port_reg,
.set_port_reg_be = rtl838x_set_port_reg,
.l2_ctrl_0 = RTL838X_L2_CTRL_0,
.l2_ctrl_1 = RTL838X_L2_CTRL_1,
.l2_port_aging_out = RTL838X_L2_PORT_AGING_OUT,
+ .set_ageing_time = rtl838x_set_ageing_time,
.smi_poll_ctrl = RTL838X_SMI_POLL_CTRL,
.l2_tbl_flush_ctrl = RTL838X_L2_TBL_FLUSH_CTRL,
.exec_tbl0_cmd = rtl838x_exec_tbl0_cmd,
.vlan_profile_dump = rtl838x_vlan_profile_dump,
.vlan_profile_setup = rtl838x_vlan_profile_setup,
.vlan_fwd_on_inner = rtl838x_vlan_fwd_on_inner,
+ .set_vlan_igr_filter = rtl838x_set_igr_filter,
+ .set_vlan_egr_filter = rtl838x_set_egr_filter,
+ .enable_learning = rtl838x_enable_learning,
+ .enable_flood = rtl838x_enable_flood,
+ .enable_mcast_flood = rtl838x_enable_mcast_flood,
+ .enable_bcast_flood = rtl838x_enable_bcast_flood,
.stp_get = rtl838x_stp_get,
.stp_set = rtl838x_stp_set,
.mac_port_ctrl = rtl838x_mac_port_ctrl,
.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,
.vlan_port_tag_sts_ctrl = RTL838X_VLAN_PORT_TAG_STS_CTRL,
+ .vlan_port_pvidmode_set = rtl838x_vlan_port_pvidmode_set,
+ .vlan_port_pvid_set = rtl838x_vlan_port_pvid_set,
.trk_mbr_ctr = rtl838x_trk_mbr_ctr,
.rma_bpdu_fld_pmask = RTL838X_RMA_BPDU_FLD_PMSK,
.spcl_trap_eapol_ctrl = RTL838X_SPCL_TRAP_EAPOL_CTRL,
.pie_rule_write = rtl838x_pie_rule_write,
.pie_rule_add = rtl838x_pie_rule_add,
.pie_rule_rm = rtl838x_pie_rule_rm,
+ .l2_learning_setup = rtl838x_l2_learning_setup,
.packet_cntr_read = rtl838x_packet_cntr_read,
.packet_cntr_clear = rtl838x_packet_cntr_clear,
+ .route_read = rtl838x_route_read,
+ .route_write = rtl838x_route_write,
+ .l3_setup = rtl838x_l3_setup,
+ .set_distribution_algorithm = rtl838x_set_distribution_algorithm,
+ .set_receive_management_action = rtl838x_set_receive_management_action,
};
irqreturn_t rtl838x_switch_irq(int irq, void *dev_id)
int rtl838x_smi_wait_op(int timeout)
{
- do {
- timeout--;
- udelay(10);
- } while ((sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_1) & 0x1) && (timeout >= 0));
- if (timeout <= 0)
- return -1;
- return 0;
+ unsigned long end = jiffies + usecs_to_jiffies(timeout);
+
+ while (1) {
+ if (!(sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_1) & 0x1))
+ return 0;
+
+ if (time_after(jiffies, end))
+ break;
+
+ usleep_range(10, 20);
+ }
+
+ pr_err("rtl838x_smi_wait_op: timeout\n");
+ return -1;
}
/*
mutex_lock(&smi_lock);
- if (rtl838x_smi_wait_op(10000))
+ if (rtl838x_smi_wait_op(100000))
goto timeout;
sw_w32_mask(0xffff0000, port << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2);
sw_w32(v | park_page, RTL838X_SMI_ACCESS_PHY_CTRL_1);
sw_w32_mask(0, 1, RTL838X_SMI_ACCESS_PHY_CTRL_1);
- if (rtl838x_smi_wait_op(10000))
+ if (rtl838x_smi_wait_op(100000))
goto timeout;
*val = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_2) & 0xffff;
return -ENOTSUPP;
mutex_lock(&smi_lock);
- if (rtl838x_smi_wait_op(10000))
+ if (rtl838x_smi_wait_op(100000))
goto timeout;
sw_w32(BIT(port), RTL838X_SMI_ACCESS_PHY_CTRL_0);
sw_w32(v | park_page, RTL838X_SMI_ACCESS_PHY_CTRL_1);
sw_w32_mask(0, 1, RTL838X_SMI_ACCESS_PHY_CTRL_1);
- if (rtl838x_smi_wait_op(10000))
+ if (rtl838x_smi_wait_op(100000))
goto timeout;
mutex_unlock(&smi_lock);
mutex_lock(&smi_lock);
- if (rtl838x_smi_wait_op(10000))
+ if (rtl838x_smi_wait_op(100000))
goto timeout;
sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0);
v = 1 << 1 | 0 << 2 | 1;
sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1);
- if (rtl838x_smi_wait_op(10000))
+ if (rtl838x_smi_wait_op(100000))
goto timeout;
*val = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_2) & 0xffff;
val &= 0xffff;
mutex_lock(&smi_lock);
- if (rtl838x_smi_wait_op(10000))
+ if (rtl838x_smi_wait_op(100000))
goto timeout;
sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0);
v = 1 << 1 | 1 << 2 | 1;
sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1);
- if (rtl838x_smi_wait_op(10000))
+ if (rtl838x_smi_wait_op(100000))
goto timeout;
mutex_unlock(&smi_lock);