1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3 * Copyright (C) 2023, 2025 Intel Corporation
4 */
5 #include <linux/dmi.h>
6 #include "iwl-drv.h"
7 #include "iwl-debug.h"
8 #include "regulatory.h"
9 #include "fw/runtime.h"
10 #include "fw/uefi.h"
11
12 #define GET_BIOS_TABLE(__name, ...) \
13 do { \
14 int ret = -ENOENT; \
15 if (fwrt->uefi_tables_lock_status > UEFI_WIFI_GUID_UNLOCKED) \
16 ret = iwl_uefi_get_ ## __name(__VA_ARGS__); \
17 if (ret < 0) \
18 ret = iwl_acpi_get_ ## __name(__VA_ARGS__); \
19 return ret; \
20 } while (0)
21
22 #define IWL_BIOS_TABLE_LOADER(__name) \
23 int iwl_bios_get_ ## __name(struct iwl_fw_runtime *fwrt) \
24 {GET_BIOS_TABLE(__name, fwrt); } \
25 IWL_EXPORT_SYMBOL(iwl_bios_get_ ## __name)
26
27 #define IWL_BIOS_TABLE_LOADER_DATA(__name, data_type) \
28 int iwl_bios_get_ ## __name(struct iwl_fw_runtime *fwrt, \
29 data_type * data) \
30 {GET_BIOS_TABLE(__name, fwrt, data); } \
31 IWL_EXPORT_SYMBOL(iwl_bios_get_ ## __name)
32
33 IWL_BIOS_TABLE_LOADER(wrds_table);
34 IWL_BIOS_TABLE_LOADER(ewrd_table);
35 IWL_BIOS_TABLE_LOADER(wgds_table);
36 IWL_BIOS_TABLE_LOADER(ppag_table);
37 IWL_BIOS_TABLE_LOADER(phy_filters);
38 IWL_BIOS_TABLE_LOADER_DATA(tas_table, struct iwl_tas_data);
39 IWL_BIOS_TABLE_LOADER_DATA(pwr_limit, u64);
40 IWL_BIOS_TABLE_LOADER_DATA(mcc, char);
41 IWL_BIOS_TABLE_LOADER_DATA(eckv, u32);
42 IWL_BIOS_TABLE_LOADER_DATA(wbem, u32);
43 IWL_BIOS_TABLE_LOADER_DATA(dsbr, u32);
44
45
46 static const struct dmi_system_id dmi_ppag_approved_list[] = {
47 { .ident = "HP",
48 .matches = {
49 DMI_MATCH(DMI_SYS_VENDOR, "HP"),
50 },
51 },
52 { .ident = "SAMSUNG",
53 .matches = {
54 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"),
55 },
56 },
57 { .ident = "MSFT",
58 .matches = {
59 DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
60 },
61 },
62 { .ident = "ASUSTEK",
63 .matches = {
64 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
65 },
66 },
67 { .ident = "ASUS",
68 .matches = {
69 DMI_MATCH(DMI_SYS_VENDOR, "ASUS"),
70 },
71 },
72 { .ident = "GOOGLE-HP",
73 .matches = {
74 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
75 DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
76 },
77 },
78 { .ident = "GOOGLE-ASUS",
79 .matches = {
80 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
81 DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTek COMPUTER INC."),
82 },
83 },
84 { .ident = "GOOGLE-SAMSUNG",
85 .matches = {
86 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
87 DMI_MATCH(DMI_BOARD_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"),
88 },
89 },
90 { .ident = "DELL",
91 .matches = {
92 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
93 },
94 },
95 { .ident = "DELL",
96 .matches = {
97 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
98 },
99 },
100 { .ident = "RAZER",
101 .matches = {
102 DMI_MATCH(DMI_SYS_VENDOR, "Razer"),
103 },
104 },
105 { .ident = "Honor",
106 .matches = {
107 DMI_MATCH(DMI_SYS_VENDOR, "HONOR"),
108 },
109 },
110 { .ident = "WIKO",
111 .matches = {
112 DMI_MATCH(DMI_SYS_VENDOR, "WIKO"),
113 },
114 },
115 {}
116 };
117
118 static const struct dmi_system_id dmi_tas_approved_list[] = {
119 { .ident = "HP",
120 .matches = {
121 DMI_MATCH(DMI_SYS_VENDOR, "HP"),
122 },
123 },
124 { .ident = "SAMSUNG",
125 .matches = {
126 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"),
127 },
128 },
129 { .ident = "LENOVO",
130 .matches = {
131 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
132 },
133 },
134 { .ident = "DELL",
135 .matches = {
136 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
137 },
138 },
139 { .ident = "MSFT",
140 .matches = {
141 DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
142 },
143 },
144 { .ident = "Acer",
145 .matches = {
146 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
147 },
148 },
149 { .ident = "ASUSTEK",
150 .matches = {
151 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
152 },
153 },
154 { .ident = "ASUS",
155 .matches = {
156 DMI_MATCH(DMI_SYS_VENDOR, "ASUS"),
157 },
158 },
159 { .ident = "GOOGLE-HP",
160 .matches = {
161 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
162 DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
163 },
164 },
165 { .ident = "MSI",
166 .matches = {
167 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."),
168 },
169 },
170 { .ident = "Honor",
171 .matches = {
172 DMI_MATCH(DMI_SYS_VENDOR, "HONOR"),
173 },
174 },
175 /* keep last */
176 {}
177 };
178
iwl_sar_geo_support(struct iwl_fw_runtime * fwrt)179 bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt)
180 {
181 /*
182 * The PER_CHAIN_LIMIT_OFFSET_CMD command is not supported on
183 * earlier firmware versions. Unfortunately, we don't have a
184 * TLV API flag to rely on, so rely on the major version which
185 * is in the first byte of ucode_ver. This was implemented
186 * initially on version 38 and then backported to 17. It was
187 * also backported to 29, but only for 7265D devices. The
188 * intention was to have it in 36 as well, but not all 8000
189 * family got this feature enabled. The 8000 family is the
190 * only one using version 36, so skip this version entirely.
191 */
192 return IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) >= 38 ||
193 (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 17 &&
194 fwrt->trans->info.hw_rev != CSR_HW_REV_TYPE_3160) ||
195 (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 29 &&
196 ((fwrt->trans->info.hw_rev & CSR_HW_REV_TYPE_MSK) ==
197 CSR_HW_REV_TYPE_7265D));
198 }
199 IWL_EXPORT_SYMBOL(iwl_sar_geo_support);
200
iwl_sar_geo_fill_table(struct iwl_fw_runtime * fwrt,struct iwl_per_chain_offset * table,u32 n_bands,u32 n_profiles)201 int iwl_sar_geo_fill_table(struct iwl_fw_runtime *fwrt,
202 struct iwl_per_chain_offset *table,
203 u32 n_bands, u32 n_profiles)
204 {
205 int i, j;
206
207 if (!fwrt->geo_enabled)
208 return -ENODATA;
209
210 if (!iwl_sar_geo_support(fwrt))
211 return -EOPNOTSUPP;
212
213 for (i = 0; i < n_profiles; i++) {
214 for (j = 0; j < n_bands; j++) {
215 struct iwl_per_chain_offset *chain =
216 &table[i * n_bands + j];
217
218 chain->max_tx_power =
219 cpu_to_le16(fwrt->geo_profiles[i].bands[j].max);
220 chain->chain_a =
221 fwrt->geo_profiles[i].bands[j].chains[0];
222 chain->chain_b =
223 fwrt->geo_profiles[i].bands[j].chains[1];
224 IWL_DEBUG_RADIO(fwrt,
225 "SAR geographic profile[%d] Band[%d]: chain A = %d chain B = %d max_tx_power = %d\n",
226 i, j,
227 fwrt->geo_profiles[i].bands[j].chains[0],
228 fwrt->geo_profiles[i].bands[j].chains[1],
229 fwrt->geo_profiles[i].bands[j].max);
230 }
231 }
232
233 return 0;
234 }
235 IWL_EXPORT_SYMBOL(iwl_sar_geo_fill_table);
236
iwl_sar_fill_table(struct iwl_fw_runtime * fwrt,__le16 * per_chain,u32 n_subbands,int prof_a,int prof_b)237 static int iwl_sar_fill_table(struct iwl_fw_runtime *fwrt,
238 __le16 *per_chain, u32 n_subbands,
239 int prof_a, int prof_b)
240 {
241 int profs[BIOS_SAR_NUM_CHAINS] = { prof_a, prof_b };
242 int i, j;
243
244 for (i = 0; i < BIOS_SAR_NUM_CHAINS; i++) {
245 struct iwl_sar_profile *prof;
246
247 /* don't allow SAR to be disabled (profile 0 means disable) */
248 if (profs[i] == 0)
249 return -EPERM;
250
251 /* we are off by one, so allow up to BIOS_SAR_MAX_PROFILE_NUM */
252 if (profs[i] > BIOS_SAR_MAX_PROFILE_NUM)
253 return -EINVAL;
254
255 /* profiles go from 1 to 4, so decrement to access the array */
256 prof = &fwrt->sar_profiles[profs[i] - 1];
257
258 /* if the profile is disabled, do nothing */
259 if (!prof->enabled) {
260 IWL_DEBUG_RADIO(fwrt, "SAR profile %d is disabled.\n",
261 profs[i]);
262 /*
263 * if one of the profiles is disabled, we
264 * ignore all of them and return 1 to
265 * differentiate disabled from other failures.
266 */
267 return 1;
268 }
269
270 IWL_DEBUG_INFO(fwrt,
271 "SAR EWRD: chain %d profile index %d\n",
272 i, profs[i]);
273 IWL_DEBUG_RADIO(fwrt, " Chain[%d]:\n", i);
274 for (j = 0; j < n_subbands; j++) {
275 per_chain[i * n_subbands + j] =
276 cpu_to_le16(prof->chains[i].subbands[j]);
277 IWL_DEBUG_RADIO(fwrt, " Band[%d] = %d * .125dBm\n",
278 j, prof->chains[i].subbands[j]);
279 }
280 }
281
282 return 0;
283 }
284
iwl_sar_fill_profile(struct iwl_fw_runtime * fwrt,__le16 * per_chain,u32 n_tables,u32 n_subbands,int prof_a,int prof_b)285 int iwl_sar_fill_profile(struct iwl_fw_runtime *fwrt,
286 __le16 *per_chain, u32 n_tables, u32 n_subbands,
287 int prof_a, int prof_b)
288 {
289 int i, ret = 0;
290
291 for (i = 0; i < n_tables; i++) {
292 ret = iwl_sar_fill_table(fwrt,
293 &per_chain[i * n_subbands * BIOS_SAR_NUM_CHAINS],
294 n_subbands, prof_a, prof_b);
295 if (ret)
296 break;
297 }
298
299 return ret;
300 }
301 IWL_EXPORT_SYMBOL(iwl_sar_fill_profile);
302
iwl_ppag_value_valid(struct iwl_fw_runtime * fwrt,int chain,int subband)303 static bool iwl_ppag_value_valid(struct iwl_fw_runtime *fwrt, int chain,
304 int subband)
305 {
306 s8 ppag_val = fwrt->ppag_chains[chain].subbands[subband];
307
308 if ((subband == 0 &&
309 (ppag_val > IWL_PPAG_MAX_LB || ppag_val < IWL_PPAG_MIN_LB)) ||
310 (subband != 0 &&
311 (ppag_val > IWL_PPAG_MAX_HB || ppag_val < IWL_PPAG_MIN_HB))) {
312 IWL_DEBUG_RADIO(fwrt, "Invalid PPAG value: %d\n", ppag_val);
313 return false;
314 }
315 return true;
316 }
317
318 /* Utility function for iwlmvm and iwlxvt */
iwl_fill_ppag_table(struct iwl_fw_runtime * fwrt,union iwl_ppag_table_cmd * cmd,int * cmd_size)319 int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt,
320 union iwl_ppag_table_cmd *cmd, int *cmd_size)
321 {
322 u8 cmd_ver;
323 int i, j, num_sub_bands;
324 s8 *gain;
325 bool send_ppag_always;
326
327 /* many firmware images for JF lie about this */
328 if (CSR_HW_RFID_TYPE(fwrt->trans->info.hw_rf_id) ==
329 CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_JF))
330 return -EOPNOTSUPP;
331
332 if (!fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_PPAG)) {
333 IWL_DEBUG_RADIO(fwrt,
334 "PPAG capability not supported by FW, command not sent.\n");
335 return -EINVAL;
336 }
337
338 cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw,
339 WIDE_ID(PHY_OPS_GROUP,
340 PER_PLATFORM_ANT_GAIN_CMD), 1);
341 /*
342 * Starting from ver 4, driver needs to send the PPAG CMD regardless
343 * if PPAG is enabled/disabled or valid/invalid.
344 */
345 send_ppag_always = cmd_ver > 3;
346
347 /* Don't send PPAG if it is disabled */
348 if (!send_ppag_always && !fwrt->ppag_flags) {
349 IWL_DEBUG_RADIO(fwrt, "PPAG not enabled, command not sent.\n");
350 return -EINVAL;
351 }
352
353 IWL_DEBUG_RADIO(fwrt, "PPAG cmd ver is %d\n", cmd_ver);
354 if (cmd_ver == 1) {
355 num_sub_bands = IWL_NUM_SUB_BANDS_V1;
356 gain = cmd->v1.gain[0];
357 *cmd_size = sizeof(cmd->v1);
358 cmd->v1.flags = cpu_to_le32(fwrt->ppag_flags & IWL_PPAG_CMD_V1_MASK);
359 if (fwrt->ppag_bios_rev >= 1) {
360 /* in this case FW supports revision 0 */
361 IWL_DEBUG_RADIO(fwrt,
362 "PPAG table rev is %d, send truncated table\n",
363 fwrt->ppag_bios_rev);
364 }
365 } else if (cmd_ver == 5) {
366 num_sub_bands = IWL_NUM_SUB_BANDS_V2;
367 gain = cmd->v5.gain[0];
368 *cmd_size = sizeof(cmd->v5);
369 cmd->v5.flags = cpu_to_le32(fwrt->ppag_flags & IWL_PPAG_CMD_V5_MASK);
370 if (fwrt->ppag_bios_rev == 0) {
371 /* in this case FW supports revisions 1,2 or 3 */
372 IWL_DEBUG_RADIO(fwrt,
373 "PPAG table rev is 0, send padded table\n");
374 }
375 } else if (cmd_ver == 7) {
376 num_sub_bands = IWL_NUM_SUB_BANDS_V2;
377 gain = cmd->v7.gain[0];
378 *cmd_size = sizeof(cmd->v7);
379 cmd->v7.ppag_config_info.hdr.table_source =
380 fwrt->ppag_bios_source;
381 cmd->v7.ppag_config_info.hdr.table_revision =
382 fwrt->ppag_bios_rev;
383 cmd->v7.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags);
384 } else {
385 IWL_DEBUG_RADIO(fwrt, "Unsupported PPAG command version\n");
386 return -EINVAL;
387 }
388
389 /* ppag mode */
390 IWL_DEBUG_RADIO(fwrt,
391 "PPAG MODE bits were read from bios: %d\n",
392 fwrt->ppag_flags);
393
394 if (cmd_ver == 1 &&
395 !fw_has_capa(&fwrt->fw->ucode_capa,
396 IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) {
397 cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK);
398 IWL_DEBUG_RADIO(fwrt, "masking ppag China bit\n");
399 } else {
400 IWL_DEBUG_RADIO(fwrt, "isn't masking ppag China bit\n");
401 }
402
403 /* The 'flags' field is the same in v1 and v5 so we can just
404 * use v1 to access it.
405 */
406 IWL_DEBUG_RADIO(fwrt,
407 "PPAG MODE bits going to be sent: %d\n",
408 (cmd_ver < 7) ? le32_to_cpu(cmd->v1.flags) :
409 le32_to_cpu(cmd->v7.ppag_config_info.value));
410
411 for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {
412 for (j = 0; j < num_sub_bands; j++) {
413 if (!send_ppag_always &&
414 !iwl_ppag_value_valid(fwrt, i, j))
415 return -EINVAL;
416
417 gain[i * num_sub_bands + j] =
418 fwrt->ppag_chains[i].subbands[j];
419 IWL_DEBUG_RADIO(fwrt,
420 "PPAG table: chain[%d] band[%d]: gain = %d\n",
421 i, j, gain[i * num_sub_bands + j]);
422 }
423 }
424
425 return 0;
426 }
427 IWL_EXPORT_SYMBOL(iwl_fill_ppag_table);
428
iwl_is_ppag_approved(struct iwl_fw_runtime * fwrt)429 bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt)
430 {
431 if (!dmi_check_system(dmi_ppag_approved_list)) {
432 IWL_DEBUG_RADIO(fwrt,
433 "System vendor '%s' is not in the approved list, disabling PPAG.\n",
434 dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>");
435 fwrt->ppag_flags = 0;
436 return false;
437 }
438
439 return true;
440 }
441 IWL_EXPORT_SYMBOL(iwl_is_ppag_approved);
442
iwl_is_tas_approved(void)443 bool iwl_is_tas_approved(void)
444 {
445 return dmi_check_system(dmi_tas_approved_list);
446 }
447 IWL_EXPORT_SYMBOL(iwl_is_tas_approved);
448
449 struct iwl_tas_selection_data
iwl_parse_tas_selection(const u32 tas_selection_in,const u8 tbl_rev)450 iwl_parse_tas_selection(const u32 tas_selection_in, const u8 tbl_rev)
451 {
452 struct iwl_tas_selection_data tas_selection_out = {};
453 u8 override_iec = u32_get_bits(tas_selection_in,
454 IWL_WTAS_OVERRIDE_IEC_MSK);
455 u8 canada_tas_uhb = u32_get_bits(tas_selection_in,
456 IWL_WTAS_CANADA_UHB_MSK);
457 u8 enabled_iec = u32_get_bits(tas_selection_in,
458 IWL_WTAS_ENABLE_IEC_MSK);
459 u8 usa_tas_uhb = u32_get_bits(tas_selection_in,
460 IWL_WTAS_USA_UHB_MSK);
461
462 if (tbl_rev > 0) {
463 tas_selection_out.usa_tas_uhb_allowed = usa_tas_uhb;
464 tas_selection_out.override_tas_iec = override_iec;
465 tas_selection_out.enable_tas_iec = enabled_iec;
466 }
467
468 if (tbl_rev > 1)
469 tas_selection_out.canada_tas_uhb_allowed = canada_tas_uhb;
470
471 return tas_selection_out;
472 }
473 IWL_EXPORT_SYMBOL(iwl_parse_tas_selection);
474
iwl_add_mcc_to_tas_block_list(u16 * list,u8 * size,u16 mcc)475 bool iwl_add_mcc_to_tas_block_list(u16 *list, u8 *size, u16 mcc)
476 {
477 for (int i = 0; i < *size; i++) {
478 if (list[i] == mcc)
479 return true;
480 }
481
482 /* Verify that there is room for another country
483 * If *size == IWL_WTAS_BLACK_LIST_MAX, then the table is full.
484 */
485 if (*size >= IWL_WTAS_BLACK_LIST_MAX)
486 return false;
487
488 list[*size++] = mcc;
489 return true;
490 }
491 IWL_EXPORT_SYMBOL(iwl_add_mcc_to_tas_block_list);
492
iwl_bios_get_dsm(struct iwl_fw_runtime * fwrt,enum iwl_dsm_funcs func,u32 * value)493 int iwl_bios_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func,
494 u32 *value)
495 {
496 GET_BIOS_TABLE(dsm, fwrt, func, value);
497 }
498 IWL_EXPORT_SYMBOL(iwl_bios_get_dsm);
499
iwl_puncturing_is_allowed_in_bios(u32 puncturing,u16 mcc)500 bool iwl_puncturing_is_allowed_in_bios(u32 puncturing, u16 mcc)
501 {
502 /* Some kind of regulatory mess means we need to currently disallow
503 * puncturing in the US and Canada unless enabled in BIOS.
504 */
505 switch (mcc) {
506 case IWL_MCC_US:
507 return puncturing & IWL_UEFI_CNV_PUNCTURING_USA_EN_MSK;
508 case IWL_MCC_CANADA:
509 return puncturing & IWL_UEFI_CNV_PUNCTURING_CANADA_EN_MSK;
510 default:
511 return true;
512 }
513 }
514 IWL_EXPORT_SYMBOL(iwl_puncturing_is_allowed_in_bios);
515
iwl_rfi_is_enabled_in_bios(struct iwl_fw_runtime * fwrt)516 bool iwl_rfi_is_enabled_in_bios(struct iwl_fw_runtime *fwrt)
517 {
518 /* default behaviour is disabled */
519 u32 value = 0;
520 int ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_RFI_CONFIG, &value);
521
522 if (ret < 0) {
523 IWL_DEBUG_RADIO(fwrt, "Failed to get DSM RFI, ret=%d\n", ret);
524 return false;
525 }
526
527 value &= DSM_VALUE_RFI_DISABLE;
528 /* RFI BIOS CONFIG value can be 0 or 3 only.
529 * i.e 0 means DDR and DLVR enabled. 3 means DDR and DLVR disabled.
530 * 1 and 2 are invalid BIOS configurations, So, it's not possible to
531 * disable ddr/dlvr separately.
532 */
533 if (!value) {
534 IWL_DEBUG_RADIO(fwrt, "DSM RFI is evaluated to enable\n");
535 return true;
536 } else if (value == DSM_VALUE_RFI_DISABLE) {
537 IWL_DEBUG_RADIO(fwrt, "DSM RFI is evaluated to disable\n");
538 } else {
539 IWL_DEBUG_RADIO(fwrt,
540 "DSM RFI got invalid value, value=%d\n", value);
541 }
542
543 return false;
544 }
545 IWL_EXPORT_SYMBOL(iwl_rfi_is_enabled_in_bios);
546