File Index
Files and directories
Sign in
FILE

regulatory.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c C
  1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
  2/*
  3 * Copyright (C) 2024-2025 Intel Corporation
  4 */
  5
  6#include <linux/dmi.h>
  7
  8#include "fw/regulatory.h"
  9#include "fw/acpi.h"
 10#include "fw/uefi.h"
 11
 12#include "regulatory.h"
 13#include "mld.h"
 14#include "hcmd.h"
 15
 16void iwl_mld_get_bios_tables(struct iwl_mld *mld)
 17{
 18	int ret;
 19
 20	iwl_acpi_get_guid_lock_status(&mld->fwrt);
 21
 22	ret = iwl_bios_get_ppag_table(&mld->fwrt);
 23	if (ret < 0) {
 24		IWL_DEBUG_RADIO(mld,
 25				"PPAG BIOS table invalid or unavailable. (%d)\n",
 26				ret);
 27	}
 28
 29	ret = iwl_bios_get_wrds_table(&mld->fwrt);
 30	if (ret < 0) {
 31		IWL_DEBUG_RADIO(mld,
 32				"WRDS SAR BIOS table invalid or unavailable. (%d)\n",
 33				ret);
 34
 35		/* If not available, don't fail and don't bother with EWRD and
 36		 * WGDS
 37		 */
 38
 39		if (!iwl_bios_get_wgds_table(&mld->fwrt)) {
 40			/* If basic SAR is not available, we check for WGDS,
 41			 * which should *not* be available either. If it is
 42			 * available, issue an error, because we can't use SAR
 43			 * Geo without basic SAR.
 44			 */
 45			IWL_ERR(mld, "BIOS contains WGDS but no WRDS\n");
 46		}
 47
 48	} else {
 49		ret = iwl_bios_get_ewrd_table(&mld->fwrt);
 50		/* If EWRD is not available, we can still use
 51		 * WRDS, so don't fail.
 52		 */
 53		if (ret < 0)
 54			IWL_DEBUG_RADIO(mld,
 55					"EWRD SAR BIOS table invalid or unavailable. (%d)\n",
 56					ret);
 57
 58		ret = iwl_bios_get_wgds_table(&mld->fwrt);
 59		if (ret < 0)
 60			IWL_DEBUG_RADIO(mld,
 61					"Geo SAR BIOS table invalid or unavailable. (%d)\n",
 62					ret);
 63		/* we don't fail if the table is not available */
 64	}
 65
 66	iwl_uefi_get_uats_table(mld->trans, &mld->fwrt);
 67
 68	iwl_bios_get_phy_filters(&mld->fwrt);
 69}
 70
 71static int iwl_mld_geo_sar_init(struct iwl_mld *mld)
 72{
 73	u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_CHAIN_LIMIT_OFFSET_CMD);
 74	/* Only set to South Korea if the table revision is 1 */
 75	__le32 sk = cpu_to_le32(mld->fwrt.geo_rev == 1 ? 1 : 0);
 76	union iwl_geo_tx_power_profiles_cmd cmd = {
 77		.v5.ops = cpu_to_le32(IWL_PER_CHAIN_OFFSET_SET_TABLES),
 78		.v5.table_revision = sk,
 79	};
 80	int ret;
 81
 82	ret = iwl_sar_geo_fill_table(&mld->fwrt, &cmd.v5.table[0][0],
 83				     ARRAY_SIZE(cmd.v5.table[0]),
 84				     BIOS_GEO_MAX_PROFILE_NUM);
 85
 86	/* It is a valid scenario to not support SAR, or miss wgds table,
 87	 * but in that case there is no need to send the command.
 88	 */
 89	if (ret)
 90		return 0;
 91
 92	return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, sizeof(cmd.v5));
 93}
 94
 95int iwl_mld_config_sar_profile(struct iwl_mld *mld, int prof_a, int prof_b)
 96{
 97	u32 cmd_id = REDUCE_TX_POWER_CMD;
 98	struct iwl_dev_tx_power_cmd cmd = {
 99		.common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS),
100		.v10.flags = cpu_to_le32(mld->fwrt.reduced_power_flags),
101	};
102	int ret;
103
104	/* TODO: CDB - support IWL_NUM_CHAIN_TABLES_V2 */
105	ret = iwl_sar_fill_profile(&mld->fwrt, &cmd.v10.per_chain[0][0][0],
106				   IWL_NUM_CHAIN_TABLES, IWL_NUM_SUB_BANDS_V2,
107				   prof_a, prof_b);
108	/* return on error or if the profile is disabled (positive number) */
109	if (ret)
110		return ret;
111
112	return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd,
113				    sizeof(cmd.common) + sizeof(cmd.v10));
114}
115
116int iwl_mld_init_sar(struct iwl_mld *mld)
117{
118	int chain_a_prof = 1;
119	int chain_b_prof = 1;
120	int ret;
121
122	/* If no profile was chosen by the user yet, choose profile 1 (WRDS) as
123	 * default for both chains
124	 */
125	if (mld->fwrt.sar_chain_a_profile && mld->fwrt.sar_chain_b_profile) {
126		chain_a_prof = mld->fwrt.sar_chain_a_profile;
127		chain_b_prof = mld->fwrt.sar_chain_b_profile;
128	}
129
130	ret = iwl_mld_config_sar_profile(mld, chain_a_prof, chain_b_prof);
131	if (ret < 0)
132		return ret;
133
134	if (ret)
135		return 0;
136
137	return iwl_mld_geo_sar_init(mld);
138}
139
140int iwl_mld_init_sgom(struct iwl_mld *mld)
141{
142	int ret;
143	struct iwl_host_cmd cmd = {
144		.id = WIDE_ID(REGULATORY_AND_NVM_GROUP,
145			      SAR_OFFSET_MAPPING_TABLE_CMD),
146		.data[0] = &mld->fwrt.sgom_table,
147		.len[0] =  sizeof(mld->fwrt.sgom_table),
148		.dataflags[0] = IWL_HCMD_DFL_NOCOPY,
149	};
150
151	if (!mld->fwrt.sgom_enabled) {
152		IWL_DEBUG_RADIO(mld, "SGOM table is disabled\n");
153		return 0;
154	}
155
156	ret = iwl_mld_send_cmd(mld, &cmd);
157	if (ret)
158		IWL_ERR(mld,
159			"failed to send SAR_OFFSET_MAPPING_CMD (%d)\n", ret);
160
161	return ret;
162}
163
164static int iwl_mld_ppag_send_cmd(struct iwl_mld *mld)
165{
166	struct iwl_fw_runtime *fwrt = &mld->fwrt;
167	union iwl_ppag_table_cmd cmd = {
168		.v7.ppag_config_info.table_source = fwrt->ppag_bios_source,
169		.v7.ppag_config_info.table_revision = fwrt->ppag_bios_rev,
170		.v7.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags),
171	};
172	int ret;
173
174	IWL_DEBUG_RADIO(fwrt,
175			"PPAG MODE bits going to be sent: %d\n",
176			fwrt->ppag_flags);
177
178	for (int chain = 0; chain < IWL_NUM_CHAIN_LIMITS; chain++) {
179		for (int subband = 0; subband < IWL_NUM_SUB_BANDS_V2; subband++) {
180			cmd.v7.gain[chain][subband] =
181				fwrt->ppag_chains[chain].subbands[subband];
182			IWL_DEBUG_RADIO(fwrt,
183					"PPAG table: chain[%d] band[%d]: gain = %d\n",
184					chain, subband, cmd.v7.gain[chain][subband]);
185		}
186	}
187
188	IWL_DEBUG_RADIO(mld, "Sending PER_PLATFORM_ANT_GAIN_CMD\n");
189	ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP,
190						PER_PLATFORM_ANT_GAIN_CMD),
191				   &cmd, sizeof(cmd.v7));
192	if (ret < 0)
193		IWL_ERR(mld, "failed to send PER_PLATFORM_ANT_GAIN_CMD (%d)\n",
194			ret);
195
196	return ret;
197}
198
199int iwl_mld_init_ppag(struct iwl_mld *mld)
200{
201	/* no need to read the table, done in INIT stage */
202
203	if (!(iwl_is_ppag_approved(&mld->fwrt)))
204		return 0;
205
206	return iwl_mld_ppag_send_cmd(mld);
207}
208
209void iwl_mld_configure_lari(struct iwl_mld *mld)
210{
211	struct iwl_fw_runtime *fwrt = &mld->fwrt;
212	struct iwl_lari_config_change_cmd cmd = {
213		.config_bitmap = iwl_get_lari_config_bitmap(fwrt),
214	};
215	bool has_raw_dsm_capa = fw_has_capa(&fwrt->fw->ucode_capa,
216					    IWL_UCODE_TLV_CAPA_FW_ACCEPTS_RAW_DSM_TABLE);
217	int ret;
218	u32 value;
219
220	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_11AX_ENABLEMENT, &value);
221	if (!ret) {
222		if (!has_raw_dsm_capa)
223			value &= DSM_11AX_ALLOW_BITMAP;
224		cmd.oem_11ax_allow_bitmap = cpu_to_le32(value);
225	}
226
227	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_UNII4_CHAN, &value);
228	if (!ret) {
229		if (!has_raw_dsm_capa)
230			value &= DSM_UNII4_ALLOW_BITMAP;
231		cmd.oem_unii4_allow_bitmap = cpu_to_le32(value);
232	}
233
234	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ACTIVATE_CHANNEL, &value);
235	if (!ret) {
236		if (!has_raw_dsm_capa)
237			value &= CHAN_STATE_ACTIVE_BITMAP_CMD_V12;
238		cmd.chan_state_active_bitmap = cpu_to_le32(value);
239	}
240
241	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_6E, &value);
242	if (!ret)
243		cmd.oem_uhb_allow_bitmap = cpu_to_le32(value);
244
245	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_FORCE_DISABLE_CHANNELS, &value);
246	if (!ret) {
247		if (!has_raw_dsm_capa)
248			value &= DSM_FORCE_DISABLE_CHANNELS_ALLOWED_BITMAP;
249		cmd.force_disable_channels_bitmap = cpu_to_le32(value);
250	}
251
252	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENERGY_DETECTION_THRESHOLD,
253			       &value);
254	if (!ret) {
255		if (!has_raw_dsm_capa)
256			value &= DSM_EDT_ALLOWED_BITMAP;
257		cmd.edt_bitmap = cpu_to_le32(value);
258	}
259
260	ret = iwl_bios_get_wbem(fwrt, &value);
261	if (!ret)
262		cmd.oem_320mhz_allow_bitmap = cpu_to_le32(value);
263
264	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_11BE, &value);
265	if (!ret)
266		cmd.oem_11be_allow_bitmap = cpu_to_le32(value);
267
268	if (!cmd.config_bitmap &&
269	    !cmd.oem_uhb_allow_bitmap &&
270	    !cmd.oem_11ax_allow_bitmap &&
271	    !cmd.oem_unii4_allow_bitmap &&
272	    !cmd.chan_state_active_bitmap &&
273	    !cmd.force_disable_channels_bitmap &&
274	    !cmd.edt_bitmap &&
275	    !cmd.oem_320mhz_allow_bitmap &&
276	    !cmd.oem_11be_allow_bitmap)
277		return;
278
279	IWL_DEBUG_RADIO(mld,
280			"sending LARI_CONFIG_CHANGE, config_bitmap=0x%x, oem_11ax_allow_bitmap=0x%x\n",
281			le32_to_cpu(cmd.config_bitmap),
282			le32_to_cpu(cmd.oem_11ax_allow_bitmap));
283	IWL_DEBUG_RADIO(mld,
284			"sending LARI_CONFIG_CHANGE, oem_unii4_allow_bitmap=0x%x, chan_state_active_bitmap=0x%x\n",
285			le32_to_cpu(cmd.oem_unii4_allow_bitmap),
286			le32_to_cpu(cmd.chan_state_active_bitmap));
287	IWL_DEBUG_RADIO(mld,
288			"sending LARI_CONFIG_CHANGE, oem_uhb_allow_bitmap=0x%x, force_disable_channels_bitmap=0x%x\n",
289			le32_to_cpu(cmd.oem_uhb_allow_bitmap),
290			le32_to_cpu(cmd.force_disable_channels_bitmap));
291	IWL_DEBUG_RADIO(mld,
292			"sending LARI_CONFIG_CHANGE, edt_bitmap=0x%x, oem_320mhz_allow_bitmap=0x%x\n",
293			le32_to_cpu(cmd.edt_bitmap),
294			le32_to_cpu(cmd.oem_320mhz_allow_bitmap));
295	IWL_DEBUG_RADIO(mld,
296			"sending LARI_CONFIG_CHANGE, oem_11be_allow_bitmap=0x%x\n",
297			le32_to_cpu(cmd.oem_11be_allow_bitmap));
298
299	ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(REGULATORY_AND_NVM_GROUP,
300						LARI_CONFIG_CHANGE), &cmd);
301	if (ret)
302		IWL_DEBUG_RADIO(mld,
303				"Failed to send LARI_CONFIG_CHANGE (%d)\n",
304				ret);
305}
306
307void iwl_mld_init_uats(struct iwl_mld *mld)
308{
309	int ret;
310	struct iwl_host_cmd cmd = {
311		.id = WIDE_ID(REGULATORY_AND_NVM_GROUP,
312			      MCC_ALLOWED_AP_TYPE_CMD),
313		.data[0] = &mld->fwrt.uats_table,
314		.len[0] =  sizeof(mld->fwrt.uats_table),
315		.dataflags[0] = IWL_HCMD_DFL_NOCOPY,
316	};
317
318	if (!mld->fwrt.uats_valid)
319		return;
320
321	ret = iwl_mld_send_cmd(mld, &cmd);
322	if (ret)
323		IWL_ERR(mld, "failed to send MCC_ALLOWED_AP_TYPE_CMD (%d)\n",
324			ret);
325}
326
327void iwl_mld_init_tas(struct iwl_mld *mld)
328{
329	int ret;
330	struct iwl_tas_data data = {};
331	struct iwl_tas_config_cmd cmd = {};
332	u32 cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP, TAS_CONFIG);
333
334	BUILD_BUG_ON(ARRAY_SIZE(data.block_list_array) !=
335		     IWL_WTAS_BLACK_LIST_MAX);
336	BUILD_BUG_ON(ARRAY_SIZE(cmd.block_list_array) !=
337		     IWL_WTAS_BLACK_LIST_MAX);
338
339	if (!fw_has_capa(&mld->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TAS_CFG)) {
340		IWL_DEBUG_RADIO(mld, "TAS not enabled in FW\n");
341		return;
342	}
343
344	ret = iwl_bios_get_tas_table(&mld->fwrt, &data);
345	if (ret < 0) {
346		IWL_DEBUG_RADIO(mld,
347				"TAS table invalid or unavailable. (%d)\n",
348				ret);
349		return;
350	}
351
352	if (!iwl_is_tas_approved()) {
353		IWL_DEBUG_RADIO(mld,
354				"System vendor '%s' is not in the approved list, disabling TAS in US and Canada.\n",
355				dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>");
356		if ((!iwl_add_mcc_to_tas_block_list(data.block_list_array,
357						    &data.block_list_size,
358						    IWL_MCC_US)) ||
359		    (!iwl_add_mcc_to_tas_block_list(data.block_list_array,
360						    &data.block_list_size,
361						    IWL_MCC_CANADA))) {
362			IWL_DEBUG_RADIO(mld,
363					"Unable to add US/Canada to TAS block list, disabling TAS\n");
364			return;
365		}
366	} else {
367		IWL_DEBUG_RADIO(mld,
368				"System vendor '%s' is in the approved list.\n",
369				dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>");
370	}
371
372	cmd.block_list_size = cpu_to_le16(data.block_list_size);
373	for (u8 i = 0; i < data.block_list_size; i++)
374		cmd.block_list_array[i] =
375			cpu_to_le16(data.block_list_array[i]);
376	cmd.tas_config_info.table_source = data.table_source;
377	cmd.tas_config_info.table_revision = data.table_revision;
378	cmd.tas_config_info.value = cpu_to_le32(data.tas_selection);
379
380	ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd);
381	if (ret)
382		IWL_DEBUG_RADIO(mld, "failed to send TAS_CONFIG (%d)\n", ret);
383}
~/ Read-only file preview.