realtek: Add SoC-specific VLAN setup routine
authorBirger Koblitz <git@birger-koblitz.de>
Sat, 1 May 2021 09:22:05 +0000 (11:22 +0200)
committerPetr Štetiar <ynezz@true.cz>
Fri, 7 May 2021 05:05:16 +0000 (07:05 +0200)
This adds SoC specific VLAN configuration routines, which
alsoe sets up the portmask table entries that are referred to
in the vlan profiles registers for unknown multicast flooding.

Signed-off-by: Birger Koblitz <git@birger-koblitz.de>
target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c
target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h
target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c
target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c

index 773225b3c40f633d54a1c6f51e19570644d9d649..b31b1ae6baf71e67aebe95d7a86cb8c41c4f182d 100644 (file)
@@ -12,10 +12,10 @@ void rtl838x_print_matrix(void)
 
        ptr8 = RTL838X_SW_BASE + RTL838X_PORT_ISO_CTRL(0);
        for (i = 0; i < 28; i += 8)
-               pr_info("> %8x %8x %8x %8x %8x %8x %8x %8x\n",
+               pr_debug("> %8x %8x %8x %8x %8x %8x %8x %8x\n",
                        ptr8[i + 0], ptr8[i + 1], ptr8[i + 2], ptr8[i + 3],
                        ptr8[i + 4], ptr8[i + 5], ptr8[i + 6], ptr8[i + 7]);
-       pr_info("CPU_PORT> %8x\n", ptr8[28]);
+       pr_debug("CPU_PORT> %8x\n", ptr8[28]);
 }
 
 static inline int rtl838x_port_iso_ctrl(int p)
@@ -42,56 +42,54 @@ static inline int rtl838x_tbl_access_data_0(int i)
 
 static void rtl838x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info)
 {
-       u32 cmd, v;
+       u32 v;
+       // Read VLAN table (0) via register 0
+       struct table_reg *r = rtl_table_get(RTL8380_TBL_0, 0);
+
+       rtl_table_read(r, vlan);
+       info->tagged_ports = sw_r32(rtl_table_data(r, 0));
+       v = sw_r32(rtl_table_data(r, 1));
+       pr_debug("VLAN_READ %d: %016llx %08x\n", vlan, info->tagged_ports, v);
+       rtl_table_release(r);
 
-       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));
+       // Read UNTAG table (0) via table register 1
+       r = rtl_table_get(RTL8380_TBL_1, 0);
+       rtl_table_read(r, vlan);
+       info->untagged_ports = sw_r32(rtl_table_data(r, 0));
+       rtl_table_release(r);
 }
 
 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;
+       // Access VLAN table (0) via register 0
+       struct table_reg *r = rtl_table_get(RTL8380_TBL_0, 0);
 
-       sw_w32(info->tagged_ports, RTL838X_TBL_ACCESS_DATA_0(0));
+       sw_w32(info->tagged_ports, rtl_table_data(r, 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, rtl_table_data(r, 1));
 
-       sw_w32(v, RTL838X_TBL_ACCESS_DATA_0(1));
-       rtl838x_exec_tbl0_cmd(cmd);
+       rtl_table_write(r, vlan);
+       rtl_table_release(r);
 }
 
 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);
+       // Access UNTAG table (0) via register 1
+       struct table_reg *r = rtl_table_get(RTL8380_TBL_1, 0);
+
+       sw_w32(portmask & 0x1fffffff, rtl_table_data(r, 0));
+       rtl_table_write(r, vlan);
+       rtl_table_release(r);
 }
 
 static inline int rtl838x_mac_force_mode_ctrl(int p)
@@ -202,9 +200,43 @@ static u64 rtl838x_read_cam(int idx, struct rtl838x_l2_entry *e)
        return entry;
 }
 
-static inline int rtl838x_vlan_profile(int profile)
+static u64 rtl838x_read_mcast_pmask(int idx)
 {
-       return RTL838X_VLAN_PROFILE(profile);
+       u32 portmask;
+       // Read MC_PMSK (2) via register RTL8380_TBL_L2
+       struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 2);
+
+       rtl_table_read(q, idx);
+       portmask = sw_r32(rtl_table_data(q, 0));
+       rtl_table_release(q);
+
+       return portmask;
+}
+
+static void rtl838x_write_mcast_pmask(int idx, u64 portmask)
+{
+       // Access MC_PMSK (2) via register RTL8380_TBL_L2
+       struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 2);
+
+       sw_w32(((u32)portmask) & 0x1fffffff, rtl_table_data(q, 0));
+       rtl_table_write(q, idx);
+       rtl_table_release(q);
+}
+
+static void rtl838x_vlan_profile_setup(int profile)
+{
+       u32 pmask_id = UNKNOWN_MC_PMASK;
+       // Enable L2 Learning BIT 0, portmask UNKNOWN_MC_PMASK for unknown MC traffic flooding
+       u32 p = 1 | pmask_id << 1 | pmask_id << 10 | pmask_id << 19;
+
+       sw_w32(p, RTL838X_VLAN_PROFILE(profile));
+
+       /* RTL8380 and RTL8390 use an index into the portmask table to set the
+        * unknown multicast portmask, setup a default at a safe location
+        * On RTL93XX, the portmask is directly set in the profile,
+        * see e.g. rtl9300_vlan_profile_setup
+        */
+       rtl838x_write_mcast_pmask(UNKNOWN_MC_PMASK, 0xfffffff);
 }
 
 static inline int rtl838x_vlan_port_egr_filter(int port)
@@ -373,6 +405,7 @@ const struct rtl838x_reg rtl838x_reg = {
        .vlan_set_untagged = rtl838x_vlan_set_untagged,
        .mac_force_mode_ctrl = rtl838x_mac_force_mode_ctrl,
        .vlan_profile_dump = rtl838x_vlan_profile_dump,
+       .vlan_profile_setup = rtl838x_vlan_profile_setup,
        .stp_get = rtl838x_stp_get,
        .stp_set = rtl838x_stp_set,
        .mac_port_ctrl = rtl838x_mac_port_ctrl,
@@ -398,6 +431,8 @@ const struct rtl838x_reg rtl838x_reg = {
        .init_eee = rtl838x_init_eee,
        .port_eee_set = rtl838x_port_eee_set,
        .eee_port_ability = rtl838x_eee_port_ability,
+       .read_mcast_pmask = rtl838x_read_mcast_pmask,
+       .write_mcast_pmask = rtl838x_write_mcast_pmask,
 };
 
 irqreturn_t rtl838x_switch_irq(int irq, void *dev_id)
@@ -641,19 +676,18 @@ u32 rtl838x_hash(struct rtl838x_switch_priv *priv, u64 seed)
        return h;
 }
 
-void rtl838x_vlan_profile_dump(int index)
+void rtl838x_vlan_profile_dump(int profile)
 {
-       u32 profile;
+       u32 p;
 
-       if (index < 0 || index > 7)
+       if (profile < 0 || profile > 7)
                return;
 
-       profile = sw_r32(RTL838X_VLAN_PROFILE(index));
+       p = sw_r32(RTL838X_VLAN_PROFILE(profile));
 
-       pr_info("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);
+       pr_info("VLAN profile %d: L2 learning: %d, UNKN L2MC FLD PMSK %d, \
+               UNKN IPMC FLD PMSK %d, UNKN IPv6MC FLD PMSK: %d",
+               profile, p & 1, (p >> 1) & 0x1ff, (p >> 10) & 0x1ff, (p >> 19) & 0x1ff);
 }
 
 void rtl8380_sds_rst(int mac)
index 928a23af7777d4564201cd5c6ca9319599900efe..35d5b1702a256fcf4d70f5398b951a37ed127bc6 100644 (file)
 /* Debug features */
 #define RTL930X_STAT_PRVTE_DROP_COUNTER0       (0xB5B8)
 
+#define MAX_VLANS 4096
 #define MAX_LAGS 16
 #define MAX_PRIOS 8
+#define MAX_MC_GROUPS 512
+#define UNKNOWN_MC_PMASK (MAX_MC_GROUPS - 1)
 
 enum phy_type {
        PHY_NONE = 0,
@@ -426,6 +429,7 @@ struct rtl838x_reg {
        void (*vlan_set_tagged)(u32 vlan, struct rtl838x_vlan_info *info);
        void (*vlan_set_untagged)(u32 vlan, u64 portmask);
        void (*vlan_profile_dump)(int index);
+       void (*vlan_profile_setup)(int profile);
        void (*stp_get)(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]);
        void (*stp_set)(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]);
        int  (*mac_force_mode_ctrl)(int port);
@@ -454,6 +458,8 @@ struct rtl838x_reg {
        void (*port_eee_set)(struct rtl838x_switch_priv *priv, int port, bool enable);
        int (*eee_port_ability)(struct rtl838x_switch_priv *priv,
                                struct ethtool_eee *e, int port);
+       u64 (*read_mcast_pmask)(int idx);
+       void (*write_mcast_pmask)(int idx, u64 portmask);
 };
 
 struct rtl838x_switch_priv {
index b2846deb1136bf05c8084e9bc988c6f891181814..825655cd544c9a1418c333350a88bb98b3a1c211 100644 (file)
@@ -48,68 +48,71 @@ static inline int rtl839x_tbl_access_data_0(int i)
 
 static void rtl839x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info)
 {
-       u32 cmd;
-       u64 v;
-       u32 u, w;
-
-       cmd = 1 << 16 /* Execute cmd */
-               | 0 << 15 /* Read */
-               | 0 << 12 /* Table type 0b000 */
-               | (vlan & 0xfff);
-       rtl839x_exec_tbl0_cmd(cmd);
-
-       v = sw_r32(RTL839X_TBL_ACCESS_DATA_0(0));
-       v <<= 32;
-       u = sw_r32(RTL839X_TBL_ACCESS_DATA_0(1));
-       v |= u;
-       info->tagged_ports = v >> 11;
-
-       w = sw_r32(RTL839X_TBL_ACCESS_DATA_0(2));
-
-       info->profile_id = w >> 30 | ((u & 1) << 2);
-       info->hash_mc_fid = !!(u & 2);
-       info->hash_uc_fid = !!(u & 4);
-       info->fid = (u >> 3) & 0xff;
-
-       cmd = 1 << 15 /* Execute cmd */
-               | 0 << 14 /* Read */
-               | 0 << 12 /* Table type 0b00 */
-               | (vlan & 0xfff);
-       rtl839x_exec_tbl1_cmd(cmd);
-       v = sw_r32(RTL839X_TBL_ACCESS_DATA_1(0));
-       v <<= 32;
-       v |= sw_r32(RTL839X_TBL_ACCESS_DATA_1(1));
-       info->untagged_ports = v >> 11;
+       u32 u, v, w;
+       // Read VLAN table (0) via register 0
+       struct table_reg *r = rtl_table_get(RTL8390_TBL_0, 0);
+
+       rtl_table_read(r, vlan);
+       u = sw_r32(rtl_table_data(r, 0));
+       v = sw_r32(rtl_table_data(r, 1));
+       w = sw_r32(rtl_table_data(r, 2));
+       rtl_table_release(r);
+
+       info->tagged_ports = u;
+       info->tagged_ports = (info->tagged_ports << 21) | ((v >> 11) & 0x1fffff);
+       info->profile_id = w >> 30 | ((v & 1) << 2);
+       info->hash_mc_fid = !!(w & BIT(2));
+       info->hash_uc_fid = !!(w & BIT(3));
+       info->fid = (v >> 3) & 0xff;
+
+       // Read UNTAG table (0) via table register 1
+       r = rtl_table_get(RTL8390_TBL_1, 0);
+       rtl_table_read(r, vlan);
+       u = sw_r32(rtl_table_data(r, 0));
+       v = sw_r32(rtl_table_data(r, 1));
+       rtl_table_release(r);
+
+       info->untagged_ports = u;
+       info->untagged_ports = (info->untagged_ports << 21) | ((v >> 11) & 0x1fffff);
 }
 
 static void rtl839x_vlan_set_tagged(u32 vlan, struct rtl838x_vlan_info *info)
 {
-       u32 cmd = BIT(16) /* Execute cmd */
-               | BIT(15) /* Write */
-               | 0 << 12 /* Table type 0b00 */
-               | (vlan & 0xfff);
-       u32 w;
-       u64 v = info->tagged_ports << 11;
+       u32 u, v, w;
+       // Access VLAN table (0) via register 0
+       struct table_reg *r = rtl_table_get(RTL8390_TBL_0, 0);
 
-       v |= info->profile_id >> 2;
-       v |= info->hash_mc_fid ? 2 : 0;
-       v |= info->hash_uc_fid ? 4 : 0;
+       u = info->tagged_ports >> 21;
+       v = info->tagged_ports << 11;
        v |= ((u32)info->fid) << 3;
-       rtl839x_set_port_reg_be(v, RTL838X_TBL_ACCESS_DATA_0(0));
+       v |= info->hash_uc_fid ? BIT(2) : 0;
+       v |= info->hash_mc_fid ? BIT(1) : 0;
+       v |= (info->profile_id & 0x4) ? 1 : 0;
+       w = ((u32)(info->profile_id & 3)) << 30;
+
+       sw_w32(u, rtl_table_data(r, 0));
+       sw_w32(v, rtl_table_data(r, 1));
+       sw_w32(w, rtl_table_data(r, 2));
 
-       w = info->profile_id;
-       sw_w32(w << 30, RTL838X_TBL_ACCESS_DATA_0(2));
-       rtl839x_exec_tbl0_cmd(cmd);
+       rtl_table_write(r, vlan);
+       rtl_table_release(r);
 }
 
 static void rtl839x_vlan_set_untagged(u32 vlan, u64 portmask)
 {
-       u32 cmd = BIT(16) /* Execute cmd */
-               | BIT(15) /* Write */
-               | 0 << 12 /* Table type 0b00 */
-               | (vlan & 0xfff);
-       rtl839x_set_port_reg_be(portmask << 11, RTL838X_TBL_ACCESS_DATA_1(0));
-       rtl839x_exec_tbl1_cmd(cmd);
+       u32 u, v;
+
+       // Access UNTAG table (0) via table register 1
+       struct table_reg *r = rtl_table_get(RTL8390_TBL_1, 0);
+
+       u = portmask >> 21;
+       v = portmask << 11;
+
+       sw_w32(u, rtl_table_data(r, 0));
+       sw_w32(v, rtl_table_data(r, 1));
+       rtl_table_write(r, vlan);
+
+       rtl_table_release(r);
 }
 
 static inline int rtl839x_mac_force_mode_ctrl(int p)
@@ -243,9 +246,47 @@ static u64 rtl839x_read_cam(int idx, struct rtl838x_l2_entry *e)
        return entry;
 }
 
-static inline int rtl839x_vlan_profile(int profile)
+static u64 rtl839x_read_mcast_pmask(int idx)
 {
-       return RTL839X_VLAN_PROFILE(profile);
+       u64 portmask;
+       // Read MC_PMSK (2) via register RTL8390_TBL_L2
+       struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 2);
+
+       rtl_table_read(q, idx);
+       portmask = sw_r32(rtl_table_data(q, 0));
+       portmask <<= 32;
+       portmask |= sw_r32(rtl_table_data(q, 1));
+       portmask >>= 11;  // LSB is bit 11 in data registers
+       rtl_table_release(q);
+
+       return portmask;
+}
+
+static void rtl839x_write_mcast_pmask(int idx, u64 portmask)
+{
+       // Access MC_PMSK (2) via register RTL8380_TBL_L2
+       struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 2);
+
+       portmask <<= 11; // LSB is bit 11 in data registers
+       sw_w32((u32)(portmask >> 32), rtl_table_data(q, 0));
+       sw_w32((u32)((portmask & 0xfffff800)), rtl_table_data(q, 1));
+       rtl_table_write(q, idx);
+       rtl_table_release(q);
+}
+
+static void rtl839x_vlan_profile_setup(int profile)
+{
+       u32 p[2];
+       u32 pmask_id = UNKNOWN_MC_PMASK;
+
+       p[0] = pmask_id; // Use portmaks 0xfff for unknown IPv6 MC flooding
+       // Enable L2 Learning BIT 0, portmask UNKNOWN_MC_PMASK for IP/L2-MC traffic flooding
+       p[1] = 1 | pmask_id << 1 | pmask_id << 13;
+
+       sw_w32(p[0], RTL839X_VLAN_PROFILE(profile));
+       sw_w32(p[1], RTL839X_VLAN_PROFILE(profile) + 4);
+
+       rtl839x_write_mcast_pmask(UNKNOWN_MC_PMASK, 0x000fffffffffffff);
 }
 
 static inline int rtl839x_vlan_port_egr_filter(int port)
@@ -477,20 +518,21 @@ u32 rtl839x_hash(struct rtl838x_switch_priv *priv, u64 seed)
        return h;
 }
 
-void rtl839x_vlan_profile_dump(int index)
+void rtl839x_vlan_profile_dump(int profile)
 {
-       u32 profile, profile1;
+       u32 p[2];
 
-       if (index < 0 || index > 7)
+       if (profile < 0 || profile > 7)
                return;
 
-       profile1 = sw_r32(RTL839X_VLAN_PROFILE(index) + 4);
-       profile = sw_r32(RTL839X_VLAN_PROFILE(index));
+       p[0] = sw_r32(RTL839X_VLAN_PROFILE(profile));
+       p[1] = sw_r32(RTL839X_VLAN_PROFILE(profile) + 4);
 
-       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) & 0xfff, (profile >> 13) & 0xfff,
-               (profile1) & 0xfff);
+       pr_info("VLAN profile %d: L2 learning: %d, UNKN L2MC FLD PMSK %d, \
+               UNKN IPMC FLD PMSK %d, UNKN IPv6MC FLD PMSK: %d",
+               profile, p[1] & 1, (p[1] >> 1) & 0xfff, (p[1] >> 13) & 0xfff,
+               (p[0]) & 0xfff);
+       pr_info("VLAN profile %d: raw %08x, %08x\n", profile, p[0], p[1]);
 }
 
 static void rtl839x_stp_get(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[])
@@ -626,6 +668,7 @@ const struct rtl838x_reg rtl839x_reg = {
        .vlan_set_tagged = rtl839x_vlan_set_tagged,
        .vlan_set_untagged = rtl839x_vlan_set_untagged,
        .vlan_profile_dump = rtl839x_vlan_profile_dump,
+       .vlan_profile_setup = rtl839x_vlan_profile_setup,
        .stp_get = rtl839x_stp_get,
        .stp_set = rtl839x_stp_set,
        .mac_force_mode_ctrl = rtl839x_mac_force_mode_ctrl,
@@ -652,4 +695,6 @@ const struct rtl838x_reg rtl839x_reg = {
        .init_eee = rtl839x_init_eee,
        .port_eee_set = rtl839x_port_eee_set,
        .eee_port_ability = rtl839x_eee_port_ability,
+       .read_mcast_pmask = rtl839x_read_mcast_pmask,
+       .write_mcast_pmask = rtl839x_write_mcast_pmask,
 };
index 4214c7694dd45dc4bb3f0006ee46278dd2eb5c19..a6e5cb763fd3d8df1395bffe856aa81ca123af66 100644 (file)
@@ -54,7 +54,7 @@ inline static int rtl930x_trk_mbr_ctr(int group)
 static void rtl930x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info)
 {
        u32 v, w;
-       // Read VLAN table (0) via register 0
+       // Read VLAN table (1) via register 0
        struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 1);
 
        rtl_table_read(r, vlan);
@@ -99,22 +99,28 @@ static void rtl930x_vlan_set_tagged(u32 vlan, struct rtl838x_vlan_info *info)
        rtl_table_release(r);
 }
 
-void rtl930x_vlan_profile_dump(int index)
+void rtl930x_vlan_profile_dump(int profile)
 {
-       u32 profile[5];
+       u32 p[5];
 
-       if (index < 0 || index > 7)
+       if (profile < 0 || profile > 7)
                return;
 
-       profile[0] = sw_r32(RTL930X_VLAN_PROFILE_SET(index));
-       profile[1] = sw_r32(RTL930X_VLAN_PROFILE_SET(index) + 4);
-       profile[2] = sw_r32(RTL930X_VLAN_PROFILE_SET(index) + 8) & 0x1FFFFFFF;
-       profile[3] = sw_r32(RTL930X_VLAN_PROFILE_SET(index) + 12) & 0x1FFFFFFF;
-       profile[4] = sw_r32(RTL930X_VLAN_PROFILE_SET(index) + 16) & 0x1FFFFFFF;
+       p[0] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile));
+       p[1] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 4);
+       p[2] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 8) & 0x1FFFFFFF;
+       p[3] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 12) & 0x1FFFFFFF;
+       p[4] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 16) & 0x1FFFFFFF;
 
-       pr_debug("VLAN %d: L2 learning: %d, L2 Unknown MultiCast Field %x, \
-               IPv4 Unknown MultiCast Field %x, IPv6 Unknown MultiCast Field: %x",
-               index, profile[0] & (3 << 21), profile[2], profile[3], profile[4]);
+       pr_info("VLAN %d: L2 learn: %d; Unknown MC PMasks: L2 %0x, IPv4 %0x, IPv6: %0x",
+               profile, p[0] & (3 << 21), p[2], p[3], p[4]);
+       pr_info("  Routing enabled: IPv4 UC %c, IPv6 UC %c, IPv4 MC %c, IPv6 MC %c\n",
+               p[0] & BIT(17) ? 'y' : 'n', p[0] & BIT(16) ? 'y' : 'n',
+               p[0] & BIT(13) ? 'y' : 'n', p[0] & BIT(12) ? 'y' : 'n');
+       pr_info("  Bridge enabled: IPv4 MC %c, IPv6 MC %c,\n",
+               p[0] & BIT(15) ? 'y' : 'n', p[0] & BIT(14) ? 'y' : 'n');
+       pr_info("VLAN profile %d: raw %08x %08x %08x %08x %08x\n",
+               profile, p[0], p[1], p[2], p[3], p[4]);
 }
 
 static void rtl930x_vlan_set_untagged(u32 vlan, u64 portmask)
@@ -126,6 +132,28 @@ static void rtl930x_vlan_set_untagged(u32 vlan, u64 portmask)
        rtl_table_release(r);
 }
 
+static void rtl930x_vlan_profile_setup(int profile)
+{
+       u32 p[5];
+
+       pr_info("In %s\n", __func__);
+       p[0] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile));
+       p[1] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 4);
+
+       // Enable routing of Ipv4/6 Unicast and IPv4/6 Multicast traffic
+       p[0] |= BIT(17) | BIT(16) | BIT(13) | BIT(12);
+       p[2] = 0x0fffffff; // L2 unknwon MC flooding portmask: all but the CPU-port
+       p[3] = 0x0fffffff; // IPv4 unknwon MC flooding portmask
+       p[4] = 0x0fffffff; // IPv6 unknwon MC flooding portmask
+
+       sw_w32(p[0], RTL930X_VLAN_PROFILE_SET(profile));
+       sw_w32(p[1], RTL930X_VLAN_PROFILE_SET(profile) + 4);
+       sw_w32(p[2], RTL930X_VLAN_PROFILE_SET(profile) + 8);
+       sw_w32(p[3], RTL930X_VLAN_PROFILE_SET(profile) + 12);
+       sw_w32(p[4], RTL930X_VLAN_PROFILE_SET(profile) + 16);
+       pr_info("Leaving %s\n", __func__);
+}
+
 static void rtl930x_stp_get(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[])
 {
        int i;
@@ -256,6 +284,35 @@ static u64 rtl930x_read_cam(int idx, struct rtl838x_l2_entry *e)
        return entry;
 }
 
+static u64 rtl930x_read_mcast_pmask(int idx)
+{
+       u32 portmask;
+       // Read MC_PORTMASK (2) via register RTL9300_TBL_L2
+       struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 2);
+
+       rtl_table_read(q, idx);
+       portmask = sw_r32(rtl_table_data(q, 0));
+       portmask >>= 3;
+       rtl_table_release(q);
+
+       pr_debug("%s: Index idx %d has portmask %08x\n", __func__, idx, portmask);
+       return portmask;
+}
+
+static void rtl930x_write_mcast_pmask(int idx, u64 portmask)
+{
+       u32 pm = portmask;
+
+       // Access MC_PORTMASK (2) via register RTL9300_TBL_L2
+       struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 2);
+
+       pr_debug("%s: Index idx %d has portmask %08x\n", __func__, idx, pm);
+       pm <<= 3;
+       sw_w32(pm, rtl_table_data(q, 0));
+       rtl_table_write(q, idx);
+       rtl_table_release(q);
+}
+
 u64 rtl930x_traffic_get(int source)
 {
        u32 v;
@@ -669,6 +726,7 @@ const struct rtl838x_reg rtl930x_reg = {
        .vlan_set_tagged = rtl930x_vlan_set_tagged,
        .vlan_set_untagged = rtl930x_vlan_set_untagged,
        .vlan_profile_dump = rtl930x_vlan_profile_dump,
+       .vlan_profile_setup = rtl930x_vlan_profile_setup,
        .stp_get = rtl930x_stp_get,
        .stp_set = rtl930x_stp_set,
        .mac_force_mode_ctrl = rtl930x_mac_force_mode_ctrl,
@@ -694,4 +752,6 @@ const struct rtl838x_reg rtl930x_reg = {
        .init_eee = rtl930x_init_eee,
        .port_eee_set = rtl930x_port_eee_set,
        .eee_port_ability = rtl930x_eee_port_ability,
+       .read_mcast_pmask = rtl930x_read_mcast_pmask,
+       .write_mcast_pmask = rtl930x_write_mcast_pmask,
 };