]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.c
Merge tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dledford/rdma
[karo-tx-linux.git] / drivers / net / wireless / realtek / rtl8xxxu / rtl8xxxu.c
index bb43937788ce3b76d0aa06386d78fcbfdeeb2bdd..abdff458b80f7a8b586423b56cff1d4434d99724 100644 (file)
@@ -1676,6 +1676,24 @@ static int rtl8723a_channel_to_group(int channel)
        return group;
 }
 
+static int rtl8723b_channel_to_group(int channel)
+{
+       int group;
+
+       if (channel < 3)
+               group = 0;
+       else if (channel < 6)
+               group = 1;
+       else if (channel < 9)
+               group = 2;
+       else if (channel < 12)
+               group = 3;
+       else
+               group = 4;
+
+       return group;
+}
+
 static void rtl8723au_config_channel(struct ieee80211_hw *hw)
 {
        struct rtl8xxxu_priv *priv = hw->priv;
@@ -2037,6 +2055,45 @@ rtl8723a_set_tx_power(struct rtl8xxxu_priv *priv, int channel, bool ht40)
        }
 }
 
+static void
+rtl8723b_set_tx_power(struct rtl8xxxu_priv *priv, int channel, bool ht40)
+{
+       u32 val32, ofdm, mcs;
+       u8 cck, ofdmbase, mcsbase;
+       int group, tx_idx;
+
+       tx_idx = 0;
+       group = rtl8723b_channel_to_group(channel);
+
+       cck = priv->cck_tx_power_index_B[group];
+       val32 = rtl8xxxu_read32(priv, REG_TX_AGC_A_CCK1_MCS32);
+       val32 &= 0xffff00ff;
+       val32 |= (cck << 8);
+       rtl8xxxu_write32(priv, REG_TX_AGC_A_CCK1_MCS32, val32);
+
+       val32 = rtl8xxxu_read32(priv, REG_TX_AGC_B_CCK11_A_CCK2_11);
+       val32 &= 0xff;
+       val32 |= ((cck << 8) | (cck << 16) | (cck << 24));
+       rtl8xxxu_write32(priv, REG_TX_AGC_B_CCK11_A_CCK2_11, val32);
+
+       ofdmbase = priv->ht40_1s_tx_power_index_B[group];
+       ofdmbase += priv->ofdm_tx_power_diff[tx_idx].b;
+       ofdm = ofdmbase | ofdmbase << 8 | ofdmbase << 16 | ofdmbase << 24;
+
+       rtl8xxxu_write32(priv, REG_TX_AGC_A_RATE18_06, ofdm);
+       rtl8xxxu_write32(priv, REG_TX_AGC_A_RATE54_24, ofdm);
+
+       mcsbase = priv->ht40_1s_tx_power_index_B[group];
+       if (ht40)
+               mcsbase += priv->ht40_tx_power_diff[tx_idx++].b;
+       else
+               mcsbase += priv->ht20_tx_power_diff[tx_idx++].b;
+       mcs = mcsbase | mcsbase << 8 | mcsbase << 16 | mcsbase << 24;
+
+       rtl8xxxu_write32(priv, REG_TX_AGC_A_MCS03_MCS00, mcs);
+       rtl8xxxu_write32(priv, REG_TX_AGC_A_MCS07_MCS04, mcs);
+}
+
 static void rtl8xxxu_set_linktype(struct rtl8xxxu_priv *priv,
                                  enum nl80211_iftype linktype)
 {
@@ -2288,31 +2345,31 @@ static int rtl8723au_parse_efuse(struct rtl8xxxu_priv *priv)
 
        memcpy(priv->cck_tx_power_index_A,
               efuse->cck_tx_power_index_A,
-              sizeof(priv->cck_tx_power_index_A));
+              sizeof(efuse->cck_tx_power_index_A));
        memcpy(priv->cck_tx_power_index_B,
               efuse->cck_tx_power_index_B,
-              sizeof(priv->cck_tx_power_index_B));
+              sizeof(efuse->cck_tx_power_index_B));
 
        memcpy(priv->ht40_1s_tx_power_index_A,
               efuse->ht40_1s_tx_power_index_A,
-              sizeof(priv->ht40_1s_tx_power_index_A));
+              sizeof(efuse->ht40_1s_tx_power_index_A));
        memcpy(priv->ht40_1s_tx_power_index_B,
               efuse->ht40_1s_tx_power_index_B,
-              sizeof(priv->ht40_1s_tx_power_index_B));
+              sizeof(efuse->ht40_1s_tx_power_index_B));
 
        memcpy(priv->ht20_tx_power_index_diff,
               efuse->ht20_tx_power_index_diff,
-              sizeof(priv->ht20_tx_power_index_diff));
+              sizeof(efuse->ht20_tx_power_index_diff));
        memcpy(priv->ofdm_tx_power_index_diff,
               efuse->ofdm_tx_power_index_diff,
-              sizeof(priv->ofdm_tx_power_index_diff));
+              sizeof(efuse->ofdm_tx_power_index_diff));
 
        memcpy(priv->ht40_max_power_offset,
               efuse->ht40_max_power_offset,
-              sizeof(priv->ht40_max_power_offset));
+              sizeof(efuse->ht40_max_power_offset));
        memcpy(priv->ht20_max_power_offset,
               efuse->ht20_max_power_offset,
-              sizeof(priv->ht20_max_power_offset));
+              sizeof(efuse->ht20_max_power_offset));
 
        if (priv->efuse_wifi.efuse8723.version >= 0x01) {
                priv->has_xtalk = 1;
@@ -2328,21 +2385,54 @@ static int rtl8723au_parse_efuse(struct rtl8xxxu_priv *priv)
 static int rtl8723bu_parse_efuse(struct rtl8xxxu_priv *priv)
 {
        struct rtl8723bu_efuse *efuse = &priv->efuse_wifi.efuse8723bu;
+       int i;
 
        if (efuse->rtl_id != cpu_to_le16(0x8129))
                return -EINVAL;
 
        ether_addr_copy(priv->mac_addr, efuse->mac_addr);
 
-       memcpy(priv->cck_tx_power_index_A, efuse->cck_tx_power_index_A,
-              sizeof(priv->cck_tx_power_index_A));
-       memcpy(priv->cck_tx_power_index_B, efuse->cck_tx_power_index_B,
-              sizeof(priv->cck_tx_power_index_B));
+       memcpy(priv->cck_tx_power_index_A, efuse->tx_power_index_A.cck_base,
+              sizeof(efuse->tx_power_index_A.cck_base));
+       memcpy(priv->cck_tx_power_index_B, efuse->tx_power_index_B.cck_base,
+              sizeof(efuse->tx_power_index_B.cck_base));
+
+       memcpy(priv->ht40_1s_tx_power_index_A,
+              efuse->tx_power_index_A.ht40_base,
+              sizeof(efuse->tx_power_index_A.ht40_base));
+       memcpy(priv->ht40_1s_tx_power_index_B,
+              efuse->tx_power_index_B.ht40_base,
+              sizeof(efuse->tx_power_index_B.ht40_base));
 
-       memcpy(priv->ht40_1s_tx_power_index_A, efuse->ht40_1s_tx_power_index_A,
-              sizeof(priv->ht40_1s_tx_power_index_A));
-       memcpy(priv->ht40_1s_tx_power_index_B, efuse->ht40_1s_tx_power_index_B,
-              sizeof(priv->ht40_1s_tx_power_index_B));
+       priv->ofdm_tx_power_diff[0].a =
+               efuse->tx_power_index_A.ht20_ofdm_1s_diff.a;
+       priv->ofdm_tx_power_diff[0].b =
+               efuse->tx_power_index_B.ht20_ofdm_1s_diff.a;
+
+       priv->ht20_tx_power_diff[0].a =
+               efuse->tx_power_index_A.ht20_ofdm_1s_diff.b;
+       priv->ht20_tx_power_diff[0].b =
+               efuse->tx_power_index_B.ht20_ofdm_1s_diff.b;
+
+       priv->ht40_tx_power_diff[0].a = 0;
+       priv->ht40_tx_power_diff[0].b = 0;
+
+       for (i = 1; i < RTL8723B_TX_COUNT; i++) {
+               priv->ofdm_tx_power_diff[i].a =
+                       efuse->tx_power_index_A.pwr_diff[i - 1].ofdm;
+               priv->ofdm_tx_power_diff[i].b =
+                       efuse->tx_power_index_B.pwr_diff[i - 1].ofdm;
+
+               priv->ht20_tx_power_diff[i].a =
+                       efuse->tx_power_index_A.pwr_diff[i - 1].ht20;
+               priv->ht20_tx_power_diff[i].b =
+                       efuse->tx_power_index_B.pwr_diff[i - 1].ht20;
+
+               priv->ht40_tx_power_diff[i].a =
+                       efuse->tx_power_index_A.pwr_diff[i - 1].ht40;
+               priv->ht40_tx_power_diff[i].b =
+                       efuse->tx_power_index_B.pwr_diff[i - 1].ht40;
+       }
 
        priv->has_xtalk = 1;
        priv->xtalk = priv->efuse_wifi.efuse8723bu.xtal_k & 0x3f;
@@ -2383,34 +2473,34 @@ static int rtl8192cu_parse_efuse(struct rtl8xxxu_priv *priv)
 
        memcpy(priv->cck_tx_power_index_A,
               efuse->cck_tx_power_index_A,
-              sizeof(priv->cck_tx_power_index_A));
+              sizeof(efuse->cck_tx_power_index_A));
        memcpy(priv->cck_tx_power_index_B,
               efuse->cck_tx_power_index_B,
-              sizeof(priv->cck_tx_power_index_B));
+              sizeof(efuse->cck_tx_power_index_B));
 
        memcpy(priv->ht40_1s_tx_power_index_A,
               efuse->ht40_1s_tx_power_index_A,
-              sizeof(priv->ht40_1s_tx_power_index_A));
+              sizeof(efuse->ht40_1s_tx_power_index_A));
        memcpy(priv->ht40_1s_tx_power_index_B,
               efuse->ht40_1s_tx_power_index_B,
-              sizeof(priv->ht40_1s_tx_power_index_B));
+              sizeof(efuse->ht40_1s_tx_power_index_B));
        memcpy(priv->ht40_2s_tx_power_index_diff,
               efuse->ht40_2s_tx_power_index_diff,
-              sizeof(priv->ht40_2s_tx_power_index_diff));
+              sizeof(efuse->ht40_2s_tx_power_index_diff));
 
        memcpy(priv->ht20_tx_power_index_diff,
               efuse->ht20_tx_power_index_diff,
-              sizeof(priv->ht20_tx_power_index_diff));
+              sizeof(efuse->ht20_tx_power_index_diff));
        memcpy(priv->ofdm_tx_power_index_diff,
               efuse->ofdm_tx_power_index_diff,
-              sizeof(priv->ofdm_tx_power_index_diff));
+              sizeof(efuse->ofdm_tx_power_index_diff));
 
        memcpy(priv->ht40_max_power_offset,
               efuse->ht40_max_power_offset,
-              sizeof(priv->ht40_max_power_offset));
+              sizeof(efuse->ht40_max_power_offset));
        memcpy(priv->ht20_max_power_offset,
               efuse->ht20_max_power_offset,
-              sizeof(priv->ht20_max_power_offset));
+              sizeof(efuse->ht20_max_power_offset));
 
        dev_info(&priv->udev->dev, "Vendor: %.7s\n",
                 efuse->vendor_name);
@@ -2472,6 +2562,10 @@ static int rtl8192eu_parse_efuse(struct rtl8xxxu_priv *priv)
                                 raw[i + 6], raw[i + 7]);
                }
        }
+       /*
+        * Temporarily disable 8192eu support
+        */
+       return -EINVAL;
        return 0;
 }
 
@@ -2630,12 +2724,44 @@ static void rtl8xxxu_reset_8051(struct rtl8xxxu_priv *priv)
        val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1);
        val8 &= ~BIT(0);
        rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8);
+
        sys_func = rtl8xxxu_read16(priv, REG_SYS_FUNC);
        sys_func &= ~SYS_FUNC_CPU_ENABLE;
        rtl8xxxu_write16(priv, REG_SYS_FUNC, sys_func);
+
        val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1);
        val8 |= BIT(0);
        rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8);
+
+       sys_func |= SYS_FUNC_CPU_ENABLE;
+       rtl8xxxu_write16(priv, REG_SYS_FUNC, sys_func);
+}
+
+static void rtl8723bu_reset_8051(struct rtl8xxxu_priv *priv)
+{
+       u8 val8;
+       u16 sys_func;
+
+       val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL);
+       val8 &= ~BIT(1);
+       rtl8xxxu_write8(priv, REG_RSV_CTRL, val8);
+
+       val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1);
+       val8 &= ~BIT(0);
+       rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8);
+
+       sys_func = rtl8xxxu_read16(priv, REG_SYS_FUNC);
+       sys_func &= ~SYS_FUNC_CPU_ENABLE;
+       rtl8xxxu_write16(priv, REG_SYS_FUNC, sys_func);
+
+       val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL);
+       val8 &= ~BIT(1);
+       rtl8xxxu_write8(priv, REG_RSV_CTRL, val8);
+
+       val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1);
+       val8 |= BIT(0);
+       rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8);
+
        sys_func |= SYS_FUNC_CPU_ENABLE;
        rtl8xxxu_write16(priv, REG_SYS_FUNC, sys_func);
 }
@@ -2668,7 +2794,7 @@ static int rtl8xxxu_start_firmware(struct rtl8xxxu_priv *priv)
         * Reset the 8051 in order for the firmware to start running,
         * otherwise it won't come up on the 8192eu
         */
-       rtl8xxxu_reset_8051(priv);
+       priv->fops->reset_8051(priv);
 
        /* Wait for firmware to become ready */
        for (i = 0; i < RTL8XXXU_FIRMWARE_POLL_MAX; i++) {
@@ -2715,7 +2841,7 @@ static int rtl8xxxu_download_firmware(struct rtl8xxxu_priv *priv)
        if (val8 & MCU_FW_RAM_SEL) {
                pr_info("do the RAM reset\n");
                rtl8xxxu_write8(priv, REG_MCU_FW_DL, 0x00);
-               rtl8xxxu_reset_8051(priv);
+               priv->fops->reset_8051(priv);
        }
 
        /* MCU firmware download enable */
@@ -2946,14 +3072,14 @@ static void rtl8723bu_phy_init_antenna_selection(struct rtl8xxxu_priv *priv)
        val32 &= ~BIT(23);
        rtl8xxxu_write32(priv, REG_LEDCFG0, val32);
 
-       val32 = rtl8xxxu_read32(priv, 0x0944);
+       val32 = rtl8xxxu_read32(priv, REG_RFE_BUFFER);
        val32 |= (BIT(0) | BIT(1));
-       rtl8xxxu_write32(priv, 0x0944, val32);
+       rtl8xxxu_write32(priv, REG_RFE_BUFFER, val32);
 
-       val32 = rtl8xxxu_read32(priv, 0x0930);
+       val32 = rtl8xxxu_read32(priv, REG_RFE_CTRL_ANTA_SRC);
        val32 &= 0xffffff00;
        val32 |= 0x77;
-       rtl8xxxu_write32(priv, 0x0930, val32);
+       rtl8xxxu_write32(priv, REG_RFE_CTRL_ANTA_SRC, val32);
 
        val32 = rtl8xxxu_read32(priv, REG_PWR_DATA);
        val32 |= PWR_DATA_EEPRPAD_RFE_CTRL_EN;
@@ -5150,6 +5276,64 @@ exit:
        return ret;
 }
 
+static int rtl8723bu_active_to_emu(struct rtl8xxxu_priv *priv)
+{
+       u8 val8;
+       u16 val16;
+       u32 val32;
+       int count, ret;
+
+       /* Turn off RF */
+       rtl8xxxu_write8(priv, REG_RF_CTRL, 0);
+
+       /* Enable rising edge triggering interrupt */
+       val16 = rtl8xxxu_read16(priv, REG_GPIO_INTM);
+       val16 &= ~GPIO_INTM_EDGE_TRIG_IRQ;
+       rtl8xxxu_write16(priv, REG_GPIO_INTM, val16);
+
+       /* Release WLON reset 0x04[16]= 1*/
+       val32 = rtl8xxxu_read32(priv, REG_GPIO_INTM);
+       val32 |= APS_FSMCO_WLON_RESET;
+       rtl8xxxu_write32(priv, REG_GPIO_INTM, val32);
+
+       /* 0x0005[1] = 1 turn off MAC by HW state machine*/
+       val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+       val8 |= BIT(1);
+       rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
+
+       for (count = RTL8XXXU_MAX_REG_POLL; count; count--) {
+               val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+               if ((val8 & BIT(1)) == 0)
+                       break;
+               udelay(10);
+       }
+
+       if (!count) {
+               dev_warn(&priv->udev->dev, "%s: Disabling MAC timed out\n",
+                        __func__);
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       /* Enable BT control XTAL setting */
+       val8 = rtl8xxxu_read8(priv, REG_AFE_MISC);
+       val8 &= ~AFE_MISC_WL_XTAL_CTRL;
+       rtl8xxxu_write8(priv, REG_AFE_MISC, val8);
+
+       /* 0x0000[5] = 1 analog Ips to digital, 1:isolation */
+       val8 = rtl8xxxu_read8(priv, REG_SYS_ISO_CTRL);
+       val8 |= SYS_ISO_ANALOG_IPS;
+       rtl8xxxu_write8(priv, REG_SYS_ISO_CTRL, val8);
+
+       /* 0x0020[0] = 0 disable LDOA12 MACRO block*/
+       val8 = rtl8xxxu_read8(priv, REG_LDOA15_CTRL);
+       val8 &= ~LDOA15_ENABLE;
+       rtl8xxxu_write8(priv, REG_LDOA15_CTRL, val8);
+
+exit:
+       return ret;
+}
+
 static int rtl8xxxu_active_to_lps(struct rtl8xxxu_priv *priv)
 {
        u8 val8;
@@ -5539,6 +5723,39 @@ static int rtl8xxxu_emu_to_disabled(struct rtl8xxxu_priv *priv)
        return 0;
 }
 
+static int rtl8xxxu_flush_fifo(struct rtl8xxxu_priv *priv)
+{
+       struct device *dev = &priv->udev->dev;
+       u32 val32;
+       int retry, retval;
+
+       rtl8xxxu_write8(priv, REG_TXPAUSE, 0xff);
+
+       val32 = rtl8xxxu_read32(priv, REG_RXPKT_NUM);
+       val32 |= RXPKT_NUM_RW_RELEASE_EN;
+       rtl8xxxu_write32(priv, REG_RXPKT_NUM, val32);
+
+       retry = 100;
+       retval = -EBUSY;
+
+       do {
+               val32 = rtl8xxxu_read32(priv, REG_RXPKT_NUM);
+               if (val32 & RXPKT_NUM_RXDMA_IDLE) {
+                       retval = 0;
+                       break;
+               }
+       } while (retry--);
+
+       rtl8xxxu_write16(priv, REG_RQPN_NPQ, 0);
+       rtl8xxxu_write32(priv, REG_RQPN, 0x80000000);
+       mdelay(2);
+
+       if (!retry)
+               dev_warn(dev, "Failed to flush FIFO\n");
+
+       return retval;
+}
+
 static int rtl8723au_power_on(struct rtl8xxxu_priv *priv)
 {
        u8 val8;
@@ -5809,6 +6026,8 @@ static void rtl8xxxu_power_off(struct rtl8xxxu_priv *priv)
                rtl8xxxu_write32(priv, REG_FPGA0_XCD_RF_PARM, val32);
        }
 
+       rtl8xxxu_flush_fifo(priv);
+
        rtl8xxxu_active_to_lps(priv);
 
        /* Turn off RF */
@@ -5842,6 +6061,41 @@ static void rtl8xxxu_power_off(struct rtl8xxxu_priv *priv)
        rtl8xxxu_write8(priv, REG_RSV_CTRL, 0x0e);
 }
 
+static void rtl8723bu_power_off(struct rtl8xxxu_priv *priv)
+{
+       u8 val8;
+       u16 val16;
+
+       rtl8xxxu_flush_fifo(priv);
+
+       /*
+        * Disable TX report timer
+        */
+       val8 = rtl8xxxu_read8(priv, REG_TX_REPORT_CTRL);
+       val8 &= ~TX_REPORT_CTRL_TIMER_ENABLE;
+       rtl8xxxu_write8(priv, REG_TX_REPORT_CTRL, val8);
+
+       rtl8xxxu_write16(priv, REG_CR, 0x0000);
+
+       rtl8xxxu_active_to_lps(priv);
+
+       /* Reset Firmware if running in RAM */
+       if (rtl8xxxu_read8(priv, REG_MCU_FW_DL) & MCU_FW_RAM_SEL)
+               rtl8xxxu_firmware_self_reset(priv);
+
+       /* Reset MCU */
+       val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC);
+       val16 &= ~SYS_FUNC_CPU_ENABLE;
+       rtl8xxxu_write16(priv, REG_SYS_FUNC, val16);
+
+       /* Reset MCU ready status */
+       rtl8xxxu_write8(priv, REG_MCU_FW_DL, 0x00);
+
+       rtl8723bu_active_to_emu(priv);
+       rtl8xxxu_emu_to_disabled(priv);
+}
+
+#ifdef NEED_PS_TDMA
 static void rtl8723bu_set_ps_tdma(struct rtl8xxxu_priv *priv,
                                  u8 arg1, u8 arg2, u8 arg3, u8 arg4, u8 arg5)
 {
@@ -5856,8 +6110,9 @@ static void rtl8723bu_set_ps_tdma(struct rtl8xxxu_priv *priv,
        h2c.b_type_dma.data5 = arg5;
        rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.b_type_dma));
 }
+#endif
 
-static void rtl8723bu_init_bt(struct rtl8xxxu_priv *priv)
+static void rtl8723b_enable_rf(struct rtl8xxxu_priv *priv)
 {
        struct h2c_cmd h2c;
        u32 val32;
@@ -5894,7 +6149,7 @@ static void rtl8723bu_init_bt(struct rtl8xxxu_priv *priv)
        /*
         * WLAN action by PTA
         */
-       rtl8xxxu_write8(priv, REG_WLAN_ACT_CONTROL_8723B, 0x0c);
+       rtl8xxxu_write8(priv, REG_WLAN_ACT_CONTROL_8723B, 0x04);
 
        /*
         * BT select S0/S1 controlled by WiFi
@@ -5904,7 +6159,7 @@ static void rtl8723bu_init_bt(struct rtl8xxxu_priv *priv)
        rtl8xxxu_write8(priv, 0x0067, val8);
 
        val32 = rtl8xxxu_read32(priv, REG_PWR_DATA);
-       val32 |= BIT(11);
+       val32 |= PWR_DATA_EEPRPAD_RFE_CTRL_EN;
        rtl8xxxu_write32(priv, REG_PWR_DATA, val32);
 
        /*
@@ -5912,9 +6167,9 @@ static void rtl8723bu_init_bt(struct rtl8xxxu_priv *priv)
         */
        rtl8xxxu_write8(priv, 0x0974, 0xff);
 
-       val32 = rtl8xxxu_read32(priv, 0x0944);
+       val32 = rtl8xxxu_read32(priv, REG_RFE_BUFFER);
        val32 |= (BIT(0) | BIT(1));
-       rtl8xxxu_write32(priv, 0x0944, val32);
+       rtl8xxxu_write32(priv, REG_RFE_BUFFER, val32);
 
        rtl8xxxu_write8(priv, REG_RFE_CTRL_ANTA_SRC, 0x77);
 
@@ -5944,24 +6199,79 @@ static void rtl8723bu_init_bt(struct rtl8xxxu_priv *priv)
        /*
         * Software control, antenna at WiFi side
         */
+#ifdef NEED_PS_TDMA
        rtl8723bu_set_ps_tdma(priv, 0x08, 0x00, 0x00, 0x00, 0x00);
+#endif
+
+       rtl8xxxu_write32(priv, REG_BT_COEX_TABLE1, 0x55555555);
+       rtl8xxxu_write32(priv, REG_BT_COEX_TABLE2, 0x55555555);
+       rtl8xxxu_write32(priv, REG_BT_COEX_TABLE3, 0x00ffffff);
+       rtl8xxxu_write8(priv, REG_BT_COEX_TABLE4, 0x03);
 
        memset(&h2c, 0, sizeof(struct h2c_cmd));
        h2c.bt_info.cmd = H2C_8723B_BT_INFO;
        h2c.bt_info.data = BIT(0);
        rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.bt_info));
 
-       rtl8xxxu_write32(priv, REG_BT_COEX_TABLE1, 0x55555555);
-       rtl8xxxu_write32(priv, REG_BT_COEX_TABLE2, 0x5a5a5a5a);
-       rtl8xxxu_write32(priv, REG_BT_COEX_TABLE3, 0x00ffffff);
-       rtl8xxxu_write32(priv, REG_BT_COEX_TABLE4, 0x00000003);
-
        memset(&h2c, 0, sizeof(struct h2c_cmd));
        h2c.ignore_wlan.cmd = H2C_8723B_BT_IGNORE_WLANACT;
        h2c.ignore_wlan.data = 0;
        rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.ignore_wlan));
 }
 
+static void rtl8723b_disable_rf(struct rtl8xxxu_priv *priv)
+{
+       u32 val32;
+
+       rtl8xxxu_write8(priv, REG_TXPAUSE, 0xff);
+
+       val32 = rtl8xxxu_read32(priv, REG_RX_WAIT_CCA);
+       val32 &= ~(BIT(22) | BIT(23));
+       rtl8xxxu_write32(priv, REG_RX_WAIT_CCA, val32);
+}
+
+static void rtl8723bu_init_aggregation(struct rtl8xxxu_priv *priv)
+{
+       u32 agg_rx;
+       u8 agg_ctrl;
+
+       /*
+        * For now simply disable RX aggregation
+        */
+       agg_ctrl = rtl8xxxu_read8(priv, REG_TRXDMA_CTRL);
+       agg_ctrl &= ~TRXDMA_CTRL_RXDMA_AGG_EN;
+
+       agg_rx = rtl8xxxu_read32(priv, REG_RXDMA_AGG_PG_TH);
+       agg_rx &= ~RXDMA_USB_AGG_ENABLE;
+       agg_rx &= ~0xff0f;
+
+       rtl8xxxu_write8(priv, REG_TRXDMA_CTRL, agg_ctrl);
+       rtl8xxxu_write32(priv, REG_RXDMA_AGG_PG_TH, agg_rx);
+}
+
+static void rtl8723bu_init_statistics(struct rtl8xxxu_priv *priv)
+{
+       u32 val32;
+
+       /* Time duration for NHM unit: 4us, 0x2710=40ms */
+       rtl8xxxu_write16(priv, REG_NHM_TIMER_8723B + 2, 0x2710);
+       rtl8xxxu_write16(priv, REG_NHM_TH9_TH10_8723B + 2, 0xffff);
+       rtl8xxxu_write32(priv, REG_NHM_TH3_TO_TH0_8723B, 0xffffff52);
+       rtl8xxxu_write32(priv, REG_NHM_TH7_TO_TH4_8723B, 0xffffffff);
+       /* TH8 */
+       val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK);
+       val32 |= 0xff;
+       rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32);
+       /* Enable CCK */
+       val32 = rtl8xxxu_read32(priv, REG_NHM_TH9_TH10_8723B);
+       val32 |= BIT(8) | BIT(9) | BIT(10);
+       rtl8xxxu_write32(priv, REG_NHM_TH9_TH10_8723B, val32);
+       /* Max power amongst all RX antennas */
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_FA_RSTC);
+       val32 |= BIT(7);
+       rtl8xxxu_write32(priv, REG_OFDM0_FA_RSTC, val32);
+}
+
 static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
 {
        struct rtl8xxxu_priv *priv = hw->priv;
@@ -6005,7 +6315,7 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
                 */
                if (priv->rtlchip == 0x8723bu) {
                        val8 = rtl8xxxu_read8(priv, REG_TX_REPORT_CTRL);
-                       val8 |= BIT(1);
+                       val8 |= TX_REPORT_CTRL_TIMER_ENABLE;
                        rtl8xxxu_write8(priv, REG_TX_REPORT_CTRL, val8);
                        /* Set MAX RPT MACID */
                        rtl8xxxu_write8(priv, REG_TX_REPORT_CTRL + 1, 0x02);
@@ -6028,18 +6338,6 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
        if (ret)
                goto exit;
 
-       /* Fix USB interface interference issue */
-       if (priv->rtlchip == 0x8723a) {
-               rtl8xxxu_write8(priv, 0xfe40, 0xe0);
-               rtl8xxxu_write8(priv, 0xfe41, 0x8d);
-               rtl8xxxu_write8(priv, 0xfe42, 0x80);
-               rtl8xxxu_write32(priv, REG_TXDMA_OFFSET_CHK, 0xfd0320);
-       } else {
-               val32 = rtl8xxxu_read32(priv, REG_TXDMA_OFFSET_CHK);
-               val32 |= TXDMA_OFFSET_DROP_DATA_EN;
-               rtl8xxxu_write32(priv, REG_TXDMA_OFFSET_CHK, val32);
-       }
-
        /* Solve too many protocol error on USB bus */
        /* Can't do this for 8188/8192 UMC A cut parts */
        if (priv->rtlchip == 0x8723a ||
@@ -6093,6 +6391,13 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
        case 0x8723b:
                rftable = rtl8723bu_radioa_1t_init_table;
                ret = rtl8xxxu_init_phy_rf(priv, rftable, RF_A);
+               /*
+                * PHY LCK
+                */
+               rtl8xxxu_write_rfreg(priv, RF_A, 0xb0, 0xdfbe0);
+               rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_MODE_AG, 0x8c01);
+               msleep(200);
+               rtl8xxxu_write_rfreg(priv, RF_A, 0xb0, 0xdffe0);
                break;
        case 0x8188c:
                if (priv->hi_pa)
@@ -6120,14 +6425,28 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
        if (ret)
                goto exit;
 
-       if (priv->rtlchip == 0x8723a) { /* Reduce 80M spur */
+       /*
+        * Chip specific quirks
+        */
+       if (priv->rtlchip == 0x8723a) {
+               /* Fix USB interface interference issue */
+               rtl8xxxu_write8(priv, 0xfe40, 0xe0);
+               rtl8xxxu_write8(priv, 0xfe41, 0x8d);
+               rtl8xxxu_write8(priv, 0xfe42, 0x80);
+               rtl8xxxu_write32(priv, REG_TXDMA_OFFSET_CHK, 0xfd0320);
+
+               /* Reduce 80M spur */
                rtl8xxxu_write32(priv, REG_AFE_XTAL_CTRL, 0x0381808d);
                rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff83);
                rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff82);
                rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff83);
+       } else {
+               val32 = rtl8xxxu_read32(priv, REG_TXDMA_OFFSET_CHK);
+               val32 |= TXDMA_OFFSET_DROP_DATA_EN;
+               rtl8xxxu_write32(priv, REG_TXDMA_OFFSET_CHK, val32);
        }
 
-       if (!macpower){
+       if (!macpower) {
                if (priv->ep_tx_normal_queue)
                        val8 = TX_PAGE_NUM_NORM_PQ;
                else
@@ -6182,7 +6501,10 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
        /*
         * Set RX page boundary
         */
-       rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, 0x27ff);
+       if (priv->rtlchip == 0x8723b)
+               rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, 0x3f7f);
+       else
+               rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, 0x27ff);
        /*
         * Transfer page size is always 128
         */
@@ -6275,6 +6597,42 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
        rtl8xxxu_write8(priv, REG_BEACON_DMA_TIME, BEACON_DMA_ATIME_INT_TIME);
        rtl8xxxu_write16(priv, REG_BEACON_TCFG, 0x660F);
 
+       /*
+        * Initialize burst parameters
+        */
+       if (priv->rtlchip == 0x8723b) {
+               /*
+                * For USB high speed set 512B packets
+                */
+               val8 = rtl8xxxu_read8(priv, REG_RXDMA_PRO_8723B);
+               val8 &= ~(BIT(4) | BIT(5));
+               val8 |= BIT(4);
+               val8 |= BIT(1) | BIT(2) | BIT(3);
+               rtl8xxxu_write8(priv, REG_RXDMA_PRO_8723B, val8);
+
+               /*
+                * For USB high speed set 512B packets
+                */
+               val8 = rtl8xxxu_read8(priv, REG_HT_SINGLE_AMPDU_8723B);
+               val8 |= BIT(7);
+               rtl8xxxu_write8(priv, REG_HT_SINGLE_AMPDU_8723B, val8);
+
+               rtl8xxxu_write16(priv, REG_MAX_AGGR_NUM, 0x0c14);
+               rtl8xxxu_write8(priv, REG_AMPDU_MAX_TIME_8723B, 0x5e);
+               rtl8xxxu_write32(priv, REG_AGGLEN_LMT, 0xffffffff);
+               rtl8xxxu_write8(priv, REG_RX_PKT_LIMIT, 0x18);
+               rtl8xxxu_write8(priv, REG_PIFS, 0x00);
+               rtl8xxxu_write8(priv, REG_USTIME_TSF_8723B, 0x50);
+               rtl8xxxu_write8(priv, REG_USTIME_EDCA, 0x50);
+
+               val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL);
+               val8 |= BIT(5) | BIT(6);
+               rtl8xxxu_write8(priv, REG_RSV_CTRL, val8);
+       }
+
+       if (priv->fops->init_aggregation)
+               priv->fops->init_aggregation(priv);
+
        /*
         * Enable CCK and OFDM block
         */
@@ -6290,7 +6648,7 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
        /*
         * Start out with default power levels for channel 6, 20MHz
         */
-       rtl8723a_set_tx_power(priv, 1, false);
+       priv->fops->set_tx_power(priv, 1, false);
 
        /* Let the 8051 take control of antenna setting */
        val8 = rtl8xxxu_read8(priv, REG_LEDCFG2);
@@ -6304,6 +6662,9 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
 
        rtl8xxxu_write16(priv, REG_FAST_EDCA_CTRL, 0);
 
+       if (priv->fops->init_statistics)
+               priv->fops->init_statistics(priv);
+
        rtl8723a_phy_lc_calibrate(priv);
 
        priv->fops->phy_iq_calibrate(priv);
@@ -6311,11 +6672,11 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
        /*
         * This should enable thermal meter
         */
-       rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_T_METER, 0x60);
-
-       /* Init BT hw config. */
-       if (priv->fops->init_bt)
-               priv->fops->init_bt(priv);
+       if (priv->fops->has_s0s1)
+               rtl8xxxu_write_rfreg(priv,
+                                    RF_A, RF6052_REG_T_METER_8723B, 0x37cf8);
+       else
+               rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_T_METER, 0x60);
 
        /* Set NAV_UPPER to 30000us */
        val8 = ((30000 + NAV_UPPER_UNIT - 1) / NAV_UPPER_UNIT);
@@ -6347,7 +6708,7 @@ static void rtl8xxxu_disable_device(struct ieee80211_hw *hw)
 {
        struct rtl8xxxu_priv *priv = hw->priv;
 
-       rtl8xxxu_power_off(priv);
+       priv->fops->power_off(priv);
 }
 
 static void rtl8xxxu_cam_write(struct rtl8xxxu_priv *priv,
@@ -6414,11 +6775,13 @@ static void rtl8xxxu_sw_scan_complete(struct ieee80211_hw *hw,
        rtl8xxxu_write8(priv, REG_BEACON_CTRL, val8);
 }
 
-static void rtl8xxxu_update_rate_mask(struct rtl8xxxu_priv *priv,
-                                     u32 ramask, int sgi)
+static void rtl8723au_update_rate_mask(struct rtl8xxxu_priv *priv,
+                                      u32 ramask, int sgi)
 {
        struct h2c_cmd h2c;
 
+       memset(&h2c, 0, sizeof(struct h2c_cmd));
+
        h2c.ramask.cmd = H2C_SET_RATE_MASK;
        h2c.ramask.mask_lo = cpu_to_le16(ramask & 0xffff);
        h2c.ramask.mask_hi = cpu_to_le16(ramask >> 16);
@@ -6432,6 +6795,65 @@ static void rtl8xxxu_update_rate_mask(struct rtl8xxxu_priv *priv,
        rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.ramask));
 }
 
+static void rtl8723bu_update_rate_mask(struct rtl8xxxu_priv *priv,
+                                      u32 ramask, int sgi)
+{
+       struct h2c_cmd h2c;
+       u8 bw = 0;
+
+       memset(&h2c, 0, sizeof(struct h2c_cmd));
+
+       h2c.b_macid_cfg.cmd = H2C_8723B_MACID_CFG_RAID;
+       h2c.b_macid_cfg.ramask0 = ramask & 0xff;
+       h2c.b_macid_cfg.ramask1 = (ramask >> 8) & 0xff;
+       h2c.b_macid_cfg.ramask2 = (ramask >> 16) & 0xff;
+       h2c.b_macid_cfg.ramask3 = (ramask >> 24) & 0xff;
+
+       h2c.ramask.arg = 0x80;
+       h2c.b_macid_cfg.data1 = 0;
+       if (sgi)
+               h2c.b_macid_cfg.data1 |= BIT(7);
+
+       h2c.b_macid_cfg.data2 = bw;
+
+       dev_dbg(&priv->udev->dev, "%s: rate mask %08x, arg %02x, size %zi\n",
+               __func__, ramask, h2c.ramask.arg, sizeof(h2c.b_macid_cfg));
+       rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.b_macid_cfg));
+}
+
+static void rtl8723au_report_connect(struct rtl8xxxu_priv *priv,
+                                    u8 macid, bool connect)
+{
+       struct h2c_cmd h2c;
+
+       memset(&h2c, 0, sizeof(struct h2c_cmd));
+
+       h2c.joinbss.cmd = H2C_JOIN_BSS_REPORT;
+
+       if (connect)
+               h2c.joinbss.data = H2C_JOIN_BSS_CONNECT;
+       else
+               h2c.joinbss.data = H2C_JOIN_BSS_DISCONNECT;
+
+       rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.joinbss));
+}
+
+static void rtl8723bu_report_connect(struct rtl8xxxu_priv *priv,
+                                    u8 macid, bool connect)
+{
+       struct h2c_cmd h2c;
+
+       memset(&h2c, 0, sizeof(struct h2c_cmd));
+
+       h2c.media_status_rpt.cmd = H2C_8723B_MEDIA_STATUS_RPT;
+       if (connect)
+               h2c.media_status_rpt.parm |= BIT(0);
+       else
+               h2c.media_status_rpt.parm &= ~BIT(0);
+
+       rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.media_status_rpt));
+}
+
 static void rtl8xxxu_set_basic_rates(struct rtl8xxxu_priv *priv, u32 rate_cfg)
 {
        u32 val32;
@@ -6464,11 +6886,8 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        u8 val8;
 
        if (changed & BSS_CHANGED_ASSOC) {
-               struct h2c_cmd h2c;
-
                dev_dbg(dev, "Changed ASSOC: %i!\n", bss_conf->assoc);
 
-               memset(&h2c, 0, sizeof(struct h2c_cmd));
                rtl8xxxu_set_linktype(priv, vif->type);
 
                if (bss_conf->assoc) {
@@ -6498,7 +6917,7 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                                sgi = 1;
                        rcu_read_unlock();
 
-                       rtl8xxxu_update_rate_mask(priv, ramask, sgi);
+                       priv->fops->update_rate_mask(priv, ramask, sgi);
 
                        rtl8xxxu_write8(priv, REG_BCN_MAX_ERR, 0xff);
 
@@ -6508,16 +6927,14 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        rtl8xxxu_write16(priv, REG_BCN_PSR_RPT,
                                         0xc000 | bss_conf->aid);
 
-                       h2c.joinbss.data = H2C_JOIN_BSS_CONNECT;
+                       priv->fops->report_connect(priv, 0, true);
                } else {
                        val8 = rtl8xxxu_read8(priv, REG_BEACON_CTRL);
                        val8 |= BEACON_DISABLE_TSF_UPDATE;
                        rtl8xxxu_write8(priv, REG_BEACON_CTRL, val8);
 
-                       h2c.joinbss.data = H2C_JOIN_BSS_DISCONNECT;
+                       priv->fops->report_connect(priv, 0, false);
                }
-               h2c.joinbss.cmd = H2C_JOIN_BSS_REPORT;
-               rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.joinbss));
        }
 
        if (changed & BSS_CHANGED_ERP_PREAMBLE) {
@@ -6592,7 +7009,12 @@ static u32 rtl8xxxu_queue_select(struct ieee80211_hw *hw, struct sk_buff *skb)
        return queue;
 }
 
-static void rtl8xxxu_calc_tx_desc_csum(struct rtl8xxxu_tx_desc *tx_desc)
+/*
+ * Despite newer chips 8723b/8812/8821 having a larger TX descriptor
+ * format. The descriptor checksum is still only calculated over the
+ * initial 32 bytes of the descriptor!
+ */
+static void rtl8xxxu_calc_tx_desc_csum(struct rtl8723au_tx_desc *tx_desc)
 {
        __le16 *ptr = (__le16 *)tx_desc;
        u16 csum = 0;
@@ -6604,7 +7026,7 @@ static void rtl8xxxu_calc_tx_desc_csum(struct rtl8xxxu_tx_desc *tx_desc)
         */
        tx_desc->csum = cpu_to_le16(0);
 
-       for (i = 0; i < (sizeof(struct rtl8xxxu_tx_desc) / sizeof(u16)); i++)
+       for (i = 0; i < (sizeof(struct rtl8723au_tx_desc) / sizeof(u16)); i++)
                csum = csum ^ le16_to_cpu(ptr[i]);
 
        tx_desc->csum |= cpu_to_le16(csum);
@@ -6673,13 +7095,15 @@ static void rtl8xxxu_tx_complete(struct urb *urb)
        struct sk_buff *skb = (struct sk_buff *)urb->context;
        struct ieee80211_tx_info *tx_info;
        struct ieee80211_hw *hw;
+       struct rtl8xxxu_priv *priv;
        struct rtl8xxxu_tx_urb *tx_urb =
                container_of(urb, struct rtl8xxxu_tx_urb, urb);
 
        tx_info = IEEE80211_SKB_CB(skb);
        hw = tx_info->rate_driver_data[0];
+       priv = hw->priv;
 
-       skb_pull(skb, sizeof(struct rtl8xxxu_tx_desc));
+       skb_pull(skb, priv->fops->tx_desc_size);
 
        ieee80211_tx_info_clear_status(tx_info);
        tx_info->status.rates[0].idx = -1;
@@ -6690,7 +7114,7 @@ static void rtl8xxxu_tx_complete(struct urb *urb)
 
        ieee80211_tx_status_irqsafe(hw, skb);
 
-       rtl8xxxu_free_tx_urb(hw->priv, tx_urb);
+       rtl8xxxu_free_tx_urb(priv, tx_urb);
 }
 
 static void rtl8xxxu_dump_action(struct device *dev,
@@ -6740,7 +7164,8 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
        struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
        struct ieee80211_rate *tx_rate = ieee80211_get_tx_rate(hw, tx_info);
        struct rtl8xxxu_priv *priv = hw->priv;
-       struct rtl8xxxu_tx_desc *tx_desc;
+       struct rtl8723au_tx_desc *tx_desc;
+       struct rtl8723bu_tx_desc *tx_desc40;
        struct rtl8xxxu_tx_urb *tx_urb;
        struct ieee80211_sta *sta = NULL;
        struct ieee80211_vif *vif = tx_info->control.vif;
@@ -6749,16 +7174,18 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
        u16 pktlen = skb->len;
        u16 seq_number;
        u16 rate_flag = tx_info->control.rates[0].flags;
+       int tx_desc_size = priv->fops->tx_desc_size;
        int ret;
+       bool usedesc40, ampdu_enable;
 
-       if (skb_headroom(skb) < sizeof(struct rtl8xxxu_tx_desc)) {
+       if (skb_headroom(skb) < tx_desc_size) {
                dev_warn(dev,
                         "%s: Not enough headroom (%i) for tx descriptor\n",
                         __func__, skb_headroom(skb));
                goto error;
        }
 
-       if (unlikely(skb->len > (65535 - sizeof(struct rtl8xxxu_tx_desc)))) {
+       if (unlikely(skb->len > (65535 - tx_desc_size))) {
                dev_warn(dev, "%s: Trying to send over-sized skb (%i)\n",
                         __func__, skb->len);
                goto error;
@@ -6777,17 +7204,17 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
        if (ieee80211_is_action(hdr->frame_control))
                rtl8xxxu_dump_action(dev, hdr);
 
+       usedesc40 = (tx_desc_size == 40);
        tx_info->rate_driver_data[0] = hw;
 
        if (control && control->sta)
                sta = control->sta;
 
-       tx_desc = (struct rtl8xxxu_tx_desc *)
-               skb_push(skb, sizeof(struct rtl8xxxu_tx_desc));
+       tx_desc = (struct rtl8723au_tx_desc *)skb_push(skb, tx_desc_size);
 
-       memset(tx_desc, 0, sizeof(struct rtl8xxxu_tx_desc));
+       memset(tx_desc, 0, tx_desc_size);
        tx_desc->pkt_size = cpu_to_le16(pktlen);
-       tx_desc->pkt_offset = sizeof(struct rtl8xxxu_tx_desc);
+       tx_desc->pkt_offset = tx_desc_size;
 
        tx_desc->txdw0 =
                TXDESC_OWN | TXDESC_FIRST_SEGMENT | TXDESC_LAST_SEGMENT;
@@ -6813,19 +7240,8 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
                }
        }
 
-       seq_number = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
-       tx_desc->txdw3 = cpu_to_le32((u32)seq_number << TXDESC_SEQ_SHIFT);
-
-       if (rate_flag & IEEE80211_TX_RC_MCS)
-               rate = tx_info->control.rates[0].idx + DESC_RATE_MCS0;
-       else
-               rate = tx_rate->hw_value;
-       tx_desc->txdw5 = cpu_to_le32(rate);
-
-       if (ieee80211_is_data(hdr->frame_control))
-               tx_desc->txdw5 |= cpu_to_le32(0x0001ff00);
-
        /* (tx_info->flags & IEEE80211_TX_CTL_AMPDU) && */
+       ampdu_enable = false;
        if (ieee80211_is_data_qos(hdr->frame_control) && sta) {
                if (sta->ht_cap.ht_supported) {
                        u32 ampdu, val32;
@@ -6833,35 +7249,118 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
                        ampdu = (u32)sta->ht_cap.ampdu_density;
                        val32 = ampdu << TXDESC_AMPDU_DENSITY_SHIFT;
                        tx_desc->txdw2 |= cpu_to_le32(val32);
-                       tx_desc->txdw1 |= cpu_to_le32(TXDESC_AGG_ENABLE);
-               } else
-                       tx_desc->txdw1 |= cpu_to_le32(TXDESC_BK);
-       } else
-               tx_desc->txdw1 |= cpu_to_le32(TXDESC_BK);
 
-       if (ieee80211_is_data_qos(hdr->frame_control))
-               tx_desc->txdw4 |= cpu_to_le32(TXDESC_QOS);
-       if (rate_flag & IEEE80211_TX_RC_USE_SHORT_PREAMBLE ||
-           (sta && vif && vif->bss_conf.use_short_preamble))
-               tx_desc->txdw4 |= cpu_to_le32(TXDESC_SHORT_PREAMBLE);
-       if (rate_flag & IEEE80211_TX_RC_SHORT_GI ||
-           (ieee80211_is_data_qos(hdr->frame_control) &&
-            sta && sta->ht_cap.cap &
-            (IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20))) {
-               tx_desc->txdw5 |= cpu_to_le32(TXDESC_SHORT_GI);
-       }
-       if (ieee80211_is_mgmt(hdr->frame_control)) {
-               tx_desc->txdw5 = cpu_to_le32(tx_rate->hw_value);
-               tx_desc->txdw4 |= cpu_to_le32(TXDESC_USE_DRIVER_RATE);
-               tx_desc->txdw5 |= cpu_to_le32(6 << TXDESC_RETRY_LIMIT_SHIFT);
-               tx_desc->txdw5 |= cpu_to_le32(TXDESC_RETRY_LIMIT_ENABLE);
+                       ampdu_enable = true;
+               }
        }
 
-       if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) {
-               /* Use RTS rate 24M - does the mac80211 tell us which to use? */
-               tx_desc->txdw4 |= cpu_to_le32(DESC_RATE_24M);
-               tx_desc->txdw4 |= cpu_to_le32(TXDESC_RTS_CTS_ENABLE);
-               tx_desc->txdw4 |= cpu_to_le32(TXDESC_HW_RTS_ENABLE);
+       if (rate_flag & IEEE80211_TX_RC_MCS)
+               rate = tx_info->control.rates[0].idx + DESC_RATE_MCS0;
+       else
+               rate = tx_rate->hw_value;
+
+       seq_number = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
+       if (!usedesc40) {
+               tx_desc->txdw5 = cpu_to_le32(rate);
+
+               if (ieee80211_is_data(hdr->frame_control))
+                       tx_desc->txdw5 |= cpu_to_le32(0x0001ff00);
+
+               tx_desc->txdw3 =
+                       cpu_to_le32((u32)seq_number << TXDESC_SEQ_SHIFT_8723A);
+
+               if (ampdu_enable)
+                       tx_desc->txdw1 |= cpu_to_le32(TXDESC_AGG_ENABLE_8723A);
+               else
+                       tx_desc->txdw1 |= cpu_to_le32(TXDESC_AGG_BREAK_8723A);
+
+               if (ieee80211_is_mgmt(hdr->frame_control)) {
+                       tx_desc->txdw5 = cpu_to_le32(tx_rate->hw_value);
+                       tx_desc->txdw4 |=
+                               cpu_to_le32(TXDESC_USE_DRIVER_RATE_8723A);
+                       tx_desc->txdw5 |=
+                               cpu_to_le32(6 <<
+                                           TXDESC_RETRY_LIMIT_SHIFT_8723A);
+                       tx_desc->txdw5 |=
+                               cpu_to_le32(TXDESC_RETRY_LIMIT_ENABLE_8723A);
+               }
+
+               if (ieee80211_is_data_qos(hdr->frame_control))
+                       tx_desc->txdw4 |= cpu_to_le32(TXDESC_QOS_8723A);
+
+               if (rate_flag & IEEE80211_TX_RC_USE_SHORT_PREAMBLE ||
+                   (sta && vif && vif->bss_conf.use_short_preamble))
+                       tx_desc->txdw4 |=
+                               cpu_to_le32(TXDESC_SHORT_PREAMBLE_8723A);
+
+               if (rate_flag & IEEE80211_TX_RC_SHORT_GI ||
+                   (ieee80211_is_data_qos(hdr->frame_control) &&
+                    sta && sta->ht_cap.cap &
+                    (IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20))) {
+                       tx_desc->txdw5 |= cpu_to_le32(TXDESC_SHORT_GI);
+               }
+
+               if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) {
+                       /*
+                        * Use RTS rate 24M - does the mac80211 tell
+                        * us which to use?
+                        */
+                       tx_desc->txdw4 |=
+                               cpu_to_le32(DESC_RATE_24M <<
+                                           TXDESC_RTS_RATE_SHIFT_8723A);
+                       tx_desc->txdw4 |=
+                               cpu_to_le32(TXDESC_RTS_CTS_ENABLE_8723A);
+                       tx_desc->txdw4 |=
+                               cpu_to_le32(TXDESC_HW_RTS_ENABLE_8723A);
+               }
+       } else {
+               tx_desc40 = (struct rtl8723bu_tx_desc *)tx_desc;
+
+               tx_desc40->txdw4 = cpu_to_le32(rate);
+               if (ieee80211_is_data(hdr->frame_control)) {
+                       tx_desc->txdw4 |=
+                               cpu_to_le32(0x1f <<
+                                           TXDESC_DATA_RATE_FB_SHIFT_8723B);
+               }
+
+               tx_desc40->txdw9 =
+                       cpu_to_le32((u32)seq_number << TXDESC_SEQ_SHIFT_8723B);
+
+               if (ampdu_enable)
+                       tx_desc40->txdw2 |=
+                               cpu_to_le32(TXDESC_AGG_ENABLE_8723B);
+               else
+                       tx_desc40->txdw2 |= cpu_to_le32(TXDESC_AGG_BREAK_8723B);
+
+               if (ieee80211_is_mgmt(hdr->frame_control)) {
+                       tx_desc40->txdw4 = cpu_to_le32(tx_rate->hw_value);
+                       tx_desc40->txdw3 |=
+                               cpu_to_le32(TXDESC_USE_DRIVER_RATE_8723B);
+                       tx_desc40->txdw4 |=
+                               cpu_to_le32(6 <<
+                                           TXDESC_RETRY_LIMIT_SHIFT_8723B);
+                       tx_desc40->txdw4 |=
+                               cpu_to_le32(TXDESC_RETRY_LIMIT_ENABLE_8723B);
+               }
+
+               if (rate_flag & IEEE80211_TX_RC_USE_SHORT_PREAMBLE ||
+                   (sta && vif && vif->bss_conf.use_short_preamble))
+                       tx_desc40->txdw5 |=
+                               cpu_to_le32(TXDESC_SHORT_PREAMBLE_8723B);
+
+               if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) {
+                       /*
+                        * Use RTS rate 24M - does the mac80211 tell
+                        * us which to use?
+                        */
+                       tx_desc->txdw4 |=
+                               cpu_to_le32(DESC_RATE_24M <<
+                                           TXDESC_RTS_RATE_SHIFT_8723B);
+                       tx_desc->txdw3 |=
+                               cpu_to_le32(TXDESC_RTS_CTS_ENABLE_8723B);
+                       tx_desc->txdw3 |=
+                               cpu_to_le32(TXDESC_HW_RTS_ENABLE_8723B);
+               }
        }
 
        rtl8xxxu_calc_tx_desc_csum(tx_desc);
@@ -6883,13 +7382,13 @@ error:
 
 static void rtl8xxxu_rx_parse_phystats(struct rtl8xxxu_priv *priv,
                                       struct ieee80211_rx_status *rx_status,
-                                      struct rtl8xxxu_rx_desc *rx_desc,
-                                      struct rtl8723au_phy_stats *phy_stats)
+                                      struct rtl8723au_phy_stats *phy_stats,
+                                      u32 rxmcs)
 {
        if (phy_stats->sgi_en)
                rx_status->flag |= RX_FLAG_SHORT_GI;
 
-       if (rx_desc->rxmcs < DESC_RATE_6M) {
+       if (rxmcs < DESC_RATE_6M) {
                /*
                 * Handle PHY stats for CCK rates
                 */
@@ -7017,7 +7516,8 @@ static int rtl8723au_parse_rx_desc(struct rtl8xxxu_priv *priv,
        skb_pull(skb, drvinfo_sz + desc_shift);
 
        if (rx_desc->phy_stats)
-               rtl8xxxu_rx_parse_phystats(priv, rx_status, rx_desc, phy_stats);
+               rtl8xxxu_rx_parse_phystats(priv, rx_status, phy_stats,
+                                          rx_desc->rxmcs);
 
        rx_status->mactime = le32_to_cpu(rx_desc->tsfl);
        rx_status->flag |= RX_FLAG_MACTIME_START;
@@ -7047,7 +7547,6 @@ static int rtl8723bu_parse_rx_desc(struct rtl8xxxu_priv *priv,
                (struct rtl8723bu_rx_desc *)skb->data;
        struct rtl8723au_phy_stats *phy_stats;
        int drvinfo_sz, desc_shift;
-       int rx_type;
 
        skb_pull(skb, sizeof(struct rtl8723bu_rx_desc));
 
@@ -7057,6 +7556,16 @@ static int rtl8723bu_parse_rx_desc(struct rtl8xxxu_priv *priv,
        desc_shift = rx_desc->shift;
        skb_pull(skb, drvinfo_sz + desc_shift);
 
+       if (rx_desc->rpt_sel) {
+               struct device *dev = &priv->udev->dev;
+               dev_dbg(dev, "%s: C2H packet\n", __func__);
+               return RX_TYPE_C2H;
+       }
+
+       if (rx_desc->phy_stats)
+               rtl8xxxu_rx_parse_phystats(priv, rx_status, phy_stats,
+                                          rx_desc->rxmcs);
+
        rx_status->mactime = le32_to_cpu(rx_desc->tsfl);
        rx_status->flag |= RX_FLAG_MACTIME_START;
 
@@ -7074,15 +7583,7 @@ static int rtl8723bu_parse_rx_desc(struct rtl8xxxu_priv *priv,
                rx_status->rate_idx = rx_desc->rxmcs;
        }
 
-       if (rx_desc->rpt_sel) {
-               struct device *dev = &priv->udev->dev;
-               dev_dbg(dev, "%s: C2H packet\n", __func__);
-               rx_type = RX_TYPE_C2H;
-       } else {
-               rx_type = RX_TYPE_DATA_PKT;
-       }
-
-       return rx_type;
+       return RX_TYPE_DATA_PKT;
 }
 
 static void rtl8723bu_handle_c2h(struct rtl8xxxu_priv *priv,
@@ -7094,29 +7595,38 @@ static void rtl8723bu_handle_c2h(struct rtl8xxxu_priv *priv,
 
        len = skb->len - 2;
 
-       dev_info(dev, "C2H ID %02x seq %02x, len %02x source %02x\n",
-                c2h->id, c2h->seq, len, c2h->bt_info.response_source);
+       dev_dbg(dev, "C2H ID %02x seq %02x, len %02x source %02x\n",
+               c2h->id, c2h->seq, len, c2h->bt_info.response_source);
 
        switch(c2h->id) {
        case C2H_8723B_BT_INFO:
                if (c2h->bt_info.response_source >
                    BT_INFO_SRC_8723B_BT_ACTIVE_SEND)
-                       dev_info(dev, "C2H_BT_INFO WiFi only firmware\n");
+                       dev_dbg(dev, "C2H_BT_INFO WiFi only firmware\n");
                else
-                       dev_info(dev, "C2H_BT_INFO BT/WiFi coexist firmware\n");
+                       dev_dbg(dev, "C2H_BT_INFO BT/WiFi coexist firmware\n");
 
                if (c2h->bt_info.bt_has_reset)
-                       dev_info(dev, "BT has been reset\n");
+                       dev_dbg(dev, "BT has been reset\n");
                if (c2h->bt_info.tx_rx_mask)
-                       dev_info(dev, "BT TRx mask\n");
+                       dev_dbg(dev, "BT TRx mask\n");
 
                break;
        case C2H_8723B_BT_MP_INFO:
-               dev_info(dev, "C2H_MP_INFO ext ID %02x, status %02x\n",
-                        c2h->bt_mp_info.ext_id, c2h->bt_mp_info.status);
+               dev_dbg(dev, "C2H_MP_INFO ext ID %02x, status %02x\n",
+                       c2h->bt_mp_info.ext_id, c2h->bt_mp_info.status);
+               break;
+       case C2H_8723B_RA_REPORT:
+               dev_dbg(dev,
+                       "C2H RA RPT: rate %02x, unk %i, macid %02x, noise %i\n",
+                       c2h->ra_report.rate, c2h->ra_report.dummy0_0,
+                       c2h->ra_report.macid, c2h->ra_report.noisy_state);
                break;
        default:
-               pr_info("%s: Unhandled C2H event %02x\n", __func__, c2h->id);
+               dev_info(dev, "Unhandled C2H event %02x seq %02x\n",
+                        c2h->id, c2h->seq);
+               print_hex_dump(KERN_INFO, "C2H content: ", DUMP_PREFIX_NONE,
+                              16, 1, c2h->raw.payload, len, false);
                break;
        }
 }
@@ -7310,9 +7820,9 @@ static int rtl8xxxu_config(struct ieee80211_hw *hw, u32 changed)
 
                channel = hw->conf.chandef.chan->hw_value;
 
-               rtl8723a_set_tx_power(priv, channel, ht40);
+               priv->fops->set_tx_power(priv, channel, ht40);
 
-               rtl8723au_config_channel(hw);
+               priv->fops->config_channel(hw);
        }
 
 exit:
@@ -7568,7 +8078,7 @@ static int rtl8xxxu_start(struct ieee80211_hw *hw)
        init_usb_anchor(&priv->tx_anchor);
        init_usb_anchor(&priv->int_anchor);
 
-       rtl8723a_enable_rf(priv);
+       priv->fops->enable_rf(priv);
        if (priv->usb_interrupts) {
                ret = rtl8xxxu_submit_int_urb(hw);
                if (ret)
@@ -7651,7 +8161,7 @@ static void rtl8xxxu_stop(struct ieee80211_hw *hw)
        if (priv->usb_interrupts)
                usb_kill_anchored_urbs(&priv->int_anchor);
 
-       rtl8723a_disable_rf(priv);
+       priv->fops->disable_rf(priv);
 
        /*
         * Disable interrupts
@@ -7882,7 +8392,7 @@ static int rtl8xxxu_probe(struct usb_interface *interface,
        SET_IEEE80211_DEV(priv->hw, &interface->dev);
        SET_IEEE80211_PERM_ADDR(hw, priv->mac_addr);
 
-       hw->extra_tx_headroom = sizeof(struct rtl8xxxu_tx_desc);
+       hw->extra_tx_headroom = priv->fops->tx_desc_size;
        ieee80211_hw_set(hw, SIGNAL_DBM);
        /*
         * The firmware handles rate control
@@ -7930,13 +8440,21 @@ static struct rtl8xxxu_fileops rtl8723au_fops = {
        .parse_efuse = rtl8723au_parse_efuse,
        .load_firmware = rtl8723au_load_firmware,
        .power_on = rtl8723au_power_on,
+       .power_off = rtl8xxxu_power_off,
+       .reset_8051 = rtl8xxxu_reset_8051,
        .llt_init = rtl8xxxu_init_llt_table,
        .phy_iq_calibrate = rtl8723au_phy_iq_calibrate,
        .config_channel = rtl8723au_config_channel,
        .parse_rx_desc = rtl8723au_parse_rx_desc,
+       .enable_rf = rtl8723a_enable_rf,
+       .disable_rf = rtl8723a_disable_rf,
+       .set_tx_power = rtl8723a_set_tx_power,
+       .update_rate_mask = rtl8723au_update_rate_mask,
+       .report_connect = rtl8723au_report_connect,
        .writeN_block_size = 1024,
        .mbox_ext_reg = REG_HMBOX_EXT_0,
        .mbox_ext_width = 2,
+       .tx_desc_size = sizeof(struct rtl8723au_tx_desc),
        .adda_1t_init = 0x0b1b25a0,
        .adda_1t_path_on = 0x0bdb25a0,
        .adda_2t_path_on_a = 0x04db25a4,
@@ -7947,15 +8465,24 @@ static struct rtl8xxxu_fileops rtl8723bu_fops = {
        .parse_efuse = rtl8723bu_parse_efuse,
        .load_firmware = rtl8723bu_load_firmware,
        .power_on = rtl8723bu_power_on,
+       .power_off = rtl8723bu_power_off,
+       .reset_8051 = rtl8723bu_reset_8051,
        .llt_init = rtl8xxxu_auto_llt_table,
        .phy_init_antenna_selection = rtl8723bu_phy_init_antenna_selection,
        .phy_iq_calibrate = rtl8723bu_phy_iq_calibrate,
        .config_channel = rtl8723bu_config_channel,
-       .init_bt = rtl8723bu_init_bt,
        .parse_rx_desc = rtl8723bu_parse_rx_desc,
+       .init_aggregation = rtl8723bu_init_aggregation,
+       .init_statistics = rtl8723bu_init_statistics,
+       .enable_rf = rtl8723b_enable_rf,
+       .disable_rf = rtl8723b_disable_rf,
+       .set_tx_power = rtl8723b_set_tx_power,
+       .update_rate_mask = rtl8723bu_update_rate_mask,
+       .report_connect = rtl8723bu_report_connect,
        .writeN_block_size = 1024,
        .mbox_ext_reg = REG_HMBOX_EXT0_8723B,
        .mbox_ext_width = 4,
+       .tx_desc_size = sizeof(struct rtl8723bu_tx_desc),
        .has_s0s1 = 1,
        .adda_1t_init = 0x01c00014,
        .adda_1t_path_on = 0x01c00014,
@@ -7969,13 +8496,21 @@ static struct rtl8xxxu_fileops rtl8192cu_fops = {
        .parse_efuse = rtl8192cu_parse_efuse,
        .load_firmware = rtl8192cu_load_firmware,
        .power_on = rtl8192cu_power_on,
+       .power_off = rtl8xxxu_power_off,
+       .reset_8051 = rtl8xxxu_reset_8051,
        .llt_init = rtl8xxxu_init_llt_table,
        .phy_iq_calibrate = rtl8723au_phy_iq_calibrate,
        .config_channel = rtl8723au_config_channel,
        .parse_rx_desc = rtl8723au_parse_rx_desc,
+       .enable_rf = rtl8723a_enable_rf,
+       .disable_rf = rtl8723a_disable_rf,
+       .set_tx_power = rtl8723a_set_tx_power,
+       .update_rate_mask = rtl8723au_update_rate_mask,
+       .report_connect = rtl8723au_report_connect,
        .writeN_block_size = 128,
        .mbox_ext_reg = REG_HMBOX_EXT_0,
        .mbox_ext_width = 2,
+       .tx_desc_size = sizeof(struct rtl8723au_tx_desc),
        .adda_1t_init = 0x0b1b25a0,
        .adda_1t_path_on = 0x0bdb25a0,
        .adda_2t_path_on_a = 0x04db25a4,
@@ -7988,13 +8523,21 @@ static struct rtl8xxxu_fileops rtl8192eu_fops = {
        .parse_efuse = rtl8192eu_parse_efuse,
        .load_firmware = rtl8192eu_load_firmware,
        .power_on = rtl8192eu_power_on,
+       .power_off = rtl8xxxu_power_off,
+       .reset_8051 = rtl8xxxu_reset_8051,
        .llt_init = rtl8xxxu_auto_llt_table,
        .phy_iq_calibrate = rtl8723bu_phy_iq_calibrate,
        .config_channel = rtl8723bu_config_channel,
        .parse_rx_desc = rtl8723bu_parse_rx_desc,
+       .enable_rf = rtl8723b_enable_rf,
+       .disable_rf = rtl8723b_disable_rf,
+       .set_tx_power = rtl8723b_set_tx_power,
+       .update_rate_mask = rtl8723au_update_rate_mask,
+       .report_connect = rtl8723au_report_connect,
        .writeN_block_size = 128,
        .mbox_ext_reg = REG_HMBOX_EXT0_8723B,
        .mbox_ext_width = 4,
+       .tx_desc_size = sizeof(struct rtl8723au_tx_desc),
        .has_s0s1 = 1,
        .adda_1t_init = 0x0fc01616,
        .adda_1t_path_on = 0x0fc01616,