From: Emmanuel Grumbach When support for the MCC update response version 8 was added, the API change was implemented only partially. That version introduced two new pieces of information about 320 MHz: the global capability REG_CAPA_V5_320MHZ_ALLOWED and a per-channel bit, BIT(12), in the channel flags carried by the response. Only the former was picked up. BIT(12) kept its previous meaning in the driver, NVM_CHANNEL_DC_HIGH, a leftover from old devices that nothing used, so the new bit was silently dropped. The two are not equivalent, the per-channel bit can be more restrictive than the global capability. When that happens, cfg80211 is told that 320 MHz is allowed, mac80211 builds a 320 MHz channel context, and the firmware rejects the resulting PHY_CONTEXT_CMD and asserts with 0x200014FC. Rename the bit to NVM_CHANNEL_320MHZ and set NL80211_RRF_NO_320MHZ for the channels that don't have it, so that we cap the bandwidth at 160 MHz instead of asserting the firmware. The bit is checked in every band, so that a future band that supports 320 MHz is covered as well. Fixes: e9b63341dc15 ("wifi: iwlwifi: update response for mcc_update command") Assisted-by: GitHub-Copilot:claude-opus-5 Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221675 Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit --- .../wireless/intel/iwlwifi/iwl-nvm-parse.c | 6 ++--- .../wireless/intel/iwlwifi/iwl-nvm-parse.h | 7 ++++-- .../wireless/intel/iwlwifi/tests/nvm_parse.c | 24 +++++++++++++++---- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 863d5e358152..f4df00514fb1 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -106,8 +106,6 @@ static const u16 iwl_unii9_nvm_channels[] = { #define IWL_NVM_NUM_CHANNELS_EXT 51 #define IWL_NVM_NUM_CHANNELS_UHB 110 #define IWL_NVM_NUM_CHANNELS_UNII9 ARRAY_SIZE(iwl_unii9_nvm_channels) -#define NUM_2GHZ_CHANNELS 14 -#define NUM_5GHZ_CHANNELS 37 #define FIRST_2GHZ_HT_MINUS 5 #define LAST_2GHZ_HT_PLUS 9 #define N_HW_ADDR_MASK 0xF @@ -280,7 +278,7 @@ static inline void iwl_nvm_print_channel_flags(struct device *dev, u32 level, CHECK_AND_PRINT_I(40MHZ), CHECK_AND_PRINT_I(80MHZ), CHECK_AND_PRINT_I(160MHZ), - CHECK_AND_PRINT_I(DC_HIGH), + CHECK_AND_PRINT_I(320MHZ), CHECK_AND_PRINT_I(VLP), CHECK_AND_PRINT_I(AFC)); #undef CHECK_AND_PRINT_I @@ -1631,6 +1629,8 @@ u32 iwl_nvm_get_regdom_bw_flags(const u16 *nvm_chan, flags |= NL80211_RRF_NO_80MHZ; if (!(nvm_flags & NVM_CHANNEL_160MHZ)) flags |= NL80211_RRF_NO_160MHZ; + if (!(nvm_flags & NVM_CHANNEL_320MHZ)) + flags |= NL80211_RRF_NO_320MHZ; if (!(nvm_flags & NVM_CHANNEL_ACTIVE)) flags |= NL80211_RRF_NO_IR; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h index 9ebb72d3726a..7b11682f5d45 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h @@ -10,6 +10,9 @@ #include "iwl-nvm-utils.h" #include "mei/iwl-mei.h" +#define NUM_2GHZ_CHANNELS 14 +#define NUM_5GHZ_CHANNELS 37 + /** * enum iwl_nvm_sbands_flags - modification flags for the channel profiles * @@ -82,7 +85,7 @@ struct iwl_reg_capa { * @NVM_CHANNEL_40MHZ: 40 MHz channel okay * @NVM_CHANNEL_80MHZ: 80 MHz channel okay * @NVM_CHANNEL_160MHZ: 160 MHz channel okay - * @NVM_CHANNEL_DC_HIGH: DC HIGH required/allowed (?) + * @NVM_CHANNEL_320MHZ: 320 MHz channel okay * @NVM_CHANNEL_VLP: client support connection to UHB VLP AP * @NVM_CHANNEL_AFC: client support connection to UHB AFC AP * @NVM_CHANNEL_VLP_AP_NOT_ALLOWED: UHB VLP AP not allowed, @@ -101,7 +104,7 @@ enum iwl_nvm_channel_flags { NVM_CHANNEL_40MHZ = BIT(9), NVM_CHANNEL_80MHZ = BIT(10), NVM_CHANNEL_160MHZ = BIT(11), - NVM_CHANNEL_DC_HIGH = BIT(12), + NVM_CHANNEL_320MHZ = BIT(12), NVM_CHANNEL_VLP = BIT(13), NVM_CHANNEL_AFC = BIT(14), NVM_CHANNEL_VLP_AP_NOT_ALLOWED = BIT(15), diff --git a/drivers/net/wireless/intel/iwlwifi/tests/nvm_parse.c b/drivers/net/wireless/intel/iwlwifi/tests/nvm_parse.c index 853911900bfd..5a50807e3181 100644 --- a/drivers/net/wireless/intel/iwlwifi/tests/nvm_parse.c +++ b/drivers/net/wireless/intel/iwlwifi/tests/nvm_parse.c @@ -2,7 +2,7 @@ /* * KUnit tests for NVM parse * - * Copyright (C) 2025 Intel Corporation + * Copyright (C) 2025-2026 Intel Corporation */ #include #include @@ -12,7 +12,9 @@ MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); static const struct nvm_flag_case { const char *desc; + int ch_idx; u16 nvm_flags; + struct iwl_reg_capa reg_capa; u32 reg_rule_flags; u32 set_reg_rule_flags; u32 clear_reg_rule_flags; @@ -36,6 +38,20 @@ static const struct nvm_flag_case { .clear_reg_rule_flags = NL80211_RRF_ALLOW_6GHZ_VLP_AP | NL80211_RRF_NO_6GHZ_VLP_CLIENT, }, + { + .desc = "Allow 320 MHz on a 6 GHz channel that reports it", + .ch_idx = NUM_2GHZ_CHANNELS + NUM_5GHZ_CHANNELS, + .nvm_flags = NVM_CHANNEL_320MHZ, + .reg_capa = { .allow_320mhz = true, }, + .clear_reg_rule_flags = NL80211_RRF_NO_320MHZ, + }, + { + .desc = "Restrict 320 MHz on a 6 GHz channel without the bit", + .ch_idx = NUM_2GHZ_CHANNELS + NUM_5GHZ_CHANNELS, + .nvm_flags = 0, + .reg_capa = { .allow_320mhz = true, }, + .set_reg_rule_flags = NL80211_RRF_NO_320MHZ, + }, }; KUNIT_ARRAY_PARAM_DESC(nvm_flag, nvm_flag_cases, desc) @@ -43,11 +59,11 @@ KUNIT_ARRAY_PARAM_DESC(nvm_flag, nvm_flag_cases, desc) static void test_nvm_flags(struct kunit *test) { const struct nvm_flag_case *params = test->param_value; - struct iwl_reg_capa reg_capa = {}; u32 flags = 0; - flags = iwl_nvm_get_regdom_bw_flags(NULL, 0, params->nvm_flags, - reg_capa); + flags = iwl_nvm_get_regdom_bw_flags(NULL, params->ch_idx, + params->nvm_flags, + params->reg_capa); if ((params->set_reg_rule_flags & flags) != params->set_reg_rule_flags) KUNIT_FAIL(test, "Expected set bits:0x%08x flags:0x%08x\n", -- 2.34.1