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 	ret = iwl_uefi_get_uats_table(mld->trans, &mld->fwrt);
67 	if (ret)
68 		IWL_DEBUG_RADIO(mld, "failed to read UATS table (%d)\n", ret);
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 	union iwl_geo_tx_power_profiles_cmd cmd;
75 	u16 len;
76 	u32 n_bands;
77 	__le32 sk = cpu_to_le32(0);
78 	int ret;
79 	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id,
80 					   IWL_FW_CMD_VER_UNKNOWN);
81 
82 	BUILD_BUG_ON(offsetof(struct iwl_geo_tx_power_profiles_cmd_v4, ops) !=
83 		     offsetof(struct iwl_geo_tx_power_profiles_cmd_v5, ops));
84 
85 	cmd.v4.ops = cpu_to_le32(IWL_PER_CHAIN_OFFSET_SET_TABLES);
86 
87 	/* Only set to South Korea if the table revision is 1 */
88 	if (mld->fwrt.geo_rev == 1)
89 		sk = cpu_to_le32(1);
90 
91 	if (cmd_ver == 5) {
92 		len = sizeof(cmd.v5);
93 		n_bands = ARRAY_SIZE(cmd.v5.table[0]);
94 		cmd.v5.table_revision = sk;
95 	} else if (cmd_ver == 4) {
96 		len = sizeof(cmd.v4);
97 		n_bands = ARRAY_SIZE(cmd.v4.table[0]);
98 		cmd.v4.table_revision = sk;
99 	} else {
100 		return -EOPNOTSUPP;
101 	}
102 
103 	BUILD_BUG_ON(offsetof(struct iwl_geo_tx_power_profiles_cmd_v4, table) !=
104 		     offsetof(struct iwl_geo_tx_power_profiles_cmd_v5, table));
105 	/* the table is at the same position for all versions, so set use v4 */
106 	ret = iwl_sar_geo_fill_table(&mld->fwrt, &cmd.v4.table[0][0],
107 				     n_bands, BIOS_GEO_MAX_PROFILE_NUM);
108 
109 	/* It is a valid scenario to not support SAR, or miss wgds table,
110 	 * but in that case there is no need to send the command.
111 	 */
112 	if (ret)
113 		return 0;
114 
115 	return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, len);
116 }
117 
iwl_mld_config_sar_profile(struct iwl_mld * mld,int prof_a,int prof_b)118 int iwl_mld_config_sar_profile(struct iwl_mld *mld, int prof_a, int prof_b)
119 {
120 	u32 cmd_id = REDUCE_TX_POWER_CMD;
121 	struct iwl_dev_tx_power_cmd cmd = {
122 		.common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS),
123 	};
124 	__le16 *per_chain;
125 	int ret;
126 	u16 len = sizeof(cmd.common);
127 	u32 n_subbands;
128 	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id,
129 					   IWL_FW_CMD_VER_UNKNOWN);
130 
131 	if (cmd_ver == 10) {
132 		len += sizeof(cmd.v10);
133 		n_subbands = IWL_NUM_SUB_BANDS_V2;
134 		per_chain = &cmd.v10.per_chain[0][0][0];
135 		cmd.v10.flags =
136 			cpu_to_le32(mld->fwrt.reduced_power_flags);
137 	} else if (cmd_ver == 9) {
138 		len += sizeof(cmd.v9);
139 		n_subbands = IWL_NUM_SUB_BANDS_V1;
140 		per_chain = &cmd.v9.per_chain[0][0];
141 	} else {
142 		return -EOPNOTSUPP;
143 	}
144 
145 	/* TODO: CDB - support IWL_NUM_CHAIN_TABLES_V2 */
146 	ret = iwl_sar_fill_profile(&mld->fwrt, per_chain,
147 				   IWL_NUM_CHAIN_TABLES,
148 				   n_subbands, prof_a, prof_b);
149 	/* return on error or if the profile is disabled (positive number) */
150 	if (ret)
151 		return ret;
152 
153 	return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, len);
154 }
155 
iwl_mld_init_sar(struct iwl_mld * mld)156 int iwl_mld_init_sar(struct iwl_mld *mld)
157 {
158 	int chain_a_prof = 1;
159 	int chain_b_prof = 1;
160 	int ret;
161 
162 	/* If no profile was chosen by the user yet, choose profile 1 (WRDS) as
163 	 * default for both chains
164 	 */
165 	if (mld->fwrt.sar_chain_a_profile && mld->fwrt.sar_chain_b_profile) {
166 		chain_a_prof = mld->fwrt.sar_chain_a_profile;
167 		chain_b_prof = mld->fwrt.sar_chain_b_profile;
168 	}
169 
170 	ret = iwl_mld_config_sar_profile(mld, chain_a_prof, chain_b_prof);
171 	if (ret < 0)
172 		return ret;
173 
174 	if (ret)
175 		return 0;
176 
177 	return iwl_mld_geo_sar_init(mld);
178 }
179 
iwl_mld_init_sgom(struct iwl_mld * mld)180 int iwl_mld_init_sgom(struct iwl_mld *mld)
181 {
182 	int ret;
183 	struct iwl_host_cmd cmd = {
184 		.id = WIDE_ID(REGULATORY_AND_NVM_GROUP,
185 			      SAR_OFFSET_MAPPING_TABLE_CMD),
186 		.data[0] = &mld->fwrt.sgom_table,
187 		.len[0] =  sizeof(mld->fwrt.sgom_table),
188 		.dataflags[0] = IWL_HCMD_DFL_NOCOPY,
189 	};
190 
191 	if (!mld->fwrt.sgom_enabled) {
192 		IWL_DEBUG_RADIO(mld, "SGOM table is disabled\n");
193 		return 0;
194 	}
195 
196 	ret = iwl_mld_send_cmd(mld, &cmd);
197 	if (ret)
198 		IWL_ERR(mld,
199 			"failed to send SAR_OFFSET_MAPPING_CMD (%d)\n", ret);
200 
201 	return ret;
202 }
203 
iwl_mld_ppag_send_cmd(struct iwl_mld * mld)204 static int iwl_mld_ppag_send_cmd(struct iwl_mld *mld)
205 {
206 	union iwl_ppag_table_cmd cmd = {};
207 	int ret, len;
208 
209 	ret = iwl_fill_ppag_table(&mld->fwrt, &cmd, &len);
210 	/* Not supporting PPAG table is a valid scenario */
211 	if (ret < 0)
212 		return 0;
213 
214 	IWL_DEBUG_RADIO(mld, "Sending PER_PLATFORM_ANT_GAIN_CMD\n");
215 	ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP,
216 						PER_PLATFORM_ANT_GAIN_CMD),
217 				   &cmd, len);
218 	if (ret < 0)
219 		IWL_ERR(mld, "failed to send PER_PLATFORM_ANT_GAIN_CMD (%d)\n",
220 			ret);
221 
222 	return ret;
223 }
224 
iwl_mld_init_ppag(struct iwl_mld * mld)225 int iwl_mld_init_ppag(struct iwl_mld *mld)
226 {
227 	/* no need to read the table, done in INIT stage */
228 
229 	if (!(iwl_is_ppag_approved(&mld->fwrt)))
230 		return 0;
231 
232 	return iwl_mld_ppag_send_cmd(mld);
233 }
234 
iwl_mld_configure_lari(struct iwl_mld * mld)235 void iwl_mld_configure_lari(struct iwl_mld *mld)
236 {
237 	struct iwl_fw_runtime *fwrt = &mld->fwrt;
238 	struct iwl_lari_config_change_cmd cmd = {
239 		.config_bitmap = iwl_get_lari_config_bitmap(fwrt),
240 	};
241 	int ret;
242 	u32 value;
243 
244 	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_11AX_ENABLEMENT, &value);
245 	if (!ret)
246 		cmd.oem_11ax_allow_bitmap = cpu_to_le32(value);
247 
248 	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_UNII4_CHAN, &value);
249 	if (!ret)
250 		cmd.oem_unii4_allow_bitmap =
251 			cpu_to_le32(value &= DSM_UNII4_ALLOW_BITMAP);
252 
253 	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ACTIVATE_CHANNEL, &value);
254 	if (!ret)
255 		cmd.chan_state_active_bitmap = cpu_to_le32(value);
256 
257 	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_6E, &value);
258 	if (!ret)
259 		cmd.oem_uhb_allow_bitmap = cpu_to_le32(value);
260 
261 	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_FORCE_DISABLE_CHANNELS, &value);
262 	if (!ret)
263 		cmd.force_disable_channels_bitmap = cpu_to_le32(value);
264 
265 	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENERGY_DETECTION_THRESHOLD,
266 			       &value);
267 	if (!ret)
268 		cmd.edt_bitmap = cpu_to_le32(value);
269 
270 	ret = iwl_bios_get_wbem(fwrt, &value);
271 	if (!ret)
272 		cmd.oem_320mhz_allow_bitmap = cpu_to_le32(value);
273 
274 	ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_11BE, &value);
275 	if (!ret)
276 		cmd.oem_11be_allow_bitmap = cpu_to_le32(value);
277 
278 	if (!cmd.config_bitmap &&
279 	    !cmd.oem_uhb_allow_bitmap &&
280 	    !cmd.oem_11ax_allow_bitmap &&
281 	    !cmd.oem_unii4_allow_bitmap &&
282 	    !cmd.chan_state_active_bitmap &&
283 	    !cmd.force_disable_channels_bitmap &&
284 	    !cmd.edt_bitmap &&
285 	    !cmd.oem_320mhz_allow_bitmap &&
286 	    !cmd.oem_11be_allow_bitmap)
287 		return;
288 
289 	IWL_DEBUG_RADIO(mld,
290 			"sending LARI_CONFIG_CHANGE, config_bitmap=0x%x, oem_11ax_allow_bitmap=0x%x\n",
291 			le32_to_cpu(cmd.config_bitmap),
292 			le32_to_cpu(cmd.oem_11ax_allow_bitmap));
293 	IWL_DEBUG_RADIO(mld,
294 			"sending LARI_CONFIG_CHANGE, oem_unii4_allow_bitmap=0x%x, chan_state_active_bitmap=0x%x\n",
295 			le32_to_cpu(cmd.oem_unii4_allow_bitmap),
296 			le32_to_cpu(cmd.chan_state_active_bitmap));
297 	IWL_DEBUG_RADIO(mld,
298 			"sending LARI_CONFIG_CHANGE, oem_uhb_allow_bitmap=0x%x, force_disable_channels_bitmap=0x%x\n",
299 			le32_to_cpu(cmd.oem_uhb_allow_bitmap),
300 			le32_to_cpu(cmd.force_disable_channels_bitmap));
301 	IWL_DEBUG_RADIO(mld,
302 			"sending LARI_CONFIG_CHANGE, edt_bitmap=0x%x, oem_320mhz_allow_bitmap=0x%x\n",
303 			le32_to_cpu(cmd.edt_bitmap),
304 			le32_to_cpu(cmd.oem_320mhz_allow_bitmap));
305 	IWL_DEBUG_RADIO(mld,
306 			"sending LARI_CONFIG_CHANGE, oem_11be_allow_bitmap=0x%x\n",
307 			le32_to_cpu(cmd.oem_11be_allow_bitmap));
308 
309 	ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(REGULATORY_AND_NVM_GROUP,
310 						LARI_CONFIG_CHANGE), &cmd);
311 	if (ret)
312 		IWL_DEBUG_RADIO(mld,
313 				"Failed to send LARI_CONFIG_CHANGE (%d)\n",
314 				ret);
315 }
316 
iwl_mld_init_uats(struct iwl_mld * mld)317 void iwl_mld_init_uats(struct iwl_mld *mld)
318 {
319 	int ret;
320 	struct iwl_host_cmd cmd = {
321 		.id = WIDE_ID(REGULATORY_AND_NVM_GROUP,
322 			      MCC_ALLOWED_AP_TYPE_CMD),
323 		.data[0] = &mld->fwrt.uats_table,
324 		.len[0] =  sizeof(mld->fwrt.uats_table),
325 		.dataflags[0] = IWL_HCMD_DFL_NOCOPY,
326 	};
327 
328 	if (!mld->fwrt.uats_valid)
329 		return;
330 
331 	ret = iwl_mld_send_cmd(mld, &cmd);
332 	if (ret)
333 		IWL_ERR(mld, "failed to send MCC_ALLOWED_AP_TYPE_CMD (%d)\n",
334 			ret);
335 }
336 
iwl_mld_init_tas(struct iwl_mld * mld)337 void iwl_mld_init_tas(struct iwl_mld *mld)
338 {
339 	int ret;
340 	struct iwl_tas_data data = {};
341 	struct iwl_tas_config_cmd cmd = {};
342 	u32 cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP, TAS_CONFIG);
343 
344 	BUILD_BUG_ON(ARRAY_SIZE(data.block_list_array) !=
345 		     IWL_WTAS_BLACK_LIST_MAX);
346 	BUILD_BUG_ON(ARRAY_SIZE(cmd.block_list_array) !=
347 		     IWL_WTAS_BLACK_LIST_MAX);
348 
349 	if (!fw_has_capa(&mld->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TAS_CFG)) {
350 		IWL_DEBUG_RADIO(mld, "TAS not enabled in FW\n");
351 		return;
352 	}
353 
354 	ret = iwl_bios_get_tas_table(&mld->fwrt, &data);
355 	if (ret < 0) {
356 		IWL_DEBUG_RADIO(mld,
357 				"TAS table invalid or unavailable. (%d)\n",
358 				ret);
359 		return;
360 	}
361 
362 	if (!iwl_is_tas_approved()) {
363 		IWL_DEBUG_RADIO(mld,
364 				"System vendor '%s' is not in the approved list, disabling TAS in US and Canada.\n",
365 				dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>");
366 		if ((!iwl_add_mcc_to_tas_block_list(data.block_list_array,
367 						    &data.block_list_size,
368 						    IWL_MCC_US)) ||
369 		    (!iwl_add_mcc_to_tas_block_list(data.block_list_array,
370 						    &data.block_list_size,
371 						    IWL_MCC_CANADA))) {
372 			IWL_DEBUG_RADIO(mld,
373 					"Unable to add US/Canada to TAS block list, disabling TAS\n");
374 			return;
375 		}
376 	} else {
377 		IWL_DEBUG_RADIO(mld,
378 				"System vendor '%s' is in the approved list.\n",
379 				dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>");
380 	}
381 
382 	cmd.block_list_size = cpu_to_le16(data.block_list_size);
383 	for (u8 i = 0; i < data.block_list_size; i++)
384 		cmd.block_list_array[i] =
385 			cpu_to_le16(data.block_list_array[i]);
386 	cmd.tas_config_info.table_source = data.table_source;
387 	cmd.tas_config_info.table_revision = data.table_revision;
388 	cmd.tas_config_info.value = cpu_to_le32(data.tas_selection);
389 
390 	ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd);
391 	if (ret)
392 		IWL_DEBUG_RADIO(mld, "failed to send TAS_CONFIG (%d)\n", ret);
393 }
394