diff options
4 files changed, 9109 insertions, 2 deletions
@@ -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 + |