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