summarylogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--PKGBUILD9
-rw-r--r--sys-kernel_arch-sources-g14_files-8011-mt76-mt7921-fix-stupid-mediatek-wth-a-hammer.patch6575
-rw-r--r--sys-kernel_arch-sources-g14_files-9008-fix-cpu-hotplug.patch73
-rw-r--r--sys-kernel_arch-sources-g14_files-9009-amd-pstate-sqashed.patch2454
4 files changed, 9109 insertions, 2 deletions
diff --git a/PKGBUILD b/PKGBUILD
index 7dae1f19b5f4..b63e3d743d2e 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -1,7 +1,7 @@
# Maintainer: Jan Alexander Steffens (heftig) <jan.steffens@gmail.com>
pkgbase=linux-g14
-pkgver=5.14.1.arch1
+pkgver=5.14.2.arch1
pkgrel=1
pkgdesc='Linux'
_srctag=v${pkgver%.*}-${pkgver##*.}
@@ -47,7 +47,7 @@ source=(
# mediatek mt7921 bt/wifi patches
- #"sys-kernel_arch-sources-g14_files-8011-Bluetooth-btusb-Add-support-for-Lite-On-Mediatek-Chi.patch"
+ "sys-kernel_arch-sources-g14_files-8011-mt76-mt7921-fix-stupid-mediatek-wth-a-hammer.patch"
#"sys-kernel_arch-sources-g14_files-8012-mt76-mt7921-continue-to-probe-driver-when-fw-already.patch"
"sys-kernel_arch-sources-g14_files-8013-mt76-mt7921-Fix-out-of-order-process-by-invalid-even.patch"
"sys-kernel_arch-sources-g14_files-8014-mt76-mt7921-Add-mt7922-support.patch"
@@ -64,6 +64,10 @@ source=(
"sys-kernel_arch-sources-g14_files-9006-amd-c3-entry.patch"
"sys-kernel_arch-sources-g14_files-9007-squashed-net-tcp_bbr-bbr2-for-5.14.y.patch"
+
+ "sys-kernel_arch-sources-g14_files-9008-fix-cpu-hotplug.patch"
+ "sys-kernel_arch-sources-g14_files-9009-amd-pstate-sqashed.patch"
+
)
validpgpkeys=(
@@ -85,6 +89,7 @@ sha256sums=('SKIP'
'4ef12029ea73ca924b6397e1de4911e84d9e77ddaccdab1ef579823d848524e8'
'6fe5fe83e06d552f541bdca55d0d9e2cd6e70dd3047c01da749a0ff57fe706cc'
'de8c9747637768c4356c06aa65c3f157c526aa420f21fdd5edd0ed06f720a62e'
+ '63b3c3b2a016209cbbea1b327dd3e977f74943821f65c04c0efb2fe967741bb1'
'2163cb2e394a013042a40cd3b00dae788603284b20d71e262995366c5534e480'
'a01cf700d79b983807e2285be1b30df6e02db6adfd9c9027fe2dfa8ca5a74bc9'
'3d6d1e6374688c5f1abbc40b15c03c62f695e9d04d790556c57f8ec5d0d6a3f9'
diff --git a/sys-kernel_arch-sources-g14_files-8011-mt76-mt7921-fix-stupid-mediatek-wth-a-hammer.patch b/sys-kernel_arch-sources-g14_files-8011-mt76-mt7921-fix-stupid-mediatek-wth-a-hammer.patch
new file mode 100644
index 000000000000..b573e0fe6ac6
--- /dev/null
+++ b/sys-kernel_arch-sources-g14_files-8011-mt76-mt7921-fix-stupid-mediatek-wth-a-hammer.patch
@@ -0,0 +1,6575 @@
+diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c
+index 5e1c1506a4c6..e5c324dd24f9 100644
+--- a/drivers/net/wireless/mediatek/mt76/dma.c
++++ b/drivers/net/wireless/mediatek/mt76/dma.c
+@@ -191,7 +191,6 @@ mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q,
+
+ q->entry[idx].txwi = txwi;
+ q->entry[idx].skb = skb;
+- q->entry[idx].wcid = 0xffff;
+
+ return idx;
+ }
+diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
+index d03aedc3286b..03fe62837557 100644
+--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
++++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
+@@ -83,22 +83,6 @@ static const struct ieee80211_tpt_blink mt76_tpt_blink[] = {
+ { .throughput = 300 * 1024, .blink_time = 50 },
+ };
+
+-struct ieee80211_rate mt76_rates[] = {
+- CCK_RATE(0, 10),
+- CCK_RATE(1, 20),
+- CCK_RATE(2, 55),
+- CCK_RATE(3, 110),
+- OFDM_RATE(11, 60),
+- OFDM_RATE(15, 90),
+- OFDM_RATE(10, 120),
+- OFDM_RATE(14, 180),
+- OFDM_RATE(9, 240),
+- OFDM_RATE(13, 360),
+- OFDM_RATE(8, 480),
+- OFDM_RATE(12, 540),
+-};
+-EXPORT_SYMBOL_GPL(mt76_rates);
+-
+ static int mt76_led_init(struct mt76_dev *dev)
+ {
+ struct device_node *np = dev->dev->of_node;
+@@ -331,6 +315,17 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw)
+ ieee80211_hw_set(hw, MFP_CAPABLE);
+ ieee80211_hw_set(hw, AP_LINK_PS);
+ ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
++
++ wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
++ wiphy->interface_modes =
++ BIT(NL80211_IFTYPE_STATION) |
++ BIT(NL80211_IFTYPE_AP) |
++#ifdef CONFIG_MAC80211_MESH
++ BIT(NL80211_IFTYPE_MESH_POINT) |
++#endif
++ BIT(NL80211_IFTYPE_P2P_CLIENT) |
++ BIT(NL80211_IFTYPE_P2P_GO) |
++ BIT(NL80211_IFTYPE_ADHOC);
+ }
+
+ struct mt76_phy *
+@@ -351,17 +346,6 @@ mt76_alloc_phy(struct mt76_dev *dev, unsigned int size,
+ phy->hw = hw;
+ phy->priv = hw->priv + phy_size;
+
+- hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+- hw->wiphy->interface_modes =
+- BIT(NL80211_IFTYPE_STATION) |
+- BIT(NL80211_IFTYPE_AP) |
+-#ifdef CONFIG_MAC80211_MESH
+- BIT(NL80211_IFTYPE_MESH_POINT) |
+-#endif
+- BIT(NL80211_IFTYPE_P2P_CLIENT) |
+- BIT(NL80211_IFTYPE_P2P_GO) |
+- BIT(NL80211_IFTYPE_ADHOC);
+-
+ return phy;
+ }
+ EXPORT_SYMBOL_GPL(mt76_alloc_phy);
+@@ -444,17 +428,6 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
+ mutex_init(&dev->mcu.mutex);
+ dev->tx_worker.fn = mt76_tx_worker;
+
+- hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+- hw->wiphy->interface_modes =
+- BIT(NL80211_IFTYPE_STATION) |
+- BIT(NL80211_IFTYPE_AP) |
+-#ifdef CONFIG_MAC80211_MESH
+- BIT(NL80211_IFTYPE_MESH_POINT) |
+-#endif
+- BIT(NL80211_IFTYPE_P2P_CLIENT) |
+- BIT(NL80211_IFTYPE_P2P_GO) |
+- BIT(NL80211_IFTYPE_ADHOC);
+-
+ spin_lock_init(&dev->token_lock);
+ idr_init(&dev->token);
+
+@@ -659,19 +632,20 @@ void mt76_update_survey_active_time(struct mt76_phy *phy, ktime_t time)
+ }
+ EXPORT_SYMBOL_GPL(mt76_update_survey_active_time);
+
+-void mt76_update_survey(struct mt76_phy *phy)
++void mt76_update_survey(struct mt76_dev *dev)
+ {
+- struct mt76_dev *dev = phy->dev;
+ ktime_t cur_time;
+
+ if (dev->drv->update_survey)
+- dev->drv->update_survey(phy);
++ dev->drv->update_survey(dev);
+
+ cur_time = ktime_get_boottime();
+- mt76_update_survey_active_time(phy, cur_time);
++ mt76_update_survey_active_time(&dev->phy, cur_time);
++ if (dev->phy2)
++ mt76_update_survey_active_time(dev->phy2, cur_time);
+
+ if (dev->drv->drv_flags & MT_DRV_SW_RX_AIRTIME) {
+- struct mt76_channel_state *state = phy->chan_state;
++ struct mt76_channel_state *state = dev->phy.chan_state;
+
+ spin_lock_bh(&dev->cc_lock);
+ state->cc_bss_rx += dev->cur_cc_bss_rx;
+@@ -690,7 +664,7 @@ void mt76_set_channel(struct mt76_phy *phy)
+ int timeout = HZ / 5;
+
+ wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(phy), timeout);
+- mt76_update_survey(phy);
++ mt76_update_survey(dev);
+
+ phy->chandef = *chandef;
+ phy->chan_state = mt76_channel_state(phy, chandef->chan);
+@@ -715,7 +689,7 @@ int mt76_get_survey(struct ieee80211_hw *hw, int idx,
+
+ mutex_lock(&dev->mutex);
+ if (idx == 0 && dev->drv->update_survey)
+- mt76_update_survey(phy);
++ mt76_update_survey(dev);
+
+ sband = &phy->sband_2g;
+ if (idx >= sband->sband.n_channels) {
+diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
+index 25c5ceef5257..0c23edbfbdbb 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt76.h
++++ b/drivers/net/wireless/mediatek/mt76/mt76.h
+@@ -336,7 +336,6 @@ enum {
+ struct mt76_hw_cap {
+ bool has_2ghz;
+ bool has_5ghz;
+- bool has_6ghz;
+ };
+
+ #define MT_DRV_TXWI_NO_FREE BIT(0)
+@@ -353,7 +352,7 @@ struct mt76_driver_ops {
+ u16 token_size;
+ u8 mcs_rates;
+
+- void (*update_survey)(struct mt76_phy *phy);
++ void (*update_survey)(struct mt76_dev *dev);
+
+ int (*tx_prepare_skb)(struct mt76_dev *dev, void *txwi_ptr,
+ enum mt76_txq_id qid, struct mt76_wcid *wcid,
+@@ -755,21 +754,6 @@ enum mt76_phy_type {
+ MT_PHY_TYPE_HE_MU,
+ };
+
+-#define CCK_RATE(_idx, _rate) { \
+- .bitrate = _rate, \
+- .flags = IEEE80211_RATE_SHORT_PREAMBLE, \
+- .hw_value = (MT_PHY_TYPE_CCK << 8) | (_idx), \
+- .hw_value_short = (MT_PHY_TYPE_CCK << 8) | (4 + _idx), \
+-}
+-
+-#define OFDM_RATE(_idx, _rate) { \
+- .bitrate = _rate, \
+- .hw_value = (MT_PHY_TYPE_OFDM << 8) | (_idx), \
+- .hw_value_short = (MT_PHY_TYPE_OFDM << 8) | (_idx), \
+-}
+-
+-extern struct ieee80211_rate mt76_rates[12];
+-
+ #define __mt76_rr(dev, ...) (dev)->bus->rr((dev), __VA_ARGS__)
+ #define __mt76_wr(dev, ...) (dev)->bus->wr((dev), __VA_ARGS__)
+ #define __mt76_rmw(dev, ...) (dev)->bus->rmw((dev), __VA_ARGS__)
+@@ -1063,7 +1047,7 @@ void mt76_release_buffered_frames(struct ieee80211_hw *hw,
+ bool more_data);
+ bool mt76_has_tx_pending(struct mt76_phy *phy);
+ void mt76_set_channel(struct mt76_phy *phy);
+-void mt76_update_survey(struct mt76_phy *phy);
++void mt76_update_survey(struct mt76_dev *dev);
+ void mt76_update_survey_active_time(struct mt76_phy *phy, ktime_t time);
+ int mt76_get_survey(struct ieee80211_hw *hw, int idx,
+ struct survey_info *survey);
+@@ -1088,14 +1072,7 @@ struct sk_buff *mt76_tx_status_skb_get(struct mt76_dev *dev,
+ struct sk_buff_head *list);
+ void mt76_tx_status_skb_done(struct mt76_dev *dev, struct sk_buff *skb,
+ struct sk_buff_head *list);
+-void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid, struct sk_buff *skb,
+- struct list_head *free_list);
+-static inline void
+-mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid, struct sk_buff *skb)
+-{
+- __mt76_tx_complete_skb(dev, wcid, skb, NULL);
+-}
+-
++void mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid, struct sk_buff *skb);
+ void mt76_tx_status_check(struct mt76_dev *dev, struct mt76_wcid *wcid,
+ bool flush);
+ int mt76_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+@@ -1292,15 +1269,4 @@ mt76_token_put(struct mt76_dev *dev, int token)
+
+ return txwi;
+ }
+-
+-static inline int
+-mt76_get_next_pkt_id(struct mt76_wcid *wcid)
+-{
+- wcid->packet_id = (wcid->packet_id + 1) & MT_PACKET_ID_MASK;
+- if (wcid->packet_id == MT_PACKET_ID_NO_ACK ||
+- wcid->packet_id == MT_PACKET_ID_NO_SKB)
+- wcid->packet_id = MT_PACKET_ID_FIRST;
+-
+- return wcid->packet_id;
+-}
+ #endif
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/init.c b/drivers/net/wireless/mediatek/mt76/mt7603/init.c
+index 031d39a48a55..e1b2cfa56074 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7603/init.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7603/init.c
+@@ -304,6 +304,34 @@ mt7603_init_hardware(struct mt7603_dev *dev)
+ return 0;
+ }
+
++#define CCK_RATE(_idx, _rate) { \
++ .bitrate = _rate, \
++ .flags = IEEE80211_RATE_SHORT_PREAMBLE, \
++ .hw_value = (MT_PHY_TYPE_CCK << 8) | (_idx), \
++ .hw_value_short = (MT_PHY_TYPE_CCK << 8) | (4 + _idx), \
++}
++
++#define OFDM_RATE(_idx, _rate) { \
++ .bitrate = _rate, \
++ .hw_value = (MT_PHY_TYPE_OFDM << 8) | (_idx), \
++ .hw_value_short = (MT_PHY_TYPE_OFDM << 8) | (_idx), \
++}
++
++static struct ieee80211_rate mt7603_rates[] = {
++ CCK_RATE(0, 10),
++ CCK_RATE(1, 20),
++ CCK_RATE(2, 55),
++ CCK_RATE(3, 110),
++ OFDM_RATE(11, 60),
++ OFDM_RATE(15, 90),
++ OFDM_RATE(10, 120),
++ OFDM_RATE(14, 180),
++ OFDM_RATE(9, 240),
++ OFDM_RATE(13, 360),
++ OFDM_RATE(8, 480),
++ OFDM_RATE(12, 540),
++};
++
+ static const struct ieee80211_iface_limit if_limits[] = {
+ {
+ .max = 1,
+@@ -541,8 +569,8 @@ int mt7603_register_device(struct mt7603_dev *dev)
+
+ wiphy->reg_notifier = mt7603_regd_notifier;
+
+- ret = mt76_register_device(&dev->mt76, true, mt76_rates,
+- ARRAY_SIZE(mt76_rates));
++ ret = mt76_register_device(&dev->mt76, true, mt7603_rates,
++ ARRAY_SIZE(mt7603_rates));
+ if (ret)
+ return ret;
+
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
+index 3972c56136a2..3aa7483e929f 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
+@@ -1226,7 +1226,7 @@ mt7603_mac_add_txs_skb(struct mt7603_dev *dev, struct mt7603_sta *sta, int pid,
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ if (!mt7603_fill_txs(dev, sta, info, txs_data)) {
+- info->status.rates[0].count = 0;
++ ieee80211_tx_info_clear_status(info);
+ info->status.rates[0].idx = -1;
+ }
+
+@@ -1597,12 +1597,12 @@ mt7603_watchdog_check(struct mt7603_dev *dev, u8 *counter,
+ return true;
+ }
+
+-void mt7603_update_channel(struct mt76_phy *mphy)
++void mt7603_update_channel(struct mt76_dev *mdev)
+ {
+- struct mt7603_dev *dev = container_of(mphy->dev, struct mt7603_dev, mt76);
++ struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76);
+ struct mt76_channel_state *state;
+
+- state = mphy->chan_state;
++ state = mdev->phy.chan_state;
+ state->cc_busy += mt76_rr(dev, MT_MIB_STAT_CCA);
+ }
+
+@@ -1819,7 +1819,7 @@ void mt7603_mac_work(struct work_struct *work)
+ mutex_lock(&dev->mt76.mutex);
+
+ dev->mphy.mac_work_count++;
+- mt76_update_survey(&dev->mphy);
++ mt76_update_survey(&dev->mt76);
+ mt7603_edcca_check(dev);
+
+ for (i = 0, idx = 0; i < 2; i++) {
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h
+index 0fd46d907638..1df5b9fed2bb 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h
++++ b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h
+@@ -256,7 +256,7 @@ void mt7603_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+
+ void mt7603_pre_tbtt_tasklet(struct tasklet_struct *t);
+
+-void mt7603_update_channel(struct mt76_phy *mphy);
++void mt7603_update_channel(struct mt76_dev *mdev);
+
+ void mt7603_edcca_set_strict(struct mt7603_dev *dev, bool val);
+ void mt7603_cca_stats_reset(struct mt7603_dev *dev);
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/Makefile b/drivers/net/wireless/mediatek/mt76/mt7615/Makefile
+index 83f9861ff522..e8fc4a7ae9bc 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7615/Makefile
++++ b/drivers/net/wireless/mediatek/mt76/mt7615/Makefile
+@@ -1,4 +1,4 @@
+-# SPDX-License-Identifier: ISC
++#SPDX-License-Identifier: ISC
+
+ obj-$(CONFIG_MT7615_COMMON) += mt7615-common.o
+ obj-$(CONFIG_MT7615E) += mt7615e.o
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c
+index cb4659771fd9..676bb22726d6 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c
+@@ -75,7 +75,7 @@ mt7615_pm_set(void *data, u64 val)
+ if (!mt7615_wait_for_mcu_init(dev))
+ return 0;
+
+- if (!mt7615_firmware_offload(dev) || mt76_is_usb(&dev->mt76))
++ if (!mt7615_firmware_offload(dev) || !mt76_is_mmio(&dev->mt76))
+ return -EOPNOTSUPP;
+
+ if (val == pm->enable)
+@@ -319,6 +319,24 @@ mt7615_radio_read(struct seq_file *s, void *data)
+ return 0;
+ }
+
++static int mt7615_read_temperature(struct seq_file *s, void *data)
++{
++ struct mt7615_dev *dev = dev_get_drvdata(s->private);
++ int temp;
++
++ if (!mt7615_wait_for_mcu_init(dev))
++ return 0;
++
++ /* cpu */
++ mt7615_mutex_acquire(dev);
++ temp = mt7615_mcu_get_temperature(dev, 0);
++ mt7615_mutex_release(dev);
++
++ seq_printf(s, "Temperature: %d\n", temp);
++
++ return 0;
++}
++
+ static int
+ mt7615_queues_acq(struct seq_file *s, void *data)
+ {
+@@ -548,6 +566,8 @@ int mt7615_init_debugfs(struct mt7615_dev *dev)
+
+ debugfs_create_file("reset_test", 0200, dir, dev,
+ &fops_reset_test);
++ debugfs_create_devm_seqfile(dev->mt76.dev, "temperature", dir,
++ mt7615_read_temperature);
+ debugfs_create_file("ext_mac_addr", 0600, dir, dev, &fops_ext_mac_addr);
+
+ debugfs_create_u32("rf_wfidx", 0600, dir, &dev->debugfs_rf_wf);
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/dma.c b/drivers/net/wireless/mediatek/mt76/mt7615/dma.c
+index 00aefea1bf61..8004ae5c16a9 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7615/dma.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7615/dma.c
+@@ -81,7 +81,7 @@ static int mt7615_poll_tx(struct napi_struct *napi, int budget)
+ if (napi_complete(napi))
+ mt7615_irq_enable(dev, mt7615_tx_mcu_int_mask(dev));
+
+- mt76_connac_pm_unref(&dev->mphy, &dev->pm);
++ mt76_connac_pm_unref(&dev->pm);
+
+ return 0;
+ }
+@@ -99,7 +99,7 @@ static int mt7615_poll_rx(struct napi_struct *napi, int budget)
+ return 0;
+ }
+ done = mt76_dma_rx_poll(napi, budget);
+- mt76_connac_pm_unref(&dev->mphy, &dev->pm);
++ mt76_connac_pm_unref(&dev->pm);
+
+ return done;
+ }
+@@ -222,9 +222,14 @@ void mt7615_dma_start(struct mt7615_dev *dev)
+ int mt7615_dma_init(struct mt7615_dev *dev)
+ {
+ int rx_ring_size = MT7615_RX_RING_SIZE;
++ int rx_buf_size = MT_RX_BUF_SIZE;
+ u32 mask;
+ int ret;
+
++ /* Increase buffer size to receive large VHT MPDUs */
++ if (dev->mphy.cap.has_5ghz)
++ rx_buf_size *= 2;
++
+ mt76_dma_attach(&dev->mt76);
+
+ mt76_wr(dev, MT_WPDMA_GLO_CFG,
+@@ -265,7 +270,7 @@ int mt7615_dma_init(struct mt7615_dev *dev)
+
+ /* init rx queues */
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU], 1,
+- MT7615_RX_MCU_RING_SIZE, MT_RX_BUF_SIZE,
++ MT7615_RX_MCU_RING_SIZE, rx_buf_size,
+ MT_RX_RING_BASE);
+ if (ret)
+ return ret;
+@@ -274,7 +279,7 @@ int mt7615_dma_init(struct mt7615_dev *dev)
+ rx_ring_size /= 2;
+
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], 0,
+- rx_ring_size, MT_RX_BUF_SIZE, MT_RX_RING_BASE);
++ rx_ring_size, rx_buf_size, MT_RX_RING_BASE);
+ if (ret)
+ return ret;
+
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/init.c b/drivers/net/wireless/mediatek/mt76/mt7615/init.c
+index 2f1ac644e018..0d01fd3c77b5 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7615/init.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7615/init.c
+@@ -8,61 +8,11 @@
+ */
+
+ #include <linux/etherdevice.h>
+-#include <linux/hwmon.h>
+-#include <linux/hwmon-sysfs.h>
+ #include "mt7615.h"
+ #include "mac.h"
+ #include "mcu.h"
+ #include "eeprom.h"
+
+-static ssize_t mt7615_thermal_show_temp(struct device *dev,
+- struct device_attribute *attr,
+- char *buf)
+-{
+- struct mt7615_dev *mdev = dev_get_drvdata(dev);
+- int temperature;
+-
+- if (!mt7615_wait_for_mcu_init(mdev))
+- return 0;
+-
+- mt7615_mutex_acquire(mdev);
+- temperature = mt7615_mcu_get_temperature(mdev);
+- mt7615_mutex_release(mdev);
+-
+- if (temperature < 0)
+- return temperature;
+-
+- /* display in millidegree celcius */
+- return sprintf(buf, "%u\n", temperature * 1000);
+-}
+-
+-static SENSOR_DEVICE_ATTR(temp1_input, 0444, mt7615_thermal_show_temp,
+- NULL, 0);
+-
+-static struct attribute *mt7615_hwmon_attrs[] = {
+- &sensor_dev_attr_temp1_input.dev_attr.attr,
+- NULL,
+-};
+-ATTRIBUTE_GROUPS(mt7615_hwmon);
+-
+-int mt7615_thermal_init(struct mt7615_dev *dev)
+-{
+- struct wiphy *wiphy = mt76_hw(dev)->wiphy;
+- struct device *hwmon;
+-
+- if (!IS_REACHABLE(CONFIG_HWMON))
+- return 0;
+-
+- hwmon = devm_hwmon_device_register_with_groups(&wiphy->dev,
+- wiphy_name(wiphy), dev,
+- mt7615_hwmon_groups);
+- if (IS_ERR(hwmon))
+- return PTR_ERR(hwmon);
+-
+- return 0;
+-}
+-EXPORT_SYMBOL_GPL(mt7615_thermal_init);
+-
+ static void
+ mt7615_phy_init(struct mt7615_dev *dev)
+ {
+@@ -224,6 +174,35 @@ bool mt7615_wait_for_mcu_init(struct mt7615_dev *dev)
+ }
+ EXPORT_SYMBOL_GPL(mt7615_wait_for_mcu_init);
+
++#define CCK_RATE(_idx, _rate) { \
++ .bitrate = _rate, \
++ .flags = IEEE80211_RATE_SHORT_PREAMBLE, \
++ .hw_value = (MT_PHY_TYPE_CCK << 8) | (_idx), \
++ .hw_value_short = (MT_PHY_TYPE_CCK << 8) | (4 + (_idx)), \
++}
++
++#define OFDM_RATE(_idx, _rate) { \
++ .bitrate = _rate, \
++ .hw_value = (MT_PHY_TYPE_OFDM << 8) | (_idx), \
++ .hw_value_short = (MT_PHY_TYPE_OFDM << 8) | (_idx), \
++}
++
++struct ieee80211_rate mt7615_rates[] = {
++ CCK_RATE(0, 10),
++ CCK_RATE(1, 20),
++ CCK_RATE(2, 55),
++ CCK_RATE(3, 110),
++ OFDM_RATE(11, 60),
++ OFDM_RATE(15, 90),
++ OFDM_RATE(10, 120),
++ OFDM_RATE(14, 180),
++ OFDM_RATE(9, 240),
++ OFDM_RATE(13, 360),
++ OFDM_RATE(8, 480),
++ OFDM_RATE(12, 540),
++};
++EXPORT_SYMBOL_GPL(mt7615_rates);
++
+ static const struct ieee80211_iface_limit if_limits[] = {
+ {
+ .max = 1,
+@@ -493,8 +472,8 @@ int mt7615_register_ext_phy(struct mt7615_dev *dev)
+ for (i = 0; i <= MT_TXQ_PSD ; i++)
+ mphy->q_tx[i] = dev->mphy.q_tx[i];
+
+- ret = mt76_register_phy(mphy, true, mt76_rates,
+- ARRAY_SIZE(mt76_rates));
++ ret = mt76_register_phy(mphy, true, mt7615_rates,
++ ARRAY_SIZE(mt7615_rates));
+ if (ret)
+ ieee80211_free_hw(mphy->hw);
+
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
+index ff3f85e4087c..4873154d082e 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
+@@ -20,7 +20,7 @@
+ #define to_rssi(field, rxv) ((FIELD_GET(field, rxv) - 220) / 2)
+
+ static const struct mt7615_dfs_radar_spec etsi_radar_specs = {
+- .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 },
++ .pulse_th = { 40, -10, -80, 800, 3360, 128, 5200 },
+ .radar_pattern = {
+ [5] = { 1, 0, 6, 32, 28, 0, 17, 990, 5010, 1, 1 },
+ [6] = { 1, 0, 9, 32, 28, 0, 27, 615, 5010, 1, 1 },
+@@ -34,7 +34,7 @@ static const struct mt7615_dfs_radar_spec etsi_radar_specs = {
+ };
+
+ static const struct mt7615_dfs_radar_spec fcc_radar_specs = {
+- .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 },
++ .pulse_th = { 40, -10, -80, 800, 3360, 128, 5200 },
+ .radar_pattern = {
+ [0] = { 1, 0, 9, 32, 28, 0, 13, 508, 3076, 1, 1 },
+ [1] = { 1, 0, 12, 32, 28, 0, 17, 140, 240, 1, 1 },
+@@ -45,7 +45,7 @@ static const struct mt7615_dfs_radar_spec fcc_radar_specs = {
+ };
+
+ static const struct mt7615_dfs_radar_spec jp_radar_specs = {
+- .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 },
++ .pulse_th = { 40, -10, -80, 800, 3360, 128, 5200 },
+ .radar_pattern = {
+ [0] = { 1, 0, 8, 32, 28, 0, 13, 508, 3076, 1, 1 },
+ [1] = { 1, 0, 12, 32, 28, 0, 17, 140, 240, 1, 1 },
+@@ -1102,7 +1102,7 @@ void mt7615_mac_set_rates(struct mt7615_phy *phy, struct mt7615_sta *sta,
+ idx = idx > HW_BSSID_MAX ? HW_BSSID_0 : idx;
+ addr = idx > 1 ? MT_LPON_TCR2(idx): MT_LPON_TCR0(idx);
+
+- mt76_rmw(dev, addr, MT_LPON_TCR_MODE, MT_LPON_TCR_READ); /* TSF read */
++ mt76_set(dev, addr, MT_LPON_TCR_MODE); /* TSF read */
+ sta->rate_set_tsf = mt76_rr(dev, MT_LPON_UTTR0) & ~BIT(0);
+ sta->rate_set_tsf |= rd.rateset;
+
+@@ -1429,7 +1429,7 @@ static bool mt7615_mac_add_txs_skb(struct mt7615_dev *dev,
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ if (!mt7615_fill_txs(dev, sta, info, txs_data)) {
+- info->status.rates[0].count = 0;
++ ieee80211_tx_info_clear_status(info);
+ info->status.rates[0].idx = -1;
+ }
+
+@@ -1859,41 +1859,43 @@ mt7615_phy_update_channel(struct mt76_phy *mphy, int idx)
+ state->noise = -(phy->noise >> 4);
+ }
+
+-static void mt7615_update_survey(struct mt7615_dev *dev)
++static void __mt7615_update_channel(struct mt7615_dev *dev)
+ {
+ struct mt76_dev *mdev = &dev->mt76;
+- ktime_t cur_time;
+-
+- /* MT7615 can only update both phys simultaneously
+- * since some reisters are shared across bands.
+- */
+
+ mt7615_phy_update_channel(&mdev->phy, 0);
+ if (mdev->phy2)
+ mt7615_phy_update_channel(mdev->phy2, 1);
+
+- cur_time = ktime_get_boottime();
+-
+- mt76_update_survey_active_time(&mdev->phy, cur_time);
+- if (mdev->phy2)
+- mt76_update_survey_active_time(mdev->phy2, cur_time);
+-
+ /* reset obss airtime */
+ mt76_set(dev, MT_WF_RMAC_MIB_TIME0, MT_WF_RMAC_MIB_RXTIME_CLR);
+ }
+
+-void mt7615_update_channel(struct mt76_phy *mphy)
++void mt7615_update_channel(struct mt76_dev *mdev)
+ {
+- struct mt7615_dev *dev = container_of(mphy->dev, struct mt7615_dev, mt76);
++ struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76);
+
+ if (mt76_connac_pm_wake(&dev->mphy, &dev->pm))
+ return;
+
+- mt7615_update_survey(dev);
++ __mt7615_update_channel(dev);
+ mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
+ }
+ EXPORT_SYMBOL_GPL(mt7615_update_channel);
+
++static void mt7615_update_survey(struct mt7615_dev *dev)
++{
++ struct mt76_dev *mdev = &dev->mt76;
++ ktime_t cur_time;
++
++ __mt7615_update_channel(dev);
++ cur_time = ktime_get_boottime();
++
++ mt76_update_survey_active_time(&mdev->phy, cur_time);
++ if (mdev->phy2)
++ mt76_update_survey_active_time(mdev->phy2, cur_time);
++}
++
+ static void
+ mt7615_mac_update_mib_stats(struct mt7615_phy *phy)
+ {
+@@ -1942,26 +1944,15 @@ void mt7615_pm_wake_work(struct work_struct *work)
+ mphy = dev->phy.mt76;
+
+ if (!mt7615_mcu_set_drv_ctrl(dev)) {
+- struct mt76_dev *mdev = &dev->mt76;
+ int i;
+
+- if (mt76_is_sdio(mdev)) {
+- mt76_worker_schedule(&mdev->sdio.txrx_worker);
+- } else {
+- mt76_for_each_q_rx(mdev, i)
+- napi_schedule(&mdev->napi[i]);
+- mt76_connac_pm_dequeue_skbs(mphy, &dev->pm);
+- mt76_queue_tx_cleanup(dev, mdev->q_mcu[MT_MCUQ_WM],
+- false);
+- }
+-
+- if (test_bit(MT76_STATE_RUNNING, &mphy->state)) {
+- unsigned long timeout;
+-
+- timeout = mt7615_get_macwork_timeout(dev);
++ mt76_for_each_q_rx(&dev->mt76, i)
++ napi_schedule(&dev->mt76.napi[i]);
++ mt76_connac_pm_dequeue_skbs(mphy, &dev->pm);
++ mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_WM], false);
++ if (test_bit(MT76_STATE_RUNNING, &mphy->state))
+ ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
+- timeout);
+- }
++ MT7615_WATCHDOG_TIME);
+ }
+
+ ieee80211_wake_queues(mphy->hw);
+@@ -1996,7 +1987,6 @@ void mt7615_mac_work(struct work_struct *work)
+ {
+ struct mt7615_phy *phy;
+ struct mt76_phy *mphy;
+- unsigned long timeout;
+
+ mphy = (struct mt76_phy *)container_of(work, struct mt76_phy,
+ mac_work.work);
+@@ -2015,9 +2005,8 @@ void mt7615_mac_work(struct work_struct *work)
+ mt7615_mutex_release(phy->dev);
+
+ mt76_tx_status_check(mphy->dev, NULL, false);
+-
+- timeout = mt7615_get_macwork_timeout(phy->dev);
+- ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work, timeout);
++ ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
++ MT7615_WATCHDOG_TIME);
+ }
+
+ void mt7615_tx_token_put(struct mt7615_dev *dev)
+@@ -2098,12 +2087,14 @@ mt7615_dfs_init_radar_specs(struct mt7615_phy *phy)
+ {
+ const struct mt7615_dfs_radar_spec *radar_specs;
+ struct mt7615_dev *dev = phy->dev;
+- int err, i, lpn = 500;
++ int err, i;
+
+ switch (dev->mt76.region) {
+ case NL80211_DFS_FCC:
+ radar_specs = &fcc_radar_specs;
+- lpn = 8;
++ err = mt7615_mcu_set_fcc5_lpn(dev, 8);
++ if (err < 0)
++ return err;
+ break;
+ case NL80211_DFS_ETSI:
+ radar_specs = &etsi_radar_specs;
+@@ -2115,11 +2106,6 @@ mt7615_dfs_init_radar_specs(struct mt7615_phy *phy)
+ return -EINVAL;
+ }
+
+- /* avoid FCC radar detection in non-FCC region */
+- err = mt7615_mcu_set_fcc5_lpn(dev, lpn);
+- if (err < 0)
+- return err;
+-
+ for (i = 0; i < ARRAY_SIZE(radar_specs->radar_pattern); i++) {
+ err = mt7615_mcu_set_radar_th(dev, i,
+ &radar_specs->radar_pattern[i]);
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
+index dada43d6d879..39733b351ac4 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
+@@ -28,7 +28,6 @@ static int mt7615_start(struct ieee80211_hw *hw)
+ {
+ struct mt7615_dev *dev = mt7615_hw_dev(hw);
+ struct mt7615_phy *phy = mt7615_hw_phy(hw);
+- unsigned long timeout;
+ bool running;
+ int ret;
+
+@@ -79,8 +78,8 @@ static int mt7615_start(struct ieee80211_hw *hw)
+
+ set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+
+- timeout = mt7615_get_macwork_timeout(dev);
+- ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work, timeout);
++ ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
++ MT7615_WATCHDOG_TIME);
+
+ if (!running)
+ mt7615_mac_reset_counters(dev);
+@@ -241,6 +240,8 @@ static int mt7615_add_interface(struct ieee80211_hw *hw,
+ }
+
+ ret = mt7615_mcu_add_dev_info(phy, vif, true);
++ if (ret)
++ goto out;
+ out:
+ mt7615_mutex_release(dev);
+
+@@ -351,12 +352,10 @@ int mt7615_set_channel(struct mt7615_phy *phy)
+ mt7615_mutex_release(dev);
+
+ mt76_worker_schedule(&dev->mt76.tx_worker);
+- if (!mt76_testmode_enabled(phy->mt76)) {
+- unsigned long timeout = mt7615_get_macwork_timeout(dev);
+-
++ if (!mt76_testmode_enabled(phy->mt76))
+ ieee80211_queue_delayed_work(phy->mt76->hw,
+- &phy->mt76->mac_work, timeout);
+- }
++ &phy->mt76->mac_work,
++ MT7615_WATCHDOG_TIME);
+
+ return ret;
+ }
+@@ -696,7 +695,7 @@ static void mt7615_sta_rate_tbl_update(struct ieee80211_hw *hw,
+ msta->n_rates = i;
+ if (mt76_connac_pm_ref(phy->mt76, &dev->pm)) {
+ mt7615_mac_set_rates(phy, msta, NULL, msta->rates);
+- mt76_connac_pm_unref(phy->mt76, &dev->pm);
++ mt76_connac_pm_unref(&dev->pm);
+ }
+ spin_unlock_bh(&dev->mt76.lock);
+ }
+@@ -712,7 +711,7 @@ void mt7615_tx_worker(struct mt76_worker *w)
+ }
+
+ mt76_tx_worker_run(&dev->mt76);
+- mt76_connac_pm_unref(&dev->mphy, &dev->pm);
++ mt76_connac_pm_unref(&dev->pm);
+ }
+
+ static void mt7615_tx(struct ieee80211_hw *hw,
+@@ -742,7 +741,7 @@ static void mt7615_tx(struct ieee80211_hw *hw,
+
+ if (mt76_connac_pm_ref(mphy, &dev->pm)) {
+ mt76_tx(mphy, control->sta, wcid, skb);
+- mt76_connac_pm_unref(mphy, &dev->pm);
++ mt76_connac_pm_unref(&dev->pm);
+ return;
+ }
+
+@@ -882,8 +881,7 @@ mt7615_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+
+ mt7615_mutex_acquire(dev);
+
+- /* TSF read */
+- mt76_rmw(dev, reg, MT_LPON_TCR_MODE, MT_LPON_TCR_READ);
++ mt76_set(dev, reg, MT_LPON_TCR_MODE); /* TSF read */
+ tsf.t32[0] = mt76_rr(dev, MT_LPON_UTTR0);
+ tsf.t32[1] = mt76_rr(dev, MT_LPON_UTTR1);
+
+@@ -913,33 +911,7 @@ mt7615_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ mt76_wr(dev, MT_LPON_UTTR0, tsf.t32[0]);
+ mt76_wr(dev, MT_LPON_UTTR1, tsf.t32[1]);
+ /* TSF software overwrite */
+- mt76_rmw(dev, reg, MT_LPON_TCR_MODE, MT_LPON_TCR_WRITE);
+-
+- mt7615_mutex_release(dev);
+-}
+-
+-static void
+-mt7615_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+- s64 timestamp)
+-{
+- struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv;
+- struct mt7615_dev *dev = mt7615_hw_dev(hw);
+- union {
+- u64 t64;
+- u32 t32[2];
+- } tsf = { .t64 = timestamp, };
+- u16 idx = mvif->mt76.omac_idx;
+- u32 reg;
+-
+- idx = idx > HW_BSSID_MAX ? HW_BSSID_0 : idx;
+- reg = idx > 1 ? MT_LPON_TCR2(idx): MT_LPON_TCR0(idx);
+-
+- mt7615_mutex_acquire(dev);
+-
+- mt76_wr(dev, MT_LPON_UTTR0, tsf.t32[0]);
+- mt76_wr(dev, MT_LPON_UTTR1, tsf.t32[1]);
+- /* TSF software adjust*/
+- mt76_rmw(dev, reg, MT_LPON_TCR_MODE, MT_LPON_TCR_ADJUST);
++ mt76_set(dev, reg, MT_LPON_TCR_WRITE);
+
+ mt7615_mutex_release(dev);
+ }
+@@ -1190,7 +1162,7 @@ static void mt7615_sta_set_decap_offload(struct ieee80211_hw *hw,
+ else
+ clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
+
+- mt7615_mcu_set_sta_decap_offload(dev, vif, sta);
++ mt7615_mcu_sta_update_hdr_trans(dev, vif, sta);
+ }
+
+ #ifdef CONFIG_PM
+@@ -1228,7 +1200,6 @@ static int mt7615_resume(struct ieee80211_hw *hw)
+ {
+ struct mt7615_phy *phy = mt7615_hw_phy(hw);
+ struct mt7615_dev *dev = mt7615_hw_dev(hw);
+- unsigned long timeout;
+ bool running;
+
+ mt7615_mutex_acquire(dev);
+@@ -1252,8 +1223,8 @@ static int mt7615_resume(struct ieee80211_hw *hw)
+ mt76_connac_mcu_set_suspend_iter,
+ phy->mt76);
+
+- timeout = mt7615_get_macwork_timeout(dev);
+- ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work, timeout);
++ ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
++ MT7615_WATCHDOG_TIME);
+
+ mt7615_mutex_release(dev);
+
+@@ -1307,7 +1278,6 @@ const struct ieee80211_ops mt7615_ops = {
+ .get_stats = mt7615_get_stats,
+ .get_tsf = mt7615_get_tsf,
+ .set_tsf = mt7615_set_tsf,
+- .offset_tsf = mt7615_offset_tsf,
+ .get_survey = mt76_get_survey,
+ .get_antenna = mt76_get_antenna,
+ .set_antenna = mt7615_set_antenna,
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
+index f8a09692d3e4..257a2c4ddf36 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
+@@ -430,10 +430,6 @@ mt7615_mcu_rx_radar_detected(struct mt7615_dev *dev, struct sk_buff *skb)
+
+ r = (struct mt7615_mcu_rdd_report *)skb->data;
+
+- if (!dev->radar_pattern.n_pulses && !r->long_detected &&
+- !r->constant_prf_detected && !r->staggered_prf_detected)
+- return;
+-
+ if (r->band_idx && dev->mt76.phy2)
+ mphy = dev->mt76.phy2;
+
+@@ -1028,10 +1024,9 @@ mt7615_mcu_wtbl_sta_add(struct mt7615_phy *phy, struct ieee80211_vif *vif,
+ if (IS_ERR(sskb))
+ return PTR_ERR(sskb);
+
+- mt76_connac_mcu_sta_basic_tlv(sskb, vif, sta, enable, true);
++ mt76_connac_mcu_sta_basic_tlv(sskb, vif, sta, enable);
+ if (enable && sta)
+- mt76_connac_mcu_sta_tlv(phy->mt76, sskb, sta, vif, 0,
+- MT76_STA_INFO_STATE_ASSOC);
++ mt76_connac_mcu_sta_tlv(phy->mt76, sskb, sta, vif, 0);
+
+ wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(&dev->mt76, &msta->wcid,
+ WTBL_RESET_AND_SET, NULL,
+@@ -1045,8 +1040,8 @@ mt7615_mcu_wtbl_sta_add(struct mt7615_phy *phy, struct ieee80211_vif *vif,
+ if (sta)
+ mt76_connac_mcu_wtbl_ht_tlv(&dev->mt76, wskb, sta,
+ NULL, wtbl_hdr);
+- mt76_connac_mcu_wtbl_hdr_trans_tlv(wskb, vif, &msta->wcid,
+- NULL, wtbl_hdr);
++ mt76_connac_mcu_wtbl_hdr_trans_tlv(wskb, &msta->wcid, NULL,
++ wtbl_hdr);
+ }
+
+ cmd = enable ? MCU_EXT_CMD_WTBL_UPDATE : MCU_EXT_CMD_STA_REC_UPDATE;
+@@ -1066,26 +1061,6 @@ mt7615_mcu_wtbl_sta_add(struct mt7615_phy *phy, struct ieee80211_vif *vif,
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb, cmd, true);
+ }
+
+-static int
+-mt7615_mcu_wtbl_update_hdr_trans(struct mt7615_dev *dev,
+- struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta)
+-{
+- struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv;
+- struct wtbl_req_hdr *wtbl_hdr;
+- struct sk_buff *skb = NULL;
+-
+- wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(&dev->mt76, &msta->wcid,
+- WTBL_SET, NULL, &skb);
+- if (IS_ERR(wtbl_hdr))
+- return PTR_ERR(wtbl_hdr);
+-
+- mt76_connac_mcu_wtbl_hdr_trans_tlv(skb, vif, &msta->wcid, NULL,
+- wtbl_hdr);
+- return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_EXT_CMD_WTBL_UPDATE,
+- true);
+-}
+-
+ static const struct mt7615_mcu_ops wtbl_update_ops = {
+ .add_beacon_offload = mt7615_mcu_add_beacon_offload,
+ .set_pm_state = mt7615_mcu_ctrl_pm_state,
+@@ -1096,7 +1071,6 @@ static const struct mt7615_mcu_ops wtbl_update_ops = {
+ .sta_add = mt7615_mcu_wtbl_sta_add,
+ .set_drv_ctrl = mt7615_mcu_drv_pmctrl,
+ .set_fw_ctrl = mt7615_mcu_fw_pmctrl,
+- .set_sta_decap_offload = mt7615_mcu_wtbl_update_hdr_trans,
+ };
+
+ static int
+@@ -1158,12 +1132,11 @@ __mt7615_mcu_add_sta(struct mt76_phy *phy, struct ieee80211_vif *vif,
+ .vif = vif,
+ .offload_fw = offload_fw,
+ .enable = enable,
+- .newly = true,
+ .cmd = cmd,
+ };
+
+ info.wcid = sta ? (struct mt76_wcid *)sta->drv_priv : &mvif->sta.wcid;
+- return mt76_connac_mcu_sta_cmd(phy, &info);
++ return mt76_connac_mcu_add_sta_cmd(phy, &info);
+ }
+
+ static int
+@@ -1174,18 +1147,6 @@ mt7615_mcu_add_sta(struct mt7615_phy *phy, struct ieee80211_vif *vif,
+ MCU_EXT_CMD_STA_REC_UPDATE, false);
+ }
+
+-static int
+-mt7615_mcu_sta_update_hdr_trans(struct mt7615_dev *dev,
+- struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta)
+-{
+- struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv;
+-
+- return mt76_connac_mcu_sta_update_hdr_trans(&dev->mt76,
+- vif, &msta->wcid,
+- MCU_EXT_CMD_STA_REC_UPDATE);
+-}
+-
+ static const struct mt7615_mcu_ops sta_update_ops = {
+ .add_beacon_offload = mt7615_mcu_add_beacon_offload,
+ .set_pm_state = mt7615_mcu_ctrl_pm_state,
+@@ -1196,9 +1157,27 @@ static const struct mt7615_mcu_ops sta_update_ops = {
+ .sta_add = mt7615_mcu_add_sta,
+ .set_drv_ctrl = mt7615_mcu_drv_pmctrl,
+ .set_fw_ctrl = mt7615_mcu_fw_pmctrl,
+- .set_sta_decap_offload = mt7615_mcu_sta_update_hdr_trans,
+ };
+
++int mt7615_mcu_sta_update_hdr_trans(struct mt7615_dev *dev,
++ struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta)
++{
++ struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv;
++ struct wtbl_req_hdr *wtbl_hdr;
++ struct sk_buff *skb = NULL;
++
++ wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(&dev->mt76, &msta->wcid,
++ WTBL_SET, NULL, &skb);
++ if (IS_ERR(wtbl_hdr))
++ return PTR_ERR(wtbl_hdr);
++
++ mt76_connac_mcu_wtbl_hdr_trans_tlv(skb, &msta->wcid, NULL, wtbl_hdr);
++
++ return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_EXT_CMD_WTBL_UPDATE,
++ true);
++}
++
+ static int
+ mt7615_mcu_uni_ctrl_pm_state(struct mt7615_dev *dev, int band, int state)
+ {
+@@ -1364,18 +1343,6 @@ mt7615_mcu_uni_rx_ba(struct mt7615_dev *dev,
+ MCU_UNI_CMD_STA_REC_UPDATE, true);
+ }
+
+-static int
+-mt7615_mcu_sta_uni_update_hdr_trans(struct mt7615_dev *dev,
+- struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta)
+-{
+- struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv;
+-
+- return mt76_connac_mcu_sta_update_hdr_trans(&dev->mt76,
+- vif, &msta->wcid,
+- MCU_UNI_CMD_STA_REC_UPDATE);
+-}
+-
+ static const struct mt7615_mcu_ops uni_update_ops = {
+ .add_beacon_offload = mt7615_mcu_uni_add_beacon_offload,
+ .set_pm_state = mt7615_mcu_uni_ctrl_pm_state,
+@@ -1386,7 +1353,6 @@ static const struct mt7615_mcu_ops uni_update_ops = {
+ .sta_add = mt7615_mcu_uni_add_sta,
+ .set_drv_ctrl = mt7615_mcu_lp_drv_pmctrl,
+ .set_fw_ctrl = mt7615_mcu_fw_pmctrl,
+- .set_sta_decap_offload = mt7615_mcu_sta_uni_update_hdr_trans,
+ };
+
+ int mt7615_mcu_restart(struct mt76_dev *dev)
+@@ -2361,12 +2327,14 @@ int mt7615_mcu_set_chan_info(struct mt7615_phy *phy, int cmd)
+ return mt76_mcu_send_msg(&dev->mt76, cmd, &req, sizeof(req), true);
+ }
+
+-int mt7615_mcu_get_temperature(struct mt7615_dev *dev)
++int mt7615_mcu_get_temperature(struct mt7615_dev *dev, int index)
+ {
+ struct {
+ u8 action;
+ u8 rsv[3];
+- } req = {};
++ } req = {
++ .action = index,
++ };
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_GET_TEMP, &req,
+ sizeof(req), true);
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7615/mmio.c
+index 71719c787511..202ea235415e 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7615/mmio.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7615/mmio.c
+@@ -229,7 +229,7 @@ int mt7615_mmio_probe(struct device *pdev, void __iomem *mem_base,
+ GFP_KERNEL);
+ if (!bus_ops) {
+ ret = -ENOMEM;
+- goto err_free_dev;
++ goto error;
+ }
+
+ bus_ops->rr = mt7615_rr;
+@@ -242,20 +242,17 @@ int mt7615_mmio_probe(struct device *pdev, void __iomem *mem_base,
+ ret = devm_request_irq(mdev->dev, irq, mt7615_irq_handler,
+ IRQF_SHARED, KBUILD_MODNAME, dev);
+ if (ret)
+- goto err_free_dev;
++ goto error;
+
+ if (is_mt7663(mdev))
+ mt76_wr(dev, MT_PCIE_IRQ_ENABLE, 1);
+
+ ret = mt7615_register_device(dev);
+ if (ret)
+- goto err_free_irq;
++ goto error;
+
+ return 0;
+-
+-err_free_irq:
+- devm_free_irq(pdev, irq, dev);
+-err_free_dev:
++error:
+ mt76_free_device(&dev->mt76);
+
+ return ret;
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h
+index d0c64a9b09cf..989f05ed4377 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h
++++ b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h
+@@ -20,6 +20,7 @@
+ MT7615_MAX_INTERFACES)
+
+ #define MT7615_PM_TIMEOUT (HZ / 12)
++#define MT7615_WATCHDOG_TIME (HZ / 10)
+ #define MT7615_HW_SCAN_TIMEOUT (HZ / 10)
+ #define MT7615_RESET_TIMEOUT (30 * HZ)
+ #define MT7615_RATE_RETRY 2
+@@ -201,7 +202,6 @@ struct mt7615_phy {
+ #define mt7615_mcu_set_pm(dev, ...) (dev)->mcu_ops->set_pm_state((dev), __VA_ARGS__)
+ #define mt7615_mcu_set_drv_ctrl(dev) (dev)->mcu_ops->set_drv_ctrl((dev))
+ #define mt7615_mcu_set_fw_ctrl(dev) (dev)->mcu_ops->set_fw_ctrl((dev))
+-#define mt7615_mcu_set_sta_decap_offload(dev, ...) (dev)->mcu_ops->set_sta_decap_offload((dev), __VA_ARGS__)
+ struct mt7615_mcu_ops {
+ int (*add_tx_ba)(struct mt7615_dev *dev,
+ struct ieee80211_ampdu_params *params,
+@@ -221,9 +221,6 @@ struct mt7615_mcu_ops {
+ int (*set_pm_state)(struct mt7615_dev *dev, int band, int state);
+ int (*set_drv_ctrl)(struct mt7615_dev *dev);
+ int (*set_fw_ctrl)(struct mt7615_dev *dev);
+- int (*set_sta_decap_offload)(struct mt7615_dev *dev,
+- struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta);
+ };
+
+ struct mt7615_dev {
+@@ -359,7 +356,6 @@ static inline int mt7622_wmac_init(struct mt7615_dev *dev)
+ }
+ #endif
+
+-int mt7615_thermal_init(struct mt7615_dev *dev);
+ int mt7615_mmio_probe(struct device *pdev, void __iomem *mem_base,
+ int irq, const u32 *map);
+ u32 mt7615_reg_map(struct mt7615_dev *dev, u32 addr);
+@@ -460,12 +456,6 @@ static inline u32 mt7615_tx_mcu_int_mask(struct mt7615_dev *dev)
+ return MT_INT_TX_DONE(dev->mt76.q_mcu[MT_MCUQ_WM]->hw_idx);
+ }
+
+-static inline unsigned long
+-mt7615_get_macwork_timeout(struct mt7615_dev *dev)
+-{
+- return dev->pm.enable ? HZ / 3 : HZ / 10;
+-}
+-
+ void mt7615_dma_reset(struct mt7615_dev *dev);
+ void mt7615_scan_work(struct work_struct *work);
+ void mt7615_roc_work(struct work_struct *work);
+@@ -476,7 +466,7 @@ int mt7615_set_channel(struct mt7615_phy *phy);
+ void mt7615_init_work(struct mt7615_dev *dev);
+
+ int mt7615_mcu_restart(struct mt76_dev *dev);
+-void mt7615_update_channel(struct mt76_phy *mphy);
++void mt7615_update_channel(struct mt76_dev *mdev);
+ bool mt7615_mac_wtbl_update(struct mt7615_dev *dev, int idx, u32 mask);
+ void mt7615_mac_reset_counters(struct mt7615_dev *dev);
+ void mt7615_mac_cca_stats_reset(struct mt7615_phy *phy);
+@@ -504,7 +494,7 @@ u32 mt7615_rf_rr(struct mt7615_dev *dev, u32 wf, u32 reg);
+ int mt7615_rf_wr(struct mt7615_dev *dev, u32 wf, u32 reg, u32 val);
+ int mt7615_mcu_set_dbdc(struct mt7615_dev *dev);
+ int mt7615_mcu_set_eeprom(struct mt7615_dev *dev);
+-int mt7615_mcu_get_temperature(struct mt7615_dev *dev);
++int mt7615_mcu_get_temperature(struct mt7615_dev *dev, int index);
+ int mt7615_mcu_set_tx_power(struct mt7615_phy *phy);
+ void mt7615_mcu_exit(struct mt7615_dev *dev);
+ void mt7615_mcu_fill_msg(struct mt7615_dev *dev, struct sk_buff *skb,
+@@ -528,6 +518,9 @@ void mt7615_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ void mt7615_mac_work(struct work_struct *work);
+ void mt7615_txp_skb_unmap(struct mt76_dev *dev,
+ struct mt76_txwi_cache *txwi);
++int mt7615_mcu_sta_update_hdr_trans(struct mt7615_dev *dev,
++ struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta);
+ int mt7615_mcu_set_rx_hdr_trans_blacklist(struct mt7615_dev *dev);
+ int mt7615_mcu_set_fcc5_lpn(struct mt7615_dev *dev, int val);
+ int mt7615_mcu_set_pulse_th(struct mt7615_dev *dev,
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c b/drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c
+index a2465b49ecd0..ec8ec1a2033f 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c
+@@ -98,7 +98,7 @@ mt7615_led_set_config(struct led_classdev *led_cdev,
+ addr = mt7615_reg_map(dev, MT_LED_CTRL);
+ mt76_wr(dev, addr, val);
+
+- mt76_connac_pm_unref(&dev->mphy, &dev->pm);
++ mt76_connac_pm_unref(&dev->pm);
+ }
+
+ static int
+@@ -147,12 +147,8 @@ int mt7615_register_device(struct mt7615_dev *dev)
+ if (ret)
+ return ret;
+
+- ret = mt76_register_device(&dev->mt76, true, mt76_rates,
+- ARRAY_SIZE(mt76_rates));
+- if (ret)
+- return ret;
+-
+- ret = mt7615_thermal_init(dev);
++ ret = mt76_register_device(&dev->mt76, true, mt7615_rates,
++ ARRAY_SIZE(mt7615_rates));
+ if (ret)
+ return ret;
+
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c
+index da87c02a73eb..cc278d8cb888 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c
+@@ -268,7 +268,6 @@ void mt7615_mac_reset_work(struct work_struct *work)
+ struct mt7615_phy *phy2;
+ struct mt76_phy *ext_phy;
+ struct mt7615_dev *dev;
+- unsigned long timeout;
+
+ dev = container_of(work, struct mt7615_dev, reset_work);
+ ext_phy = dev->mt76.phy2;
+@@ -346,11 +345,11 @@ void mt7615_mac_reset_work(struct work_struct *work)
+
+ mt7615_mutex_release(dev);
+
+- timeout = mt7615_get_macwork_timeout(dev);
+ ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work,
+- timeout);
++ MT7615_WATCHDOG_TIME);
+ if (phy2)
+ ieee80211_queue_delayed_work(ext_phy->hw,
+- &phy2->mt76->mac_work, timeout);
++ &phy2->mt76->mac_work,
++ MT7615_WATCHDOG_TIME);
+
+ }
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/regs.h b/drivers/net/wireless/mediatek/mt76/mt7615/regs.h
+index 6712ad9faeaa..63c081bb04d0 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7615/regs.h
++++ b/drivers/net/wireless/mediatek/mt76/mt7615/regs.h
+@@ -463,9 +463,7 @@ enum mt7615_reg_base {
+ #define MT_LPON_TCR0(_n) MT_LPON(0x010 + ((_n) * 4))
+ #define MT_LPON_TCR2(_n) MT_LPON(0x0f8 + ((_n) - 2) * 4)
+ #define MT_LPON_TCR_MODE GENMASK(1, 0)
+-#define MT_LPON_TCR_READ GENMASK(1, 0)
+ #define MT_LPON_TCR_WRITE BIT(0)
+-#define MT_LPON_TCR_ADJUST BIT(1)
+
+ #define MT_LPON_UTTR0 MT_LPON(0x018)
+ #define MT_LPON_UTTR1 MT_LPON(0x01c)
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/sdio.h b/drivers/net/wireless/mediatek/mt76/mt7615/sdio.h
+index 03877d89e152..05180971de84 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7615/sdio.h
++++ b/drivers/net/wireless/mediatek/mt76/mt7615/sdio.h
+@@ -1,4 +1,4 @@
+-/* SPDX-License-Identifier: ISC */
++// SPDX-License-Identifier: ISC
+ /* Copyright (C) 2020 MediaTek Inc.
+ *
+ * Author: Sean Wang <sean.wang@mediatek.com>
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/sdio_mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/sdio_mcu.c
+index 45c1cd3b9f49..d1be78b0711c 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7615/sdio_mcu.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7615/sdio_mcu.c
+@@ -55,7 +55,6 @@ static int __mt7663s_mcu_drv_pmctrl(struct mt7615_dev *dev)
+ {
+ struct sdio_func *func = dev->mt76.sdio.func;
+ struct mt76_phy *mphy = &dev->mt76.phy;
+- struct mt76_connac_pm *pm = &dev->pm;
+ u32 status;
+ int ret;
+
+@@ -67,45 +66,37 @@ static int __mt7663s_mcu_drv_pmctrl(struct mt7615_dev *dev)
+ status & WHLPCR_IS_DRIVER_OWN, 2000, 1000000);
+ if (ret < 0) {
+ dev_err(dev->mt76.dev, "Cannot get ownership from device");
+- } else {
+- clear_bit(MT76_STATE_PM, &mphy->state);
++ set_bit(MT76_STATE_PM, &mphy->state);
++ sdio_release_host(func);
+
+- pm->stats.last_wake_event = jiffies;
+- pm->stats.doze_time += pm->stats.last_wake_event -
+- pm->stats.last_doze_event;
++ return ret;
+ }
++
+ sdio_release_host(func);
++ dev->pm.last_activity = jiffies;
+
+- return ret;
++ return 0;
+ }
+
+ static int mt7663s_mcu_drv_pmctrl(struct mt7615_dev *dev)
+ {
+ struct mt76_phy *mphy = &dev->mt76.phy;
+- int ret = 0;
+-
+- mutex_lock(&dev->pm.mutex);
+
+- if (test_bit(MT76_STATE_PM, &mphy->state))
+- ret = __mt7663s_mcu_drv_pmctrl(dev);
++ if (test_and_clear_bit(MT76_STATE_PM, &mphy->state))
++ return __mt7663s_mcu_drv_pmctrl(dev);
+
+- mutex_unlock(&dev->pm.mutex);
+-
+- return ret;
++ return 0;
+ }
+
+ static int mt7663s_mcu_fw_pmctrl(struct mt7615_dev *dev)
+ {
+ struct sdio_func *func = dev->mt76.sdio.func;
+ struct mt76_phy *mphy = &dev->mt76.phy;
+- struct mt76_connac_pm *pm = &dev->pm;
+- int ret = 0;
+ u32 status;
++ int ret;
+
+- mutex_lock(&pm->mutex);
+-
+- if (mt76_connac_skip_fw_pmctrl(mphy, pm))
+- goto out;
++ if (test_and_set_bit(MT76_STATE_PM, &mphy->state))
++ return 0;
+
+ sdio_claim_host(func);
+
+@@ -116,15 +107,9 @@ static int mt7663s_mcu_fw_pmctrl(struct mt7615_dev *dev)
+ if (ret < 0) {
+ dev_err(dev->mt76.dev, "Cannot set ownership to device");
+ clear_bit(MT76_STATE_PM, &mphy->state);
+- } else {
+- pm->stats.last_doze_event = jiffies;
+- pm->stats.awake_time += pm->stats.last_doze_event -
+- pm->stats.last_wake_event;
+ }
+
+ sdio_release_host(func);
+-out:
+- mutex_unlock(&pm->mutex);
+
+ return ret;
+ }
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/sdio_txrx.c b/drivers/net/wireless/mediatek/mt76/mt7615/sdio_txrx.c
+index 04f4c89b7499..4393dd21ebbb 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7615/sdio_txrx.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7615/sdio_txrx.c
+@@ -283,15 +283,9 @@ void mt7663s_txrx_worker(struct mt76_worker *w)
+ {
+ struct mt76_sdio *sdio = container_of(w, struct mt76_sdio,
+ txrx_worker);
+- struct mt76_dev *mdev = container_of(sdio, struct mt76_dev, sdio);
+- struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76);
++ struct mt76_dev *dev = container_of(sdio, struct mt76_dev, sdio);
+ int i, nframes, ret;
+
+- if (!mt76_connac_pm_ref(&dev->mphy, &dev->pm)) {
+- queue_work(mdev->wq, &dev->pm.wake_work);
+- return;
+- }
+-
+ /* disable interrupt */
+ sdio_claim_host(sdio->func);
+ sdio_writel(sdio->func, WHLPCR_INT_EN_CLR, MCR_WHLPCR, NULL);
+@@ -301,16 +295,16 @@ void mt7663s_txrx_worker(struct mt76_worker *w)
+
+ /* tx */
+ for (i = 0; i <= MT_TXQ_PSD; i++) {
+- ret = mt7663s_tx_run_queue(mdev, mdev->phy.q_tx[i]);
++ ret = mt7663s_tx_run_queue(dev, dev->phy.q_tx[i]);
+ if (ret > 0)
+ nframes += ret;
+ }
+- ret = mt7663s_tx_run_queue(mdev, mdev->q_mcu[MT_MCUQ_WM]);
++ ret = mt7663s_tx_run_queue(dev, dev->q_mcu[MT_MCUQ_WM]);
+ if (ret > 0)
+ nframes += ret;
+
+ /* rx */
+- ret = mt7663s_rx_handler(mdev);
++ ret = mt7663s_rx_handler(dev);
+ if (ret > 0)
+ nframes += ret;
+ } while (nframes > 0);
+@@ -318,8 +312,6 @@ void mt7663s_txrx_worker(struct mt76_worker *w)
+ /* enable interrupt */
+ sdio_writel(sdio->func, WHLPCR_INT_EN_SET, MCR_WHLPCR, NULL);
+ sdio_release_host(sdio->func);
+-
+- mt76_connac_pm_unref(&dev->mphy, &dev->pm);
+ }
+
+ void mt7663s_sdio_irq(struct sdio_func *func)
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/soc.c b/drivers/net/wireless/mediatek/mt76/mt7615/soc.c
+index f13d1b418742..be9a69fe1b38 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7615/soc.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7615/soc.c
+@@ -31,6 +31,7 @@ int mt7622_wmac_init(struct mt7615_dev *dev)
+
+ static int mt7622_wmac_probe(struct platform_device *pdev)
+ {
++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ void __iomem *mem_base;
+ int irq;
+
+@@ -38,7 +39,7 @@ static int mt7622_wmac_probe(struct platform_device *pdev)
+ if (irq < 0)
+ return irq;
+
+- mem_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
++ mem_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mem_base))
+ return PTR_ERR(mem_base);
+
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c b/drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c
+index 996d48cca18a..7010101f6b14 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c
+@@ -123,7 +123,7 @@ static int mt7663_usb_sdio_set_rates(struct mt7615_dev *dev,
+ idx = idx > HW_BSSID_MAX ? HW_BSSID_0 : idx;
+ addr = idx > 1 ? MT_LPON_TCR2(idx): MT_LPON_TCR0(idx);
+
+- mt76_rmw(dev, addr, MT_LPON_TCR_MODE, MT_LPON_TCR_READ); /* TSF read */
++ mt76_set(dev, addr, MT_LPON_TCR_MODE); /* TSF read */
+ val = mt76_rr(dev, MT_LPON_UTTR0);
+ sta->rate_set_tsf = (val & ~BIT(0)) | rate->rateset;
+
+@@ -324,8 +324,8 @@ int mt7663_usb_sdio_register_device(struct mt7615_dev *dev)
+ hw->max_tx_fragments = 1;
+ }
+
+- err = mt76_register_device(&dev->mt76, true, mt76_rates,
+- ARRAY_SIZE(mt76_rates));
++ err = mt76_register_device(&dev->mt76, true, mt7615_rates,
++ ARRAY_SIZE(mt7615_rates));
+ if (err < 0)
+ return err;
+
+diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h
+index f49d97d0a1c5..75223b6e1c87 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h
++++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h
+@@ -46,7 +46,6 @@ enum {
+
+ struct mt76_connac_pm {
+ bool enable;
+- bool ds_enable;
+ bool suspended;
+
+ spinlock_t txq_lock;
+@@ -119,15 +118,11 @@ mt76_connac_pm_ref(struct mt76_phy *phy, struct mt76_connac_pm *pm)
+ }
+
+ static inline void
+-mt76_connac_pm_unref(struct mt76_phy *phy, struct mt76_connac_pm *pm)
++mt76_connac_pm_unref(struct mt76_connac_pm *pm)
+ {
+ spin_lock_bh(&pm->wake.lock);
+-
++ pm->wake.count--;
+ pm->last_activity = jiffies;
+- if (--pm->wake.count == 0 &&
+- test_bit(MT76_STATE_MCU_RUNNING, &phy->state))
+- mt76_connac_power_save_sched(phy, pm);
+-
+ spin_unlock_bh(&pm->wake.lock);
+ }
+
+diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c
+index af43bcb54578..5f2705fbd680 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c
++++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c
+@@ -10,7 +10,7 @@ int mt76_connac_pm_wake(struct mt76_phy *phy, struct mt76_connac_pm *pm)
+ if (!pm->enable)
+ return 0;
+
+- if (mt76_is_usb(dev))
++ if (!mt76_is_mmio(dev))
+ return 0;
+
+ cancel_delayed_work_sync(&pm->ps_work);
+@@ -37,7 +37,7 @@ void mt76_connac_power_save_sched(struct mt76_phy *phy,
+ {
+ struct mt76_dev *dev = phy->dev;
+
+- if (mt76_is_usb(dev))
++ if (!mt76_is_mmio(dev))
+ return;
+
+ if (!pm->enable)
+diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
+index 5c3a81e5f559..e5721603586f 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
++++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
+@@ -304,7 +304,7 @@ EXPORT_SYMBOL_GPL(mt76_connac_mcu_alloc_wtbl_req);
+ void mt76_connac_mcu_sta_basic_tlv(struct sk_buff *skb,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+- bool enable, bool newly)
++ bool enable)
+ {
+ struct sta_rec_basic *basic;
+ struct tlv *tlv;
+@@ -316,8 +316,7 @@ void mt76_connac_mcu_sta_basic_tlv(struct sk_buff *skb,
+ basic->extra_info = cpu_to_le16(EXTRA_INFO_VER);
+
+ if (enable) {
+- if (newly)
+- basic->extra_info |= cpu_to_le16(EXTRA_INFO_NEW);
++ basic->extra_info |= cpu_to_le16(EXTRA_INFO_NEW);
+ basic->conn_state = CONN_STATE_PORT_SECURE;
+ } else {
+ basic->conn_state = CONN_STATE_DISCONNECT;
+@@ -394,7 +393,6 @@ mt76_connac_mcu_sta_uapsd(struct sk_buff *skb, struct ieee80211_vif *vif,
+ }
+
+ void mt76_connac_mcu_wtbl_hdr_trans_tlv(struct sk_buff *skb,
+- struct ieee80211_vif *vif,
+ struct mt76_wcid *wcid,
+ void *sta_wtbl, void *wtbl_tlv)
+ {
+@@ -406,46 +404,9 @@ void mt76_connac_mcu_wtbl_hdr_trans_tlv(struct sk_buff *skb,
+ wtbl_tlv, sta_wtbl);
+ htr = (struct wtbl_hdr_trans *)tlv;
+ htr->no_rx_trans = !test_bit(MT_WCID_FLAG_HDR_TRANS, &wcid->flags);
+-
+- if (vif->type == NL80211_IFTYPE_STATION)
+- htr->to_ds = true;
+- else
+- htr->from_ds = true;
+-
+- if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) {
+- htr->to_ds = true;
+- htr->from_ds = true;
+- }
+ }
+ EXPORT_SYMBOL_GPL(mt76_connac_mcu_wtbl_hdr_trans_tlv);
+
+-int mt76_connac_mcu_sta_update_hdr_trans(struct mt76_dev *dev,
+- struct ieee80211_vif *vif,
+- struct mt76_wcid *wcid, int cmd)
+-{
+- struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+- struct wtbl_req_hdr *wtbl_hdr;
+- struct tlv *sta_wtbl;
+- struct sk_buff *skb;
+-
+- skb = mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid);
+- if (IS_ERR(skb))
+- return PTR_ERR(skb);
+-
+- sta_wtbl = mt76_connac_mcu_add_tlv(skb, STA_REC_WTBL,
+- sizeof(struct tlv));
+-
+- wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(dev, wcid, WTBL_SET,
+- sta_wtbl, &skb);
+- if (IS_ERR(wtbl_hdr))
+- return PTR_ERR(wtbl_hdr);
+-
+- mt76_connac_mcu_wtbl_hdr_trans_tlv(skb, vif, wcid, sta_wtbl, wtbl_hdr);
+-
+- return mt76_mcu_skb_send_msg(dev, skb, cmd, true);
+-}
+-EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_update_hdr_trans);
+-
+ void mt76_connac_mcu_wtbl_generic_tlv(struct mt76_dev *dev,
+ struct sk_buff *skb,
+ struct ieee80211_vif *vif,
+@@ -710,7 +671,7 @@ mt76_connac_get_phy_mode_v2(struct mt76_phy *mphy, struct ieee80211_vif *vif,
+ void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb,
+ struct ieee80211_sta *sta,
+ struct ieee80211_vif *vif,
+- u8 rcpi, u8 sta_state)
++ u8 rcpi)
+ {
+ struct cfg80211_chan_def *chandef = &mphy->chandef;
+ enum nl80211_band band = chandef->chan->band;
+@@ -775,7 +736,7 @@ void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb,
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_STATE, sizeof(*state));
+ state = (struct sta_rec_state *)tlv;
+- state->state = sta_state;
++ state->state = 2;
+
+ if (sta->vht_cap.vht_supported) {
+ state->vht_opmode = sta->bandwidth;
+@@ -867,8 +828,8 @@ void mt76_connac_mcu_wtbl_ht_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+ }
+ EXPORT_SYMBOL_GPL(mt76_connac_mcu_wtbl_ht_tlv);
+
+-int mt76_connac_mcu_sta_cmd(struct mt76_phy *phy,
+- struct mt76_sta_cmd_info *info)
++int mt76_connac_mcu_add_sta_cmd(struct mt76_phy *phy,
++ struct mt76_sta_cmd_info *info)
+ {
+ struct mt76_vif *mvif = (struct mt76_vif *)info->vif->drv_priv;
+ struct mt76_dev *dev = phy->dev;
+@@ -882,11 +843,10 @@ int mt76_connac_mcu_sta_cmd(struct mt76_phy *phy,
+
+ if (info->sta || !info->offload_fw)
+ mt76_connac_mcu_sta_basic_tlv(skb, info->vif, info->sta,
+- info->enable, info->newly);
++ info->enable);
+ if (info->sta && info->enable)
+ mt76_connac_mcu_sta_tlv(phy, skb, info->sta,
+- info->vif, info->rcpi,
+- info->state);
++ info->vif, info->rcpi);
+
+ sta_wtbl = mt76_connac_mcu_add_tlv(skb, STA_REC_WTBL,
+ sizeof(struct tlv));
+@@ -901,8 +861,6 @@ int mt76_connac_mcu_sta_cmd(struct mt76_phy *phy,
+ mt76_connac_mcu_wtbl_generic_tlv(dev, skb, info->vif,
+ info->sta, sta_wtbl,
+ wtbl_hdr);
+- mt76_connac_mcu_wtbl_hdr_trans_tlv(skb, info->vif, info->wcid,
+- sta_wtbl, wtbl_hdr);
+ if (info->sta)
+ mt76_connac_mcu_wtbl_ht_tlv(dev, skb, info->sta,
+ sta_wtbl, wtbl_hdr);
+@@ -910,7 +868,7 @@ int mt76_connac_mcu_sta_cmd(struct mt76_phy *phy,
+
+ return mt76_mcu_skb_send_msg(dev, skb, info->cmd, true);
+ }
+-EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_cmd);
++EXPORT_SYMBOL_GPL(mt76_connac_mcu_add_sta_cmd);
+
+ void mt76_connac_mcu_wtbl_ba_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+ struct ieee80211_ampdu_params *params,
+@@ -939,10 +897,8 @@ void mt76_connac_mcu_wtbl_ba_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+ ba->rst_ba_sb = 1;
+ }
+
+- if (is_mt7921(dev)) {
+- ba->ba_winsize = enable ? cpu_to_le16(params->buf_size) : 0;
++ if (is_mt7921(dev))
+ return;
+- }
+
+ if (enable && tx) {
+ u8 ba_range[] = { 4, 8, 12, 24, 36, 48, 54, 64 };
+@@ -1317,7 +1273,6 @@ int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy,
+ u8 pad[3];
+ } __packed hdr;
+ struct bss_info_uni_he he;
+- struct bss_info_uni_bss_color bss_color;
+ } he_req = {
+ .hdr = {
+ .bss_idx = mvif->idx,
+@@ -1326,21 +1281,8 @@ int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy,
+ .tag = cpu_to_le16(UNI_BSS_INFO_HE_BASIC),
+ .len = cpu_to_le16(sizeof(struct bss_info_uni_he)),
+ },
+- .bss_color = {
+- .tag = cpu_to_le16(UNI_BSS_INFO_BSS_COLOR),
+- .len = cpu_to_le16(sizeof(struct bss_info_uni_bss_color)),
+- .enable = 0,
+- .bss_color = 0,
+- },
+ };
+
+- if (enable) {
+- he_req.bss_color.enable =
+- vif->bss_conf.he_bss_color.enabled;
+- he_req.bss_color.bss_color =
+- vif->bss_conf.he_bss_color.color;
+- }
+-
+ mt76_connac_mcu_uni_bss_he_tlv(phy, vif,
+ (struct tlv *)&he_req.he);
+ err = mt76_mcu_send_msg(mdev, MCU_UNI_CMD_BSS_INFO_UPDATE,
+@@ -1523,16 +1465,14 @@ int mt76_connac_mcu_sched_scan_req(struct mt76_phy *phy,
+ req->version = 1;
+ req->seq_num = mvif->scan_seq_num | ext_phy << 7;
+
+- if (sreq->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+- u8 *addr = is_mt7663(phy->dev) ? req->mt7663.random_mac
+- : req->mt7921.random_mac;
+-
+- req->scan_func = 1;
+- get_random_mask_addr(addr, sreq->mac_addr,
++ if (is_mt7663(phy->dev) &&
++ (sreq->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) {
++ get_random_mask_addr(req->mt7663.random_mac, sreq->mac_addr,
+ sreq->mac_addr_mask);
+- }
+- if (is_mt7921(phy->dev))
++ req->scan_func = 1;
++ } else if (is_mt7921(phy->dev)) {
+ req->mt7921.bss_idx = mvif->idx;
++ }
+
+ req->ssids_num = sreq->n_ssids;
+ for (i = 0; i < req->ssids_num; i++) {
+@@ -1618,26 +1558,6 @@ int mt76_connac_mcu_set_deep_sleep(struct mt76_dev *dev, bool enable)
+ }
+ EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_deep_sleep);
+
+-int mt76_connac_sta_state_dp(struct mt76_dev *dev,
+- enum ieee80211_sta_state old_state,
+- enum ieee80211_sta_state new_state)
+-{
+- if ((old_state == IEEE80211_STA_ASSOC &&
+- new_state == IEEE80211_STA_AUTHORIZED) ||
+- (old_state == IEEE80211_STA_NONE &&
+- new_state == IEEE80211_STA_NOTEXIST))
+- mt76_connac_mcu_set_deep_sleep(dev, true);
+-
+- if ((old_state == IEEE80211_STA_NOTEXIST &&
+- new_state == IEEE80211_STA_NONE) ||
+- (old_state == IEEE80211_STA_AUTHORIZED &&
+- new_state == IEEE80211_STA_ASSOC))
+- mt76_connac_mcu_set_deep_sleep(dev, false);
+-
+- return 0;
+-}
+-EXPORT_SYMBOL_GPL(mt76_connac_sta_state_dp);
+-
+ void mt76_connac_mcu_coredump_event(struct mt76_dev *dev, struct sk_buff *skb,
+ struct mt76_connac_coredump *coredump)
+ {
+@@ -1652,60 +1572,6 @@ void mt76_connac_mcu_coredump_event(struct mt76_dev *dev, struct sk_buff *skb,
+ }
+ EXPORT_SYMBOL_GPL(mt76_connac_mcu_coredump_event);
+
+-int mt76_connac_mcu_get_nic_capability(struct mt76_phy *phy)
+-{
+- struct mt76_connac_cap_hdr {
+- __le16 n_element;
+- u8 rsv[2];
+- } __packed * hdr;
+- struct sk_buff *skb;
+- int ret, i;
+-
+- ret = mt76_mcu_send_and_get_msg(phy->dev, MCU_CMD_GET_NIC_CAPAB, NULL,
+- 0, true, &skb);
+- if (ret)
+- return ret;
+-
+- hdr = (struct mt76_connac_cap_hdr *)skb->data;
+- if (skb->len < sizeof(*hdr)) {
+- ret = -EINVAL;
+- goto out;
+- }
+-
+- skb_pull(skb, sizeof(*hdr));
+-
+- for (i = 0; i < le16_to_cpu(hdr->n_element); i++) {
+- struct tlv_hdr {
+- __le32 type;
+- __le32 len;
+- } __packed * tlv = (struct tlv_hdr *)skb->data;
+- int len;
+-
+- if (skb->len < sizeof(*tlv))
+- break;
+-
+- skb_pull(skb, sizeof(*tlv));
+-
+- len = le32_to_cpu(tlv->len);
+- if (skb->len < len)
+- break;
+-
+- switch (le32_to_cpu(tlv->type)) {
+- case MT_NIC_CAP_6G:
+- phy->cap.has_6ghz = skb->data[0];
+- break;
+- default:
+- break;
+- }
+- skb_pull(skb, len);
+- }
+-out:
+- dev_kfree_skb(skb);
+-
+- return ret;
+-}
+-EXPORT_SYMBOL_GPL(mt76_connac_mcu_get_nic_capability);
+-
+ static void
+ mt76_connac_mcu_build_sku(struct mt76_dev *dev, s8 *sku,
+ struct mt76_power_limits *limits,
+@@ -1768,15 +1634,12 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
+ 142, 144, 149, 151, 153, 155, 157,
+ 159, 161, 165
+ };
+- int i, n_chan, batch_size, idx = 0, tx_power, last_ch;
+ struct mt76_connac_sku_tlv sku_tlbv;
++ int i, n_chan, batch_size, idx = 0;
+ struct mt76_power_limits limits;
+ const u8 *ch_list;
+
+ sku_len = is_mt7921(dev) ? sizeof(sku_tlbv) : sizeof(sku_tlbv) - 92;
+- tx_power = 2 * phy->hw->conf.power_level;
+- if (!tx_power)
+- tx_power = 127;
+
+ if (band == NL80211_BAND_2GHZ) {
+ n_chan = ARRAY_SIZE(chan_list_2ghz);
+@@ -1787,48 +1650,39 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
+ }
+ batch_size = DIV_ROUND_UP(n_chan, batch_len);
+
+- if (!phy->cap.has_5ghz)
+- last_ch = chan_list_2ghz[n_chan - 1];
+- else
+- last_ch = chan_list_5ghz[n_chan - 1];
+-
+ for (i = 0; i < batch_size; i++) {
++ bool last_msg = i == batch_size - 1;
++ int num_ch = last_msg ? n_chan % batch_len : batch_len;
+ struct mt76_connac_tx_power_limit_tlv tx_power_tlv = {
+ .band = band == NL80211_BAND_2GHZ ? 1 : 2,
++ .n_chan = num_ch,
++ .last_msg = last_msg,
+ };
+- int j, err, msg_len, num_ch;
+ struct sk_buff *skb;
++ int j, err, msg_len;
+
+- num_ch = i == batch_size - 1 ? n_chan % batch_len : batch_len;
+ msg_len = sizeof(tx_power_tlv) + num_ch * sizeof(sku_tlbv);
+ skb = mt76_mcu_msg_alloc(dev, NULL, msg_len);
+ if (!skb)
+ return -ENOMEM;
+
+- skb_reserve(skb, sizeof(tx_power_tlv));
+-
+ BUILD_BUG_ON(sizeof(dev->alpha2) > sizeof(tx_power_tlv.alpha2));
+ memcpy(tx_power_tlv.alpha2, dev->alpha2, sizeof(dev->alpha2));
+- tx_power_tlv.n_chan = num_ch;
+
++ skb_put_data(skb, &tx_power_tlv, sizeof(tx_power_tlv));
+ for (j = 0; j < num_ch; j++, idx++) {
+ struct ieee80211_channel chan = {
+ .hw_value = ch_list[idx],
+ .band = band,
+ };
+
+- mt76_get_rate_power_limits(phy, &chan, &limits,
+- tx_power);
++ mt76_get_rate_power_limits(phy, &chan, &limits, 127);
+
+- tx_power_tlv.last_msg = ch_list[idx] == last_ch;
+ sku_tlbv.channel = ch_list[idx];
+-
+ mt76_connac_mcu_build_sku(dev, sku_tlbv.pwr_limit,
+ &limits, band);
+ skb_put_data(skb, &sku_tlbv, sku_len);
+ }
+- __skb_push(skb, sizeof(tx_power_tlv));
+- memcpy(skb->data, &tx_power_tlv, sizeof(tx_power_tlv));
+
+ err = mt76_mcu_skb_send_msg(dev, skb,
+ MCU_CMD_SET_RATE_TX_POWER, false);
+@@ -1843,20 +1697,11 @@ int mt76_connac_mcu_set_rate_txpower(struct mt76_phy *phy)
+ {
+ int err;
+
+- if (phy->cap.has_2ghz) {
+- err = mt76_connac_mcu_rate_txpower_band(phy,
+- NL80211_BAND_2GHZ);
+- if (err < 0)
+- return err;
+- }
+- if (phy->cap.has_5ghz) {
+- err = mt76_connac_mcu_rate_txpower_band(phy,
+- NL80211_BAND_5GHZ);
+- if (err < 0)
+- return err;
+- }
++ err = mt76_connac_mcu_rate_txpower_band(phy, NL80211_BAND_2GHZ);
++ if (err < 0)
++ return err;
+
+- return 0;
++ return mt76_connac_mcu_rate_txpower_band(phy, NL80211_BAND_5GHZ);
+ }
+ EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_rate_txpower);
+
+diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+index 1c73beb22677..facebed1e301 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
++++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+@@ -559,7 +559,6 @@ enum {
+ MCU_CMD_SET_RATE_TX_POWER = MCU_CE_PREFIX | 0x5d,
+ MCU_CMD_SCHED_SCAN_ENABLE = MCU_CE_PREFIX | 0x61,
+ MCU_CMD_SCHED_SCAN_REQ = MCU_CE_PREFIX | 0x62,
+- MCU_CMD_GET_NIC_CAPAB = MCU_CE_PREFIX | 0x8a,
+ MCU_CMD_REG_WRITE = MCU_CE_PREFIX | 0xc0,
+ MCU_CMD_REG_READ = MCU_CE_PREFIX | MCU_QUERY_MASK | 0xc0,
+ MCU_CMD_CHIP_CONFIG = MCU_CE_PREFIX | 0xca,
+@@ -576,7 +575,6 @@ enum {
+ enum {
+ UNI_BSS_INFO_BASIC = 0,
+ UNI_BSS_INFO_RLM = 2,
+- UNI_BSS_INFO_BSS_COLOR = 4,
+ UNI_BSS_INFO_HE_BASIC = 5,
+ UNI_BSS_INFO_BCN_CONTENT = 7,
+ UNI_BSS_INFO_QBSS = 15,
+@@ -592,28 +590,6 @@ enum {
+ UNI_OFFLOAD_OFFLOAD_BMC_RPY_DETECT,
+ };
+
+-enum {
+- MT_NIC_CAP_TX_RESOURCE,
+- MT_NIC_CAP_TX_EFUSE_ADDR,
+- MT_NIC_CAP_COEX,
+- MT_NIC_CAP_SINGLE_SKU,
+- MT_NIC_CAP_CSUM_OFFLOAD,
+- MT_NIC_CAP_HW_VER,
+- MT_NIC_CAP_SW_VER,
+- MT_NIC_CAP_MAC_ADDR,
+- MT_NIC_CAP_PHY,
+- MT_NIC_CAP_MAC,
+- MT_NIC_CAP_FRAME_BUF,
+- MT_NIC_CAP_BEAM_FORM,
+- MT_NIC_CAP_LOCATION,
+- MT_NIC_CAP_MUMIMO,
+- MT_NIC_CAP_BUFFER_MODE_INFO,
+- MT_NIC_CAP_HW_ADIE_VERSION = 0x14,
+- MT_NIC_CAP_ANTSWP = 0x16,
+- MT_NIC_CAP_WFDMA_REALLOC,
+- MT_NIC_CAP_6G,
+-};
+-
+ #define UNI_WOW_DETECT_TYPE_MAGIC BIT(0)
+ #define UNI_WOW_DETECT_TYPE_ANY BIT(1)
+ #define UNI_WOW_DETECT_TYPE_DISCONNECT BIT(2)
+@@ -802,9 +778,7 @@ struct mt76_connac_sched_scan_req {
+ } mt7663;
+ struct {
+ u8 bss_idx;
+- u8 pad2[19];
+- u8 random_mac[ETH_ALEN];
+- u8 pad3[38];
++ u8 pad2[63];
+ } mt7921;
+ };
+ } __packed;
+@@ -815,14 +789,6 @@ struct mt76_connac_sched_scan_done {
+ __le16 pad;
+ } __packed;
+
+-struct bss_info_uni_bss_color {
+- __le16 tag;
+- __le16 len;
+- u8 enable;
+- u8 bss_color;
+- u8 rsv[2];
+-} __packed;
+-
+ struct bss_info_uni_he {
+ __le16 tag;
+ __le16 len;
+@@ -927,12 +893,6 @@ struct mt76_connac_suspend_tlv {
+ u8 pad[5];
+ } __packed;
+
+-enum mt76_sta_info_state {
+- MT76_STA_INFO_STATE_NONE,
+- MT76_STA_INFO_STATE_AUTH,
+- MT76_STA_INFO_STATE_ASSOC
+-};
+-
+ struct mt76_sta_cmd_info {
+ struct ieee80211_sta *sta;
+ struct mt76_wcid *wcid;
+@@ -941,10 +901,8 @@ struct mt76_sta_cmd_info {
+
+ bool offload_fw;
+ bool enable;
+- bool newly;
+ int cmd;
+ u8 rcpi;
+- u8 state;
+ };
+
+ #define MT_SKU_POWER_LIMIT 161
+@@ -1014,23 +972,18 @@ int mt76_connac_mcu_set_channel_domain(struct mt76_phy *phy);
+ int mt76_connac_mcu_set_vif_ps(struct mt76_dev *dev, struct ieee80211_vif *vif);
+ void mt76_connac_mcu_sta_basic_tlv(struct sk_buff *skb,
+ struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta, bool enable,
+- bool newly);
++ struct ieee80211_sta *sta, bool enable);
+ void mt76_connac_mcu_wtbl_generic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, void *sta_wtbl,
+ void *wtbl_tlv);
+ void mt76_connac_mcu_wtbl_hdr_trans_tlv(struct sk_buff *skb,
+- struct ieee80211_vif *vif,
+ struct mt76_wcid *wcid,
+ void *sta_wtbl, void *wtbl_tlv);
+-int mt76_connac_mcu_sta_update_hdr_trans(struct mt76_dev *dev,
+- struct ieee80211_vif *vif,
+- struct mt76_wcid *wcid, int cmd);
+ void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb,
+ struct ieee80211_sta *sta,
+ struct ieee80211_vif *vif,
+- u8 rcpi, u8 state);
++ u8 rcpi);
+ void mt76_connac_mcu_wtbl_ht_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+ struct ieee80211_sta *sta, void *sta_wtbl,
+ void *wtbl_tlv);
+@@ -1052,8 +1005,8 @@ int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy,
+ struct ieee80211_vif *vif,
+ struct mt76_wcid *wcid,
+ bool enable);
+-int mt76_connac_mcu_sta_cmd(struct mt76_phy *phy,
+- struct mt76_sta_cmd_info *info);
++int mt76_connac_mcu_add_sta_cmd(struct mt76_phy *phy,
++ struct mt76_sta_cmd_info *info);
+ void mt76_connac_mcu_beacon_loss_iter(void *priv, u8 *mac,
+ struct ieee80211_vif *vif);
+ int mt76_connac_mcu_set_rts_thresh(struct mt76_dev *dev, u32 val, u8 band);
+@@ -1064,7 +1017,6 @@ int mt76_connac_mcu_init_download(struct mt76_dev *dev, u32 addr, u32 len,
+ int mt76_connac_mcu_start_patch(struct mt76_dev *dev);
+ int mt76_connac_mcu_patch_sem_ctrl(struct mt76_dev *dev, bool get);
+ int mt76_connac_mcu_start_firmware(struct mt76_dev *dev, u32 addr, u32 option);
+-int mt76_connac_mcu_get_nic_capability(struct mt76_phy *phy);
+
+ int mt76_connac_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *scan_req);
+@@ -1085,9 +1037,6 @@ int mt76_connac_mcu_update_gtk_rekey(struct ieee80211_hw *hw,
+ int mt76_connac_mcu_set_hif_suspend(struct mt76_dev *dev, bool suspend);
+ void mt76_connac_mcu_set_suspend_iter(void *priv, u8 *mac,
+ struct ieee80211_vif *vif);
+-int mt76_connac_sta_state_dp(struct mt76_dev *dev,
+- enum ieee80211_sta_state old_state,
+- enum ieee80211_sta_state new_state);
+ int mt76_connac_mcu_chip_config(struct mt76_dev *dev);
+ int mt76_connac_mcu_set_deep_sleep(struct mt76_dev *dev, bool enable);
+ void mt76_connac_mcu_coredump_event(struct mt76_dev *dev, struct sk_buff *skb,
+diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c
+index cea24213186c..dd66fd12a2e6 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c
++++ b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c
+@@ -68,7 +68,7 @@ static void mt76x0_set_chip_cap(struct mt76x02_dev *dev)
+ nic_conf1 &= 0xff00;
+
+ if (nic_conf1 & MT_EE_NIC_CONF_1_HW_RF_CTRL)
+- dev_dbg(dev->mt76.dev,
++ dev_err(dev->mt76.dev,
+ "driver does not support HW RF ctrl\n");
+
+ if (!mt76x02_field_valid(nic_conf0 >> 8))
+diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
+index c32e6dc68773..10d66775c391 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
++++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
+@@ -1022,12 +1022,12 @@ void mt76x02_mac_set_tx_protection(struct mt76x02_dev *dev, bool legacy_prot,
+ mt76_wr(dev, MT_TX_PROT_CFG6 + i * 4, vht_prot[i]);
+ }
+
+-void mt76x02_update_channel(struct mt76_phy *mphy)
++void mt76x02_update_channel(struct mt76_dev *mdev)
+ {
+- struct mt76x02_dev *dev = container_of(mphy->dev, struct mt76x02_dev, mt76);
++ struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
+ struct mt76_channel_state *state;
+
+- state = mphy->chan_state;
++ state = mdev->phy.chan_state;
+ state->cc_busy += mt76_rr(dev, MT_CH_BUSY);
+
+ spin_lock_bh(&dev->mt76.cc_lock);
+@@ -1169,7 +1169,7 @@ void mt76x02_mac_work(struct work_struct *work)
+
+ mutex_lock(&dev->mt76.mutex);
+
+- mt76_update_survey(&dev->mphy);
++ mt76_update_survey(&dev->mt76);
+ for (i = 0, idx = 0; i < 16; i++) {
+ u32 val = mt76_rr(dev, MT_TX_AGG_CNT(i));
+
+diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h
+index 5dc6c834111e..0cfbaca50210 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h
++++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h
+@@ -195,7 +195,7 @@ void mt76x02_mac_write_txwi(struct mt76x02_dev *dev, struct mt76x02_txwi *txwi,
+ struct ieee80211_sta *sta, int len);
+ void mt76x02_mac_poll_tx_status(struct mt76x02_dev *dev, bool irq);
+ void mt76x02_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e);
+-void mt76x02_update_channel(struct mt76_phy *mphy);
++void mt76x02_update_channel(struct mt76_dev *mdev);
+ void mt76x02_mac_work(struct work_struct *work);
+
+ void mt76x02_mac_cc_reset(struct mt76x02_dev *dev);
+diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_util.c b/drivers/net/wireless/mediatek/mt76/mt76x02_util.c
+index ccdbab341271..02db5d66735d 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt76x02_util.c
++++ b/drivers/net/wireless/mediatek/mt76/mt76x02_util.c
+@@ -7,18 +7,24 @@
+ #include <linux/module.h>
+ #include "mt76x02.h"
+
+-#define MT76x02_CCK_RATE(_idx, _rate) { \
++#define CCK_RATE(_idx, _rate) { \
+ .bitrate = _rate, \
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE, \
+ .hw_value = (MT_PHY_TYPE_CCK << 8) | (_idx), \
+ .hw_value_short = (MT_PHY_TYPE_CCK << 8) | (8 + (_idx)), \
+ }
+
++#define OFDM_RATE(_idx, _rate) { \
++ .bitrate = _rate, \
++ .hw_value = (MT_PHY_TYPE_OFDM << 8) | (_idx), \
++ .hw_value_short = (MT_PHY_TYPE_OFDM << 8) | (_idx), \
++}
++
+ struct ieee80211_rate mt76x02_rates[] = {
+- MT76x02_CCK_RATE(0, 10),
+- MT76x02_CCK_RATE(1, 20),
+- MT76x02_CCK_RATE(2, 55),
+- MT76x02_CCK_RATE(3, 110),
++ CCK_RATE(0, 10),
++ CCK_RATE(1, 20),
++ CCK_RATE(2, 55),
++ CCK_RATE(3, 110),
+ OFDM_RATE(0, 60),
+ OFDM_RATE(1, 90),
+ OFDM_RATE(2, 120),
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/Makefile b/drivers/net/wireless/mediatek/mt76/mt7915/Makefile
+index 80e49244348e..40c8061787e9 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7915/Makefile
++++ b/drivers/net/wireless/mediatek/mt76/mt7915/Makefile
+@@ -1,4 +1,4 @@
+-# SPDX-License-Identifier: ISC
++#SPDX-License-Identifier: ISC
+
+ obj-$(CONFIG_MT7915E) += mt7915e.o
+
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c
+index 64048243e34b..6a8ddeeecbe9 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c
+@@ -3,7 +3,6 @@
+
+ #include "mt7915.h"
+ #include "eeprom.h"
+-#include "mcu.h"
+
+ /** global debugfs **/
+
+@@ -17,7 +16,7 @@ mt7915_implicit_txbf_set(void *data, u64 val)
+
+ dev->ibf = !!val;
+
+- return mt7915_mcu_set_txbf(dev, MT_BF_TYPE_UPDATE);
++ return mt7915_mcu_set_txbf_type(dev);
+ }
+
+ static int
+@@ -148,9 +147,6 @@ mt7915_txbf_stat_read_phy(struct mt7915_phy *phy, struct seq_file *s)
+ {
+ struct mt7915_dev *dev = s->private;
+ bool ext_phy = phy != &dev->phy;
+- static const char * const bw[] = {
+- "BW20", "BW40", "BW80", "BW160"
+- };
+ int cnt;
+
+ if (!phy)
+@@ -168,16 +164,11 @@ mt7915_txbf_stat_read_phy(struct mt7915_phy *phy, struct seq_file *s)
+ seq_puts(s, "Tx Beamformer Rx feedback statistics: ");
+
+ cnt = mt76_rr(dev, MT_ETBF_RX_FB_CNT(ext_phy));
+- seq_printf(s, "All: %ld, HE: %ld, VHT: %ld, HT: %ld, ",
++ seq_printf(s, "All: %ld, HE: %ld, VHT: %ld, HT: %ld\n",
+ FIELD_GET(MT_ETBF_RX_FB_ALL, cnt),
+ FIELD_GET(MT_ETBF_RX_FB_HE, cnt),
+ FIELD_GET(MT_ETBF_RX_FB_VHT, cnt),
+ FIELD_GET(MT_ETBF_RX_FB_HT, cnt));
+- cnt = mt76_rr(dev, MT_ETBF_RX_FB_CONT(ext_phy));
+- seq_printf(s, "%s, NC: %ld, NR: %ld\n",
+- bw[FIELD_GET(MT_ETBF_RX_FB_BW, cnt)],
+- FIELD_GET(MT_ETBF_RX_FB_NC, cnt),
+- FIELD_GET(MT_ETBF_RX_FB_NR, cnt));
+
+ /* Tx Beamformee Rx NDPA & Tx feedback report */
+ cnt = mt76_rr(dev, MT_ETBF_TX_NDP_BFRP(ext_phy));
+@@ -213,7 +204,7 @@ mt7915_tx_stats_show(struct seq_file *file, void *data)
+ mt7915_txbf_stat_read_phy(mt7915_ext_phy(dev), file);
+
+ /* Tx amsdu info */
+- seq_puts(file, "Tx MSDU statistics:\n");
++ seq_puts(file, "Tx MSDU stat:\n");
+ for (i = 0, n = 0; i < ARRAY_SIZE(stat); i++) {
+ stat[i] = mt76_rr(dev, MT_PLE_AMSDU_PACK_MSDU_CNT(i));
+ n += stat[i];
+@@ -233,6 +224,18 @@ mt7915_tx_stats_show(struct seq_file *file, void *data)
+
+ DEFINE_SHOW_ATTRIBUTE(mt7915_tx_stats);
+
++static int mt7915_read_temperature(struct seq_file *s, void *data)
++{
++ struct mt7915_dev *dev = dev_get_drvdata(s->private);
++ int temp;
++
++ /* cpu */
++ temp = mt7915_mcu_get_temperature(dev, 0);
++ seq_printf(s, "Temperature: %d\n", temp);
++
++ return 0;
++}
++
+ static int
+ mt7915_queues_acq(struct seq_file *s, void *data)
+ {
+@@ -304,23 +307,54 @@ mt7915_puts_rate_txpower(struct seq_file *s, struct mt7915_phy *phy)
+ "RU26", "RU52", "RU106", "RU242/SU20",
+ "RU484/SU40", "RU996/SU80", "RU2x996/SU160"
+ };
+- s8 txpower[MT7915_SKU_RATE_NUM], *buf;
+- int i;
++ struct mt7915_dev *dev = dev_get_drvdata(s->private);
++ bool ext_phy = phy != &dev->phy;
++ u32 reg_base;
++ int i, idx = 0;
+
+ if (!phy)
+ return;
+
+- seq_printf(s, "\nBand %d\n", phy != &phy->dev->phy);
++ reg_base = MT_TMAC_FP0R0(ext_phy);
++ seq_printf(s, "\nBand %d\n", ext_phy);
+
+- mt7915_mcu_get_txpower_sku(phy, txpower, sizeof(txpower));
+- for (i = 0, buf = txpower; i < ARRAY_SIZE(mt7915_sku_group_len); i++) {
+- u8 mcs_num = mt7915_sku_group_len[i];
++ for (i = 0; i < ARRAY_SIZE(mt7915_sku_group_len); i++) {
++ u8 cnt, mcs_num = mt7915_sku_group_len[i];
++ s8 txpower[12];
++ int j;
+
+- if (i >= SKU_VHT_BW20 && i <= SKU_VHT_BW160)
++ if (i == SKU_HT_BW20 || i == SKU_HT_BW40) {
++ mcs_num = 8;
++ } else if (i >= SKU_VHT_BW20 && i <= SKU_VHT_BW160) {
+ mcs_num = 10;
++ } else if (i == SKU_HE_RU26) {
++ reg_base = MT_TMAC_FP0R18(ext_phy);
++ idx = 0;
++ }
++
++ for (j = 0, cnt = 0; j < DIV_ROUND_UP(mcs_num, 4); j++) {
++ u32 val;
++
++ if (i == SKU_VHT_BW160 && idx == 60) {
++ reg_base = MT_TMAC_FP0R15(ext_phy);
++ idx = 0;
++ }
++
++ val = mt76_rr(dev, reg_base + (idx / 4) * 4);
++
++ if (idx && idx % 4)
++ val >>= (idx % 4) * 8;
++
++ while (val > 0 && cnt < mcs_num) {
++ s8 pwr = FIELD_GET(MT_TMAC_FP_MASK, val);
++
++ txpower[cnt++] = pwr;
++ val >>= 8;
++ idx++;
++ }
++ }
+
+- mt76_seq_puts_array(s, sku_group_name[i], buf, mcs_num);
+- buf += mt7915_sku_group_len[i];
++ mt76_seq_puts_array(s, sku_group_name[i], txpower, mcs_num);
+ }
+ }
+
+@@ -356,6 +390,8 @@ int mt7915_init_debugfs(struct mt7915_dev *dev)
+ debugfs_create_file("radar_trigger", 0200, dir, dev,
+ &fops_radar_trigger);
+ debugfs_create_file("ser_trigger", 0200, dir, dev, &fops_ser_trigger);
++ debugfs_create_devm_seqfile(dev->mt76.dev, "temperature", dir,
++ mt7915_read_temperature);
+ debugfs_create_devm_seqfile(dev->mt76.dev, "txpower_sku", dir,
+ mt7915_read_rate_txpower);
+
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c
+index 9182568f95c7..11d0b760abd7 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c
+@@ -19,6 +19,39 @@ int mt7915_init_tx_queues(struct mt7915_phy *phy, int idx, int n_desc)
+ return 0;
+ }
+
++void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
++ struct sk_buff *skb)
++{
++ struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76);
++ __le32 *rxd = (__le32 *)skb->data;
++ enum rx_pkt_type type;
++
++ type = FIELD_GET(MT_RXD0_PKT_TYPE, le32_to_cpu(rxd[0]));
++
++ switch (type) {
++ case PKT_TYPE_TXRX_NOTIFY:
++ mt7915_mac_tx_free(dev, skb);
++ break;
++ case PKT_TYPE_RX_EVENT:
++ mt7915_mcu_rx_event(dev, skb);
++ break;
++#ifdef CONFIG_NL80211_TESTMODE
++ case PKT_TYPE_TXRXV:
++ mt7915_mac_fill_rx_vector(dev, skb);
++ break;
++#endif
++ case PKT_TYPE_NORMAL:
++ if (!mt7915_mac_fill_rx(dev, skb)) {
++ mt76_rx(&dev->mt76, q, skb);
++ return;
++ }
++ fallthrough;
++ default:
++ dev_kfree_skb(skb);
++ break;
++ }
++}
++
+ static void
+ mt7915_tx_cleanup(struct mt7915_dev *dev)
+ {
+@@ -79,6 +112,8 @@ void mt7915_dma_prefetch(struct mt7915_dev *dev)
+
+ int mt7915_dma_init(struct mt7915_dev *dev)
+ {
++ /* Increase buffer size to receive large VHT/HE MPDUs */
++ int rx_buf_size = MT_RX_BUF_SIZE * 2;
+ u32 hif1_ofs = 0;
+ int ret;
+
+@@ -142,28 +177,28 @@ int mt7915_dma_init(struct mt7915_dev *dev)
+ /* event from WM */
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU],
+ MT7915_RXQ_MCU_WM, MT7915_RX_MCU_RING_SIZE,
+- MT_RX_BUF_SIZE, MT_RX_EVENT_RING_BASE);
++ rx_buf_size, MT_RX_EVENT_RING_BASE);
+ if (ret)
+ return ret;
+
+ /* event from WA */
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA],
+ MT7915_RXQ_MCU_WA, MT7915_RX_MCU_RING_SIZE,
+- MT_RX_BUF_SIZE, MT_RX_EVENT_RING_BASE);
++ rx_buf_size, MT_RX_EVENT_RING_BASE);
+ if (ret)
+ return ret;
+
+ /* rx data queue */
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN],
+ MT7915_RXQ_BAND0, MT7915_RX_RING_SIZE,
+- MT_RX_BUF_SIZE, MT_RX_DATA_RING_BASE);
++ rx_buf_size, MT_RX_DATA_RING_BASE);
+ if (ret)
+ return ret;
+
+ if (dev->dbdc_support) {
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_EXT],
+ MT7915_RXQ_BAND1, MT7915_RX_RING_SIZE,
+- MT_RX_BUF_SIZE,
++ rx_buf_size,
+ MT_RX_DATA_RING_BASE + hif1_ofs);
+ if (ret)
+ return ret;
+@@ -172,7 +207,7 @@ int mt7915_dma_init(struct mt7915_dev *dev)
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_EXT_WA],
+ MT7915_RXQ_MCU_WA_EXT,
+ MT7915_RX_MCU_RING_SIZE,
+- MT_RX_BUF_SIZE,
++ rx_buf_size,
+ MT_RX_EVENT_RING_BASE + hif1_ofs);
+ if (ret)
+ return ret;
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
+index ee3d64434821..8ededf2e5279 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
+@@ -4,12 +4,22 @@
+ #include "mt7915.h"
+ #include "eeprom.h"
+
++static u32 mt7915_eeprom_read(struct mt7915_dev *dev, u32 offset)
++{
++ u8 *data = dev->mt76.eeprom.data;
++
++ if (data[offset] == 0xff && !dev->flash_mode)
++ mt7915_mcu_get_eeprom(dev, offset);
++
++ return data[offset];
++}
++
+ static int mt7915_eeprom_load_precal(struct mt7915_dev *dev)
+ {
+ struct mt76_dev *mdev = &dev->mt76;
+- u8 *eeprom = mdev->eeprom.data;
+- u32 val = eeprom[MT_EE_DO_PRE_CAL];
++ u32 val;
+
++ val = mt7915_eeprom_read(dev, MT_EE_DO_PRE_CAL);
+ if (val != (MT_EE_WIFI_CAL_DPD | MT_EE_WIFI_CAL_GROUP))
+ return 0;
+
+@@ -33,13 +43,7 @@ static int mt7915_eeprom_load(struct mt7915_dev *dev)
+ dev->flash_mode = true;
+ ret = mt7915_eeprom_load_precal(dev);
+ } else {
+- u32 block_num, i;
+-
+- block_num = DIV_ROUND_UP(MT7915_EEPROM_SIZE,
+- MT7915_EEPROM_BLOCK_SIZE);
+- for (i = 0; i < block_num; i++)
+- mt7915_mcu_get_eeprom(dev,
+- i * MT7915_EEPROM_BLOCK_SIZE);
++ memset(dev->mt76.eeprom.data, -1, MT7915_EEPROM_SIZE);
+ }
+
+ return ret;
+@@ -48,7 +52,10 @@ static int mt7915_eeprom_load(struct mt7915_dev *dev)
+ static int mt7915_check_eeprom(struct mt7915_dev *dev)
+ {
+ u8 *eeprom = dev->mt76.eeprom.data;
+- u16 val = get_unaligned_le16(eeprom);
++ u16 val;
++
++ mt7915_eeprom_read(dev, MT_EE_CHIP_ID);
++ val = get_unaligned_le16(eeprom);
+
+ switch (val) {
+ case 0x7915:
+@@ -62,10 +69,9 @@ void mt7915_eeprom_parse_band_config(struct mt7915_phy *phy)
+ {
+ struct mt7915_dev *dev = phy->dev;
+ bool ext_phy = phy != &dev->phy;
+- u8 *eeprom = dev->mt76.eeprom.data;
+ u32 val;
+
+- val = eeprom[MT_EE_WIFI_CONF + ext_phy];
++ val = mt7915_eeprom_read(dev, MT_EE_WIFI_CONF + ext_phy);
+ val = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
+ if (val == MT_EE_BAND_SEL_DEFAULT && dev->dbdc_support)
+ val = ext_phy ? MT_EE_BAND_SEL_5GHZ : MT_EE_BAND_SEL_2GHZ;
+@@ -137,7 +143,6 @@ int mt7915_eeprom_get_target_power(struct mt7915_dev *dev,
+ struct ieee80211_channel *chan,
+ u8 chain_idx)
+ {
+- u8 *eeprom = dev->mt76.eeprom.data;
+ int index, target_power;
+ bool tssi_on;
+
+@@ -148,18 +153,18 @@ int mt7915_eeprom_get_target_power(struct mt7915_dev *dev,
+
+ if (chan->band == NL80211_BAND_2GHZ) {
+ index = MT_EE_TX0_POWER_2G + chain_idx * 3;
+- target_power = eeprom[index];
++ target_power = mt7915_eeprom_read(dev, index);
+
+ if (!tssi_on)
+- target_power += eeprom[index + 1];
++ target_power += mt7915_eeprom_read(dev, index + 1);
+ } else {
+ int group = mt7915_get_channel_group(chan->hw_value);
+
+ index = MT_EE_TX0_POWER_5G + chain_idx * 12;
+- target_power = eeprom[index + group];
++ target_power = mt7915_eeprom_read(dev, index + group);
+
+ if (!tssi_on)
+- target_power += eeprom[index + 8];
++ target_power += mt7915_eeprom_read(dev, index + 8);
+ }
+
+ return target_power;
+@@ -167,14 +172,13 @@ int mt7915_eeprom_get_target_power(struct mt7915_dev *dev,
+
+ s8 mt7915_eeprom_get_power_delta(struct mt7915_dev *dev, int band)
+ {
+- u8 *eeprom = dev->mt76.eeprom.data;
+ u32 val;
+ s8 delta;
+
+ if (band == NL80211_BAND_2GHZ)
+- val = eeprom[MT_EE_RATE_DELTA_2G];
++ val = mt7915_eeprom_read(dev, MT_EE_RATE_DELTA_2G);
+ else
+- val = eeprom[MT_EE_RATE_DELTA_5G];
++ val = mt7915_eeprom_read(dev, MT_EE_RATE_DELTA_5G);
+
+ if (!(val & MT_EE_RATE_DELTA_EN))
+ return 0;
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
+index 4798d6344305..feb2aa57ef22 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
+@@ -2,14 +2,39 @@
+ /* Copyright (C) 2020 MediaTek Inc. */
+
+ #include <linux/etherdevice.h>
+-#include <linux/hwmon.h>
+-#include <linux/hwmon-sysfs.h>
+-#include <linux/thermal.h>
+ #include "mt7915.h"
+ #include "mac.h"
+ #include "mcu.h"
+ #include "eeprom.h"
+
++#define CCK_RATE(_idx, _rate) { \
++ .bitrate = _rate, \
++ .flags = IEEE80211_RATE_SHORT_PREAMBLE, \
++ .hw_value = (MT_PHY_TYPE_CCK << 8) | (_idx), \
++ .hw_value_short = (MT_PHY_TYPE_CCK << 8) | (4 + (_idx)), \
++}
++
++#define OFDM_RATE(_idx, _rate) { \
++ .bitrate = _rate, \
++ .hw_value = (MT_PHY_TYPE_OFDM << 8) | (_idx), \
++ .hw_value_short = (MT_PHY_TYPE_OFDM << 8) | (_idx), \
++}
++
++static struct ieee80211_rate mt7915_rates[] = {
++ CCK_RATE(0, 10),
++ CCK_RATE(1, 20),
++ CCK_RATE(2, 55),
++ CCK_RATE(3, 110),
++ OFDM_RATE(11, 60),
++ OFDM_RATE(15, 90),
++ OFDM_RATE(10, 120),
++ OFDM_RATE(14, 180),
++ OFDM_RATE(9, 240),
++ OFDM_RATE(13, 360),
++ OFDM_RATE(8, 480),
++ OFDM_RATE(12, 540),
++};
++
+ static const struct ieee80211_iface_limit if_limits[] = {
+ {
+ .max = 1,
+@@ -42,117 +67,6 @@ static const struct ieee80211_iface_combination if_comb[] = {
+ }
+ };
+
+-static ssize_t mt7915_thermal_show_temp(struct device *dev,
+- struct device_attribute *attr,
+- char *buf)
+-{
+- struct mt7915_phy *phy = dev_get_drvdata(dev);
+- int temperature;
+-
+- temperature = mt7915_mcu_get_temperature(phy);
+- if (temperature < 0)
+- return temperature;
+-
+- /* display in millidegree celcius */
+- return sprintf(buf, "%u\n", temperature * 1000);
+-}
+-
+-static SENSOR_DEVICE_ATTR(temp1_input, 0444, mt7915_thermal_show_temp,
+- NULL, 0);
+-
+-static struct attribute *mt7915_hwmon_attrs[] = {
+- &sensor_dev_attr_temp1_input.dev_attr.attr,
+- NULL,
+-};
+-ATTRIBUTE_GROUPS(mt7915_hwmon);
+-
+-static int
+-mt7915_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev,
+- unsigned long *state)
+-{
+- *state = MT7915_THERMAL_THROTTLE_MAX;
+-
+- return 0;
+-}
+-
+-static int
+-mt7915_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev,
+- unsigned long *state)
+-{
+- struct mt7915_phy *phy = cdev->devdata;
+-
+- *state = phy->throttle_state;
+-
+- return 0;
+-}
+-
+-static int
+-mt7915_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev,
+- unsigned long state)
+-{
+- struct mt7915_phy *phy = cdev->devdata;
+- int ret;
+-
+- if (state > MT7915_THERMAL_THROTTLE_MAX)
+- return -EINVAL;
+-
+- if (state == phy->throttle_state)
+- return 0;
+-
+- ret = mt7915_mcu_set_thermal_throttling(phy, state);
+- if (ret)
+- return ret;
+-
+- phy->throttle_state = state;
+-
+- return 0;
+-}
+-
+-static const struct thermal_cooling_device_ops mt7915_thermal_ops = {
+- .get_max_state = mt7915_thermal_get_max_throttle_state,
+- .get_cur_state = mt7915_thermal_get_cur_throttle_state,
+- .set_cur_state = mt7915_thermal_set_cur_throttle_state,
+-};
+-
+-static void mt7915_unregister_thermal(struct mt7915_phy *phy)
+-{
+- struct wiphy *wiphy = phy->mt76->hw->wiphy;
+-
+- if (!phy->cdev)
+- return;
+-
+- sysfs_remove_link(&wiphy->dev.kobj, "cooling_device");
+- thermal_cooling_device_unregister(phy->cdev);
+-}
+-
+-static int mt7915_thermal_init(struct mt7915_phy *phy)
+-{
+- struct wiphy *wiphy = phy->mt76->hw->wiphy;
+- struct thermal_cooling_device *cdev;
+- struct device *hwmon;
+-
+- cdev = thermal_cooling_device_register(wiphy_name(wiphy), phy,
+- &mt7915_thermal_ops);
+- if (!IS_ERR(cdev)) {
+- if (sysfs_create_link(&wiphy->dev.kobj, &cdev->device.kobj,
+- "cooling_device") < 0)
+- thermal_cooling_device_unregister(cdev);
+- else
+- phy->cdev = cdev;
+- }
+-
+- if (!IS_REACHABLE(CONFIG_HWMON))
+- return 0;
+-
+- hwmon = devm_hwmon_device_register_with_groups(&wiphy->dev,
+- wiphy_name(wiphy), phy,
+- mt7915_hwmon_groups);
+- if (IS_ERR(hwmon))
+- return PTR_ERR(hwmon);
+-
+- return 0;
+-}
+-
+ static void
+ mt7915_init_txpower(struct mt7915_dev *dev,
+ struct ieee80211_supported_band *sband)
+@@ -287,6 +201,7 @@ mt7915_mac_init_band(struct mt7915_dev *dev, u8 band)
+ FIELD_PREP(MT_MDP_RCFR1_RX_DROPPED_MCAST, MT_MDP_TO_HIF);
+ mt76_rmw(dev, MT_MDP_BNRCFR1(band), mask, set);
+
++ mt76_set(dev, MT_WF_RMAC_MIB_TIME0(band), MT_WF_RMAC_MIB_RXTIME_EN);
+ mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(band), MT_WF_RMAC_MIB_RXTIME_EN);
+
+ mt76_rmw_field(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_MAX_RX_LEN, 1536);
+@@ -313,19 +228,20 @@ static int mt7915_txbf_init(struct mt7915_dev *dev)
+ {
+ int ret;
+
++
+ if (dev->dbdc_support) {
+- ret = mt7915_mcu_set_txbf(dev, MT_BF_MODULE_UPDATE);
++ ret = mt7915_mcu_set_txbf_module(dev);
+ if (ret)
+ return ret;
+ }
+
+ /* trigger sounding packets */
+- ret = mt7915_mcu_set_txbf(dev, MT_BF_SOUNDING_ON);
++ ret = mt7915_mcu_set_txbf_sounding(dev);
+ if (ret)
+ return ret;
+
+ /* enable eBF */
+- return mt7915_mcu_set_txbf(dev, MT_BF_TYPE_UPDATE);
++ return mt7915_mcu_set_txbf_type(dev);
+ }
+
+ static int mt7915_register_ext_phy(struct mt7915_dev *dev)
+@@ -365,12 +281,8 @@ static int mt7915_register_ext_phy(struct mt7915_dev *dev)
+ if (ret)
+ goto error;
+
+- ret = mt76_register_phy(mphy, true, mt76_rates,
+- ARRAY_SIZE(mt76_rates));
+- if (ret)
+- goto error;
+-
+- ret = mt7915_thermal_init(phy);
++ ret = mt76_register_phy(mphy, true, mt7915_rates,
++ ARRAY_SIZE(mt7915_rates));
+ if (ret)
+ goto error;
+
+@@ -667,6 +579,8 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band,
+
+ switch (i) {
+ case NL80211_IFTYPE_AP:
++ he_cap_elem->mac_cap_info[0] |=
++ IEEE80211_HE_MAC_CAP0_TWT_RES;
+ he_cap_elem->mac_cap_info[2] |=
+ IEEE80211_HE_MAC_CAP2_BSR;
+ he_cap_elem->mac_cap_info[4] |=
+@@ -680,6 +594,8 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band,
+ IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT;
+ break;
+ case NL80211_IFTYPE_STATION:
++ he_cap_elem->mac_cap_info[0] |=
++ IEEE80211_HE_MAC_CAP0_TWT_REQ;
+ he_cap_elem->mac_cap_info[1] |=
+ IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US;
+
+@@ -774,7 +690,6 @@ static void mt7915_unregister_ext_phy(struct mt7915_dev *dev)
+ if (!phy)
+ return;
+
+- mt7915_unregister_thermal(phy);
+ mt76_unregister_phy(mphy);
+ ieee80211_free_hw(mphy->hw);
+ }
+@@ -816,12 +731,8 @@ int mt7915_register_device(struct mt7915_dev *dev)
+ dev->mt76.test_ops = &mt7915_testmode_ops;
+ #endif
+
+- ret = mt76_register_device(&dev->mt76, true, mt76_rates,
+- ARRAY_SIZE(mt76_rates));
+- if (ret)
+- return ret;
+-
+- ret = mt7915_thermal_init(&dev->phy);
++ ret = mt76_register_device(&dev->mt76, true, mt7915_rates,
++ ARRAY_SIZE(mt7915_rates));
+ if (ret)
+ return ret;
+
+@@ -837,12 +748,10 @@ int mt7915_register_device(struct mt7915_dev *dev)
+ void mt7915_unregister_device(struct mt7915_dev *dev)
+ {
+ mt7915_unregister_ext_phy(dev);
+- mt7915_unregister_thermal(&dev->phy);
+ mt76_unregister_device(&dev->mt76);
+ mt7915_mcu_exit(dev);
+ mt7915_tx_token_put(dev);
+ mt7915_dma_cleanup(dev);
+- tasklet_disable(&dev->irq_tasklet);
+
+ mt76_free_device(&dev->mt76);
+ }
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
+index 2462704094b0..f4544c46c173 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
+@@ -307,8 +307,7 @@ mt7915_mac_decode_he_radiotap(struct sk_buff *skb,
+ }
+ }
+
+-static int
+-mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb)
++int mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb)
+ {
+ struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+ struct mt76_phy *mphy = &dev->mt76.phy;
+@@ -624,10 +623,9 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb)
+ return 0;
+ }
+
+-static void
+-mt7915_mac_fill_rx_vector(struct mt7915_dev *dev, struct sk_buff *skb)
+-{
+ #ifdef CONFIG_NL80211_TESTMODE
++void mt7915_mac_fill_rx_vector(struct mt7915_dev *dev, struct sk_buff *skb)
++{
+ struct mt7915_phy *phy = &dev->phy;
+ __le32 *rxd = (__le32 *)skb->data;
+ __le32 *rxv_hdr = rxd + 2;
+@@ -665,10 +663,10 @@ mt7915_mac_fill_rx_vector(struct mt7915_dev *dev, struct sk_buff *skb)
+
+ phy->test.last_freq_offset = foe;
+ phy->test.last_snr = snr;
+-#endif
+
+ dev_kfree_skb(skb);
+ }
++#endif
+
+ static void
+ mt7915_mac_write_txwi_tm(struct mt7915_phy *phy, __le32 *txwi,
+@@ -900,7 +898,7 @@ mt7915_mac_write_txwi_80211(struct mt7915_dev *dev, __le32 *txwi,
+ }
+
+ void mt7915_mac_write_txwi(struct mt7915_dev *dev, __le32 *txwi,
+- struct sk_buff *skb, struct mt76_wcid *wcid, int pid,
++ struct sk_buff *skb, struct mt76_wcid *wcid,
+ struct ieee80211_key_conf *key, bool beacon)
+ {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+@@ -959,12 +957,7 @@ void mt7915_mac_write_txwi(struct mt7915_dev *dev, __le32 *txwi,
+
+ txwi[3] = cpu_to_le32(val);
+ txwi[4] = 0;
+-
+- val = FIELD_PREP(MT_TXD5_PID, pid);
+- if (pid >= MT_PACKET_ID_FIRST)
+- val |= MT_TXD5_TX_STATUS_HOST;
+- txwi[5] = cpu_to_le32(val);
+-
++ txwi[5] = 0;
+ txwi[6] = 0;
+ txwi[7] = wcid->amsdu ? cpu_to_le32(MT_TXD7_HW_AMSDU) : 0;
+
+@@ -1004,11 +997,11 @@ int mt7915_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb);
+ struct ieee80211_key_conf *key = info->control.hw_key;
+ struct ieee80211_vif *vif = info->control.vif;
++ struct mt76_tx_cb *cb = mt76_tx_skb_cb(tx_info->skb);
+ struct mt76_txwi_cache *t;
+ struct mt7915_txp *txp;
+ int id, i, nbuf = tx_info->nbuf - 1;
+ u8 *txwi = (u8 *)txwi_ptr;
+- int pid;
+
+ if (unlikely(tx_info->skb->len <= ETH_HLEN))
+ return -EINVAL;
+@@ -1016,11 +1009,11 @@ int mt7915_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ if (!wcid)
+ wcid = &dev->mt76.global_wcid;
+
+- pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb);
+-
+- mt7915_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, pid, key,
++ mt7915_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, key,
+ false);
+
++ cb->wcid = wcid->idx;
++
+ txp = (struct mt7915_txp *)(txwi + MT_TXD_SIZE);
+ for (i = 0; i < nbuf; i++) {
+ txp->buf[i] = cpu_to_le32(tx_info->buf[i + 1].addr);
+@@ -1091,50 +1084,65 @@ mt7915_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi)
+ }
+
+ static void
+-mt7915_txp_skb_unmap(struct mt76_dev *dev, struct mt76_txwi_cache *t)
++mt7915_tx_complete_status(struct mt76_dev *mdev, struct sk_buff *skb,
++ struct ieee80211_sta *sta, u8 stat,
++ struct list_head *free_list)
+ {
+- struct mt7915_txp *txp;
+- int i;
+-
+- txp = mt7915_txwi_to_txp(dev, t);
+- for (i = 0; i < txp->nbuf; i++)
+- dma_unmap_single(dev->dev, le32_to_cpu(txp->buf[i]),
+- le16_to_cpu(txp->len[i]), DMA_TO_DEVICE);
+-}
++ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
++ struct ieee80211_tx_status status = {
++ .sta = sta,
++ .info = info,
++ .skb = skb,
++ .free_list = free_list,
++ };
++ struct ieee80211_hw *hw;
+
+-static void
+-mt7915_txwi_free(struct mt7915_dev *dev, struct mt76_txwi_cache *t,
+- struct ieee80211_sta *sta, struct list_head *free_list)
+-{
+- struct mt76_dev *mdev = &dev->mt76;
+- struct mt76_wcid *wcid;
+- __le32 *txwi;
+- u16 wcid_idx;
++ if (sta) {
++ struct mt7915_sta *msta;
+
+- mt7915_txp_skb_unmap(mdev, t);
+- if (!t->skb)
+- goto out;
++ msta = (struct mt7915_sta *)sta->drv_priv;
++ status.rate = &msta->stats.tx_rate;
++ }
+
+- txwi = (__le32 *)mt76_get_txwi_ptr(mdev, t);
+- if (sta) {
+- wcid = (struct mt76_wcid *)sta->drv_priv;
+- wcid_idx = wcid->idx;
++#ifdef CONFIG_NL80211_TESTMODE
++ if (mt76_is_testmode_skb(mdev, skb, &hw)) {
++ struct mt7915_phy *phy = mt7915_hw_phy(hw);
++ struct ieee80211_vif *vif = phy->monitor_vif;
++ struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+
+- if (likely(t->skb->protocol != cpu_to_be16(ETH_P_PAE)))
+- mt7915_tx_check_aggr(sta, txwi);
+- } else {
+- wcid_idx = FIELD_GET(MT_TXD1_WLAN_IDX, le32_to_cpu(txwi[1]));
++ mt76_tx_complete_skb(mdev, mvif->sta.wcid.idx, skb);
++ return;
+ }
++#endif
++
++ hw = mt76_tx_status_get_hw(mdev, skb);
+
+- __mt76_tx_complete_skb(mdev, wcid_idx, t->skb, free_list);
++ if (info->flags & IEEE80211_TX_CTL_AMPDU)
++ info->flags |= IEEE80211_TX_STAT_AMPDU;
+
+-out:
+- t->skb = NULL;
+- mt76_put_txwi(mdev, t);
++ if (stat)
++ ieee80211_tx_info_clear_status(info);
++
++ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
++ info->flags |= IEEE80211_TX_STAT_ACK;
++
++ info->status.tx_time = 0;
++ ieee80211_tx_status_ext(hw, &status);
+ }
+
+-static void
+-mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb)
++void mt7915_txp_skb_unmap(struct mt76_dev *dev,
++ struct mt76_txwi_cache *t)
++{
++ struct mt7915_txp *txp;
++ int i;
++
++ txp = mt7915_txwi_to_txp(dev, t);
++ for (i = 0; i < txp->nbuf; i++)
++ dma_unmap_single(dev->dev, le32_to_cpu(txp->buf[i]),
++ le16_to_cpu(txp->len[i]), DMA_TO_DEVICE);
++}
++
++void mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb)
+ {
+ struct mt7915_tx_free *free = (struct mt7915_tx_free *)skb->data;
+ struct mt76_dev *mdev = &dev->mt76;
+@@ -1199,7 +1207,28 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb)
+ if (!txwi)
+ continue;
+
+- mt7915_txwi_free(dev, txwi, sta, &free_list);
++ mt7915_txp_skb_unmap(mdev, txwi);
++ if (txwi->skb) {
++ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txwi->skb);
++ void *txwi_ptr = mt76_get_txwi_ptr(mdev, txwi);
++
++ if (likely(txwi->skb->protocol != cpu_to_be16(ETH_P_PAE)))
++ mt7915_tx_check_aggr(sta, txwi_ptr);
++
++ if (sta && !info->tx_time_est) {
++ struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv;
++ int pending;
++
++ pending = atomic_dec_return(&wcid->non_aql_packets);
++ if (pending < 0)
++ atomic_cmpxchg(&wcid->non_aql_packets, pending, 0);
++ }
++
++ mt7915_tx_complete_status(mdev, txwi->skb, sta, stat, &free_list);
++ txwi->skb = NULL;
++ }
++
++ mt76_put_txwi(mdev, txwi);
+ }
+
+ mt7915_mac_sta_poll(dev);
+@@ -1217,120 +1246,6 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb)
+ }
+ }
+
+-static bool
+-mt7915_mac_add_txs_skb(struct mt7915_dev *dev, struct mt76_wcid *wcid, int pid,
+- __le32 *txs_data)
+-{
+- struct mt76_dev *mdev = &dev->mt76;
+- struct ieee80211_tx_info *info;
+- struct sk_buff_head list;
+- struct sk_buff *skb;
+-
+- mt76_tx_status_lock(mdev, &list);
+- skb = mt76_tx_status_skb_get(mdev, wcid, pid, &list);
+- if (!skb)
+- goto out;
+-
+- info = IEEE80211_SKB_CB(skb);
+- if (!(txs_data[0] & le32_to_cpu(MT_TXS0_ACK_ERROR_MASK)))
+- info->flags |= IEEE80211_TX_STAT_ACK;
+-
+- info->status.ampdu_len = 1;
+- info->status.ampdu_ack_len = !!(info->flags &
+- IEEE80211_TX_STAT_ACK);
+-
+- info->status.rates[0].idx = -1;
+- mt76_tx_status_skb_done(mdev, skb, &list);
+-
+-out:
+- mt76_tx_status_unlock(mdev, &list);
+-
+- return !!skb;
+-}
+-
+-static void mt7915_mac_add_txs(struct mt7915_dev *dev, void *data)
+-{
+- struct mt7915_sta *msta = NULL;
+- struct mt76_wcid *wcid;
+- __le32 *txs_data = data;
+- u16 wcidx;
+- u32 txs;
+- u8 pid;
+-
+- txs = le32_to_cpu(txs_data[0]);
+- if (FIELD_GET(MT_TXS0_TXS_FORMAT, txs) > 1)
+- return;
+-
+- txs = le32_to_cpu(txs_data[2]);
+- wcidx = FIELD_GET(MT_TXS2_WCID, txs);
+-
+- txs = le32_to_cpu(txs_data[3]);
+- pid = FIELD_GET(MT_TXS3_PID, txs);
+-
+- if (pid < MT_PACKET_ID_FIRST)
+- return;
+-
+- if (wcidx >= MT7915_WTBL_SIZE)
+- return;
+-
+- rcu_read_lock();
+-
+- wcid = rcu_dereference(dev->mt76.wcid[wcidx]);
+- if (!wcid)
+- goto out;
+-
+- mt7915_mac_add_txs_skb(dev, wcid, pid, txs_data);
+-
+- if (!wcid->sta)
+- goto out;
+-
+- msta = container_of(wcid, struct mt7915_sta, wcid);
+- spin_lock_bh(&dev->sta_poll_lock);
+- if (list_empty(&msta->poll_list))
+- list_add_tail(&msta->poll_list, &dev->sta_poll_list);
+- spin_unlock_bh(&dev->sta_poll_lock);
+-
+-out:
+- rcu_read_unlock();
+-}
+-
+-void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+- struct sk_buff *skb)
+-{
+- struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76);
+- __le32 *rxd = (__le32 *)skb->data;
+- __le32 *end = (__le32 *)&skb->data[skb->len];
+- enum rx_pkt_type type;
+-
+- type = FIELD_GET(MT_RXD0_PKT_TYPE, le32_to_cpu(rxd[0]));
+-
+- switch (type) {
+- case PKT_TYPE_TXRX_NOTIFY:
+- mt7915_mac_tx_free(dev, skb);
+- break;
+- case PKT_TYPE_RX_EVENT:
+- mt7915_mcu_rx_event(dev, skb);
+- break;
+- case PKT_TYPE_TXRXV:
+- mt7915_mac_fill_rx_vector(dev, skb);
+- break;
+- case PKT_TYPE_TXS:
+- for (rxd += 2; rxd + 8 <= end; rxd += 8)
+- mt7915_mac_add_txs(dev, rxd);
+- dev_kfree_skb(skb);
+- break;
+- case PKT_TYPE_NORMAL:
+- if (!mt7915_mac_fill_rx(dev, skb)) {
+- mt76_rx(&dev->mt76, q, skb);
+- return;
+- }
+- fallthrough;
+- default:
+- dev_kfree_skb(skb);
+- break;
+- }
+-}
+-
+ void mt7915_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e)
+ {
+ struct mt7915_dev *dev;
+@@ -1352,8 +1267,15 @@ void mt7915_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e)
+ e->skb = t ? t->skb : NULL;
+ }
+
+- if (e->skb)
+- mt76_tx_complete_skb(mdev, e->wcid, e->skb);
++ if (e->skb) {
++ struct mt76_tx_cb *cb = mt76_tx_skb_cb(e->skb);
++ struct mt76_wcid *wcid;
++
++ wcid = rcu_dereference(dev->mt76.wcid[cb->wcid]);
++
++ mt7915_tx_complete_status(mdev, e->skb, wcid_to_sta(wcid), 0,
++ NULL);
++ }
+ }
+
+ void mt7915_mac_cca_stats_reset(struct mt7915_phy *phy)
+@@ -1387,10 +1309,14 @@ void mt7915_mac_reset_counters(struct mt7915_phy *phy)
+ memset(&dev->mt76.aggr_stats[i], 0, sizeof(dev->mt76.aggr_stats) / 2);
+
+ /* reset airtime counters */
++ mt76_rr(dev, MT_MIB_SDR9(ext_phy));
++ mt76_rr(dev, MT_MIB_SDR36(ext_phy));
++ mt76_rr(dev, MT_MIB_SDR37(ext_phy));
++
++ mt76_set(dev, MT_WF_RMAC_MIB_TIME0(ext_phy),
++ MT_WF_RMAC_MIB_RXTIME_CLR);
+ mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(ext_phy),
+ MT_WF_RMAC_MIB_RXTIME_CLR);
+-
+- mt7915_mcu_get_chan_mib_info(phy, true);
+ }
+
+ void mt7915_mac_set_timing(struct mt7915_phy *phy)
+@@ -1484,24 +1410,53 @@ mt7915_phy_get_nf(struct mt7915_phy *phy, int idx)
+ return sum / n;
+ }
+
+-void mt7915_update_channel(struct mt76_phy *mphy)
++static void
++mt7915_phy_update_channel(struct mt76_phy *mphy, int idx)
+ {
++ struct mt7915_dev *dev = container_of(mphy->dev, struct mt7915_dev, mt76);
+ struct mt7915_phy *phy = (struct mt7915_phy *)mphy->priv;
+- struct mt76_channel_state *state = mphy->chan_state;
+- bool ext_phy = phy != &phy->dev->phy;
++ struct mt76_channel_state *state;
++ u64 busy_time, tx_time, rx_time, obss_time;
+ int nf;
+
+- mt7915_mcu_get_chan_mib_info(phy, false);
++ busy_time = mt76_get_field(dev, MT_MIB_SDR9(idx),
++ MT_MIB_SDR9_BUSY_MASK);
++ tx_time = mt76_get_field(dev, MT_MIB_SDR36(idx),
++ MT_MIB_SDR36_TXTIME_MASK);
++ rx_time = mt76_get_field(dev, MT_MIB_SDR37(idx),
++ MT_MIB_SDR37_RXTIME_MASK);
++ obss_time = mt76_get_field(dev, MT_WF_RMAC_MIB_AIRTIME14(idx),
++ MT_MIB_OBSSTIME_MASK);
+
+- nf = mt7915_phy_get_nf(phy, ext_phy);
++ nf = mt7915_phy_get_nf(phy, idx);
+ if (!phy->noise)
+ phy->noise = nf << 4;
+ else if (nf)
+ phy->noise += nf - (phy->noise >> 4);
+
++ state = mphy->chan_state;
++ state->cc_busy += busy_time;
++ state->cc_tx += tx_time;
++ state->cc_rx += rx_time + obss_time;
++ state->cc_bss_rx += rx_time;
+ state->noise = -(phy->noise >> 4);
+ }
+
++void mt7915_update_channel(struct mt76_dev *mdev)
++{
++ struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76);
++
++ mt7915_phy_update_channel(&mdev->phy, 0);
++ if (mdev->phy2)
++ mt7915_phy_update_channel(mdev->phy2, 1);
++
++ /* reset obss airtime */
++ mt76_set(dev, MT_WF_RMAC_MIB_TIME0(0), MT_WF_RMAC_MIB_RXTIME_CLR);
++ if (mdev->phy2)
++ mt76_set(dev, MT_WF_RMAC_MIB_TIME0(1),
++ MT_WF_RMAC_MIB_RXTIME_CLR);
++}
++
+ static bool
+ mt7915_wait_reset_state(struct mt7915_dev *dev, u32 state)
+ {
+@@ -1588,18 +1543,14 @@ mt7915_dma_reset(struct mt7915_dev *dev)
+ mt76_set(dev, MT_WFDMA0_GLO_CFG,
+ MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN);
+ mt76_set(dev, MT_WFDMA1_GLO_CFG,
+- MT_WFDMA1_GLO_CFG_TX_DMA_EN | MT_WFDMA1_GLO_CFG_RX_DMA_EN |
+- MT_WFDMA1_GLO_CFG_OMIT_TX_INFO |
+- MT_WFDMA1_GLO_CFG_OMIT_RX_INFO);
++ MT_WFDMA1_GLO_CFG_TX_DMA_EN | MT_WFDMA1_GLO_CFG_RX_DMA_EN);
+ if (dev->hif2) {
+ mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs,
+ (MT_WFDMA0_GLO_CFG_TX_DMA_EN |
+ MT_WFDMA0_GLO_CFG_RX_DMA_EN));
+ mt76_set(dev, MT_WFDMA1_GLO_CFG + hif1_ofs,
+ (MT_WFDMA1_GLO_CFG_TX_DMA_EN |
+- MT_WFDMA1_GLO_CFG_RX_DMA_EN |
+- MT_WFDMA1_GLO_CFG_OMIT_TX_INFO |
+- MT_WFDMA1_GLO_CFG_OMIT_RX_INFO));
++ MT_WFDMA1_GLO_CFG_RX_DMA_EN));
+ }
+ }
+
+@@ -1610,7 +1561,14 @@ void mt7915_tx_token_put(struct mt7915_dev *dev)
+
+ spin_lock_bh(&dev->mt76.token_lock);
+ idr_for_each_entry(&dev->mt76.token, txwi, id) {
+- mt7915_txwi_free(dev, txwi, NULL, NULL);
++ mt7915_txp_skb_unmap(&dev->mt76, txwi);
++ if (txwi->skb) {
++ struct ieee80211_hw *hw;
++
++ hw = mt76_tx_status_get_hw(&dev->mt76, txwi->skb);
++ ieee80211_free_txskb(hw, txwi->skb);
++ }
++ mt76_put_txwi(&dev->mt76, txwi);
+ dev->mt76.token_count--;
+ }
+ spin_unlock_bh(&dev->mt76.token_lock);
+@@ -1643,6 +1601,11 @@ void mt7915_mac_reset_work(struct work_struct *work)
+ set_bit(MT76_RESET, &phy2->mt76->state);
+ cancel_delayed_work_sync(&phy2->mt76->mac_work);
+ }
++ /* lock/unlock all queues to ensure that no tx is pending */
++ mt76_txq_schedule_all(&dev->mphy);
++ if (ext_phy)
++ mt76_txq_schedule_all(ext_phy);
++
+ mt76_worker_disable(&dev->mt76.tx_worker);
+ napi_disable(&dev->mt76.napi[0]);
+ napi_disable(&dev->mt76.napi[1]);
+@@ -1668,6 +1631,10 @@ void mt7915_mac_reset_work(struct work_struct *work)
+ if (phy2)
+ clear_bit(MT76_RESET, &phy2->mt76->state);
+
++ mt76_worker_enable(&dev->mt76.tx_worker);
++ napi_enable(&dev->mt76.tx_napi);
++ napi_schedule(&dev->mt76.tx_napi);
++
+ napi_enable(&dev->mt76.napi[0]);
+ napi_schedule(&dev->mt76.napi[0]);
+
+@@ -1676,20 +1643,14 @@ void mt7915_mac_reset_work(struct work_struct *work)
+
+ napi_enable(&dev->mt76.napi[2]);
+ napi_schedule(&dev->mt76.napi[2]);
+- tasklet_schedule(&dev->irq_tasklet);
+-
+- mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_RESET_DONE);
+- mt7915_wait_reset_state(dev, MT_MCU_CMD_NORMAL_STATE);
+-
+- mt76_worker_enable(&dev->mt76.tx_worker);
+-
+- napi_enable(&dev->mt76.tx_napi);
+- napi_schedule(&dev->mt76.tx_napi);
+
+ ieee80211_wake_queues(mt76_hw(dev));
+ if (ext_phy)
+ ieee80211_wake_queues(ext_phy->hw);
+
++ mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_RESET_DONE);
++ mt7915_wait_reset_state(dev, MT_MCU_CMD_NORMAL_STATE);
++
+ mutex_unlock(&dev->mt76.mutex);
+
+ mt7915_update_beacons(dev);
+@@ -1703,7 +1664,7 @@ void mt7915_mac_reset_work(struct work_struct *work)
+ }
+
+ static void
+-mt7915_mac_update_stats(struct mt7915_phy *phy)
++mt7915_mac_update_mib_stats(struct mt7915_phy *phy)
+ {
+ struct mt7915_dev *dev = phy->dev;
+ struct mib_stats *mib = &phy->mib;
+@@ -1785,10 +1746,8 @@ void mt7915_mac_sta_rc_work(struct work_struct *work)
+
+ if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED |
+ IEEE80211_RC_NSS_CHANGED |
+- IEEE80211_RC_BW_CHANGED)) {
+- mt7915_mcu_add_he(dev, vif, sta);
++ IEEE80211_RC_BW_CHANGED))
+ mt7915_mcu_add_rate_ctrl(dev, vif, sta);
+- }
+
+ if (changed & IEEE80211_RC_SMPS_CHANGED)
+ mt7915_mcu_add_smps(dev, vif, sta);
+@@ -1810,11 +1769,11 @@ void mt7915_mac_work(struct work_struct *work)
+
+ mutex_lock(&mphy->dev->mutex);
+
+- mt76_update_survey(mphy);
++ mt76_update_survey(mphy->dev);
+ if (++mphy->mac_work_count == 5) {
+ mphy->mac_work_count = 0;
+
+- mt7915_mac_update_stats(phy);
++ mt7915_mac_update_mib_stats(phy);
+ }
+
+ if (++phy->sta_work_count == 10) {
+@@ -1824,8 +1783,6 @@ void mt7915_mac_work(struct work_struct *work)
+
+ mutex_unlock(&mphy->dev->mutex);
+
+- mt76_tx_status_check(mphy->dev, NULL, false);
+-
+ ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
+ MT7915_WATCHDOG_TIME);
+ }
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.h b/drivers/net/wireless/mediatek/mt76/mt7915/mac.h
+index eb1885f4bd8e..0f929fb53027 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.h
++++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.h
+@@ -304,62 +304,6 @@ struct mt7915_tx_free {
+ /* will support this field in further revision */
+ #define MT_TX_FREE_RATE GENMASK(13, 0)
+
+-#define MT_TXS0_FIXED_RATE BIT(31)
+-#define MT_TXS0_BW GENMASK(30, 29)
+-#define MT_TXS0_TID GENMASK(28, 26)
+-#define MT_TXS0_AMPDU BIT(25)
+-#define MT_TXS0_TXS_FORMAT GENMASK(24, 23)
+-#define MT_TXS0_BA_ERROR BIT(22)
+-#define MT_TXS0_PS_FLAG BIT(21)
+-#define MT_TXS0_TXOP_TIMEOUT BIT(20)
+-#define MT_TXS0_BIP_ERROR BIT(19)
+-
+-#define MT_TXS0_QUEUE_TIMEOUT BIT(18)
+-#define MT_TXS0_RTS_TIMEOUT BIT(17)
+-#define MT_TXS0_ACK_TIMEOUT BIT(16)
+-#define MT_TXS0_ACK_ERROR_MASK GENMASK(18, 16)
+-
+-#define MT_TXS0_TX_STATUS_HOST BIT(15)
+-#define MT_TXS0_TX_STATUS_MCU BIT(14)
+-#define MT_TXS0_TX_RATE GENMASK(13, 0)
+-
+-#define MT_TXS1_SEQNO GENMASK(31, 20)
+-#define MT_TXS1_RESP_RATE GENMASK(19, 16)
+-#define MT_TXS1_RXV_SEQNO GENMASK(15, 8)
+-#define MT_TXS1_TX_POWER_DBM GENMASK(7, 0)
+-
+-#define MT_TXS2_BF_STATUS GENMASK(31, 30)
+-#define MT_TXS2_LAST_TX_RATE GENMASK(29, 27)
+-#define MT_TXS2_SHARED_ANTENNA BIT(26)
+-#define MT_TXS2_WCID GENMASK(25, 16)
+-#define MT_TXS2_TX_DELAY GENMASK(15, 0)
+-
+-#define MT_TXS3_PID GENMASK(31, 24)
+-#define MT_TXS3_ANT_ID GENMASK(23, 0)
+-
+-#define MT_TXS4_TIMESTAMP GENMASK(31, 0)
+-
+-#define MT_TXS5_F0_FINAL_MPDU BIT(31)
+-#define MT_TXS5_F0_QOS BIT(30)
+-#define MT_TXS5_F0_TX_COUNT GENMASK(29, 25)
+-#define MT_TXS5_F0_FRONT_TIME GENMASK(24, 0)
+-#define MT_TXS5_F1_MPDU_TX_COUNT GENMASK(31, 24)
+-#define MT_TXS5_F1_MPDU_TX_BYTES GENMASK(23, 0)
+-
+-#define MT_TXS6_F0_NOISE_3 GENMASK(31, 24)
+-#define MT_TXS6_F0_NOISE_2 GENMASK(23, 16)
+-#define MT_TXS6_F0_NOISE_1 GENMASK(15, 8)
+-#define MT_TXS6_F0_NOISE_0 GENMASK(7, 0)
+-#define MT_TXS6_F1_MPDU_FAIL_COUNT GENMASK(31, 24)
+-#define MT_TXS6_F1_MPDU_FAIL_BYTES GENMASK(23, 0)
+-
+-#define MT_TXS7_F0_RCPI_3 GENMASK(31, 24)
+-#define MT_TXS7_F0_RCPI_2 GENMASK(23, 16)
+-#define MT_TXS7_F0_RCPI_1 GENMASK(15, 8)
+-#define MT_TXS7_F0_RCPI_0 GENMASK(7, 0)
+-#define MT_TXS7_F1_MPDU_RETRY_COUNT GENMASK(31, 24)
+-#define MT_TXS7_F1_MPDU_RETRY_BYTES GENMASK(23, 0)
+-
+ struct mt7915_dfs_pulse {
+ u32 max_width; /* us */
+ int max_pwr; /* dbm */
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
+index c25f8da590dd..e5bd687546b6 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
+@@ -139,6 +139,12 @@ static int get_omac_idx(enum nl80211_iftype type, u64 mask)
+ if (type != NL80211_IFTYPE_STATION)
+ break;
+
++ /* next, try to find a free repeater entry for the sta */
++ i = get_free_idx(mask >> REPEATER_BSSID_START, 0,
++ REPEATER_BSSID_MAX - REPEATER_BSSID_START);
++ if (i)
++ return i + 32 - 1;
++
+ i = get_free_idx(mask, EXT_BSSID_1, EXT_BSSID_MAX);
+ if (i)
+ return i - 1;
+@@ -166,22 +172,6 @@ static int get_omac_idx(enum nl80211_iftype type, u64 mask)
+ return -1;
+ }
+
+-static void mt7915_init_bitrate_mask(struct ieee80211_vif *vif)
+-{
+- struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(mvif->bitrate_mask.control); i++) {
+- mvif->bitrate_mask.control[i].legacy = GENMASK(31, 0);
+- memset(mvif->bitrate_mask.control[i].ht_mcs, GENMASK(7, 0),
+- sizeof(mvif->bitrate_mask.control[i].ht_mcs));
+- memset(mvif->bitrate_mask.control[i].vht_mcs, GENMASK(15, 0),
+- sizeof(mvif->bitrate_mask.control[i].vht_mcs));
+- memset(mvif->bitrate_mask.control[i].he_mcs, GENMASK(15, 0),
+- sizeof(mvif->bitrate_mask.control[i].he_mcs));
+- }
+-}
+-
+ static int mt7915_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+ {
+@@ -251,8 +241,6 @@ static int mt7915_add_interface(struct ieee80211_hw *hw,
+ vif->offload_flags = 0;
+ vif->offload_flags |= IEEE80211_OFFLOAD_ENCAP_4ADDR;
+
+- mt7915_init_bitrate_mask(vif);
+-
+ out:
+ mutex_unlock(&dev->mt76.mutex);
+
+@@ -810,8 +798,7 @@ mt7915_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+
+ n = mvif->omac_idx > HW_BSSID_MAX ? HW_BSSID_0 : mvif->omac_idx;
+ /* TSF software read */
+- mt76_rmw(dev, MT_LPON_TCR(band, n), MT_LPON_TCR_SW_MODE,
+- MT_LPON_TCR_SW_READ);
++ mt76_set(dev, MT_LPON_TCR(band, n), MT_LPON_TCR_SW_MODE);
+ tsf.t32[0] = mt76_rr(dev, MT_LPON_UTTR0(band));
+ tsf.t32[1] = mt76_rr(dev, MT_LPON_UTTR1(band));
+
+@@ -840,34 +827,7 @@ mt7915_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ mt76_wr(dev, MT_LPON_UTTR0(band), tsf.t32[0]);
+ mt76_wr(dev, MT_LPON_UTTR1(band), tsf.t32[1]);
+ /* TSF software overwrite */
+- mt76_rmw(dev, MT_LPON_TCR(band, n), MT_LPON_TCR_SW_MODE,
+- MT_LPON_TCR_SW_WRITE);
+-
+- mutex_unlock(&dev->mt76.mutex);
+-}
+-
+-static void
+-mt7915_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+- s64 timestamp)
+-{
+- struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+- struct mt7915_dev *dev = mt7915_hw_dev(hw);
+- struct mt7915_phy *phy = mt7915_hw_phy(hw);
+- bool band = phy != &dev->phy;
+- union {
+- u64 t64;
+- u32 t32[2];
+- } tsf = { .t64 = timestamp, };
+- u16 n;
+-
+- mutex_lock(&dev->mt76.mutex);
+-
+- n = mvif->omac_idx > HW_BSSID_MAX ? HW_BSSID_0 : mvif->omac_idx;
+- mt76_wr(dev, MT_LPON_UTTR0(band), tsf.t32[0]);
+- mt76_wr(dev, MT_LPON_UTTR1(band), tsf.t32[1]);
+- /* TSF software adjust*/
+- mt76_rmw(dev, MT_LPON_TCR(band, n), MT_LPON_TCR_SW_MODE,
+- MT_LPON_TCR_SW_ADJUST);
++ mt76_set(dev, MT_LPON_TCR(band, n), MT_LPON_TCR_SW_WRITE);
+
+ mutex_unlock(&dev->mt76.mutex);
+ }
+@@ -951,15 +911,17 @@ static void mt7915_sta_statistics(struct ieee80211_hw *hw,
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
+ }
+
+-static void mt7915_sta_rc_work(void *data, struct ieee80211_sta *sta)
++static void
++mt7915_sta_rc_update(struct ieee80211_hw *hw,
++ struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta,
++ u32 changed)
+ {
++ struct mt7915_dev *dev = mt7915_hw_dev(hw);
+ struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
+- struct mt7915_dev *dev = msta->vif->phy->dev;
+- struct ieee80211_hw *hw = msta->vif->phy->mt76->hw;
+- u32 *changed = data;
+
+ spin_lock_bh(&dev->sta_poll_lock);
+- msta->stats.changed |= *changed;
++ msta->stats.changed |= changed;
+ if (list_empty(&msta->rc_list))
+ list_add_tail(&msta->rc_list, &dev->sta_rc_list);
+ spin_unlock_bh(&dev->sta_poll_lock);
+@@ -967,39 +929,6 @@ static void mt7915_sta_rc_work(void *data, struct ieee80211_sta *sta)
+ ieee80211_queue_work(hw, &dev->rc_work);
+ }
+
+-static void mt7915_sta_rc_update(struct ieee80211_hw *hw,
+- struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta,
+- u32 changed)
+-{
+- mt7915_sta_rc_work(&changed, sta);
+-}
+-
+-static int
+-mt7915_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+- const struct cfg80211_bitrate_mask *mask)
+-{
+- struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+- enum nl80211_band band = mvif->phy->mt76->chandef.chan->band;
+- u32 changed;
+-
+- if (mask->control[band].gi == NL80211_TXRATE_FORCE_LGI)
+- return -EINVAL;
+-
+- changed = IEEE80211_RC_SUPP_RATES_CHANGED;
+- mvif->bitrate_mask = *mask;
+-
+- /* Update firmware rate control to add a boundary on top of table
+- * to limit the rate selection for each peer, so when set bitrates
+- * vht-mcs-5 1:9, which actually means nss = 1 mcs = 0~9. This only
+- * applies to data frames as for the other mgmt, mcast, bcast still
+- * use legacy rates as it is.
+- */
+- ieee80211_iterate_stations_atomic(hw, mt7915_sta_rc_work, &changed);
+-
+- return 0;
+-}
+-
+ static void mt7915_sta_set_4addr(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+@@ -1058,11 +987,9 @@ const struct ieee80211_ops mt7915_ops = {
+ .get_stats = mt7915_get_stats,
+ .get_tsf = mt7915_get_tsf,
+ .set_tsf = mt7915_set_tsf,
+- .offset_tsf = mt7915_offset_tsf,
+ .get_survey = mt76_get_survey,
+ .get_antenna = mt76_get_antenna,
+ .set_antenna = mt7915_set_antenna,
+- .set_bitrate_mask = mt7915_set_bitrate_mask,
+ .set_coverage_class = mt7915_set_coverage_class,
+ .sta_statistics = mt7915_sta_statistics,
+ .sta_set_4addr = mt7915_sta_set_4addr,
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
+index 43960770a9af..106177072d18 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
+@@ -147,10 +147,10 @@ mt7915_get_he_phy_cap(struct mt7915_phy *phy, struct ieee80211_vif *vif)
+ }
+
+ static u8
+-mt7915_get_phy_mode(struct ieee80211_vif *vif, struct ieee80211_sta *sta)
++mt7915_get_phy_mode(struct mt76_phy *mphy, struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta)
+ {
+- struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+- enum nl80211_band band = mvif->phy->mt76->chandef.chan->band;
++ enum nl80211_band band = mphy->chandef.chan->band;
+ struct ieee80211_sta_ht_cap *ht_cap;
+ struct ieee80211_sta_vht_cap *vht_cap;
+ const struct ieee80211_sta_he_cap *he_cap;
+@@ -163,7 +163,7 @@ mt7915_get_phy_mode(struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+ } else {
+ struct ieee80211_supported_band *sband;
+
+- sband = mvif->phy->mt76->hw->wiphy->bands[band];
++ sband = mphy->hw->wiphy->bands[band];
+
+ ht_cap = &sband->ht_cap;
+ vht_cap = &sband->vht_cap;
+@@ -209,112 +209,6 @@ mt7915_mcu_get_sta_nss(u16 mcs_map)
+ return nss - 1;
+ }
+
+-static void
+-mt7915_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs,
+- const u16 *mask)
+-{
+- struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
+- struct cfg80211_chan_def *chandef = &msta->vif->phy->mt76->chandef;
+- int nss, max_nss = sta->rx_nss > 3 ? 4 : sta->rx_nss;
+- u16 mcs_map;
+-
+- switch (chandef->width) {
+- case NL80211_CHAN_WIDTH_80P80:
+- mcs_map = le16_to_cpu(sta->he_cap.he_mcs_nss_supp.rx_mcs_80p80);
+- break;
+- case NL80211_CHAN_WIDTH_160:
+- mcs_map = le16_to_cpu(sta->he_cap.he_mcs_nss_supp.rx_mcs_160);
+- break;
+- default:
+- mcs_map = le16_to_cpu(sta->he_cap.he_mcs_nss_supp.rx_mcs_80);
+- break;
+- }
+-
+- for (nss = 0; nss < max_nss; nss++) {
+- int mcs;
+-
+- switch ((mcs_map >> (2 * nss)) & 0x3) {
+- case IEEE80211_HE_MCS_SUPPORT_0_11:
+- mcs = GENMASK(11, 0);
+- break;
+- case IEEE80211_HE_MCS_SUPPORT_0_9:
+- mcs = GENMASK(9, 0);
+- break;
+- case IEEE80211_HE_MCS_SUPPORT_0_7:
+- mcs = GENMASK(7, 0);
+- break;
+- default:
+- mcs = 0;
+- }
+-
+- mcs = mcs ? fls(mcs & mask[nss]) - 1 : -1;
+-
+- switch (mcs) {
+- case 0 ... 7:
+- mcs = IEEE80211_HE_MCS_SUPPORT_0_7;
+- break;
+- case 8 ... 9:
+- mcs = IEEE80211_HE_MCS_SUPPORT_0_9;
+- break;
+- case 10 ... 11:
+- mcs = IEEE80211_HE_MCS_SUPPORT_0_11;
+- break;
+- default:
+- mcs = IEEE80211_HE_MCS_NOT_SUPPORTED;
+- break;
+- }
+- mcs_map &= ~(0x3 << (nss * 2));
+- mcs_map |= mcs << (nss * 2);
+-
+- /* only support 2ss on 160MHz */
+- if (nss > 1 && (sta->bandwidth == IEEE80211_STA_RX_BW_160))
+- break;
+- }
+-
+- *he_mcs = cpu_to_le16(mcs_map);
+-}
+-
+-static void
+-mt7915_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs,
+- const u16 *mask)
+-{
+- u16 mcs_map = le16_to_cpu(sta->vht_cap.vht_mcs.rx_mcs_map);
+- int nss, max_nss = sta->rx_nss > 3 ? 4 : sta->rx_nss;
+- u16 mcs;
+-
+- for (nss = 0; nss < max_nss; nss++, mcs_map >>= 2) {
+- switch (mcs_map & 0x3) {
+- case IEEE80211_VHT_MCS_SUPPORT_0_9:
+- mcs = GENMASK(9, 0);
+- break;
+- case IEEE80211_VHT_MCS_SUPPORT_0_8:
+- mcs = GENMASK(8, 0);
+- break;
+- case IEEE80211_VHT_MCS_SUPPORT_0_7:
+- mcs = GENMASK(7, 0);
+- break;
+- default:
+- mcs = 0;
+- }
+-
+- vht_mcs[nss] = cpu_to_le16(mcs & mask[nss]);
+-
+- /* only support 2ss on 160MHz */
+- if (nss > 1 && (sta->bandwidth == IEEE80211_STA_RX_BW_160))
+- break;
+- }
+-}
+-
+-static void
+-mt7915_mcu_set_sta_ht_mcs(struct ieee80211_sta *sta, u8 *ht_mcs,
+- const u8 *mask)
+-{
+- int nss, max_nss = sta->rx_nss > 3 ? 4 : sta->rx_nss;
+-
+- for (nss = 0; nss < max_nss; nss++)
+- ht_mcs[nss] = sta->ht_cap.mcs.rx_mask[nss] & mask[nss];
+-}
+-
+ static int
+ mt7915_mcu_parse_response(struct mt76_dev *mdev, int cmd,
+ struct sk_buff *skb, int seq)
+@@ -455,24 +349,6 @@ mt7915_mcu_rx_csa_notify(struct mt7915_dev *dev, struct sk_buff *skb)
+ mt7915_mcu_csa_finish, mphy->hw);
+ }
+
+-static void
+-mt7915_mcu_rx_thermal_notify(struct mt7915_dev *dev, struct sk_buff *skb)
+-{
+- struct mt76_phy *mphy = &dev->mt76.phy;
+- struct mt7915_mcu_thermal_notify *t;
+- struct mt7915_phy *phy;
+-
+- t = (struct mt7915_mcu_thermal_notify *)skb->data;
+- if (t->ctrl.ctrl_id != THERMAL_PROTECT_ENABLE)
+- return;
+-
+- if (t->ctrl.band_idx && dev->mt76.phy2)
+- mphy = dev->mt76.phy2;
+-
+- phy = (struct mt7915_phy *)mphy->priv;
+- phy->throttle_state = t->ctrl.duty.duty_cycle;
+-}
+-
+ static void
+ mt7915_mcu_rx_radar_detected(struct mt7915_dev *dev, struct sk_buff *skb)
+ {
+@@ -593,7 +469,6 @@ mt7915_mcu_tx_rate_report(struct mt7915_dev *dev, struct sk_buff *skb)
+ u16 attempts = le16_to_cpu(ra->attempts);
+ u16 curr = le16_to_cpu(ra->curr_rate);
+ u16 wcidx = le16_to_cpu(ra->wlan_idx);
+- struct ieee80211_tx_status status = {};
+ struct mt76_phy *mphy = &dev->mphy;
+ struct mt7915_sta_stats *stats;
+ struct mt7915_sta *msta;
+@@ -625,13 +500,6 @@ mt7915_mcu_tx_rate_report(struct mt7915_dev *dev, struct sk_buff *skb)
+
+ stats->per = 1000 * (attempts - success) / attempts;
+ }
+-
+- status.sta = wcid_to_sta(wcid);
+- if (!status.sta)
+- return;
+-
+- status.rate = &stats->tx_rate;
+- ieee80211_tx_status_ext(mphy->hw, &status);
+ }
+
+ static void
+@@ -663,9 +531,6 @@ mt7915_mcu_rx_ext_event(struct mt7915_dev *dev, struct sk_buff *skb)
+ struct mt7915_mcu_rxd *rxd = (struct mt7915_mcu_rxd *)skb->data;
+
+ switch (rxd->ext_eid) {
+- case MCU_EXT_EVENT_THERMAL_PROTECT:
+- mt7915_mcu_rx_thermal_notify(dev, skb);
+- break;
+ case MCU_EXT_EVENT_RDD_REPORT:
+ mt7915_mcu_rx_radar_detected(dev, skb);
+ break;
+@@ -868,7 +733,7 @@ mt7915_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ memcpy(bss->bssid, vif->bss_conf.bssid, ETH_ALEN);
+ bss->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int);
+ bss->dtim_period = vif->bss_conf.dtim_period;
+- bss->phy_mode = mt7915_get_phy_mode(vif, NULL);
++ bss->phy_mode = mt7915_get_phy_mode(phy->mt76, vif, NULL);
+ } else {
+ memcpy(bss->bssid, phy->mt76->macaddr, ETH_ALEN);
+ }
+@@ -1471,11 +1336,8 @@ mt7915_mcu_sta_basic_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ static void
+ mt7915_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ {
+- struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
+ struct ieee80211_sta_he_cap *he_cap = &sta->he_cap;
+ struct ieee80211_he_cap_elem *elem = &he_cap->he_cap_elem;
+- enum nl80211_band band = msta->vif->phy->mt76->chandef.chan->band;
+- const u16 *mcs_mask = msta->vif->bitrate_mask.control[band].he_mcs;
+ struct sta_rec_he *he;
+ struct tlv *tlv;
+ u32 cap = 0;
+@@ -1566,18 +1428,15 @@ mt7915_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ case IEEE80211_STA_RX_BW_160:
+ if (elem->phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)
+- mt7915_mcu_set_sta_he_mcs(sta,
+- &he->max_nss_mcs[CMD_HE_MCS_BW8080],
+- mcs_mask);
++ he->max_nss_mcs[CMD_HE_MCS_BW8080] =
++ he_cap->he_mcs_nss_supp.rx_mcs_80p80;
+
+- mt7915_mcu_set_sta_he_mcs(sta,
+- &he->max_nss_mcs[CMD_HE_MCS_BW160],
+- mcs_mask);
++ he->max_nss_mcs[CMD_HE_MCS_BW160] =
++ he_cap->he_mcs_nss_supp.rx_mcs_160;
+ fallthrough;
+ default:
+- mt7915_mcu_set_sta_he_mcs(sta,
+- &he->max_nss_mcs[CMD_HE_MCS_BW80],
+- mcs_mask);
++ he->max_nss_mcs[CMD_HE_MCS_BW80] =
++ he_cap->he_mcs_nss_supp.rx_mcs_80;
+ break;
+ }
+
+@@ -1685,18 +1544,27 @@ mt7915_mcu_sta_muru_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ HE_PHY(CAP2_UL_MU_PARTIAL_MU_MIMO, elem->phy_cap_info[2]);
+ }
+
+-static void
+-mt7915_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
++static int
++mt7915_mcu_add_mu(struct mt7915_dev *dev, struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta)
+ {
+- struct sta_rec_vht *vht;
+- struct tlv *tlv;
++ struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
++ struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
++ struct sk_buff *skb;
++ int len = sizeof(struct sta_req_hdr) + sizeof(struct sta_rec_muru);
++
++ if (!sta->vht_cap.vht_supported && !sta->he_cap.has_he)
++ return 0;
++
++ skb = mt7915_mcu_alloc_sta_req(dev, mvif, msta, len);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
+
+- tlv = mt7915_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht));
++ /* starec muru */
++ mt7915_mcu_sta_muru_tlv(skb, sta);
+
+- vht = (struct sta_rec_vht *)tlv;
+- vht->vht_cap = cpu_to_le32(sta->vht_cap.cap);
+- vht->vht_rx_mcs_map = sta->vht_cap.vht_mcs.rx_mcs_map;
+- vht->vht_tx_mcs_map = sta->vht_cap.vht_mcs.tx_mcs_map;
++ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
++ MCU_EXT_CMD(STA_REC_UPDATE), true);
+ }
+
+ static void
+@@ -1748,6 +1616,17 @@ mt7915_mcu_sta_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
+ mt7915_mcu_sta_amsdu_tlv(skb, sta);
+ }
+
++ /* starec vht */
++ if (sta->vht_cap.vht_supported) {
++ struct sta_rec_vht *vht;
++
++ tlv = mt7915_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht));
++ vht = (struct sta_rec_vht *)tlv;
++ vht->vht_cap = cpu_to_le32(sta->vht_cap.cap);
++ vht->vht_rx_mcs_map = sta->vht_cap.vht_mcs.rx_mcs_map;
++ vht->vht_tx_mcs_map = sta->vht_cap.vht_mcs.tx_mcs_map;
++ }
++
+ /* starec he */
+ if (sta->he_cap.has_he)
+ mt7915_mcu_sta_he_tlv(skb, sta);
+@@ -2137,21 +2016,26 @@ mt7915_mcu_add_txbf(struct mt7915_dev *dev, struct ieee80211_vif *vif,
+ vc = mt7915_get_he_phy_cap(phy, vif);
+ ve = &vc->he_cap_elem;
+
+- ebfee = !!(HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]) &&
++ ebfee = !!((HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]) ||
++ HE_PHY(CAP4_MU_BEAMFORMER, pe->phy_cap_info[4])) &&
+ HE_PHY(CAP4_SU_BEAMFORMEE, ve->phy_cap_info[4]));
+- ebf = !!(HE_PHY(CAP3_SU_BEAMFORMER, ve->phy_cap_info[3]) &&
++ ebf = !!((HE_PHY(CAP3_SU_BEAMFORMER, ve->phy_cap_info[3]) ||
++ HE_PHY(CAP4_MU_BEAMFORMER, ve->phy_cap_info[4])) &&
+ HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]));
+ } else if (sta->vht_cap.vht_supported) {
+ struct ieee80211_sta_vht_cap *pc;
+ struct ieee80211_sta_vht_cap *vc;
++ u32 cr, ce;
+
+ pc = &sta->vht_cap;
+ vc = &phy->mt76->sband_5g.sband.vht_cap;
++ cr = IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
++ IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
++ ce = IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
++ IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+
+- ebfee = !!((pc->cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) &&
+- (vc->cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE));
+- ebf = !!((vc->cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) &&
+- (pc->cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE));
++ ebfee = !!((pc->cap & cr) && (vc->cap & ce));
++ ebf = !!((vc->cap & cr) && (pc->cap & ce));
+ }
+
+ /* must keep each tag independent */
+@@ -2195,47 +2079,57 @@ static void
+ mt7915_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7915_dev *dev,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+ {
+- struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+- struct cfg80211_chan_def *chandef = &mvif->phy->mt76->chandef;
+- struct cfg80211_bitrate_mask *mask = &mvif->bitrate_mask;
+- enum nl80211_band band = chandef->chan->band;
++ struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
++ struct mt76_phy *mphy = &dev->mphy;
++ enum nl80211_band band;
+ struct sta_rec_ra *ra;
+ struct tlv *tlv;
+- u32 supp_rate = sta->supp_rates[band];
+- u32 cap = sta->wme ? STA_CAP_WMM : 0;
++ u32 supp_rate, n_rates, cap = sta->wme ? STA_CAP_WMM : 0;
++ u8 i, nss = sta->rx_nss, mcs = 0;
+
+ tlv = mt7915_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra));
+ ra = (struct sta_rec_ra *)tlv;
+
++ if (msta->wcid.ext_phy && dev->mt76.phy2)
++ mphy = dev->mt76.phy2;
++
++ band = mphy->chandef.chan->band;
++ supp_rate = sta->supp_rates[band];
++ n_rates = hweight32(supp_rate);
++
+ ra->valid = true;
+ ra->auto_rate = true;
+- ra->phy_mode = mt7915_get_phy_mode(vif, sta);
+- ra->channel = chandef->chan->hw_value;
++ ra->phy_mode = mt7915_get_phy_mode(mphy, vif, sta);
++ ra->channel = mphy->chandef.chan->hw_value;
+ ra->bw = sta->bandwidth;
++ ra->rate_len = n_rates;
+ ra->phy.bw = sta->bandwidth;
+
+- if (supp_rate) {
+- supp_rate &= mask->control[band].legacy;
+- ra->rate_len = hweight32(supp_rate);
+-
++ if (n_rates) {
+ if (band == NL80211_BAND_2GHZ) {
+ ra->supp_mode = MODE_CCK;
+ ra->supp_cck_rate = supp_rate & GENMASK(3, 0);
++ ra->phy.type = MT_PHY_TYPE_CCK;
+
+- if (ra->rate_len > 4) {
++ if (n_rates > 4) {
+ ra->supp_mode |= MODE_OFDM;
+ ra->supp_ofdm_rate = supp_rate >> 4;
++ ra->phy.type = MT_PHY_TYPE_OFDM;
+ }
+ } else {
+ ra->supp_mode = MODE_OFDM;
+ ra->supp_ofdm_rate = supp_rate;
++ ra->phy.type = MT_PHY_TYPE_OFDM;
+ }
+ }
+
+ if (sta->ht_cap.ht_supported) {
+- const u8 *mcs_mask = mask->control[band].ht_mcs;
++ for (i = 0; i < nss; i++)
++ ra->ht_mcs[i] = sta->ht_cap.mcs.rx_mask[i];
+
++ ra->supp_ht_mcs = *(__le32 *)ra->ht_mcs;
+ ra->supp_mode |= MODE_HT;
++ mcs = hweight32(le32_to_cpu(ra->supp_ht_mcs)) - 1;
+ ra->af = sta->ht_cap.ampdu_factor;
+ ra->ht_gf = !!(sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD);
+
+@@ -2250,16 +2144,13 @@ mt7915_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7915_dev *dev,
+ cap |= STA_CAP_RX_STBC;
+ if (sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING)
+ cap |= STA_CAP_LDPC;
+-
+- mt7915_mcu_set_sta_ht_mcs(sta, ra->ht_mcs, mcs_mask);
+- ra->supp_ht_mcs = *(__le32 *)ra->ht_mcs;
+ }
+
+ if (sta->vht_cap.vht_supported) {
+- const u16 *mcs_mask = mask->control[band].vht_mcs;
+- u8 af;
++ u16 mcs_map = le16_to_cpu(sta->vht_cap.vht_mcs.rx_mcs_map);
++ u16 vht_mcs;
++ u8 af, mcs_prev;
+
+- ra->supp_mode |= MODE_VHT;
+ af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK,
+ sta->vht_cap.cap);
+ ra->af = max_t(u8, ra->af, af);
+@@ -2276,7 +2167,33 @@ mt7915_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7915_dev *dev,
+ if (sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC)
+ cap |= STA_CAP_VHT_LDPC;
+
+- mt7915_mcu_set_sta_vht_mcs(sta, ra->supp_vht_mcs, mcs_mask);
++ ra->supp_mode |= MODE_VHT;
++ for (mcs = 0, i = 0; i < nss; i++, mcs_map >>= 2) {
++ switch (mcs_map & 0x3) {
++ case IEEE80211_VHT_MCS_SUPPORT_0_9:
++ vht_mcs = GENMASK(9, 0);
++ break;
++ case IEEE80211_VHT_MCS_SUPPORT_0_8:
++ vht_mcs = GENMASK(8, 0);
++ break;
++ case IEEE80211_VHT_MCS_SUPPORT_0_7:
++ vht_mcs = GENMASK(7, 0);
++ break;
++ default:
++ vht_mcs = 0;
++ }
++
++ ra->supp_vht_mcs[i] = cpu_to_le16(vht_mcs);
++
++ mcs_prev = hweight16(vht_mcs) - 1;
++ if (mcs_prev > mcs)
++ mcs = mcs_prev;
++
++ /* only support 2ss on 160MHz */
++ if (i > 1 && (ra->bw == CMD_CBW_160MHZ ||
++ ra->bw == CMD_CBW_8080MHZ))
++ break;
++ }
+ }
+
+ if (sta->he_cap.has_he) {
+@@ -2284,7 +2201,28 @@ mt7915_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7915_dev *dev,
+ cap |= STA_CAP_HE;
+ }
+
+- ra->sta_cap = cpu_to_le32(cap);
++ ra->sta_status = cpu_to_le32(cap);
++
++ switch (BIT(fls(ra->supp_mode) - 1)) {
++ case MODE_VHT:
++ ra->phy.type = MT_PHY_TYPE_VHT;
++ ra->phy.mcs = mcs;
++ ra->phy.nss = nss;
++ ra->phy.stbc = !!(sta->vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC);
++ ra->phy.ldpc = !!(sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC);
++ ra->phy.sgi =
++ !!(sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80);
++ break;
++ case MODE_HT:
++ ra->phy.type = MT_PHY_TYPE_HT;
++ ra->phy.mcs = mcs;
++ ra->phy.ldpc = sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING;
++ ra->phy.stbc = !!(sta->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC);
++ ra->phy.sgi = !!(sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20);
++ break;
++ default:
++ break;
++ }
+ }
+
+ int mt7915_mcu_add_rate_ctrl(struct mt7915_dev *dev, struct ieee80211_vif *vif,
+@@ -2305,87 +2243,6 @@ int mt7915_mcu_add_rate_ctrl(struct mt7915_dev *dev, struct ieee80211_vif *vif,
+ MCU_EXT_CMD(STA_REC_UPDATE), true);
+ }
+
+-int mt7915_mcu_add_he(struct mt7915_dev *dev, struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta)
+-{
+- struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+- struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
+- struct sk_buff *skb;
+- int len;
+-
+- if (!sta->he_cap.has_he)
+- return 0;
+-
+- len = sizeof(struct sta_req_hdr) + sizeof(struct sta_rec_he);
+-
+- skb = mt7915_mcu_alloc_sta_req(dev, mvif, msta, len);
+- if (IS_ERR(skb))
+- return PTR_ERR(skb);
+-
+- mt7915_mcu_sta_he_tlv(skb, sta);
+-
+- return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+- MCU_EXT_CMD(STA_REC_UPDATE), true);
+-}
+-
+-static int
+-mt7915_mcu_add_group(struct mt7915_dev *dev, struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta)
+-{
+-#define MT_STA_BSS_GROUP 1
+- struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+- struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
+- struct {
+- __le32 action;
+- u8 wlan_idx_lo;
+- u8 status;
+- u8 wlan_idx_hi;
+- u8 rsv0[5];
+- __le32 val;
+- u8 rsv1[8];
+- } __packed req = {
+- .action = cpu_to_le32(MT_STA_BSS_GROUP),
+- .wlan_idx_lo = to_wcid_lo(msta->wcid.idx),
+- .wlan_idx_hi = to_wcid_hi(msta->wcid.idx),
+- .val = cpu_to_le32(mvif->idx % 16),
+- };
+-
+- return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_DRR_CTRL), &req,
+- sizeof(req), true);
+-}
+-
+-static int
+-mt7915_mcu_add_mu(struct mt7915_dev *dev, struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta)
+-{
+- struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+- struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
+- struct sk_buff *skb;
+- int ret;
+-
+- if (!sta->vht_cap.vht_supported && !sta->he_cap.has_he)
+- return 0;
+-
+- ret = mt7915_mcu_add_group(dev, vif, sta);
+- if (ret)
+- return ret;
+-
+- skb = mt7915_mcu_alloc_sta_req(dev, mvif, msta,
+- MT7915_STA_UPDATE_MAX_SIZE);
+- if (IS_ERR(skb))
+- return PTR_ERR(skb);
+-
+- /* wait until TxBF and MU ready to update stare vht */
+-
+- /* starec muru */
+- mt7915_mcu_sta_muru_tlv(skb, sta);
+- /* starec vht */
+- mt7915_mcu_sta_vht_tlv(skb, sta);
+-
+- return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+- MCU_EXT_CMD(STA_REC_UPDATE), true);
+-}
+-
+ int mt7915_mcu_add_sta_adv(struct mt7915_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, bool enable)
+ {
+@@ -2396,14 +2253,17 @@ int mt7915_mcu_add_sta_adv(struct mt7915_dev *dev, struct ieee80211_vif *vif,
+
+ /* must keep the order */
+ ret = mt7915_mcu_add_txbf(dev, vif, sta, enable);
+- if (ret || !enable)
++ if (ret)
+ return ret;
+
+ ret = mt7915_mcu_add_mu(dev, vif, sta);
+ if (ret)
+ return ret;
+
+- return mt7915_mcu_add_rate_ctrl(dev, vif, sta);
++ if (enable)
++ return mt7915_mcu_add_rate_ctrl(dev, vif, sta);
++
++ return 0;
+ }
+
+ int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif,
+@@ -2572,7 +2432,7 @@ mt7915_mcu_beacon_cont(struct mt7915_dev *dev, struct sk_buff *rskb,
+ cont->csa_ofs = cpu_to_le16(offs->cntdwn_counter_offs[0] - 4);
+
+ buf = (u8 *)tlv + sizeof(*cont);
+- mt7915_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, 0, NULL,
++ mt7915_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL,
+ true);
+ memcpy(buf + MT_TXD_SIZE, skb->data, skb->len);
+ }
+@@ -3447,8 +3307,7 @@ int mt7915_mcu_set_eeprom(struct mt7915_dev *dev)
+ int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset)
+ {
+ struct mt7915_mcu_eeprom_info req = {
+- .addr = cpu_to_le32(round_down(offset,
+- MT7915_EEPROM_BLOCK_SIZE)),
++ .addr = cpu_to_le32(round_down(offset, 16)),
+ };
+ struct mt7915_mcu_eeprom_info *res;
+ struct sk_buff *skb;
+@@ -3462,7 +3321,7 @@ int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset)
+
+ res = (struct mt7915_mcu_eeprom_info *)skb->data;
+ buf = dev->mt76.eeprom.data + le32_to_cpu(res->addr);
+- memcpy(buf, res->data, MT7915_EEPROM_BLOCK_SIZE);
++ memcpy(buf, res->data, 16);
+ dev_kfree_skb(skb);
+
+ return 0;
+@@ -3611,128 +3470,22 @@ int mt7915_mcu_apply_tx_dpd(struct mt7915_phy *phy)
+ return 0;
+ }
+
+-int mt7915_mcu_get_chan_mib_info(struct mt7915_phy *phy, bool chan_switch)
+-{
+- /* strict order */
+- static const enum mt7915_chan_mib_offs offs[] = {
+- MIB_BUSY_TIME, MIB_TX_TIME, MIB_RX_TIME, MIB_OBSS_AIRTIME
+- };
+- struct mt76_channel_state *state = phy->mt76->chan_state;
+- struct mt76_channel_state *state_ts = &phy->state_ts;
+- struct mt7915_dev *dev = phy->dev;
+- struct mt7915_mcu_mib *res, req[4];
+- struct sk_buff *skb;
+- int i, ret;
+-
+- for (i = 0; i < 4; i++) {
+- req[i].band = cpu_to_le32(phy != &dev->phy);
+- req[i].offs = cpu_to_le32(offs[i]);
+- }
+-
+- ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_CMD(GET_MIB_INFO),
+- req, sizeof(req), true, &skb);
+- if (ret)
+- return ret;
+-
+- res = (struct mt7915_mcu_mib *)(skb->data + 20);
+-
+- if (chan_switch)
+- goto out;
+-
+-#define __res_u64(s) le64_to_cpu(res[s].data)
+- state->cc_busy += __res_u64(0) - state_ts->cc_busy;
+- state->cc_tx += __res_u64(1) - state_ts->cc_tx;
+- state->cc_bss_rx += __res_u64(2) - state_ts->cc_bss_rx;
+- state->cc_rx += __res_u64(2) + __res_u64(3) - state_ts->cc_rx;
+-
+-out:
+- state_ts->cc_busy = __res_u64(0);
+- state_ts->cc_tx = __res_u64(1);
+- state_ts->cc_bss_rx = __res_u64(2);
+- state_ts->cc_rx = __res_u64(2) + __res_u64(3);
+-#undef __res_u64
+-
+- dev_kfree_skb(skb);
+-
+- return 0;
+-}
+-
+-int mt7915_mcu_get_temperature(struct mt7915_phy *phy)
++int mt7915_mcu_get_temperature(struct mt7915_dev *dev, int index)
+ {
+- struct mt7915_dev *dev = phy->dev;
+ struct {
+ u8 ctrl_id;
+ u8 action;
+- u8 dbdc_idx;
++ u8 band;
+ u8 rsv[5];
+ } req = {
+ .ctrl_id = THERMAL_SENSOR_TEMP_QUERY,
+- .dbdc_idx = phy != &dev->phy,
++ .action = index,
+ };
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(THERMAL_CTRL), &req,
+ sizeof(req), true);
+ }
+
+-int mt7915_mcu_set_thermal_throttling(struct mt7915_phy *phy, u8 state)
+-{
+- struct mt7915_dev *dev = phy->dev;
+- struct {
+- struct mt7915_mcu_thermal_ctrl ctrl;
+-
+- __le32 trigger_temp;
+- __le32 restore_temp;
+- __le16 sustain_time;
+- u8 rsv[2];
+- } __packed req = {
+- .ctrl = {
+- .band_idx = phy != &dev->phy,
+- },
+- };
+- int level;
+-
+-#define TRIGGER_TEMPERATURE 122
+-#define RESTORE_TEMPERATURE 116
+-#define SUSTAIN_PERIOD 10
+-
+- if (!state) {
+- req.ctrl.ctrl_id = THERMAL_PROTECT_DISABLE;
+- goto out;
+- }
+-
+- /* set duty cycle and level */
+- for (level = 0; level < 4; level++) {
+- int ret;
+-
+- req.ctrl.ctrl_id = THERMAL_PROTECT_DUTY_CONFIG;
+- req.ctrl.duty.duty_level = level;
+- req.ctrl.duty.duty_cycle = state;
+- state = state * 4 / 5;
+-
+- ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(THERMAL_PROT),
+- &req, sizeof(req.ctrl), false);
+- if (ret)
+- return ret;
+- }
+-
+- /* currently use fixed values for throttling, and would be better
+- * to implement thermal zone for dynamic trip in the long run.
+- */
+-
+- /* set high-temperature trigger threshold */
+- req.ctrl.ctrl_id = THERMAL_PROTECT_ENABLE;
+- req.trigger_temp = cpu_to_le32(TRIGGER_TEMPERATURE);
+- req.restore_temp = cpu_to_le32(RESTORE_TEMPERATURE);
+- req.sustain_time = cpu_to_le16(SUSTAIN_PERIOD);
+-
+-out:
+- req.ctrl.type.protect_type = 1;
+- req.ctrl.type.trigger_type = 1;
+-
+- return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(THERMAL_PROT),
+- &req, sizeof(req), false);
+-}
+-
+ int mt7915_mcu_get_tx_rate(struct mt7915_dev *dev, u32 cmd, u16 wlan_idx)
+ {
+ struct {
+@@ -3753,6 +3506,7 @@ int mt7915_mcu_get_tx_rate(struct mt7915_dev *dev, u32 cmd, u16 wlan_idx)
+
+ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy)
+ {
++#define MT7915_SKU_RATE_NUM 161
+ struct mt7915_dev *dev = phy->dev;
+ struct mt76_phy *mphy = phy->mt76;
+ struct ieee80211_hw *hw = mphy->hw;
+@@ -3802,39 +3556,6 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy)
+ sizeof(req), true);
+ }
+
+-int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len)
+-{
+-#define RATE_POWER_INFO 2
+- struct mt7915_dev *dev = phy->dev;
+- struct {
+- u8 format_id;
+- u8 category;
+- u8 band;
+- u8 _rsv;
+- } __packed req = {
+- .format_id = 7,
+- .category = RATE_POWER_INFO,
+- .band = phy != &dev->phy,
+- };
+- s8 res[MT7915_SKU_RATE_NUM][2];
+- struct sk_buff *skb;
+- int ret, i;
+-
+- ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+- MCU_EXT_CMD(TX_POWER_FEATURE_CTRL),
+- &req, sizeof(req), true, &skb);
+- if (ret)
+- return ret;
+-
+- memcpy(res, skb->data + 4, sizeof(res));
+- for (i = 0; i < len; i++)
+- txpower[i] = res[i][req.band];
+-
+- dev_kfree_skb(skb);
+-
+- return 0;
+-}
+-
+ int mt7915_mcu_set_test_param(struct mt7915_dev *dev, u8 param, bool test_mode,
+ u8 en)
+ {
+@@ -3893,50 +3614,57 @@ int mt7915_mcu_set_ser(struct mt7915_dev *dev, u8 action, u8 set, u8 band)
+ &req, sizeof(req), false);
+ }
+
+-int mt7915_mcu_set_txbf(struct mt7915_dev *dev, u8 action)
++int mt7915_mcu_set_txbf_module(struct mt7915_dev *dev)
+ {
++#define MT_BF_MODULE_UPDATE 25
+ struct {
+ u8 action;
+- union {
+- struct {
+- u8 snd_mode;
+- u8 sta_num;
+- u8 rsv;
+- u8 wlan_idx[4];
+- __le32 snd_period; /* ms */
+- } __packed snd;
+- struct {
+- bool ebf;
+- bool ibf;
+- u8 rsv;
+- } __packed type;
+- struct {
+- u8 bf_num;
+- u8 bf_bitmap;
+- u8 bf_sel[8];
+- u8 rsv[5];
+- } __packed mod;
+- };
++ u8 bf_num;
++ u8 bf_bitmap;
++ u8 bf_sel[8];
++ u8 rsv[8];
+ } __packed req = {
+- .action = action,
++ .action = MT_BF_MODULE_UPDATE,
++ .bf_num = 2,
++ .bf_bitmap = GENMASK(1, 0),
+ };
+
+-#define MT_BF_PROCESSING 4
+- switch (action) {
+- case MT_BF_SOUNDING_ON:
+- req.snd.snd_mode = MT_BF_PROCESSING;
+- break;
+- case MT_BF_TYPE_UPDATE:
+- req.type.ebf = true;
+- req.type.ibf = dev->ibf;
+- break;
+- case MT_BF_MODULE_UPDATE:
+- req.mod.bf_num = 2;
+- req.mod.bf_bitmap = GENMASK(1, 0);
+- break;
+- default:
+- return -EINVAL;
+- }
++ return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TXBF_ACTION), &req,
++ sizeof(req), true);
++}
++
++int mt7915_mcu_set_txbf_type(struct mt7915_dev *dev)
++{
++#define MT_BF_TYPE_UPDATE 20
++ struct {
++ u8 action;
++ bool ebf;
++ bool ibf;
++ u8 rsv;
++ } __packed req = {
++ .action = MT_BF_TYPE_UPDATE,
++ .ebf = true,
++ .ibf = dev->ibf,
++ };
++
++ return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TXBF_ACTION), &req,
++ sizeof(req), true);
++}
++
++int mt7915_mcu_set_txbf_sounding(struct mt7915_dev *dev)
++{
++#define MT_BF_PROCESSING 4
++ struct {
++ u8 action;
++ u8 snd_mode;
++ u8 sta_num;
++ u8 rsv;
++ u8 wlan_idx[4];
++ __le32 snd_period; /* ms */
++ } __packed req = {
++ .action = true,
++ .snd_mode = MT_BF_PROCESSING,
++ };
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TXBF_ACTION), &req,
+ sizeof(req), true);
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
+index e68a562cc5b4..c0255c3ac7d0 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
++++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
+@@ -68,29 +68,6 @@ struct mt7915_mcu_rxd {
+ u8 s2d_index;
+ };
+
+-struct mt7915_mcu_thermal_ctrl {
+- u8 ctrl_id;
+- u8 band_idx;
+- union {
+- struct {
+- u8 protect_type; /* 1: duty admit, 2: radio off */
+- u8 trigger_type; /* 0: low, 1: high */
+- } __packed type;
+- struct {
+- u8 duty_level; /* level 0~3 */
+- u8 duty_cycle;
+- } __packed duty;
+- };
+-} __packed;
+-
+-struct mt7915_mcu_thermal_notify {
+- struct mt7915_mcu_rxd rxd;
+-
+- struct mt7915_mcu_thermal_ctrl ctrl;
+- __le32 temperature;
+- u8 rsv[8];
+-} __packed;
+-
+ struct mt7915_mcu_csa_notify {
+ struct mt7915_mcu_rxd rxd;
+
+@@ -216,19 +193,6 @@ struct mt7915_mcu_phy_rx_info {
+ #define MT_RA_RATE_DCM_EN BIT(4)
+ #define MT_RA_RATE_BW GENMASK(14, 13)
+
+-struct mt7915_mcu_mib {
+- __le32 band;
+- __le32 offs;
+- __le64 data;
+-} __packed;
+-
+-enum mt7915_chan_mib_offs {
+- MIB_BUSY_TIME = 14,
+- MIB_TX_TIME = 81,
+- MIB_RX_TIME,
+- MIB_OBSS_AIRTIME = 86
+-};
+-
+ struct edca {
+ u8 queue;
+ u8 set;
+@@ -298,7 +262,6 @@ enum {
+ MCU_EXT_CMD_FW_LOG_2_HOST = 0x13,
+ MCU_EXT_CMD_TXBF_ACTION = 0x1e,
+ MCU_EXT_CMD_EFUSE_BUFFER_MODE = 0x21,
+- MCU_EXT_CMD_THERMAL_PROT = 0x23,
+ MCU_EXT_CMD_STA_REC_UPDATE = 0x25,
+ MCU_EXT_CMD_BSS_INFO_UPDATE = 0x26,
+ MCU_EXT_CMD_EDCA_UPDATE = 0x27,
+@@ -314,7 +277,6 @@ enum {
+ MCU_EXT_CMD_MUAR_UPDATE = 0x48,
+ MCU_EXT_CMD_SET_RX_PATH = 0x4e,
+ MCU_EXT_CMD_TX_POWER_FEATURE_CTRL = 0x58,
+- MCU_EXT_CMD_GET_MIB_INFO = 0x5a,
+ MCU_EXT_CMD_MWDS_SUPPORT = 0x80,
+ MCU_EXT_CMD_SET_SER_TRIGGER = 0x81,
+ MCU_EXT_CMD_SCS_CTRL = 0x82,
+@@ -957,7 +919,7 @@ struct sta_rec_ra {
+ u8 op_vht_rx_nss;
+ u8 op_vht_rx_nss_type;
+
+- __le32 sta_cap;
++ __le32 sta_status;
+
+ struct ra_phy phy;
+ } __packed;
+@@ -1104,28 +1066,11 @@ enum {
+ THERMAL_SENSOR_TASK_CTRL,
+ };
+
+-enum {
+- THERMAL_PROTECT_PARAMETER_CTRL,
+- THERMAL_PROTECT_BASIC_INFO,
+- THERMAL_PROTECT_ENABLE,
+- THERMAL_PROTECT_DISABLE,
+- THERMAL_PROTECT_DUTY_CONFIG,
+- THERMAL_PROTECT_MECH_INFO,
+- THERMAL_PROTECT_DUTY_INFO,
+- THERMAL_PROTECT_STATE_ACT,
+-};
+-
+ enum {
+ MT_EBF = BIT(0), /* explicit beamforming */
+ MT_IBF = BIT(1) /* implicit beamforming */
+ };
+
+-enum {
+- MT_BF_SOUNDING_ON = 1,
+- MT_BF_TYPE_UPDATE = 20,
+- MT_BF_MODULE_UPDATE = 25
+-};
+-
+ #define MT7915_WTBL_UPDATE_MAX_SIZE (sizeof(struct wtbl_req_hdr) + \
+ sizeof(struct wtbl_generic) + \
+ sizeof(struct wtbl_rx) + \
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
+index 3f613fae6218..4ea8972d4e2f 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
++++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
+@@ -9,7 +9,7 @@
+ #include "../mt76.h"
+ #include "regs.h"
+
+-#define MT7915_MAX_INTERFACES 19
++#define MT7915_MAX_INTERFACES 32
+ #define MT7915_MAX_WMM_SETS 4
+ #define MT7915_WTBL_SIZE 288
+ #define MT7915_WTBL_RESERVED (MT7915_WTBL_SIZE - 1)
+@@ -31,7 +31,6 @@
+ #define MT7915_ROM_PATCH "mediatek/mt7915_rom_patch.bin"
+
+ #define MT7915_EEPROM_SIZE 3584
+-#define MT7915_EEPROM_BLOCK_SIZE 16
+ #define MT7915_TOKEN_SIZE 8192
+
+ #define MT7915_CFEND_RATE_DEFAULT 0x49 /* OFDM 24M */
+@@ -39,10 +38,6 @@
+ #define MT7915_5G_RATE_DEFAULT 0x4b /* OFDM 6M */
+ #define MT7915_2G_RATE_DEFAULT 0x0 /* CCK 1M */
+
+-#define MT7915_THERMAL_THROTTLE_MAX 100
+-
+-#define MT7915_SKU_RATE_NUM 161
+-
+ struct mt7915_vif;
+ struct mt7915_sta;
+ struct mt7915_dfs_pulse;
+@@ -105,7 +100,6 @@ struct mt7915_vif {
+ struct mt7915_phy *phy;
+
+ struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
+- struct cfg80211_bitrate_mask bitrate_mask;
+ };
+
+ struct mib_stats {
+@@ -132,9 +126,6 @@ struct mt7915_phy {
+
+ struct ieee80211_vif *monitor_vif;
+
+- struct thermal_cooling_device *cdev;
+- u8 throttle_state;
+-
+ u32 rxfilter;
+ u64 omac_mask;
+
+@@ -150,7 +141,6 @@ struct mt7915_phy {
+ u32 ampdu_ref;
+
+ struct mib_stats mib;
+- struct mt76_channel_state state_ts;
+ struct list_head stats_list;
+
+ u8 sta_work_count;
+@@ -179,7 +169,6 @@ struct mt7915_dev {
+ struct mt7915_hif *hif2;
+
+ const struct mt76_bus_ops *bus_ops;
+- struct tasklet_struct irq_tasklet;
+ struct mt7915_phy phy;
+
+ u16 chainmask;
+@@ -333,8 +322,6 @@ int mt7915_mcu_add_obss_spr(struct mt7915_dev *dev, struct ieee80211_vif *vif,
+ bool enable);
+ int mt7915_mcu_add_rate_ctrl(struct mt7915_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+-int mt7915_mcu_add_he(struct mt7915_dev *dev, struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta);
+ int mt7915_mcu_add_smps(struct mt7915_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+ int mt7915_set_channel(struct mt7915_phy *phy);
+@@ -355,8 +342,9 @@ int mt7915_mcu_set_rts_thresh(struct mt7915_phy *phy, u32 val);
+ int mt7915_mcu_set_pm(struct mt7915_dev *dev, int band, int enter);
+ int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable);
+ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy);
+-int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len);
+-int mt7915_mcu_set_txbf(struct mt7915_dev *dev, u8 action);
++int mt7915_mcu_set_txbf_type(struct mt7915_dev *dev);
++int mt7915_mcu_set_txbf_module(struct mt7915_dev *dev);
++int mt7915_mcu_set_txbf_sounding(struct mt7915_dev *dev);
+ int mt7915_mcu_set_fcc5_lpn(struct mt7915_dev *dev, int val);
+ int mt7915_mcu_set_pulse_th(struct mt7915_dev *dev,
+ const struct mt7915_dfs_pulse *pulse);
+@@ -364,9 +352,7 @@ int mt7915_mcu_set_radar_th(struct mt7915_dev *dev, int index,
+ const struct mt7915_dfs_pattern *pattern);
+ int mt7915_mcu_apply_group_cal(struct mt7915_dev *dev);
+ int mt7915_mcu_apply_tx_dpd(struct mt7915_phy *phy);
+-int mt7915_mcu_get_chan_mib_info(struct mt7915_phy *phy, bool chan_switch);
+-int mt7915_mcu_get_temperature(struct mt7915_phy *phy);
+-int mt7915_mcu_set_thermal_throttling(struct mt7915_phy *phy, u8 state);
++int mt7915_mcu_get_temperature(struct mt7915_dev *dev, int index);
+ int mt7915_mcu_get_tx_rate(struct mt7915_dev *dev, u32 cmd, u16 wlan_idx);
+ int mt7915_mcu_get_rx_rate(struct mt7915_phy *phy, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, struct rate_info *rate);
+@@ -388,11 +374,9 @@ void mt7915_dual_hif_set_irq_mask(struct mt7915_dev *dev, bool write_reg,
+ static inline void mt7915_irq_enable(struct mt7915_dev *dev, u32 mask)
+ {
+ if (dev->hif2)
+- mt7915_dual_hif_set_irq_mask(dev, false, 0, mask);
++ mt7915_dual_hif_set_irq_mask(dev, true, 0, mask);
+ else
+- mt76_set_irq_mask(&dev->mt76, 0, 0, mask);
+-
+- tasklet_schedule(&dev->irq_tasklet);
++ mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, 0, mask);
+ }
+
+ static inline void mt7915_irq_disable(struct mt7915_dev *dev, u32 mask)
+@@ -408,9 +392,12 @@ void mt7915_mac_reset_counters(struct mt7915_phy *phy);
+ void mt7915_mac_cca_stats_reset(struct mt7915_phy *phy);
+ void mt7915_mac_enable_nf(struct mt7915_dev *dev, bool ext_phy);
+ void mt7915_mac_write_txwi(struct mt7915_dev *dev, __le32 *txwi,
+- struct sk_buff *skb, struct mt76_wcid *wcid, int pid,
++ struct sk_buff *skb, struct mt76_wcid *wcid,
+ struct ieee80211_key_conf *key, bool beacon);
+ void mt7915_mac_set_timing(struct mt7915_phy *phy);
++int mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb);
++void mt7915_mac_fill_rx_vector(struct mt7915_dev *dev, struct sk_buff *skb);
++void mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb);
+ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+ void mt7915_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+@@ -430,11 +417,13 @@ void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+ struct sk_buff *skb);
+ void mt7915_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps);
+ void mt7915_stats_work(struct work_struct *work);
++void mt7915_txp_skb_unmap(struct mt76_dev *dev,
++ struct mt76_txwi_cache *txwi);
+ int mt76_dfs_start_rdd(struct mt7915_dev *dev, bool force);
+ int mt7915_dfs_init_radar_detector(struct mt7915_phy *phy);
+ void mt7915_set_stream_he_caps(struct mt7915_phy *phy);
+ void mt7915_set_stream_vht_txbf_caps(struct mt7915_phy *phy);
+-void mt7915_update_channel(struct mt76_phy *mphy);
++void mt7915_update_channel(struct mt76_dev *mdev);
+ int mt7915_init_debugfs(struct mt7915_dev *dev);
+ #ifdef CONFIG_MAC80211_DEBUGFS
+ void mt7915_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c
+index 340b364da5f0..643f171884cf 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c
+@@ -94,15 +94,11 @@ mt7915_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q)
+ }
+
+ /* TODO: support 2/4/6/8 MSI-X vectors */
+-static void mt7915_irq_tasklet(struct tasklet_struct *t)
++static irqreturn_t mt7915_irq_handler(int irq, void *dev_instance)
+ {
+- struct mt7915_dev *dev = from_tasklet(dev, t, irq_tasklet);
++ struct mt7915_dev *dev = dev_instance;
+ u32 intr, intr1, mask;
+
+- mt76_wr(dev, MT_INT_MASK_CSR, 0);
+- if (dev->hif2)
+- mt76_wr(dev, MT_INT1_MASK_CSR, 0);
+-
+ intr = mt76_rr(dev, MT_INT_SOURCE_CSR);
+ intr &= dev->mt76.mmio.irqmask;
+ mt76_wr(dev, MT_INT_SOURCE_CSR, intr);
+@@ -115,6 +111,9 @@ static void mt7915_irq_tasklet(struct tasklet_struct *t)
+ intr |= intr1;
+ }
+
++ if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state))
++ return IRQ_NONE;
++
+ trace_dev_irq(&dev->mt76, intr, dev->mt76.mmio.irqmask);
+
+ mask = intr & MT_INT_RX_DONE_ALL;
+@@ -151,20 +150,6 @@ static void mt7915_irq_tasklet(struct tasklet_struct *t)
+ wake_up(&dev->reset_wait);
+ }
+ }
+-}
+-
+-static irqreturn_t mt7915_irq_handler(int irq, void *dev_instance)
+-{
+- struct mt7915_dev *dev = dev_instance;
+-
+- mt76_wr(dev, MT_INT_MASK_CSR, 0);
+- if (dev->hif2)
+- mt76_wr(dev, MT_INT1_MASK_CSR, 0);
+-
+- if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state))
+- return IRQ_NONE;
+-
+- tasklet_schedule(&dev->irq_tasklet);
+
+ return IRQ_HANDLED;
+ }
+@@ -255,8 +240,6 @@ static int mt7915_pci_probe(struct pci_dev *pdev,
+ if (ret)
+ return ret;
+
+- mt76_pci_disable_aspm(pdev);
+-
+ if (id->device == 0x7916)
+ return mt7915_pci_hif2_probe(pdev);
+
+@@ -267,18 +250,10 @@ static int mt7915_pci_probe(struct pci_dev *pdev,
+
+ dev = container_of(mdev, struct mt7915_dev, mt76);
+
+- ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
+- if (ret < 0)
+- goto free;
+-
+ ret = mt7915_mmio_init(mdev, pcim_iomap_table(pdev)[0], pdev->irq);
+ if (ret)
+ goto error;
+
+- tasklet_setup(&dev->irq_tasklet, mt7915_irq_tasklet);
+-
+- mt76_wr(dev, MT_INT_MASK_CSR, 0);
+-
+ /* master switch of PCIe tnterrupt enable */
+ mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
+
+@@ -291,14 +266,10 @@ static int mt7915_pci_probe(struct pci_dev *pdev,
+
+ ret = mt7915_register_device(dev);
+ if (ret)
+- goto free_irq;
++ goto error;
+
+ return 0;
+-free_irq:
+- devm_free_irq(mdev->dev, pdev->irq, dev);
+ error:
+- pci_free_irq_vectors(pdev);
+-free:
+ mt76_free_device(&dev->mt76);
+
+ return ret;
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h
+index a213b5cb82f8..efe0f2904c66 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h
++++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h
+@@ -82,6 +82,11 @@
+ #define MT_TMAC_CTCR0_INS_DDLMT_EN BIT(17)
+ #define MT_TMAC_CTCR0_INS_DDLMT_VHT_SMPDU_EN BIT(18)
+
++#define MT_TMAC_FP0R0(_band) MT_WF_TMAC(_band, 0x020)
++#define MT_TMAC_FP0R15(_band) MT_WF_TMAC(_band, 0x080)
++#define MT_TMAC_FP0R18(_band) MT_WF_TMAC(_band, 0x270)
++#define MT_TMAC_FP_MASK GENMASK(7, 0)
++
+ #define MT_TMAC_TFCR0(_band) MT_WF_TMAC(_band, 0x1e0)
+
+ #define MT_WF_DMA_BASE(_band) ((_band) ? 0xa1e00 : 0x21e00)
+@@ -99,11 +104,6 @@
+ #define MT_ETBF_TX_FB_CPL GENMASK(31, 16)
+ #define MT_ETBF_TX_FB_TRI GENMASK(15, 0)
+
+-#define MT_ETBF_RX_FB_CONT(_band) MT_WF_ETBF(_band, 0x068)
+-#define MT_ETBF_RX_FB_BW GENMASK(7, 6)
+-#define MT_ETBF_RX_FB_NC GENMASK(5, 3)
+-#define MT_ETBF_RX_FB_NR GENMASK(2, 0)
+-
+ #define MT_ETBF_TX_APP_CNT(_band) MT_WF_ETBF(_band, 0x0f0)
+ #define MT_ETBF_TX_IBF_CNT GENMASK(31, 16)
+ #define MT_ETBF_TX_EBF_CNT GENMASK(15, 0)
+@@ -124,8 +124,6 @@
+ #define MT_LPON_TCR(_band, n) MT_WF_LPON(_band, 0x0a8 + (n) * 4)
+ #define MT_LPON_TCR_SW_MODE GENMASK(1, 0)
+ #define MT_LPON_TCR_SW_WRITE BIT(0)
+-#define MT_LPON_TCR_SW_ADJUST BIT(1)
+-#define MT_LPON_TCR_SW_READ GENMASK(1, 0)
+
+ /* MIB: band 0(0x24800), band 1(0xa4800) */
+ #define MT_WF_MIB_BASE(_band) ((_band) ? 0xa4800 : 0x24800)
+@@ -134,9 +132,20 @@
+ #define MT_MIB_SDR3(_band) MT_WF_MIB(_band, 0x014)
+ #define MT_MIB_SDR3_FCS_ERR_MASK GENMASK(15, 0)
+
++#define MT_MIB_SDR9(_band) MT_WF_MIB(_band, 0x02c)
++#define MT_MIB_SDR9_BUSY_MASK GENMASK(23, 0)
++
++#define MT_MIB_SDR16(_band) MT_WF_MIB(_band, 0x048)
++#define MT_MIB_SDR16_BUSY_MASK GENMASK(23, 0)
++
+ #define MT_MIB_SDR34(_band) MT_WF_MIB(_band, 0x090)
+ #define MT_MIB_MU_BF_TX_CNT GENMASK(15, 0)
+
++#define MT_MIB_SDR36(_band) MT_WF_MIB(_band, 0x098)
++#define MT_MIB_SDR36_TXTIME_MASK GENMASK(23, 0)
++#define MT_MIB_SDR37(_band) MT_WF_MIB(_band, 0x09c)
++#define MT_MIB_SDR37_RXTIME_MASK GENMASK(23, 0)
++
+ #define MT_MIB_DR8(_band) MT_WF_MIB(_band, 0x0c0)
+ #define MT_MIB_DR9(_band) MT_WF_MIB(_band, 0x0c4)
+ #define MT_MIB_DR11(_band) MT_WF_MIB(_band, 0x0cc)
+@@ -149,6 +158,9 @@
+ #define MT_MIB_BA_MISS_COUNT_MASK GENMASK(15, 0)
+ #define MT_MIB_ACK_FAIL_COUNT_MASK GENMASK(31, 16)
+
++#define MT_MIB_MB_SDR2(_band, n) MT_WF_MIB(_band, 0x108 + ((n) << 4))
++#define MT_MIB_FRAME_RETRIES_COUNT_MASK GENMASK(15, 0)
++
+ #define MT_TX_AGG_CNT(_band, n) MT_WF_MIB(_band, 0x0a8 + ((n) << 2))
+ #define MT_TX_AGG_CNT2(_band, n) MT_WF_MIB(_band, 0x164 + ((n) << 2))
+ #define MT_MIB_ARNG(_band, n) MT_WF_MIB(_band, 0x4b8 + ((n) << 2))
+@@ -246,10 +258,14 @@
+ #define MT_WF_RFCR1_DROP_CFEND BIT(7)
+ #define MT_WF_RFCR1_DROP_CFACK BIT(8)
+
+-#define MT_WF_RMAC_MIB_AIRTIME0(_band) MT_WF_RMAC(_band, 0x0380)
++#define MT_WF_RMAC_MIB_TIME0(_band) MT_WF_RMAC(_band, 0x03c4)
+ #define MT_WF_RMAC_MIB_RXTIME_CLR BIT(31)
+ #define MT_WF_RMAC_MIB_RXTIME_EN BIT(30)
+
++#define MT_WF_RMAC_MIB_AIRTIME14(_band) MT_WF_RMAC(_band, 0x03b8)
++#define MT_MIB_OBSSTIME_MASK GENMASK(23, 0)
++#define MT_WF_RMAC_MIB_AIRTIME0(_band) MT_WF_RMAC(_band, 0x0380)
++
+ /* WFDMA0 */
+ #define MT_WFDMA0_BASE 0xd4000
+ #define MT_WFDMA0(ofs) (MT_WFDMA0_BASE + (ofs))
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.h b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.h
+index 397a6b5532bc..8f8533ef9859 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.h
++++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.h
+@@ -1,4 +1,4 @@
+-/* SPDX-License-Identifier: ISC */
++// SPDX-License-Identifier: ISC
+ /* Copyright (C) 2020 MediaTek Inc. */
+
+ #ifndef __MT7915_TESTMODE_H
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/Makefile b/drivers/net/wireless/mediatek/mt76/mt7921/Makefile
+index 0ebb59966a08..e531666f9fb4 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/Makefile
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/Makefile
+@@ -1,4 +1,4 @@
+-# SPDX-License-Identifier: ISC
++#SPDX-License-Identifier: ISC
+
+ obj-$(CONFIG_MT7921E) += mt7921e.o
+
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c
+index 77468bdae460..6602903c0d02 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c
+@@ -250,9 +250,6 @@ mt7921_pm_set(void *data, u64 val)
+ ieee80211_iterate_active_interfaces(mphy->hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7921_pm_interface_iter, mphy->priv);
+-
+- mt76_connac_mcu_set_deep_sleep(&dev->mt76, pm->ds_enable);
+-
+ mt7921_mutex_release(dev);
+
+ return 0;
+@@ -270,36 +267,6 @@ mt7921_pm_get(void *data, u64 *val)
+
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_pm, mt7921_pm_get, mt7921_pm_set, "%lld\n");
+
+-static int
+-mt7921_deep_sleep_set(void *data, u64 val)
+-{
+- struct mt7921_dev *dev = data;
+- struct mt76_connac_pm *pm = &dev->pm;
+- bool enable = !!val;
+-
+- mt7921_mutex_acquire(dev);
+- if (pm->ds_enable != enable) {
+- mt76_connac_mcu_set_deep_sleep(&dev->mt76, enable);
+- pm->ds_enable = enable;
+- }
+- mt7921_mutex_release(dev);
+-
+- return 0;
+-}
+-
+-static int
+-mt7921_deep_sleep_get(void *data, u64 *val)
+-{
+- struct mt7921_dev *dev = data;
+-
+- *val = dev->pm.ds_enable;
+-
+- return 0;
+-}
+-
+-DEFINE_DEBUGFS_ATTRIBUTE(fops_ds, mt7921_deep_sleep_get,
+- mt7921_deep_sleep_set, "%lld\n");
+-
+ static int
+ mt7921_pm_stats(struct seq_file *s, void *data)
+ {
+@@ -391,7 +358,6 @@ int mt7921_init_debugfs(struct mt7921_dev *dev)
+ debugfs_create_file("chip_reset", 0600, dir, dev, &fops_reset);
+ debugfs_create_devm_seqfile(dev->mt76.dev, "runtime_pm_stats", dir,
+ mt7921_pm_stats);
+- debugfs_create_file("deep-sleep", 0600, dir, dev, &fops_ds);
+
+ return 0;
+ }
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/dma.c b/drivers/net/wireless/mediatek/mt76/mt7921/dma.c
+index 7d7d43a5422f..7fca7dc466b8 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/dma.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/dma.c
+@@ -74,7 +74,7 @@ static int mt7921_poll_tx(struct napi_struct *napi, int budget)
+ mt7921_tx_cleanup(dev);
+ if (napi_complete(napi))
+ mt7921_irq_enable(dev, MT_INT_TX_DONE_ALL);
+- mt76_connac_pm_unref(&dev->mphy, &dev->pm);
++ mt76_connac_pm_unref(&dev->pm);
+
+ return 0;
+ }
+@@ -92,7 +92,7 @@ static int mt7921_poll_rx(struct napi_struct *napi, int budget)
+ return 0;
+ }
+ done = mt76_dma_rx_poll(napi, budget);
+- mt76_connac_pm_unref(&dev->mphy, &dev->pm);
++ mt76_connac_pm_unref(&dev->pm);
+
+ return done;
+ }
+@@ -380,7 +380,9 @@ int mt7921_wpdma_reinit_cond(struct mt7921_dev *dev)
+
+ int mt7921_dma_init(struct mt7921_dev *dev)
+ {
++ /* Increase buffer size to receive large VHT/HE MPDUs */
+ struct mt76_bus_ops *bus_ops;
++ int rx_buf_size = MT_RX_BUF_SIZE * 2;
+ int ret;
+
+ dev->bus_ops = dev->mt76.bus;
+@@ -428,7 +430,7 @@ int mt7921_dma_init(struct mt7921_dev *dev)
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU],
+ MT7921_RXQ_MCU_WM,
+ MT7921_RX_MCU_RING_SIZE,
+- MT_RX_BUF_SIZE, MT_RX_EVENT_RING_BASE);
++ rx_buf_size, MT_RX_EVENT_RING_BASE);
+ if (ret)
+ return ret;
+
+@@ -436,14 +438,14 @@ int mt7921_dma_init(struct mt7921_dev *dev)
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA],
+ MT7921_RXQ_MCU_WM,
+ MT7921_RX_MCU_RING_SIZE,
+- MT_RX_BUF_SIZE, MT_WFDMA0(0x540));
++ rx_buf_size, MT_WFDMA0(0x540));
+ if (ret)
+ return ret;
+
+ /* rx data */
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN],
+ MT7921_RXQ_BAND0, MT7921_RX_RING_SIZE,
+- MT_RX_BUF_SIZE, MT_RX_DATA_RING_BASE);
++ rx_buf_size, MT_RX_DATA_RING_BASE);
+ if (ret)
+ return ret;
+
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/init.c b/drivers/net/wireless/mediatek/mt76/mt7921/init.c
+index a9ce10b98827..db7e436076b3 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/init.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c
+@@ -7,6 +7,34 @@
+ #include "mcu.h"
+ #include "eeprom.h"
+
++#define CCK_RATE(_idx, _rate) { \
++ .bitrate = _rate, \
++ .flags = IEEE80211_RATE_SHORT_PREAMBLE, \
++ .hw_value = (MT_PHY_TYPE_CCK << 8) | (_idx), \
++ .hw_value_short = (MT_PHY_TYPE_CCK << 8) | (4 + (_idx)), \
++}
++
++#define OFDM_RATE(_idx, _rate) { \
++ .bitrate = _rate, \
++ .hw_value = (MT_PHY_TYPE_OFDM << 8) | (_idx), \
++ .hw_value_short = (MT_PHY_TYPE_OFDM << 8) | (_idx), \
++}
++
++static struct ieee80211_rate mt7921_rates[] = {
++ CCK_RATE(0, 10),
++ CCK_RATE(1, 20),
++ CCK_RATE(2, 55),
++ CCK_RATE(3, 110),
++ OFDM_RATE(11, 60),
++ OFDM_RATE(15, 90),
++ OFDM_RATE(10, 120),
++ OFDM_RATE(14, 180),
++ OFDM_RATE(9, 240),
++ OFDM_RATE(13, 360),
++ OFDM_RATE(8, 480),
++ OFDM_RATE(12, 540),
++};
++
+ static const struct ieee80211_iface_limit if_limits[] = {
+ {
+ .max = MT7921_MAX_INTERFACES,
+@@ -51,7 +79,6 @@ mt7921_init_wiphy(struct ieee80211_hw *hw)
+ hw->queues = 4;
+ hw->max_rx_aggregation_subframes = 64;
+ hw->max_tx_aggregation_subframes = 128;
+- hw->netdev_features = NETIF_F_RXCSUM;
+
+ hw->radiotap_timestamp.units_pos =
+ IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US;
+@@ -62,8 +89,6 @@ mt7921_init_wiphy(struct ieee80211_hw *hw)
+ hw->vif_data_size = sizeof(struct mt7921_vif);
+
+ wiphy->iface_combinations = if_comb;
+- wiphy->flags &= ~WIPHY_FLAG_IBSS_RSN;
+- wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+ wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
+ wiphy->max_scan_ie_len = MT76_CONNAC_SCAN_IE_LEN;
+ wiphy->max_scan_ssids = 4;
+@@ -76,14 +101,12 @@ mt7921_init_wiphy(struct ieee80211_hw *hw)
+ wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+ wiphy->reg_notifier = mt7921_regd_notifier;
+
+- wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
+- NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
++ wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SET_SCAN_DWELL);
+
+ ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS);
+ ieee80211_hw_set(hw, HAS_RATE_CONTROL);
+ ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD);
+- ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD);
+ ieee80211_hw_set(hw, WANT_MONITOR_VIF);
+ ieee80211_hw_set(hw, SUPPORTS_PS);
+ ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
+@@ -111,15 +134,14 @@ mt7921_mac_init_band(struct mt7921_dev *dev, u8 band)
+ mt76_clear(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN);
+ }
+
+-int mt7921_mac_init(struct mt7921_dev *dev)
++void mt7921_mac_init(struct mt7921_dev *dev)
+ {
+ int i;
+
+ mt76_rmw_field(dev, MT_MDP_DCR1, MT_MDP_DCR1_MAX_RX_LEN, 1536);
+- /* enable hardware de-agg */
+- mt76_set(dev, MT_MDP_DCR0, MT_MDP_DCR0_DAMSDU_EN);
+- /* enable hardware rx header translation */
+- mt76_set(dev, MT_MDP_DCR0, MT_MDP_DCR0_RX_HDR_TRANS_EN);
++ /* disable hardware de-agg */
++ mt76_clear(dev, MT_MDP_DCR0, MT_MDP_DCR0_DAMSDU_EN);
++ mt76_clear(dev, MT_MDP_DCR0, MT_MDP_DCR0_RX_HDR_TRANS_EN);
+
+ for (i = 0; i < MT7921_WTBL_SIZE; i++)
+ mt7921_mac_wtbl_update(dev, i,
+@@ -127,7 +149,7 @@ int mt7921_mac_init(struct mt7921_dev *dev)
+ for (i = 0; i < 2; i++)
+ mt7921_mac_init_band(dev, i);
+
+- return mt76_connac_mcu_set_rts_thresh(&dev->mt76, 0x92b, 0);
++ mt76_connac_mcu_set_rts_thresh(&dev->mt76, 0x92b, 0);
+ }
+
+ static int mt7921_init_hardware(struct mt7921_dev *dev)
+@@ -167,7 +189,9 @@ static int mt7921_init_hardware(struct mt7921_dev *dev)
+ dev->mt76.global_wcid.tx_info |= MT_WCID_TX_INFO_SET;
+ rcu_assign_pointer(dev->mt76.wcid[idx], &dev->mt76.global_wcid);
+
+- return mt7921_mac_init(dev);
++ mt7921_mac_init(dev);
++
++ return 0;
+ }
+
+ int mt7921_register_device(struct mt7921_dev *dev)
+@@ -186,6 +210,7 @@ int mt7921_register_device(struct mt7921_dev *dev)
+ mutex_init(&dev->pm.mutex);
+ init_waitqueue_head(&dev->pm.wait);
+ spin_lock_init(&dev->pm.txq_lock);
++ set_bit(MT76_STATE_PM, &dev->mphy.state);
+ INIT_LIST_HEAD(&dev->phy.stats_list);
+ INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7921_mac_work);
+ INIT_DELAYED_WORK(&dev->phy.scan_work, mt7921_scan_work);
+@@ -200,8 +225,6 @@ int mt7921_register_device(struct mt7921_dev *dev)
+ dev->pm.idle_timeout = MT7921_PM_TIMEOUT;
+ dev->pm.stats.last_wake_event = jiffies;
+ dev->pm.stats.last_doze_event = jiffies;
+- dev->pm.enable = true;
+- dev->pm.ds_enable = true;
+
+ ret = mt7921_init_hardware(dev);
+ if (ret)
+@@ -216,33 +239,19 @@ int mt7921_register_device(struct mt7921_dev *dev)
+ IEEE80211_HT_CAP_MAX_AMSDU;
+ dev->mphy.sband_5g.sband.vht_cap.cap |=
+ IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 |
+- IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK |
+- IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+- IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
+- (3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
+-
++ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
+ dev->mphy.hw->wiphy->available_antennas_rx = dev->mphy.chainmask;
+ dev->mphy.hw->wiphy->available_antennas_tx = dev->mphy.chainmask;
+
+ mt76_set_stream_caps(&dev->mphy, true);
+ mt7921_set_stream_he_caps(&dev->phy);
+
+- ret = mt76_register_device(&dev->mt76, true, mt76_rates,
+- ARRAY_SIZE(mt76_rates));
++ ret = mt76_register_device(&dev->mt76, true, mt7921_rates,
++ ARRAY_SIZE(mt7921_rates));
+ if (ret)
+ return ret;
+
+- ret = mt7921_init_debugfs(dev);
+- if (ret)
+- return ret;
+-
+- ret = mt76_connac_mcu_set_deep_sleep(&dev->mt76, dev->pm.ds_enable);
+- if (ret)
+- return ret;
+-
+- dev->hw_init_done = true;
+-
+- return 0;
++ return mt7921_init_debugfs(dev);
+ }
+
+ void mt7921_unregister_device(struct mt7921_dev *dev)
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
+index 7fe2e3a50428..d7d8c909acdf 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
+@@ -308,24 +308,21 @@ mt7921_mac_assoc_rssi(struct mt7921_dev *dev, struct sk_buff *skb)
+
+ int mt7921_mac_fill_rx(struct mt7921_dev *dev, struct sk_buff *skb)
+ {
+- u32 csum_mask = MT_RXD0_NORMAL_IP_SUM | MT_RXD0_NORMAL_UDP_TCP_SUM;
+ struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+- bool hdr_trans, unicast, insert_ccmp_hdr = false;
+- u8 chfreq, qos_ctl = 0, remove_pad, amsdu_info;
+- __le32 *rxv = NULL, *rxd = (__le32 *)skb->data;
+ struct mt76_phy *mphy = &dev->mt76.phy;
+ struct mt7921_phy *phy = &dev->phy;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_hdr *hdr;
+- u32 rxd0 = le32_to_cpu(rxd[0]);
++ __le32 *rxd = (__le32 *)skb->data;
++ __le32 *rxv = NULL;
++ u32 mode = 0;
+ u32 rxd1 = le32_to_cpu(rxd[1]);
+ u32 rxd2 = le32_to_cpu(rxd[2]);
+ u32 rxd3 = le32_to_cpu(rxd[3]);
+- u32 rxd4 = le32_to_cpu(rxd[4]);
+- u16 seq_ctrl = 0;
+- __le16 fc = 0;
+- u32 mode = 0;
++ bool unicast, insert_ccmp_hdr = false;
++ u8 remove_pad;
+ int i, idx;
++ u8 chfreq;
+
+ memset(status, 0, sizeof(*status));
+
+@@ -335,13 +332,9 @@ int mt7921_mac_fill_rx(struct mt7921_dev *dev, struct sk_buff *skb)
+ if (!test_bit(MT76_STATE_RUNNING, &mphy->state))
+ return -EINVAL;
+
+- if (rxd2 & MT_RXD2_NORMAL_AMSDU_ERR)
+- return -EINVAL;
+-
+ chfreq = FIELD_GET(MT_RXD3_NORMAL_CH_FREQ, rxd3);
+ unicast = FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) == MT_RXD3_NORMAL_U2M;
+ idx = FIELD_GET(MT_RXD1_NORMAL_WLAN_IDX, rxd1);
+- hdr_trans = rxd2 & MT_RXD2_NORMAL_HDR_TRANS;
+ status->wcid = mt7921_rx_get_wcid(dev, idx, unicast);
+
+ if (status->wcid) {
+@@ -364,9 +357,6 @@ int mt7921_mac_fill_rx(struct mt7921_dev *dev, struct sk_buff *skb)
+ if (!sband->channels)
+ return -EINVAL;
+
+- if ((rxd0 & csum_mask) == csum_mask)
+- skb->ip_summed = CHECKSUM_UNNECESSARY;
+-
+ if (rxd1 & MT_RXD1_NORMAL_FCS_ERR)
+ status->flag |= RX_FLAG_FAILED_FCS_CRC;
+
+@@ -387,13 +377,6 @@ int mt7921_mac_fill_rx(struct mt7921_dev *dev, struct sk_buff *skb)
+
+ rxd += 6;
+ if (rxd1 & MT_RXD1_NORMAL_GROUP_4) {
+- u32 v0 = le32_to_cpu(rxd[0]);
+- u32 v2 = le32_to_cpu(rxd[2]);
+-
+- fc = cpu_to_le16(FIELD_GET(MT_RXD6_FRAME_CONTROL, v0));
+- seq_ctrl = FIELD_GET(MT_RXD8_SEQ_CTRL, v2);
+- qos_ctl = FIELD_GET(MT_RXD8_QOS_CTL, v2);
+-
+ rxd += 4;
+ if ((u8 *)rxd - skb->data >= skb->len)
+ return -EINVAL;
+@@ -573,35 +556,10 @@ int mt7921_mac_fill_rx(struct mt7921_dev *dev, struct sk_buff *skb)
+
+ skb_pull(skb, (u8 *)rxd - skb->data + 2 * remove_pad);
+
+- amsdu_info = FIELD_GET(MT_RXD4_NORMAL_PAYLOAD_FORMAT, rxd4);
+- status->amsdu = !!amsdu_info;
+- if (status->amsdu) {
+- status->first_amsdu = amsdu_info == MT_RXD4_FIRST_AMSDU_FRAME;
+- status->last_amsdu = amsdu_info == MT_RXD4_LAST_AMSDU_FRAME;
+- if (!hdr_trans) {
+- memmove(skb->data + 2, skb->data,
+- ieee80211_get_hdrlen_from_skb(skb));
+- skb_pull(skb, 2);
+- }
+- }
++ if (insert_ccmp_hdr) {
++ u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1);
+
+- if (!hdr_trans) {
+- if (insert_ccmp_hdr) {
+- u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1);
+-
+- mt76_insert_ccmp_hdr(skb, key_id);
+- }
+-
+- hdr = mt76_skb_get_hdr(skb);
+- fc = hdr->frame_control;
+- if (ieee80211_is_data_qos(fc)) {
+- seq_ctrl = le16_to_cpu(hdr->seq_ctrl);
+- qos_ctl = *ieee80211_get_qos_ctl(hdr);
+- }
+- } else {
+- status->flag &= ~(RX_FLAG_RADIOTAP_HE |
+- RX_FLAG_RADIOTAP_HE_MU);
+- status->flag |= RX_FLAG_8023;
++ mt76_insert_ccmp_hdr(skb, key_id);
+ }
+
+ mt7921_mac_assoc_rssi(dev, skb);
+@@ -609,12 +567,14 @@ int mt7921_mac_fill_rx(struct mt7921_dev *dev, struct sk_buff *skb)
+ if (rxv && status->flag & RX_FLAG_RADIOTAP_HE)
+ mt7921_mac_decode_he_radiotap(skb, status, rxv, mode);
+
+- if (!status->wcid || !ieee80211_is_data_qos(fc))
++ hdr = mt76_skb_get_hdr(skb);
++ if (!status->wcid || !ieee80211_is_data_qos(hdr->frame_control))
+ return 0;
+
+- status->aggr = unicast && !ieee80211_is_qos_nullfunc(fc);
+- status->seqno = IEEE80211_SEQ_TO_SN(seq_ctrl);
+- status->qos_ctl = qos_ctl;
++ status->aggr = unicast &&
++ !ieee80211_is_qos_nullfunc(hdr->frame_control);
++ status->qos_ctl = *ieee80211_get_qos_ctl(hdr);
++ status->seqno = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
+
+ return 0;
+ }
+@@ -732,23 +692,6 @@ mt7921_mac_write_txwi_80211(struct mt7921_dev *dev, __le32 *txwi,
+ txwi[7] |= cpu_to_le32(val);
+ }
+
+-static void mt7921_update_txs(struct mt76_wcid *wcid, __le32 *txwi)
+-{
+- struct mt7921_sta *msta = container_of(wcid, struct mt7921_sta, wcid);
+- u32 pid, frame_type = FIELD_GET(MT_TXD2_FRAME_TYPE, txwi[2]);
+-
+- if (!(frame_type & (IEEE80211_FTYPE_DATA >> 2)))
+- return;
+-
+- if (time_is_after_eq_jiffies(msta->next_txs_ts))
+- return;
+-
+- msta->next_txs_ts = jiffies + msecs_to_jiffies(250);
+- pid = mt76_get_next_pkt_id(wcid);
+- txwi[5] |= cpu_to_le32(MT_TXD5_TX_STATUS_MCU |
+- FIELD_PREP(MT_TXD5_PID, pid));
+-}
+-
+ void mt7921_mac_write_txwi(struct mt7921_dev *dev, __le32 *txwi,
+ struct sk_buff *skb, struct mt76_wcid *wcid,
+ struct ieee80211_key_conf *key, bool beacon)
+@@ -825,8 +768,6 @@ void mt7921_mac_write_txwi(struct mt7921_dev *dev, __le32 *txwi,
+ txwi[6] |= cpu_to_le32(val);
+ txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE);
+ }
+-
+- mt7921_update_txs(wcid, txwi);
+ }
+
+ static void
+@@ -1229,18 +1170,18 @@ mt7921_phy_update_channel(struct mt76_phy *mphy, int idx)
+ state->noise = -(phy->noise >> 4);
+ }
+
+-void mt7921_update_channel(struct mt76_phy *mphy)
++void mt7921_update_channel(struct mt76_dev *mdev)
+ {
+- struct mt7921_dev *dev = container_of(mphy->dev, struct mt7921_dev, mt76);
++ struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76);
+
+- if (mt76_connac_pm_wake(mphy, &dev->pm))
++ if (mt76_connac_pm_wake(&dev->mphy, &dev->pm))
+ return;
+
+- mt7921_phy_update_channel(mphy, 0);
++ mt7921_phy_update_channel(&mdev->phy, 0);
+ /* reset obss airtime */
+ mt76_set(dev, MT_WF_RMAC_MIB_TIME0(0), MT_WF_RMAC_MIB_RXTIME_CLR);
+
+- mt76_connac_power_save_sched(mphy, &dev->pm);
++ mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
+ }
+
+ void mt7921_tx_token_put(struct mt7921_dev *dev)
+@@ -1288,7 +1229,6 @@ mt7921_mac_reset(struct mt7921_dev *dev)
+ mt76_wr(dev, MT_WFDMA0_HOST_INT_ENA, 0);
+ mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0);
+
+- set_bit(MT76_RESET, &dev->mphy.state);
+ set_bit(MT76_MCU_RESET, &dev->mphy.state);
+ wake_up(&dev->mt76.mcu.wait);
+ skb_queue_purge(&dev->mt76.mcu.res_q);
+@@ -1304,13 +1244,19 @@ mt7921_mac_reset(struct mt7921_dev *dev)
+ mt7921_tx_token_put(dev);
+ idr_init(&dev->mt76.token);
+
+- mt7921_wpdma_reset(dev, true);
++ err = mt7921_wpdma_reset(dev, true);
++ if (err)
++ return err;
+
+ mt76_for_each_q_rx(&dev->mt76, i) {
+ napi_enable(&dev->mt76.napi[i]);
+ napi_schedule(&dev->mt76.napi[i]);
+ }
+
++ napi_enable(&dev->mt76.tx_napi);
++ napi_schedule(&dev->mt76.tx_napi);
++ mt76_worker_enable(&dev->mt76.tx_worker);
++
+ clear_bit(MT76_MCU_RESET, &dev->mphy.state);
+
+ mt76_wr(dev, MT_WFDMA0_HOST_INT_ENA,
+@@ -1320,25 +1266,14 @@ mt7921_mac_reset(struct mt7921_dev *dev)
+
+ err = mt7921_run_firmware(dev);
+ if (err)
+- goto out;
++ return err;
+
+ err = mt7921_mcu_set_eeprom(dev);
+ if (err)
+- goto out;
+-
+- err = mt7921_mac_init(dev);
+- if (err)
+- goto out;
+-
+- err = __mt7921_start(&dev->phy);
+-out:
+- clear_bit(MT76_RESET, &dev->mphy.state);
+-
+- napi_enable(&dev->mt76.tx_napi);
+- napi_schedule(&dev->mt76.tx_napi);
+- mt76_worker_enable(&dev->mt76.tx_worker);
++ return err;
+
+- return err;
++ mt7921_mac_init(dev);
++ return __mt7921_start(&dev->phy);
+ }
+
+ /* system error recovery */
+@@ -1390,13 +1325,11 @@ void mt7921_reset(struct mt76_dev *mdev)
+ {
+ struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76);
+
+- if (!dev->hw_init_done)
+- return;
+-
+- if (dev->hw_full_reset)
++ if (!test_bit(MT76_STATE_RUNNING, &dev->mphy.state))
+ return;
+
+- queue_work(dev->mt76.wq, &dev->reset_work);
++ if (!dev->hw_full_reset)
++ queue_work(dev->mt76.wq, &dev->reset_work);
+ }
+
+ static void
+@@ -1430,6 +1363,30 @@ mt7921_mac_update_mib_stats(struct mt7921_phy *phy)
+ }
+ }
+
++static void
++mt7921_mac_sta_stats_work(struct mt7921_phy *phy)
++{
++ struct mt7921_dev *dev = phy->dev;
++ struct mt7921_sta *msta;
++ LIST_HEAD(list);
++
++ spin_lock_bh(&dev->sta_poll_lock);
++ list_splice_init(&phy->stats_list, &list);
++
++ while (!list_empty(&list)) {
++ msta = list_first_entry(&list, struct mt7921_sta, stats_list);
++ list_del_init(&msta->stats_list);
++ spin_unlock_bh(&dev->sta_poll_lock);
++
++ /* query wtbl info to report tx rate for further devices */
++ mt7921_get_wtbl_info(dev, msta->wcid.idx);
++
++ spin_lock_bh(&dev->sta_poll_lock);
++ }
++
++ spin_unlock_bh(&dev->sta_poll_lock);
++}
++
+ void mt7921_mac_work(struct work_struct *work)
+ {
+ struct mt7921_phy *phy;
+@@ -1441,12 +1398,16 @@ void mt7921_mac_work(struct work_struct *work)
+
+ mt7921_mutex_acquire(phy->dev);
+
+- mt76_update_survey(mphy);
++ mt76_update_survey(mphy->dev);
+ if (++mphy->mac_work_count == 2) {
+ mphy->mac_work_count = 0;
+
+ mt7921_mac_update_mib_stats(phy);
+ }
++ if (++phy->sta_work_count == 4) {
++ phy->sta_work_count = 0;
++ mt7921_mac_sta_stats_work(phy);
++ }
+
+ mt7921_mutex_release(phy->dev);
+ ieee80211_queue_delayed_work(phy->mt76->hw, &mphy->mac_work,
+@@ -1482,15 +1443,13 @@ void mt7921_pm_power_save_work(struct work_struct *work)
+ {
+ struct mt7921_dev *dev;
+ unsigned long delta;
+- struct mt76_phy *mphy;
+
+ dev = (struct mt7921_dev *)container_of(work, struct mt7921_dev,
+ pm.ps_work.work);
+- mphy = dev->phy.mt76;
+
+ delta = dev->pm.idle_timeout;
+- if (test_bit(MT76_HW_SCANNING, &mphy->state) ||
+- test_bit(MT76_HW_SCHED_SCANNING, &mphy->state))
++ if (test_bit(MT76_HW_SCANNING, &dev->mphy.state) ||
++ test_bit(MT76_HW_SCHED_SCANNING, &dev->mphy.state))
+ goto out;
+
+ if (time_is_after_jiffies(dev->pm.last_activity + delta)) {
+@@ -1498,10 +1457,8 @@ void mt7921_pm_power_save_work(struct work_struct *work)
+ goto out;
+ }
+
+- if (!mt7921_mcu_fw_pmctrl(dev)) {
+- cancel_delayed_work_sync(&mphy->mac_work);
++ if (!mt7921_mcu_fw_pmctrl(dev))
+ return;
+- }
+ out:
+ queue_delayed_work(dev->mt76.wq, &dev->pm.ps_work, delta);
+ }
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.h b/drivers/net/wireless/mediatek/mt76/mt7921/mac.h
+index 3af67fac213d..109c8849d106 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.h
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.h
+@@ -88,9 +88,6 @@ enum rx_pkt_type {
+
+ /* RXD DW4 */
+ #define MT_RXD4_NORMAL_PAYLOAD_FORMAT GENMASK(1, 0)
+-#define MT_RXD4_FIRST_AMSDU_FRAME GENMASK(1, 0)
+-#define MT_RXD4_MID_AMSDU_FRAME BIT(1)
+-#define MT_RXD4_LAST_AMSDU_FRAME BIT(0)
+ #define MT_RXD4_NORMAL_PATTERN_DROP BIT(9)
+ #define MT_RXD4_NORMAL_CLS BIT(10)
+ #define MT_RXD4_NORMAL_OFLD GENMASK(12, 11)
+@@ -100,17 +97,6 @@ enum rx_pkt_type {
+ #define MT_RXD3_NORMAL_PF_MODE BIT(29)
+ #define MT_RXD3_NORMAL_PF_STS GENMASK(31, 30)
+
+-/* RXD GROUP4 */
+-#define MT_RXD6_FRAME_CONTROL GENMASK(15, 0)
+-#define MT_RXD6_TA_LO GENMASK(31, 16)
+-
+-#define MT_RXD7_TA_HI GENMASK(31, 0)
+-
+-#define MT_RXD8_SEQ_CTRL GENMASK(15, 0)
+-#define MT_RXD8_QOS_CTL GENMASK(31, 16)
+-
+-#define MT_RXD9_HT_CONTROL GENMASK(31, 0)
+-
+ /* P-RXV DW0 */
+ #define MT_PRXV_TX_RATE GENMASK(6, 0)
+ #define MT_PRXV_TX_DCM BIT(4)
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+index 63ec140c9c37..992a74e122e5 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+@@ -79,14 +79,13 @@ mt7921_init_he_caps(struct mt7921_phy *phy, enum nl80211_band band,
+ he_cap_elem->phy_cap_info[1] =
+ IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD;
+ he_cap_elem->phy_cap_info[2] =
+- IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+ IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ |
+- IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ |
+- IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO |
+- IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO;
++ IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ;
+
+ switch (i) {
+ case NL80211_IFTYPE_STATION:
++ he_cap_elem->mac_cap_info[0] |=
++ IEEE80211_HE_MAC_CAP0_TWT_REQ;
+ he_cap_elem->mac_cap_info[1] |=
+ IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US;
+
+@@ -103,15 +102,7 @@ mt7921_init_he_caps(struct mt7921_phy *phy, enum nl80211_band band,
+ he_cap_elem->phy_cap_info[3] |=
+ IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK |
+ IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK;
+- he_cap_elem->phy_cap_info[4] |=
+- IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE |
+- IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4;
+- he_cap_elem->phy_cap_info[5] |=
+- IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
+- IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK;
+ he_cap_elem->phy_cap_info[6] |=
+- IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+- IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+ IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB |
+ IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE |
+ IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT;
+@@ -325,7 +316,7 @@ static void mt7921_remove_interface(struct ieee80211_hw *hw,
+ spin_unlock_bh(&dev->sta_poll_lock);
+ }
+
+-static int mt7921_set_channel(struct mt7921_phy *phy)
++int mt7921_set_channel(struct mt7921_phy *phy)
+ {
+ struct mt7921_dev *dev = phy->dev;
+ int ret;
+@@ -389,7 +380,6 @@ static int mt7921_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ case WLAN_CIPHER_SUITE_WEP104:
+ if (!mvif->wep_sta)
+ return -EOPNOTSUPP;
+- break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
+@@ -442,9 +432,6 @@ static int mt7921_config(struct ieee80211_hw *hw, u32 changed)
+
+ mt7921_mutex_acquire(dev);
+
+- if (changed & IEEE80211_CONF_CHANGE_POWER)
+- mt76_connac_mcu_set_rate_txpower(phy->mt76);
+-
+ if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+ bool enabled = !!(hw->conf.flags & IEEE80211_CONF_MONITOR);
+
+@@ -590,8 +577,7 @@ static void mt7921_bss_info_changed(struct ieee80211_hw *hw,
+ mt7921_mcu_uni_bss_ps(dev, vif);
+
+ if (changed & BSS_CHANGED_ASSOC) {
+- mt7921_mcu_sta_update(dev, NULL, vif, true,
+- MT76_STA_INFO_STATE_ASSOC);
++ mt7921_mcu_sta_add(dev, NULL, vif, true);
+ mt7921_bss_bcnft_apply(dev, vif, info->assoc);
+ }
+
+@@ -630,14 +616,17 @@ int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ if (ret)
+ return ret;
+
+- if (vif->type == NL80211_IFTYPE_STATION)
++ if (vif->type == NL80211_IFTYPE_STATION) {
+ mvif->wep_sta = msta;
++ if (!sta->tdls)
++ mt76_connac_mcu_uni_add_bss(&dev->mphy, vif,
++ &mvif->sta.wcid, true);
++ }
+
+ mt7921_mac_wtbl_update(dev, idx,
+ MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+
+- ret = mt7921_mcu_sta_update(dev, sta, vif, true,
+- MT76_STA_INFO_STATE_NONE);
++ ret = mt7921_mcu_sta_add(dev, sta, vif, true);
+ if (ret)
+ return ret;
+
+@@ -646,27 +635,6 @@ int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ return 0;
+ }
+
+-void mt7921_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta)
+-{
+- struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76);
+- struct mt7921_sta *msta = (struct mt7921_sta *)sta->drv_priv;
+- struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
+-
+- mt7921_mutex_acquire(dev);
+-
+- if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls)
+- mt76_connac_mcu_uni_add_bss(&dev->mphy, vif, &mvif->sta.wcid,
+- true);
+-
+- mt7921_mac_wtbl_update(dev, msta->wcid.idx,
+- MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+-
+- mt7921_mcu_sta_update(dev, sta, vif, true, MT76_STA_INFO_STATE_ASSOC);
+-
+- mt7921_mutex_release(dev);
+-}
+-
+ void mt7921_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+ {
+@@ -676,7 +644,7 @@ void mt7921_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ mt76_connac_free_pending_tx_skbs(&dev->pm, &msta->wcid);
+ mt76_connac_pm_wake(&dev->mphy, &dev->pm);
+
+- mt7921_mcu_sta_update(dev, sta, vif, false, MT76_STA_INFO_STATE_NONE);
++ mt7921_mcu_sta_add(dev, sta, vif, false);
+ mt7921_mac_wtbl_update(dev, msta->wcid.idx,
+ MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+
+@@ -711,7 +679,7 @@ void mt7921_tx_worker(struct mt76_worker *w)
+ }
+
+ mt76_txq_schedule_all(&dev->mphy);
+- mt76_connac_pm_unref(&dev->mphy, &dev->pm);
++ mt76_connac_pm_unref(&dev->pm);
+ }
+
+ static void mt7921_tx(struct ieee80211_hw *hw,
+@@ -741,7 +709,7 @@ static void mt7921_tx(struct ieee80211_hw *hw,
+
+ if (mt76_connac_pm_ref(mphy, &dev->pm)) {
+ mt76_tx(mphy, control->sta, wcid, skb);
+- mt76_connac_pm_unref(mphy, &dev->pm);
++ mt76_connac_pm_unref(&dev->pm);
+ return;
+ }
+
+@@ -822,21 +790,20 @@ mt7921_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ return ret;
+ }
+
+-static int mt7921_sta_state(struct ieee80211_hw *hw,
+- struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta,
+- enum ieee80211_sta_state old_state,
+- enum ieee80211_sta_state new_state)
++static int
++mt7921_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta)
+ {
+- struct mt7921_dev *dev = mt7921_hw_dev(hw);
+-
+- if (dev->pm.ds_enable) {
+- mt7921_mutex_acquire(dev);
+- mt76_connac_sta_state_dp(&dev->mt76, old_state, new_state);
+- mt7921_mutex_release(dev);
+- }
++ return mt76_sta_state(hw, vif, sta, IEEE80211_STA_NOTEXIST,
++ IEEE80211_STA_NONE);
++}
+
+- return mt76_sta_state(hw, vif, sta, old_state, new_state);
++static int
++mt7921_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta)
++{
++ return mt76_sta_state(hw, vif, sta, IEEE80211_STA_NONE,
++ IEEE80211_STA_NOTEXIST);
+ }
+
+ static int
+@@ -1155,23 +1122,6 @@ static void mt7921_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ HZ / 2);
+ }
+
+-static void mt7921_sta_set_decap_offload(struct ieee80211_hw *hw,
+- struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta,
+- bool enabled)
+-{
+- struct mt7921_sta *msta = (struct mt7921_sta *)sta->drv_priv;
+- struct mt7921_dev *dev = mt7921_hw_dev(hw);
+-
+- if (enabled)
+- set_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
+- else
+- clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
+-
+- mt76_connac_mcu_sta_update_hdr_trans(&dev->mt76, vif, &msta->wcid,
+- MCU_UNI_CMD_STA_REC_UPDATE);
+-}
+-
+ const struct ieee80211_ops mt7921_ops = {
+ .tx = mt7921_tx,
+ .start = mt7921_start,
+@@ -1182,10 +1132,10 @@ const struct ieee80211_ops mt7921_ops = {
+ .conf_tx = mt7921_conf_tx,
+ .configure_filter = mt7921_configure_filter,
+ .bss_info_changed = mt7921_bss_info_changed,
+- .sta_state = mt7921_sta_state,
++ .sta_add = mt7921_sta_add,
++ .sta_remove = mt7921_sta_remove,
+ .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
+ .set_key = mt7921_set_key,
+- .sta_set_decap_offload = mt7921_sta_set_decap_offload,
+ .ampdu_action = mt7921_ampdu_action,
+ .set_rts_threshold = mt7921_set_rts_threshold,
+ .wake_tx_queue = mt76_wake_tx_queue,
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
+index 9fbaacc67cfa..fc0d7dc3a5f3 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
+@@ -398,6 +398,43 @@ mt7921_mcu_tx_rate_parse(struct mt76_phy *mphy,
+ }
+ }
+
++static void
++mt7921_mcu_tx_rate_report(struct mt7921_dev *dev, struct sk_buff *skb,
++ u16 wlan_idx)
++{
++ struct mt7921_mcu_wlan_info_event *wtbl_info;
++ struct mt76_phy *mphy = &dev->mphy;
++ struct mt7921_sta_stats *stats;
++ struct rate_info rate = {};
++ struct mt7921_sta *msta;
++ struct mt76_wcid *wcid;
++ u8 idx;
++
++ if (wlan_idx >= MT76_N_WCIDS)
++ return;
++
++ wtbl_info = (struct mt7921_mcu_wlan_info_event *)skb->data;
++ idx = wtbl_info->rate_info.rate_idx;
++ if (idx >= ARRAY_SIZE(wtbl_info->rate_info.rate))
++ return;
++
++ rcu_read_lock();
++
++ wcid = rcu_dereference(dev->mt76.wcid[wlan_idx]);
++ if (!wcid)
++ goto out;
++
++ msta = container_of(wcid, struct mt7921_sta, wcid);
++ stats = &msta->stats;
++
++ /* current rate */
++ mt7921_mcu_tx_rate_parse(mphy, &wtbl_info->peer_cap, &rate,
++ le16_to_cpu(wtbl_info->rate_info.rate[idx]));
++ stats->tx_rate = rate;
++out:
++ rcu_read_unlock();
++}
++
+ static void
+ mt7921_mcu_scan_event(struct mt7921_dev *dev, struct sk_buff *skb)
+ {
+@@ -497,49 +534,6 @@ mt7921_mcu_low_power_event(struct mt7921_dev *dev, struct sk_buff *skb)
+ trace_lp_event(dev, event->state);
+ }
+
+-static void
+-mt7921_mcu_tx_done_event(struct mt7921_dev *dev, struct sk_buff *skb)
+-{
+- struct mt7921_mcu_tx_done_event *event;
+- struct mt7921_sta *msta;
+- struct mt7921_phy *mphy = &dev->phy;
+- struct mt7921_mcu_peer_cap peer;
+- struct ieee80211_sta *sta;
+- LIST_HEAD(list);
+-
+- skb_pull(skb, sizeof(struct mt7921_mcu_rxd));
+- event = (struct mt7921_mcu_tx_done_event *)skb->data;
+-
+- spin_lock_bh(&dev->sta_poll_lock);
+- list_splice_init(&mphy->stats_list, &list);
+-
+- while (!list_empty(&list)) {
+- msta = list_first_entry(&list, struct mt7921_sta, stats_list);
+- list_del_init(&msta->stats_list);
+-
+- if (msta->wcid.idx != event->wlan_idx)
+- continue;
+-
+- spin_unlock_bh(&dev->sta_poll_lock);
+-
+- sta = wcid_to_sta(&msta->wcid);
+-
+- /* peer config based on IEEE SPEC */
+- memset(&peer, 0x0, sizeof(peer));
+- peer.bw = event->bw;
+- peer.g2 = !!(sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20);
+- peer.g4 = !!(sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40);
+- peer.g8 = !!(sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80);
+- peer.g16 = !!(sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160);
+- mt7921_mcu_tx_rate_parse(mphy->mt76, &peer,
+- &msta->stats.tx_rate, event->tx_rate);
+-
+- spin_lock_bh(&dev->sta_poll_lock);
+- break;
+- }
+- spin_unlock_bh(&dev->sta_poll_lock);
+-}
+-
+ static void
+ mt7921_mcu_rx_unsolicited_event(struct mt7921_dev *dev, struct sk_buff *skb)
+ {
+@@ -566,9 +560,6 @@ mt7921_mcu_rx_unsolicited_event(struct mt7921_dev *dev, struct sk_buff *skb)
+ case MCU_EVENT_LP_INFO:
+ mt7921_mcu_low_power_event(dev, skb);
+ break;
+- case MCU_EVENT_TX_DONE:
+- mt7921_mcu_tx_done_event(dev, skb);
+- break;
+ default:
+ break;
+ }
+@@ -589,7 +580,6 @@ void mt7921_mcu_rx_event(struct mt7921_dev *dev, struct sk_buff *skb)
+ rxd->eid == MCU_EVENT_SCHED_SCAN_DONE ||
+ rxd->eid == MCU_EVENT_BSS_ABSENCE ||
+ rxd->eid == MCU_EVENT_SCAN_DONE ||
+- rxd->eid == MCU_EVENT_TX_DONE ||
+ rxd->eid == MCU_EVENT_DBG_MSG ||
+ rxd->eid == MCU_EVENT_COREDUMP ||
+ rxd->eid == MCU_EVENT_LP_INFO ||
+@@ -956,6 +946,8 @@ static int mt7921_load_firmware(struct mt7921_dev *dev)
+ dev->mt76.hw->wiphy->wowlan = &mt76_connac_wowlan_support;
+ #endif /* CONFIG_PM */
+
++ clear_bit(MT76_STATE_PM, &dev->mphy.state);
++
+ dev_err(dev->mt76.dev, "Firmware init done\n");
+
+ return 0;
+@@ -989,7 +981,7 @@ int mt7921_run_firmware(struct mt7921_dev *dev)
+ set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
+ mt7921_mcu_fw_log_2_host(dev, 1);
+
+- return mt76_connac_mcu_get_nic_capability(&dev->mphy);
++ return 0;
+ }
+
+ int mt7921_mcu_init(struct mt7921_dev *dev)
+@@ -1156,6 +1148,26 @@ int mt7921_mcu_get_eeprom(struct mt7921_dev *dev, u32 offset)
+ return 0;
+ }
+
++u32 mt7921_get_wtbl_info(struct mt7921_dev *dev, u32 wlan_idx)
++{
++ struct mt7921_mcu_wlan_info wtbl_info = {
++ .wlan_idx = cpu_to_le32(wlan_idx),
++ };
++ struct sk_buff *skb;
++ int ret;
++
++ ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_CMD_GET_WTBL,
++ &wtbl_info, sizeof(wtbl_info), true,
++ &skb);
++ if (ret)
++ return ret;
++
++ mt7921_mcu_tx_rate_report(dev, skb, wlan_idx);
++ dev_kfree_skb(skb);
++
++ return 0;
++}
++
+ int mt7921_mcu_uni_bss_ps(struct mt7921_dev *dev, struct ieee80211_vif *vif)
+ {
+ struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
+@@ -1268,9 +1280,8 @@ int mt7921_mcu_set_bss_pm(struct mt7921_dev *dev, struct ieee80211_vif *vif,
+ sizeof(req), false);
+ }
+
+-int mt7921_mcu_sta_update(struct mt7921_dev *dev, struct ieee80211_sta *sta,
+- struct ieee80211_vif *vif, bool enable,
+- enum mt76_sta_info_state state)
++int mt7921_mcu_sta_add(struct mt7921_dev *dev, struct ieee80211_sta *sta,
++ struct ieee80211_vif *vif, bool enable)
+ {
+ struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
+ int rssi = -ewma_rssi_read(&mvif->rssi);
+@@ -1279,7 +1290,6 @@ int mt7921_mcu_sta_update(struct mt7921_dev *dev, struct ieee80211_sta *sta,
+ .vif = vif,
+ .enable = enable,
+ .cmd = MCU_UNI_CMD_STA_REC_UPDATE,
+- .state = state,
+ .offload_fw = true,
+ .rcpi = to_rcpi(rssi),
+ };
+@@ -1287,9 +1297,8 @@ int mt7921_mcu_sta_update(struct mt7921_dev *dev, struct ieee80211_sta *sta,
+
+ msta = sta ? (struct mt7921_sta *)sta->drv_priv : NULL;
+ info.wcid = msta ? &msta->wcid : &mvif->sta.wcid;
+- info.newly = msta ? state != MT76_STA_INFO_STATE_ASSOC : true;
+
+- return mt76_connac_mcu_sta_cmd(&dev->mphy, &info);
++ return mt76_connac_mcu_add_sta_cmd(&dev->mphy, &info);
+ }
+
+ int __mt7921_mcu_drv_pmctrl(struct mt7921_dev *dev)
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.h
+index de3c091f6736..adad20819341 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.h
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.h
+@@ -81,7 +81,6 @@ enum {
+ MCU_EVENT_REG_ACCESS = 0x05,
+ MCU_EVENT_LP_INFO = 0x07,
+ MCU_EVENT_SCAN_DONE = 0x0d,
+- MCU_EVENT_TX_DONE = 0x0f,
+ MCU_EVENT_BSS_ABSENCE = 0x11,
+ MCU_EVENT_BSS_BEACON_LOSS = 0x13,
+ MCU_EVENT_CH_PRIVILEGE = 0x18,
+@@ -255,6 +254,86 @@ struct mt7921_mcu_reg_event {
+ __le32 val;
+ } __packed;
+
++struct mt7921_mcu_tx_config {
++ u8 peer_addr[ETH_ALEN];
++ u8 sw;
++ u8 dis_rx_hdr_tran;
++
++ u8 aad_om;
++ u8 pfmu_idx;
++ __le16 partial_aid;
++
++ u8 ibf;
++ u8 ebf;
++ u8 is_ht;
++ u8 is_vht;
++
++ u8 mesh;
++ u8 baf_en;
++ u8 cf_ack;
++ u8 rdg_ba;
++
++ u8 rdg;
++ u8 pm;
++ u8 rts;
++ u8 smps;
++
++ u8 txop_ps;
++ u8 not_update_ipsm;
++ u8 skip_tx;
++ u8 ldpc;
++
++ u8 qos;
++ u8 from_ds;
++ u8 to_ds;
++ u8 dyn_bw;
++
++ u8 amdsu_cross_lg;
++ u8 check_per;
++ u8 gid_63;
++ u8 he;
++
++ u8 vht_ibf;
++ u8 vht_ebf;
++ u8 vht_ldpc;
++ u8 he_ldpc;
++} __packed;
++
++struct mt7921_mcu_sec_config {
++ u8 wpi_flag;
++ u8 rv;
++ u8 ikv;
++ u8 rkv;
++
++ u8 rcid;
++ u8 rca1;
++ u8 rca2;
++ u8 even_pn;
++
++ u8 key_id;
++ u8 muar_idx;
++ u8 cipher_suit;
++ u8 rsv[1];
++} __packed;
++
++struct mt7921_mcu_key_config {
++ u8 key[32];
++} __packed;
++
++struct mt7921_mcu_rate_info {
++ u8 mpdu_fail;
++ u8 mpdu_tx;
++ u8 rate_idx;
++ u8 rsv[1];
++ __le16 rate[8];
++} __packed;
++
++struct mt7921_mcu_ba_config {
++ u8 ba_en;
++ u8 rsv[3];
++ __le32 ba_winsize;
++} __packed;
++
+ struct mt7921_mcu_ant_id_config {
+ u8 ant_id[4];
+ } __packed;
+@@ -278,6 +357,41 @@ struct mt7921_mcu_peer_cap {
+ u8 rsv[1];
+ } __packed;
+
++struct mt7921_mcu_rx_cnt {
++ u8 rx_rcpi[4];
++ u8 rx_cc[4];
++ u8 rx_cc_sel;
++ u8 ce_rmsd;
++ u8 rsv[2];
++} __packed;
++
++struct mt7921_mcu_tx_cnt {
++ __le16 rate1_cnt;
++ __le16 rate1_fail_cnt;
++ __le16 rate2_cnt;
++ __le16 rate3_cnt;
++ __le16 cur_bw_tx_cnt;
++ __le16 cur_bw_tx_fail_cnt;
++ __le16 other_bw_tx_cnt;
++ __le16 other_bw_tx_fail_cnt;
++} __packed;
++
++struct mt7921_mcu_wlan_info_event {
++ struct mt7921_mcu_tx_config tx_config;
++ struct mt7921_mcu_sec_config sec_config;
++ struct mt7921_mcu_key_config key_config;
++ struct mt7921_mcu_rate_info rate_info;
++ struct mt7921_mcu_ba_config ba_config;
++ struct mt7921_mcu_peer_cap peer_cap;
++ struct mt7921_mcu_rx_cnt rx_cnt;
++ struct mt7921_mcu_tx_cnt tx_cnt;
++} __packed;
++
++struct mt7921_mcu_wlan_info {
++ __le32 wlan_idx;
++ struct mt7921_mcu_wlan_info_event event;
++} __packed;
++
+ struct mt7921_txpwr_req {
+ u8 ver;
+ u8 action;
+@@ -293,31 +407,4 @@ struct mt7921_txpwr_event {
+ struct mt7921_txpwr txpwr;
+ } __packed;
+
+-struct mt7921_mcu_tx_done_event {
+- u8 pid;
+- u8 status;
+- u16 seq;
+-
+- u8 wlan_idx;
+- u8 tx_cnt;
+- u16 tx_rate;
+-
+- u8 flag;
+- u8 tid;
+- u8 rsp_rate;
+- u8 mcs;
+-
+- u8 bw;
+- u8 tx_pwr;
+- u8 reason;
+- u8 rsv0[1];
+-
+- u32 delay;
+- u32 timestamp;
+- u32 applied_flag;
+-
+- u8 txs[28];
+-
+- u8 rsv1[32];
+-} __packed;
+ #endif
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
+index 2d8bd6bfc820..957084c3ca43 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
+@@ -92,8 +92,6 @@ struct mt7921_sta {
+ unsigned long ampdu_state;
+
+ struct mt7921_sta_key_conf bip;
+-
+- unsigned long next_txs_ts;
+ };
+
+ DECLARE_EWMA(rssi, 10, 8);
+@@ -160,8 +158,7 @@ struct mt7921_dev {
+ u16 chainmask;
+
+ struct work_struct reset_work;
+- bool hw_full_reset:1;
+- bool hw_init_done:1;
++ bool hw_full_reset;
+
+ struct list_head sta_poll_list;
+ spinlock_t sta_poll_lock;
+@@ -262,9 +259,9 @@ int mt7921_mcu_init(struct mt7921_dev *dev);
+ int mt7921_mcu_add_key(struct mt7921_dev *dev, struct ieee80211_vif *vif,
+ struct mt7921_sta *msta, struct ieee80211_key_conf *key,
+ enum set_key_cmd cmd);
+-int mt7921_mcu_sta_update(struct mt7921_dev *dev, struct ieee80211_sta *sta,
+- struct ieee80211_vif *vif, bool enable,
+- enum mt76_sta_info_state state);
++int mt7921_set_channel(struct mt7921_phy *phy);
++int mt7921_mcu_sta_add(struct mt7921_dev *dev, struct ieee80211_sta *sta,
++ struct ieee80211_vif *vif, bool enable);
+ int mt7921_mcu_set_chan_info(struct mt7921_phy *phy, int cmd);
+ int mt7921_mcu_set_tx(struct mt7921_dev *dev, struct ieee80211_vif *vif);
+ int mt7921_mcu_set_eeprom(struct mt7921_dev *dev);
+@@ -324,7 +321,7 @@ static inline bool mt7921_dma_need_reinit(struct mt7921_dev *dev)
+ return !mt76_get_field(dev, MT_WFDMA_DUMMY_CR, MT_WFDMA_NEED_REINIT);
+ }
+
+-int mt7921_mac_init(struct mt7921_dev *dev);
++void mt7921_mac_init(struct mt7921_dev *dev);
+ bool mt7921_mac_wtbl_update(struct mt7921_dev *dev, int idx, u32 mask);
+ void mt7921_mac_reset_counters(struct mt7921_phy *phy);
+ void mt7921_mac_write_txwi(struct mt7921_dev *dev, __le32 *txwi,
+@@ -336,8 +333,6 @@ void mt7921_mac_fill_rx_vector(struct mt7921_dev *dev, struct sk_buff *skb);
+ void mt7921_mac_tx_free(struct mt7921_dev *dev, struct sk_buff *skb);
+ int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+-void mt7921_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+- struct ieee80211_sta *sta);
+ void mt7921_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+ void mt7921_mac_work(struct work_struct *work);
+@@ -360,7 +355,7 @@ void mt7921_stats_work(struct work_struct *work);
+ void mt7921_txp_skb_unmap(struct mt76_dev *dev,
+ struct mt76_txwi_cache *txwi);
+ void mt7921_set_stream_he_caps(struct mt7921_phy *phy);
+-void mt7921_update_channel(struct mt76_phy *mphy);
++void mt7921_update_channel(struct mt76_dev *mdev);
+ int mt7921_init_debugfs(struct mt7921_dev *dev);
+
+ int mt7921_mcu_uni_tx_ba(struct mt7921_dev *dev,
+@@ -370,6 +365,7 @@ int mt7921_mcu_uni_rx_ba(struct mt7921_dev *dev,
+ struct ieee80211_ampdu_params *params,
+ bool enable);
+ void mt7921_scan_work(struct work_struct *work);
++u32 mt7921_get_wtbl_info(struct mt7921_dev *dev, u32 wlan_idx);
+ int mt7921_mcu_uni_bss_ps(struct mt7921_dev *dev, struct ieee80211_vif *vif);
+ int mt7921_mcu_uni_bss_bcnft(struct mt7921_dev *dev, struct ieee80211_vif *vif,
+ bool enable);
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
+index c3905bcab360..13263f50dc00 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
+@@ -106,7 +106,6 @@ static int mt7921_pci_probe(struct pci_dev *pdev,
+ .rx_poll_complete = mt7921_rx_poll_complete,
+ .sta_ps = mt7921_sta_ps,
+ .sta_add = mt7921_mac_sta_add,
+- .sta_assoc = mt7921_mac_sta_assoc,
+ .sta_remove = mt7921_mac_sta_remove,
+ .update_survey = mt7921_update_channel,
+ };
+@@ -208,10 +207,8 @@ static int mt7921_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+ goto restore_suspend;
+ }
+
+- /* always enable deep sleep during suspend to reduce
+- * power consumption
+- */
+- mt76_connac_mcu_set_deep_sleep(&dev->mt76, true);
++ if (!pm->enable)
++ mt76_connac_mcu_set_deep_sleep(&dev->mt76, true);
+
+ napi_disable(&mdev->tx_napi);
+ mt76_worker_disable(&mdev->tx_worker);
+@@ -254,7 +251,7 @@ static int mt7921_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+ }
+ napi_enable(&mdev->tx_napi);
+
+- if (!pm->ds_enable)
++ if (!pm->enable)
+ mt76_connac_mcu_set_deep_sleep(&dev->mt76, false);
+
+ if (hif_suspend)
+@@ -270,10 +267,9 @@ static int mt7921_pci_resume(struct pci_dev *pdev)
+ {
+ struct mt76_dev *mdev = pci_get_drvdata(pdev);
+ struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76);
+- struct mt76_connac_pm *pm = &dev->pm;
+ int i, err;
+
+- pm->suspended = false;
++ dev->pm.suspended = false;
+ err = pci_set_power_state(pdev, PCI_D0);
+ if (err)
+ return err;
+@@ -304,8 +300,7 @@ static int mt7921_pci_resume(struct pci_dev *pdev)
+ napi_enable(&mdev->tx_napi);
+ napi_schedule(&mdev->tx_napi);
+
+- /* restore previous ds setting */
+- if (!pm->ds_enable)
++ if (!dev->pm.enable)
+ mt76_connac_mcu_set_deep_sleep(&dev->mt76, false);
+
+ if (!test_bit(MT76_STATE_SUSPEND, &dev->mphy.state))
+diff --git a/drivers/net/wireless/mediatek/mt76/sdio.c b/drivers/net/wireless/mediatek/mt76/sdio.c
+index 783a15635ec5..a18d2896ee1f 100644
+--- a/drivers/net/wireless/mediatek/mt76/sdio.c
++++ b/drivers/net/wireless/mediatek/mt76/sdio.c
+@@ -184,6 +184,9 @@ static int mt76s_process_tx_queue(struct mt76_dev *dev, struct mt76_queue *q)
+ if (!q->queued)
+ wake_up(&dev->tx_wait);
+
++ if (!mcu)
++ mt76_txq_schedule(&dev->phy, q->qid);
++
+ return nframes;
+ }
+
+@@ -192,28 +195,19 @@ static void mt76s_status_worker(struct mt76_worker *w)
+ struct mt76_sdio *sdio = container_of(w, struct mt76_sdio,
+ status_worker);
+ struct mt76_dev *dev = container_of(sdio, struct mt76_dev, sdio);
+- bool resched = false;
+ int i, nframes;
+
+ do {
+- int ndata_frames = 0;
+-
+ nframes = mt76s_process_tx_queue(dev, dev->q_mcu[MT_MCUQ_WM]);
+
+ for (i = 0; i <= MT_TXQ_PSD; i++)
+- ndata_frames += mt76s_process_tx_queue(dev,
+- dev->phy.q_tx[i]);
+- nframes += ndata_frames;
+- if (ndata_frames > 0)
+- resched = true;
++ nframes += mt76s_process_tx_queue(dev,
++ dev->phy.q_tx[i]);
+
+ if (dev->drv->tx_status_data &&
+ !test_and_set_bit(MT76_READING_STATS, &dev->phy.state))
+ queue_work(dev->wq, &dev->sdio.stat_work);
+ } while (nframes > 0);
+-
+- if (resched)
+- mt76_worker_schedule(&dev->sdio.txrx_worker);
+ }
+
+ static void mt76s_tx_status_data(struct work_struct *work)
+@@ -262,7 +256,6 @@ mt76s_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
+
+ q->entry[q->head].skb = tx_info.skb;
+ q->entry[q->head].buf_sz = len;
+- q->entry[q->head].wcid = 0xffff;
+
+ smp_wmb();
+
+diff --git a/drivers/net/wireless/mediatek/mt76/testmode.c b/drivers/net/wireless/mediatek/mt76/testmode.c
+index f73ffbd6e622..f614c887f323 100644
+--- a/drivers/net/wireless/mediatek/mt76/testmode.c
++++ b/drivers/net/wireless/mediatek/mt76/testmode.c
+@@ -88,8 +88,17 @@ static void
+ mt76_testmode_free_skb(struct mt76_phy *phy)
+ {
+ struct mt76_testmode_data *td = &phy->test;
++ struct sk_buff *skb = td->tx_skb;
++
++ if (!skb)
++ return;
+
+- dev_kfree_skb(td->tx_skb);
++ if (skb_has_frag_list(skb)) {
++ kfree_skb_list(skb_shinfo(skb)->frag_list);
++ skb_shinfo(skb)->frag_list = NULL;
++ }
++
++ dev_kfree_skb(skb);
+ td->tx_skb = NULL;
+ }
+
+@@ -521,14 +530,6 @@ mt76_testmode_dump_stats(struct mt76_phy *phy, struct sk_buff *msg)
+ u64 rx_fcs_error = 0;
+ int i;
+
+- if (dev->test_ops->dump_stats) {
+- int ret;
+-
+- ret = dev->test_ops->dump_stats(phy, msg);
+- if (ret)
+- return ret;
+- }
+-
+ for (i = 0; i < ARRAY_SIZE(td->rx_stats.packets); i++) {
+ rx_packets += td->rx_stats.packets[i];
+ rx_fcs_error += td->rx_stats.fcs_error[i];
+@@ -543,6 +544,9 @@ mt76_testmode_dump_stats(struct mt76_phy *phy, struct sk_buff *msg)
+ MT76_TM_STATS_ATTR_PAD))
+ return -EMSGSIZE;
+
++ if (dev->test_ops->dump_stats)
++ return dev->test_ops->dump_stats(phy, msg);
++
+ return 0;
+ }
+
+diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c
+index f0f7a913eaab..441d06e30b1a 100644
+--- a/drivers/net/wireless/mediatek/mt76/tx.c
++++ b/drivers/net/wireless/mediatek/mt76/tx.c
+@@ -54,23 +54,11 @@ mt76_tx_status_unlock(struct mt76_dev *dev, struct sk_buff_head *list)
+
+ spin_unlock_bh(&dev->status_list.lock);
+
+- rcu_read_lock();
+ while ((skb = __skb_dequeue(list)) != NULL) {
+- struct ieee80211_tx_status status = {
+- .skb = skb,
+- .info = IEEE80211_SKB_CB(skb),
+- };
+- struct mt76_tx_cb *cb = mt76_tx_skb_cb(skb);
+- struct mt76_wcid *wcid;
+-
+- wcid = rcu_dereference(dev->wcid[cb->wcid]);
+- if (wcid)
+- status.sta = wcid_to_sta(wcid);
+-
+ hw = mt76_tx_status_get_hw(dev, skb);
+- ieee80211_tx_status_ext(hw, &status);
++ ieee80211_tx_status(hw, skb);
+ }
+- rcu_read_unlock();
++
+ }
+ EXPORT_SYMBOL_GPL(mt76_tx_status_unlock);
+
+@@ -92,7 +80,7 @@ __mt76_tx_status_skb_done(struct mt76_dev *dev, struct sk_buff *skb, u8 flags,
+
+ /* Tx status can be unreliable. if it fails, mark the frame as ACKed */
+ if (flags & MT_TX_CB_TXS_FAILED) {
+- info->status.rates[0].count = 0;
++ ieee80211_tx_info_clear_status(info);
+ info->status.rates[0].idx = -1;
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ }
+@@ -129,7 +117,12 @@ mt76_tx_status_skb_add(struct mt76_dev *dev, struct mt76_wcid *wcid,
+ spin_lock_bh(&dev->status_list.lock);
+
+ memset(cb, 0, sizeof(*cb));
+- pid = mt76_get_next_pkt_id(wcid);
++ wcid->packet_id = (wcid->packet_id + 1) & MT_PACKET_ID_MASK;
++ if (wcid->packet_id == MT_PACKET_ID_NO_ACK ||
++ wcid->packet_id == MT_PACKET_ID_NO_SKB)
++ wcid->packet_id = MT_PACKET_ID_FIRST;
++
++ pid = wcid->packet_id;
+ cb->wcid = wcid->idx;
+ cb->pktid = pid;
+ cb->jiffies = jiffies;
+@@ -180,37 +173,36 @@ mt76_tx_status_check(struct mt76_dev *dev, struct mt76_wcid *wcid, bool flush)
+ EXPORT_SYMBOL_GPL(mt76_tx_status_check);
+
+ static void
+-mt76_tx_check_non_aql(struct mt76_dev *dev, struct mt76_wcid *wcid,
+- struct sk_buff *skb)
++mt76_tx_check_non_aql(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *skb)
+ {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
++ struct mt76_wcid *wcid;
+ int pending;
+
+- if (!wcid || info->tx_time_est)
++ if (info->tx_time_est)
++ return;
++
++ if (wcid_idx >= ARRAY_SIZE(dev->wcid))
+ return;
+
+- pending = atomic_dec_return(&wcid->non_aql_packets);
+- if (pending < 0)
+- atomic_cmpxchg(&wcid->non_aql_packets, pending, 0);
++ rcu_read_lock();
++
++ wcid = rcu_dereference(dev->wcid[wcid_idx]);
++ if (wcid) {
++ pending = atomic_dec_return(&wcid->non_aql_packets);
++ if (pending < 0)
++ atomic_cmpxchg(&wcid->non_aql_packets, pending, 0);
++ }
++
++ rcu_read_unlock();
+ }
+
+-void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *skb,
+- struct list_head *free_list)
++void mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *skb)
+ {
+- struct ieee80211_tx_status status = {
+- .skb = skb,
+- .free_list = free_list,
+- };
+- struct mt76_wcid *wcid = NULL;
+ struct ieee80211_hw *hw;
+ struct sk_buff_head list;
+
+- rcu_read_lock();
+-
+- if (wcid_idx < ARRAY_SIZE(dev->wcid))
+- wcid = rcu_dereference(dev->wcid[wcid_idx]);
+-
+- mt76_tx_check_non_aql(dev, wcid, skb);
++ mt76_tx_check_non_aql(dev, wcid_idx, skb);
+
+ #ifdef CONFIG_NL80211_TESTMODE
+ if (mt76_is_testmode_skb(dev, skb, &hw)) {
+@@ -222,25 +214,21 @@ void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *
+ wake_up(&dev->tx_wait);
+
+ dev_kfree_skb_any(skb);
+- goto out;
++ return;
+ }
+ #endif
+
+ if (!skb->prev) {
+ hw = mt76_tx_status_get_hw(dev, skb);
+- status.sta = wcid_to_sta(wcid);
+- ieee80211_tx_status_ext(hw, &status);
+- goto out;
++ ieee80211_free_txskb(hw, skb);
++ return;
+ }
+
+ mt76_tx_status_lock(dev, &list);
+ __mt76_tx_status_skb_done(dev, skb, MT_TX_CB_DMA_DONE, &list);
+ mt76_tx_status_unlock(dev, &list);
+-
+-out:
+- rcu_read_unlock();
+ }
+-EXPORT_SYMBOL_GPL(__mt76_tx_complete_skb);
++EXPORT_SYMBOL_GPL(mt76_tx_complete_skb);
+
+ static int
+ __mt76_tx_queue_skb(struct mt76_phy *phy, int qid, struct sk_buff *skb,
+@@ -256,15 +244,11 @@ __mt76_tx_queue_skb(struct mt76_phy *phy, int qid, struct sk_buff *skb,
+
+ non_aql = !info->tx_time_est;
+ idx = dev->queue_ops->tx_queue_skb(dev, q, skb, wcid, sta);
+- if (idx < 0 || !sta)
++ if (idx < 0 || !sta || !non_aql)
+ return idx;
+
+ wcid = (struct mt76_wcid *)sta->drv_priv;
+ q->entry[idx].wcid = wcid->idx;
+-
+- if (!non_aql)
+- return idx;
+-
+ pending = atomic_inc_return(&wcid->non_aql_packets);
+ if (stop && pending >= MT_MAX_NON_AQL_PKT)
+ *stop = true;
+diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
+index 1e9f60bb811a..30bc54e98c58 100644
+--- a/drivers/net/wireless/mediatek/mt76/usb.c
++++ b/drivers/net/wireless/mediatek/mt76/usb.c
+@@ -925,7 +925,6 @@ mt76u_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
+
+ q->head = (q->head + 1) % q->ndesc;
+ q->entry[idx].skb = tx_info.skb;
+- q->entry[idx].wcid = 0xffff;
+ q->queued++;
+
+ return idx;
+diff --git a/drivers/net/wireless/mediatek/mt7601u/usb.c b/drivers/net/wireless/mediatek/mt7601u/usb.c
+index cc772045d526..6bcc4a13ae6c 100644
+--- a/drivers/net/wireless/mediatek/mt7601u/usb.c
++++ b/drivers/net/wireless/mediatek/mt7601u/usb.c
+@@ -26,7 +26,6 @@ static const struct usb_device_id mt7601u_device_table[] = {
+ { USB_DEVICE(0x2717, 0x4106) },
+ { USB_DEVICE(0x2955, 0x0001) },
+ { USB_DEVICE(0x2955, 0x1001) },
+- { USB_DEVICE(0x2955, 0x1003) },
+ { USB_DEVICE(0x2a5f, 0x1000) },
+ { USB_DEVICE(0x7392, 0x7710) },
+ { 0, }
diff --git a/sys-kernel_arch-sources-g14_files-9008-fix-cpu-hotplug.patch b/sys-kernel_arch-sources-g14_files-9008-fix-cpu-hotplug.patch
new file mode 100644
index 000000000000..f80a0850d67a
--- /dev/null
+++ b/sys-kernel_arch-sources-g14_files-9008-fix-cpu-hotplug.patch
@@ -0,0 +1,73 @@
+Date: Fri, 13 Aug 2021 11:18:42 -0500 [thread overview]
+Message-ID: <20210813161842.222414-1-mario.limonciello@amd.com> (raw)
+
+A number of systems are showing "hotplug capable" CPUs when they
+are not really hotpluggable. This is because the MADT has extra
+CPU entries to support different CPUs that may be inserted into
+the socket with different numbers of cores.
+
+Starting with ACPI 6.3 the spec has an Online Capable bit in the
+MADT used to determine whether or not a CPU is hotplug capable
+when the enabled bit is not set.
+
+Link: https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html?#local-apic-flags
+Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
+---
+ arch/x86/kernel/acpi/boot.c | 10 ++++++++++
+ include/acpi/actbl2.h | 1 +
+ 2 files changed, 11 insertions(+)
+
+Changes from v1->v2:
+ * Check the revision field in MADT to determine if it matches the
+ bump from ACPI 6.3 as suggested by Hanjun Guo
+ * Update description
+
+diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
+index e55e0c1fad8c..bfa69a5c9c0b 100644
+--- a/arch/x86/kernel/acpi/boot.c
++++ b/arch/x86/kernel/acpi/boot.c
+@@ -53,6 +53,8 @@ int acpi_ioapic;
+ int acpi_strict;
+ int acpi_disable_cmcff;
+
++bool acpi_support_online_capable;
++
+ /* ACPI SCI override configuration */
+ u8 acpi_sci_flags __initdata;
+ u32 acpi_sci_override_gsi __initdata = INVALID_ACPI_IRQ;
+@@ -138,6 +140,8 @@ static int __init acpi_parse_madt(struct acpi_table_header *table)
+
+ pr_debug("Local APIC address 0x%08x\n", madt->address);
+ }
++ if (madt->header.revision >= 5)
++ acpi_support_online_capable = true;
+
+ default_acpi_madt_oem_check(madt->header.oem_id,
+ madt->header.oem_table_id);
+@@ -239,6 +243,12 @@ acpi_parse_lapic(union acpi_subtable_headers * header, const unsigned long end)
+ if (processor->id == 0xff)
+ return 0;
+
++ /* don't register processors that can not be onlined */
++ if (acpi_support_online_capable &&
++ !(processor->lapic_flags & ACPI_MADT_ENABLED) &&
++ !(processor->lapic_flags & ACPI_MADT_ONLINE_CAPABLE))
++ return 0;
++
+ /*
+ * We need to register disabled CPU as well to permit
+ * counting disabled CPUs. This allows us to size
+diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h
+index 2069ac38a4e2..fae45e383987 100644
+--- a/include/acpi/actbl2.h
++++ b/include/acpi/actbl2.h
+@@ -808,6 +808,7 @@ struct acpi_madt_multiproc_wakeup_mailbox {
+ /* MADT Local APIC flags */
+
+ #define ACPI_MADT_ENABLED (1) /* 00: Processor is usable if set */
++#define ACPI_MADT_ONLINE_CAPABLE (2) /* 01: System HW supports enabling processor at runtime */
+
+ /* MADT MPS INTI flags (inti_flags) */
+
+--
+2.25.1 \ No newline at end of file
diff --git a/sys-kernel_arch-sources-g14_files-9009-amd-pstate-sqashed.patch b/sys-kernel_arch-sources-g14_files-9009-amd-pstate-sqashed.patch
new file mode 100644
index 000000000000..d313ef86c88f
--- /dev/null
+++ b/sys-kernel_arch-sources-g14_files-9009-amd-pstate-sqashed.patch
@@ -0,0 +1,2454 @@
+From 674082f34ac8bfab164a630a275eac291e1bd7be Mon Sep 17 00:00:00 2001
+From: Huang Rui <ray.huang@amd.com>
+Date: Thu, 28 Jan 2021 10:50:26 +0800
+Subject: [PATCH 01/19] x86/cpufreatures: add AMD CPPC extension feature flag
+
+Add Collaborative Processor Performance Control Extension feature flag
+for AMD processors.
+
+Signed-off-by: Huang Rui <ray.huang@amd.com>
+---
+ arch/x86/include/asm/cpufeatures.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
+index d0ce5cfd3ac1..f7aea50e3371 100644
+--- a/arch/x86/include/asm/cpufeatures.h
++++ b/arch/x86/include/asm/cpufeatures.h
+@@ -313,6 +313,7 @@
+ #define X86_FEATURE_AMD_SSBD (13*32+24) /* "" Speculative Store Bypass Disable */
+ #define X86_FEATURE_VIRT_SSBD (13*32+25) /* Virtualized Speculative Store Bypass Disable */
+ #define X86_FEATURE_AMD_SSB_NO (13*32+26) /* "" Speculative Store Bypass is fixed in hardware. */
++#define X86_FEATURE_AMD_CPPC_EXT (13*32+27) /* Collaborative Processor Performance Control Extension */
+
+ /* Thermal and Power Management Leaf, CPUID level 0x00000006 (EAX), word 14 */
+ #define X86_FEATURE_DTHERM (14*32+ 0) /* Digital Thermal Sensor */
+--
+2.33.0
+
+From 8180f01cf18d8ba79dac94a817294e85a8d84dc4 Mon Sep 17 00:00:00 2001
+From: Huang Rui <ray.huang@amd.com>
+Date: Mon, 25 Jan 2021 15:50:24 +0800
+Subject: [PATCH 02/19] x86/msr: add AMD CPPC MSR definitions
+
+AMD CPPC (Collaborative Processor Performance Control) function uses MSR
+registers to manage the performance hints. So add the MSR register macro
+here.
+
+Signed-off-by: Huang Rui <ray.huang@amd.com>
+---
+ arch/x86/include/asm/msr-index.h | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
+index a7c413432b33..ce42e15cf303 100644
+--- a/arch/x86/include/asm/msr-index.h
++++ b/arch/x86/include/asm/msr-index.h
+@@ -486,6 +486,23 @@
+
+ #define MSR_AMD64_VIRT_SPEC_CTRL 0xc001011f
+
++/* AMD Collaborative Processor Performance Control MSRs */
++#define MSR_AMD_CPPC_CAP1 0xc00102b0
++#define MSR_AMD_CPPC_ENABLE 0xc00102b1
++#define MSR_AMD_CPPC_CAP2 0xc00102b2
++#define MSR_AMD_CPPC_REQ 0xc00102b3
++#define MSR_AMD_CPPC_STATUS 0xc00102b4
++
++#define CAP1_LOWEST_PERF(x) (((x) >> 0) & 0xff)
++#define CAP1_LOWNONLIN_PERF(x) (((x) >> 8) & 0xff)
++#define CAP1_NOMINAL_PERF(x) (((x) >> 16) & 0xff)
++#define CAP1_HIGHEST_PERF(x) (((x) >> 24) & 0xff)
++
++#define REQ_MAX_PERF(x) (((x) & 0xff) << 0)
++#define REQ_MIN_PERF(x) (((x) & 0xff) << 8)
++#define REQ_DES_PERF(x) (((x) & 0xff) << 16)
++#define REQ_ENERGY_PERF_PREF(x) (((x) & 0xff) << 24)
++
+ /* Fam 17h MSRs */
+ #define MSR_F17H_IRPERF 0xc00000e9
+
+--
+2.33.0
+
+From 8dc33d1590f80a60f12325e7c646a8551a41adf1 Mon Sep 17 00:00:00 2001
+From: Jinzhou Su <Jinzhou.Su@amd.com>
+Date: Mon, 9 Aug 2021 19:04:17 +0800
+Subject: [PATCH 03/19] ACPI: CPPC: add cppc enable register function
+
+Export the cppc enable register function for future use.
+
+Signed-off-by: Jinzhou Su <Jinzhou.Su@amd.com>
+Signed-off-by: Huang Rui <ray.huang@amd.com>
+---
+ drivers/acpi/cppc_acpi.c | 42 ++++++++++++++++++++++++++++++++++++++++
+ include/acpi/cppc_acpi.h | 5 +++++
+ 2 files changed, 47 insertions(+)
+
+diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
+index a4d4eebba1da..de4b30545215 100644
+--- a/drivers/acpi/cppc_acpi.c
++++ b/drivers/acpi/cppc_acpi.c
+@@ -1220,6 +1220,48 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
+ }
+ EXPORT_SYMBOL_GPL(cppc_get_perf_ctrs);
+
++/**
++ * cppc_set_enable - Set to enable CPPC register.
++ * @cpu: CPU for which to enable CPPC register.
++ * @enable: enable field to write into share memory.
++ *
++ * Return: 0 for success, -ERRNO otherwise.
++ */
++int cppc_set_enable(int cpu, u32 enable)
++{
++ int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
++ struct cpc_register_resource *enable_reg;
++ struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
++ struct cppc_pcc_data *pcc_ss_data = NULL;
++ int ret = -1;
++
++ if (!cpc_desc) {
++ pr_debug("No CPC descriptor for CPU:%d\n", cpu);
++ return -ENODEV;
++ }
++
++ enable_reg = &cpc_desc->cpc_regs[ENABLE];
++
++ if (CPC_IN_PCC(enable_reg)) {
++
++ if (pcc_ss_id < 0)
++ return -EIO;
++
++ ret = cpc_write(cpu, enable_reg, enable);
++ if (ret)
++ return ret;
++
++ pcc_ss_data = pcc_data[pcc_ss_id];
++
++ down_write(&pcc_ss_data->pcc_lock);
++ send_pcc_cmd(pcc_ss_id, CMD_WRITE);
++ up_write(&pcc_ss_data->pcc_lock);
++ }
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(cppc_set_enable);
++
+ /**
+ * cppc_set_perf - Set a CPU's performance controls.
+ * @cpu: CPU for which to set performance controls.
+diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
+index 9f4985b4d64d..3fdae40a75fc 100644
+--- a/include/acpi/cppc_acpi.h
++++ b/include/acpi/cppc_acpi.h
+@@ -137,6 +137,7 @@ struct cppc_cpudata {
+ extern int cppc_get_desired_perf(int cpunum, u64 *desired_perf);
+ extern int cppc_get_perf_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_ctrs);
+ extern int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls);
++extern int cppc_set_enable(int cpu, u32 enable);
+ extern int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps);
+ extern bool acpi_cpc_valid(void);
+ extern int acpi_get_psd_map(unsigned int cpu, struct cppc_cpudata *cpu_data);
+@@ -157,6 +158,10 @@ static inline int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
+ {
+ return -ENOTSUPP;
+ }
++static inline int cppc_set_enable(int cpu, u32 enable)
++{
++ return -ENOTSUPP;
++}
+ static inline int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps)
+ {
+ return -ENOTSUPP;
+--
+2.33.0
+
+From df9ad0b99a9f0e3371aa94e49fe92a2c2a9fa95d Mon Sep 17 00:00:00 2001
+From: Huang Rui <ray.huang@amd.com>
+Date: Thu, 10 Jun 2021 18:04:45 +0800
+Subject: [PATCH 04/19] cpufreq: amd: introduce a new amd pstate driver to
+ support future processors
+
+amd-pstate is the AMD CPU performance scaling driver that introduces a
+new CPU frequency control mechanism on AMD Zen based CPU series in Linux
+kernel. The new mechanism is based on Collaborative processor
+performance control (CPPC) which is finer grain frequency management
+than legacy ACPI hardware P-States. Current AMD CPU platforms are using
+the ACPI P-states driver to manage CPU frequency and clocks with
+switching only in 3 P-states. AMD P-States is to replace the ACPI
+P-states controls, allows a flexible, low-latency interface for the
+Linux kernel to directly communicate the performance hints to hardware.
+
+"amd-pstate" leverages the Linux kernel governors such as *schedutil*,
+*ondemand*, etc. to manage the performance hints which are provided by CPPC
+hardware functionality. The first version for amd-pstate is to support one
+of the Zen3 processors, and we will support more in future after we verify
+the hardware and SBIOS functionalities.
+
+There are two types of hardware implementations for amd-pstate: one is full
+MSR support and another is shared memory support. It can use
+X86_FEATURE_AMD_CPPC_EXT feature flag to distinguish the different types.
+
+Using the new AMD P-States method + kernel governors (*schedutil*,
+*ondemand*, ...) to manage the frequency update is the most appropriate
+bridge between AMD Zen based hardware processor and Linux kernel, the
+processor is able to ajust to the most efficiency frequency according to
+the kernel scheduler loading.
+
+Performance Per Watt (PPW) Caculation:
+
+The PPW caculation is referred by below paper:
+https://software.intel.com/content/dam/develop/external/us/en/documents/performance-per-what-paper.pdf
+
+Below formula is referred from below spec to measure the PPW:
+
+(F / t) / P = F * t / (t * E) = F / E,
+
+"F" is the number of frames per second.
+"P" is power measurd in watts.
+"E" is energy measured in joules.
+
+We use the RAPL interface with "perf" tool to get the energy data of the
+package power.
+
+The data comparsions between amd-pstate and acpi-freq module are tested on
+AMD Cezanne processor:
+
+1) TBench CPU benchmark:
+
++---------------------------------------------------------------------+
+| |
+| TBench (Performance Per Watt) |
+| Higher is better |
++-------------------+------------------------+------------------------+
+| | Performance Per Watt | Performance Per Watt |
+| Kernel Module | (Schedutil) | (Ondemand) |
+| | Unit: MB / (s * J) | Unit: MB / (s * J) |
++-------------------+------------------------+------------------------+
+| | | |
+| acpi-cpufreq | 3.022 | 2.969 |
+| | | |
++-------------------+------------------------+------------------------+
+| | | |
+| amd-pstate | 3.131 | 3.284 |
+| | | |
++-------------------+------------------------+------------------------+
+
+2) Gitsource CPU benchmark:
+
++---------------------------------------------------------------------+
+| |
+| Gitsource (Performance Per Watt) |
+| Higher is better |
++-------------------+------------------------+------------------------+
+| | Performance Per Watt | Performance Per Watt |
+| Kernel Module | (Schedutil) | (Ondemand) |
+| | Unit: 1 / (s * J) | Unit: 1 / (s * J) |
++-------------------+------------------------+------------------------+
+| | | |
+| acpi-cpufreq | 3.42172E-07 | 2.74508E-07 |
+| | | |
++-------------------+------------------------+------------------------+
+| | | |
+| amd-pstate | 4.09141E-07 | 3.47610E-07 |
+| | | |
++-------------------+------------------------+------------------------+
+
+3) Speedometer 2.0 CPU benchmark:
+
++---------------------------------------------------------------------+
+| |
+| Speedometer 2.0 (Performance Per Watt) |
+| Higher is better |
++-------------------+------------------------+------------------------+
+| | Performance Per Watt | Performance Per Watt |
+| Kernel Module | (Schedutil) | (Ondemand) |
+| | Unit: 1 / (s * J) | Unit: 1 / (s * J) |
++-------------------+------------------------+------------------------+
+| | | |
+| acpi-cpufreq | 0.116111767 | 0.110321664 |
+| | | |
++-------------------+------------------------+------------------------+
+| | | |
+| amd-pstate | 0.115825281 | 0.122024299 |
+| | | |
++-------------------+------------------------+------------------------+
+
+According to above average data, we can see this solution has shown better
+performance per watt scaling on mobile CPU benchmarks in most of cases.
+
+Signed-off-by: Huang Rui <ray.huang@amd.com>
+---
+ drivers/cpufreq/Kconfig.x86 | 13 +
+ drivers/cpufreq/Makefile | 1 +
+ drivers/cpufreq/amd-pstate.c | 478 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 492 insertions(+)
+ create mode 100644 drivers/cpufreq/amd-pstate.c
+
+diff --git a/drivers/cpufreq/Kconfig.x86 b/drivers/cpufreq/Kconfig.x86
+index 92701a18bdd9..9cd7e338bdcd 100644
+--- a/drivers/cpufreq/Kconfig.x86
++++ b/drivers/cpufreq/Kconfig.x86
+@@ -34,6 +34,19 @@ config X86_PCC_CPUFREQ
+
+ If in doubt, say N.
+
++config X86_AMD_PSTATE
++ tristate "AMD Processor P-State driver"
++ depends on X86
++ select ACPI_PROCESSOR if ACPI
++ select ACPI_CPPC_LIB if X86_64 && ACPI && SCHED_MC_PRIO
++ select CPU_FREQ_GOV_SCHEDUTIL if SMP
++ help
++ This driver adds a CPUFreq driver which utilizes a fine grain
++ processor performance freqency control range instead of legacy
++ performance levels. This driver also supports newer AMD CPUs.
++
++ If in doubt, say N.
++
+ config X86_ACPI_CPUFREQ
+ tristate "ACPI Processor P-States driver"
+ depends on ACPI_PROCESSOR
+diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
+index 27d3bd7ea9d4..3d4bd7141cf8 100644
+--- a/drivers/cpufreq/Makefile
++++ b/drivers/cpufreq/Makefile
+@@ -24,6 +24,7 @@ obj-$(CONFIG_CPUFREQ_DT_PLATDEV) += cpufreq-dt-platdev.o
+ # powernow-k8 can load then. ACPI is preferred to all other hardware-specific drivers.
+ # speedstep-* is preferred over p4-clockmod.
+
++obj-$(CONFIG_X86_AMD_PSTATE) += amd-pstate.o
+ obj-$(CONFIG_X86_ACPI_CPUFREQ) += acpi-cpufreq.o
+ obj-$(CONFIG_X86_POWERNOW_K8) += powernow-k8.o
+ obj-$(CONFIG_X86_PCC_CPUFREQ) += pcc-cpufreq.o
+diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
+new file mode 100644
+index 000000000000..4c9c9bf1d72b
+--- /dev/null
++++ b/drivers/cpufreq/amd-pstate.c
+@@ -0,0 +1,478 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * amd-pstate.c - AMD Processor P-state Frequency Driver
++ *
++ * Copyright (C) 2021 Advanced Micro Devices, Inc. All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License along with
++ * this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ *
++ * Author: Huang Rui <ray.huang@amd.com>
++ */
++
++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/smp.h>
++#include <linux/sched.h>
++#include <linux/cpufreq.h>
++#include <linux/compiler.h>
++#include <linux/dmi.h>
++#include <linux/slab.h>
++#include <linux/acpi.h>
++#include <linux/io.h>
++#include <linux/delay.h>
++#include <linux/uaccess.h>
++
++#include <acpi/processor.h>
++#include <acpi/cppc_acpi.h>
++
++#include <asm/msr.h>
++#include <asm/processor.h>
++#include <asm/cpufeature.h>
++#include <asm/cpu_device_id.h>
++
++#define AMD_PSTATE_TRANSITION_LATENCY 0x20000
++#define AMD_PSTATE_TRANSITION_DELAY 500
++
++static struct cpufreq_driver amd_pstate_driver;
++
++struct amd_cpudata {
++ int cpu;
++
++ struct freq_qos_request req[2];
++ struct cpufreq_policy *policy;
++
++ u64 cppc_req_cached;
++
++ u32 highest_perf;
++ u32 nominal_perf;
++ u32 lowest_nonlinear_perf;
++ u32 lowest_perf;
++
++ u32 max_freq;
++ u32 min_freq;
++ u32 nominal_freq;
++ u32 lowest_nonlinear_freq;
++};
++
++struct amd_pstate_perf_funcs {
++ int (*enable)(bool enable);
++ int (*init_perf)(struct amd_cpudata *cpudata);
++ void (*update_perf)(struct amd_cpudata *cpudata,
++ u32 min_perf, u32 des_perf,
++ u32 max_perf, bool fast_switch);
++};
++
++static inline int pstate_enable(bool enable)
++{
++ return wrmsrl_safe(MSR_AMD_CPPC_ENABLE, enable ? 1 : 0);
++}
++
++static int
++amd_pstate_enable(struct amd_pstate_perf_funcs *funcs, bool enable)
++{
++ if (!funcs)
++ return -EINVAL;
++
++ return funcs->enable(enable);
++}
++
++static int pstate_init_perf(struct amd_cpudata *cpudata)
++{
++ u64 cap1;
++
++ int ret = rdmsrl_safe_on_cpu(cpudata->cpu, MSR_AMD_CPPC_CAP1,
++ &cap1);
++ if (ret)
++ return ret;
++
++ /* Some AMD processors has specific power features that the cppc entry
++ * doesn't indicate the highest performance. It will introduce the
++ * feature in following days.
++ */
++ WRITE_ONCE(cpudata->highest_perf, amd_get_highest_perf());
++
++ WRITE_ONCE(cpudata->nominal_perf, CAP1_NOMINAL_PERF(cap1));
++ WRITE_ONCE(cpudata->lowest_nonlinear_perf, CAP1_LOWNONLIN_PERF(cap1));
++ WRITE_ONCE(cpudata->lowest_perf, CAP1_LOWEST_PERF(cap1));
++
++ return 0;
++}
++
++static int amd_pstate_init_perf(struct amd_cpudata *cpudata)
++{
++ struct amd_pstate_perf_funcs *funcs = cpufreq_get_driver_data();
++
++ if (!funcs)
++ return -EINVAL;
++
++ return funcs->init_perf(cpudata);
++}
++
++static void pstate_update_perf(struct amd_cpudata *cpudata,
++ u32 min_perf, u32 des_perf, u32 max_perf,
++ bool fast_switch)
++{
++ if (fast_switch)
++ wrmsrl(MSR_AMD_CPPC_REQ, READ_ONCE(cpudata->cppc_req_cached));
++ else
++ wrmsrl_on_cpu(cpudata->cpu, MSR_AMD_CPPC_REQ,
++ READ_ONCE(cpudata->cppc_req_cached));
++}
++
++static int
++amd_pstate_update_perf(struct amd_cpudata *cpudata, u32 min_perf,
++ u32 des_perf, u32 max_perf, bool fast_switch)
++{
++ struct amd_pstate_perf_funcs *funcs = cpufreq_get_driver_data();
++
++ if (!funcs)
++ return -EINVAL;
++
++ funcs->update_perf(cpudata, min_perf, des_perf,
++ max_perf, fast_switch);
++
++ return 0;
++}
++
++static int
++amd_pstate_update(struct amd_cpudata *cpudata, u32 min_perf,
++ u32 des_perf, u32 max_perf, bool fast_switch)
++{
++ u64 prev = READ_ONCE(cpudata->cppc_req_cached);
++ u64 value = prev;
++
++ value &= ~REQ_MIN_PERF(~0L);
++ value |= REQ_MIN_PERF(min_perf);
++
++ value &= ~REQ_DES_PERF(~0L);
++ value |= REQ_DES_PERF(des_perf);
++
++ value &= ~REQ_MAX_PERF(~0L);
++ value |= REQ_MAX_PERF(max_perf);
++
++ if (value == prev)
++ return 0;
++
++ WRITE_ONCE(cpudata->cppc_req_cached, value);
++
++ return amd_pstate_update_perf(cpudata, min_perf, des_perf,
++ max_perf, fast_switch);
++}
++
++static int amd_pstate_verify(struct cpufreq_policy_data *policy)
++{
++ cpufreq_verify_within_cpu_limits(policy);
++
++ return 0;
++}
++
++static int amd_pstate_target(struct cpufreq_policy *policy,
++ unsigned int target_freq,
++ unsigned int relation)
++{
++ int ret;
++ struct cpufreq_freqs freqs;
++ struct amd_cpudata *cpudata = policy->driver_data;
++ unsigned long amd_max_perf, amd_min_perf, amd_des_perf,
++ amd_cap_perf;
++
++ if (!cpudata->max_freq)
++ return -ENODEV;
++
++ amd_cap_perf = READ_ONCE(cpudata->highest_perf);
++ amd_min_perf = READ_ONCE(cpudata->lowest_nonlinear_perf);
++ amd_max_perf = amd_cap_perf;
++
++ freqs.old = policy->cur;
++ freqs.new = target_freq;
++
++ amd_des_perf = DIV_ROUND_CLOSEST(target_freq * amd_cap_perf,
++ cpudata->max_freq);
++
++ cpufreq_freq_transition_begin(policy, &freqs);
++ ret = amd_pstate_update(cpudata, amd_min_perf, amd_des_perf,
++ amd_max_perf, false);
++ cpufreq_freq_transition_end(policy, &freqs, false);
++
++ return ret;
++}
++
++static int amd_get_min_freq(struct amd_cpudata *cpudata)
++{
++ struct cppc_perf_caps cppc_perf;
++
++ int ret = cppc_get_perf_caps(cpudata->cpu, &cppc_perf);
++ if (ret)
++ return ret;
++
++ /* Switch to khz */
++ return cppc_perf.lowest_freq * 1000;
++}
++
++static int amd_get_max_freq(struct amd_cpudata *cpudata)
++{
++ struct cppc_perf_caps cppc_perf;
++ u32 max_perf, max_freq, nominal_freq, nominal_perf;
++ u64 boost_ratio;
++
++ int ret = cppc_get_perf_caps(cpudata->cpu, &cppc_perf);
++ if (ret)
++ return ret;
++
++ nominal_freq = cppc_perf.nominal_freq;
++ nominal_perf = READ_ONCE(cpudata->nominal_perf);
++ max_perf = READ_ONCE(cpudata->highest_perf);
++
++ boost_ratio = div_u64(max_perf << SCHED_CAPACITY_SHIFT,
++ nominal_perf);
++
++ max_freq = nominal_freq * boost_ratio >> SCHED_CAPACITY_SHIFT;
++
++ /* Switch to khz */
++ return max_freq * 1000;
++}
++
++static int amd_get_nominal_freq(struct amd_cpudata *cpudata)
++{
++ struct cppc_perf_caps cppc_perf;
++ u32 nominal_freq;
++
++ int ret = cppc_get_perf_caps(cpudata->cpu, &cppc_perf);
++ if (ret)
++ return ret;
++
++ nominal_freq = cppc_perf.nominal_freq;
++
++ /* Switch to khz */
++ return nominal_freq * 1000;
++}
++
++static int amd_get_lowest_nonlinear_freq(struct amd_cpudata *cpudata)
++{
++ struct cppc_perf_caps cppc_perf;
++ u32 lowest_nonlinear_freq, lowest_nonlinear_perf,
++ nominal_freq, nominal_perf;
++ u64 lowest_nonlinear_ratio;
++
++ int ret = cppc_get_perf_caps(cpudata->cpu, &cppc_perf);
++ if (ret)
++ return ret;
++
++ nominal_freq = cppc_perf.nominal_freq;
++ nominal_perf = READ_ONCE(cpudata->nominal_perf);
++
++ lowest_nonlinear_perf = cppc_perf.lowest_nonlinear_perf;
++
++ lowest_nonlinear_ratio = div_u64(lowest_nonlinear_perf <<
++ SCHED_CAPACITY_SHIFT, nominal_perf);
++
++ lowest_nonlinear_freq = nominal_freq * lowest_nonlinear_ratio >> SCHED_CAPACITY_SHIFT;
++
++ /* Switch to khz */
++ return lowest_nonlinear_freq * 1000;
++}
++
++static int amd_pstate_init_freqs_in_cpudata(struct amd_cpudata *cpudata,
++ u32 max_freq, u32 min_freq,
++ u32 nominal_freq,
++ u32 lowest_nonlinear_freq)
++{
++ if (!cpudata)
++ return -EINVAL;
++
++ /* Initial processor data capability frequencies */
++ cpudata->max_freq = max_freq;
++ cpudata->min_freq = min_freq;
++ cpudata->nominal_freq = nominal_freq;
++ cpudata->lowest_nonlinear_freq = lowest_nonlinear_freq;
++
++ return 0;
++}
++
++static struct amd_pstate_perf_funcs pstate_funcs = {
++ .enable = pstate_enable,
++ .init_perf = pstate_init_perf,
++ .update_perf = pstate_update_perf,
++};
++
++static int amd_pstate_cpu_init(struct cpufreq_policy *policy)
++{
++ int min_freq, max_freq, nominal_freq, lowest_nonlinear_freq, ret;
++ unsigned int cpu = policy->cpu;
++ struct device *dev;
++ struct amd_cpudata *cpudata;
++
++ dev = get_cpu_device(policy->cpu);
++ if (!dev)
++ return -ENODEV;
++
++ cpudata = kzalloc(sizeof(*cpudata), GFP_KERNEL);
++ if (!cpudata)
++ return -ENOMEM;
++
++ cpudata->cpu = cpu;
++ cpudata->policy = policy;
++
++ ret = amd_pstate_init_perf(cpudata);
++ if (ret)
++ goto free_cpudata1;
++
++ min_freq = amd_get_min_freq(cpudata);
++ max_freq = amd_get_max_freq(cpudata);
++ nominal_freq = amd_get_nominal_freq(cpudata);
++ lowest_nonlinear_freq = amd_get_lowest_nonlinear_freq(cpudata);
++
++ if (min_freq < 0 || max_freq < 0 || min_freq > max_freq) {
++ dev_err(dev, "min_freq(%d) or max_freq(%d) value is incorrect\n",
++ min_freq, max_freq);
++ ret = -EINVAL;
++ goto free_cpudata1;
++ }
++
++ policy->cpuinfo.transition_latency = AMD_PSTATE_TRANSITION_LATENCY;
++ policy->transition_delay_us = AMD_PSTATE_TRANSITION_DELAY;
++
++ policy->min = min_freq;
++ policy->max = max_freq;
++
++ policy->cpuinfo.min_freq = min_freq;
++ policy->cpuinfo.max_freq = max_freq;
++
++ /* It will be updated by governor */
++ policy->cur = policy->cpuinfo.min_freq;
++
++ ret = freq_qos_add_request(&policy->constraints, &cpudata->req[0],
++ FREQ_QOS_MIN, policy->cpuinfo.min_freq);
++ if (ret < 0) {
++ dev_err(dev, "Failed to add min-freq constraint (%d)\n", ret);
++ goto free_cpudata1;
++ }
++
++ ret = freq_qos_add_request(&policy->constraints, &cpudata->req[1],
++ FREQ_QOS_MAX, policy->cpuinfo.max_freq);
++ if (ret < 0) {
++ dev_err(dev, "Failed to add max-freq constraint (%d)\n", ret);
++ goto free_cpudata2;
++ }
++
++ ret = amd_pstate_init_freqs_in_cpudata(cpudata, max_freq, min_freq,
++ nominal_freq,
++ lowest_nonlinear_freq);
++ if (ret) {
++ dev_err(dev, "Failed to init cpudata (%d)\n", ret);
++ goto free_cpudata3;
++ }
++
++ policy->driver_data = cpudata;
++
++ return 0;
++
++free_cpudata3:
++ freq_qos_remove_request(&cpudata->req[1]);
++free_cpudata2:
++ freq_qos_remove_request(&cpudata->req[0]);
++free_cpudata1:
++ kfree(cpudata);
++ return ret;
++}
++
++static int amd_pstate_cpu_exit(struct cpufreq_policy *policy)
++{
++ struct amd_cpudata *cpudata;
++
++ cpudata = policy->driver_data;
++
++ freq_qos_remove_request(&cpudata->req[1]);
++ freq_qos_remove_request(&cpudata->req[0]);
++ kfree(cpudata);
++
++ return 0;
++}
++
++static struct cpufreq_driver amd_pstate_driver = {
++ .flags = CPUFREQ_CONST_LOOPS | CPUFREQ_NEED_UPDATE_LIMITS,
++ .verify = amd_pstate_verify,
++ .target = amd_pstate_target,
++ .init = amd_pstate_cpu_init,
++ .exit = amd_pstate_cpu_exit,
++ .name = "amd-pstate",
++};
++
++static int __init amd_pstate_init(void)
++{
++ int ret;
++ struct amd_pstate_perf_funcs *funcs;
++
++ if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
++ return -ENODEV;
++
++ if (!acpi_cpc_valid()) {
++ pr_debug("%s, the _CPC object is not present in SBIOS\n",
++ __func__);
++ return -ENODEV;
++ }
++
++ /* don't keep reloading if cpufreq_driver exists */
++ if (cpufreq_get_current_driver())
++ return -EEXIST;
++
++ /* capability check */
++ if (!boot_cpu_has(X86_FEATURE_AMD_CPPC_EXT)) {
++ pr_debug("%s, AMD CPPC extension functionality is supported\n",
++ __func__);
++ return -ENODEV;
++ }
++
++ funcs = &pstate_funcs;
++
++ /* enable amd pstate feature */
++ ret = amd_pstate_enable(funcs, true);
++ if (ret) {
++ pr_err("%s, failed to enable amd-pstate with return %d\n",
++ __func__, ret);
++ return ret;
++ }
++
++ amd_pstate_driver.driver_data = funcs;
++
++ ret = cpufreq_register_driver(&amd_pstate_driver);
++ if (ret) {
++ pr_err("%s, return %d\n", __func__, ret);
++ return ret;
++ }
++
++ return 0;
++}
++
++static void __exit amd_pstate_exit(void)
++{
++ struct amd_pstate_perf_funcs *funcs;
++
++ funcs = cpufreq_get_driver_data();
++
++ cpufreq_unregister_driver(&amd_pstate_driver);
++
++ amd_pstate_enable(funcs, false);
++}
++
++module_init(amd_pstate_init);
++module_exit(amd_pstate_exit);
++
++MODULE_AUTHOR("Huang Rui <ray.huang@amd.com>");
++MODULE_DESCRIPTION("AMD Processor P-state Frequency Driver");
++MODULE_LICENSE("GPL");
+--
+2.33.0
+
+From 54beca4738acc38c08710cfcb1c3312755000cf6 Mon Sep 17 00:00:00 2001
+From: Huang Rui <ray.huang@amd.com>
+Date: Fri, 13 Aug 2021 18:43:47 +0800
+Subject: [PATCH 05/19] cpufreq: amd: add fast switch function for amd-pstate
+ module
+
+Introduce the fast switch function for amd-pstate module on the AMD
+processors which support the full MSR register control. It's able to
+decrease the lattency on interrupt context.
+
+Signed-off-by: Huang Rui <ray.huang@amd.com>
+---
+ drivers/cpufreq/amd-pstate.c | 64 ++++++++++++++++++++++++++++++++++++
+ 1 file changed, 64 insertions(+)
+
+diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
+index 4c9c9bf1d72b..32b4f6d79783 100644
+--- a/drivers/cpufreq/amd-pstate.c
++++ b/drivers/cpufreq/amd-pstate.c
+@@ -212,6 +212,66 @@ static int amd_pstate_target(struct cpufreq_policy *policy,
+ return ret;
+ }
+
++static void amd_pstate_adjust_perf(unsigned int cpu,
++ unsigned long min_perf,
++ unsigned long target_perf,
++ unsigned long capacity)
++{
++ unsigned long amd_max_perf, amd_min_perf, amd_des_perf,
++ amd_cap_perf, lowest_nonlinear_perf;
++ struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
++ struct amd_cpudata *cpudata = policy->driver_data;
++
++ amd_cap_perf = READ_ONCE(cpudata->highest_perf);
++ lowest_nonlinear_perf = READ_ONCE(cpudata->lowest_nonlinear_perf);
++
++ if (target_perf < capacity)
++ amd_des_perf = DIV_ROUND_UP(amd_cap_perf * target_perf,
++ capacity);
++
++ amd_min_perf = READ_ONCE(cpudata->highest_perf);
++ if (min_perf < capacity)
++ amd_min_perf = DIV_ROUND_UP(amd_cap_perf * min_perf, capacity);
++
++ if (amd_min_perf < lowest_nonlinear_perf)
++ amd_min_perf = lowest_nonlinear_perf;
++
++ amd_max_perf = amd_cap_perf;
++ if (amd_max_perf < amd_min_perf)
++ amd_max_perf = amd_min_perf;
++
++ amd_des_perf = clamp_t(unsigned long, amd_des_perf,
++ amd_min_perf, amd_max_perf);
++
++ amd_pstate_update(cpudata, amd_min_perf, amd_des_perf,
++ amd_max_perf, true);
++}
++
++static unsigned int amd_pstate_fast_switch(struct cpufreq_policy *policy,
++ unsigned int target_freq)
++{
++ u64 ratio;
++ struct amd_cpudata *cpudata = policy->driver_data;
++ unsigned long amd_max_perf, amd_min_perf, amd_des_perf, nominal_perf;
++
++ if (!cpudata->max_freq)
++ return -ENODEV;
++
++ amd_max_perf = READ_ONCE(cpudata->highest_perf);
++ amd_min_perf = READ_ONCE(cpudata->lowest_nonlinear_perf);
++
++ amd_des_perf = DIV_ROUND_UP(target_freq * amd_max_perf,
++ cpudata->max_freq);
++
++ amd_pstate_update(cpudata, amd_min_perf, amd_des_perf,
++ amd_max_perf, true);
++
++ nominal_perf = READ_ONCE(cpudata->nominal_perf);
++ ratio = div_u64(amd_des_perf << SCHED_CAPACITY_SHIFT, nominal_perf);
++
++ return cpudata->nominal_freq * ratio >> SCHED_CAPACITY_SHIFT;
++}
++
+ static int amd_get_min_freq(struct amd_cpudata *cpudata)
+ {
+ struct cppc_perf_caps cppc_perf;
+@@ -356,6 +416,8 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy)
+ /* It will be updated by governor */
+ policy->cur = policy->cpuinfo.min_freq;
+
++ policy->fast_switch_possible = true;
++
+ ret = freq_qos_add_request(&policy->constraints, &cpudata->req[0],
+ FREQ_QOS_MIN, policy->cpuinfo.min_freq);
+ if (ret < 0) {
+@@ -408,6 +470,8 @@ static struct cpufreq_driver amd_pstate_driver = {
+ .flags = CPUFREQ_CONST_LOOPS | CPUFREQ_NEED_UPDATE_LIMITS,
+ .verify = amd_pstate_verify,
+ .target = amd_pstate_target,
++ .fast_switch = amd_pstate_fast_switch,
++ .adjust_perf = amd_pstate_adjust_perf,
+ .init = amd_pstate_cpu_init,
+ .exit = amd_pstate_cpu_exit,
+ .name = "amd-pstate",
+--
+2.33.0
+
+From ad7cc7a238ee889b9001d95fef83172763b95939 Mon Sep 17 00:00:00 2001
+From: Huang Rui <ray.huang@amd.com>
+Date: Mon, 9 Aug 2021 19:06:51 +0800
+Subject: [PATCH 06/19] cpufreq: amd: add acpi cppc function as the backend for
+ legacy processors
+
+In some old Zen based processors, they are using the shared memory that
+exposed from ACPI SBIOS.
+
+Signed-off-by: Jinzhou Su <Jinzhou.Su@amd.com>
+Signed-off-by: Huang Rui <ray.huang@amd.com>
+---
+ drivers/cpufreq/amd-pstate.c | 63 ++++++++++++++++++++++++++++++++----
+ 1 file changed, 57 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
+index 32b4f6d79783..a46cd5dd9f7c 100644
+--- a/drivers/cpufreq/amd-pstate.c
++++ b/drivers/cpufreq/amd-pstate.c
+@@ -82,6 +82,19 @@ static inline int pstate_enable(bool enable)
+ return wrmsrl_safe(MSR_AMD_CPPC_ENABLE, enable ? 1 : 0);
+ }
+
++static int cppc_enable(bool enable)
++{
++ int cpu, ret = 0;
++
++ for_each_online_cpu(cpu) {
++ ret = cppc_set_enable(cpu, enable ? 1 : 0);
++ if (ret)
++ return ret;
++ }
++
++ return ret;
++}
++
+ static int
+ amd_pstate_enable(struct amd_pstate_perf_funcs *funcs, bool enable)
+ {
+@@ -113,6 +126,24 @@ static int pstate_init_perf(struct amd_cpudata *cpudata)
+ return 0;
+ }
+
++static int cppc_init_perf(struct amd_cpudata *cpudata)
++{
++ struct cppc_perf_caps cppc_perf;
++
++ int ret = cppc_get_perf_caps(cpudata->cpu, &cppc_perf);
++ if (ret)
++ return ret;
++
++ WRITE_ONCE(cpudata->highest_perf, amd_get_highest_perf());
++
++ WRITE_ONCE(cpudata->nominal_perf, cppc_perf.nominal_perf);
++ WRITE_ONCE(cpudata->lowest_nonlinear_perf,
++ cppc_perf.lowest_nonlinear_perf);
++ WRITE_ONCE(cpudata->lowest_perf, cppc_perf.lowest_perf);
++
++ return 0;
++}
++
+ static int amd_pstate_init_perf(struct amd_cpudata *cpudata)
+ {
+ struct amd_pstate_perf_funcs *funcs = cpufreq_get_driver_data();
+@@ -134,6 +165,19 @@ static void pstate_update_perf(struct amd_cpudata *cpudata,
+ READ_ONCE(cpudata->cppc_req_cached));
+ }
+
++static void cppc_update_perf(struct amd_cpudata *cpudata,
++ u32 min_perf, u32 des_perf,
++ u32 max_perf, bool fast_switch)
++{
++ struct cppc_perf_ctrls perf_ctrls;
++
++ perf_ctrls.max_perf = max_perf;
++ perf_ctrls.min_perf = min_perf;
++ perf_ctrls.desired_perf = des_perf;
++
++ cppc_set_perf(cpudata->cpu, &perf_ctrls);
++}
++
+ static int
+ amd_pstate_update_perf(struct amd_cpudata *cpudata, u32 min_perf,
+ u32 des_perf, u32 max_perf, bool fast_switch)
+@@ -370,6 +414,12 @@ static struct amd_pstate_perf_funcs pstate_funcs = {
+ .update_perf = pstate_update_perf,
+ };
+
++static struct amd_pstate_perf_funcs cppc_funcs = {
++ .enable = cppc_enable,
++ .init_perf = cppc_init_perf,
++ .update_perf = cppc_update_perf,
++};
++
+ static int amd_pstate_cpu_init(struct cpufreq_policy *policy)
+ {
+ int min_freq, max_freq, nominal_freq, lowest_nonlinear_freq, ret;
+@@ -416,7 +466,8 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy)
+ /* It will be updated by governor */
+ policy->cur = policy->cpuinfo.min_freq;
+
+- policy->fast_switch_possible = true;
++ if (boot_cpu_has(X86_FEATURE_AMD_CPPC_EXT))
++ policy->fast_switch_possible = true;
+
+ ret = freq_qos_add_request(&policy->constraints, &cpudata->req[0],
+ FREQ_QOS_MIN, policy->cpuinfo.min_freq);
+@@ -471,7 +522,6 @@ static struct cpufreq_driver amd_pstate_driver = {
+ .verify = amd_pstate_verify,
+ .target = amd_pstate_target,
+ .fast_switch = amd_pstate_fast_switch,
+- .adjust_perf = amd_pstate_adjust_perf,
+ .init = amd_pstate_cpu_init,
+ .exit = amd_pstate_cpu_exit,
+ .name = "amd-pstate",
+@@ -496,14 +546,15 @@ static int __init amd_pstate_init(void)
+ return -EEXIST;
+
+ /* capability check */
+- if (!boot_cpu_has(X86_FEATURE_AMD_CPPC_EXT)) {
++ if (boot_cpu_has(X86_FEATURE_AMD_CPPC_EXT)) {
+ pr_debug("%s, AMD CPPC extension functionality is supported\n",
+ __func__);
+- return -ENODEV;
++ funcs = &pstate_funcs;
++ amd_pstate_driver.adjust_perf = amd_pstate_adjust_perf;
++ } else {
++ funcs = &cppc_funcs;
+ }
+
+- funcs = &pstate_funcs;
+-
+ /* enable amd pstate feature */
+ ret = amd_pstate_enable(funcs, true);
+ if (ret) {
+--
+2.33.0
+
+From e007c18166d11d78757454ebfac967ae460ebf08 Mon Sep 17 00:00:00 2001
+From: Huang Rui <ray.huang@amd.com>
+Date: Thu, 10 Jun 2021 20:24:00 +0800
+Subject: [PATCH 07/19] cpufreq: amd: add trace for amd-pstate module
+
+Add trace event to monitor the performance value changes which is
+controlled by cpu governors.
+
+Signed-off-by: Huang Rui <ray.huang@amd.com>
+---
+ drivers/cpufreq/Makefile | 6 +-
+ drivers/cpufreq/amd-pstate-trace.c | 2 +
+ drivers/cpufreq/amd-pstate-trace.h | 96 ++++++++++++++++++++++++++++++
+ drivers/cpufreq/amd-pstate.c | 19 ++++--
+ 4 files changed, 118 insertions(+), 5 deletions(-)
+ create mode 100644 drivers/cpufreq/amd-pstate-trace.c
+ create mode 100644 drivers/cpufreq/amd-pstate-trace.h
+
+diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
+index 3d4bd7141cf8..c1909475eaf9 100644
+--- a/drivers/cpufreq/Makefile
++++ b/drivers/cpufreq/Makefile
+@@ -17,6 +17,10 @@ obj-$(CONFIG_CPU_FREQ_GOV_ATTR_SET) += cpufreq_governor_attr_set.o
+ obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o
+ obj-$(CONFIG_CPUFREQ_DT_PLATDEV) += cpufreq-dt-platdev.o
+
++# Traces
++CFLAGS_amd-pstate-trace.o := -I$(src)
++amd_pstate-y := amd-pstate.o amd-pstate-trace.o
++
+ ##################################################################################
+ # x86 drivers.
+ # Link order matters. K8 is preferred to ACPI because of firmware bugs in early
+@@ -24,7 +28,7 @@ obj-$(CONFIG_CPUFREQ_DT_PLATDEV) += cpufreq-dt-platdev.o
+ # powernow-k8 can load then. ACPI is preferred to all other hardware-specific drivers.
+ # speedstep-* is preferred over p4-clockmod.
+
+-obj-$(CONFIG_X86_AMD_PSTATE) += amd-pstate.o
++obj-$(CONFIG_X86_AMD_PSTATE) += amd_pstate.o
+ obj-$(CONFIG_X86_ACPI_CPUFREQ) += acpi-cpufreq.o
+ obj-$(CONFIG_X86_POWERNOW_K8) += powernow-k8.o
+ obj-$(CONFIG_X86_PCC_CPUFREQ) += pcc-cpufreq.o
+diff --git a/drivers/cpufreq/amd-pstate-trace.c b/drivers/cpufreq/amd-pstate-trace.c
+new file mode 100644
+index 000000000000..891b696dcd69
+--- /dev/null
++++ b/drivers/cpufreq/amd-pstate-trace.c
+@@ -0,0 +1,2 @@
++#define CREATE_TRACE_POINTS
++#include "amd-pstate-trace.h"
+diff --git a/drivers/cpufreq/amd-pstate-trace.h b/drivers/cpufreq/amd-pstate-trace.h
+new file mode 100644
+index 000000000000..50c85e150f30
+--- /dev/null
++++ b/drivers/cpufreq/amd-pstate-trace.h
+@@ -0,0 +1,96 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * amd-pstate-trace.h - AMD Processor P-state Frequency Driver Tracer
++ *
++ * Copyright (C) 2021 Advanced Micro Devices, Inc. All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License along with
++ * this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ *
++ * Author: Huang Rui <ray.huang@amd.com>
++ */
++
++#if !defined(_AMD_PSTATE_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
++#define _AMD_PSTATE_TRACE_H
++
++#include <linux/cpufreq.h>
++#include <linux/tracepoint.h>
++#include <linux/trace_events.h>
++
++#undef TRACE_SYSTEM
++#define TRACE_SYSTEM amd_cpu
++
++#undef TRACE_INCLUDE_FILE
++#define TRACE_INCLUDE_FILE amd-pstate-trace
++
++#define TPS(x) tracepoint_string(x)
++
++TRACE_EVENT(amd_pstate_perf,
++
++ TP_PROTO(unsigned long min_perf,
++ unsigned long target_perf,
++ unsigned long capacity,
++ unsigned int cpu_id,
++ u64 prev,
++ u64 value,
++ int type
++ ),
++
++ TP_ARGS(min_perf,
++ target_perf,
++ capacity,
++ cpu_id,
++ prev,
++ value,
++ type
++ ),
++
++ TP_STRUCT__entry(
++ __field(unsigned long, min_perf)
++ __field(unsigned long, target_perf)
++ __field(unsigned long, capacity)
++ __field(unsigned int, cpu_id)
++ __field(u64, prev)
++ __field(u64, value)
++ __field(int, type)
++ ),
++
++ TP_fast_assign(
++ __entry->min_perf = min_perf;
++ __entry->target_perf = target_perf;
++ __entry->capacity = capacity;
++ __entry->cpu_id = cpu_id;
++ __entry->prev = prev;
++ __entry->value = value;
++ __entry->type = type;
++ ),
++
++ TP_printk("amd_min_perf=%lu amd_des_perf=%lu amd_max_perf=%lu cpu_id=%u prev=0x%llx value=0x%llx type=0x%d",
++ (unsigned long)__entry->min_perf,
++ (unsigned long)__entry->target_perf,
++ (unsigned long)__entry->capacity,
++ (unsigned int)__entry->cpu_id,
++ (u64)__entry->prev,
++ (u64)__entry->value,
++ (int)__entry->type
++ )
++);
++
++#endif /* _AMD_PSTATE_TRACE_H */
++
++/* This part must be outside protection */
++#undef TRACE_INCLUDE_PATH
++#define TRACE_INCLUDE_PATH .
++
++#include <trace/define_trace.h>
+diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
+index a46cd5dd9f7c..ea965a122431 100644
+--- a/drivers/cpufreq/amd-pstate.c
++++ b/drivers/cpufreq/amd-pstate.c
+@@ -44,10 +44,18 @@
+ #include <asm/processor.h>
+ #include <asm/cpufeature.h>
+ #include <asm/cpu_device_id.h>
++#include "amd-pstate-trace.h"
+
+ #define AMD_PSTATE_TRANSITION_LATENCY 0x20000
+ #define AMD_PSTATE_TRANSITION_DELAY 500
+
++enum switch_type
++{
++ AMD_TARGET = 0,
++ AMD_ADJUST_PERF,
++ AMD_FAST_SWITCH,
++};
++
+ static struct cpufreq_driver amd_pstate_driver;
+
+ struct amd_cpudata {
+@@ -195,7 +203,8 @@ amd_pstate_update_perf(struct amd_cpudata *cpudata, u32 min_perf,
+
+ static int
+ amd_pstate_update(struct amd_cpudata *cpudata, u32 min_perf,
+- u32 des_perf, u32 max_perf, bool fast_switch)
++ u32 des_perf, u32 max_perf, bool fast_switch,
++ enum switch_type type)
+ {
+ u64 prev = READ_ONCE(cpudata->cppc_req_cached);
+ u64 value = prev;
+@@ -209,6 +218,8 @@ amd_pstate_update(struct amd_cpudata *cpudata, u32 min_perf,
+ value &= ~REQ_MAX_PERF(~0L);
+ value |= REQ_MAX_PERF(max_perf);
+
++ trace_amd_pstate_perf(min_perf, des_perf, max_perf,
++ cpudata->cpu, prev, value, type);
+ if (value == prev)
+ return 0;
+
+@@ -250,7 +261,7 @@ static int amd_pstate_target(struct cpufreq_policy *policy,
+
+ cpufreq_freq_transition_begin(policy, &freqs);
+ ret = amd_pstate_update(cpudata, amd_min_perf, amd_des_perf,
+- amd_max_perf, false);
++ amd_max_perf, false, AMD_TARGET);
+ cpufreq_freq_transition_end(policy, &freqs, false);
+
+ return ret;
+@@ -288,7 +299,7 @@ static void amd_pstate_adjust_perf(unsigned int cpu,
+ amd_min_perf, amd_max_perf);
+
+ amd_pstate_update(cpudata, amd_min_perf, amd_des_perf,
+- amd_max_perf, true);
++ amd_max_perf, true, AMD_ADJUST_PERF);
+ }
+
+ static unsigned int amd_pstate_fast_switch(struct cpufreq_policy *policy,
+@@ -308,7 +319,7 @@ static unsigned int amd_pstate_fast_switch(struct cpufreq_policy *policy,
+ cpudata->max_freq);
+
+ amd_pstate_update(cpudata, amd_min_perf, amd_des_perf,
+- amd_max_perf, true);
++ amd_max_perf, true, AMD_FAST_SWITCH);
+
+ nominal_perf = READ_ONCE(cpudata->nominal_perf);
+ ratio = div_u64(amd_des_perf << SCHED_CAPACITY_SHIFT, nominal_perf);
+--
+2.33.0
+
+From 24ab2966561da669b9879a6167e2cc7e5929735c Mon Sep 17 00:00:00 2001
+From: Huang Rui <ray.huang@amd.com>
+Date: Thu, 10 Jun 2021 23:13:00 +0800
+Subject: [PATCH 08/19] cpufreq: amd: add boost mode support for amd-pstate
+
+If the sbios supports the boost mode of amd-pstate, let's switch to
+boost enabled by default.
+
+Signed-off-by: Huang Rui <ray.huang@amd.com>
+---
+ drivers/cpufreq/amd-pstate.c | 50 ++++++++++++++++++++++++++++++++++++
+ 1 file changed, 50 insertions(+)
+
+diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
+index ea965a122431..67a9a117f524 100644
+--- a/drivers/cpufreq/amd-pstate.c
++++ b/drivers/cpufreq/amd-pstate.c
+@@ -75,6 +75,8 @@ struct amd_cpudata {
+ u32 min_freq;
+ u32 nominal_freq;
+ u32 lowest_nonlinear_freq;
++
++ bool boost_supported;
+ };
+
+ struct amd_pstate_perf_funcs {
+@@ -229,6 +231,19 @@ amd_pstate_update(struct amd_cpudata *cpudata, u32 min_perf,
+ max_perf, fast_switch);
+ }
+
++static bool amd_pstate_boost_supported(struct amd_cpudata *cpudata)
++{
++ u32 highest_perf, nominal_perf;
++
++ highest_perf = READ_ONCE(cpudata->highest_perf);
++ nominal_perf = READ_ONCE(cpudata->nominal_perf);
++
++ if (highest_perf > nominal_perf)
++ return true;
++
++ return false;
++}
++
+ static int amd_pstate_verify(struct cpufreq_policy_data *policy)
+ {
+ cpufreq_verify_within_cpu_limits(policy);
+@@ -402,6 +417,37 @@ static int amd_get_lowest_nonlinear_freq(struct amd_cpudata *cpudata)
+ return lowest_nonlinear_freq * 1000;
+ }
+
++static int amd_pstate_set_boost(struct cpufreq_policy *policy, int state)
++{
++ struct amd_cpudata *cpudata = policy->driver_data;
++ int ret;
++
++ if (!cpudata->boost_supported) {
++ pr_err("Boost mode is not supported by this processor or SBIOS\n");
++ return -EINVAL;
++ }
++
++ if (state)
++ policy->cpuinfo.max_freq = cpudata->max_freq;
++ else
++ policy->cpuinfo.max_freq = cpudata->nominal_freq;
++
++ policy->max = policy->cpuinfo.max_freq;
++
++ ret = freq_qos_update_request(&cpudata->req[1],
++ policy->cpuinfo.max_freq);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static void amd_pstate_boost_init(struct amd_cpudata *cpudata)
++{
++ cpudata->boost_supported = true;
++ amd_pstate_driver.boost_enabled = true;
++}
++
+ static int amd_pstate_init_freqs_in_cpudata(struct amd_cpudata *cpudata,
+ u32 max_freq, u32 min_freq,
+ u32 nominal_freq,
+@@ -504,6 +550,9 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy)
+
+ policy->driver_data = cpudata;
+
++ if (amd_pstate_boost_supported(cpudata))
++ amd_pstate_boost_init(cpudata);
++
+ return 0;
+
+ free_cpudata3:
+@@ -535,6 +584,7 @@ static struct cpufreq_driver amd_pstate_driver = {
+ .fast_switch = amd_pstate_fast_switch,
+ .init = amd_pstate_cpu_init,
+ .exit = amd_pstate_cpu_exit,
++ .set_boost = amd_pstate_set_boost,
+ .name = "amd-pstate",
+ };
+
+--
+2.33.0
+
+From fdbec5a44fab1213e459312b542e27dc0181d7e4 Mon Sep 17 00:00:00 2001
+From: Huang Rui <ray.huang@amd.com>
+Date: Sat, 19 Jun 2021 23:41:30 +0800
+Subject: [PATCH 09/19] cpufreq: amd: add amd-pstate checking support check
+ attribute
+
+The amd-pstate hardware support check will be needed by cpupower to know
+whether amd-pstate is enabled and supported.
+
+Signed-off-by: Huang Rui <ray.huang@amd.com>
+---
+ drivers/cpufreq/amd-pstate.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
+index 67a9a117f524..48dedd5af101 100644
+--- a/drivers/cpufreq/amd-pstate.c
++++ b/drivers/cpufreq/amd-pstate.c
+@@ -577,6 +577,19 @@ static int amd_pstate_cpu_exit(struct cpufreq_policy *policy)
+ return 0;
+ }
+
++static ssize_t show_is_amd_pstate_enabled(struct cpufreq_policy *policy,
++ char *buf)
++{
++ return sprintf(&buf[0], "%d\n", acpi_cpc_valid() ? 1 : 0);
++}
++
++cpufreq_freq_attr_ro(is_amd_pstate_enabled);
++
++static struct freq_attr *amd_pstate_attr[] = {
++ &is_amd_pstate_enabled,
++ NULL,
++};
++
+ static struct cpufreq_driver amd_pstate_driver = {
+ .flags = CPUFREQ_CONST_LOOPS | CPUFREQ_NEED_UPDATE_LIMITS,
+ .verify = amd_pstate_verify,
+@@ -586,6 +599,7 @@ static struct cpufreq_driver amd_pstate_driver = {
+ .exit = amd_pstate_cpu_exit,
+ .set_boost = amd_pstate_set_boost,
+ .name = "amd-pstate",
++ .attr = amd_pstate_attr,
+ };
+
+ static int __init amd_pstate_init(void)
+--
+2.33.0
+
+From 4043d05067a68c58255a6759c528bb78db656327 Mon Sep 17 00:00:00 2001
+From: Huang Rui <ray.huang@amd.com>
+Date: Sun, 20 Jun 2021 13:26:01 +0800
+Subject: [PATCH 10/19] cpufreq: amd: add amd-pstate frequencies attributes
+
+Introduce sysfs attributes to get the different level processor
+frequencies.
+
+Signed-off-by: Huang Rui <ray.huang@amd.com>
+---
+ drivers/cpufreq/amd-pstate.c | 80 +++++++++++++++++++++++++++++++++++-
+ 1 file changed, 79 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
+index 48dedd5af101..3c727a22cb69 100644
+--- a/drivers/cpufreq/amd-pstate.c
++++ b/drivers/cpufreq/amd-pstate.c
+@@ -577,16 +577,94 @@ static int amd_pstate_cpu_exit(struct cpufreq_policy *policy)
+ return 0;
+ }
+
+-static ssize_t show_is_amd_pstate_enabled(struct cpufreq_policy *policy,
++/* Sysfs attributes */
++
++static ssize_t show_amd_pstate_max_freq(struct cpufreq_policy *policy,
+ char *buf)
++{
++ int ret = 0, max_freq;
++ struct amd_cpudata *cpudata;
++
++ cpudata = policy->driver_data;
++
++ max_freq = amd_get_max_freq(cpudata);
++ if (max_freq < 0)
++ return max_freq;
++
++ ret += sprintf(&buf[ret], "%u\n", max_freq);
++
++ return ret;
++}
++
++static ssize_t show_amd_pstate_nominal_freq(struct cpufreq_policy *policy,
++ char *buf)
++{
++ int ret = 0, nominal_freq;
++ struct amd_cpudata *cpudata;
++
++ cpudata = policy->driver_data;
++
++ nominal_freq = amd_get_nominal_freq(cpudata);
++ if (nominal_freq < 0)
++ return nominal_freq;
++
++ ret += sprintf(&buf[ret], "%u\n", nominal_freq);
++
++ return ret;
++}
++
++static ssize_t
++show_amd_pstate_lowest_nonlinear_freq(struct cpufreq_policy *policy, char *buf)
++{
++ int ret = 0, freq;
++ struct amd_cpudata *cpudata;
++
++ cpudata = policy->driver_data;
++
++ freq = amd_get_lowest_nonlinear_freq(cpudata);
++ if (freq < 0)
++ return freq;
++
++ ret += sprintf(&buf[ret], "%u\n", freq);
++
++ return ret;
++}
++
++static ssize_t show_amd_pstate_min_freq(struct cpufreq_policy *policy, char *buf)
++{
++ int ret = 0;
++ int freq;
++ struct amd_cpudata *cpudata;
++
++ cpudata = policy->driver_data;
++
++ freq = amd_get_min_freq(cpudata);
++ if (freq < 0)
++ return freq;
++
++ ret += sprintf(&buf[ret], "%u\n", freq);
++
++ return ret;
++}
++
++static ssize_t show_is_amd_pstate_enabled(struct cpufreq_policy *policy,
++ char *buf)
+ {
+ return sprintf(&buf[0], "%d\n", acpi_cpc_valid() ? 1 : 0);
+ }
+
+ cpufreq_freq_attr_ro(is_amd_pstate_enabled);
++cpufreq_freq_attr_ro(amd_pstate_max_freq);
++cpufreq_freq_attr_ro(amd_pstate_nominal_freq);
++cpufreq_freq_attr_ro(amd_pstate_lowest_nonlinear_freq);
++cpufreq_freq_attr_ro(amd_pstate_min_freq);
+
+ static struct freq_attr *amd_pstate_attr[] = {
+ &is_amd_pstate_enabled,
++ &amd_pstate_max_freq,
++ &amd_pstate_nominal_freq,
++ &amd_pstate_lowest_nonlinear_freq,
++ &amd_pstate_min_freq,
+ NULL,
+ };
+
+--
+2.33.0
+
+From 4b56a82d37ecb8e9f9df2d0b055939577ff147cf Mon Sep 17 00:00:00 2001
+From: Huang Rui <ray.huang@amd.com>
+Date: Sun, 20 Jun 2021 15:01:08 +0800
+Subject: [PATCH 11/19] cpufreq: amd: add amd-pstate performance attributes
+
+Introduce sysfs attributes to get the different level amd-pstate
+performances.
+
+Signed-off-by: Huang Rui <ray.huang@amd.com>
+---
+ drivers/cpufreq/amd-pstate.c | 66 ++++++++++++++++++++++++++++++++++++
+ 1 file changed, 66 insertions(+)
+
+diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
+index 3c727a22cb69..9c60388d45ed 100644
+--- a/drivers/cpufreq/amd-pstate.c
++++ b/drivers/cpufreq/amd-pstate.c
+@@ -647,6 +647,62 @@ static ssize_t show_amd_pstate_min_freq(struct cpufreq_policy *policy, char *buf
+ return ret;
+ }
+
++static ssize_t
++show_amd_pstate_highest_perf(struct cpufreq_policy *policy, char *buf)
++{
++ int ret = 0;
++ u32 perf;
++ struct amd_cpudata *cpudata = policy->driver_data;
++
++ perf = READ_ONCE(cpudata->highest_perf);
++
++ ret += sprintf(&buf[ret], "%u\n", perf);
++
++ return ret;
++}
++
++static ssize_t
++show_amd_pstate_nominal_perf(struct cpufreq_policy *policy, char *buf)
++{
++ int ret = 0;
++ u32 perf;
++ struct amd_cpudata *cpudata = policy->driver_data;
++
++ perf = READ_ONCE(cpudata->nominal_perf);
++
++ ret += sprintf(&buf[ret], "%u\n", perf);
++
++ return ret;
++}
++
++static ssize_t
++show_amd_pstate_lowest_nonlinear_perf(struct cpufreq_policy *policy, char *buf)
++{
++ int ret = 0;
++ u32 perf;
++ struct amd_cpudata *cpudata = policy->driver_data;
++
++ perf = READ_ONCE(cpudata->lowest_nonlinear_perf);
++
++ ret += sprintf(&buf[ret], "%u\n", perf);
++
++ return ret;
++}
++
++static ssize_t
++show_amd_pstate_lowest_perf(struct cpufreq_policy *policy, char *buf)
++{
++ int ret = 0;
++ u32 perf;
++ struct amd_cpudata *cpudata = policy->driver_data;
++
++ perf = READ_ONCE(cpudata->lowest_perf);
++
++ ret += sprintf(&buf[ret], "%u\n", perf);
++
++ return ret;
++}
++
+ static ssize_t show_is_amd_pstate_enabled(struct cpufreq_policy *policy,
+ char *buf)
+ {
+@@ -654,17 +710,27 @@ static ssize_t show_is_amd_pstate_enabled(struct cpufreq_policy *policy,
+ }
+
+ cpufreq_freq_attr_ro(is_amd_pstate_enabled);
++
+ cpufreq_freq_attr_ro(amd_pstate_max_freq);
+ cpufreq_freq_attr_ro(amd_pstate_nominal_freq);
+ cpufreq_freq_attr_ro(amd_pstate_lowest_nonlinear_freq);
+ cpufreq_freq_attr_ro(amd_pstate_min_freq);
+
++cpufreq_freq_attr_ro(amd_pstate_highest_perf);
++cpufreq_freq_attr_ro(amd_pstate_nominal_perf);
++cpufreq_freq_attr_ro(amd_pstate_lowest_nonlinear_perf);
++cpufreq_freq_attr_ro(amd_pstate_lowest_perf);
++
+ static struct freq_attr *amd_pstate_attr[] = {
+ &is_amd_pstate_enabled,
+ &amd_pstate_max_freq,
+ &amd_pstate_nominal_freq,
+ &amd_pstate_lowest_nonlinear_freq,
+ &amd_pstate_min_freq,
++ &amd_pstate_highest_perf,
++ &amd_pstate_nominal_perf,
++ &amd_pstate_lowest_nonlinear_perf,
++ &amd_pstate_lowest_perf,
+ NULL,
+ };
+
+--
+2.33.0
+
+From 5b16452c4363a46a28bd65a00a4a3282bdb7ec69 Mon Sep 17 00:00:00 2001
+From: Huang Rui <ray.huang@amd.com>
+Date: Mon, 14 Jun 2021 22:52:01 +0800
+Subject: [PATCH 12/19] cpupower: add AMD P-state capability flag
+
+Add AMD P-state capability flag in cpupower to indicate AMD new P-state
+kernel module support on Ryzen processors.
+
+Signed-off-by: Huang Rui <ray.huang@amd.com>
+---
+ tools/power/cpupower/utils/helpers/helpers.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h
+index 33ffacee7fcb..b4813efdfb00 100644
+--- a/tools/power/cpupower/utils/helpers/helpers.h
++++ b/tools/power/cpupower/utils/helpers/helpers.h
+@@ -73,6 +73,7 @@ enum cpupower_cpu_vendor {X86_VENDOR_UNKNOWN = 0, X86_VENDOR_INTEL,
+ #define CPUPOWER_CAP_AMD_HW_PSTATE 0x00000100
+ #define CPUPOWER_CAP_AMD_PSTATEDEF 0x00000200
+ #define CPUPOWER_CAP_AMD_CPB_MSR 0x00000400
++#define CPUPOWER_CAP_AMD_PSTATE 0x00000800
+
+ #define CPUPOWER_AMD_CPBDIS 0x02000000
+
+--
+2.33.0
+
+From 456b88736d16414c3cce9dd5101b05dfe38baa18 Mon Sep 17 00:00:00 2001
+From: Huang Rui <ray.huang@amd.com>
+Date: Sun, 27 Jun 2021 22:25:39 +0800
+Subject: [PATCH 13/19] cpupower: add the function to check amd-pstate enabled
+
+Introduce the cpupower_amd_pstate_enabled() to check whether the kernel
+mode enables amd-pstate.
+
+Signed-off-by: Huang Rui <ray.huang@amd.com>
+---
+ tools/power/cpupower/utils/helpers/helpers.h | 5 +++++
+ tools/power/cpupower/utils/helpers/misc.c | 20 ++++++++++++++++++++
+ 2 files changed, 25 insertions(+)
+
+diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h
+index b4813efdfb00..eb43c14d1728 100644
+--- a/tools/power/cpupower/utils/helpers/helpers.h
++++ b/tools/power/cpupower/utils/helpers/helpers.h
+@@ -136,6 +136,11 @@ extern int decode_pstates(unsigned int cpu, int boost_states,
+
+ extern int cpufreq_has_boost_support(unsigned int cpu, int *support,
+ int *active, int * states);
++
++/* AMD PSTATE enabling **************************/
++
++extern unsigned long cpupower_amd_pstate_enabled(unsigned int cpu);
++
+ /*
+ * CPUID functions returning a single datum
+ */
+diff --git a/tools/power/cpupower/utils/helpers/misc.c b/tools/power/cpupower/utils/helpers/misc.c
+index fc6e34511721..07d80775fb68 100644
+--- a/tools/power/cpupower/utils/helpers/misc.c
++++ b/tools/power/cpupower/utils/helpers/misc.c
+@@ -83,6 +83,26 @@ int cpupower_intel_set_perf_bias(unsigned int cpu, unsigned int val)
+ return 0;
+ }
+
++unsigned long cpupower_amd_pstate_enabled(unsigned int cpu)
++{
++ char linebuf[MAX_LINE_LEN];
++ char path[SYSFS_PATH_MAX];
++ unsigned long val;
++ char *endp;
++
++ snprintf(path, sizeof(path),
++ PATH_TO_CPU "cpu%u/cpufreq/is_amd_pstate_enabled", cpu);
++
++ if (cpupower_read_sysfs(path, linebuf, MAX_LINE_LEN) == 0)
++ return 0;
++
++ val = strtoul(linebuf, &endp, 0);
++ if (endp == linebuf || errno == ERANGE)
++ return 0;
++
++ return val;
++}
++
+ #endif /* #if defined(__i386__) || defined(__x86_64__) */
+
+ /* get_cpustate
+--
+2.33.0
+
+From 9537cd2a2aa718faba5d61c1196d7b05c52a89f4 Mon Sep 17 00:00:00 2001
+From: Huang Rui <ray.huang@amd.com>
+Date: Sun, 27 Jun 2021 22:40:14 +0800
+Subject: [PATCH 14/19] cpupower: initial AMD P-state capability
+
+If kernel enables AMD P-state, cpupower won't need to respond ACPI
+hardware P-states function anymore.
+
+Signed-off-by: Huang Rui <ray.huang@amd.com>
+---
+ tools/power/cpupower/utils/helpers/cpuid.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/tools/power/cpupower/utils/helpers/cpuid.c b/tools/power/cpupower/utils/helpers/cpuid.c
+index 72eb43593180..78218c54acca 100644
+--- a/tools/power/cpupower/utils/helpers/cpuid.c
++++ b/tools/power/cpupower/utils/helpers/cpuid.c
+@@ -149,6 +149,19 @@ int get_cpu_info(struct cpupower_cpu_info *cpu_info)
+ if (ext_cpuid_level >= 0x80000008 &&
+ cpuid_ebx(0x80000008) & (1 << 4))
+ cpu_info->caps |= CPUPOWER_CAP_AMD_RDPRU;
++
++ if (cpupower_amd_pstate_enabled(0)) {
++ cpu_info->caps |= CPUPOWER_CAP_AMD_PSTATE;
++
++ /*
++ * If AMD P-state is enabled, the firmware will treat
++ * AMD P-state function as high priority.
++ */
++ cpu_info->caps &= ~CPUPOWER_CAP_AMD_CPB;
++ cpu_info->caps &= ~CPUPOWER_CAP_AMD_CPB_MSR;
++ cpu_info->caps &= ~CPUPOWER_CAP_AMD_HW_PSTATE;
++ cpu_info->caps &= ~CPUPOWER_CAP_AMD_PSTATEDEF;
++ }
+ }
+
+ if (cpu_info->vendor == X86_VENDOR_INTEL) {
+--
+2.33.0
+
+From d3f392c951479c73e5a4b0c8d94901aafd5b9da7 Mon Sep 17 00:00:00 2001
+From: Huang Rui <ray.huang@amd.com>
+Date: Sun, 20 Jun 2021 17:07:25 +0800
+Subject: [PATCH 15/19] cpupower: add amd-pstate sysfs entries into libcpufreq
+
+These amd-pstate sysfs entries will be used on cpupower for amd-pstate
+kernel module.
+
+Signed-off-by: Huang Rui <ray.huang@amd.com>
+---
+ tools/power/cpupower/lib/cpufreq.c | 18 +++++++++++++++++-
+ 1 file changed, 17 insertions(+), 1 deletion(-)
+
+diff --git a/tools/power/cpupower/lib/cpufreq.c b/tools/power/cpupower/lib/cpufreq.c
+index c3b56db8b921..3f92ddadaad2 100644
+--- a/tools/power/cpupower/lib/cpufreq.c
++++ b/tools/power/cpupower/lib/cpufreq.c
+@@ -69,6 +69,14 @@ enum cpufreq_value {
+ SCALING_MIN_FREQ,
+ SCALING_MAX_FREQ,
+ STATS_NUM_TRANSITIONS,
++ AMD_PSTATE_HIGHEST_PERF,
++ AMD_PSTATE_NOMINAL_PERF,
++ AMD_PSTATE_LOWEST_NONLINEAR_PERF,
++ AMD_PSTATE_LOWEST_PERF,
++ AMD_PSTATE_MAX_FREQ,
++ AMD_PSTATE_NOMINAL_FREQ,
++ AMD_PSTATE_LOWEST_NONLINEAR_FREQ,
++ AMD_PSTATE_MIN_FREQ,
+ MAX_CPUFREQ_VALUE_READ_FILES
+ };
+
+@@ -80,7 +88,15 @@ static const char *cpufreq_value_files[MAX_CPUFREQ_VALUE_READ_FILES] = {
+ [SCALING_CUR_FREQ] = "scaling_cur_freq",
+ [SCALING_MIN_FREQ] = "scaling_min_freq",
+ [SCALING_MAX_FREQ] = "scaling_max_freq",
+- [STATS_NUM_TRANSITIONS] = "stats/total_trans"
++ [STATS_NUM_TRANSITIONS] = "stats/total_trans",
++ [AMD_PSTATE_HIGHEST_PERF] = "amd_pstate_highest_perf",
++ [AMD_PSTATE_NOMINAL_PERF] = "amd_pstate_nominal_perf",
++ [AMD_PSTATE_LOWEST_NONLINEAR_PERF] = "amd_pstate_lowest_nonlinear_perf",
++ [AMD_PSTATE_LOWEST_PERF] = "amd_pstate_lowest_perf",
++ [AMD_PSTATE_MAX_FREQ] = "amd_pstate_max_freq",
++ [AMD_PSTATE_NOMINAL_FREQ] = "amd_pstate_nominal_freq",
++ [AMD_PSTATE_LOWEST_NONLINEAR_FREQ] = "amd_pstate_lowest_nonlinear_freq",
++ [AMD_PSTATE_MIN_FREQ] = "amd_pstate_min_freq"
+ };
+
+
+--
+2.33.0
+
+From c7249e524ce793bfe0575f45a0f8245f51b02af7 Mon Sep 17 00:00:00 2001
+From: Huang Rui <ray.huang@amd.com>
+Date: Sun, 20 Jun 2021 17:35:45 +0800
+Subject: [PATCH 16/19] cpupower: enable boost state support for amd-pstate
+ module
+
+The AMD P-state boost API is different from ACPI hardware P-states, so
+implement the support for amd-pstate kernel module.
+
+Signed-off-by: Huang Rui <ray.huang@amd.com>
+---
+ tools/power/cpupower/lib/cpufreq.c | 20 ++++++++++++++++++++
+ tools/power/cpupower/lib/cpufreq.h | 3 +++
+ tools/power/cpupower/utils/helpers/misc.c | 7 +++++++
+ 3 files changed, 30 insertions(+)
+
+diff --git a/tools/power/cpupower/lib/cpufreq.c b/tools/power/cpupower/lib/cpufreq.c
+index 3f92ddadaad2..37da87bdcfb1 100644
+--- a/tools/power/cpupower/lib/cpufreq.c
++++ b/tools/power/cpupower/lib/cpufreq.c
+@@ -790,3 +790,23 @@ unsigned long cpufreq_get_transitions(unsigned int cpu)
+ {
+ return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS);
+ }
++
++int amd_pstate_boost_support(unsigned int cpu)
++{
++ unsigned int highest_perf, nominal_perf;
++
++ highest_perf = sysfs_cpufreq_get_one_value(cpu, AMD_PSTATE_HIGHEST_PERF);
++ nominal_perf = sysfs_cpufreq_get_one_value(cpu, AMD_PSTATE_NOMINAL_PERF);
++
++ return highest_perf > nominal_perf ? 1 : 0;
++}
++
++int amd_pstate_boost_enabled(unsigned int cpu)
++{
++ unsigned int cpuinfo_max, amd_pstate_max;
++
++ cpuinfo_max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ);
++ amd_pstate_max = sysfs_cpufreq_get_one_value(cpu, AMD_PSTATE_MAX_FREQ);
++
++ return cpuinfo_max == amd_pstate_max ? 1 : 0;
++}
+diff --git a/tools/power/cpupower/lib/cpufreq.h b/tools/power/cpupower/lib/cpufreq.h
+index 95f4fd9e2656..d54d02a7a4f4 100644
+--- a/tools/power/cpupower/lib/cpufreq.h
++++ b/tools/power/cpupower/lib/cpufreq.h
+@@ -203,6 +203,9 @@ int cpufreq_modify_policy_governor(unsigned int cpu, char *governor);
+ int cpufreq_set_frequency(unsigned int cpu,
+ unsigned long target_frequency);
+
++int amd_pstate_boost_support(unsigned int cpu);
++int amd_pstate_boost_enabled(unsigned int cpu);
++
+ #ifdef __cplusplus
+ }
+ #endif
+diff --git a/tools/power/cpupower/utils/helpers/misc.c b/tools/power/cpupower/utils/helpers/misc.c
+index 07d80775fb68..aba979320760 100644
+--- a/tools/power/cpupower/utils/helpers/misc.c
++++ b/tools/power/cpupower/utils/helpers/misc.c
+@@ -10,6 +10,7 @@
+ #if defined(__i386__) || defined(__x86_64__)
+
+ #include "cpupower_intern.h"
++#include "cpufreq.h"
+
+ #define MSR_AMD_HWCR 0xc0010015
+
+@@ -39,6 +40,12 @@ int cpufreq_has_boost_support(unsigned int cpu, int *support, int *active,
+ if (ret)
+ return ret;
+ }
++ } if ((cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATE) &&
++ amd_pstate_boost_support(cpu)) {
++ *support = 1;
++
++ if (amd_pstate_boost_enabled(cpu))
++ *active = 1;
+ } else if (cpupower_cpu_info.caps & CPUPOWER_CAP_INTEL_IDA)
+ *support = *active = 1;
+ return 0;
+--
+2.33.0
+
+From cf79733de00a260c6d3e5038f00cdb91a71df9af Mon Sep 17 00:00:00 2001
+From: Huang Rui <ray.huang@amd.com>
+Date: Sun, 20 Jun 2021 18:25:55 +0800
+Subject: [PATCH 17/19] cpupower: add amd-pstate get data function to query the
+ info
+
+Frequency-info needs an interface to query the current amd-pstate data.
+
+Signed-off-by: Huang Rui <ray.huang@amd.com>
+---
+ tools/power/cpupower/lib/cpufreq.c | 6 ++++++
+ tools/power/cpupower/lib/cpufreq.h | 13 +++++++++++++
+ 2 files changed, 19 insertions(+)
+
+diff --git a/tools/power/cpupower/lib/cpufreq.c b/tools/power/cpupower/lib/cpufreq.c
+index 37da87bdcfb1..1443080868da 100644
+--- a/tools/power/cpupower/lib/cpufreq.c
++++ b/tools/power/cpupower/lib/cpufreq.c
+@@ -810,3 +810,9 @@ int amd_pstate_boost_enabled(unsigned int cpu)
+
+ return cpuinfo_max == amd_pstate_max ? 1 : 0;
+ }
++
++unsigned amd_pstate_get_data(unsigned int cpu, enum amd_pstate_param param)
++{
++ return sysfs_cpufreq_get_one_value(cpu,
++ param + AMD_PSTATE_HIGHEST_PERF);
++}
+diff --git a/tools/power/cpupower/lib/cpufreq.h b/tools/power/cpupower/lib/cpufreq.h
+index d54d02a7a4f4..954e72704fc0 100644
+--- a/tools/power/cpupower/lib/cpufreq.h
++++ b/tools/power/cpupower/lib/cpufreq.h
+@@ -206,6 +206,19 @@ int cpufreq_set_frequency(unsigned int cpu,
+ int amd_pstate_boost_support(unsigned int cpu);
+ int amd_pstate_boost_enabled(unsigned int cpu);
+
++enum amd_pstate_param {
++ HIGHEST_PERF,
++ NOMINAL_PERF,
++ LOWEST_NONLINEAR_PERF,
++ LOWEST_PERF,
++ MAX_FREQ,
++ NOMINAL_FREQ,
++ LOWEST_NONLINEAR_FREQ,
++ MIN_FREQ,
++};
++
++unsigned amd_pstate_get_data(unsigned int cpu, enum amd_pstate_param param);
++
+ #ifdef __cplusplus
+ }
+ #endif
+--
+2.33.0
+
+From 573a0ff1add6bb6df585d6535abaaa0e1afc61c9 Mon Sep 17 00:00:00 2001
+From: Huang Rui <ray.huang@amd.com>
+Date: Thu, 10 Jun 2021 23:48:03 +0800
+Subject: [PATCH 18/19] cpupower: print amd-pstate information on cpupower
+
+amd-pstate kernel module is using the fine grain frequency instead of
+acpi hardware pstate. So the performance and frequency values should be
+printed in frequency-info.
+
+Signed-off-by: Huang Rui <ray.huang@amd.com>
+---
+ tools/power/cpupower/utils/cpufreq-info.c | 27 ++++++++++++++++++++---
+ 1 file changed, 24 insertions(+), 3 deletions(-)
+
+diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c
+index f9895e31ff5a..9eabed209adc 100644
+--- a/tools/power/cpupower/utils/cpufreq-info.c
++++ b/tools/power/cpupower/utils/cpufreq-info.c
+@@ -183,9 +183,30 @@ static int get_boost_mode_x86(unsigned int cpu)
+ printf(_(" Supported: %s\n"), support ? _("yes") : _("no"));
+ printf(_(" Active: %s\n"), active ? _("yes") : _("no"));
+
+- if ((cpupower_cpu_info.vendor == X86_VENDOR_AMD &&
+- cpupower_cpu_info.family >= 0x10) ||
+- cpupower_cpu_info.vendor == X86_VENDOR_HYGON) {
++ if (cpupower_cpu_info.vendor == X86_VENDOR_AMD &&
++ cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATE) {
++ printf(_(" AMD PSTATE Highest Performance: %u. Maximum Frequency: "),
++ amd_pstate_get_data(cpu, HIGHEST_PERF));
++ print_speed(amd_pstate_get_data(cpu, MAX_FREQ));
++ printf(".\n");
++
++ printf(_(" AMD PSTATE Nominal Performance: %u. Nominal Frequency: "),
++ amd_pstate_get_data(cpu, NOMINAL_PERF));
++ print_speed(amd_pstate_get_data(cpu, NOMINAL_FREQ));
++ printf(".\n");
++
++ printf(_(" AMD PSTATE Lowest Non-linear Performance: %u. Lowest Non-linear Frequency: "),
++ amd_pstate_get_data(cpu, LOWEST_NONLINEAR_PERF));
++ print_speed(amd_pstate_get_data(cpu, LOWEST_NONLINEAR_FREQ));
++ printf(".\n");
++
++ printf(_(" AMD PSTATE Lowest Performance: %u. Lowest Frequency: "),
++ amd_pstate_get_data(cpu, LOWEST_PERF));
++ print_speed(amd_pstate_get_data(cpu, MIN_FREQ));
++ printf(".\n");
++ } else if ((cpupower_cpu_info.vendor == X86_VENDOR_AMD &&
++ cpupower_cpu_info.family >= 0x10) ||
++ cpupower_cpu_info.vendor == X86_VENDOR_HYGON) {
+ ret = decode_pstates(cpu, b_states, pstates, &pstate_no);
+ if (ret)
+ return ret;
+--
+2.33.0
+
+From abfcbc164c1aa0c63d5e256854bad977a9645586 Mon Sep 17 00:00:00 2001
+From: Huang Rui <ray.huang@amd.com>
+Date: Thu, 10 Jun 2021 23:40:18 +0800
+Subject: [PATCH 19/19] Documentation: amd-pstate: add amd-pstate driver
+ introduction
+
+Introduce the amd-pstate driver design and implementation.
+
+Signed-off-by: Huang Rui <ray.huang@amd.com>
+---
+ Documentation/admin-guide/pm/amd_pstate.rst | 377 ++++++++++++++++++
+ .../admin-guide/pm/working-state.rst | 1 +
+ 2 files changed, 378 insertions(+)
+ create mode 100644 Documentation/admin-guide/pm/amd_pstate.rst
+
+diff --git a/Documentation/admin-guide/pm/amd_pstate.rst b/Documentation/admin-guide/pm/amd_pstate.rst
+new file mode 100644
+index 000000000000..c3659dde0cee
+--- /dev/null
++++ b/Documentation/admin-guide/pm/amd_pstate.rst
+@@ -0,0 +1,377 @@
++.. SPDX-License-Identifier: GPL-2.0
++.. include:: <isonum.txt>
++
++===============================================
++``amd-pstate`` CPU Performance Scaling Driver
++===============================================
++
++:Copyright: |copy| 2021 Advanced Micro Devices, Inc.
++
++:Author: Huang Rui <ray.huang@amd.com>
++
++
++Introduction
++===================
++
++``amd-pstate`` is the AMD CPU performance scaling driver that introduces a
++new CPU frequency control mechanism on modern AMD APU and CPU series in
++Linux kernel. The new mechanism is based on Collaborative Processor
++Performance Control (CPPC) which provides finer grain frequency management
++than legacy ACPI hardware P-States. Current AMD CPU/APU platforms are using
++the ACPI P-states driver to manage CPU frequency and clocks with switching
++only in 3 P-states. CPPC replaces the ACPI P-states controls, allows a
++flexible, low-latency interface for the Linux kernel to directly
++communicate the performance hints to hardware.
++
++``amd-pstate`` leverages the Linux kernel governors such as ``schedutil``,
++``ondemand``, etc. to manage the performance hints which are provided by
++CPPC hardware functionality that internally follows the hardware
++specification (for details refer to AMD64 Architecture Programmer's Manual
++Volume 2: System Programming [1]_). Currently ``amd-pstate`` supports basic
++frequency control function according to kernel governors on some of the
++Zen2 and Zen3 processors, and we will implement more AMD specific functions
++in future after we verify them on the hardware and SBIOS.
++
++
++AMD CPPC Overview
++=======================
++
++Collaborative Processor Performance Control (CPPC) interface enumerates a
++continuous, abstract, and unit-less performance value in a scale that is
++not tied to a specific performance state / frequency. This is an ACPI
++standard [2]_ which software can specify application performance goals and
++hints as a relative target to the infrastructure limits. AMD processors
++provides the low latency register model (MSR) instead of AML code
++interpreter for performance adjustments. ``amd-pstate`` will initialize a
++``struct cpufreq_driver`` instance ``amd_pstate_driver`` with the callbacks
++to manage each performance update behavior. ::
++
++ Highest Perf ------>+-----------------------+ +-----------------------+
++ | | | |
++ | | | |
++ | | Max Perf ---->| |
++ | | | |
++ | | | |
++ Nominal Perf ------>+-----------------------+ +-----------------------+
++ | | | |
++ | | | |
++ | | | |
++ | | | |
++ | | | |
++ | | | |
++ | | Desired Perf ---->| |
++ | | | |
++ | | | |
++ | | | |
++ | | | |
++ | | | |
++ | | | |
++ | | | |
++ | | | |
++ | | | |
++ Lowest non- | | | |
++ linear perf ------>+-----------------------+ +-----------------------+
++ | | | |
++ | | Lowest perf ---->| |
++ | | | |
++ Lowest perf ------>+-----------------------+ +-----------------------+
++ | | | |
++ | | | |
++ | | | |
++ 0 ------>+-----------------------+ +-----------------------+
++
++ AMD P-States Performance Scale
++
++
++.. _perf_cap:
++
++AMD CPPC Performance Capability
++--------------------------------
++
++Highest Performance (RO)
++.........................
++
++It is the absolute maximum performance an individual processor may reach,
++assuming ideal conditions. This performance level may not be sustainable
++for long durations and may only be achievable if other platform components
++are in a specific state; for example, it may require other processors be in
++an idle state. This would be equivalent to the highest frequencies
++supported by the processor.
++
++Nominal (Guaranteed) Performance (RO)
++......................................
++
++It is the maximum sustained performance level of the processor, assuming
++ideal operating conditions. In absence of an external constraint (power,
++thermal, etc.) this is the performance level the processor is expected to
++be able to maintain continuously. All cores/processors are expected to be
++able to sustain their nominal performance state simultaneously.
++
++Lowest non-linear Performance (RO)
++...................................
++
++It is the lowest performance level at which nonlinear power savings are
++achieved, for example, due to the combined effects of voltage and frequency
++scaling. Above this threshold, lower performance levels should be generally
++more energy efficient than higher performance levels. This register
++effectively conveys the most efficient performance level to ``amd-pstate``.
++
++Lowest Performance (RO)
++........................
++
++It is the absolute lowest performance level of the processor. Selecting a
++performance level lower than the lowest nonlinear performance level may
++cause an efficiency penalty but should reduce the instantaneous power
++consumption of the processor.
++
++AMD CPPC Performance Control
++------------------------------
++
++``amd-pstate`` passes performance goals through these registers. The
++register drives the behavior of the desired performance target.
++
++Minimum requested performance (RW)
++...................................
++
++``amd-pstate`` specifies the minimum allowed performance level.
++
++Maximum requested performance (RW)
++...................................
++
++``amd-pstate`` specifies a limit the maximum performance that is expected
++to be supplied by the hardware.
++
++Desired performance target (RW)
++...................................
++
++``amd-pstate`` specifies a desired target in the CPPC performance scale as
++a relative number. This can be expressed as percentage of nominal
++performance (infrastructure max). Below the nominal sustained performance
++level, desired performance expresses the average performance level of the
++processor subject to hardware. Above the nominal performance level,
++processor must provide at least nominal performance requested and go higher
++if current operating conditions allow.
++
++Energy Performance Preference (EPP) (RW)
++.........................................
++
++Provides a hint to the hardware if software wants to bias toward performance
++(0x0) or energy efficiency (0xff).
++
++
++Key Governors Support
++=======================
++
++``amd-pstate`` can be used with all the (generic) scaling governors listed
++by the ``scaling_available_governors`` policy attribute in ``sysfs``. Then,
++it is responsible for the configuration of policy objects corresponding to
++CPUs and provides the ``CPUFreq`` core (and the scaling governors attached
++to the policy objects) with accurate information on the maximum and minimum
++operating frequencies supported by the hardware. Users can check the
++``scaling_cur_freq`` information comes from the ``CPUFreq`` core.
++
++``amd-pstate`` mainly supports ``schedutil`` and ``ondemand`` for dynamic
++frequency control. It is to fine tune the processor configuration on
++``amd-pstate`` to the ``schedutil`` with CPU CFS scheduler. ``amd-pstate``
++registers adjust_perf callback to implement the CPPC similar performance
++update behavior. It is initialized by ``sugov_start`` and then populate the
++CPU's update_util_data pointer to assign ``sugov_update_single_perf`` as
++the utilization update callback function in CPU scheduler. CPU scheduler
++will call ``cpufreq_update_util`` and assign the target performance
++according to the ``struct sugov_cpu`` that utilization update belongs to.
++Then ``amd-pstate`` updates the desired performance according to the CPU
++scheduler assigned.
++
++
++Processor Support
++=======================
++
++The ``amd-pstate`` initialization will fail if the _CPC in ACPI SBIOS is
++not existed at the detected processor, and it uses ``acpi_cpc_valid`` to
++check the _CPC existence. All Zen based processors support legacy ACPI
++hardware P-States function, so while the ``amd-pstate`` fails to be
++initialized, the kernel will fall back to initialize ``acpi-cpufreq``
++driver.
++
++There are two types of hardware implementations for ``amd-pstate``: one is
++`Full MSR Support <perf_cap_>`_ and another is `Shared Memory Support
++<perf_cap_>`_. It can use :c:macro:`X86_FEATURE_AMD_CPPC_EXT` feature flag
++(for details refer to Processor Programming Reference (PPR) for AMD Family
++19h Model 21h, Revision B0 Processors [3]_) to indicate the different
++types. ``amd-pstate`` is to register different ``amd_pstate_perf_funcs``
++instances for different hardware implementations.
++
++Currently, some of Zen2 and Zen3 processors support ``amd-pstate``. In the
++future, it will be supported on more and more AMD processors.
++
++Full MSR Support
++-----------------
++
++Some new Zen3 processors such as Cezanne provide the MSR registers directly
++while the :c:macro:`X86_FEATURE_AMD_CPPC_EXT` CPU feature flag is set.
++``amd-pstate`` can handle the MSR register to implement the fast switch
++function in ``CPUFreq`` that can shrink latency of frequency control on the
++interrupt context.
++
++Shared Memory Support
++----------------------
++
++If :c:macro:`X86_FEATURE_AMD_CPPC_EXT` CPU feature flag is not set, that
++means the processor supports shared memory solution. In this case,
++``amd-pstate`` uses the ``cppc_acpi`` helper methods to implement the
++callback functions of ``amd_pstate_perf_funcs``.
++
++
++AMD P-States and ACPI hardware P-States always can be supported in one
++processor. But AMD P-States has the higher priority and if it is enabled
++with :c:macro:`MSR_AMD_CPPC_ENABLE` or ``cppc_set_enable``, it will respond
++to the request from AMD P-States.
++
++
++User Space Interface in ``sysfs``
++==================================
++
++``amd-pstate`` exposes several global attributes (files) in ``sysfs`` to
++control its functionality at the system level. They located in the
++``/sys/devices/system/cpu/cpufreq/policyX/`` directory and affect all CPUs. ::
++
++ root@hr-test1:/home/ray# ls /sys/devices/system/cpu/cpufreq/policy0/*amd*
++ /sys/devices/system/cpu/cpufreq/policy0/amd_pstate_highest_perf
++ /sys/devices/system/cpu/cpufreq/policy0/amd_pstate_lowest_nonlinear_freq
++ /sys/devices/system/cpu/cpufreq/policy0/amd_pstate_lowest_nonlinear_perf
++ /sys/devices/system/cpu/cpufreq/policy0/amd_pstate_lowest_perf
++ /sys/devices/system/cpu/cpufreq/policy0/amd_pstate_max_freq
++ /sys/devices/system/cpu/cpufreq/policy0/amd_pstate_min_freq
++ /sys/devices/system/cpu/cpufreq/policy0/amd_pstate_nominal_freq
++ /sys/devices/system/cpu/cpufreq/policy0/amd_pstate_nominal_perf
++ /sys/devices/system/cpu/cpufreq/policy0/is_amd_pstate_enabled
++
++
++``is_amd_pstate_enabled``
++
++Query whether current kernel loads ``amd-pstate`` to enable the AMD
++P-States functionality.
++This attribute is read-only.
++
++``amd_pstate_highest_perf / amd_pstate_max_freq``
++
++Maximum CPPC performance and CPU frequency that the driver is allowed to
++set in percent of the maximum supported CPPC performance level (the highest
++performance supported in `AMD CPPC Performance Capability <perf_cap_>`_).
++This attribute is read-only.
++
++``amd_pstate_nominal_perf / amd_pstate_nominal_freq``
++
++Nominal CPPC performance and CPU frequency that the driver is allowed to
++set in percent of the maximum supported CPPC performance level (Please see
++nominal performance in `AMD CPPC Performance Capability <perf_cap_>`_).
++This attribute is read-only.
++
++``amd_pstate_lowest_nonlinear_perf / amd_pstate_lowest_nonlinear_freq``
++
++The lowest non-linear CPPC performance and CPU frequency that the driver is
++allowed to set in percent of the maximum supported CPPC performance level
++(Please see the lowest non-linear performance in `AMD CPPC Performance
++Capability <perf_cap_>`_).
++This attribute is read-only.
++
++``amd_pstate_lowest_perf / amd_pstate_min_freq``
++
++The lowest physical CPPC performance and CPU frequency.
++This attribute is read-only.
++
++
++``amd-pstate`` vs ``acpi-cpufreq``
++======================================
++
++On majority of AMD platforms supported by ``acpi-cpufreq``, the ACPI tables
++provided by the platform firmware used for CPU performance scaling, but
++only provides 3 P-states on AMD processors.
++However, on modern AMD APU and CPU series, it provides the collaborative
++processor performance control according to ACPI protocol and customize this
++for AMD platforms. That is fine-grain and continuous frequency range
++instead of the legacy hardware P-states. ``amd-pstate`` is the kernel
++module which supports the new AMD P-States mechanism on most of future AMD
++platforms. The AMD P-States mechanism will be the more performance and energy
++efficiency frequency management method on AMD processors.
++
++``cpupower`` tool support for ``amd-pstate``
++===============================================
++
++``amd-pstate`` is supported on ``cpupower`` tool that can be used to dump the frequency
++information. And it is in progress to support more and more operations for new
++``amd-pstate`` module with this tool. ::
++
++ root@hr-test1:/home/ray# cpupower frequency-info
++ analyzing CPU 0:
++ driver: amd-pstate
++ CPUs which run at the same hardware frequency: 0
++ CPUs which need to have their frequency coordinated by software: 0
++ maximum transition latency: 131 us
++ hardware limits: 400 MHz - 4.68 GHz
++ available cpufreq governors: ondemand conservative powersave userspace performance schedutil
++ current policy: frequency should be within 400 MHz and 4.68 GHz.
++ The governor "schedutil" may decide which speed to use
++ within this range.
++ current CPU frequency: Unable to call hardware
++ current CPU frequency: 4.02 GHz (asserted by call to kernel)
++ boost state support:
++ Supported: yes
++ Active: yes
++ AMD PSTATE Highest Performance: 166. Maximum Frequency: 4.68 GHz.
++ AMD PSTATE Nominal Performance: 117. Nominal Frequency: 3.30 GHz.
++ AMD PSTATE Lowest Non-linear Performance: 39. Lowest Non-linear Frequency: 1.10 GHz.
++ AMD PSTATE Lowest Performance: 15. Lowest Frequency: 400 MHz.
++
++
++Diagnostics and Tuning
++=======================
++
++Trace Events
++--------------
++
++There are two static trace events that can be used for ``amd-pstate``
++diagnostics. One of them is the cpu_frequency trace event generally used
++by ``CPUFreq``, and the other one is the ``amd_pstate_perf`` trace event
++specific to ``amd-pstate``. The following sequence of shell commands can
++be used to enable them and see their output (if the kernel is generally
++configured to support event tracing). ::
++
++ root@hr-test1:/home/ray# cd /sys/kernel/tracing/
++ root@hr-test1:/sys/kernel/tracing# echo 1 > events/amd_cpu/enable
++ root@hr-test1:/sys/kernel/tracing# cat trace
++ # tracer: nop
++ #
++ # entries-in-buffer/entries-written: 47827/42233061 #P:2
++ #
++ # _-----=> irqs-off
++ # / _----=> need-resched
++ # | / _---=> hardirq/softirq
++ # || / _--=> preempt-depth
++ # ||| / delay
++ # TASK-PID CPU# |||| TIMESTAMP FUNCTION
++ # | | | |||| | |
++ <idle>-0 [000] d.s. 244057.464842: amd_pstate_perf: amd_min_perf=39 amd_des_perf=39 amd_max_perf=166 cpu_id=0 prev=0x2727a6 value=0x2727a6
++ <idle>-0 [000] d.h. 244057.475436: amd_pstate_perf: amd_min_perf=39 amd_des_perf=39 amd_max_perf=166 cpu_id=0 prev=0x2727a6 value=0x2727a6
++ <idle>-0 [000] d.h. 244057.476629: amd_pstate_perf: amd_min_perf=39 amd_des_perf=39 amd_max_perf=166 cpu_id=0 prev=0x2727a6 value=0x2727a6
++ <idle>-0 [000] d.s. 244057.484847: amd_pstate_perf: amd_min_perf=39 amd_des_perf=39 amd_max_perf=166 cpu_id=0 prev=0x2727a6 value=0x2727a6
++ <idle>-0 [000] d.h. 244057.499821: amd_pstate_perf: amd_min_perf=39 amd_des_perf=39 amd_max_perf=166 cpu_id=0 prev=0x2727a6 value=0x2727a6
++ avahi-daemon-528 [000] d... 244057.513568: amd_pstate_perf: amd_min_perf=39 amd_des_perf=39 amd_max_perf=166 cpu_id=0 prev=0x2727a6 value=0x2727a6
++
++The cpu_frequency trace event will be triggered either by the ``schedutil`` scaling
++governor (for the policies it is attached to), or by the ``CPUFreq`` core (for the
++policies with other scaling governors).
++
++
++Reference
++===========
++
++.. [1] AMD64 Architecture Programmer's Manual Volume 2: System Programming,
++ https://www.amd.com/system/files/TechDocs/24593.pdf
++
++.. [2] Advanced Configuration and Power Interface Specification,
++ https://uefi.org/sites/default/files/resources/ACPI_Spec_6_4_Jan22.pdf
++
++.. [3] Processor Programming Reference (PPR) for AMD Family 19h Model 21h, Revision B0 Processors
++ https://www.amd.com/system/files/TechDocs/55898_B1_pub_0.50.zip
++
+diff --git a/Documentation/admin-guide/pm/working-state.rst b/Documentation/admin-guide/pm/working-state.rst
+index f40994c422dc..28db6156b55d 100644
+--- a/Documentation/admin-guide/pm/working-state.rst
++++ b/Documentation/admin-guide/pm/working-state.rst
+@@ -11,6 +11,7 @@ Working-State Power Management
+ intel_idle
+ cpufreq
+ intel_pstate
++ amd_pstate
+ cpufreq_drivers
+ intel_epb
+ intel-speed-select
+--
+2.33.0
+