// SPDX-License-Identifier: GPL-2.0-only #include #include "rtl83xx.h" extern struct mutex smi_lock; static inline void rtl838x_mask_port_reg(u64 clear, u64 set, int reg) { sw_w32_mask((u32)clear, (u32)set, reg); } static inline void rtl838x_set_port_reg(u64 set, int reg) { sw_w32(set, reg); } static inline u64 rtl838x_get_port_reg(int reg) { return ((u64) sw_r32(reg)); } static inline int rtl838x_stat_port_std_mib(int p) { return RTL838X_STAT_PORT_STD_MIB + (p << 8); } static inline int rtl838x_port_iso_ctrl(int p) { return RTL838X_PORT_ISO_CTRL(p); } static inline void rtl838x_exec_tbl0_cmd(u32 cmd) { sw_w32(cmd, RTL838X_TBL_ACCESS_CTRL_0); do { } while (sw_r32(RTL838X_TBL_ACCESS_CTRL_0) & BIT(15)); } static inline void rtl838x_exec_tbl1_cmd(u32 cmd) { sw_w32(cmd, RTL838X_TBL_ACCESS_CTRL_1); do { } while (sw_r32(RTL838X_TBL_ACCESS_CTRL_1) & BIT(15)); } static inline int rtl838x_tbl_access_data_0(int i) { return RTL838X_TBL_ACCESS_DATA_0(i); } static void rtl838x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info) { u32 cmd, v; cmd = BIT(15) /* Execute cmd */ | BIT(14) /* Read */ | 0 << 12 /* Table type 0b00 */ | (vlan & 0xfff); rtl838x_exec_tbl0_cmd(cmd); info->tagged_ports = sw_r32(RTL838X_TBL_ACCESS_DATA_0(0)); v = sw_r32(RTL838X_TBL_ACCESS_DATA_0(1)); info->profile_id = v & 0x7; info->hash_mc_fid = !!(v & 0x8); info->hash_uc_fid = !!(v & 0x10); info->fid = (v >> 5) & 0x3f; cmd = BIT(15) /* Execute cmd */ | BIT(14) /* Read */ | 0 << 12 /* Table type 0b00 */ | (vlan & 0xfff); rtl838x_exec_tbl1_cmd(cmd); info->untagged_ports = sw_r32(RTL838X_TBL_ACCESS_DATA_1(0)); } static void rtl838x_vlan_set_tagged(u32 vlan, struct rtl838x_vlan_info *info) { u32 cmd = BIT(15) /* Execute cmd */ | 0 << 14 /* Write */ | 0 << 12 /* Table type 0b00 */ | (vlan & 0xfff); u32 v; sw_w32(info->tagged_ports, RTL838X_TBL_ACCESS_DATA_0(0)); v = info->profile_id; v |= info->hash_mc_fid ? 0x8 : 0; v |= info->hash_uc_fid ? 0x10 : 0; v |= ((u32)info->fid) << 5; sw_w32(v, RTL838X_TBL_ACCESS_DATA_0(1)); rtl838x_exec_tbl0_cmd(cmd); } static void rtl838x_vlan_set_untagged(u32 vlan, u64 portmask) { u32 cmd = BIT(15) /* Execute cmd */ | 0 << 14 /* Write */ | 0 << 12 /* Table type 0b00 */ | (vlan & 0xfff); sw_w32(portmask & 0x1fffffff, RTL838X_TBL_ACCESS_DATA_1(0)); rtl838x_exec_tbl1_cmd(cmd); } static inline int rtl838x_mac_force_mode_ctrl(int p) { return RTL838X_MAC_FORCE_MODE_CTRL + (p << 2); } static inline int rtl838x_mac_port_ctrl(int p) { return RTL838X_MAC_PORT_CTRL(p); } static inline int rtl838x_l2_port_new_salrn(int p) { return RTL838X_L2_PORT_NEW_SALRN(p); } static inline int rtl838x_l2_port_new_sa_fwd(int p) { return RTL838X_L2_PORT_NEW_SA_FWD(p); } static inline int rtl838x_mir_ctrl(int group) { return RTL838X_MIR_CTRL(group); } static inline int rtl838x_mir_dpm(int group) { return RTL838X_MIR_DPM_CTRL(group); } static inline int rtl838x_mir_spm(int group) { return RTL838X_MIR_SPM_CTRL(group); } static inline int rtl838x_mac_link_spd_sts(int p) { return RTL838X_MAC_LINK_SPD_STS(p); } static u64 rtl838x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e) { u64 entry; u32 r[3]; /* 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]); entry = (((u64) r[1]) << 32) | (r[2] & 0xfffff000) | (r[0] & 0xfff); return entry; } static u64 rtl838x_read_cam(int idx, struct rtl838x_l2_entry *e) { u64 entry; u32 r[3]; 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]); entry = (((u64) r[1]) << 32) | (r[2] & 0xfffff000) | (r[0] & 0xfff); return entry; } static inline int rtl838x_vlan_profile(int profile) { return RTL838X_VLAN_PROFILE(profile); } static inline int rtl838x_vlan_port_egr_filter(int port) { return RTL838X_VLAN_PORT_EGR_FLTR; } static inline int rtl838x_vlan_port_igr_filter(int port) { return RTL838X_VLAN_PORT_IGR_FLTR(port); } static inline int rtl838x_vlan_port_pb(int port) { return RTL838X_VLAN_PORT_PB_VLAN(port); } static inline int rtl838x_vlan_port_tag_sts_ctrl(int port) { return RTL838X_VLAN_PORT_TAG_STS_CTRL(port); } const struct rtl838x_reg rtl838x_reg = { .mask_port_reg_be = rtl838x_mask_port_reg, .set_port_reg_be = rtl838x_set_port_reg, .get_port_reg_be = rtl838x_get_port_reg, .mask_port_reg_le = rtl838x_mask_port_reg, .set_port_reg_le = rtl838x_set_port_reg, .get_port_reg_le = rtl838x_get_port_reg, .stat_port_rst = RTL838X_STAT_PORT_RST, .stat_rst = RTL838X_STAT_RST, .stat_port_std_mib = rtl838x_stat_port_std_mib, .port_iso_ctrl = rtl838x_port_iso_ctrl, .l2_ctrl_0 = RTL838X_L2_CTRL_0, .l2_ctrl_1 = RTL838X_L2_CTRL_1, .l2_port_aging_out = RTL838X_L2_PORT_AGING_OUT, .smi_poll_ctrl = RTL838X_SMI_POLL_CTRL, .l2_tbl_flush_ctrl = RTL838X_L2_TBL_FLUSH_CTRL, .exec_tbl0_cmd = rtl838x_exec_tbl0_cmd, .exec_tbl1_cmd = rtl838x_exec_tbl1_cmd, .tbl_access_data_0 = rtl838x_tbl_access_data_0, .isr_glb_src = RTL838X_ISR_GLB_SRC, .isr_port_link_sts_chg = RTL838X_ISR_PORT_LINK_STS_CHG, .imr_port_link_sts_chg = RTL838X_IMR_PORT_LINK_STS_CHG, .imr_glb = RTL838X_IMR_GLB, .vlan_tables_read = rtl838x_vlan_tables_read, .vlan_set_tagged = rtl838x_vlan_set_tagged, .vlan_set_untagged = rtl838x_vlan_set_untagged, .mac_force_mode_ctrl = rtl838x_mac_force_mode_ctrl, .mac_port_ctrl = rtl838x_mac_port_ctrl, .l2_port_new_salrn = rtl838x_l2_port_new_salrn, .l2_port_new_sa_fwd = rtl838x_l2_port_new_sa_fwd, .mir_ctrl = rtl838x_mir_ctrl, .mir_dpm = rtl838x_mir_dpm, .mir_spm = rtl838x_mir_spm, .mac_link_sts = RTL838X_MAC_LINK_STS, .mac_link_dup_sts = RTL838X_MAC_LINK_DUP_STS, .mac_link_spd_sts = rtl838x_mac_link_spd_sts, .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, .read_cam = rtl838x_read_cam, .vlan_profile = rtl838x_vlan_profile, .vlan_port_egr_filter = rtl838x_vlan_port_egr_filter, .vlan_port_igr_filter = rtl838x_vlan_port_igr_filter, .vlan_port_pb = rtl838x_vlan_port_pb, .vlan_port_tag_sts_ctrl = rtl838x_vlan_port_tag_sts_ctrl, }; irqreturn_t rtl838x_switch_irq(int irq, void *dev_id) { struct dsa_switch *ds = dev_id; u32 status = sw_r32(RTL838X_ISR_GLB_SRC); u32 ports = sw_r32(RTL838X_ISR_PORT_LINK_STS_CHG); u32 link; int i; /* Clear status */ sw_w32(ports, RTL838X_ISR_PORT_LINK_STS_CHG); pr_debug("RTL8380 Link change: status: %x, ports %x\n", status, ports); for (i = 0; i < 28; i++) { if (ports & BIT(i)) { link = sw_r32(RTL838X_MAC_LINK_STS); if (link & BIT(i)) dsa_port_phylink_mac_change(ds, i, true); else dsa_port_phylink_mac_change(ds, i, false); } } return IRQ_HANDLED; } 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; } /* * Reads a register in a page from the PHY */ int rtl838x_read_phy(u32 port, u32 page, u32 reg, u32 *val) { u32 v; u32 park_page; if (port > 31) { *val = 0xffff; return 0; } if (page > 4095 || reg > 31) return -ENOTSUPP; mutex_lock(&smi_lock); if (rtl838x_smi_wait_op(10000)) goto timeout; sw_w32_mask(0xffff0000, port << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2); park_page = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_1) & ((0x1f << 15) | 0x2); v = reg << 20 | page << 3; 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)) goto timeout; *val = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_2) & 0xffff; mutex_unlock(&smi_lock); return 0; timeout: mutex_unlock(&smi_lock); return -ETIMEDOUT; } /* * Write to a register in a page of the PHY */ int rtl838x_write_phy(u32 port, u32 page, u32 reg, u32 val) { u32 v; u32 park_page; val &= 0xffff; if (port > 31 || page > 4095 || reg > 31) return -ENOTSUPP; mutex_lock(&smi_lock); if (rtl838x_smi_wait_op(10000)) goto timeout; sw_w32(BIT(port), RTL838X_SMI_ACCESS_PHY_CTRL_0); mdelay(10); sw_w32_mask(0xffff0000, val << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2); park_page = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_1) & ((0x1f << 15) | 0x2); v = reg << 20 | page << 3 | 0x4; 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)) goto timeout; mutex_unlock(&smi_lock); return 0; timeout: mutex_unlock(&smi_lock); return -ETIMEDOUT; } void rtl8380_get_version(struct rtl838x_switch_priv *priv) { u32 rw_save, info_save; u32 info; rw_save = sw_r32(RTL838X_INT_RW_CTRL); sw_w32(rw_save | 0x3, RTL838X_INT_RW_CTRL); info_save = sw_r32(RTL838X_CHIP_INFO); sw_w32(info_save | 0xA0000000, RTL838X_CHIP_INFO); info = sw_r32(RTL838X_CHIP_INFO); sw_w32(info_save, RTL838X_CHIP_INFO); sw_w32(rw_save, RTL838X_INT_RW_CTRL); if ((info & 0xFFFF) == 0x6275) { if (((info >> 16) & 0x1F) == 0x1) priv->version = RTL8380_VERSION_A; else if (((info >> 16) & 0x1F) == 0x2) priv->version = RTL8380_VERSION_B; else priv->version = RTL8380_VERSION_B; } else { priv->version = '-'; } } /* * 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 index) { u32 profile; if (index < 0 || index > 7) return; profile = sw_r32(RTL838X_VLAN_PROFILE(index)); pr_debug("VLAN %d: L2 learning: %d, L2 Unknown MultiCast Field %x, \ IPv4 Unknown MultiCast Field %x, IPv6 Unknown MultiCast Field: %x", index, profile & 1, (profile >> 1) & 0x1ff, (profile >> 10) & 0x1ff, (profile >> 19) & 0x1ff); }