wifi: iwlwifi: fw: Add support for UATS table in UHB
authorMukesh Sisodiya <mukesh.sisodiya@intel.com>
Sun, 22 Oct 2023 14:55:50 +0000 (17:55 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 23 Oct 2023 10:49:30 +0000 (12:49 +0200)
Driver need to provide details of VLP, AFC
AP type supported for the specific MCC to firmware.
Driver will read the UATS (UHB AP type support) table
from BIOS and sent to firmware using UATS_TABLE_CMD.

Add the support for the same in the driver.

Signed-off-by: Mukesh Sisodiya <mukesh.sisodiya@intel.com>
Signed-off-by: Gregory Greenman <gregory.greenman@intel.com>
Link: https://lore.kernel.org/r/20231022173519.eb6cf7be17b2.I8977a660564412056d9fd383d57b236cd4b22d00@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
drivers/net/wireless/intel/iwlwifi/fw/runtime.h
drivers/net/wireless/intel/iwlwifi/fw/uefi.c
drivers/net/wireless/intel/iwlwifi/fw/uefi.h
drivers/net/wireless/intel/iwlwifi/mvm/fw.c

index 0fa88ee76477570b4ad5a652b64e43a08f66ba7d..dfe0bebabc811fb3441d2b7750cbe837bde76179 100644 (file)
@@ -44,6 +44,11 @@ enum iwl_regulatory_and_nvm_subcmd_ids {
         */
        SAR_OFFSET_MAPPING_TABLE_CMD = 0x4,
 
+       /**
+        * @UATS_TABLE_CMD: &struct iwl_uats_table_cmd
+        */
+       UATS_TABLE_CMD = 0x5,
+
        /**
         * @PNVM_INIT_COMPLETE_NTFY: &struct iwl_pnvm_init_complete_ntfy
         */
@@ -650,4 +655,17 @@ struct iwl_pnvm_init_complete_ntfy {
        __le32 status;
 } __packed; /* PNVM_INIT_COMPLETE_NTFY_S_VER_1 */
 
+#define UATS_TABLE_ROW_SIZE    26
+#define UATS_TABLE_COL_SIZE    13
+
+/**
+ * struct iwl_uats_table_cmd - struct for UATS_TABLE_CMD
+ * @offset_map: mapping a mcc to UHB AP type support (UATS) allowed
+ * @reserved: reserved
+ */
+struct iwl_uats_table_cmd {
+       u8 offset_map[UATS_TABLE_ROW_SIZE][UATS_TABLE_COL_SIZE];
+       __le16 reserved;
+} __packed; /* UATS_TABLE_CMD_S_VER_1 */
+
 #endif /* __iwl_fw_api_nvm_reg_h__ */
index 702586945533eefe9b1ac880945f30904208a0ce..357727774db908f1a5e13f1a4199ec391cb69d04 100644 (file)
@@ -98,6 +98,8 @@ struct iwl_txf_iter_data {
  * @cur_fw_img: current firmware image, must be maintained by
  *     the driver by calling &iwl_fw_set_current_image()
  * @dump: debug dump data
+ * @uats_enabled: VLP or AFC AP is enabled
+ * @uats_table: AP type table
  */
 struct iwl_fw_runtime {
        struct iwl_trans *trans;
@@ -171,6 +173,8 @@ struct iwl_fw_runtime {
        struct iwl_sar_offset_mapping_cmd sgom_table;
        bool sgom_enabled;
        u8 reduced_power_flags;
+       bool uats_enabled;
+       struct iwl_uats_table_cmd uats_table;
 #endif
 };
 
index 9877988db0d277c5b418fc38c91473473e70b43e..2964c5fb11e9e53f80a56debd5a793f212845338 100644 (file)
@@ -388,4 +388,54 @@ void iwl_uefi_get_sgom_table(struct iwl_trans *trans,
        kfree(data);
 }
 IWL_EXPORT_SYMBOL(iwl_uefi_get_sgom_table);
+
+static int iwl_uefi_uats_parse(struct uefi_cnv_wlan_uats_data *uats_data,
+                              struct iwl_fw_runtime *fwrt)
+{
+       if (uats_data->revision != 1)
+               return -EINVAL;
+
+       memcpy(fwrt->uats_table.offset_map, uats_data->offset_map,
+              sizeof(fwrt->uats_table.offset_map));
+       return 0;
+}
+
+int iwl_uefi_get_uats_table(struct iwl_trans *trans,
+                           struct iwl_fw_runtime *fwrt)
+{
+       struct uefi_cnv_wlan_uats_data *data;
+       unsigned long package_size;
+       int ret;
+
+       data = iwl_uefi_get_variable(IWL_UEFI_UATS_NAME, &IWL_EFI_VAR_GUID,
+                                    &package_size);
+       if (IS_ERR(data)) {
+               IWL_DEBUG_FW(trans,
+                            "UATS UEFI variable not found 0x%lx\n",
+                            PTR_ERR(data));
+               return -EINVAL;
+       }
+
+       if (package_size < sizeof(*data)) {
+               IWL_DEBUG_FW(trans,
+                            "Invalid UATS table UEFI variable len (%lu)\n",
+                            package_size);
+               kfree(data);
+               return -EINVAL;
+       }
+
+       IWL_DEBUG_FW(trans, "Read UATS from UEFI with size %lu\n",
+                    package_size);
+
+       ret = iwl_uefi_uats_parse(data, fwrt);
+       if (ret < 0) {
+               IWL_DEBUG_FW(trans, "Cannot read UATS table. rev is invalid\n");
+               kfree(data);
+               return ret;
+       }
+
+       kfree(data);
+       return 0;
+}
+IWL_EXPORT_SYMBOL(iwl_uefi_get_uats_table);
 #endif /* CONFIG_ACPI */
index 1369cc4855c3a5172de438f4e448d2b107f51430..bf61a8df12250b720aae9c99e4a719bf762ef9fd 100644 (file)
@@ -9,8 +9,10 @@
 #define IWL_UEFI_REDUCED_POWER_NAME    L"UefiCnvWlanReducedPower"
 #define IWL_UEFI_SGOM_NAME             L"UefiCnvWlanSarGeoOffsetMapping"
 #define IWL_UEFI_STEP_NAME             L"UefiCnvCommonSTEP"
+#define IWL_UEFI_UATS_NAME             L"CnvUefiWlanUATS"
 
 #define IWL_SGOM_MAP_SIZE              339
+#define IWL_UATS_MAP_SIZE              339
 
 struct pnvm_sku_package {
        u8 rev;
@@ -25,6 +27,11 @@ struct uefi_cnv_wlan_sgom_data {
        u8 offset_map[IWL_SGOM_MAP_SIZE - 1];
 } __packed;
 
+struct uefi_cnv_wlan_uats_data {
+       u8 revision;
+       u8 offset_map[IWL_UATS_MAP_SIZE - 1];
+} __packed;
+
 struct uefi_cnv_common_step_data {
        u8 revision;
        u8 step_mode;
@@ -82,10 +89,20 @@ iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data,
 
 #if defined(CONFIG_EFI) && defined(CONFIG_ACPI)
 void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt);
+int iwl_uefi_get_uats_table(struct iwl_trans *trans,
+                           struct iwl_fw_runtime *fwrt);
 #else
 static inline
 void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt)
 {
 }
+
+static inline
+int iwl_uefi_get_uats_table(struct iwl_trans *trans,
+                           struct iwl_fw_runtime *fwrt)
+{
+       return 0;
+}
+
 #endif
 #endif /* __iwl_fw_uefi__ */
index 103233c0f38f069d143ccdb42bde2c8535259f6f..403bd17b8b7a2da8271aac315fd1eb67fa889329 100644 (file)
@@ -15,6 +15,7 @@
 #include "iwl-prph.h"
 #include "fw/acpi.h"
 #include "fw/pnvm.h"
+#include "fw/uefi.h"
 
 #include "mvm.h"
 #include "fw/dbg.h"
@@ -29,6 +30,9 @@
 #define IWL_TAS_US_MCC 0x5553
 #define IWL_TAS_CANADA_MCC 0x4341
 
+#define IWL_UATS_VLP_AP_SUPPORTED BIT(29)
+#define IWL_UATS_AFC_AP_SUPPORTED BIT(30)
+
 struct iwl_mvm_alive_data {
        bool valid;
        u32 scd_base_addr;
@@ -487,6 +491,52 @@ static void iwl_mvm_phy_filter_init(struct iwl_mvm *mvm,
 }
 
 #if defined(CONFIG_ACPI) && defined(CONFIG_EFI)
+static void iwl_mvm_uats_init(struct iwl_mvm *mvm)
+{
+       u8 cmd_ver;
+       int ret;
+       struct iwl_host_cmd cmd = {
+               .id = WIDE_ID(REGULATORY_AND_NVM_GROUP,
+                             UATS_TABLE_CMD),
+               .flags = 0,
+               .data[0] = &mvm->fwrt.uats_table,
+               .len[0] =  sizeof(mvm->fwrt.uats_table),
+               .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+       };
+
+       if (!(mvm->trans->trans_cfg->device_family >=
+             IWL_DEVICE_FAMILY_AX210)) {
+               IWL_DEBUG_RADIO(mvm, "UATS feature is not supported\n");
+               return;
+       }
+
+       if (!mvm->fwrt.uats_enabled) {
+               IWL_DEBUG_RADIO(mvm, "UATS feature is disabled\n");
+               return;
+       }
+
+       cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd.id,
+                                       IWL_FW_CMD_VER_UNKNOWN);
+       if (cmd_ver != 1) {
+               IWL_DEBUG_RADIO(mvm,
+                               "UATS_TABLE_CMD ver %d not supported\n",
+                               cmd_ver);
+               return;
+       }
+
+       ret = iwl_uefi_get_uats_table(mvm->trans, &mvm->fwrt);
+       if (ret < 0) {
+               IWL_ERR(mvm, "failed to read UATS table (%d)\n", ret);
+               return;
+       }
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+       if (ret < 0)
+               IWL_ERR(mvm, "failed to send UATS_TABLE_CMD (%d)\n", ret);
+       else
+               IWL_DEBUG_RADIO(mvm, "UATS_TABLE_CMD sent to FW\n");
+}
+
 static int iwl_mvm_sgom_init(struct iwl_mvm *mvm)
 {
        u8 cmd_ver;
@@ -526,6 +576,10 @@ static int iwl_mvm_sgom_init(struct iwl_mvm *mvm)
 {
        return 0;
 }
+
+static void iwl_mvm_uats_init(struct iwl_mvm *mvm)
+{
+}
 #endif
 
 static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm)
@@ -1336,6 +1390,10 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm)
                                        "Failed to send LARI_CONFIG_CHANGE (%d)\n",
                                        ret);
        }
+
+       if (le32_to_cpu(cmd.oem_uhb_allow_bitmap) & IWL_UATS_VLP_AP_SUPPORTED ||
+           le32_to_cpu(cmd.oem_uhb_allow_bitmap) & IWL_UATS_AFC_AP_SUPPORTED)
+               mvm->fwrt.uats_enabled = TRUE;
 }
 
 void iwl_mvm_get_acpi_tables(struct iwl_mvm *mvm)
@@ -1745,6 +1803,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
 
        iwl_mvm_tas_init(mvm);
        iwl_mvm_leds_sync(mvm);
+       iwl_mvm_uats_init(mvm);
 
        if (iwl_rfi_supported(mvm)) {
                if (iwl_mvm_eval_dsm_rfi(mvm) == DSM_VALUE_RFI_ENABLE)