iwlwifi: Add support for SAR South Korea limitation
South Korea is adding a more strict SAR limit called "Limb SAR". Currently, WGDS SAR offset group 3 is not used (not mapped to any country). In order to be able to comply with South Korea new restriction: - OEM will use WGDS SAR offset group 3 to South Korea limitation. - OEM will change WGDS revision to 1 (currently latest revision is 0) to notify that Korea Limb SAR applied. - Driver will read the WGDS table and pass the values to FW (as usual) - Driver will pass to FW an indication that Korea Limb SAR is applied in case table revision is 1. Signed-off-by: Haim Dreyfuss <haim.dreyfuss@intel.com> Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
This commit is contained in:
committed by
Luca Coelho
parent
ee3399e523
commit
0c3d728223
@ -97,7 +97,7 @@ IWL_EXPORT_SYMBOL(iwl_acpi_get_object);
|
|||||||
|
|
||||||
union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev,
|
union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev,
|
||||||
union acpi_object *data,
|
union acpi_object *data,
|
||||||
int data_size)
|
int data_size, int *tbl_rev)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
union acpi_object *wifi_pkg;
|
union acpi_object *wifi_pkg;
|
||||||
@ -113,16 +113,19 @@ union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev,
|
|||||||
/*
|
/*
|
||||||
* We need at least two packages, one for the revision and one
|
* We need at least two packages, one for the revision and one
|
||||||
* for the data itself. Also check that the revision is valid
|
* for the data itself. Also check that the revision is valid
|
||||||
* (i.e. it is an integer set to 0).
|
* (i.e. it is an integer smaller than 2, as we currently support only
|
||||||
|
* 2 revisions).
|
||||||
*/
|
*/
|
||||||
if (data->type != ACPI_TYPE_PACKAGE ||
|
if (data->type != ACPI_TYPE_PACKAGE ||
|
||||||
data->package.count < 2 ||
|
data->package.count < 2 ||
|
||||||
data->package.elements[0].type != ACPI_TYPE_INTEGER ||
|
data->package.elements[0].type != ACPI_TYPE_INTEGER ||
|
||||||
data->package.elements[0].integer.value != 0) {
|
data->package.elements[0].integer.value > 1) {
|
||||||
IWL_DEBUG_DEV_RADIO(dev, "Unsupported packages structure\n");
|
IWL_DEBUG_DEV_RADIO(dev, "Unsupported packages structure\n");
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*tbl_rev = data->package.elements[0].integer.value;
|
||||||
|
|
||||||
/* loop through all the packages to find the one for WiFi */
|
/* loop through all the packages to find the one for WiFi */
|
||||||
for (i = 1; i < data->package.count; i++) {
|
for (i = 1; i < data->package.count; i++) {
|
||||||
union acpi_object *domain;
|
union acpi_object *domain;
|
||||||
@ -151,14 +154,15 @@ int iwl_acpi_get_mcc(struct device *dev, char *mcc)
|
|||||||
{
|
{
|
||||||
union acpi_object *wifi_pkg, *data;
|
union acpi_object *wifi_pkg, *data;
|
||||||
u32 mcc_val;
|
u32 mcc_val;
|
||||||
int ret;
|
int ret, tbl_rev;
|
||||||
|
|
||||||
data = iwl_acpi_get_object(dev, ACPI_WRDD_METHOD);
|
data = iwl_acpi_get_object(dev, ACPI_WRDD_METHOD);
|
||||||
if (IS_ERR(data))
|
if (IS_ERR(data))
|
||||||
return PTR_ERR(data);
|
return PTR_ERR(data);
|
||||||
|
|
||||||
wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_WRDD_WIFI_DATA_SIZE);
|
wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_WRDD_WIFI_DATA_SIZE,
|
||||||
if (IS_ERR(wifi_pkg)) {
|
&tbl_rev);
|
||||||
|
if (IS_ERR(wifi_pkg) || tbl_rev != 0) {
|
||||||
ret = PTR_ERR(wifi_pkg);
|
ret = PTR_ERR(wifi_pkg);
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
@ -185,6 +189,7 @@ u64 iwl_acpi_get_pwr_limit(struct device *dev)
|
|||||||
{
|
{
|
||||||
union acpi_object *data, *wifi_pkg;
|
union acpi_object *data, *wifi_pkg;
|
||||||
u64 dflt_pwr_limit;
|
u64 dflt_pwr_limit;
|
||||||
|
int tbl_rev;
|
||||||
|
|
||||||
data = iwl_acpi_get_object(dev, ACPI_SPLC_METHOD);
|
data = iwl_acpi_get_object(dev, ACPI_SPLC_METHOD);
|
||||||
if (IS_ERR(data)) {
|
if (IS_ERR(data)) {
|
||||||
@ -193,8 +198,8 @@ u64 iwl_acpi_get_pwr_limit(struct device *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data,
|
wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data,
|
||||||
ACPI_SPLC_WIFI_DATA_SIZE);
|
ACPI_SPLC_WIFI_DATA_SIZE, &tbl_rev);
|
||||||
if (IS_ERR(wifi_pkg) ||
|
if (IS_ERR(wifi_pkg) || tbl_rev != 0 ||
|
||||||
wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER) {
|
wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER) {
|
||||||
dflt_pwr_limit = 0;
|
dflt_pwr_limit = 0;
|
||||||
goto out_free;
|
goto out_free;
|
||||||
@ -211,14 +216,15 @@ IWL_EXPORT_SYMBOL(iwl_acpi_get_pwr_limit);
|
|||||||
int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk)
|
int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk)
|
||||||
{
|
{
|
||||||
union acpi_object *wifi_pkg, *data;
|
union acpi_object *wifi_pkg, *data;
|
||||||
int ret;
|
int ret, tbl_rev;
|
||||||
|
|
||||||
data = iwl_acpi_get_object(dev, ACPI_ECKV_METHOD);
|
data = iwl_acpi_get_object(dev, ACPI_ECKV_METHOD);
|
||||||
if (IS_ERR(data))
|
if (IS_ERR(data))
|
||||||
return PTR_ERR(data);
|
return PTR_ERR(data);
|
||||||
|
|
||||||
wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_ECKV_WIFI_DATA_SIZE);
|
wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_ECKV_WIFI_DATA_SIZE,
|
||||||
if (IS_ERR(wifi_pkg)) {
|
&tbl_rev);
|
||||||
|
if (IS_ERR(wifi_pkg) || tbl_rev != 0) {
|
||||||
ret = PTR_ERR(wifi_pkg);
|
ret = PTR_ERR(wifi_pkg);
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@
|
|||||||
void *iwl_acpi_get_object(struct device *dev, acpi_string method);
|
void *iwl_acpi_get_object(struct device *dev, acpi_string method);
|
||||||
union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev,
|
union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev,
|
||||||
union acpi_object *data,
|
union acpi_object *data,
|
||||||
int data_size);
|
int data_size, int *tbl_rev);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* iwl_acpi_get_mcc - read MCC from ACPI, if available
|
* iwl_acpi_get_mcc - read MCC from ACPI, if available
|
||||||
@ -131,7 +131,8 @@ static inline void *iwl_acpi_get_object(struct device *dev, acpi_string method)
|
|||||||
|
|
||||||
static inline union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev,
|
static inline union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev,
|
||||||
union acpi_object *data,
|
union acpi_object *data,
|
||||||
int data_size)
|
int data_size,
|
||||||
|
int *tbl_rev)
|
||||||
{
|
{
|
||||||
return ERR_PTR(-ENOENT);
|
return ERR_PTR(-ENOENT);
|
||||||
}
|
}
|
||||||
|
@ -419,14 +419,26 @@ struct iwl_per_chain_offset_group {
|
|||||||
struct iwl_per_chain_offset hb;
|
struct iwl_per_chain_offset hb;
|
||||||
} __packed; /* PER_CHAIN_LIMIT_OFFSET_GROUP_S_VER_1 */
|
} __packed; /* PER_CHAIN_LIMIT_OFFSET_GROUP_S_VER_1 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct iwl_geo_tx_power_profile_cmd_v1 - struct for GEO_TX_POWER_LIMIT cmd.
|
||||||
|
* @ops: operations, value from &enum iwl_geo_per_chain_offset_operation
|
||||||
|
* @table: offset profile per band.
|
||||||
|
*/
|
||||||
|
struct iwl_geo_tx_power_profiles_cmd_v1 {
|
||||||
|
__le32 ops;
|
||||||
|
struct iwl_per_chain_offset_group table[IWL_NUM_GEO_PROFILES];
|
||||||
|
} __packed; /* GEO_TX_POWER_LIMIT_VER_1 */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct iwl_geo_tx_power_profile_cmd - struct for GEO_TX_POWER_LIMIT cmd.
|
* struct iwl_geo_tx_power_profile_cmd - struct for GEO_TX_POWER_LIMIT cmd.
|
||||||
* @ops: operations, value from &enum iwl_geo_per_chain_offset_operation
|
* @ops: operations, value from &enum iwl_geo_per_chain_offset_operation
|
||||||
* @table: offset profile per band.
|
* @table: offset profile per band.
|
||||||
|
* @table_revision: BIOS table revision.
|
||||||
*/
|
*/
|
||||||
struct iwl_geo_tx_power_profiles_cmd {
|
struct iwl_geo_tx_power_profiles_cmd {
|
||||||
__le32 ops;
|
__le32 ops;
|
||||||
struct iwl_per_chain_offset_group table[IWL_NUM_GEO_PROFILES];
|
struct iwl_per_chain_offset_group table[IWL_NUM_GEO_PROFILES];
|
||||||
|
__le32 table_revision;
|
||||||
} __packed; /* GEO_TX_POWER_LIMIT */
|
} __packed; /* GEO_TX_POWER_LIMIT */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -287,6 +287,8 @@ typedef unsigned int __bitwise iwl_ucode_tlv_api_t;
|
|||||||
* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S.
|
* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S.
|
||||||
* @IWL_UCODE_TLV_API_MBSSID_HE: This ucode supports v2 of
|
* @IWL_UCODE_TLV_API_MBSSID_HE: This ucode supports v2 of
|
||||||
* STA_CONTEXT_DOT11AX_API_S
|
* STA_CONTEXT_DOT11AX_API_S
|
||||||
|
* @IWL_UCODE_TLV_CAPA_SAR_TABLE_VER: This ucode supports different sar
|
||||||
|
* version tables.
|
||||||
*
|
*
|
||||||
* @NUM_IWL_UCODE_TLV_API: number of bits used
|
* @NUM_IWL_UCODE_TLV_API: number of bits used
|
||||||
*/
|
*/
|
||||||
@ -319,6 +321,7 @@ enum iwl_ucode_tlv_api {
|
|||||||
IWL_UCODE_TLV_API_MBSSID_HE = (__force iwl_ucode_tlv_api_t)52,
|
IWL_UCODE_TLV_API_MBSSID_HE = (__force iwl_ucode_tlv_api_t)52,
|
||||||
IWL_UCODE_TLV_API_WOWLAN_TCP_SYN_WAKE = (__force iwl_ucode_tlv_api_t)53,
|
IWL_UCODE_TLV_API_WOWLAN_TCP_SYN_WAKE = (__force iwl_ucode_tlv_api_t)53,
|
||||||
IWL_UCODE_TLV_API_FTM_RTT_ACCURACY = (__force iwl_ucode_tlv_api_t)54,
|
IWL_UCODE_TLV_API_FTM_RTT_ACCURACY = (__force iwl_ucode_tlv_api_t)54,
|
||||||
|
IWL_UCODE_TLV_API_SAR_TABLE_VER = (__force iwl_ucode_tlv_api_t)55,
|
||||||
IWL_UCODE_TLV_API_ADWELL_HB_DEF_N_AP = (__force iwl_ucode_tlv_api_t)57,
|
IWL_UCODE_TLV_API_ADWELL_HB_DEF_N_AP = (__force iwl_ucode_tlv_api_t)57,
|
||||||
|
|
||||||
NUM_IWL_UCODE_TLV_API
|
NUM_IWL_UCODE_TLV_API
|
||||||
|
@ -683,15 +683,15 @@ static int iwl_mvm_sar_get_wrds_table(struct iwl_mvm *mvm)
|
|||||||
{
|
{
|
||||||
union acpi_object *wifi_pkg, *table, *data;
|
union acpi_object *wifi_pkg, *table, *data;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
int ret;
|
int ret, tbl_rev;
|
||||||
|
|
||||||
data = iwl_acpi_get_object(mvm->dev, ACPI_WRDS_METHOD);
|
data = iwl_acpi_get_object(mvm->dev, ACPI_WRDS_METHOD);
|
||||||
if (IS_ERR(data))
|
if (IS_ERR(data))
|
||||||
return PTR_ERR(data);
|
return PTR_ERR(data);
|
||||||
|
|
||||||
wifi_pkg = iwl_acpi_get_wifi_pkg(mvm->dev, data,
|
wifi_pkg = iwl_acpi_get_wifi_pkg(mvm->dev, data,
|
||||||
ACPI_WRDS_WIFI_DATA_SIZE);
|
ACPI_WRDS_WIFI_DATA_SIZE, &tbl_rev);
|
||||||
if (IS_ERR(wifi_pkg)) {
|
if (IS_ERR(wifi_pkg) || tbl_rev != 0) {
|
||||||
ret = PTR_ERR(wifi_pkg);
|
ret = PTR_ERR(wifi_pkg);
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
@ -720,15 +720,15 @@ static int iwl_mvm_sar_get_ewrd_table(struct iwl_mvm *mvm)
|
|||||||
{
|
{
|
||||||
union acpi_object *wifi_pkg, *data;
|
union acpi_object *wifi_pkg, *data;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
int i, n_profiles, ret;
|
int i, n_profiles, ret, tbl_rev;
|
||||||
|
|
||||||
data = iwl_acpi_get_object(mvm->dev, ACPI_EWRD_METHOD);
|
data = iwl_acpi_get_object(mvm->dev, ACPI_EWRD_METHOD);
|
||||||
if (IS_ERR(data))
|
if (IS_ERR(data))
|
||||||
return PTR_ERR(data);
|
return PTR_ERR(data);
|
||||||
|
|
||||||
wifi_pkg = iwl_acpi_get_wifi_pkg(mvm->dev, data,
|
wifi_pkg = iwl_acpi_get_wifi_pkg(mvm->dev, data,
|
||||||
ACPI_EWRD_WIFI_DATA_SIZE);
|
ACPI_EWRD_WIFI_DATA_SIZE, &tbl_rev);
|
||||||
if (IS_ERR(wifi_pkg)) {
|
if (IS_ERR(wifi_pkg) || tbl_rev != 0) {
|
||||||
ret = PTR_ERR(wifi_pkg);
|
ret = PTR_ERR(wifi_pkg);
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
@ -779,7 +779,7 @@ out_free:
|
|||||||
static int iwl_mvm_sar_get_wgds_table(struct iwl_mvm *mvm)
|
static int iwl_mvm_sar_get_wgds_table(struct iwl_mvm *mvm)
|
||||||
{
|
{
|
||||||
union acpi_object *wifi_pkg, *data;
|
union acpi_object *wifi_pkg, *data;
|
||||||
int i, j, ret;
|
int i, j, ret, tbl_rev;
|
||||||
int idx = 1;
|
int idx = 1;
|
||||||
|
|
||||||
data = iwl_acpi_get_object(mvm->dev, ACPI_WGDS_METHOD);
|
data = iwl_acpi_get_object(mvm->dev, ACPI_WGDS_METHOD);
|
||||||
@ -787,12 +787,13 @@ static int iwl_mvm_sar_get_wgds_table(struct iwl_mvm *mvm)
|
|||||||
return PTR_ERR(data);
|
return PTR_ERR(data);
|
||||||
|
|
||||||
wifi_pkg = iwl_acpi_get_wifi_pkg(mvm->dev, data,
|
wifi_pkg = iwl_acpi_get_wifi_pkg(mvm->dev, data,
|
||||||
ACPI_WGDS_WIFI_DATA_SIZE);
|
ACPI_WGDS_WIFI_DATA_SIZE, &tbl_rev);
|
||||||
if (IS_ERR(wifi_pkg)) {
|
if (IS_ERR(wifi_pkg) || tbl_rev > 1) {
|
||||||
ret = PTR_ERR(wifi_pkg);
|
ret = PTR_ERR(wifi_pkg);
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mvm->geo_rev = tbl_rev;
|
||||||
for (i = 0; i < ACPI_NUM_GEO_PROFILES; i++) {
|
for (i = 0; i < ACPI_NUM_GEO_PROFILES; i++) {
|
||||||
for (j = 0; j < ACPI_GEO_TABLE_SIZE; j++) {
|
for (j = 0; j < ACPI_GEO_TABLE_SIZE; j++) {
|
||||||
union acpi_object *entry;
|
union acpi_object *entry;
|
||||||
@ -879,15 +880,29 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm)
|
|||||||
{
|
{
|
||||||
struct iwl_geo_tx_power_profiles_resp *resp;
|
struct iwl_geo_tx_power_profiles_resp *resp;
|
||||||
int ret;
|
int ret;
|
||||||
|
u16 len;
|
||||||
|
void *data;
|
||||||
|
struct iwl_geo_tx_power_profiles_cmd geo_cmd;
|
||||||
|
struct iwl_geo_tx_power_profiles_cmd_v1 geo_cmd_v1;
|
||||||
|
struct iwl_host_cmd cmd;
|
||||||
|
|
||||||
struct iwl_geo_tx_power_profiles_cmd geo_cmd = {
|
if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_SAR_TABLE_VER)) {
|
||||||
.ops = cpu_to_le32(IWL_PER_CHAIN_OFFSET_GET_CURRENT_TABLE),
|
geo_cmd.ops =
|
||||||
};
|
cpu_to_le32(IWL_PER_CHAIN_OFFSET_GET_CURRENT_TABLE);
|
||||||
struct iwl_host_cmd cmd = {
|
len = sizeof(geo_cmd);
|
||||||
|
data = &geo_cmd;
|
||||||
|
} else {
|
||||||
|
geo_cmd_v1.ops =
|
||||||
|
cpu_to_le32(IWL_PER_CHAIN_OFFSET_GET_CURRENT_TABLE);
|
||||||
|
len = sizeof(geo_cmd_v1);
|
||||||
|
data = &geo_cmd_v1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = (struct iwl_host_cmd){
|
||||||
.id = WIDE_ID(PHY_OPS_GROUP, GEO_TX_POWER_LIMIT),
|
.id = WIDE_ID(PHY_OPS_GROUP, GEO_TX_POWER_LIMIT),
|
||||||
.len = { sizeof(geo_cmd), },
|
.len = { len, },
|
||||||
.flags = CMD_WANT_SKB,
|
.flags = CMD_WANT_SKB,
|
||||||
.data = { &geo_cmd },
|
.data = { data },
|
||||||
};
|
};
|
||||||
|
|
||||||
ret = iwl_mvm_send_cmd(mvm, &cmd);
|
ret = iwl_mvm_send_cmd(mvm, &cmd);
|
||||||
@ -957,6 +972,16 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm)
|
|||||||
i, j, value[1], value[2], value[0]);
|
i, j, value[1], value[2], value[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd.table_revision = cpu_to_le32(mvm->geo_rev);
|
||||||
|
|
||||||
|
if (!fw_has_api(&mvm->fw->ucode_capa,
|
||||||
|
IWL_UCODE_TLV_API_SAR_TABLE_VER)) {
|
||||||
|
return iwl_mvm_send_cmd_pdu(mvm, cmd_wide_id, 0,
|
||||||
|
sizeof(struct iwl_geo_tx_power_profiles_cmd_v1),
|
||||||
|
&cmd);
|
||||||
|
}
|
||||||
|
|
||||||
return iwl_mvm_send_cmd_pdu(mvm, cmd_wide_id, 0, sizeof(cmd), &cmd);
|
return iwl_mvm_send_cmd_pdu(mvm, cmd_wide_id, 0, sizeof(cmd), &cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1180,6 +1180,7 @@ struct iwl_mvm {
|
|||||||
#ifdef CONFIG_ACPI
|
#ifdef CONFIG_ACPI
|
||||||
struct iwl_mvm_sar_profile sar_profiles[ACPI_SAR_PROFILE_NUM];
|
struct iwl_mvm_sar_profile sar_profiles[ACPI_SAR_PROFILE_NUM];
|
||||||
struct iwl_mvm_geo_profile geo_profiles[ACPI_NUM_GEO_PROFILES];
|
struct iwl_mvm_geo_profile geo_profiles[ACPI_NUM_GEO_PROFILES];
|
||||||
|
u32 geo_rev;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user