mt76 fixes for 7.3

- mt7925 ACPI SAR fixes
 - MLO fixes
 - sta handling fixes
 - mt7915 napi disable fix
 -----BEGIN PGP SIGNATURE-----
 Comment: GPGTools - http://gpgtools.org
 
 iF0EABECAB0WIQR10Rp9kadxD0kAQu/XfRQdAqdu9QUCasTFpgAKCRDXfRQdAqdu
 9R6sAJ9NkBQV/FFjzYB7/XgwwwNwG+7BKwCgxSMKBJM18DDh9yUTYYp+WDFdbSY=
 =b8ye
 -----END PGP SIGNATURE-----

Merge tag 'mt76-fixes-2026-10-05' of https://github.com/nbd168/wireless

Felix Fietkau says:
===================
mt76 fixes for 7.3

- mt7925 ACPI SAR fixes
- MLO fixes
- sta handling fixes
- mt7915 napi disable fix
===================

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2026-10-06 12:46:20 +02:00
commit 95148fe661
12 changed files with 144 additions and 53 deletions

View File

@ -1426,6 +1426,13 @@ mtxq_to_txq(struct mt76_txq *mtxq)
return container_of(ptr, struct ieee80211_txq, drv_priv);
}
/* peer-wide state uses the wcid of the primary link */
static inline struct mt76_wcid *
mt76_wcid_primary(struct mt76_wcid *wcid)
{
return wcid->def_wcid ? wcid->def_wcid : wcid;
}
static inline struct ieee80211_sta *
wcid_to_sta(struct mt76_wcid *wcid)
{

View File

@ -230,6 +230,7 @@ mt7603_mac_init(struct mt7603_dev *dev)
eth_broadcast_addr(bc_addr);
mt7603_wtbl_init(dev, MT7603_WTBL_RESERVED, -1, bc_addr);
mt76_wcid_init(&dev->global_sta.wcid, 0);
dev->global_sta.wcid.idx = MT7603_WTBL_RESERVED;
rcu_assign_pointer(dev->mt76.wcid[MT7603_WTBL_RESERVED],
&dev->global_sta.wcid);

View File

@ -1238,6 +1238,12 @@ static void mt7915_unregister_ext_phy(struct mt7915_dev *dev)
static void mt7915_stop_hardware(struct mt7915_dev *dev)
{
int i;
mt76_for_each_q_rx(&dev->mt76, i) {
if (!mt76_queue_is_wed_rro(&dev->mt76.q_rx[i]))
napi_disable(&dev->mt76.napi[i]);
}
mt7915_mcu_exit(dev);
mt76_connac2_tx_token_put(&dev->mt76);
mt7915_dma_cleanup(dev);

View File

@ -805,6 +805,7 @@ __mt7915_drop_sta(void *ptr, u8 *mac, struct ieee80211_vif *vif)
msta = (struct mt7915_sta *)sta->drv_priv;
mt7915_mcu_add_sta(data->dev, vif, sta, CONN_STATE_DISCONNECT, false);
msta->wcid.tx_info &= ~MT_WCID_TX_INFO_SET;
msta->wcid.sta_disabled = 1;
msta->wcid.sta = 0;
}
@ -871,6 +872,7 @@ int mt7915_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif,
mutex_unlock(&dev->mt76.mutex);
mt7915_mcu_add_sta(dev, vif, sta, CONN_STATE_DISCONNECT, false);
msta->wcid.tx_info &= ~MT_WCID_TX_INFO_SET;
msta->wcid.sta_disabled = 1;
msta->wcid.sta = 0;
return 0;

View File

@ -798,7 +798,8 @@ mt7925_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
val = FIELD_PREP(MT_TXD1_WLAN_IDX, wcid->idx) |
FIELD_PREP(MT_TXD1_OWN_MAC, omac_idx);
if (band_idx)
/* 0xff means BAND_AUTO to the firmware, and TGID cannot encode it */
if (band_idx && band_idx != 0xff)
val |= FIELD_PREP(MT_TXD1_TGID, band_idx);
txwi[1] = cpu_to_le32(val);

View File

@ -2294,6 +2294,18 @@ mt7925_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
mvif->valid_links = new_links;
/* Restore the legacy BSS after disabling MLO */
if (old_links && !new_links) {
mt792x_mac_link_bss_remove(dev, &mvif->bss_conf,
&mvif->sta.deflink);
err = mt7925_mac_link_bss_add(dev, &vif->bss_conf,
&mvif->sta.deflink);
if (err < 0) {
mt792x_mutex_release(dev);
return err;
}
}
mt792x_mutex_release(dev);
return 0;

View File

@ -108,69 +108,99 @@ mt792x_asar_acpi_read_mtcl(struct mt792x_dev *dev, u8 **table, u8 *version)
return ret;
}
/* A table's layout is decided by the version byte in MTCL, but some firmware
* labels a version 2 table as version 1. Accept whichever layout the table
* actually has: for every accepted entry count the two do not overlap, since
* one is 6 + 5n bytes and the other 7 + 7n.
*/
static bool
mt792x_asar_fits(u8 *table, int len, int prelen, int sarlen, int nr_off,
int min, int max)
{
int tblcnt;
if (len < prelen || (len - prelen) % sarlen)
return false;
tblcnt = (len - prelen) / sarlen;
if (tblcnt < min || tblcnt > max)
return false;
return tblcnt == table[nr_off];
}
/* MTDS : Dynamic SAR Power Table */
static int
mt792x_asar_acpi_read_mtds(struct mt792x_dev *dev, u8 **table, u8 version)
mt792x_asar_acpi_read_mtds(struct mt792x_dev *dev, u8 **table, u8 version,
u8 *used)
{
int len, ret, sarlen, prelen, tblcnt;
bool enable;
int len, ret;
u8 *t;
ret = mt792x_acpi_read(dev, MT792x_ACPI_MTDS, table, &len);
if (ret)
return ret;
/* Table content validation */
switch (version) {
case 1:
enable = ((struct mt792x_asar_dyn *)*table)->enable;
sarlen = sizeof(struct mt792x_asar_dyn_limit);
prelen = sizeof(struct mt792x_asar_dyn);
break;
case 2:
enable = ((struct mt792x_asar_dyn_v2 *)*table)->enable;
sarlen = sizeof(struct mt792x_asar_dyn_limit_v2);
prelen = sizeof(struct mt792x_asar_dyn_v2);
break;
default:
return -EINVAL;
}
t = *table;
tblcnt = (len - prelen) / sarlen;
if (!enable ||
tblcnt > MT792x_ASAR_MAX_DYN || tblcnt < MT792x_ASAR_MIN_DYN)
/* Table content validation */
if (mt792x_asar_fits(t, len, sizeof(struct mt792x_asar_dyn),
sizeof(struct mt792x_asar_dyn_limit),
offsetof(struct mt792x_asar_dyn, nr_tbl),
MT792x_ASAR_MIN_DYN, MT792x_ASAR_MAX_DYN))
*used = 1;
else if (mt792x_asar_fits(t, len, sizeof(struct mt792x_asar_dyn_v2),
sizeof(struct mt792x_asar_dyn_limit_v2),
offsetof(struct mt792x_asar_dyn_v2, nr_tbl),
MT792x_ASAR_MIN_DYN, MT792x_ASAR_MAX_DYN))
*used = 2;
else
return -EINVAL;
if (!t[offsetof(struct mt792x_asar_dyn, enable)])
return -EINVAL;
if (version && *used != version)
dev_info(dev->mt76.dev,
"MTDS is v%u, MTCL says v%u; using v%u\n",
*used, version, *used);
return 0;
}
/* MTGS : Geo SAR Power Table */
static int
mt792x_asar_acpi_read_mtgs(struct mt792x_dev *dev, u8 **table, u8 version)
mt792x_asar_acpi_read_mtgs(struct mt792x_dev *dev, u8 **table, u8 version,
u8 *used)
{
int len, ret, sarlen, prelen, tblcnt;
int len, ret;
u8 *t;
ret = mt792x_acpi_read(dev, MT792x_ACPI_MTGS, table, &len);
if (ret)
return ret;
/* Table content validation */
switch (version) {
case 1:
sarlen = sizeof(struct mt792x_asar_geo_limit);
prelen = sizeof(struct mt792x_asar_geo);
break;
case 2:
sarlen = sizeof(struct mt792x_asar_geo_limit_v2);
prelen = sizeof(struct mt792x_asar_geo_v2);
break;
default:
return -EINVAL;
}
t = *table;
tblcnt = (len - prelen) / sarlen;
if (tblcnt > MT792x_ASAR_MAX_GEO || tblcnt < MT792x_ASAR_MIN_GEO)
/* Table content validation */
if (mt792x_asar_fits(t, len, sizeof(struct mt792x_asar_geo),
sizeof(struct mt792x_asar_geo_limit),
offsetof(struct mt792x_asar_geo, nr_tbl),
MT792x_ASAR_MIN_GEO, MT792x_ASAR_MAX_GEO))
*used = 1;
else if (mt792x_asar_fits(t, len, sizeof(struct mt792x_asar_geo_v2),
sizeof(struct mt792x_asar_geo_limit_v2),
offsetof(struct mt792x_asar_geo_v2, nr_tbl),
MT792x_ASAR_MIN_GEO, MT792x_ASAR_MAX_GEO))
*used = 2;
else
return -EINVAL;
if (version && *used != version)
dev_info(dev->mt76.dev,
"MTGS is v%u, MTCL says v%u; using v%u\n",
*used, version, *used);
return 0;
}
@ -205,14 +235,16 @@ int mt792x_init_acpi_sar(struct mt792x_dev *dev)
asar->countrylist = NULL;
}
ret = mt792x_asar_acpi_read_mtds(dev, (u8 **)&asar->dyn, asar->ver);
ret = mt792x_asar_acpi_read_mtds(dev, (u8 **)&asar->dyn, asar->ver,
&asar->dyn_ver);
if (ret) {
devm_kfree(dev->mt76.dev, asar->dyn);
asar->dyn = NULL;
}
/* MTGS is optional */
ret = mt792x_asar_acpi_read_mtgs(dev, (u8 **)&asar->geo, asar->ver);
ret = mt792x_asar_acpi_read_mtgs(dev, (u8 **)&asar->geo, asar->ver,
&asar->geo_ver);
if (ret) {
devm_kfree(dev->mt76.dev, asar->geo);
asar->geo = NULL;
@ -254,7 +286,7 @@ mt792x_asar_get_geo_pwr(struct mt792x_phy *phy,
break;
}
if (asar->ver == 1) {
if (asar->geo_ver == 1) {
band_pwr = &asar->geo->tbl[idx].band[0];
max = ARRAY_SIZE(asar->geo->tbl[idx].band);
} else {
@ -279,10 +311,18 @@ mt792x_asar_get_geo_pwr(struct mt792x_phy *phy,
if (idx >= max)
return dyn_power;
geo_power = (band_pwr + idx)->pwr;
dyn_power += (band_pwr + idx)->offset;
band_pwr += idx;
return min(geo_power, dyn_power);
/* Some OEM tables carry 0xff here; narrowed to s8 that is -1 and
* wins the min() below. Take it as no limit.
*/
if (band_pwr->pwr == 0xff)
return dyn_power;
geo_power = band_pwr->pwr;
return min_t(int, geo_power,
min_t(int, dyn_power + band_pwr->offset, 127));
}
static s8
@ -297,7 +337,7 @@ mt792x_asar_range_pwr(struct mt792x_phy *phy,
if (!capa)
return 127;
if (asar->ver == 1) {
if (asar->dyn_ver == 1) {
limit = &asar->dyn->tbl[0].frp[0];
max = ARRAY_SIZE(asar->dyn->tbl[0].frp);
} else {
@ -315,7 +355,9 @@ mt792x_asar_range_pwr(struct mt792x_phy *phy,
else
band = NL80211_BAND_2GHZ;
return mt792x_asar_get_geo_pwr(phy, band, limit[idx]);
/* 0xff: no dynamic limit, see mt792x_asar_get_geo_pwr() */
return mt792x_asar_get_geo_pwr(phy, band,
limit[idx] == 0xff ? 127 : limit[idx]);
}
int mt792x_init_acpi_sar_power(struct mt792x_phy *phy, bool set_default)

View File

@ -105,6 +105,8 @@ struct mt792x_asar_fg {
struct mt792x_acpi_sar {
u8 ver;
u8 dyn_ver;
u8 geo_ver;
union {
struct mt792x_asar_dyn *dyn;
struct mt792x_asar_dyn_v2 *dyn_v2;

View File

@ -540,6 +540,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
ieee80211_hw_set(hw, NO_VIRTUAL_MONITOR);
ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
ieee80211_hw_set(hw, CHANCTX_STA_CSA);
ieee80211_hw_set(hw, CONNECTION_MONITOR);
hw->max_tx_fragments = 4;
wiphy->txq_memory_limit = 32 << 20; /* 32 MiB */
@ -1247,7 +1248,7 @@ static int mt7996_variant_fem_init(struct mt7996_dev *dev)
adie_ver = u32_get_bits(regval, MT_ADIE_VERSION_MASK);
idx = MT7976C_EFUSE_OFFSET % MT7996_EEPROM_BLOCK_SIZE;
is_7976c = adie_ver == 0x8a10 || adie_ver == 0x8b00 ||
adie_ver == 0x8c10 || buf[idx] == 0xc;
adie_ver == 0x8c10 || (!ret && buf[idx] == 0xc);
adie_id = u32_get_bits(regval, MT_ADIE_CHIP_ID_MASK);
if (adie_id == 0x7975 || adie_id == 0x7979 ||

View File

@ -310,7 +310,14 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,
if (vif->type == NL80211_IFTYPE_AP)
return mt7996_mcu_mld_link_oper(dev, link_conf, link,
true);
return 0;
/* update the link address */
ret = mt7996_mcu_add_dev_info(phy, vif, link_conf, mlink, true);
if (ret)
return ret;
return mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink,
msta_link, true);
}
mlink->idx = __ffs64(~dev->mt76.vif_mask);
@ -1426,6 +1433,7 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif,
else if (sta->mlo && links == BIT(link_id)) /* last link */
mt7996_mcu_teardown_mld_sta(dev, link,
msta_link);
msta_link->wcid.tx_info &= ~MT_WCID_TX_INFO_SET;
msta_link->wcid.sta_disabled = 1;
msta_link->wcid.sta = 0;
links = links & ~BIT(link_id);

View File

@ -284,6 +284,9 @@ struct mt7996_mcu_wed_rro_ba_delete_event {
__le16 len;
__le16 session_id;
__le16 wlan_id;
u8 tid;
u8 __rsv1;
u8 __rsv2[2];
} __packed;

View File

@ -234,6 +234,8 @@ mt76_tx_check_non_aql(struct mt76_dev *dev, struct mt76_wcid *wcid,
if (!wcid || info->tx_time_est)
return;
wcid = mt76_wcid_primary(wcid);
pending = atomic_dec_return(&wcid->non_aql_packets);
if (pending < 0)
atomic_cmpxchg(&wcid->non_aql_packets, pending, 0);
@ -344,7 +346,8 @@ __mt76_tx_queue_skb(struct mt76_phy *phy, int qid, struct sk_buff *skb,
if (!non_aql)
return idx;
pending = atomic_inc_return(&wcid->non_aql_packets);
/* the hardware can report the completion on a different link */
pending = atomic_inc_return(&mt76_wcid_primary(wcid)->non_aql_packets);
if (stop && pending >= MT_MAX_NON_AQL_PKT)
*stop = true;
@ -506,7 +509,8 @@ mt76_txq_send_burst(struct mt76_phy *phy, struct mt76_queue *q,
return 0;
}
if (atomic_read(&wcid->non_aql_packets) >= MT_MAX_NON_AQL_PKT)
if (atomic_read(&mt76_wcid_primary(wcid)->non_aql_packets) >=
MT_MAX_NON_AQL_PKT)
return 0;
skb = mt76_txq_dequeue(phy, mtxq);
@ -603,7 +607,8 @@ mt76_txq_schedule_list(struct mt76_phy *phy, enum mt76_txq_id qid)
continue;
}
if (atomic_read(&wcid->non_aql_packets) >= MT_MAX_NON_AQL_PKT)
if (atomic_read(&mt76_wcid_primary(wcid)->non_aql_packets) >=
MT_MAX_NON_AQL_PKT)
continue;
if (dev->queue_ops->tx_cleanup &&
q->queued + 2 * MT_TXQ_FREE_THR >= q->ndesc) {
@ -675,7 +680,8 @@ mt76_txq_schedule_pending_wcid(struct mt76_phy *phy, struct mt76_wcid *wcid,
if ((dev->drv->drv_flags & MT_DRV_HW_MGMT_TXQ) &&
!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) &&
!ieee80211_is_data_present(hdr->frame_control) &&
(!ieee80211_is_bufferable_mmpdu(skb) ||
(!(wcid->tx_info & MT_WCID_TX_INFO_SET) ||
!ieee80211_is_bufferable_mmpdu(skb) ||
ieee80211_is_deauth(hdr->frame_control) ||
ieee80211_is_disassoc(hdr->frame_control) ||
head == &wcid->tx_offchannel))