802.11ax PCIe wireless network (Wi-Fi 6E) adapter
 
 config RTW89_8922AE
-       tristate "Realtek 8922AE PCI wireless network (Wi-Fi 7) adapter"
+       tristate "Realtek 8922AE/8922AE-VS PCI wireless network (Wi-Fi 7) adapter"
        depends on PCI
        select RTW89_CORE
        select RTW89_PCI
        select RTW89_8922A
        help
-         Select this option will enable support for 8922AE chipset
+         Select this option will enable support for 8922AE/8922AE-VS chipset
 
          802.11be PCIe wireless network (Wi-Fi 7) adapter
          supporting 2x2 2GHz/5GHz/6GHz 4096-QAM 160MHz channels.
 
+         The variant 8922AE-VS has the same features except 1024-QAM.
+
 config RTW89_DEBUG
        bool
 
 
        struct ieee80211_eht_mcs_nss_supp *eht_nss;
        struct ieee80211_sta_eht_cap *eht_cap;
        struct rtw89_hal *hal = &rtwdev->hal;
+       bool support_mcs_12_13 = true;
        bool support_320mhz = false;
+       u8 val, val_mcs13;
        int sts = 8;
-       u8 val;
 
        if (chip->chip_gen == RTW89_CHIP_AX)
                return;
 
+       if (hal->no_mcs_12_13)
+               support_mcs_12_13 = false;
+
        if (band == NL80211_BAND_6GHZ &&
            chip->support_bandwidths & BIT(NL80211_CHAN_WIDTH_320))
                support_320mhz = true;
 
        val = u8_encode_bits(hal->rx_nss, IEEE80211_EHT_MCS_NSS_RX) |
              u8_encode_bits(hal->tx_nss, IEEE80211_EHT_MCS_NSS_TX);
+       val_mcs13 = support_mcs_12_13 ? val : 0;
+
        eht_nss->bw._80.rx_tx_mcs9_max_nss = val;
        eht_nss->bw._80.rx_tx_mcs11_max_nss = val;
-       eht_nss->bw._80.rx_tx_mcs13_max_nss = val;
+       eht_nss->bw._80.rx_tx_mcs13_max_nss = val_mcs13;
        eht_nss->bw._160.rx_tx_mcs9_max_nss = val;
        eht_nss->bw._160.rx_tx_mcs11_max_nss = val;
-       eht_nss->bw._160.rx_tx_mcs13_max_nss = val;
+       eht_nss->bw._160.rx_tx_mcs13_max_nss = val_mcs13;
        if (support_320mhz) {
                eht_nss->bw._320.rx_tx_mcs9_max_nss = val;
                eht_nss->bw._320.rx_tx_mcs11_max_nss = val;
-               eht_nss->bw._320.rx_tx_mcs13_max_nss = val;
+               eht_nss->bw._320.rx_tx_mcs13_max_nss = val_mcs13;
        }
 }
 
 
 struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device,
                                           u32 bus_data_size,
-                                          const struct rtw89_chip_info *chip)
+                                          const struct rtw89_chip_info *chip,
+                                          const struct rtw89_chip_variant *variant)
 {
        struct rtw89_fw_info early_fw = {};
        const struct firmware *firmware;
        rtwdev->dev = device;
        rtwdev->ops = ops;
        rtwdev->chip = chip;
+       rtwdev->variant = variant;
        rtwdev->fw.req.firmware = firmware;
        rtwdev->fw.fw_format = fw_format;
        rtwdev->support_mlo = support_mlo;
 
        const struct rtw89_xtal_info *xtal_info;
 };
 
+struct rtw89_chip_variant {
+       bool no_mcs_12_13: 1;
+       u32 fw_min_ver_code;
+};
+
 union rtw89_bus_info {
        const struct rtw89_pci_info *pci;
 };
 
 struct rtw89_driver_info {
        const struct rtw89_chip_info *chip;
+       const struct rtw89_chip_variant *variant;
        const struct dmi_system_id *quirks;
        union rtw89_bus_info bus;
 };
        bool ant_diversity_fixed;
        bool support_cckpd;
        bool support_igi;
+       bool no_mcs_12_13;
+
        atomic_t roc_chanctx_idx;
 
        DECLARE_BITMAP(changes, NUM_OF_RTW89_CHANCTX_CHANGES);
        enum rtw89_mlo_dbcc_mode mlo_dbcc_mode;
        struct rtw89_hw_scan_info scan_info;
        const struct rtw89_chip_info *chip;
+       const struct rtw89_chip_variant *variant;
        const struct rtw89_pci_info *pci_info;
        const struct rtw89_rfe_parms *rfe_parms;
        struct rtw89_hal hal;
 void rtw89_core_unregister(struct rtw89_dev *rtwdev);
 struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device,
                                           u32 bus_data_size,
-                                          const struct rtw89_chip_info *chip);
+                                          const struct rtw89_chip_info *chip,
+                                          const struct rtw89_chip_variant *variant);
 void rtw89_free_ieee80211_hw(struct rtw89_dev *rtwdev);
 u8 rtw89_acquire_mac_id(struct rtw89_dev *rtwdev);
 void rtw89_release_mac_id(struct rtw89_dev *rtwdev, u8 mac_id);
 
        return firmware;
 }
 
+static int rtw89_fw_validate_ver_required(struct rtw89_dev *rtwdev)
+{
+       const struct rtw89_chip_variant *variant = rtwdev->variant;
+       const struct rtw89_fw_suit *fw_suit;
+       u32 suit_ver_code;
+
+       if (!variant)
+               return 0;
+
+       fw_suit = rtw89_fw_suit_get(rtwdev, RTW89_FW_NORMAL);
+       suit_ver_code = RTW89_FW_SUIT_VER_CODE(fw_suit);
+
+       if (variant->fw_min_ver_code > suit_ver_code) {
+               rtw89_err(rtwdev, "minimum required firmware version is 0x%x\n",
+                         variant->fw_min_ver_code);
+               return -ENOENT;
+       }
+
+       return 0;
+}
+
 int rtw89_fw_recognize(struct rtw89_dev *rtwdev)
 {
        const struct rtw89_chip_info *chip = rtwdev->chip;
                return ret;
 
 normal_done:
+       ret = rtw89_fw_validate_ver_required(rtwdev);
+       if (ret)
+               return ret;
+
        /* It still works if wowlan firmware isn't existing. */
        __rtw89_fw_recognize(rtwdev, RTW89_FW_WOWLAN, false);
 
 
 
 static int rtw89_mac_setup_phycap_part1(struct rtw89_dev *rtwdev)
 {
+       const struct rtw89_chip_variant *variant = rtwdev->variant;
        const struct rtw89_c2hreg_phycap *phycap;
        struct rtw89_mac_c2h_info c2h_info = {};
+       struct rtw89_hal *hal = &rtwdev->hal;
        u8 qam_raw, qam;
        int ret;
 
                break;
        }
 
-       rtw89_debug(rtwdev, RTW89_DBG_FW, "phycap qam=%d/%d\n", qam_raw, qam);
+       if ((variant && variant->no_mcs_12_13) ||
+           qam <= RTW89_C2HREG_PHYCAP_P1_W2_QAM_1024)
+               hal->no_mcs_12_13 = true;
+
+       rtw89_debug(rtwdev, RTW89_DBG_FW, "phycap qam=%d/%d no_mcs_12_13=%d\n",
+                   qam_raw, qam, hal->no_mcs_12_13);
 
        return 0;
 }
 
 
        rtwdev = rtw89_alloc_ieee80211_hw(&pdev->dev,
                                          sizeof(struct rtw89_pci),
-                                         info->chip);
+                                         info->chip, info->variant);
        if (!rtwdev) {
                dev_err(&pdev->dev, "failed to allocate hw\n");
                return -ENOMEM;
 
 static const u64
 rtw89_ra_mask_eht_rates[4] = {RA_MASK_EHT_1SS_RATES, RA_MASK_EHT_2SS_RATES,
                              RA_MASK_EHT_3SS_RATES, RA_MASK_EHT_4SS_RATES};
+static const u64
+rtw89_ra_mask_eht_mcs0_11[4] = {RA_MASK_EHT_1SS_MCS0_11, RA_MASK_EHT_2SS_MCS0_11,
+                               RA_MASK_EHT_3SS_MCS0_11, RA_MASK_EHT_4SS_MCS0_11};
 
 static void rtw89_phy_ra_gi_ltf(struct rtw89_dev *rtwdev,
                                struct rtw89_sta_link *rtwsta_link,
        if (link_sta->eht_cap.has_eht) {
                mode |= RTW89_RA_MODE_EHT;
                ra_mask |= get_eht_ra_mask(link_sta);
-               high_rate_masks = rtw89_ra_mask_eht_rates;
+
+               if (rtwdev->hal.no_mcs_12_13)
+                       high_rate_masks = rtw89_ra_mask_eht_mcs0_11;
+               else
+                       high_rate_masks = rtw89_ra_mask_eht_rates;
+
                rtw89_phy_ra_gi_ltf(rtwdev, rtwsta_link, link_sta,
                                    chan, &fix_giltf_en, &fix_giltf);
        } else if (link_sta->he_cap.has_he) {
 
 #define RA_MASK_EHT_2SS_RATES  GENMASK_ULL(43, 28)
 #define RA_MASK_EHT_3SS_RATES  GENMASK_ULL(59, 44)
 #define RA_MASK_EHT_4SS_RATES  GENMASK_ULL(62, 60)
+#define RA_MASK_EHT_1SS_MCS0_11        GENMASK_ULL(23, 12)
+#define RA_MASK_EHT_2SS_MCS0_11        GENMASK_ULL(39, 28)
+#define RA_MASK_EHT_3SS_MCS0_11        GENMASK_ULL(55, 44)
+#define RA_MASK_EHT_4SS_MCS0_11        GENMASK_ULL(62, 60)
 #define RA_MASK_EHT_RATES      GENMASK_ULL(62, 12)
 
 #define CFO_TRK_ENABLE_TH (2 << 2)
 
 
 static const struct rtw89_driver_info rtw89_8851be_info = {
        .chip = &rtw8851b_chip_info,
+       .variant = NULL,
        .quirks = NULL,
        .bus = {
                .pci = &rtw8851b_pci_info,
 
 
 static const struct rtw89_driver_info rtw89_8852ae_info = {
        .chip = &rtw8852a_chip_info,
+       .variant = NULL,
        .quirks = NULL,
        .bus = {
                .pci = &rtw8852a_pci_info,
 
 
 static const struct rtw89_driver_info rtw89_8852be_info = {
        .chip = &rtw8852b_chip_info,
+       .variant = NULL,
        .quirks = NULL,
        .bus = {
                .pci = &rtw8852b_pci_info,
 
 
 static const struct rtw89_driver_info rtw89_8852bte_info = {
        .chip = &rtw8852bt_chip_info,
+       .variant = NULL,
        .quirks = NULL,
        .bus = {
                .pci = &rtw8852bt_pci_info,
 
 
 static const struct rtw89_driver_info rtw89_8852ce_info = {
        .chip = &rtw8852c_chip_info,
+       .variant = NULL,
        .quirks = rtw8852c_pci_quirks,
        .bus = {
                .pci = &rtw8852c_pci_info,
 
 };
 EXPORT_SYMBOL(rtw8922a_chip_info);
 
+const struct rtw89_chip_variant rtw8922ae_vs_variant = {
+       .no_mcs_12_13 = true,
+       .fw_min_ver_code = RTW89_FW_VER_CODE(0, 35, 54, 0),
+};
+EXPORT_SYMBOL(rtw8922ae_vs_variant);
+
 MODULE_FIRMWARE(RTW8922A_MODULE_FIRMWARE);
 MODULE_AUTHOR("Realtek Corporation");
 MODULE_DESCRIPTION("Realtek 802.11be wireless 8922A driver");
 
 } __packed;
 
 extern const struct rtw89_chip_info rtw8922a_chip_info;
+extern const struct rtw89_chip_variant rtw8922ae_vs_variant;
 
 #endif
 
 
 static const struct rtw89_driver_info rtw89_8922ae_info = {
        .chip = &rtw8922a_chip_info,
+       .variant = NULL,
+       .quirks = NULL,
+       .bus = {
+               .pci = &rtw8922a_pci_info,
+       },
+};
+
+static const struct rtw89_driver_info rtw89_8922ae_vs_info = {
+       .chip = &rtw8922a_chip_info,
+       .variant = &rtw8922ae_vs_variant,
        .quirks = NULL,
        .bus = {
                .pci = &rtw8922a_pci_info,
                PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8922),
                .driver_data = (kernel_ulong_t)&rtw89_8922ae_info,
        },
+       {
+               PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x892B),
+               .driver_data = (kernel_ulong_t)&rtw89_8922ae_vs_info,
+       },
        {},
 };
 MODULE_DEVICE_TABLE(pci, rtw89_8922ae_id_table);
 module_pci_driver(rtw89_8922ae_driver);
 
 MODULE_AUTHOR("Realtek Corporation");
-MODULE_DESCRIPTION("Realtek 802.11be wireless 8922AE driver");
+MODULE_DESCRIPTION("Realtek 802.11be wireless 8922AE/8922AE-VS driver");
 MODULE_LICENSE("Dual BSD/GPL");