Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/iwlwifi/mld/regulatory.c
48287 views
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
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
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
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
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
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
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
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
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
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
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
371