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