realtek: Add support for Layer 2 Multicast
[openwrt/openwrt.git] / target / linux / realtek / files-5.4 / drivers / net / dsa / rtl83xx / rtl838x.c
index 773225b3c40f633d54a1c6f51e19570644d9d649..dfd773c5e6fc318b23a394e76d1386b7eb0e176f 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,98 @@ 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);
+}
+
+/* Sets the L2 forwarding to be based on either the inner VLAN tag or the outer
+ */
+static void rtl838x_vlan_fwd_on_inner(int port, bool is_set)
+{
+       if (is_set)
+               sw_w32_mask(BIT(port), 0, RTL838X_VLAN_PORT_FWD);
+       else
+               sw_w32_mask(0, BIT(port), RTL838X_VLAN_PORT_FWD);
+}
+
+static u64 rtl838x_l2_hash_seed(u64 mac, u32 vid)
+{
+       return mac << 12 | vid;
+}
+
+/*
+ * Applies the same hash algorithm as the one used currently by the ASIC to the seed
+ * and returns a key into the L2 hash table
+ */
+static u32 rtl838x_l2_hash_key(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;
 }
 
 static inline int rtl838x_mac_force_mode_ctrl(int p)
@@ -124,87 +166,239 @@ inline static int rtl838x_trk_mbr_ctr(int group)
        return RTL838X_TRK_MBR_CTR + (group << 2);
 }
 
-static u64 rtl838x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e)
+/*
+ * Fills an L2 entry structure from the SoC registers
+ */
+static void rtl838x_fill_l2_entry(u32 r[], struct rtl838x_l2_entry *e)
+{
+       /* Table contains different entry types, we need to identify the right one:
+        * Check for MC entries, first
+        * In contrast to the RTL93xx SoCs, there is no valid bit, use heuristics to
+        * identify valid entries
+        */
+       e->is_ip_mc = !!(r[0] & BIT(22));
+       e->is_ipv6_mc = !!(r[0] & BIT(21));
+       e->type = L2_INVALID;
+
+       if (!e->is_ip_mc && !e->is_ipv6_mc) {
+               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->rvid = r[2] & 0xfff;
+               e->vid = r[0] & 0xfff;
+
+               /* Is it a unicast entry? check multicast bit */
+               if (!(e->mac[0] & 1)) {
+                       e->is_static = !!((r[0] >> 19) & 1);
+                       e->port = (r[0] >> 12) & 0x1f;
+                       e->block_da = !!(r[1] & BIT(30));
+                       e->block_sa = !!(r[1] & BIT(31));
+                       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");
+                               e->nh_vlan_target = !!(r[0] & BIT(9));
+                               e->nh_route_id = r[0] & 0x1ff;
+                       }
+                       e->age = (r[0] >> 17) & 0x3;
+                       e->valid = true;
+                       
+                       /* A valid entry has one of mutli-cast, aging, sa/da-blocking,
+                        * next-hop or static entry bit set */
+                       if (!(r[0] & 0x007c0000) && !(r[1] & 0xd0000000))
+                               e->valid = false;
+                       else
+                               e->type = L2_UNICAST;
+               } else { // L2 multicast
+                       pr_info("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;
+       }
+       if (e->is_ip_mc)
+               e->type = IP4_MULTICAST;
+       if (e->is_ipv6_mc)
+               e->type = IP6_MULTICAST;
+}
+
+/*
+ * Fills the 3 SoC table registers r[] with the information of in the rtl838x_l2_entry
+ */
+static void rtl838x_fill_l2_row(u32 r[], struct rtl838x_l2_entry *e)
+{
+       u64 mac = ether_addr_to_u64(e->mac);
+
+       if (!e->valid) {
+               r[0] = r[1] = r[2] = 0;
+               return;
+       }
+
+       r[0] = e->is_ip_mc ? BIT(22) : 0;
+       r[0] |= e->is_ipv6_mc ? BIT(21) : 0;
+
+       if (!e->is_ip_mc && !e->is_ipv6_mc) {
+               r[1] = mac >> 20;
+               r[2] = (mac & 0xfffff) << 12;
+
+               /* Is it a unicast entry? check multicast bit */
+               if (!(e->mac[0] & 1)) {
+                       r[0] |= e->is_static ? BIT(19) : 0;
+                       r[0] |= (e->port & 0x3f) << 12;
+                       r[0] |= e->vid;
+                       r[1] |= e->block_da ? BIT(30) : 0;
+                       r[1] |= e->block_sa ? BIT(31) : 0;
+                       r[1] |= e->suspended ? BIT(29) : 0;
+                       r[2] |= e->rvid & 0xfff;
+                       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->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]);
+               }
+       } else { // IPv4 and IPv6 multicast
+               r[1] = e->mc_gip;
+               r[2] = e->mc_sip;
+               r[0] |= e->rvid;
+       }
+}
+
+/*
+ * 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 rtl838x_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(RTL8380_TBL_L2, 0); // Access L2 Table 0
+       u32 idx = (0 << 14) | (hash << 2) | pos; // Search SRAM, with hash and at pos in bucket
+       int i;
+
+       rtl_table_read(q, idx);
+       for (i= 0; i < 3; i++)
+               r[i] = sw_r32(rtl_table_data(q, i));
 
-       /* 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]);
+       rtl_table_release(q);
+
+       rtl838x_fill_l2_entry(r, e);
+       if (!e->valid)
+               return 0;
 
        entry = (((u64) r[1]) << 32) | (r[2] & 0xfffff000) | (r[0] & 0xfff);
        return entry;
 }
 
+static void rtl838x_write_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e)
+{
+       u32 r[3];
+       struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 0);
+       int i;
+
+       u32 idx = (0 << 14) | (hash << 2) | pos; // Access SRAM, with hash and at pos in bucket
+
+       rtl838x_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 rtl838x_read_cam(int idx, struct rtl838x_l2_entry *e)
 {
        u64 entry;
        u32 r[3];
+       struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 1); // Access L2 Table 1
+       int i;
+
+       rtl_table_read(q, idx);
+       for (i= 0; i < 3; i++)
+               r[i] = sw_r32(rtl_table_data(q, i));
+
+       rtl_table_release(q);
+
+       rtl838x_fill_l2_entry(r, e);
+       if (!e->valid)
+               return 0;
 
-       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]);
+       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);
        return entry;
 }
 
-static inline int rtl838x_vlan_profile(int profile)
+static void rtl838x_write_cam(int idx, struct rtl838x_l2_entry *e)
 {
-       return RTL838X_VLAN_PROFILE(profile);
+       u32 r[3];
+       struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 1); // Access L2 Table 1
+       int i;
+
+       rtl838x_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 rtl838x_read_mcast_pmask(int idx)
+{
+       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 +567,8 @@ 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,
+       .vlan_fwd_on_inner = rtl838x_vlan_fwd_on_inner,
        .stp_get = rtl838x_stp_get,
        .stp_set = rtl838x_stp_set,
        .mac_port_ctrl = rtl838x_mac_port_ctrl,
@@ -387,7 +583,9 @@ const struct rtl838x_reg rtl838x_reg = {
        .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,
+       .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,
@@ -398,6 +596,10 @@ 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,
+       .l2_hash_seed = rtl838x_l2_hash_seed, 
+       .l2_hash_key = rtl838x_l2_hash_key,
+       .read_mcast_pmask = rtl838x_read_mcast_pmask,
+       .write_mcast_pmask = rtl838x_write_mcast_pmask,
 };
 
 irqreturn_t rtl838x_switch_irq(int irq, void *dev_id)
@@ -613,47 +815,18 @@ void rtl8380_get_version(struct rtl838x_switch_priv *priv)
        }
 }
 
-/*
- * 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)
+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)