aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c')
-rw-r--r--target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c326
1 files changed, 326 insertions, 0 deletions
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c
new file mode 100644
index 0000000000..a33941a0eb
--- /dev/null
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <asm/mach-rtl838x/mach-rtl83xx.h>
+#include "rtl83xx.h"
+
+extern struct mutex smi_lock;
+extern struct rtl83xx_soc_info soc_info;
+
+inline void rtl931x_exec_tbl0_cmd(u32 cmd)
+{
+ sw_w32(cmd, RTL931X_TBL_ACCESS_CTRL_0);
+ do { } while (sw_r32(RTL931X_TBL_ACCESS_CTRL_0) & (1 << 20));
+}
+
+inline void rtl931x_exec_tbl1_cmd(u32 cmd)
+{
+ sw_w32(cmd, RTL931X_TBL_ACCESS_CTRL_1);
+ do { } while (sw_r32(RTL931X_TBL_ACCESS_CTRL_1) & (1 << 17));
+}
+
+inline int rtl931x_tbl_access_data_0(int i)
+{
+ return RTL931X_TBL_ACCESS_DATA_0(i);
+}
+
+void rtl931x_vlan_profile_dump(int index)
+{
+ u64 profile[4];
+
+ if (index < 0 || index > 15)
+ return;
+
+ profile[0] = sw_r32(RTL931X_VLAN_PROFILE_SET(index));
+ profile[1] = (sw_r32(RTL931X_VLAN_PROFILE_SET(index) + 4) & 0x1FFFFFFFULL) << 32
+ | (sw_r32(RTL931X_VLAN_PROFILE_SET(index) + 8) & 0xFFFFFFFF);
+ profile[2] = (sw_r32(RTL931X_VLAN_PROFILE_SET(index) + 16) & 0xFFFFFFFFULL) << 32
+ | (sw_r32(RTL931X_VLAN_PROFILE_SET(index) + 12) & 0x1FFFFFFULL);
+ profile[3] = (sw_r32(RTL931X_VLAN_PROFILE_SET(index) + 20) & 0x1FFFFFFFULL) << 32
+ | (sw_r32(RTL931X_VLAN_PROFILE_SET(index) + 24) & 0xFFFFFFFF);
+
+ pr_info("VLAN %d: L2 learning: %d, L2 Unknown MultiCast Field %llx, \
+ IPv4 Unknown MultiCast Field %llx, IPv6 Unknown MultiCast Field: %llx",
+ index, (u32) (profile[0] & (3 << 14)), profile[1], profile[2], profile[3]);
+}
+
+static void rtl931x_stp_get(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[])
+{
+ int i;
+ u32 cmd = 1 << 20 /* Execute cmd */
+ | 0 << 19 /* Read */
+ | 2 << 15 /* Table type 0b10 */
+ | (msti & 0x3fff);
+ priv->r->exec_tbl0_cmd(cmd);
+
+ for (i = 0; i < 4; i++)
+ port_state[i] = sw_r32(priv->r->tbl_access_data_0(i));
+}
+
+static void rtl931x_stp_set(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[])
+{
+ int i;
+ u32 cmd = 1 << 20 /* Execute cmd */
+ | 1 << 19 /* Write */
+ | 5 << 15 /* Table type 0b101 */
+ | (msti & 0x3fff);
+ for (i = 0; i < 4; i++)
+ sw_w32(port_state[i], priv->r->tbl_access_data_0(i));
+ priv->r->exec_tbl0_cmd(cmd);
+}
+
+inline static int rtl931x_trk_mbr_ctr(int group)
+{
+ return RTL931X_TRK_MBR_CTRL + (group << 2);
+}
+
+static void rtl931x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info)
+{
+ u32 v, w, x, y;
+ // Read VLAN table (3) via register 0
+ struct table_reg *r = rtl_table_get(RTL9310_TBL_0, 3);
+
+ rtl_table_read(r, vlan);
+ v = sw_r32(rtl_table_data(r, 0));
+ w = sw_r32(rtl_table_data(r, 1));
+ x = sw_r32(rtl_table_data(r, 2));
+ y = sw_r32(rtl_table_data(r, 3));
+ pr_debug("VLAN_READ %d: %08x %08x\n", vlan, v, w);
+ rtl_table_release(r);
+
+ info->tagged_ports = ((u64) v) << 25 | (w >> 7);
+ info->profile_id = (x >> 16) & 0xf;
+ info->hash_mc_fid = !!(x & BIT(30));
+ info->hash_uc_fid = !!(x & BIT(31));
+ info->fid = w & 0x7f;
+ // TODO: use also info in 4th register
+
+ // Read UNTAG table via table register 3
+ r = rtl_table_get(RTL9310_TBL_3, 0);
+ rtl_table_read(r, vlan);
+ v = ((u64)sw_r32(rtl_table_data(r, 0))) << 25;
+ v |= sw_r32(rtl_table_data(r, 1)) >> 7;
+ rtl_table_release(r);
+
+ info->untagged_ports = v;
+}
+
+static void rtl931x_vlan_set_tagged(u32 vlan, struct rtl838x_vlan_info *info)
+{
+ u32 v, w, x;
+ // Access VLAN table (1) via register 0
+ struct table_reg *r = rtl_table_get(RTL9310_TBL_0, 3);
+
+ v = info->tagged_ports << 7;
+ w = (info->tagged_ports & 0x7f000000) << 25;
+ w |= (u32)info->fid;
+ x = info->profile_id << 16;
+ w |= info->hash_mc_fid ? BIT(30) : 0;
+ w |= info->hash_uc_fid ? BIT(31) : 0;
+ // TODO: use also info in 4th register
+
+ sw_w32(v, rtl_table_data(r, 0));
+ sw_w32(w, rtl_table_data(r, 1));
+ sw_w32(x, rtl_table_data(r, 2));
+
+ rtl_table_write(r, vlan);
+ rtl_table_release(r);
+}
+
+static void rtl931x_vlan_set_untagged(u32 vlan, u64 portmask)
+{
+ struct table_reg *r = rtl_table_get(RTL9310_TBL_3, 0);
+
+ rtl839x_set_port_reg_be(portmask << 7, rtl_table_data(r, 0));
+ rtl_table_write(r, vlan);
+ rtl_table_release(r);
+}
+
+static inline int rtl931x_mac_force_mode_ctrl(int p)
+{
+ return RTL931X_MAC_FORCE_MODE_CTRL + (p << 2);
+}
+
+static inline int rtl931x_mac_link_spd_sts(int p)
+{
+ return RTL931X_MAC_LINK_SPD_STS(p);
+}
+
+static inline int rtl931x_mac_port_ctrl(int p)
+{
+ return RTL931X_MAC_PORT_CTRL(p);
+}
+
+static inline int rtl931x_l2_port_new_salrn(int p)
+{
+ return RTL931X_L2_PORT_NEW_SALRN(p);
+}
+
+static inline int rtl931x_l2_port_new_sa_fwd(int p)
+{
+ return RTL931X_L2_PORT_NEW_SA_FWD(p);
+}
+
+static u64 rtl931x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e)
+{
+ u64 entry = 0;
+
+ // TODO: Implement
+ return entry;
+}
+
+static u64 rtl931x_read_cam(int idx, struct rtl838x_l2_entry *e)
+{
+ u64 entry = 0;
+
+ // TODO: Implement
+ return entry;
+}
+irqreturn_t rtl931x_switch_irq(int irq, void *dev_id)
+{
+ struct dsa_switch *ds = dev_id;
+ u32 status = sw_r32(RTL931X_ISR_GLB_SRC);
+ u64 ports = rtl839x_get_port_reg_le(RTL931X_ISR_PORT_LINK_STS_CHG);
+ u64 link;
+ int i;
+
+ /* Clear status */
+ rtl839x_set_port_reg_le(ports, RTL931X_ISR_PORT_LINK_STS_CHG);
+ pr_info("RTL9310 Link change: status: %x, ports %llx\n", status, ports);
+
+ for (i = 0; i < 56; i++) {
+ if (ports & BIT_ULL(i)) {
+ link = rtl839x_get_port_reg_le(RTL931X_MAC_LINK_STS);
+ if (link & BIT_ULL(i))
+ dsa_port_phylink_mac_change(ds, i, true);
+ else
+ dsa_port_phylink_mac_change(ds, i, false);
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+
+int rtl931x_write_phy(u32 port, u32 page, u32 reg, u32 val)
+{
+ u32 v;
+ int err = 0;
+
+ val &= 0xffff;
+ if (port > 63 || page > 4095 || reg > 31)
+ return -ENOTSUPP;
+
+ mutex_lock(&smi_lock);
+ /* Clear both port registers */
+ sw_w32(0, RTL931X_SMI_INDRT_ACCESS_CTRL_2);
+ sw_w32(0, RTL931X_SMI_INDRT_ACCESS_CTRL_2 + 4);
+ sw_w32_mask(0, BIT(port), RTL931X_SMI_INDRT_ACCESS_CTRL_2+ (port % 32) * 4);
+
+ sw_w32_mask(0xffff0000, val << 16, RTL931X_SMI_INDRT_ACCESS_CTRL_3);
+
+ v = reg << 6 | page << 11 ;
+ sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0);
+
+ sw_w32(0x1ff, RTL931X_SMI_INDRT_ACCESS_CTRL_1);
+
+ v |= 1 << 3 | 1; /* Write operation and execute */
+ sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0);
+
+ do {
+ } while (sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0) & 0x1);
+
+ if (sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0) & 0x2)
+ err = -EIO;
+
+ mutex_unlock(&smi_lock);
+ return err;
+}
+
+int rtl931x_read_phy(u32 port, u32 page, u32 reg, u32 *val)
+{
+ u32 v;
+
+ if (port > 63 || page > 4095 || reg > 31)
+ return -ENOTSUPP;
+
+ mutex_lock(&smi_lock);
+
+ sw_w32_mask(0xffff, port, RTL931X_SMI_INDRT_ACCESS_CTRL_3);
+ v = reg << 6 | page << 11; // TODO: ACCESS Offset? Park page
+ sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0);
+
+ sw_w32(0x1ff, RTL931X_SMI_INDRT_ACCESS_CTRL_1);
+
+ v |= 1;
+ sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0);
+
+ do {
+ } while (sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0) & 0x1);
+
+ *val = (sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_3) & 0xffff0000) >> 16;
+
+ pr_info("%s: port %d, page: %d, reg: %x, val: %x\n", __func__, port, page, reg, *val);
+
+ mutex_unlock(&smi_lock);
+ return 0;
+}
+
+void rtl931x_print_matrix(void)
+{
+ volatile u64 *ptr = RTL838X_SW_BASE + RTL839X_PORT_ISO_CTRL(0);
+ int i;
+
+ for (i = 0; i < 52; i += 4)
+ pr_info("> %16llx %16llx %16llx %16llx\n",
+ ptr[i + 0], ptr[i + 1], ptr[i + 2], ptr[i + 3]);
+ pr_info("CPU_PORT> %16llx\n", ptr[52]);
+}
+
+const struct rtl838x_reg rtl931x_reg = {
+ .mask_port_reg_be = rtl839x_mask_port_reg_be,
+ .set_port_reg_be = rtl839x_set_port_reg_be,
+ .get_port_reg_be = rtl839x_get_port_reg_be,
+ .mask_port_reg_le = rtl839x_mask_port_reg_le,
+ .set_port_reg_le = rtl839x_set_port_reg_le,
+ .get_port_reg_le = rtl839x_get_port_reg_le,
+ .stat_port_rst = RTL931X_STAT_PORT_RST,
+ .stat_rst = RTL931X_STAT_RST,
+ .stat_port_std_mib = 0, // Not defined
+ .l2_ctrl_0 = RTL931X_L2_CTRL,
+ .l2_ctrl_1 = RTL931X_L2_AGE_CTRL,
+ .l2_port_aging_out = RTL931X_L2_PORT_AGE_CTRL,
+ // .smi_poll_ctrl does not exist
+ .l2_tbl_flush_ctrl = RTL931X_L2_TBL_FLUSH_CTRL,
+ .exec_tbl0_cmd = rtl931x_exec_tbl0_cmd,
+ .exec_tbl1_cmd = rtl931x_exec_tbl1_cmd,
+ .tbl_access_data_0 = rtl931x_tbl_access_data_0,
+ .isr_glb_src = RTL931X_ISR_GLB_SRC,
+ .isr_port_link_sts_chg = RTL931X_ISR_PORT_LINK_STS_CHG,
+ .imr_port_link_sts_chg = RTL931X_IMR_PORT_LINK_STS_CHG,
+ // imr_glb does not exist on RTL931X
+ .vlan_tables_read = rtl931x_vlan_tables_read,
+ .vlan_set_tagged = rtl931x_vlan_set_tagged,
+ .vlan_set_untagged = rtl931x_vlan_set_untagged,
+ .vlan_profile_dump = rtl931x_vlan_profile_dump,
+ .stp_get = rtl931x_stp_get,
+ .stp_set = rtl931x_stp_set,
+ .mac_force_mode_ctrl = rtl931x_mac_force_mode_ctrl,
+ .mac_port_ctrl = rtl931x_mac_port_ctrl,
+ .l2_port_new_salrn = rtl931x_l2_port_new_salrn,
+ .l2_port_new_sa_fwd = rtl931x_l2_port_new_sa_fwd,
+ .mir_ctrl = RTL931X_MIR_CTRL,
+ .mir_dpm = RTL931X_MIR_DPM_CTRL,
+ .mir_spm = RTL931X_MIR_SPM_CTRL,
+ .mac_link_sts = RTL931X_MAC_LINK_STS,
+ .mac_link_dup_sts = RTL931X_MAC_LINK_DUP_STS,
+ .mac_link_spd_sts = rtl931x_mac_link_spd_sts,
+ .mac_rx_pause_sts = RTL931X_MAC_RX_PAUSE_STS,
+ .mac_tx_pause_sts = RTL931X_MAC_TX_PAUSE_STS,
+ .read_l2_entry_using_hash = rtl931x_read_l2_entry_using_hash,
+ .read_cam = rtl931x_read_cam,
+ .vlan_port_egr_filter = RTL931X_VLAN_PORT_EGR_FLTR(0),
+ .vlan_port_igr_filter = RTL931X_VLAN_PORT_IGR_FLTR(0),
+// .vlan_port_pb = does not exist
+ .vlan_port_tag_sts_ctrl = RTL931X_VLAN_PORT_TAG_CTRL,
+ .trk_mbr_ctr = rtl931x_trk_mbr_ctr,
+};
+