xref: /linux/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c (revision 8be4d31cb8aaeea27bde4b7ddb26e28a89062ebf)
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 
iwl_mld_get_bios_tables(struct iwl_mld * mld)16 void 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 
iwl_mld_geo_sar_init(struct iwl_mld * mld)71 static 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 
iwl_mld_config_sar_profile(struct iwl_mld * mld,int prof_a,int prof_b)95 int 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 
iwl_mld_init_sar(struct iwl_mld * mld)116 int 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 
iwl_mld_init_sgom(struct iwl_mld * mld)140 int 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 
iwl_mld_ppag_send_cmd(struct iwl_mld * mld)164 static int iwl_mld_ppag_send_cmd(struct iwl_mld *mld)
165 {
166 	union iwl_ppag_table_cmd cmd = {};
167 	int ret, len;
168 
169 	ret = iwl_fill_ppag_table(&mld->fwrt, &cmd, &len);
170 	/* Not supporting PPAG table is a valid scenario */
171 	if (ret < 0)
172 		return 0;
173 
174 	IWL_DEBUG_RADIO(mld, "Sending PER_PLATFORM_ANT_GAIN_CMD\n");
175 	ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP,
176 						PER_PLATFORM_ANT_GAIN_CMD),
177 				   &cmd, len);
178 	if (ret < 0)
179 		IWL_ERR(mld, "failed to send PER_PLATFORM_ANT_GAIN_CMD (%d)\n",
180 			ret);
181 
182 	return ret;
183 }
184 
iwl_mld_init_ppag(struct iwl_mld * mld)185 int iwl_mld_init_ppag(struct iwl_mld *mld)
186 {
187 	/* no need to read the table, done in INIT stage */
188 
189 	if (!(iwl_is_ppag_approved(&mld->fwrt)))
190 		return 0;
191 
192 	return iwl_mld_ppag_send_cmd(mld);
193 }
194 
iwl_mld_configure_lari(struct iwl_mld * mld)195 void iwl_mld_configure_lari(struct iwl_mld *mld)
196 {
197 	struct iwl_fw_runtime *fwrt = &mld->fwrt;
198 	struct iwl_lari_config_change_cmd cmd = {
199 		.config_bitmap = iwl_get_lari_config_bitmap(fwrt),
200 	};
201 	bool has_raw_dsm_capa = fw_has_capa(&fwrt->fw->ucode_capa,
202 					    IWL_UCODE_TLV_CAPA_FW_ACCEPTS_RAW_DSM_TABLE);
203 	int ret;
204 	u32 value;
205 
206 	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_11AX_ENABLEMENT, &value);
207 	if (!ret) {
208 		if (!has_raw_dsm_capa)
209 			value &= DSM_11AX_ALLOW_BITMAP;
210 		cmd.oem_11ax_allow_bitmap = cpu_to_le32(value);
211 	}
212 
213 	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_UNII4_CHAN, &value);
214 	if (!ret) {
215 		if (!has_raw_dsm_capa)
216 			value &= DSM_UNII4_ALLOW_BITMAP;
217 		cmd.oem_unii4_allow_bitmap = cpu_to_le32(value);
218 	}
219 
220 	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ACTIVATE_CHANNEL, &value);
221 	if (!ret) {
222 		if (!has_raw_dsm_capa)
223 			value &= CHAN_STATE_ACTIVE_BITMAP_CMD_V12;
224 		cmd.chan_state_active_bitmap = cpu_to_le32(value);
225 	}
226 
227 	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_6E, &value);
228 	if (!ret)
229 		cmd.oem_uhb_allow_bitmap = cpu_to_le32(value);
230 
231 	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_FORCE_DISABLE_CHANNELS, &value);
232 	if (!ret) {
233 		if (!has_raw_dsm_capa)
234 			value &= DSM_FORCE_DISABLE_CHANNELS_ALLOWED_BITMAP;
235 		cmd.force_disable_channels_bitmap = cpu_to_le32(value);
236 	}
237 
238 	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENERGY_DETECTION_THRESHOLD,
239 			       &value);
240 	if (!ret) {
241 		if (!has_raw_dsm_capa)
242 			value &= DSM_EDT_ALLOWED_BITMAP;
243 		cmd.edt_bitmap = cpu_to_le32(value);
244 	}
245 
246 	ret = iwl_bios_get_wbem(fwrt, &value);
247 	if (!ret)
248 		cmd.oem_320mhz_allow_bitmap = cpu_to_le32(value);
249 
250 	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_11BE, &value);
251 	if (!ret)
252 		cmd.oem_11be_allow_bitmap = cpu_to_le32(value);
253 
254 	if (!cmd.config_bitmap &&
255 	    !cmd.oem_uhb_allow_bitmap &&
256 	    !cmd.oem_11ax_allow_bitmap &&
257 	    !cmd.oem_unii4_allow_bitmap &&
258 	    !cmd.chan_state_active_bitmap &&
259 	    !cmd.force_disable_channels_bitmap &&
260 	    !cmd.edt_bitmap &&
261 	    !cmd.oem_320mhz_allow_bitmap &&
262 	    !cmd.oem_11be_allow_bitmap)
263 		return;
264 
265 	IWL_DEBUG_RADIO(mld,
266 			"sending LARI_CONFIG_CHANGE, config_bitmap=0x%x, oem_11ax_allow_bitmap=0x%x\n",
267 			le32_to_cpu(cmd.config_bitmap),
268 			le32_to_cpu(cmd.oem_11ax_allow_bitmap));
269 	IWL_DEBUG_RADIO(mld,
270 			"sending LARI_CONFIG_CHANGE, oem_unii4_allow_bitmap=0x%x, chan_state_active_bitmap=0x%x\n",
271 			le32_to_cpu(cmd.oem_unii4_allow_bitmap),
272 			le32_to_cpu(cmd.chan_state_active_bitmap));
273 	IWL_DEBUG_RADIO(mld,
274 			"sending LARI_CONFIG_CHANGE, oem_uhb_allow_bitmap=0x%x, force_disable_channels_bitmap=0x%x\n",
275 			le32_to_cpu(cmd.oem_uhb_allow_bitmap),
276 			le32_to_cpu(cmd.force_disable_channels_bitmap));
277 	IWL_DEBUG_RADIO(mld,
278 			"sending LARI_CONFIG_CHANGE, edt_bitmap=0x%x, oem_320mhz_allow_bitmap=0x%x\n",
279 			le32_to_cpu(cmd.edt_bitmap),
280 			le32_to_cpu(cmd.oem_320mhz_allow_bitmap));
281 	IWL_DEBUG_RADIO(mld,
282 			"sending LARI_CONFIG_CHANGE, oem_11be_allow_bitmap=0x%x\n",
283 			le32_to_cpu(cmd.oem_11be_allow_bitmap));
284 
285 	ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(REGULATORY_AND_NVM_GROUP,
286 						LARI_CONFIG_CHANGE), &cmd);
287 	if (ret)
288 		IWL_DEBUG_RADIO(mld,
289 				"Failed to send LARI_CONFIG_CHANGE (%d)\n",
290 				ret);
291 }
292 
iwl_mld_init_uats(struct iwl_mld * mld)293 void iwl_mld_init_uats(struct iwl_mld *mld)
294 {
295 	int ret;
296 	struct iwl_host_cmd cmd = {
297 		.id = WIDE_ID(REGULATORY_AND_NVM_GROUP,
298 			      MCC_ALLOWED_AP_TYPE_CMD),
299 		.data[0] = &mld->fwrt.uats_table,
300 		.len[0] =  sizeof(mld->fwrt.uats_table),
301 		.dataflags[0] = IWL_HCMD_DFL_NOCOPY,
302 	};
303 
304 	if (!mld->fwrt.uats_valid)
305 		return;
306 
307 	ret = iwl_mld_send_cmd(mld, &cmd);
308 	if (ret)
309 		IWL_ERR(mld, "failed to send MCC_ALLOWED_AP_TYPE_CMD (%d)\n",
310 			ret);
311 }
312 
iwl_mld_init_tas(struct iwl_mld * mld)313 void iwl_mld_init_tas(struct iwl_mld *mld)
314 {
315 	int ret;
316 	struct iwl_tas_data data = {};
317 	struct iwl_tas_config_cmd cmd = {};
318 	u32 cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP, TAS_CONFIG);
319 
320 	BUILD_BUG_ON(ARRAY_SIZE(data.block_list_array) !=
321 		     IWL_WTAS_BLACK_LIST_MAX);
322 	BUILD_BUG_ON(ARRAY_SIZE(cmd.block_list_array) !=
323 		     IWL_WTAS_BLACK_LIST_MAX);
324 
325 	if (!fw_has_capa(&mld->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TAS_CFG)) {
326 		IWL_DEBUG_RADIO(mld, "TAS not enabled in FW\n");
327 		return;
328 	}
329 
330 	ret = iwl_bios_get_tas_table(&mld->fwrt, &data);
331 	if (ret < 0) {
332 		IWL_DEBUG_RADIO(mld,
333 				"TAS table invalid or unavailable. (%d)\n",
334 				ret);
335 		return;
336 	}
337 
338 	if (!iwl_is_tas_approved()) {
339 		IWL_DEBUG_RADIO(mld,
340 				"System vendor '%s' is not in the approved list, disabling TAS in US and Canada.\n",
341 				dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>");
342 		if ((!iwl_add_mcc_to_tas_block_list(data.block_list_array,
343 						    &data.block_list_size,
344 						    IWL_MCC_US)) ||
345 		    (!iwl_add_mcc_to_tas_block_list(data.block_list_array,
346 						    &data.block_list_size,
347 						    IWL_MCC_CANADA))) {
348 			IWL_DEBUG_RADIO(mld,
349 					"Unable to add US/Canada to TAS block list, disabling TAS\n");
350 			return;
351 		}
352 	} else {
353 		IWL_DEBUG_RADIO(mld,
354 				"System vendor '%s' is in the approved list.\n",
355 				dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>");
356 	}
357 
358 	cmd.block_list_size = cpu_to_le16(data.block_list_size);
359 	for (u8 i = 0; i < data.block_list_size; i++)
360 		cmd.block_list_array[i] =
361 			cpu_to_le16(data.block_list_array[i]);
362 	cmd.tas_config_info.table_source = data.table_source;
363 	cmd.tas_config_info.table_revision = data.table_revision;
364 	cmd.tas_config_info.value = cpu_to_le32(data.tas_selection);
365 
366 	ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd);
367 	if (ret)
368 		IWL_DEBUG_RADIO(mld, "failed to send TAS_CONFIG (%d)\n", ret);
369 }
370