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