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 struct iwl_fw_runtime *fwrt = &mld->fwrt; 167 union iwl_ppag_table_cmd cmd = { 168 .v7.ppag_config_info.table_source = fwrt->ppag_bios_source, 169 .v7.ppag_config_info.table_revision = fwrt->ppag_bios_rev, 170 .v7.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags), 171 }; 172 int ret; 173 174 IWL_DEBUG_RADIO(fwrt, 175 "PPAG MODE bits going to be sent: %d\n", 176 fwrt->ppag_flags); 177 178 for (int chain = 0; chain < IWL_NUM_CHAIN_LIMITS; chain++) { 179 for (int subband = 0; subband < IWL_NUM_SUB_BANDS_V2; subband++) { 180 cmd.v7.gain[chain][subband] = 181 fwrt->ppag_chains[chain].subbands[subband]; 182 IWL_DEBUG_RADIO(fwrt, 183 "PPAG table: chain[%d] band[%d]: gain = %d\n", 184 chain, subband, cmd.v7.gain[chain][subband]); 185 } 186 } 187 188 IWL_DEBUG_RADIO(mld, "Sending PER_PLATFORM_ANT_GAIN_CMD\n"); 189 ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP, 190 PER_PLATFORM_ANT_GAIN_CMD), 191 &cmd, sizeof(cmd.v7)); 192 if (ret < 0) 193 IWL_ERR(mld, "failed to send PER_PLATFORM_ANT_GAIN_CMD (%d)\n", 194 ret); 195 196 return ret; 197 } 198 199 int iwl_mld_init_ppag(struct iwl_mld *mld) 200 { 201 /* no need to read the table, done in INIT stage */ 202 203 if (!(iwl_is_ppag_approved(&mld->fwrt))) 204 return 0; 205 206 return iwl_mld_ppag_send_cmd(mld); 207 } 208 209 void iwl_mld_configure_lari(struct iwl_mld *mld) 210 { 211 struct iwl_fw_runtime *fwrt = &mld->fwrt; 212 struct iwl_lari_config_change_cmd cmd = { 213 .config_bitmap = iwl_get_lari_config_bitmap(fwrt), 214 }; 215 bool has_raw_dsm_capa = fw_has_capa(&fwrt->fw->ucode_capa, 216 IWL_UCODE_TLV_CAPA_FW_ACCEPTS_RAW_DSM_TABLE); 217 int ret; 218 u32 value; 219 220 ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_11AX_ENABLEMENT, &value); 221 if (!ret) { 222 if (!has_raw_dsm_capa) 223 value &= DSM_11AX_ALLOW_BITMAP; 224 cmd.oem_11ax_allow_bitmap = cpu_to_le32(value); 225 } 226 227 ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_UNII4_CHAN, &value); 228 if (!ret) { 229 if (!has_raw_dsm_capa) 230 value &= DSM_UNII4_ALLOW_BITMAP; 231 cmd.oem_unii4_allow_bitmap = cpu_to_le32(value); 232 } 233 234 ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ACTIVATE_CHANNEL, &value); 235 if (!ret) { 236 if (!has_raw_dsm_capa) 237 value &= CHAN_STATE_ACTIVE_BITMAP_CMD_V12; 238 cmd.chan_state_active_bitmap = cpu_to_le32(value); 239 } 240 241 ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_6E, &value); 242 if (!ret) 243 cmd.oem_uhb_allow_bitmap = cpu_to_le32(value); 244 245 ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_FORCE_DISABLE_CHANNELS, &value); 246 if (!ret) { 247 if (!has_raw_dsm_capa) 248 value &= DSM_FORCE_DISABLE_CHANNELS_ALLOWED_BITMAP; 249 cmd.force_disable_channels_bitmap = cpu_to_le32(value); 250 } 251 252 ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENERGY_DETECTION_THRESHOLD, 253 &value); 254 if (!ret) { 255 if (!has_raw_dsm_capa) 256 value &= DSM_EDT_ALLOWED_BITMAP; 257 cmd.edt_bitmap = cpu_to_le32(value); 258 } 259 260 ret = iwl_bios_get_wbem(fwrt, &value); 261 if (!ret) 262 cmd.oem_320mhz_allow_bitmap = cpu_to_le32(value); 263 264 ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_11BE, &value); 265 if (!ret) 266 cmd.oem_11be_allow_bitmap = cpu_to_le32(value); 267 268 if (!cmd.config_bitmap && 269 !cmd.oem_uhb_allow_bitmap && 270 !cmd.oem_11ax_allow_bitmap && 271 !cmd.oem_unii4_allow_bitmap && 272 !cmd.chan_state_active_bitmap && 273 !cmd.force_disable_channels_bitmap && 274 !cmd.edt_bitmap && 275 !cmd.oem_320mhz_allow_bitmap && 276 !cmd.oem_11be_allow_bitmap) 277 return; 278 279 IWL_DEBUG_RADIO(mld, 280 "sending LARI_CONFIG_CHANGE, config_bitmap=0x%x, oem_11ax_allow_bitmap=0x%x\n", 281 le32_to_cpu(cmd.config_bitmap), 282 le32_to_cpu(cmd.oem_11ax_allow_bitmap)); 283 IWL_DEBUG_RADIO(mld, 284 "sending LARI_CONFIG_CHANGE, oem_unii4_allow_bitmap=0x%x, chan_state_active_bitmap=0x%x\n", 285 le32_to_cpu(cmd.oem_unii4_allow_bitmap), 286 le32_to_cpu(cmd.chan_state_active_bitmap)); 287 IWL_DEBUG_RADIO(mld, 288 "sending LARI_CONFIG_CHANGE, oem_uhb_allow_bitmap=0x%x, force_disable_channels_bitmap=0x%x\n", 289 le32_to_cpu(cmd.oem_uhb_allow_bitmap), 290 le32_to_cpu(cmd.force_disable_channels_bitmap)); 291 IWL_DEBUG_RADIO(mld, 292 "sending LARI_CONFIG_CHANGE, edt_bitmap=0x%x, oem_320mhz_allow_bitmap=0x%x\n", 293 le32_to_cpu(cmd.edt_bitmap), 294 le32_to_cpu(cmd.oem_320mhz_allow_bitmap)); 295 IWL_DEBUG_RADIO(mld, 296 "sending LARI_CONFIG_CHANGE, oem_11be_allow_bitmap=0x%x\n", 297 le32_to_cpu(cmd.oem_11be_allow_bitmap)); 298 299 ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(REGULATORY_AND_NVM_GROUP, 300 LARI_CONFIG_CHANGE), &cmd); 301 if (ret) 302 IWL_DEBUG_RADIO(mld, 303 "Failed to send LARI_CONFIG_CHANGE (%d)\n", 304 ret); 305 } 306 307 void iwl_mld_init_uats(struct iwl_mld *mld) 308 { 309 int ret; 310 struct iwl_host_cmd cmd = { 311 .id = WIDE_ID(REGULATORY_AND_NVM_GROUP, 312 MCC_ALLOWED_AP_TYPE_CMD), 313 .data[0] = &mld->fwrt.uats_table, 314 .len[0] = sizeof(mld->fwrt.uats_table), 315 .dataflags[0] = IWL_HCMD_DFL_NOCOPY, 316 }; 317 318 if (!mld->fwrt.uats_valid) 319 return; 320 321 ret = iwl_mld_send_cmd(mld, &cmd); 322 if (ret) 323 IWL_ERR(mld, "failed to send MCC_ALLOWED_AP_TYPE_CMD (%d)\n", 324 ret); 325 } 326 327 void iwl_mld_init_tas(struct iwl_mld *mld) 328 { 329 int ret; 330 struct iwl_tas_data data = {}; 331 struct iwl_tas_config_cmd cmd = {}; 332 u32 cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP, TAS_CONFIG); 333 334 BUILD_BUG_ON(ARRAY_SIZE(data.block_list_array) != 335 IWL_WTAS_BLACK_LIST_MAX); 336 BUILD_BUG_ON(ARRAY_SIZE(cmd.block_list_array) != 337 IWL_WTAS_BLACK_LIST_MAX); 338 339 if (!fw_has_capa(&mld->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TAS_CFG)) { 340 IWL_DEBUG_RADIO(mld, "TAS not enabled in FW\n"); 341 return; 342 } 343 344 ret = iwl_bios_get_tas_table(&mld->fwrt, &data); 345 if (ret < 0) { 346 IWL_DEBUG_RADIO(mld, 347 "TAS table invalid or unavailable. (%d)\n", 348 ret); 349 return; 350 } 351 352 if (!iwl_is_tas_approved()) { 353 IWL_DEBUG_RADIO(mld, 354 "System vendor '%s' is not in the approved list, disabling TAS in US and Canada.\n", 355 dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>"); 356 if ((!iwl_add_mcc_to_tas_block_list(data.block_list_array, 357 &data.block_list_size, 358 IWL_MCC_US)) || 359 (!iwl_add_mcc_to_tas_block_list(data.block_list_array, 360 &data.block_list_size, 361 IWL_MCC_CANADA))) { 362 IWL_DEBUG_RADIO(mld, 363 "Unable to add US/Canada to TAS block list, disabling TAS\n"); 364 return; 365 } 366 } else { 367 IWL_DEBUG_RADIO(mld, 368 "System vendor '%s' is in the approved list.\n", 369 dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>"); 370 } 371 372 cmd.block_list_size = cpu_to_le16(data.block_list_size); 373 for (u8 i = 0; i < data.block_list_size; i++) 374 cmd.block_list_array[i] = 375 cpu_to_le16(data.block_list_array[i]); 376 cmd.tas_config_info.table_source = data.table_source; 377 cmd.tas_config_info.table_revision = data.table_revision; 378 cmd.tas_config_info.value = cpu_to_le32(data.tas_selection); 379 380 ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd); 381 if (ret) 382 IWL_DEBUG_RADIO(mld, "failed to send TAS_CONFIG (%d)\n", ret); 383 } 384