Thread (16 messages) 16 messages, 1 author, 5d ago
DORMANTno replies

[PATCH iwlwifi-next 15/15] wifi: iwlwifi: add standalone WRSS/EWSS BIOS table loading

From: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Date: 2026-09-23 13:03:24
Subsystem: intel wireless wifi link (iwlwifi), the rest · Maintainers: Miri Korenblit, Linus Torvalds

From: Pagadala Yesu Anjaneyulu <redacted>

Add support for reading WRSS and EWSS standalone SAR tables
from the BIOS in the ACPI and UEFI paths.

WRDS/EWRD tables describe SAR limits for concurrent Wi-Fi
and Bluetooth operation. When Bluetooth is not active,
those limits can unnecessarily restrict Wi-Fi transmit power.
WRSS/EWSS provide separate standalone SAR profiles for Wi-Fi-only
operation, allowing the firmware to select the appropriate
higher-power limits while still respecting the platform-specific
regulatory constraints.

Signed-off-by: Pagadala Yesu Anjaneyulu <redacted>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/fw/acpi.c  | 272 +++++++++++++-----
 drivers/net/wireless/intel/iwlwifi/fw/acpi.h  |  16 ++
 drivers/net/wireless/intel/iwlwifi/fw/init.c  |   2 +
 .../wireless/intel/iwlwifi/fw/regulatory.c    |   2 +
 .../wireless/intel/iwlwifi/fw/regulatory.h    |   5 +
 .../net/wireless/intel/iwlwifi/fw/runtime.h   |   7 +
 drivers/net/wireless/intel/iwlwifi/fw/uefi.c  | 133 ++++++++-
 drivers/net/wireless/intel/iwlwifi/fw/uefi.h  |  14 +
 8 files changed, 377 insertions(+), 74 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c
index 21470b8ae8e9..7b11316a3062 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c
@@ -525,6 +525,83 @@ iwl_acpi_parse_chains_table(union acpi_object *table,
 	return 0;
 }
 
+static int
+iwl_acpi_parse_extended_sar_profiles(union acpi_object *wifi_pkg,
+				     struct iwl_sar_profile *sar_profiles,
+				     u8 num_sub_bands, bool has_cdb)
+{
+	int n_profiles, pos, ret;
+	bool enabled;
+
+	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
+	    wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER)
+		return -EINVAL;
+
+	if (WARN_ON(ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands >
+		    ARRAY_SIZE(sar_profiles[0].chains) *
+		    ARRAY_SIZE(sar_profiles[0].chains[0].subbands)))
+		return -EINVAL;
+
+	enabled = !!(wifi_pkg->package.elements[1].integer.value);
+	n_profiles = wifi_pkg->package.elements[2].integer.value;
+
+	if (n_profiles >= BIOS_SAR_MAX_PROFILE_NUM)
+		return -EINVAL;
+
+	/* the tables start at element 3 */
+	pos = 3;
+
+	BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV0 != ACPI_SAR_NUM_CHAINS_REV1);
+	BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV2 != 2 * ACPI_SAR_NUM_CHAINS_REV0);
+
+	/* parse non-cdb chains for all profiles */
+	for (int i = 0; i < n_profiles; i++) {
+		union acpi_object *table = &wifi_pkg->package.elements[pos];
+
+		ret = iwl_acpi_parse_chains_table(table,
+						  sar_profiles[i + 1].chains,
+						  ACPI_SAR_NUM_CHAINS_REV0,
+						  num_sub_bands);
+		if (ret < 0)
+			return ret;
+
+		/* go to the next table */
+		pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands;
+	}
+
+	if (!has_cdb)
+		goto set_enabled;
+
+	if (WARN_ON(ACPI_SAR_NUM_CHAINS_REV0 * 2 * num_sub_bands >
+		    ARRAY_SIZE(sar_profiles[0].chains) *
+		    ARRAY_SIZE(sar_profiles[0].chains[0].subbands)))
+		return -EINVAL;
+
+	/* parse cdb chains for all profiles */
+	for (int i = 0; i < n_profiles; i++) {
+		struct iwl_sar_profile_chain *chains;
+		union acpi_object *table;
+
+		table = &wifi_pkg->package.elements[pos];
+		chains = &sar_profiles[i + 1].chains[ACPI_SAR_NUM_CHAINS_REV0];
+		ret = iwl_acpi_parse_chains_table(table,
+						  chains,
+						  ACPI_SAR_NUM_CHAINS_REV0,
+						  num_sub_bands);
+		if (ret < 0)
+			return ret;
+
+		/* go to the next table */
+		pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands;
+	}
+
+set_enabled:
+	for (int i = 0; i < n_profiles; i++)
+		sar_profiles[i + 1].enabled = enabled;
+
+	return 0;
+}
+
 int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt)
 {
 	union acpi_object *wifi_pkg, *table, *data;
@@ -629,8 +706,12 @@ int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt)
 	 */
 	ret = iwl_acpi_parse_chains_table(table, fwrt->sar_profiles[0].chains,
 					  num_chains, num_sub_bands);
-	if (!ret && flags & IWL_SAR_ENABLE_MSK)
+	if (ret)
+		goto out_free;
+
+	if (flags & IWL_SAR_ENABLE_MSK)
 		fwrt->sar_profiles[0].enabled = true;
+	fwrt->wrds_table_revision = tbl_rev;
 
 out_free:
 	kfree(data);
@@ -640,8 +721,7 @@ int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt)
 int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt)
 {
 	union acpi_object *wifi_pkg, *data;
-	bool enabled;
-	int i, n_profiles, tbl_rev, pos;
+	int tbl_rev;
 	int ret = 0;
 	u8 num_sub_bands;
 
@@ -713,93 +793,147 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt)
 	goto out_free;
 
 read_table:
-	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
-	    wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER) {
-		ret = -EINVAL;
+	ret = iwl_acpi_parse_extended_sar_profiles(wifi_pkg,
+						   fwrt->sar_profiles,
+						   num_sub_bands,
+						   tbl_rev >= 2);
+	if (ret)
 		goto out_free;
+
+	fwrt->ewrd_table_revision = tbl_rev;
+
+out_free:
+	kfree(data);
+	return ret;
+}
+
+int iwl_acpi_get_wsss_table(struct iwl_fw_runtime *fwrt)
+{
+	union acpi_object *data __free(kfree) = NULL;
+	union acpi_object *wifi_pkg, *table;
+	u8 num_chains, num_sub_bands;
+	int ret, tbl_rev;
+	u32 flags;
+
+	if (fwrt->wrds_table_revision == IWL_BIOS_REVISION_UNSET) {
+		IWL_DEBUG_RADIO(fwrt,
+				"Skipping standalone SAR: WRDS table was not read\n");
+		return -EINVAL;
 	}
 
-	if (WARN_ON(ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands >
-		    ARRAY_SIZE(fwrt->sar_profiles[0].chains) *
-		    ARRAY_SIZE(fwrt->sar_profiles[0].chains[0].subbands))) {
-		ret = -EINVAL;
-		goto out_free;
+	data = iwl_acpi_get_object(fwrt->dev, ACPI_WSSS_METHOD);
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	switch (fwrt->wrds_table_revision) {
+	case 3:
+		wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
+						 ACPI_WRDS_WIFI_DATA_SIZE_REV3,
+						 &tbl_rev);
+		num_chains = ACPI_SAR_NUM_CHAINS_REV2;
+		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV3;
+		break;
+	case 2:
+		wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
+						 ACPI_WRDS_WIFI_DATA_SIZE_REV2,
+						 &tbl_rev);
+		num_chains = ACPI_SAR_NUM_CHAINS_REV2;
+		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;
+		break;
+	default:
+		return -EINVAL;
 	}
 
-	enabled = !!(wifi_pkg->package.elements[1].integer.value);
-	n_profiles = wifi_pkg->package.elements[2].integer.value;
+	if (IS_ERR(wifi_pkg))
+		return PTR_ERR(wifi_pkg);
 
-	/*
-	 * Check the validity of n_profiles.  The EWRD profiles start
-	 * from index 1, so the maximum value allowed here is
-	 * ACPI_SAR_PROFILES_NUM - 1.
-	 */
-	if (n_profiles >= BIOS_SAR_MAX_PROFILE_NUM) {
-		ret = -EINVAL;
-		goto out_free;
+	if (fwrt->wrds_table_revision != tbl_rev) {
+		IWL_DEBUG_RADIO(fwrt,
+				"Skipping standalone SAR: WRDS/WSSS revision mismatch (WRDS rev %d, WSSS rev %d)\n",
+				fwrt->wrds_table_revision,
+				tbl_rev);
+		return -EINVAL;
 	}
 
-	/* the tables start at element 3 */
-	pos = 3;
+	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER)
+		return -EINVAL;
 
-	BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV0 != ACPI_SAR_NUM_CHAINS_REV1);
-	BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV2 != 2 * ACPI_SAR_NUM_CHAINS_REV0);
+	if (WARN_ON(num_chains * num_sub_bands >
+		    ARRAY_SIZE(fwrt->sar_standalone_profiles[0].chains) *
+		    ARRAY_SIZE(fwrt->sar_standalone_profiles[0].chains[0].subbands)))
+		return -EINVAL;
 
-	/* parse non-cdb chains for all profiles */
-	for (i = 0; i < n_profiles; i++) {
-		union acpi_object *table = &wifi_pkg->package.elements[pos];
+	IWL_DEBUG_RADIO(fwrt, "Reading WSSS (WRDS Standalone) tbl_rev=%d\n",
+			tbl_rev);
 
-		/* The EWRD profiles officially go from 2 to 4, but we
-		 * save them in sar_profiles[1-3] (because we don't
-		 * have profile 0).  So in the array we start from 1.
-		 */
-		ret = iwl_acpi_parse_chains_table(table,
-						  fwrt->sar_profiles[i + 1].chains,
-						  ACPI_SAR_NUM_CHAINS_REV0,
-						  num_sub_bands);
-		if (ret < 0)
-			goto out_free;
+	flags = wifi_pkg->package.elements[1].integer.value;
 
-		/* go to the next table */
-		pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands;
-	}
+	/* position of the actual table */
+	table = &wifi_pkg->package.elements[2];
 
-	/* non-cdb table revisions */
-	if (tbl_rev < 2)
-		goto set_enabled;
+	ret = iwl_acpi_parse_chains_table(table,
+					  fwrt->sar_standalone_profiles[0].chains,
+					  num_chains, num_sub_bands);
+	if (ret)
+		return ret;
 
-	if (WARN_ON(ACPI_SAR_NUM_CHAINS_REV0 * 2 * num_sub_bands >
-		    ARRAY_SIZE(fwrt->sar_profiles[0].chains) *
-		    ARRAY_SIZE(fwrt->sar_profiles[0].chains[0].subbands))) {
-		ret = -EINVAL;
-		goto out_free;
+	if (flags & IWL_SAR_ENABLE_MSK)
+		fwrt->sar_standalone_profiles[0].enabled = true;
+
+	return ret;
+}
+
+int iwl_acpi_get_ewss_table(struct iwl_fw_runtime *fwrt)
+{
+	union acpi_object *data __free(kfree) = NULL;
+	union acpi_object *wifi_pkg;
+	u8 num_sub_bands;
+	int tbl_rev;
+
+	if (fwrt->ewrd_table_revision == IWL_BIOS_REVISION_UNSET) {
+		IWL_DEBUG_RADIO(fwrt,
+				"Skipping standalone SAR: EWRD table was not read\n");
+		return -EINVAL;
 	}
 
-	/* parse cdb chains for all profiles */
-	for (i = 0; i < n_profiles; i++) {
-		struct iwl_sar_profile_chain *chains;
-		union acpi_object *table;
+	data = iwl_acpi_get_object(fwrt->dev, ACPI_EWSS_METHOD);
+	if (IS_ERR(data))
+		return PTR_ERR(data);
 
-		table = &wifi_pkg->package.elements[pos];
-		chains = &fwrt->sar_profiles[i + 1].chains[ACPI_SAR_NUM_CHAINS_REV0];
-		ret = iwl_acpi_parse_chains_table(table,
-						  chains,
-						  ACPI_SAR_NUM_CHAINS_REV0,
-						  num_sub_bands);
-		if (ret < 0)
-			goto out_free;
+	switch (fwrt->ewrd_table_revision) {
+	case 3:
+		wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
+						 ACPI_EWRD_WIFI_DATA_SIZE_REV3,
+						 &tbl_rev);
+		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV3;
+		break;
+	case 2:
+		wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
+						 ACPI_EWRD_WIFI_DATA_SIZE_REV2,
+						 &tbl_rev);
+		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;
+		break;
+	default:
+		return -EINVAL;
+	}
 
-		/* go to the next table */
-		pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands;
+	if (IS_ERR(wifi_pkg))
+		return PTR_ERR(wifi_pkg);
+
+	if (fwrt->ewrd_table_revision != tbl_rev) {
+		IWL_DEBUG_RADIO(fwrt,
+				"Skipping standalone SAR: EWRD/EWSS revision mismatch (EWRD rev %d, EWSS rev %d)\n",
+				fwrt->ewrd_table_revision, tbl_rev);
+		return -EINVAL;
 	}
 
-set_enabled:
-	for (i = 0; i < n_profiles; i++)
-		fwrt->sar_profiles[i + 1].enabled = enabled;
+	IWL_DEBUG_RADIO(fwrt, "Reading EWSS (EWRD Standalone) tbl_rev=%d\n",
+			tbl_rev);
 
-out_free:
-	kfree(data);
-	return ret;
+	return iwl_acpi_parse_extended_sar_profiles(wifi_pkg,
+						    fwrt->sar_standalone_profiles,
+						    num_sub_bands,
+						    fwrt->ewrd_table_revision >= 2);
 }
 
 int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h
index 426b0c51abec..b722beae2f00 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h
@@ -14,6 +14,8 @@
 
 #define ACPI_WRDS_METHOD	"WRDS"
 #define ACPI_EWRD_METHOD	"EWRD"
+#define ACPI_WSSS_METHOD	"WSSS"
+#define ACPI_EWSS_METHOD	"EWSS"
 #define ACPI_WGDS_METHOD	"WGDS"
 #define ACPI_WRDD_METHOD	"WRDD"
 #define ACPI_SPLC_METHOD	"SPLC"
@@ -175,6 +177,10 @@ int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt);
 
 int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt);
 
+int iwl_acpi_get_wsss_table(struct iwl_fw_runtime *fwrt);
+
+int iwl_acpi_get_ewss_table(struct iwl_fw_runtime *fwrt);
+
 int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt);
 
 int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt,
@@ -229,6 +235,16 @@ static inline int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt)
 	return -ENOENT;
 }
 
+static inline int iwl_acpi_get_wsss_table(struct iwl_fw_runtime *fwrt)
+{
+	return -ENOENT;
+}
+
+static inline int iwl_acpi_get_ewss_table(struct iwl_fw_runtime *fwrt)
+{
+	return -ENOENT;
+}
+
 static inline int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt)
 {
 	return 1;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/init.c b/drivers/net/wireless/intel/iwlwifi/fw/init.c
index d69757b0149d..f62c6a69f0dc 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/init.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/init.c
@@ -23,6 +23,8 @@ void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, struct iwl_trans *trans,
 	int i;
 
 	memset(fwrt, 0, sizeof(*fwrt));
+	fwrt->wrds_table_revision = IWL_BIOS_REVISION_UNSET;
+	fwrt->ewrd_table_revision = IWL_BIOS_REVISION_UNSET;
 	fwrt->trans = trans;
 	fwrt->fw = fw;
 	fwrt->dev = trans->dev;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
index 5dbb3cdb9916..137303e4a29b 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
@@ -30,6 +30,8 @@ IWL_EXPORT_SYMBOL(iwl_bios_get_ ## __name)
 
 IWL_BIOS_TABLE_LOADER(wrds_table);
 IWL_BIOS_TABLE_LOADER(ewrd_table);
+IWL_BIOS_TABLE_LOADER(wsss_table);
+IWL_BIOS_TABLE_LOADER(ewss_table);
 IWL_BIOS_TABLE_LOADER(wgds_table);
 IWL_BIOS_TABLE_LOADER(ppag_table);
 IWL_BIOS_TABLE_LOADER(phy_filters);
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h
index f7bd09195ab4..bd7ea0ff3d19 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h
@@ -31,6 +31,7 @@
 
 #define IWL_SAR_ENABLE_MSK		BIT(0)
 #define IWL_REDUCE_POWER_FLAGS_POS	1
+#define IWL_BIOS_REVISION_UNSET		0xFF
 
 /* PPAG gain value bounds in 1/8 dBm */
 #define IWL_PPAG_MIN_LB	-16
@@ -223,6 +224,10 @@ int iwl_bios_get_wrds_table(struct iwl_fw_runtime *fwrt);
 
 int iwl_bios_get_ewrd_table(struct iwl_fw_runtime *fwrt);
 
+int iwl_bios_get_wsss_table(struct iwl_fw_runtime *fwrt);
+
+int iwl_bios_get_ewss_table(struct iwl_fw_runtime *fwrt);
+
 int iwl_bios_get_wgds_table(struct iwl_fw_runtime *fwrt);
 
 int iwl_bios_get_ppag_table(struct iwl_fw_runtime *fwrt);
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h
index d0709ab2f593..33ce06f1a60a 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h
@@ -112,6 +112,10 @@ struct iwl_txf_iter_data {
  *	see &enum iwl_uefi_cnv_guid_status.
  *	Trust the UEFI variables if locked or in test mode.
  * @sar_profiles: sar profiles as read from WRDS/EWRD BIOS tables
+ * @sar_standalone_profiles: standalone sar profiles as read from
+ *	WSSS/EWSS BIOS tables (for Wi-Fi HB/UHB without concurrent BT)
+ * @wrds_table_revision: revision of WRDS BIOS table
+ * @ewrd_table_revision: revision of EWRD BIOS table
  * @geo_profiles: geographic profiles as read from WGDS BIOS table
  * @geo_bios_source: see &enum bios_source
  * @phy_filters: specific phy filters as read from WPFC BIOS table
@@ -206,6 +210,9 @@ struct iwl_fw_runtime {
 	bool tpc_enabled;
 #endif /* CONFIG_IWLWIFI_DEBUGFS */
 	struct iwl_sar_profile sar_profiles[BIOS_SAR_MAX_PROFILE_NUM];
+	struct iwl_sar_profile sar_standalone_profiles[BIOS_SAR_MAX_PROFILE_NUM];
+	u8 wrds_table_revision;
+	u8 ewrd_table_revision;
 	u8 sar_chain_a_profile;
 	u8 sar_chain_b_profile;
 	u8 reduced_power_flags;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c
index 871d8f5db9c3..79fd95d08732 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c
@@ -547,11 +547,11 @@ void iwl_uefi_get_uneb_table(struct iwl_trans *trans,
 }
 IWL_EXPORT_SYMBOL(iwl_uefi_get_uneb_table);
 
-static void iwl_uefi_set_sar_profile(struct iwl_fw_runtime *fwrt,
+static void iwl_uefi_set_sar_profile(struct iwl_sar_profile *profiles,
 				     const u8 *vals, u8 prof_index,
 				     u8 num_subbands, bool enabled)
 {
-	struct iwl_sar_profile *sar_prof = &fwrt->sar_profiles[prof_index];
+	struct iwl_sar_profile *sar_prof = &profiles[prof_index];
 
 	/*
 	 * Make sure fwrt has enough room to hold the data
@@ -573,7 +573,7 @@ static void iwl_uefi_set_sar_profile(struct iwl_fw_runtime *fwrt,
 				vals[chain * num_subbands + subband];
 	}
 
-	fwrt->sar_profiles[prof_index].enabled = enabled & IWL_SAR_ENABLE_MSK;
+	sar_prof->enabled = enabled & IWL_SAR_ENABLE_MSK;
 }
 
 int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt)
@@ -617,8 +617,9 @@ int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt)
 	/* The profile from WRDS is officially profile 1, but goes
 	 * into sar_profiles[0] (because we don't have a profile 0).
 	 */
-	iwl_uefi_set_sar_profile(fwrt, data->vals, 0,
+	iwl_uefi_set_sar_profile(fwrt->sar_profiles, data->vals, 0,
 				 num_subbands, data->mode);
+	fwrt->wrds_table_revision = data->revision;
 out:
 	kfree(data);
 	return ret;
@@ -670,14 +671,136 @@ int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt)
 		 * save them in sar_profiles[1-3] (because we don't
 		 * have profile 0).  So in the array we start from 1.
 		 */
-		iwl_uefi_set_sar_profile(fwrt, &data->vals[i * profile_size],
+		iwl_uefi_set_sar_profile(fwrt->sar_profiles,
+					 &data->vals[i * profile_size],
 					 i + 1, num_subbands, data->mode);
 
+	fwrt->ewrd_table_revision = data->revision;
+
 out:
 	kfree(data);
 	return ret;
 }
 
+int iwl_uefi_get_wsss_table(struct iwl_fw_runtime *fwrt)
+{
+	struct uefi_cnv_var_wrds *data __free(kfree) = NULL;
+	unsigned long expected_size;
+	unsigned long size;
+	int num_subbands;
+
+	if (fwrt->wrds_table_revision == IWL_BIOS_REVISION_UNSET) {
+		IWL_DEBUG_RADIO(fwrt,
+				"Skipping standalone SAR: WRDS table was not read\n");
+		return -EINVAL;
+	}
+
+	data = iwl_uefi_get_verified_wifi_var(fwrt,
+					      IWL_UEFI_WSSS_NAME,
+					      "WSSS",
+					      UEFI_SAR_WRDS_TABLE_SIZE_REV2,
+					      &size);
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	switch (data->revision) {
+	case 2:
+		expected_size = UEFI_SAR_WRDS_TABLE_SIZE_REV2;
+		num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV2;
+		break;
+	case 3:
+		expected_size = UEFI_SAR_WRDS_TABLE_SIZE_REV3;
+		num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV3;
+		break;
+	default:
+		IWL_DEBUG_RADIO(fwrt,
+				"Unsupported UEFI WSSS revision:%d\n",
+				data->revision);
+		return -EINVAL;
+	}
+
+	if (size != expected_size)
+		return -EINVAL;
+
+	if (fwrt->wrds_table_revision != data->revision) {
+		IWL_DEBUG_RADIO(fwrt,
+				"Skipping standalone SAR: WRDS/WSSS revision mismatch (WRDS rev %d, WSSS rev %d)\n",
+				fwrt->wrds_table_revision,
+				data->revision);
+		return -EINVAL;
+	}
+
+	IWL_DEBUG_RADIO(fwrt, "Reading WSSS (WRDS Standalone) tbl_rev=%d\n",
+			data->revision);
+	iwl_uefi_set_sar_profile(fwrt->sar_standalone_profiles,
+				 data->vals, 0,
+				 num_subbands,
+				 data->mode);
+	return 0;
+}
+
+int iwl_uefi_get_ewss_table(struct iwl_fw_runtime *fwrt)
+{
+	struct uefi_cnv_var_ewrd *data __free(kfree) = NULL;
+	unsigned long expected_size;
+	unsigned long size;
+	int num_subbands;
+	int profile_size;
+	int i;
+
+	if (fwrt->ewrd_table_revision == IWL_BIOS_REVISION_UNSET) {
+		IWL_DEBUG_RADIO(fwrt,
+				"Skipping standalone SAR: EWRD table was not read\n");
+		return -EINVAL;
+	}
+
+	data = iwl_uefi_get_verified_wifi_var(fwrt,
+					      IWL_UEFI_EWSS_NAME,
+					      "EWSS",
+					      UEFI_SAR_EWRD_TABLE_SIZE_REV2,
+					      &size);
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	switch (data->revision) {
+	case 3:
+		expected_size = UEFI_SAR_EWRD_TABLE_SIZE_REV3;
+		num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV3;
+		profile_size = UEFI_SAR_PROFILE_SIZE_REV3;
+		break;
+	case 2:
+		expected_size = UEFI_SAR_EWRD_TABLE_SIZE_REV2;
+		num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV2;
+		profile_size = UEFI_SAR_PROFILE_SIZE_REV2;
+		break;
+	default:
+		IWL_DEBUG_RADIO(fwrt,
+				"Unsupported UEFI EWSS revision:%d\n",
+				data->revision);
+		return -EINVAL;
+	}
+
+	if (size != expected_size ||
+	    data->num_profiles >= BIOS_SAR_MAX_PROFILE_NUM)
+		return -EINVAL;
+
+	if (fwrt->ewrd_table_revision != data->revision) {
+		IWL_DEBUG_RADIO(fwrt,
+				"Skipping standalone SAR: EWRD/EWSS revision mismatch (EWRD rev %d, EWSS rev %d)\n",
+				fwrt->ewrd_table_revision,
+				data->revision);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < data->num_profiles; i++)
+		iwl_uefi_set_sar_profile(fwrt->sar_standalone_profiles,
+					 &data->vals[i * profile_size],
+					 i + 1, num_subbands,
+					 data->mode);
+
+	return 0;
+}
+
 int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt)
 {
 	struct uefi_cnv_var_wgds *data;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
index 64fa1a78aacf..f4f7a447e528 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
@@ -15,6 +15,8 @@
 #define IWL_UEFI_WRDS_NAME		L"UefiCnvWlanWRDS"
 #define IWL_UEFI_GLUI_NAME		L"UefiCnvCommonGLUI"
 #define IWL_UEFI_EWRD_NAME		L"UefiCnvWlanEWRD"
+#define IWL_UEFI_WSSS_NAME		L"UefiCnvWlanWSSS"
+#define IWL_UEFI_EWSS_NAME		L"UefiCnvWlanEWSS"
 #define IWL_UEFI_WGDS_NAME		L"UefiCnvWlanWGDS"
 #define IWL_UEFI_PPAG_NAME		L"UefiCnvWlanPPAG"
 #define IWL_UEFI_WTAS_NAME		L"UefiCnvWlanWTAS"
@@ -337,6 +339,8 @@ int iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data,
 				 u32 tlv_len, struct iwl_pnvm_image *pnvm_data);
 int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt);
 int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt);
+int iwl_uefi_get_wsss_table(struct iwl_fw_runtime *fwrt);
+int iwl_uefi_get_ewss_table(struct iwl_fw_runtime *fwrt);
 int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt);
 int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt);
 int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt,
@@ -399,6 +403,16 @@ static inline int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt)
 	return -ENOENT;
 }
 
+static inline int iwl_uefi_get_wsss_table(struct iwl_fw_runtime *fwrt)
+{
+	return -ENOENT;
+}
+
+static inline int iwl_uefi_get_ewss_table(struct iwl_fw_runtime *fwrt)
+{
+	return -ENOENT;
+}
+
 static inline int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt)
 {
 	return -ENOENT;
-- 
2.34.1
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help