Commit c593d2fa authored by Ayala Barazani's avatar Ayala Barazani Committed by Luca Coelho
Browse files

iwlwifi: support SAR GEO Offset Mapping override via BIOS



Support reading the SAR Geographic Offset Mapping (SGOM) table from UEFI
to allow OEMs to override the values according to geographical regions.

Signed-off-by: default avatarAyala Barazani <ayala.barazani@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
Link: https://lore.kernel.org/r/iwlwifi.20211204174546.e6dfd8b5dd40.Ibc9a8fe2bfde345f49df5d57ec56663da6a53dc4@changeid


Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
parent 15bf5ac6
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -852,6 +852,8 @@ int iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt)
		}
	}

	fwrt->geo_num_profiles = num_profiles;
	fwrt->geo_enabled = true;
	ret = 0;
out_free:
	kfree(data);
+5 −0
Original line number Diff line number Diff line
@@ -33,6 +33,11 @@ enum iwl_regulatory_and_nvm_subcmd_ids {
	 */
	TAS_CONFIG = 0x3,

	/**
	 * @SAR_OFFSET_MAPPING_TABLE_CMD: &iwl_sar_offset_mapping_cmd
	 */
	SAR_OFFSET_MAPPING_TABLE_CMD = 0x4,

	/**
	 * @PNVM_INIT_COMPLETE_NTFY: &struct iwl_pnvm_init_complete_ntfy
	 */
+14 −0
Original line number Diff line number Diff line
@@ -503,6 +503,20 @@ union iwl_ppag_table_cmd {
	} v2;
} __packed;

#define MCC_TO_SAR_OFFSET_TABLE_ROW_SIZE	26
#define MCC_TO_SAR_OFFSET_TABLE_COL_SIZE	13

/**
 * struct iwl_sar_offset_mapping_cmd - struct for SAR_OFFSET_MAPPING_TABLE_CMD
 * @offset_map: mapping a mcc to a geo sar group
 * @reserved: reserved
 */
struct iwl_sar_offset_mapping_cmd {
	u8 offset_map[MCC_TO_SAR_OFFSET_TABLE_ROW_SIZE]
		[MCC_TO_SAR_OFFSET_TABLE_COL_SIZE];
	u16 reserved;
} __packed; /*SAR_OFFSET_MAPPING_TABLE_CMD_API_S*/

/**
 * struct iwl_beacon_filter_cmd
 * REPLY_BEACON_FILTERING_CMD = 0xd2 (command)
+4 −0
Original line number Diff line number Diff line
@@ -156,8 +156,12 @@ struct iwl_fw_runtime {
	u8 sar_chain_b_profile;
	struct iwl_geo_profile geo_profiles[ACPI_NUM_GEO_PROFILES_REV3];
	u32 geo_rev;
	u32 geo_num_profiles;
	bool geo_enabled;
	union iwl_ppag_table_cmd ppag_table;
	u32 ppag_ver;
	struct iwl_sar_offset_mapping_cmd sgom_table;
	bool sgom_enabled;
#endif
};

+88 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include "fw/uefi.h"
#include "fw/api/alive.h"
#include <linux/efi.h>
#include "fw/runtime.h"

#define IWL_EFI_VAR_GUID EFI_GUID(0x92daaf2f, 0xc02b, 0x455b,	\
				  0xb2, 0xec, 0xf5, 0xa3,	\
@@ -260,3 +261,90 @@ void *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len)

	return data;
}

#ifdef CONFIG_ACPI
static int iwl_uefi_sgom_parse(struct uefi_cnv_wlan_sgom_data *sgom_data,
			       struct iwl_fw_runtime *fwrt)
{
	int i, j;

	if (sgom_data->revision != 1)
		return -EINVAL;

	memcpy(fwrt->sgom_table.offset_map, sgom_data->offset_map,
	       sizeof(fwrt->sgom_table.offset_map));

	for (i = 0; i < MCC_TO_SAR_OFFSET_TABLE_ROW_SIZE; i++) {
		for (j = 0; j < MCC_TO_SAR_OFFSET_TABLE_COL_SIZE; j++) {
			/* since each byte is composed of to values, */
			/* one for each letter, */
			/* extract and check each of them separately */
			u8 value = fwrt->sgom_table.offset_map[i][j];
			u8 low = value & 0xF;
			u8 high = (value & 0xF0) >> 4;

			if (high > fwrt->geo_num_profiles)
				high = 0;
			if (low > fwrt->geo_num_profiles)
				low = 0;
			fwrt->sgom_table.offset_map[i][j] = (high << 4) | low;
		}
	}

	fwrt->sgom_enabled = true;
	return 0;
}

void iwl_uefi_get_sgom_table(struct iwl_trans *trans,
			     struct iwl_fw_runtime *fwrt)
{
	struct efivar_entry *sgom_efivar;
	struct uefi_cnv_wlan_sgom_data *data;
	unsigned long package_size;
	int err, ret;

	if (!fwrt->geo_enabled)
		return;

	sgom_efivar = kzalloc(sizeof(*sgom_efivar), GFP_KERNEL);
	if (!sgom_efivar)
		return;

	memcpy(&sgom_efivar->var.VariableName, IWL_UEFI_SGOM_NAME,
	       sizeof(IWL_UEFI_SGOM_NAME));
	sgom_efivar->var.VendorGuid = IWL_EFI_VAR_GUID;

	/* TODO: we hardcode a maximum length here, because reading
	 * from the UEFI is not working.  To implement this properly,
	 * we have to call efivar_entry_size().
	 */
	package_size = IWL_HARDCODED_SGOM_SIZE;

	data = kmalloc(package_size, GFP_KERNEL);
	if (!data) {
		data = ERR_PTR(-ENOMEM);
		goto out;
	}

	err = efivar_entry_get(sgom_efivar, NULL, &package_size, data);
	if (err) {
		IWL_DEBUG_FW(trans,
			     "SGOM UEFI variable not found %d\n", err);
		goto out_free;
	}

	IWL_DEBUG_FW(trans, "Read SGOM from UEFI with size %lu\n",
		     package_size);

	ret = iwl_uefi_sgom_parse(data, fwrt);
	if (ret < 0)
		IWL_DEBUG_FW(trans, "Cannot read SGOM tables. rev is invalid\n");

out_free:
	kfree(data);

out:
	kfree(sgom_efivar);
}
IWL_EXPORT_SYMBOL(iwl_uefi_get_sgom_table);
#endif /* CONFIG_ACPI */
Loading