* [PATCH iwlwifi-next 01/15] wifi: iwlwifi: mld: enable UHR in TLC
2026-03-20 8:09 [PATCH iwlwifi-next 00/15] wifi: iwlwifi: updates - 2026-03-20 Miri Korenblit
@ 2026-03-20 8:09 ` Miri Korenblit
2026-03-20 8:09 ` [PATCH iwlwifi-next 02/15] wifi: iwlwifi: mld: set UHR MCS in RX status Miri Korenblit
` (13 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Miri Korenblit @ 2026-03-20 8:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
Tell the firmware if UHR is supported, including ELR (enhanced
long range) MCS support.
Note that the spec currently doesn't differentiate between 1.5
and 3 Mbps ELR MCSes, unlike the firmware.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/mld/tlc.c | 28 +++++++++++++++++---
1 file changed, 24 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c
index 62a54c37a98c..ede385909e38 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c
@@ -36,7 +36,8 @@ iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld,
struct ieee80211_vif *vif,
struct ieee80211_link_sta *link_sta,
const struct ieee80211_sta_he_cap *own_he_cap,
- const struct ieee80211_sta_eht_cap *own_eht_cap)
+ const struct ieee80211_sta_eht_cap *own_eht_cap,
+ const struct ieee80211_sta_uhr_cap *own_uhr_cap)
{
struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap;
struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap;
@@ -90,6 +91,12 @@ iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld,
flags |= IWL_TLC_MNG_CFG_FLAGS_EHT_EXTRA_LTF_MSK;
}
+ if (link_sta->uhr_cap.has_uhr && own_uhr_cap &&
+ link_sta->uhr_cap.phy.cap & IEEE80211_UHR_PHY_CAP_ELR_RX &&
+ own_uhr_cap->phy.cap & IEEE80211_UHR_PHY_CAP_ELR_TX)
+ flags |= IWL_TLC_MNG_CFG_FLAGS_UHR_ELR_1_5_MBPS_MSK |
+ IWL_TLC_MNG_CFG_FLAGS_UHR_ELR_3_MBPS_MSK;
+
return cpu_to_le16(flags);
}
@@ -406,6 +413,7 @@ iwl_mld_fill_supp_rates(struct iwl_mld *mld, struct ieee80211_vif *vif,
struct ieee80211_supported_band *sband,
const struct ieee80211_sta_he_cap *own_he_cap,
const struct ieee80211_sta_eht_cap *own_eht_cap,
+ const struct ieee80211_sta_uhr_cap *own_uhr_cap,
struct iwl_tlc_config_cmd *cmd)
{
int i;
@@ -423,7 +431,16 @@ iwl_mld_fill_supp_rates(struct iwl_mld *mld, struct ieee80211_vif *vif,
cmd->non_ht_rates = cpu_to_le16(non_ht_rates);
cmd->mode = IWL_TLC_MNG_MODE_NON_HT;
- if (link_sta->eht_cap.has_eht && own_he_cap && own_eht_cap) {
+ if (link_sta->uhr_cap.has_uhr && own_uhr_cap) {
+ cmd->mode = IWL_TLC_MNG_MODE_UHR;
+ /*
+ * FIXME: spec currently inherits from EHT but has no
+ * finer MCS bits. Once that's there, need to add them
+ * to the bitmaps (and maybe copy this to UHR, or so.)
+ */
+ iwl_mld_fill_eht_rates(vif, link_sta, own_he_cap,
+ own_eht_cap, cmd);
+ } else if (link_sta->eht_cap.has_eht && own_he_cap && own_eht_cap) {
cmd->mode = IWL_TLC_MNG_MODE_EHT;
iwl_mld_fill_eht_rates(vif, link_sta, own_he_cap,
own_eht_cap, cmd);
@@ -519,13 +536,16 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
ieee80211_get_he_iftype_cap_vif(sband, vif);
const struct ieee80211_sta_eht_cap *own_eht_cap =
ieee80211_get_eht_iftype_cap_vif(sband, vif);
+ const struct ieee80211_sta_uhr_cap *own_uhr_cap =
+ ieee80211_get_uhr_iftype_cap_vif(sband, vif);
struct iwl_tlc_config_cmd cmd = {
/* For AP mode, use 20 MHz until the STA is authorized */
.max_ch_width = mld_sta->sta_state > IEEE80211_STA_ASSOC ?
iwl_mld_fw_bw_from_sta_bw(link_sta) :
IWL_TLC_MNG_CH_WIDTH_20MHZ,
.flags = iwl_mld_get_tlc_cmd_flags(mld, vif, link_sta,
- own_he_cap, own_eht_cap),
+ own_he_cap, own_eht_cap,
+ own_uhr_cap),
.chains = iwl_mld_get_fw_chains(mld),
.sgi_ch_width_supp = iwl_mld_get_fw_sgi(link_sta),
.max_mpdu_len = cpu_to_le16(link_sta->agg.max_amsdu_len),
@@ -555,7 +575,7 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
iwl_mld_fill_supp_rates(mld, vif, link_sta, sband,
own_he_cap, own_eht_cap,
- &cmd);
+ own_uhr_cap, &cmd);
if (cmd_ver == 6) {
cmd_ptr = &cmd;
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH iwlwifi-next 02/15] wifi: iwlwifi: mld: set UHR MCS in RX status
2026-03-20 8:09 [PATCH iwlwifi-next 00/15] wifi: iwlwifi: updates - 2026-03-20 Miri Korenblit
2026-03-20 8:09 ` [PATCH iwlwifi-next 01/15] wifi: iwlwifi: mld: enable UHR in TLC Miri Korenblit
@ 2026-03-20 8:09 ` Miri Korenblit
2026-03-20 8:09 ` [PATCH iwlwifi-next 03/15] wifi: iwlwifi: advertise UHR capabilities for such devices Miri Korenblit
` (12 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Miri Korenblit @ 2026-03-20 8:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
Handle UHR MCSes in the RX status when receiving UHR frames.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/mld/rx.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
index 214dcfde2fb4..ff6e71e3ff6e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
@@ -1407,6 +1407,7 @@ static void iwl_mld_set_rx_rate(struct iwl_mld *mld,
u32 rate_n_flags = phy_data->rate_n_flags;
u8 stbc = u32_get_bits(rate_n_flags, RATE_MCS_STBC_MSK);
u32 format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
+ u32 he_type = u32_get_bits(rate_n_flags, RATE_MCS_HE_TYPE_MSK);
bool is_sgi = rate_n_flags & RATE_MCS_SGI_MSK;
/* bandwidth may be overridden to RU by PHY ntfy */
@@ -1481,6 +1482,12 @@ static void iwl_mld_set_rx_rate(struct iwl_mld *mld,
rx_status->encoding = RX_ENC_EHT;
iwl_mld_set_rx_nonlegacy_rate_info(rate_n_flags, rx_status);
break;
+ case RATE_MCS_MOD_TYPE_UHR:
+ rx_status->encoding = RX_ENC_UHR;
+ iwl_mld_set_rx_nonlegacy_rate_info(rate_n_flags, rx_status);
+ if (he_type == RATE_MCS_HE_TYPE_UHR_ELR)
+ rx_status->uhr.elr = 1;
+ break;
default:
WARN_ON_ONCE(1);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH iwlwifi-next 03/15] wifi: iwlwifi: advertise UHR capabilities for such devices
2026-03-20 8:09 [PATCH iwlwifi-next 00/15] wifi: iwlwifi: updates - 2026-03-20 Miri Korenblit
2026-03-20 8:09 ` [PATCH iwlwifi-next 01/15] wifi: iwlwifi: mld: enable UHR in TLC Miri Korenblit
2026-03-20 8:09 ` [PATCH iwlwifi-next 02/15] wifi: iwlwifi: mld: set UHR MCS in RX status Miri Korenblit
@ 2026-03-20 8:09 ` Miri Korenblit
2026-03-20 8:09 ` [PATCH iwlwifi-next 04/15] wifi: iwlwifi: mld: support changing iftype at runtime Miri Korenblit
` (11 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Miri Korenblit @ 2026-03-20 8:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
Advertise UHR capabilities for devices supporting UHR
(i.e. devices with some, but not all, PE RFs.)
For correct support it's also needed to plumb through
the regulatory flags the firmware might have/enforce.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../net/wireless/intel/iwlwifi/cfg/rf-pe.c | 23 ++++-
.../net/wireless/intel/iwlwifi/iwl-config.h | 7 +-
.../wireless/intel/iwlwifi/iwl-nvm-parse.c | 87 +++++++++++++------
.../wireless/intel/iwlwifi/iwl-nvm-parse.h | 2 +
.../wireless/intel/iwlwifi/iwl-nvm-utils.h | 3 +-
.../net/wireless/intel/iwlwifi/mld/iface.c | 4 +
drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 2 +-
7 files changed, 95 insertions(+), 33 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c
index 2c29054ce7b8..c02478b73057 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c
@@ -4,7 +4,28 @@
*/
#include "iwl-config.h"
-/* currently iwl_rf_wh/iwl_rf_wh_160mhz are just defines for the FM ones */
+#define IWL_PE_NVM_VERSION 0x0a1d
+
+#define IWL_DEVICE_PE \
+ .ht_params = { \
+ .stbc = true, \
+ .ldpc = true, \
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | \
+ BIT(NL80211_BAND_5GHZ), \
+ }, \
+ .led_mode = IWL_LED_RF_STATE, \
+ .non_shared_ant = ANT_B, \
+ .vht_mu_mimo_supported = true, \
+ .uhb_supported = true, \
+ .eht_supported = true, \
+ .uhr_supported = true, \
+ .num_rbds = IWL_NUM_RBDS_EHT, \
+ .nvm_ver = IWL_PE_NVM_VERSION, \
+ .nvm_type = IWL_NVM_EXT
+
+const struct iwl_rf_cfg iwl_rf_pe = {
+ IWL_DEVICE_PE,
+};
const char iwl_killer_bn1850w2_name[] =
"Killer(R) Wi-Fi 8 BN1850w2 320MHz Wireless Network Adapter (BN201.D2W)";
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
index 5f40cd15e27f..da6d3686e7dd 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
@@ -416,6 +416,7 @@ struct iwl_mac_cfg {
* @nvm_type: see &enum iwl_nvm_type
* @uhb_supported: ultra high band channels supported
* @eht_supported: EHT supported
+ * @uhr_supported: UHR supported
* @num_rbds: number of receive buffer descriptors to use
* (only used for multi-queue capable devices)
*
@@ -449,7 +450,8 @@ struct iwl_rf_cfg {
lp_xtal_workaround:1,
vht_mu_mimo_supported:1,
uhb_supported:1,
- eht_supported:1;
+ eht_supported:1,
+ uhr_supported:1;
u8 valid_tx_ant;
u8 valid_rx_ant;
u8 non_shared_ant;
@@ -744,7 +746,8 @@ extern const struct iwl_rf_cfg iwl_rf_fm_160mhz;
#define iwl_rf_wh iwl_rf_fm
#define iwl_rf_wh_160mhz iwl_rf_fm_160mhz
extern const struct iwl_rf_cfg iwl_rf_wh_non_eht;
-#define iwl_rf_pe iwl_rf_fm
+extern const struct iwl_rf_cfg iwl_rf_pe;
+#define iwl_rf_pe_no_uhr iwl_rf_fm
#endif /* CONFIG_IWLMLD */
#endif /* __IWL_CONFIG_H__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
index 6d235c417fdd..b24ac3cec412 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
@@ -204,28 +204,30 @@ enum iwl_reg_capa_flags_v2 {
}; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_2 */
/**
- * enum iwl_reg_capa_flags_v4 - global flags applied for the whole regulatory
+ * enum iwl_reg_capa_flags_v5 - global flags applied for the whole regulatory
* domain.
- * @REG_CAPA_V4_160MHZ_ALLOWED: 11ac channel with a width of 160Mhz is allowed
+ * @REG_CAPA_V5_160MHZ_ALLOWED: 11ac channel with a width of 160Mhz is allowed
* for this regulatory domain (valid only in 5Ghz).
- * @REG_CAPA_V4_80MHZ_ALLOWED: 11ac channel with a width of 80Mhz is allowed
+ * @REG_CAPA_V5_80MHZ_ALLOWED: 11ac channel with a width of 80Mhz is allowed
* for this regulatory domain (valid only in 5Ghz).
- * @REG_CAPA_V4_MCS_12_ALLOWED: 11ac with MCS 12 is allowed.
- * @REG_CAPA_V4_MCS_13_ALLOWED: 11ac with MCS 13 is allowed.
- * @REG_CAPA_V4_11BE_DISABLED: 11be is forbidden for this regulatory domain.
- * @REG_CAPA_V4_11AX_DISABLED: 11ax is forbidden for this regulatory domain.
- * @REG_CAPA_V4_320MHZ_ALLOWED: 11be channel with a width of 320Mhz is allowed
+ * @REG_CAPA_V5_MCS_12_ALLOWED: 11ac with MCS 12 is allowed.
+ * @REG_CAPA_V5_MCS_13_ALLOWED: 11ac with MCS 13 is allowed.
+ * @REG_CAPA_V5_11BE_DISABLED: 11be is forbidden for this regulatory domain.
+ * @REG_CAPA_V5_11AX_DISABLED: 11ax is forbidden for this regulatory domain.
+ * @REG_CAPA_V5_320MHZ_ALLOWED: 11be channel with a width of 320Mhz is allowed
* for this regulatory domain (valid only in 5GHz).
+ * @REG_CAPA_V5_11BN_DISABLED: UHR is not allowed for this regulatory domain
*/
-enum iwl_reg_capa_flags_v4 {
- REG_CAPA_V4_160MHZ_ALLOWED = BIT(3),
- REG_CAPA_V4_80MHZ_ALLOWED = BIT(4),
- REG_CAPA_V4_MCS_12_ALLOWED = BIT(5),
- REG_CAPA_V4_MCS_13_ALLOWED = BIT(6),
- REG_CAPA_V4_11BE_DISABLED = BIT(8),
- REG_CAPA_V4_11AX_DISABLED = BIT(13),
- REG_CAPA_V4_320MHZ_ALLOWED = BIT(16),
-}; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_4 */
+enum iwl_reg_capa_flags_v5 {
+ REG_CAPA_V5_160MHZ_ALLOWED = BIT(3),
+ REG_CAPA_V5_80MHZ_ALLOWED = BIT(4),
+ REG_CAPA_V5_MCS_12_ALLOWED = BIT(5),
+ REG_CAPA_V5_MCS_13_ALLOWED = BIT(6),
+ REG_CAPA_V5_11BE_DISABLED = BIT(8),
+ REG_CAPA_V5_11AX_DISABLED = BIT(13),
+ REG_CAPA_V5_320MHZ_ALLOWED = BIT(16),
+ REG_CAPA_V5_11BN_DISABLED = BIT(17),
+}; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_4, 5 */
/*
* API v2 for reg_capa_flags is relevant from version 6 and onwards of the
@@ -544,7 +546,7 @@ static const u8 iwl_vendor_caps[] = {
0x00
};
-static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = {
+static const struct ieee80211_sband_iftype_data iwl_iftype_cap[] = {
{
.types_mask = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_P2P_CLIENT),
@@ -688,6 +690,11 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = {
*/
.eht_ppe_thres = {0xc1, 0x0e, 0xe0 }
},
+ .uhr_cap = {
+ .has_uhr = true,
+ .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
+ IEEE80211_UHR_PHY_CAP_ELR_TX,
+ },
},
{
.types_mask = BIT(NL80211_IFTYPE_AP) |
@@ -786,6 +793,11 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = {
*/
.eht_ppe_thres = {0xc1, 0x0e, 0xe0 }
},
+ .uhr_cap = {
+ .has_uhr = true,
+ .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
+ IEEE80211_UHR_PHY_CAP_ELR_TX,
+ },
},
};
@@ -853,6 +865,19 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
fips_enabled)
iftype_data->eht_cap.has_eht = false;
+ if (!data->sku_cap_11bn_enable || !iftype_data->eht_cap.has_eht)
+ iftype_data->uhr_cap.has_uhr = false;
+
+ if (sband->band != NL80211_BAND_2GHZ) {
+ /* on 5 and 6 GHz ELR is uplink only */
+ if (is_ap)
+ iftype_data->uhr_cap.phy.cap &=
+ ~IEEE80211_UHR_PHY_CAP_ELR_TX;
+ else
+ iftype_data->uhr_cap.phy.cap &=
+ ~IEEE80211_UHR_PHY_CAP_ELR_RX;
+ }
+
/* Advertise an A-MPDU exponent extension based on
* operating band
*/
@@ -1021,9 +1046,9 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans,
struct ieee80211_sband_iftype_data *iftype_data;
int i;
- BUILD_BUG_ON(sizeof(data->iftd.low) != sizeof(iwl_he_eht_capa));
- BUILD_BUG_ON(sizeof(data->iftd.high) != sizeof(iwl_he_eht_capa));
- BUILD_BUG_ON(sizeof(data->iftd.uhb) != sizeof(iwl_he_eht_capa));
+ BUILD_BUG_ON(sizeof(data->iftd.low) != sizeof(iwl_iftype_cap));
+ BUILD_BUG_ON(sizeof(data->iftd.high) != sizeof(iwl_iftype_cap));
+ BUILD_BUG_ON(sizeof(data->iftd.uhb) != sizeof(iwl_iftype_cap));
switch (sband->band) {
case NL80211_BAND_2GHZ:
@@ -1040,10 +1065,10 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans,
return;
}
- memcpy(iftype_data, iwl_he_eht_capa, sizeof(iwl_he_eht_capa));
+ memcpy(iftype_data, iwl_iftype_cap, sizeof(iwl_iftype_cap));
_ieee80211_set_sband_iftype_data(sband, iftype_data,
- ARRAY_SIZE(iwl_he_eht_capa));
+ ARRAY_SIZE(iwl_iftype_cap));
for (i = 0; i < sband->n_iftype_data; i++)
iwl_nvm_fixup_sband_iftd(trans, data, sband, &iftype_data[i],
@@ -1609,6 +1634,9 @@ u32 iwl_nvm_get_regdom_bw_flags(const u16 *nvm_chan,
if (reg_capa.disable_11be)
flags |= NL80211_RRF_NO_EHT;
+ if (reg_capa.disable_11bn)
+ flags |= NL80211_RRF_NO_UHR;
+
return flags;
}
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_nvm_get_regdom_bw_flags);
@@ -1619,11 +1647,13 @@ static struct iwl_reg_capa iwl_get_reg_capa(u32 flags, u8 resp_ver)
if (resp_ver >= REG_CAPA_V4_RESP_VER) {
reg_capa.allow_40mhz = true;
- reg_capa.allow_80mhz = flags & REG_CAPA_V4_80MHZ_ALLOWED;
- reg_capa.allow_160mhz = flags & REG_CAPA_V4_160MHZ_ALLOWED;
- reg_capa.allow_320mhz = flags & REG_CAPA_V4_320MHZ_ALLOWED;
- reg_capa.disable_11ax = flags & REG_CAPA_V4_11AX_DISABLED;
- reg_capa.disable_11be = flags & REG_CAPA_V4_11BE_DISABLED;
+ reg_capa.allow_80mhz = flags & REG_CAPA_V5_80MHZ_ALLOWED;
+ reg_capa.allow_160mhz = flags & REG_CAPA_V5_160MHZ_ALLOWED;
+ reg_capa.allow_320mhz = flags & REG_CAPA_V5_320MHZ_ALLOWED;
+ reg_capa.disable_11ax = flags & REG_CAPA_V5_11AX_DISABLED;
+ reg_capa.disable_11be = flags & REG_CAPA_V5_11BE_DISABLED;
+ /* can check: was reserved and irrelevant for pre-UHR devices */
+ reg_capa.disable_11bn = flags & REG_CAPA_V5_11BN_DISABLED;
} else if (resp_ver >= REG_CAPA_V2_RESP_VER) {
reg_capa.allow_40mhz = flags & REG_CAPA_V2_40MHZ_ALLOWED;
reg_capa.allow_80mhz = flags & REG_CAPA_V2_80MHZ_ALLOWED;
@@ -2072,6 +2102,7 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
!!(mac_flags & NVM_MAC_SKU_FLAGS_MIMO_DISABLED);
if (trans->cfg->eht_supported)
nvm->sku_cap_11be_enable = true;
+ nvm->sku_cap_11bn_enable = trans->cfg->uhr_supported;
/* Initialize PHY sku data */
nvm->valid_tx_ant = (u8)le32_to_cpu(rsp->phy_sku.tx_chains);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h
index 12f28bb0e859..e676d7c2d6cc 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h
@@ -35,6 +35,7 @@ enum iwl_nvm_sbands_flags {
* for this regulatory domain (valid only in 6 Ghz).
* @disable_11ax: 11ax is forbidden for this regulatory domain.
* @disable_11be: 11be is forbidden for this regulatory domain.
+ * @disable_11bn: UHR/11bn is not allowed for this regulatory domain
*/
struct iwl_reg_capa {
bool allow_40mhz;
@@ -43,6 +44,7 @@ struct iwl_reg_capa {
bool allow_320mhz;
bool disable_11ax;
bool disable_11be;
+ bool disable_11bn;
};
/**
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-utils.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-utils.h
index ac0a29a1c31f..353f5269d17a 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-utils.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-utils.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2005-2014, 2018, 2020-2023 Intel Corporation
+ * Copyright (C) 2005-2014, 2018, 2020-2023, 2025 Intel Corporation
* Copyright (C) 2015 Intel Mobile Communications GmbH
*/
#ifndef __iwl_eeprom_parse_h__
@@ -32,6 +32,7 @@ struct iwl_nvm_data {
bool sku_cap_ipan_enable;
bool sku_cap_mimo_disabled;
bool sku_cap_11be_enable;
+ bool sku_cap_11bn_enable;
u16 radio_cfg_type;
u8 radio_cfg_step;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
index 9215fc7e2eca..dc96214671d9 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
@@ -132,6 +132,8 @@ static void iwl_mld_mac_wifi_gen_sta_iter(void *_data,
data->support->he_support = 1;
if (link_sta->eht_cap.has_eht)
data->support->eht_support = 1;
+ if (link_sta->uhr_cap.has_uhr)
+ data->support->uhr_support = 1;
}
}
@@ -151,12 +153,14 @@ static void iwl_mld_set_wifi_gen(struct iwl_mld *mld,
/* for sniffer, set to HW capabilities */
support->he_support = 1;
support->eht_support = mld->trans->cfg->eht_supported;
+ support->uhr_support = mld->trans->cfg->uhr_supported;
break;
case NL80211_IFTYPE_AP:
/* for AP set according to the link configs */
for_each_vif_active_link(vif, link_conf, link_id) {
support->he_ap_support |= link_conf->he_support;
support->eht_support |= link_conf->eht_support;
+ support->uhr_support |= link_conf->uhr_support;
}
break;
default:
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
index dc99e7ac4726..78482db732c1 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
@@ -1069,7 +1069,7 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = {
/* PE RF */
IWL_DEV_INFO(iwl_rf_pe, iwl_bn201_name, RF_TYPE(PE)),
- IWL_DEV_INFO(iwl_rf_pe, iwl_be223_name, RF_TYPE(PE),
+ IWL_DEV_INFO(iwl_rf_pe_no_uhr, iwl_be223_name, RF_TYPE(PE),
SUBDEV_MASKED(0x0524, 0xFFF)),
IWL_DEV_INFO(iwl_rf_pe, iwl_bn203_name, RF_TYPE(PE),
SUBDEV_MASKED(0x0324, 0xFFF)),
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH iwlwifi-next 04/15] wifi: iwlwifi: mld: support changing iftype at runtime
2026-03-20 8:09 [PATCH iwlwifi-next 00/15] wifi: iwlwifi: updates - 2026-03-20 Miri Korenblit
` (2 preceding siblings ...)
2026-03-20 8:09 ` [PATCH iwlwifi-next 03/15] wifi: iwlwifi: advertise UHR capabilities for such devices Miri Korenblit
@ 2026-03-20 8:09 ` Miri Korenblit
2026-03-20 8:09 ` [PATCH iwlwifi-next 05/15] wifi: iwlwifi: add support for additional channels in NVM_GET_INFO Miri Korenblit
` (10 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Miri Korenblit @ 2026-03-20 8:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
While the interface isn't really operating, which is already
required by mac80211, we can simply remove the MAC and add
it again to change the type. Implement this simple handling.
We could almost consider moving this to mac80211 itself, as
this kind of flow should be supportable by any device, but
for now keep it here.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../net/wireless/intel/iwlwifi/mld/mac80211.c | 25 +++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
index 71a9a72c9ac0..5cd3cdffb570 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
@@ -754,6 +754,30 @@ void iwl_mld_mac80211_remove_interface(struct ieee80211_hw *hw,
mld->monitor.phy.valid = false;
}
+static
+int iwl_mld_mac80211_change_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum nl80211_iftype new_type, bool p2p)
+{
+ enum nl80211_iftype old_type = vif->type;
+ bool old_p2p = vif->p2p;
+ int ret;
+
+ iwl_mld_mac80211_remove_interface(hw, vif);
+
+ /* set the new type for adding it cleanly */
+ vif->type = new_type;
+ vif->p2p = p2p;
+
+ ret = iwl_mld_mac80211_add_interface(hw, vif);
+
+ /* restore for mac80211, it will change it again */
+ vif->type = old_type;
+ vif->p2p = old_p2p;
+
+ return ret;
+}
+
struct iwl_mld_mc_iter_data {
struct iwl_mld *mld;
int port_id;
@@ -2735,6 +2759,7 @@ const struct ieee80211_ops iwl_mld_hw_ops = {
.get_antenna = iwl_mld_get_antenna,
.set_antenna = iwl_mld_set_antenna,
.add_interface = iwl_mld_mac80211_add_interface,
+ .change_interface = iwl_mld_mac80211_change_interface,
.remove_interface = iwl_mld_mac80211_remove_interface,
.conf_tx = iwl_mld_mac80211_conf_tx,
.prepare_multicast = iwl_mld_mac80211_prepare_multicast,
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH iwlwifi-next 05/15] wifi: iwlwifi: add support for additional channels in NVM_GET_INFO
2026-03-20 8:09 [PATCH iwlwifi-next 00/15] wifi: iwlwifi: updates - 2026-03-20 Miri Korenblit
` (3 preceding siblings ...)
2026-03-20 8:09 ` [PATCH iwlwifi-next 04/15] wifi: iwlwifi: mld: support changing iftype at runtime Miri Korenblit
@ 2026-03-20 8:09 ` Miri Korenblit
2026-03-20 8:09 ` [PATCH iwlwifi-next 06/15] wifi: iwlwifi: bump core version for BZ/SC/DR Miri Korenblit
` (9 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Miri Korenblit @ 2026-03-20 8:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Emmanuel Grumbach, Johannes Berg
From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
We need to expect more channels on devices that support UNII-9.
Since iwl_ext_nvm_channels and iwl_uhb_nvm_channels are just a prefix
of iwl_unii9_nvm_channels just use iwl_unii9_nvm_channels and modify the
number of channels if the device does not support UNII-9 channels.
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../net/wireless/intel/iwlwifi/cfg/rf-pe.c | 1 +
.../wireless/intel/iwlwifi/fw/api/nvm-reg.h | 37 +++++++++--
.../net/wireless/intel/iwlwifi/iwl-config.h | 2 +
.../wireless/intel/iwlwifi/iwl-nvm-parse.c | 64 ++++++++++++-------
4 files changed, 76 insertions(+), 28 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c
index c02478b73057..06c615f4d3e8 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c
@@ -17,6 +17,7 @@
.non_shared_ant = ANT_B, \
.vht_mu_mimo_supported = true, \
.uhb_supported = true, \
+ .unii9_supported = true, \
.eht_supported = true, \
.uhr_supported = true, \
.num_rbds = IWL_NUM_RBDS_EHT, \
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
index 25c860a05b0e..0172c0747a47 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
@@ -204,7 +204,8 @@ struct iwl_nvm_get_info_phy {
} __packed; /* REGULATORY_NVM_GET_INFO_PHY_SKU_SECTION_S_VER_1 */
#define IWL_NUM_CHANNELS_V1 51
-#define IWL_NUM_CHANNELS 110
+#define IWL_NUM_CHANNELS_V2 110
+#define IWL_NUM_CHANNELS_V3 115
/**
* struct iwl_nvm_get_info_regulatory_v1 - regulatory information
@@ -219,15 +220,15 @@ struct iwl_nvm_get_info_regulatory_v1 {
} __packed; /* REGULATORY_NVM_GET_INFO_REGULATORY_S_VER_1 */
/**
- * struct iwl_nvm_get_info_regulatory - regulatory information
+ * struct iwl_nvm_get_info_regulatory_v2 - regulatory information
* @lar_enabled: is LAR enabled
* @n_channels: number of valid channels in the array
* @channel_profile: regulatory data of this channel
*/
-struct iwl_nvm_get_info_regulatory {
+struct iwl_nvm_get_info_regulatory_v2 {
__le32 lar_enabled;
__le32 n_channels;
- __le32 channel_profile[IWL_NUM_CHANNELS];
+ __le32 channel_profile[IWL_NUM_CHANNELS_V2];
} __packed; /* REGULATORY_NVM_GET_INFO_REGULATORY_S_VER_2 */
/**
@@ -244,6 +245,32 @@ struct iwl_nvm_get_info_rsp_v3 {
struct iwl_nvm_get_info_regulatory_v1 regulatory;
} __packed; /* REGULATORY_NVM_GET_INFO_RSP_API_S_VER_3 */
+/**
+ * struct iwl_nvm_get_info_rsp_v4 - response to get NVM data
+ * @general: general NVM data
+ * @mac_sku: data relating to MAC sku
+ * @phy_sku: data relating to PHY sku
+ * @regulatory: regulatory data
+ */
+struct iwl_nvm_get_info_rsp_v4 {
+ struct iwl_nvm_get_info_general general;
+ struct iwl_nvm_get_info_sku mac_sku;
+ struct iwl_nvm_get_info_phy phy_sku;
+ struct iwl_nvm_get_info_regulatory_v2 regulatory;
+} __packed; /* REGULATORY_NVM_GET_INFO_RSP_API_S_VER_4 */
+
+/**
+ * struct iwl_nvm_get_info_regulatory - regulatory information
+ * @lar_enabled: is LAR enabled
+ * @n_channels: number of valid channels in the array
+ * @channel_profile: regulatory data of this channel
+ */
+struct iwl_nvm_get_info_regulatory {
+ __le32 lar_enabled;
+ __le32 n_channels;
+ __le32 channel_profile[IWL_NUM_CHANNELS_V3];
+} __packed; /* REGULATORY_NVM_GET_INFO_REGULATORY_S_VER_3 */
+
/**
* struct iwl_nvm_get_info_rsp - response to get NVM data
* @general: general NVM data
@@ -256,7 +283,7 @@ struct iwl_nvm_get_info_rsp {
struct iwl_nvm_get_info_sku mac_sku;
struct iwl_nvm_get_info_phy phy_sku;
struct iwl_nvm_get_info_regulatory regulatory;
-} __packed; /* REGULATORY_NVM_GET_INFO_RSP_API_S_VER_4 */
+} __packed; /* REGULATORY_NVM_GET_INFO_RSP_API_S_VER_5 */
/**
* struct iwl_nvm_access_complete_cmd - NVM_ACCESS commands are completed
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
index da6d3686e7dd..8d7ddb6e8b53 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
@@ -415,6 +415,7 @@ struct iwl_mac_cfg {
* @vht_mu_mimo_supported: VHT MU-MIMO support
* @nvm_type: see &enum iwl_nvm_type
* @uhb_supported: ultra high band channels supported
+ * @unii9_supported: UNII-9 channels supported
* @eht_supported: EHT supported
* @uhr_supported: UHR supported
* @num_rbds: number of receive buffer descriptors to use
@@ -450,6 +451,7 @@ struct iwl_rf_cfg {
lp_xtal_workaround:1,
vht_mu_mimo_supported:1,
uhb_supported:1,
+ unii9_supported:1,
eht_supported:1,
uhr_supported:1;
u8 valid_tx_ant;
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
index b24ac3cec412..e8f7d258b622 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
@@ -84,16 +84,7 @@ static const u16 iwl_nvm_channels[] = {
149, 153, 157, 161, 165
};
-static const u16 iwl_ext_nvm_channels[] = {
- /* 2.4 GHz */
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- /* 5 GHz */
- 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92,
- 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
- 149, 153, 157, 161, 165, 169, 173, 177, 181
-};
-
-static const u16 iwl_uhb_nvm_channels[] = {
+static const u16 iwl_unii9_nvm_channels[] = {
/* 2.4 GHz */
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
/* 5 GHz */
@@ -104,12 +95,16 @@ static const u16 iwl_uhb_nvm_channels[] = {
1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69,
73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129,
133, 137, 141, 145, 149, 153, 157, 161, 165, 169, 173, 177, 181, 185,
- 189, 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 233
+ 189, 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 233,
+
+ /* UNII-9 */
+ 237, 241, 245, 249, 253
};
#define IWL_NVM_NUM_CHANNELS ARRAY_SIZE(iwl_nvm_channels)
-#define IWL_NVM_NUM_CHANNELS_EXT ARRAY_SIZE(iwl_ext_nvm_channels)
-#define IWL_NVM_NUM_CHANNELS_UHB ARRAY_SIZE(iwl_uhb_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
@@ -350,12 +345,15 @@ static int iwl_init_channel_map(struct iwl_trans *trans,
int num_of_ch;
const u16 *nvm_chan;
- if (cfg->uhb_supported) {
+ if (cfg->unii9_supported) {
+ num_of_ch = IWL_NVM_NUM_CHANNELS_UNII9;
+ nvm_chan = iwl_unii9_nvm_channels;
+ } else if (cfg->uhb_supported) {
num_of_ch = IWL_NVM_NUM_CHANNELS_UHB;
- nvm_chan = iwl_uhb_nvm_channels;
+ nvm_chan = iwl_unii9_nvm_channels;
} else if (cfg->nvm_type == IWL_NVM_EXT) {
num_of_ch = IWL_NVM_NUM_CHANNELS_EXT;
- nvm_chan = iwl_ext_nvm_channels;
+ nvm_chan = iwl_unii9_nvm_channels;
} else {
num_of_ch = IWL_NVM_NUM_CHANNELS;
nvm_chan = iwl_nvm_channels;
@@ -1401,7 +1399,9 @@ iwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
u8 rx_chains = fw->valid_rx_ant;
u8 tx_chains = fw->valid_rx_ant;
- if (cfg->uhb_supported)
+ if (cfg->unii9_supported)
+ data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_UNII9);
+ else if (cfg->uhb_supported)
data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_UHB);
else
data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_EXT);
@@ -1466,7 +1466,9 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
u16 lar_config;
const __le16 *ch_section;
- if (cfg->uhb_supported)
+ if (cfg->unii9_supported)
+ data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_UNII9);
+ else if (cfg->uhb_supported)
data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_UHB);
else if (cfg->nvm_type != IWL_NVM_EXT)
data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS);
@@ -1687,12 +1689,15 @@ iwl_parse_nvm_mcc_info(struct iwl_trans *trans,
int max_num_ch;
struct iwl_reg_capa reg_capa;
- if (cfg->uhb_supported) {
+ if (cfg->unii9_supported) {
+ max_num_ch = IWL_NVM_NUM_CHANNELS_UNII9;
+ nvm_chan = iwl_unii9_nvm_channels;
+ } else if (cfg->uhb_supported) {
max_num_ch = IWL_NVM_NUM_CHANNELS_UHB;
- nvm_chan = iwl_uhb_nvm_channels;
+ nvm_chan = iwl_unii9_nvm_channels;
} else if (cfg->nvm_type == IWL_NVM_EXT) {
max_num_ch = IWL_NVM_NUM_CHANNELS_EXT;
- nvm_chan = iwl_ext_nvm_channels;
+ nvm_chan = iwl_unii9_nvm_channels;
} else {
max_num_ch = IWL_NVM_NUM_CHANNELS;
nvm_chan = iwl_nvm_channels;
@@ -2041,13 +2046,26 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
struct iwl_nvm_get_info_rsp_v3 *rsp_v3;
bool v4 = fw_has_api(&fw->ucode_capa,
IWL_UCODE_TLV_API_REGULATORY_NVM_INFO);
- size_t rsp_size = v4 ? sizeof(*rsp) : sizeof(*rsp_v3);
+ size_t rsp_size;
void *channel_profile;
ret = iwl_trans_send_cmd(trans, &hcmd);
if (ret)
return ERR_PTR(ret);
+ switch (iwl_fw_lookup_notif_ver(fw, REGULATORY_AND_NVM_GROUP,
+ NVM_GET_INFO, 0)) {
+ case 5:
+ rsp_size = sizeof(struct iwl_nvm_get_info_rsp);
+ break;
+ case 4:
+ rsp_size = sizeof(struct iwl_nvm_get_info_rsp_v4);
+ break;
+ default:
+ rsp_size = sizeof(struct iwl_nvm_get_info_rsp_v3);
+ break;
+ }
+
if (WARN(iwl_rx_packet_payload_len(hcmd.resp_pkt) != rsp_size,
"Invalid payload len in NVM response from FW %d",
iwl_rx_packet_payload_len(hcmd.resp_pkt))) {
@@ -2061,7 +2079,7 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
if (empty_otp)
IWL_INFO(trans, "OTP is empty\n");
- nvm = kzalloc_flex(*nvm, channels, IWL_NUM_CHANNELS);
+ nvm = kzalloc_flex(*nvm, channels, IWL_NUM_CHANNELS_V3);
if (!nvm) {
ret = -ENOMEM;
goto out;
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH iwlwifi-next 06/15] wifi: iwlwifi: bump core version for BZ/SC/DR
2026-03-20 8:09 [PATCH iwlwifi-next 00/15] wifi: iwlwifi: updates - 2026-03-20 Miri Korenblit
` (4 preceding siblings ...)
2026-03-20 8:09 ` [PATCH iwlwifi-next 05/15] wifi: iwlwifi: add support for additional channels in NVM_GET_INFO Miri Korenblit
@ 2026-03-20 8:09 ` Miri Korenblit
2026-03-20 8:09 ` [PATCH iwlwifi-next 07/15] wifi: iwlwifi: fw: Add TLV support for BIOS revision of command Miri Korenblit
` (8 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Miri Korenblit @ 2026-03-20 8:09 UTC (permalink / raw)
To: linux-wireless
Start supporting Core 102 FW on these devices.
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 2 +-
drivers/net/wireless/intel/iwlwifi/cfg/dr.c | 2 +-
drivers/net/wireless/intel/iwlwifi/cfg/sc.c | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c
index 77db8c75e6e2..3653ddbf3ce9 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c
@@ -10,7 +10,7 @@
#include "fw/api/txq.h"
/* Highest firmware core release supported */
-#define IWL_BZ_UCODE_CORE_MAX 101
+#define IWL_BZ_UCODE_CORE_MAX 102
/* Lowest firmware API version supported */
#define IWL_BZ_UCODE_API_MIN 100
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c
index a279dcfd3083..83d893b10f8e 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c
@@ -9,7 +9,7 @@
#include "fw/api/txq.h"
/* Highest firmware core release supported */
-#define IWL_DR_UCODE_CORE_MAX 101
+#define IWL_DR_UCODE_CORE_MAX 102
/* Lowest firmware API version supported */
#define IWL_DR_UCODE_API_MIN 100
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c
index ee00b2af7a1d..749d46dc0236 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c
@@ -10,7 +10,7 @@
#include "fw/api/txq.h"
/* Highest firmware core release supported */
-#define IWL_SC_UCODE_CORE_MAX 101
+#define IWL_SC_UCODE_CORE_MAX 102
/* Lowest firmware API version supported */
#define IWL_SC_UCODE_API_MIN 100
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH iwlwifi-next 07/15] wifi: iwlwifi: fw: Add TLV support for BIOS revision of command
2026-03-20 8:09 [PATCH iwlwifi-next 00/15] wifi: iwlwifi: updates - 2026-03-20 Miri Korenblit
` (5 preceding siblings ...)
2026-03-20 8:09 ` [PATCH iwlwifi-next 06/15] wifi: iwlwifi: bump core version for BZ/SC/DR Miri Korenblit
@ 2026-03-20 8:09 ` Miri Korenblit
2026-03-20 8:09 ` [PATCH iwlwifi-next 08/15] wifi: iwlwifi: mld: eliminate duplicate WIDE_ID in PPAG command handling Miri Korenblit
` (7 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Miri Korenblit @ 2026-03-20 8:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Pagadala Yesu Anjaneyulu, Johannes Berg
From: Pagadala Yesu Anjaneyulu <pagadala.yesu.anjaneyulu@intel.com>
Add support for newer firmware API versions that support
multiple BIOS revisions. Use the new TLV provided by
firmware to determine which BIOS revision it supports.
Future patches will use this information to either
drop commands when the BIOS revision is higher than
supported or convert commands based on the command
specific implementation.
Signed-off-by: Pagadala Yesu Anjaneyulu <pagadala.yesu.anjaneyulu@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/fw/file.h | 15 +++++++++
drivers/net/wireless/intel/iwlwifi/fw/img.c | 32 +++++++++++++++++++-
drivers/net/wireless/intel/iwlwifi/fw/img.h | 8 +++++
drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 21 +++++++++++++
4 files changed, 75 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h
index 378788de1d74..f7a6f21267e9 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/file.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h
@@ -103,6 +103,7 @@ enum iwl_ucode_tlv_type {
IWL_UCODE_TLV_D3_KEK_KCK_ADDR = 67,
IWL_UCODE_TLV_CURRENT_PC = 68,
IWL_UCODE_TLV_FSEQ_BIN_VERSION = 72,
+ IWL_UCODE_TLV_CMD_BIOS_TABLE = 73,
/* contains sub-sections like PNVM file does (did) */
IWL_UCODE_TLV_PNVM_DATA = 74,
@@ -1040,6 +1041,20 @@ struct iwl_fw_cmd_version {
u8 notif_ver;
} __packed;
+/**
+ * struct iwl_fw_cmd_bios_table - firmware command BIOS revision entry
+ * @cmd: command ID
+ * @group: group ID
+ * @max_acpi_revision: max supported ACPI revision of command.
+ * @max_uefi_revision: max supported UEFI revision of command.
+ */
+struct iwl_fw_cmd_bios_table {
+ u8 cmd;
+ u8 group;
+ u8 max_acpi_revision;
+ u8 max_uefi_revision;
+} __packed;
+
struct iwl_fw_tcm_error_addr {
__le32 addr;
}; /* FW_TLV_TCM_ERROR_INFO_ADDRS_S */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.c b/drivers/net/wireless/intel/iwlwifi/fw/img.c
index c2f4fc83a22c..3cc1e3ae0858 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/img.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/img.c
@@ -1,11 +1,41 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright(c) 2019 - 2021 Intel Corporation
- * Copyright(c) 2024 Intel Corporation
+ * Copyright(c) 2024 - 2025 Intel Corporation
*/
#include <fw/api/commands.h>
#include "img.h"
+u8 iwl_fw_lookup_cmd_bios_supported_revision(const struct iwl_fw *fw,
+ enum bios_source table_source,
+ u32 cmd_id, u8 def)
+{
+ const struct iwl_fw_cmd_bios_table *entry;
+ /* prior to LONG_GROUP, we never used this CMD version API */
+ u8 grp = iwl_cmd_groupid(cmd_id) ?: LONG_GROUP;
+ u8 cmd = iwl_cmd_opcode(cmd_id);
+
+ if (table_source != BIOS_SOURCE_ACPI &&
+ table_source != BIOS_SOURCE_UEFI)
+ return def;
+
+ if (!fw->ucode_capa.cmd_bios_tables ||
+ !fw->ucode_capa.n_cmd_bios_tables)
+ return def;
+
+ entry = fw->ucode_capa.cmd_bios_tables;
+ for (int i = 0; i < fw->ucode_capa.n_cmd_bios_tables; i++, entry++) {
+ if (entry->group == grp && entry->cmd == cmd) {
+ if (table_source == BIOS_SOURCE_ACPI)
+ return entry->max_acpi_revision;
+ return entry->max_uefi_revision;
+ }
+ }
+
+ return def;
+}
+EXPORT_SYMBOL_GPL(iwl_fw_lookup_cmd_bios_supported_revision);
+
u8 iwl_fw_lookup_cmd_ver(const struct iwl_fw *fw, u32 cmd_id, u8 def)
{
const struct iwl_fw_cmd_version *entry;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.h b/drivers/net/wireless/intel/iwlwifi/fw/img.h
index 045a3e009429..94113d1db8e1 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/img.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/img.h
@@ -9,6 +9,7 @@
#include <linux/types.h>
#include "api/dbg-tlv.h"
+#include "api/nvm-reg.h"
#include "file.h"
#include "error-dump.h"
@@ -57,6 +58,9 @@ struct iwl_ucode_capabilities {
const struct iwl_fw_cmd_version *cmd_versions;
u32 n_cmd_versions;
+
+ const struct iwl_fw_cmd_bios_table *cmd_bios_tables;
+ u32 n_cmd_bios_tables;
};
static inline bool
@@ -274,6 +278,10 @@ iwl_get_ucode_image(const struct iwl_fw *fw, enum iwl_ucode_type ucode_type)
return &fw->img[ucode_type];
}
+u8 iwl_fw_lookup_cmd_bios_supported_revision(const struct iwl_fw *fw,
+ enum bios_source table_source,
+ u32 cmd_id, u8 def);
+
u8 iwl_fw_lookup_cmd_ver(const struct iwl_fw *fw, u32 cmd_id, u8 def);
u8 iwl_fw_lookup_notif_ver(const struct iwl_fw *fw, u8 grp, u8 cmd, u8 def);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
index 475b3e417efa..4cdd0fe1b788 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
@@ -133,6 +133,7 @@ static void iwl_dealloc_ucode(struct iwl_drv *drv)
kfree(drv->fw.dbg.mem_tlv);
kfree(drv->fw.iml);
kfree(drv->fw.ucode_capa.cmd_versions);
+ kfree(drv->fw.ucode_capa.cmd_bios_tables);
kfree(drv->fw.phy_integration_ver);
kfree(drv->trans->dbg.pc_data);
drv->trans->dbg.pc_data = NULL;
@@ -1426,6 +1427,26 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
return -ENOMEM;
drv->fw.pnvm_size = tlv_len;
break;
+ case IWL_UCODE_TLV_CMD_BIOS_TABLE:
+ if (tlv_len % sizeof(struct iwl_fw_cmd_bios_table)) {
+ IWL_ERR(drv,
+ "Invalid length for command bios table: %u\n",
+ tlv_len);
+ return -EINVAL;
+ }
+
+ if (capa->cmd_bios_tables) {
+ IWL_ERR(drv, "Duplicate TLV type 0x%02X detected\n",
+ tlv_type);
+ return -EINVAL;
+ }
+ capa->cmd_bios_tables = kmemdup(tlv_data, tlv_len,
+ GFP_KERNEL);
+ if (!capa->cmd_bios_tables)
+ return -ENOMEM;
+ capa->n_cmd_bios_tables =
+ tlv_len / sizeof(struct iwl_fw_cmd_bios_table);
+ break;
default:
IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
break;
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH iwlwifi-next 08/15] wifi: iwlwifi: mld: eliminate duplicate WIDE_ID in PPAG command handling
2026-03-20 8:09 [PATCH iwlwifi-next 00/15] wifi: iwlwifi: updates - 2026-03-20 Miri Korenblit
` (6 preceding siblings ...)
2026-03-20 8:09 ` [PATCH iwlwifi-next 07/15] wifi: iwlwifi: fw: Add TLV support for BIOS revision of command Miri Korenblit
@ 2026-03-20 8:09 ` Miri Korenblit
2026-03-20 8:09 ` [PATCH iwlwifi-next 09/15] wifi: iwlwifi: add CQM event support for per-link RSSI changes Miri Korenblit
` (6 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Miri Korenblit @ 2026-03-20 8:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Pagadala Yesu Anjaneyulu, Emmanuel Grumbach, Johannes Berg
From: Pagadala Yesu Anjaneyulu <pagadala.yesu.anjaneyulu@intel.com>
Extract the PER_PLATFORM_ANT_GAIN_CMD command ID into a local variable
to avoid duplicating WIDE_ID(PHY_OPS_GROUP, PER_PLATFORM_ANT_GAIN_CMD).
Signed-off-by: Pagadala Yesu Anjaneyulu <pagadala.yesu.anjaneyulu@intel.com>
Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/mld/regulatory.c | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
index 2486d78d6fc3..f91f61ca9b2e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
@@ -211,10 +211,8 @@ static int iwl_mld_ppag_send_cmd(struct iwl_mld *mld)
.v8.ppag_config_info.hdr.table_revision = fwrt->ppag_bios_rev,
.v8.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags),
};
- int cmd_ver =
- iwl_fw_lookup_cmd_ver(mld->fw,
- WIDE_ID(PHY_OPS_GROUP,
- PER_PLATFORM_ANT_GAIN_CMD), 1);
+ u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_PLATFORM_ANT_GAIN_CMD);
+ int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 1);
int cmd_len = sizeof(cmd.v8);
int ret;
@@ -271,9 +269,7 @@ static int iwl_mld_ppag_send_cmd(struct iwl_mld *mld)
}
IWL_DEBUG_RADIO(mld, "Sending PER_PLATFORM_ANT_GAIN_CMD\n");
- ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP,
- PER_PLATFORM_ANT_GAIN_CMD),
- &cmd, cmd_len);
+ ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, cmd_len);
if (ret < 0)
IWL_ERR(mld, "failed to send PER_PLATFORM_ANT_GAIN_CMD (%d)\n",
ret);
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH iwlwifi-next 09/15] wifi: iwlwifi: add CQM event support for per-link RSSI changes
2026-03-20 8:09 [PATCH iwlwifi-next 00/15] wifi: iwlwifi: updates - 2026-03-20 Miri Korenblit
` (7 preceding siblings ...)
2026-03-20 8:09 ` [PATCH iwlwifi-next 08/15] wifi: iwlwifi: mld: eliminate duplicate WIDE_ID in PPAG command handling Miri Korenblit
@ 2026-03-20 8:09 ` Miri Korenblit
2026-03-20 8:09 ` [PATCH iwlwifi-next 10/15] wifi: iwlwifi: validate the channels received in iwl_mcc_update_resp_v* Miri Korenblit
` (5 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Miri Korenblit @ 2026-03-20 8:09 UTC (permalink / raw)
To: linux-wireless
Cc: Avinash Bhatt, Emmanuel Grumbach, Johannes Berg,
Pagadala Yesu Anjaneyulu
From: Avinash Bhatt <avinash.bhatt@intel.com>
Implement CQM RSSI threshold handling by tracking the last reported RSSI
and issuing CQM low/high events when the RSSI crosses the configured
threshold with the required hysteresis. This provides proper CQM support
and enables userspace to receive per-link RSSI notifications.
Signed-off-by: Avinash Bhatt <avinash.bhatt@intel.com>
Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Reviewed-by: Pagadala Yesu Anjaneyulu <pagadala.yesu.anjaneyulu@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/mld/link.h | 2 ++
.../net/wireless/intel/iwlwifi/mld/stats.c | 26 ++++++++++++++++++-
2 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.h b/drivers/net/wireless/intel/iwlwifi/mld/link.h
index 9e4da8e4de93..ca691259fc5e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/link.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/link.h
@@ -40,6 +40,7 @@ struct iwl_probe_resp_data {
* @bcast_sta: station used for broadcast packets. Used in AP, GO and IBSS.
* @mcast_sta: station used for multicast packets. Used in AP, GO and IBSS.
* @mon_sta: station used for TX injection in monitor interface.
+ * @last_cqm_rssi_event: rssi of the last cqm rssi event
* @average_beacon_energy: average beacon energy for beacons received during
* client connections
* @ap_early_keys: The firmware cannot install keys before bcast/mcast STAs,
@@ -66,6 +67,7 @@ struct iwl_mld_link {
struct iwl_mld_int_sta bcast_sta;
struct iwl_mld_int_sta mcast_sta;
struct iwl_mld_int_sta mon_sta;
+ int last_cqm_rssi_event;
/* we can only have 2 GTK + 2 IGTK + 2 BIGTK active at a time */
struct ieee80211_key_conf *ap_early_keys[6];
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.c b/drivers/net/wireless/intel/iwlwifi/mld/stats.c
index 7b8709716324..9b3149b9d2c2 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/stats.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.c
@@ -369,15 +369,39 @@ static void iwl_mld_stats_recalc_traffic_load(struct iwl_mld *mld,
static void iwl_mld_update_link_sig(struct ieee80211_vif *vif, int sig,
struct ieee80211_bss_conf *bss_conf)
{
+ struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf);
struct iwl_mld *mld = iwl_mld_vif_from_mac80211(vif)->mld;
int exit_emlsr_thresh;
+ int last_event;
if (sig == 0) {
IWL_DEBUG_RX(mld, "RSSI is 0 - skip signal based decision\n");
return;
}
- /* TODO: task=statistics handle CQM notifications */
+ if (WARN_ON(!link))
+ return;
+
+ /* CQM Notification */
+ if (vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI) {
+ int thold = bss_conf->cqm_rssi_thold;
+ int hyst = bss_conf->cqm_rssi_hyst;
+
+ last_event = link->last_cqm_rssi_event;
+ if (thold && sig < thold &&
+ (last_event == 0 || sig < last_event - hyst)) {
+ link->last_cqm_rssi_event = sig;
+ ieee80211_cqm_rssi_notify(vif,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+ sig, GFP_KERNEL);
+ } else if (sig > thold &&
+ (last_event == 0 || sig > last_event + hyst)) {
+ link->last_cqm_rssi_event = sig;
+ ieee80211_cqm_rssi_notify(vif,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+ sig, GFP_KERNEL);
+ }
+ }
if (!iwl_mld_emlsr_active(vif)) {
/* We're not in EMLSR and our signal is bad,
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH iwlwifi-next 10/15] wifi: iwlwifi: validate the channels received in iwl_mcc_update_resp_v*
2026-03-20 8:09 [PATCH iwlwifi-next 00/15] wifi: iwlwifi: updates - 2026-03-20 Miri Korenblit
` (8 preceding siblings ...)
2026-03-20 8:09 ` [PATCH iwlwifi-next 09/15] wifi: iwlwifi: add CQM event support for per-link RSSI changes Miri Korenblit
@ 2026-03-20 8:09 ` Miri Korenblit
2026-03-20 8:09 ` [PATCH iwlwifi-next 11/15] wifi: iwlwifi: mld: add BIOS revision compatibility check for PPAG command Miri Korenblit
` (4 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Miri Korenblit @ 2026-03-20 8:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
Check with IWL_FW_CHECK that the FW doesn't send a channel that we don't
support. Otherwise, the center frequency will be 0, leading to a
warning since is_valid_reg_rule will return false, of course.
Although the warning is verbose enough, the IWL_FW_CHECK will spare some
of the debug.
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
index e8f7d258b622..b78805ef3c8b 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
@@ -23,6 +23,8 @@
#include "fw/api/commands.h"
#include "fw/api/cmdhdr.h"
#include "fw/img.h"
+#include "fw/dbg.h"
+
#include "mei/iwl-mei.h"
/* NVM offsets (in words) definitions */
@@ -1737,6 +1739,11 @@ iwl_parse_nvm_mcc_info(struct iwl_trans *trans,
band);
new_rule = false;
+ if (IWL_FW_CHECK(trans, !center_freq,
+ "Invalid channel %d (idx %d) in NVM\n",
+ nvm_chan[ch_idx], ch_idx))
+ continue;
+
if (!(ch_flags & NVM_CHANNEL_VALID)) {
iwl_nvm_print_channel_flags(dev, IWL_DL_LAR,
nvm_chan[ch_idx], ch_flags);
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH iwlwifi-next 11/15] wifi: iwlwifi: mld: add BIOS revision compatibility check for PPAG command
2026-03-20 8:09 [PATCH iwlwifi-next 00/15] wifi: iwlwifi: updates - 2026-03-20 Miri Korenblit
` (9 preceding siblings ...)
2026-03-20 8:09 ` [PATCH iwlwifi-next 10/15] wifi: iwlwifi: validate the channels received in iwl_mcc_update_resp_v* Miri Korenblit
@ 2026-03-20 8:09 ` Miri Korenblit
2026-03-20 8:09 ` [PATCH iwlwifi-next 12/15] wifi: iwlwifi: use IWL_FW_CHECK for sync timeout Miri Korenblit
` (3 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Miri Korenblit @ 2026-03-20 8:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Pagadala Yesu Anjaneyulu, Emmanuel Grumbach, Johannes Berg
From: Pagadala Yesu Anjaneyulu <pagadala.yesu.anjaneyulu@intel.com>
Prevent potential issues when newer BIOS revisions
are used with firmware that doesn't support them for
PER_PLATFORM_ANT_GAIN_CMD.
Without this check, the driver may attempt to use
BIOS configurations that are incompatible with the
current firmware version, leading to dropping of
command in firmware without any failure notification
to driver.
Signed-off-by: Pagadala Yesu Anjaneyulu <pagadala.yesu.anjaneyulu@intel.com>
Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../net/wireless/intel/iwlwifi/mld/regulatory.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
index f91f61ca9b2e..1633c8aa2e5d 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
@@ -214,6 +214,7 @@ static int iwl_mld_ppag_send_cmd(struct iwl_mld *mld)
u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_PLATFORM_ANT_GAIN_CMD);
int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 1);
int cmd_len = sizeof(cmd.v8);
+ u8 cmd_bios_rev;
int ret;
BUILD_BUG_ON(offsetof(typeof(cmd), v8.ppag_config_info.hdr) !=
@@ -249,6 +250,10 @@ static int iwl_mld_ppag_send_cmd(struct iwl_mld *mld)
}
}
cmd_len = sizeof(cmd.v7);
+ cmd_bios_rev =
+ iwl_fw_lookup_cmd_bios_supported_revision(fwrt->fw,
+ fwrt->ppag_bios_source,
+ cmd_id, 4);
} else if (cmd_ver == 8) {
for (int chain = 0; chain < ARRAY_SIZE(cmd.v8.gain); chain++) {
for (int subband = 0;
@@ -262,12 +267,22 @@ static int iwl_mld_ppag_send_cmd(struct iwl_mld *mld)
cmd.v8.gain[chain][subband]);
}
}
+ cmd_bios_rev =
+ iwl_fw_lookup_cmd_bios_supported_revision(fwrt->fw,
+ fwrt->ppag_bios_source,
+ cmd_id, 5);
} else {
WARN(1, "Bad version for PER_PLATFORM_ANT_GAIN_CMD %d\n",
cmd_ver);
return -EINVAL;
}
+ if (cmd_bios_rev < fwrt->ppag_bios_rev) {
+ IWL_ERR(mld, "BIOS revision compatibility check failed - Supported: %d, Current: %d\n",
+ cmd_bios_rev, fwrt->ppag_bios_rev);
+ return 0;
+ }
+
IWL_DEBUG_RADIO(mld, "Sending PER_PLATFORM_ANT_GAIN_CMD\n");
ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, cmd_len);
if (ret < 0)
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH iwlwifi-next 12/15] wifi: iwlwifi: use IWL_FW_CHECK for sync timeout
2026-03-20 8:09 [PATCH iwlwifi-next 00/15] wifi: iwlwifi: updates - 2026-03-20 Miri Korenblit
` (10 preceding siblings ...)
2026-03-20 8:09 ` [PATCH iwlwifi-next 11/15] wifi: iwlwifi: mld: add BIOS revision compatibility check for PPAG command Miri Korenblit
@ 2026-03-20 8:09 ` Miri Korenblit
2026-03-20 8:09 ` [PATCH iwlwifi-next 13/15] wifi: iwlwifi: pcie: don't dump on reset handshake in dump Miri Korenblit
` (2 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Miri Korenblit @ 2026-03-20 8:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg, Emmanuel Grumbach
From: Johannes Berg <johannes.berg@intel.com>
This could be a firmware issue, it didn't send all the responses
quickly enough. There are other potential issues (interrupts not
being delivered, etc.) but the FW debug data will at least give
some better information, and it's not a WARN condition anyway.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/mld/rx.c | 5 +++--
drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 7 ++++---
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
index ff6e71e3ff6e..6f40d6e47083 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
@@ -2211,8 +2211,9 @@ void iwl_mld_sync_rx_queues(struct iwl_mld *mld,
ret = wait_event_timeout(mld->rxq_sync.waitq,
READ_ONCE(mld->rxq_sync.state) == 0,
SYNC_RX_QUEUE_TIMEOUT);
- WARN_ONCE(!ret, "RXQ sync failed: state=0x%lx, cookie=%d\n",
- mld->rxq_sync.state, mld->rxq_sync.cookie);
+ IWL_FW_CHECK(mld, !ret,
+ "RXQ sync failed: state=0x%lx, cookie=%d\n",
+ mld->rxq_sync.state, mld->rxq_sync.cookie);
out:
mld->rxq_sync.state = 0;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index 090791fe0638..1ec9807e4827 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -6229,9 +6229,10 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm,
ret = wait_event_timeout(mvm->rx_sync_waitq,
READ_ONCE(mvm->queue_sync_state) == 0,
SYNC_RX_QUEUE_TIMEOUT);
- WARN_ONCE(!ret, "queue sync: failed to sync, state is 0x%lx, cookie %d\n",
- mvm->queue_sync_state,
- mvm->queue_sync_cookie);
+ IWL_FW_CHECK(mvm, !ret,
+ "queue sync: failed to sync, state is 0x%lx, cookie %d\n",
+ mvm->queue_sync_state,
+ mvm->queue_sync_cookie);
}
out:
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH iwlwifi-next 13/15] wifi: iwlwifi: pcie: don't dump on reset handshake in dump
2026-03-20 8:09 [PATCH iwlwifi-next 00/15] wifi: iwlwifi: updates - 2026-03-20 Miri Korenblit
` (11 preceding siblings ...)
2026-03-20 8:09 ` [PATCH iwlwifi-next 12/15] wifi: iwlwifi: use IWL_FW_CHECK for sync timeout Miri Korenblit
@ 2026-03-20 8:09 ` Miri Korenblit
2026-03-20 8:09 ` [PATCH iwlwifi-next 14/15] wifi: iwlwifi: mld: make iwl_mld_mac80211_iftype_to_fw() static Miri Korenblit
2026-03-20 8:09 ` [PATCH iwlwifi-next 15/15] wifi: iwlwifi: mld: remove type argument from iwl_mld_add_sta() Miri Korenblit
14 siblings, 0 replies; 16+ messages in thread
From: Miri Korenblit @ 2026-03-20 8:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg, Emmanuel Grumbach
From: Johannes Berg <johannes.berg@intel.com>
When a FW dump happens, possibly even because of a reset handshake
timeout, there's no point in attempting to dump again. Since all the
callers of the function outside the transport itself are from the FW
dump infrastructure, just split the internal function and make the
external one not dump on timeout.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c
index b15c5d486527..a50e845cea42 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c
@@ -95,7 +95,9 @@ static void iwl_pcie_gen2_apm_stop(struct iwl_trans *trans, bool op_mode_leave)
CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
}
-void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
+static void
+_iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans,
+ bool dump_on_timeout)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int ret;
@@ -133,7 +135,7 @@ void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
"timeout waiting for FW reset ACK (inta_hw=0x%x, reset_done %d)\n",
inta_hw, reset_done);
- if (!reset_done) {
+ if (!reset_done && dump_on_timeout) {
struct iwl_fw_error_dump_mode mode = {
.type = IWL_ERR_TYPE_RESET_HS_TIMEOUT,
.context = IWL_ERR_CONTEXT_FROM_OPMODE,
@@ -147,6 +149,11 @@ void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
trans_pcie->fw_reset_state = FW_RESET_IDLE;
}
+void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
+{
+ _iwl_trans_pcie_fw_reset_handshake(trans, false);
+}
+
static void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
@@ -163,7 +170,7 @@ static void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans)
* should assume that the firmware is already dead.
*/
trans->state = IWL_TRANS_NO_FW;
- iwl_trans_pcie_fw_reset_handshake(trans);
+ _iwl_trans_pcie_fw_reset_handshake(trans, true);
}
trans_pcie->is_down = true;
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH iwlwifi-next 14/15] wifi: iwlwifi: mld: make iwl_mld_mac80211_iftype_to_fw() static
2026-03-20 8:09 [PATCH iwlwifi-next 00/15] wifi: iwlwifi: updates - 2026-03-20 Miri Korenblit
` (12 preceding siblings ...)
2026-03-20 8:09 ` [PATCH iwlwifi-next 13/15] wifi: iwlwifi: pcie: don't dump on reset handshake in dump Miri Korenblit
@ 2026-03-20 8:09 ` Miri Korenblit
2026-03-20 8:09 ` [PATCH iwlwifi-next 15/15] wifi: iwlwifi: mld: remove type argument from iwl_mld_add_sta() Miri Korenblit
14 siblings, 0 replies; 16+ messages in thread
From: Miri Korenblit @ 2026-03-20 8:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
This function is only used within the file, so make it static.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/mld/iface.c | 2 +-
drivers/net/wireless/intel/iwlwifi/mld/iface.h | 2 --
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
index dc96214671d9..472592aa97fd 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
@@ -74,7 +74,7 @@ static int iwl_mld_send_mac_cmd(struct iwl_mld *mld,
return ret;
}
-int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif)
+static int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif)
{
switch (vif->type) {
case NL80211_IFTYPE_STATION:
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h
index 62fca166afd1..3e106c93f0db 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h
@@ -219,8 +219,6 @@ iwl_mld_link_from_mac80211(struct ieee80211_bss_conf *bss_conf)
return iwl_mld_link_dereference_check(mld_vif, bss_conf->link_id);
}
-int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif);
-
/* Cleanup function for struct iwl_mld_vif, will be called in restart */
void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH iwlwifi-next 15/15] wifi: iwlwifi: mld: remove type argument from iwl_mld_add_sta()
2026-03-20 8:09 [PATCH iwlwifi-next 00/15] wifi: iwlwifi: updates - 2026-03-20 Miri Korenblit
` (13 preceding siblings ...)
2026-03-20 8:09 ` [PATCH iwlwifi-next 14/15] wifi: iwlwifi: mld: make iwl_mld_mac80211_iftype_to_fw() static Miri Korenblit
@ 2026-03-20 8:09 ` Miri Korenblit
14 siblings, 0 replies; 16+ messages in thread
From: Miri Korenblit @ 2026-03-20 8:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
This is used only in a single place, and the caller always sets
the type to STATION_TYPE_PEER right now. We need to change some
of this for NAN in the future, removing the type argument will
simplify that.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/mld/mac80211.c | 2 +-
drivers/net/wireless/intel/iwlwifi/mld/sta.c | 4 ++--
drivers/net/wireless/intel/iwlwifi/mld/sta.h | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
index 5cd3cdffb570..358320051d1e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
@@ -1751,7 +1751,7 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld,
return -EBUSY;
}
- ret = iwl_mld_add_sta(mld, sta, vif, STATION_TYPE_PEER);
+ ret = iwl_mld_add_sta(mld, sta, vif);
if (ret)
return ret;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c
index f40c49377466..619f302076ad 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c
@@ -755,14 +755,14 @@ iwl_mld_init_sta(struct iwl_mld *mld, struct ieee80211_sta *sta,
}
int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta,
- struct ieee80211_vif *vif, enum iwl_fw_sta_type type)
+ struct ieee80211_vif *vif)
{
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
struct ieee80211_link_sta *link_sta;
int link_id;
int ret;
- ret = iwl_mld_init_sta(mld, sta, vif, type);
+ ret = iwl_mld_init_sta(mld, sta, vif, STATION_TYPE_PEER);
if (ret)
return ret;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.h b/drivers/net/wireless/intel/iwlwifi/mld/sta.h
index 1897b121aae2..5f6c440bf058 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/sta.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.h
@@ -190,7 +190,7 @@ iwl_mld_link_sta_from_mac80211(struct ieee80211_link_sta *link_sta)
}
int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta,
- struct ieee80211_vif *vif, enum iwl_fw_sta_type type);
+ struct ieee80211_vif *vif);
void iwl_mld_remove_sta(struct iwl_mld *mld, struct ieee80211_sta *sta);
int iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld *mld,
struct ieee80211_link_sta *link_sta);
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread