Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,37 @@
# SPDX-License-Identifier: BSD-3-Clause-Clear
config MT7921_COMMON
tristate
select MT792x_LIB
select WANT_DEV_COREDUMP
config MT7921E
tristate "MediaTek MT7921E (PCIe) support"
select MT7921_COMMON
depends on MAC80211
depends on PCI
help
This adds support for MT7921E 802.11ax 2x2:2SS wireless devices.
To compile this driver as a module, choose M here.
config MT7921S
tristate "MediaTek MT7921S (SDIO) support"
select MT76_SDIO
select MT7921_COMMON
depends on MAC80211
depends on MMC
help
This adds support for MT7921S 802.11ax 2x2:2SS wireless devices.
To compile this driver as a module, choose M here.
config MT7921U
tristate "MediaTek MT7921U (USB) support"
select MT792x_USB
select MT7921_COMMON
depends on MAC80211
depends on USB
help
This adds support for MT7921U 802.11ax 2x2:2SS wireless devices.
To compile this driver as a module, choose M here.
@@ -0,0 +1,12 @@
# SPDX-License-Identifier: BSD-3-Clause-Clear
obj-$(CONFIG_MT7921_COMMON) += mt7921-common.o
obj-$(CONFIG_MT7921E) += mt7921e.o
obj-$(CONFIG_MT7921S) += mt7921s.o
obj-$(CONFIG_MT7921U) += mt7921u.o
mt7921-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o
mt7921-common-$(CONFIG_NL80211_TESTMODE) += testmode.o
mt7921e-y := pci.o pci_mac.o pci_mcu.o
mt7921s-y := sdio.o sdio_mac.o sdio_mcu.o
mt7921u-y := usb.o
@@ -0,0 +1,294 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include "mt7921.h"
static int
mt7921_reg_set(void *data, u64 val)
{
struct mt792x_dev *dev = data;
mt792x_mutex_acquire(dev);
mt76_wr(dev, dev->mt76.debugfs_reg, val);
mt792x_mutex_release(dev);
return 0;
}
static int
mt7921_reg_get(void *data, u64 *val)
{
struct mt792x_dev *dev = data;
mt792x_mutex_acquire(dev);
*val = mt76_rr(dev, dev->mt76.debugfs_reg);
mt792x_mutex_release(dev);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_regval, mt7921_reg_get, mt7921_reg_set,
"0x%08llx\n");
static int
mt7921_fw_debug_set(void *data, u64 val)
{
struct mt792x_dev *dev = data;
mt792x_mutex_acquire(dev);
dev->fw_debug = (u8)val;
mt7921_mcu_fw_log_2_host(dev, dev->fw_debug);
mt792x_mutex_release(dev);
return 0;
}
static int
mt7921_fw_debug_get(void *data, u64 *val)
{
struct mt792x_dev *dev = data;
*val = dev->fw_debug;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug, mt7921_fw_debug_get,
mt7921_fw_debug_set, "%lld\n");
DEFINE_SHOW_ATTRIBUTE(mt792x_tx_stats);
static void
mt7921_seq_puts_array(struct seq_file *file, const char *str,
s8 *val, int len)
{
int i;
seq_printf(file, "%-16s:", str);
for (i = 0; i < len; i++)
if (val[i] == 127)
seq_printf(file, " %6s", "N.A");
else
seq_printf(file, " %6d", val[i]);
seq_puts(file, "\n");
}
#define mt7921_print_txpwr_entry(prefix, rate) \
({ \
mt7921_seq_puts_array(s, #prefix " (user)", \
txpwr.data[TXPWR_USER].rate, \
ARRAY_SIZE(txpwr.data[TXPWR_USER].rate)); \
mt7921_seq_puts_array(s, #prefix " (eeprom)", \
txpwr.data[TXPWR_EEPROM].rate, \
ARRAY_SIZE(txpwr.data[TXPWR_EEPROM].rate)); \
mt7921_seq_puts_array(s, #prefix " (tmac)", \
txpwr.data[TXPWR_MAC].rate, \
ARRAY_SIZE(txpwr.data[TXPWR_MAC].rate)); \
})
static int
mt7921_txpwr(struct seq_file *s, void *data)
{
struct mt792x_dev *dev = dev_get_drvdata(s->private);
struct mt7921_txpwr txpwr;
int ret;
mt792x_mutex_acquire(dev);
ret = mt7921_get_txpwr_info(dev, &txpwr);
mt792x_mutex_release(dev);
if (ret)
return ret;
seq_printf(s, "Tx power table (channel %d)\n", txpwr.ch);
seq_printf(s, "%-16s %6s %6s %6s %6s\n",
" ", "1m", "2m", "5m", "11m");
mt7921_print_txpwr_entry(CCK, cck);
seq_printf(s, "%-16s %6s %6s %6s %6s %6s %6s %6s %6s\n",
" ", "6m", "9m", "12m", "18m", "24m", "36m",
"48m", "54m");
mt7921_print_txpwr_entry(OFDM, ofdm);
seq_printf(s, "%-16s %6s %6s %6s %6s %6s %6s %6s %6s\n",
" ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
"mcs6", "mcs7");
mt7921_print_txpwr_entry(HT20, ht20);
seq_printf(s, "%-16s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
" ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
"mcs6", "mcs7", "mcs32");
mt7921_print_txpwr_entry(HT40, ht40);
seq_printf(s, "%-16s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
" ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
"mcs6", "mcs7", "mcs8", "mcs9", "mcs10", "mcs11");
mt7921_print_txpwr_entry(VHT20, vht20);
mt7921_print_txpwr_entry(VHT40, vht40);
mt7921_print_txpwr_entry(VHT80, vht80);
mt7921_print_txpwr_entry(VHT160, vht160);
mt7921_print_txpwr_entry(HE26, he26);
mt7921_print_txpwr_entry(HE52, he52);
mt7921_print_txpwr_entry(HE106, he106);
mt7921_print_txpwr_entry(HE242, he242);
mt7921_print_txpwr_entry(HE484, he484);
mt7921_print_txpwr_entry(HE996, he996);
mt7921_print_txpwr_entry(HE996x2, he996x2);
return 0;
}
static int
mt7921_pm_set(void *data, u64 val)
{
struct mt792x_dev *dev = data;
struct mt76_connac_pm *pm = &dev->pm;
if (mt76_is_usb(&dev->mt76))
return -EOPNOTSUPP;
mutex_lock(&dev->mt76.mutex);
if (val == pm->enable_user)
goto out;
if (!pm->enable_user) {
pm->stats.last_wake_event = jiffies;
pm->stats.last_doze_event = jiffies;
}
/* make sure the chip is awake here and ps_work is scheduled
* just at end of the this routine.
*/
pm->enable = false;
mt76_connac_pm_wake(&dev->mphy, pm);
pm->enable_user = val;
mt7921_set_runtime_pm(dev);
mt76_connac_power_save_sched(&dev->mphy, pm);
out:
mutex_unlock(&dev->mt76.mutex);
return 0;
}
static int
mt7921_pm_get(void *data, u64 *val)
{
struct mt792x_dev *dev = data;
*val = dev->pm.enable_user;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_pm, mt7921_pm_get, mt7921_pm_set, "%lld\n");
static int
mt7921_deep_sleep_set(void *data, u64 val)
{
struct mt792x_dev *dev = data;
struct mt76_connac_pm *pm = &dev->pm;
bool monitor = !!(dev->mphy.hw->conf.flags & IEEE80211_CONF_MONITOR);
bool enable = !!val;
if (mt76_is_usb(&dev->mt76))
return -EOPNOTSUPP;
mt792x_mutex_acquire(dev);
if (pm->ds_enable_user == enable)
goto out;
pm->ds_enable_user = enable;
pm->ds_enable = enable && !monitor;
mt76_connac_mcu_set_deep_sleep(&dev->mt76, pm->ds_enable);
out:
mt792x_mutex_release(dev);
return 0;
}
static int
mt7921_deep_sleep_get(void *data, u64 *val)
{
struct mt792x_dev *dev = data;
*val = dev->pm.ds_enable_user;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_ds, mt7921_deep_sleep_get,
mt7921_deep_sleep_set, "%lld\n");
DEFINE_DEBUGFS_ATTRIBUTE(fops_pm_idle_timeout, mt792x_pm_idle_timeout_get,
mt792x_pm_idle_timeout_set, "%lld\n");
static int mt7921_chip_reset(void *data, u64 val)
{
struct mt792x_dev *dev = data;
int ret = 0;
switch (val) {
case 1:
/* Reset wifisys directly. */
mt792x_reset(&dev->mt76);
break;
default:
/* Collect the core dump before reset wifisys. */
mt792x_mutex_acquire(dev);
ret = mt76_connac_mcu_chip_config(&dev->mt76);
mt792x_mutex_release(dev);
break;
}
return ret;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_reset, NULL, mt7921_chip_reset, "%lld\n");
static int
mt7921s_sched_quota_read(struct seq_file *s, void *data)
{
struct mt792x_dev *dev = dev_get_drvdata(s->private);
struct mt76_sdio *sdio = &dev->mt76.sdio;
seq_printf(s, "pse_data_quota\t%d\n", sdio->sched.pse_data_quota);
seq_printf(s, "ple_data_quota\t%d\n", sdio->sched.ple_data_quota);
seq_printf(s, "pse_mcu_quota\t%d\n", sdio->sched.pse_mcu_quota);
seq_printf(s, "sched_deficit\t%d\n", sdio->sched.deficit);
return 0;
}
int mt7921_init_debugfs(struct mt792x_dev *dev)
{
struct dentry *dir;
dir = mt76_register_debugfs_fops(&dev->mphy, &fops_regval);
if (mt76_is_mmio(&dev->mt76))
debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues",
dir, mt792x_queues_read);
else
debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues",
dir, mt76_queues_read);
debugfs_create_devm_seqfile(dev->mt76.dev, "acq", dir,
mt792x_queues_acq);
debugfs_create_devm_seqfile(dev->mt76.dev, "txpower_sku", dir,
mt7921_txpwr);
debugfs_create_file("tx_stats", 0400, dir, dev, &mt792x_tx_stats_fops);
debugfs_create_file("fw_debug", 0600, dir, dev, &fops_fw_debug);
debugfs_create_file("runtime-pm", 0600, dir, dev, &fops_pm);
debugfs_create_file("idle-timeout", 0600, dir, dev,
&fops_pm_idle_timeout);
debugfs_create_file("chip_reset", 0600, dir, dev, &fops_reset);
debugfs_create_devm_seqfile(dev->mt76.dev, "runtime_pm_stats", dir,
mt792x_pm_stats);
debugfs_create_file("deep-sleep", 0600, dir, dev, &fops_ds);
if (mt76_is_sdio(&dev->mt76))
debugfs_create_devm_seqfile(dev->mt76.dev, "sched-quota", dir,
mt7921s_sched_quota_read);
return 0;
}
@@ -0,0 +1,256 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include <linux/etherdevice.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/thermal.h>
#include <linux/firmware.h>
#include "mt7921.h"
#include "../mt76_connac2_mac.h"
#include "mcu.h"
#include "regd.h"
static ssize_t mt7921_thermal_temp_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
switch (to_sensor_dev_attr(attr)->index) {
case 0: {
struct mt792x_phy *phy = dev_get_drvdata(dev);
struct mt792x_dev *mdev = phy->dev;
int temperature;
mt792x_mutex_acquire(mdev);
temperature = mt7921_mcu_get_temperature(phy);
mt792x_mutex_release(mdev);
if (temperature < 0)
return temperature;
/* display in millidegree Celsius */
return sprintf(buf, "%u\n", temperature * 1000);
}
default:
return -EINVAL;
}
}
static SENSOR_DEVICE_ATTR_RO(temp1_input, mt7921_thermal_temp, 0);
static struct attribute *mt7921_hwmon_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
NULL,
};
ATTRIBUTE_GROUPS(mt7921_hwmon);
static int mt7921_thermal_init(struct mt792x_phy *phy)
{
struct wiphy *wiphy = phy->mt76->hw->wiphy;
struct device *hwmon;
const char *name;
if (!IS_REACHABLE(CONFIG_HWMON))
return 0;
name = devm_kasprintf(&wiphy->dev, GFP_KERNEL, "mt7921_%s",
wiphy_name(wiphy));
if (!name)
return -ENOMEM;
hwmon = devm_hwmon_device_register_with_groups(&wiphy->dev, name, phy,
mt7921_hwmon_groups);
return PTR_ERR_OR_ZERO(hwmon);
}
int mt7921_mac_init(struct mt792x_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);
for (i = 0; i < MT792x_WTBL_SIZE; i++)
mt7921_mac_wtbl_update(dev, i,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
for (i = 0; i < 2; i++)
mt792x_mac_init_band(dev, i);
return mt76_connac_mcu_set_rts_thresh(&dev->mt76, 0x92b, 0);
}
EXPORT_SYMBOL_GPL(mt7921_mac_init);
static int __mt7921_init_hardware(struct mt792x_dev *dev)
{
int ret;
/* force firmware operation mode into normal state,
* which should be set before firmware download stage.
*/
mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE);
ret = mt792x_mcu_init(dev);
if (ret)
goto out;
ret = mt76_eeprom_override(&dev->mphy);
if (ret)
goto out;
ret = mt7921_mcu_set_eeprom(dev);
if (ret)
goto out;
ret = mt7921_mac_init(dev);
out:
return ret;
}
static int mt7921_init_hardware(struct mt792x_dev *dev)
{
int ret, i;
set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state);
for (i = 0; i < MT792x_MCU_INIT_RETRY_COUNT; i++) {
ret = __mt7921_init_hardware(dev);
if (!ret)
break;
mt792x_init_reset(dev);
}
if (i == MT792x_MCU_INIT_RETRY_COUNT) {
dev_err(dev->mt76.dev, "hardware init failed\n");
return ret;
}
return 0;
}
static void mt7921_init_work(struct work_struct *work)
{
struct mt792x_dev *dev = container_of(work, struct mt792x_dev,
init_work);
int ret;
ret = mt7921_init_hardware(dev);
if (ret)
return;
mt76_set_stream_caps(&dev->mphy, true);
mt7921_set_stream_he_caps(&dev->phy);
mt792x_config_mac_addr_list(dev);
ret = mt76_register_device(&dev->mt76, true, mt76_rates,
ARRAY_SIZE(mt76_rates));
if (ret) {
dev_err(dev->mt76.dev, "register device failed\n");
return;
}
ret = mt7921_init_debugfs(dev);
if (ret) {
dev_err(dev->mt76.dev, "register debugfs failed\n");
return;
}
ret = mt7921_thermal_init(&dev->phy);
if (ret) {
dev_err(dev->mt76.dev, "thermal init failed\n");
return;
}
/* we support chip reset now */
dev->hw_init_done = true;
mt76_connac_mcu_set_deep_sleep(&dev->mt76, dev->pm.ds_enable);
}
int mt7921_register_device(struct mt792x_dev *dev)
{
struct ieee80211_hw *hw = mt76_hw(dev);
int ret;
dev->phy.dev = dev;
dev->phy.mt76 = &dev->mt76.phy;
dev->mt76.phy.priv = &dev->phy;
dev->mt76.tx_worker.fn = mt792x_tx_worker;
INIT_DELAYED_WORK(&dev->pm.ps_work, mt792x_pm_power_save_work);
INIT_WORK(&dev->pm.wake_work, mt792x_pm_wake_work);
spin_lock_init(&dev->pm.wake.lock);
mutex_init(&dev->pm.mutex);
init_waitqueue_head(&dev->pm.wait);
init_waitqueue_head(&dev->wait);
if (mt76_is_sdio(&dev->mt76))
init_waitqueue_head(&dev->mt76.sdio.wait);
spin_lock_init(&dev->pm.txq_lock);
INIT_DELAYED_WORK(&dev->mphy.mac_work, mt792x_mac_work);
INIT_DELAYED_WORK(&dev->phy.scan_work, mt7921_scan_work);
INIT_DELAYED_WORK(&dev->coredump.work, mt7921_coredump_work);
#if IS_ENABLED(CONFIG_IPV6)
INIT_WORK(&dev->ipv6_ns_work, mt7921_set_ipv6_ns_work);
skb_queue_head_init(&dev->ipv6_ns_list);
#endif
skb_queue_head_init(&dev->phy.scan_event_list);
skb_queue_head_init(&dev->coredump.msg_list);
INIT_WORK(&dev->reset_work, mt7921_mac_reset_work);
INIT_WORK(&dev->init_work, mt7921_init_work);
INIT_WORK(&dev->phy.roc_work, mt7921_roc_work);
timer_setup(&dev->phy.roc_timer, mt792x_roc_timer, 0);
init_waitqueue_head(&dev->phy.roc_wait);
dev->pm.idle_timeout = MT792x_PM_TIMEOUT;
dev->pm.stats.last_wake_event = jiffies;
dev->pm.stats.last_doze_event = jiffies;
if (!mt76_is_usb(&dev->mt76) &&
!is_mt7902(&dev->mt76)) {
dev->pm.enable_user = true;
dev->pm.enable = true;
dev->pm.ds_enable_user = true;
dev->pm.ds_enable = true;
}
if (!mt76_is_mmio(&dev->mt76))
hw->extra_tx_headroom += MT_SDIO_TXD_SIZE + MT_SDIO_HDR_SIZE;
mt792x_init_acpi_sar(dev);
ret = mt792x_init_wcid(dev);
if (ret)
return ret;
ret = mt792x_init_wiphy(hw);
if (ret)
return ret;
hw->wiphy->reg_notifier = mt7921_regd_notifier;
dev->mphy.sband_2g.sband.ht_cap.cap |=
IEEE80211_HT_CAP_LDPC_CODING |
IEEE80211_HT_CAP_MAX_AMSDU;
dev->mphy.sband_5g.sband.ht_cap.cap |=
IEEE80211_HT_CAP_LDPC_CODING |
IEEE80211_HT_CAP_MAX_AMSDU;
dev->mphy.sband_5g.sband.vht_cap.cap |=
IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
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);
if (is_mt7922(&dev->mt76))
dev->mphy.sband_5g.sband.vht_cap.cap |=
IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
IEEE80211_VHT_CAP_SHORT_GI_160;
dev->mphy.hw->wiphy->available_antennas_rx = dev->mphy.chainmask;
dev->mphy.hw->wiphy->available_antennas_tx = dev->mphy.chainmask;
queue_work(system_percpu_wq, &dev->init_work);
return 0;
}
EXPORT_SYMBOL_GPL(mt7921_register_device);
@@ -0,0 +1,878 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include <linux/devcoredump.h>
#include <linux/etherdevice.h>
#include <linux/timekeeping.h>
#include "mt7921.h"
#include "../dma.h"
#include "../mt76_connac2_mac.h"
#include "regd.h"
#include "mcu.h"
#define MT_WTBL_TXRX_CAP_RATE_OFFSET 7
#define MT_WTBL_TXRX_RATE_G2_HE 24
#define MT_WTBL_TXRX_RATE_G2 12
#define MT_WTBL_AC0_CTT_OFFSET 20
bool mt7921_mac_wtbl_update(struct mt792x_dev *dev, int idx, u32 mask)
{
mt76_rmw(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX,
FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, idx) | mask);
return mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY,
0, 5000);
}
static u32 mt7921_mac_wtbl_lmac_addr(int idx, u8 offset)
{
return MT_WTBL_LMAC_OFFS(idx, 0) + offset * 4;
}
static void mt7921_mac_sta_poll(struct mt792x_dev *dev)
{
static const u8 ac_to_tid[] = {
[IEEE80211_AC_BE] = 0,
[IEEE80211_AC_BK] = 1,
[IEEE80211_AC_VI] = 4,
[IEEE80211_AC_VO] = 6
};
struct ieee80211_sta *sta;
struct mt792x_sta *msta;
struct mt792x_link_sta *mlink;
u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS];
LIST_HEAD(sta_poll_list);
struct rate_info *rate;
s8 rssi[4];
int i;
spin_lock_bh(&dev->mt76.sta_poll_lock);
list_splice_init(&dev->mt76.sta_poll_list, &sta_poll_list);
spin_unlock_bh(&dev->mt76.sta_poll_lock);
while (true) {
bool clear = false;
u32 addr, val;
u16 idx;
u8 bw;
spin_lock_bh(&dev->mt76.sta_poll_lock);
if (list_empty(&sta_poll_list)) {
spin_unlock_bh(&dev->mt76.sta_poll_lock);
break;
}
mlink = list_first_entry(&sta_poll_list,
struct mt792x_link_sta,
wcid.poll_list);
msta = container_of(mlink, struct mt792x_sta, deflink);
list_del_init(&mlink->wcid.poll_list);
spin_unlock_bh(&dev->mt76.sta_poll_lock);
idx = mlink->wcid.idx;
addr = mt7921_mac_wtbl_lmac_addr(idx, MT_WTBL_AC0_CTT_OFFSET);
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
u32 tx_last = mlink->airtime_ac[i];
u32 rx_last = mlink->airtime_ac[i + 4];
mlink->airtime_ac[i] = mt76_rr(dev, addr);
mlink->airtime_ac[i + 4] = mt76_rr(dev, addr + 4);
tx_time[i] = mlink->airtime_ac[i] - tx_last;
rx_time[i] = mlink->airtime_ac[i + 4] - rx_last;
if ((tx_last | rx_last) & BIT(30))
clear = true;
addr += 8;
}
if (clear) {
mt7921_mac_wtbl_update(dev, idx,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
memset(mlink->airtime_ac, 0, sizeof(mlink->airtime_ac));
}
if (!mlink->wcid.sta)
continue;
sta = container_of((void *)msta, struct ieee80211_sta,
drv_priv);
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
u8 q = mt76_connac_lmac_mapping(i);
u32 tx_cur = tx_time[q];
u32 rx_cur = rx_time[q];
u8 tid = ac_to_tid[i];
if (!tx_cur && !rx_cur)
continue;
ieee80211_sta_register_airtime(sta, tid, tx_cur,
rx_cur);
}
/* We don't support reading GI info from txs packets.
* For accurate tx status reporting and AQL improvement,
* we need to make sure that flags match so polling GI
* from per-sta counters directly.
*/
rate = &mlink->wcid.rate;
addr = mt7921_mac_wtbl_lmac_addr(idx,
MT_WTBL_TXRX_CAP_RATE_OFFSET);
val = mt76_rr(dev, addr);
switch (rate->bw) {
case RATE_INFO_BW_160:
bw = IEEE80211_STA_RX_BW_160;
break;
case RATE_INFO_BW_80:
bw = IEEE80211_STA_RX_BW_80;
break;
case RATE_INFO_BW_40:
bw = IEEE80211_STA_RX_BW_40;
break;
default:
bw = IEEE80211_STA_RX_BW_20;
break;
}
if (rate->flags & RATE_INFO_FLAGS_HE_MCS) {
u8 offs = MT_WTBL_TXRX_RATE_G2_HE + 2 * bw;
rate->he_gi = (val & (0x3 << offs)) >> offs;
} else if (rate->flags &
(RATE_INFO_FLAGS_VHT_MCS | RATE_INFO_FLAGS_MCS)) {
if (val & BIT(MT_WTBL_TXRX_RATE_G2 + bw))
rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
else
rate->flags &= ~RATE_INFO_FLAGS_SHORT_GI;
}
/* get signal strength of resp frames (CTS/BA/ACK) */
addr = mt7921_mac_wtbl_lmac_addr(idx, 30);
val = mt76_rr(dev, addr);
rssi[0] = to_rssi(GENMASK(7, 0), val);
rssi[1] = to_rssi(GENMASK(15, 8), val);
rssi[2] = to_rssi(GENMASK(23, 16), val);
rssi[3] = to_rssi(GENMASK(31, 14), val);
mlink->ack_signal =
mt76_rx_signal(msta->vif->phy->mt76->antenna_mask, rssi);
ewma_avg_signal_add(&mlink->avg_ack_signal, -mlink->ack_signal);
}
}
static int
mt7921_mac_fill_rx(struct mt792x_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;
u16 hdr_gap;
__le32 *rxv = NULL, *rxd = (__le32 *)skb->data;
struct mt76_phy *mphy = &dev->mt76.phy;
struct mt792x_phy *phy = &dev->phy;
struct ieee80211_supported_band *sband;
u32 csum_status = *(u32 *)skb->cb;
u32 rxd0 = le32_to_cpu(rxd[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]);
struct mt792x_sta *msta = NULL;
struct mt792x_link_sta *mlink;
u16 seq_ctrl = 0;
__le16 fc = 0;
u8 mode = 0;
int i, idx;
memset(status, 0, sizeof(*status));
if (rxd1 & MT_RXD1_NORMAL_BAND_IDX)
return -EINVAL;
if (!test_bit(MT76_STATE_RUNNING, &mphy->state))
return -EINVAL;
if (rxd2 & MT_RXD2_NORMAL_AMSDU_ERR)
return -EINVAL;
hdr_trans = rxd2 & MT_RXD2_NORMAL_HDR_TRANS;
if (hdr_trans && (rxd1 & MT_RXD1_NORMAL_CM))
return -EINVAL;
/* ICV error or CCMP/BIP/WPI MIC error */
if (rxd1 & MT_RXD1_NORMAL_ICV_ERR)
status->flag |= RX_FLAG_ONLY_MONITOR;
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);
status->wcid = mt792x_rx_get_wcid(dev, idx, unicast);
if (status->wcid) {
mlink = container_of(status->wcid, struct mt792x_link_sta, wcid);
msta = container_of(mlink, struct mt792x_sta, deflink);
mt76_wcid_add_poll(&dev->mt76, &mlink->wcid);
}
mt792x_get_status_freq_info(status, chfreq);
switch (status->band) {
case NL80211_BAND_5GHZ:
sband = &mphy->sband_5g.sband;
break;
case NL80211_BAND_6GHZ:
sband = &mphy->sband_6g.sband;
break;
default:
sband = &mphy->sband_2g.sband;
break;
}
if (!sband->channels)
return -EINVAL;
if (mt76_is_mmio(&dev->mt76) && (rxd0 & csum_mask) == csum_mask &&
!(csum_status & (BIT(0) | BIT(2) | BIT(3))))
skb->ip_summed = CHECKSUM_UNNECESSARY;
if (rxd1 & MT_RXD1_NORMAL_FCS_ERR)
status->flag |= RX_FLAG_FAILED_FCS_CRC;
if (rxd1 & MT_RXD1_NORMAL_TKIP_MIC_ERR)
status->flag |= RX_FLAG_MMIC_ERROR;
if (FIELD_GET(MT_RXD1_NORMAL_SEC_MODE, rxd1) != 0 &&
!(rxd1 & (MT_RXD1_NORMAL_CLM | MT_RXD1_NORMAL_CM))) {
status->flag |= RX_FLAG_DECRYPTED;
status->flag |= RX_FLAG_IV_STRIPPED;
status->flag |= RX_FLAG_MMIC_STRIPPED | RX_FLAG_MIC_STRIPPED;
}
remove_pad = FIELD_GET(MT_RXD2_NORMAL_HDR_OFFSET, rxd2);
if (rxd2 & MT_RXD2_NORMAL_MAX_LEN_ERROR)
return -EINVAL;
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;
}
if (rxd1 & MT_RXD1_NORMAL_GROUP_1) {
u8 *data = (u8 *)rxd;
if (status->flag & RX_FLAG_DECRYPTED) {
switch (FIELD_GET(MT_RXD1_NORMAL_SEC_MODE, rxd1)) {
case MT_CIPHER_AES_CCMP:
case MT_CIPHER_CCMP_CCX:
case MT_CIPHER_CCMP_256:
insert_ccmp_hdr =
FIELD_GET(MT_RXD2_NORMAL_FRAG, rxd2);
fallthrough;
case MT_CIPHER_TKIP:
case MT_CIPHER_TKIP_NO_MIC:
case MT_CIPHER_GCMP:
case MT_CIPHER_GCMP_256:
status->iv[0] = data[5];
status->iv[1] = data[4];
status->iv[2] = data[3];
status->iv[3] = data[2];
status->iv[4] = data[1];
status->iv[5] = data[0];
break;
default:
break;
}
}
rxd += 4;
if ((u8 *)rxd - skb->data >= skb->len)
return -EINVAL;
}
if (rxd1 & MT_RXD1_NORMAL_GROUP_2) {
status->timestamp = le32_to_cpu(rxd[0]);
status->flag |= RX_FLAG_MACTIME_START;
if (!(rxd2 & MT_RXD2_NORMAL_NON_AMPDU)) {
status->flag |= RX_FLAG_AMPDU_DETAILS;
/* all subframes of an A-MPDU have the same timestamp */
if (phy->rx_ampdu_ts != status->timestamp) {
if (!++phy->ampdu_ref)
phy->ampdu_ref++;
}
phy->rx_ampdu_ts = status->timestamp;
status->ampdu_ref = phy->ampdu_ref;
}
rxd += 2;
if ((u8 *)rxd - skb->data >= skb->len)
return -EINVAL;
}
/* RXD Group 3 - P-RXV */
if (rxd1 & MT_RXD1_NORMAL_GROUP_3) {
u32 v0, v1;
int ret;
rxv = rxd;
rxd += 2;
if ((u8 *)rxd - skb->data >= skb->len)
return -EINVAL;
v0 = le32_to_cpu(rxv[0]);
v1 = le32_to_cpu(rxv[1]);
if (v0 & MT_PRXV_HT_AD_CODE)
status->enc_flags |= RX_ENC_FLAG_LDPC;
ret = mt76_connac2_mac_fill_rx_rate(&dev->mt76, status, sband,
rxv, &mode);
if (ret < 0)
return ret;
if (rxd1 & MT_RXD1_NORMAL_GROUP_5) {
rxd += 6;
if ((u8 *)rxd - skb->data >= skb->len)
return -EINVAL;
rxv = rxd;
/* Monitor mode would use RCPI described in GROUP 5
* instead.
*/
v1 = le32_to_cpu(rxv[0]);
rxd += 12;
if ((u8 *)rxd - skb->data >= skb->len)
return -EINVAL;
}
status->chains = mphy->antenna_mask;
status->chain_signal[0] = to_rssi(MT_PRXV_RCPI0, v1);
status->chain_signal[1] = to_rssi(MT_PRXV_RCPI1, v1);
status->chain_signal[2] = to_rssi(MT_PRXV_RCPI2, v1);
status->chain_signal[3] = to_rssi(MT_PRXV_RCPI3, v1);
status->signal = -128;
for (i = 0; i < hweight8(mphy->antenna_mask); i++) {
if (!(status->chains & BIT(i)) ||
status->chain_signal[i] >= 0)
continue;
status->signal = max(status->signal,
status->chain_signal[i]);
}
}
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;
}
hdr_gap = (u8 *)rxd - skb->data + 2 * remove_pad;
if (hdr_trans && ieee80211_has_morefrags(fc)) {
struct ieee80211_vif *vif;
int err;
if (!msta || !msta->vif)
return -EINVAL;
vif = container_of((void *)msta->vif, struct ieee80211_vif,
drv_priv);
err = mt76_connac2_reverse_frag0_hdr_trans(vif, skb, hdr_gap);
if (err)
return err;
hdr_trans = false;
} else {
skb_pull(skb, hdr_gap);
if (!hdr_trans && status->amsdu) {
memmove(skb->data + 2, skb->data,
ieee80211_get_hdrlen_from_skb(skb));
skb_pull(skb, 2);
}
}
if (!hdr_trans) {
struct ieee80211_hdr *hdr;
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_8023;
}
mt792x_mac_assoc_rssi(dev, skb);
if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023))
mt76_connac2_mac_decode_he_radiotap(&dev->mt76, skb, rxv, mode);
if (!status->wcid || !ieee80211_is_data_qos(fc))
return 0;
status->aggr = unicast && !ieee80211_is_qos_nullfunc(fc);
status->seqno = IEEE80211_SEQ_TO_SN(seq_ctrl);
status->qos_ctl = qos_ctl;
return 0;
}
void mt7921_mac_add_txs(struct mt792x_dev *dev, void *data)
{
struct mt792x_link_sta *mlink;
struct mt76_wcid *wcid;
__le32 *txs_data = data;
u16 wcidx;
u8 pid;
if (le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT) > 1)
return;
wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID);
pid = le32_get_bits(txs_data[3], MT_TXS3_PID);
if (pid < MT_PACKET_ID_FIRST)
return;
if (wcidx >= MT792x_WTBL_SIZE)
return;
rcu_read_lock();
wcid = mt76_wcid_ptr(dev, wcidx);
if (!wcid)
goto out;
mlink = container_of(wcid, struct mt792x_link_sta, wcid);
mt76_connac2_mac_add_txs_skb(&dev->mt76, wcid, pid, txs_data);
if (!wcid->sta)
goto out;
mt76_wcid_add_poll(&dev->mt76, &mlink->wcid);
out:
rcu_read_unlock();
}
static void mt7921_mac_tx_free(struct mt792x_dev *dev, void *data, int len)
{
struct mt76_connac_tx_free *free = data;
__le32 *tx_info = (__le32 *)(data + sizeof(*free));
struct mt76_dev *mdev = &dev->mt76;
struct mt76_txwi_cache *txwi;
struct ieee80211_sta *sta = NULL;
struct mt76_wcid *wcid = NULL;
struct sk_buff *skb, *tmp;
void *end = data + len;
LIST_HEAD(free_list);
bool wake = false;
u8 i, count;
/* clean DMA queues and unmap buffers first */
mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_PSD], false);
mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_BE], false);
count = le16_get_bits(free->ctrl, MT_TX_FREE_MSDU_CNT);
if (WARN_ON_ONCE((void *)&tx_info[count] > end))
return;
for (i = 0; i < count; i++) {
u32 msdu, info = le32_to_cpu(tx_info[i]);
u8 stat;
/* 1'b1: new wcid pair.
* 1'b0: msdu_id with the same 'wcid pair' as above.
*/
if (info & MT_TX_FREE_PAIR) {
struct mt792x_link_sta *mlink;
u16 idx;
count++;
idx = FIELD_GET(MT_TX_FREE_WLAN_ID, info);
wcid = mt76_wcid_ptr(dev, idx);
sta = wcid_to_sta(wcid);
if (!sta)
continue;
mlink = container_of(wcid, struct mt792x_link_sta, wcid);
mt76_wcid_add_poll(&dev->mt76, &mlink->wcid);
continue;
}
msdu = FIELD_GET(MT_TX_FREE_MSDU_ID, info);
stat = FIELD_GET(MT_TX_FREE_STATUS, info);
if (wcid) {
u32 count = FIELD_GET(MT_TX_FREE_COUNT, info);
wcid->stats.tx_retries += count ? count - 1 : 0;
wcid->stats.tx_failed += !!stat;
}
txwi = mt76_token_release(mdev, msdu, &wake);
if (!txwi)
continue;
mt76_connac2_txwi_free(mdev, txwi, sta, &free_list);
}
if (wake)
mt76_set_tx_blocked(&dev->mt76, false);
list_for_each_entry_safe(skb, tmp, &free_list, list) {
skb_list_del_init(skb);
napi_consume_skb(skb, 1);
}
rcu_read_lock();
mt7921_mac_sta_poll(dev);
rcu_read_unlock();
mt76_worker_schedule(&dev->mt76.tx_worker);
}
bool mt7921_rx_check(struct mt76_dev *mdev, void *data, int len)
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
__le32 *rxd = (__le32 *)data;
__le32 *end = (__le32 *)&rxd[len / 4];
enum rx_pkt_type type;
type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE);
switch (type) {
case PKT_TYPE_TXRX_NOTIFY:
if (!mt76_is_mmio(mdev))
return false;
mt7921_mac_tx_free(dev, data, len);
return false;
case PKT_TYPE_TXS:
for (rxd += 2; rxd + 8 <= end; rxd += 8)
mt7921_mac_add_txs(dev, rxd);
return false;
default:
return true;
}
}
EXPORT_SYMBOL_GPL(mt7921_rx_check);
void mt7921_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
struct sk_buff *skb, u32 *info)
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
__le32 *rxd = (__le32 *)skb->data;
__le32 *end = (__le32 *)&skb->data[skb->len];
enum rx_pkt_type type;
u16 flag;
type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE);
flag = le32_get_bits(rxd[0], MT_RXD0_PKT_FLAG);
if (type == PKT_TYPE_RX_EVENT && flag == 0x1)
type = PKT_TYPE_NORMAL_MCU;
switch (type) {
case PKT_TYPE_TXRX_NOTIFY:
if (!mt76_is_mmio(mdev)) {
napi_consume_skb(skb, 1);
break;
}
mt7921_mac_tx_free(dev, skb->data, skb->len);
napi_consume_skb(skb, 1);
break;
case PKT_TYPE_RX_EVENT:
mt7921_mcu_rx_event(dev, skb);
break;
case PKT_TYPE_TXS:
for (rxd += 2; rxd + 8 <= end; rxd += 8)
mt7921_mac_add_txs(dev, rxd);
dev_kfree_skb(skb);
break;
case PKT_TYPE_NORMAL_MCU:
case PKT_TYPE_NORMAL:
if (!mt7921_mac_fill_rx(dev, skb)) {
mt76_rx(&dev->mt76, q, skb);
return;
}
fallthrough;
default:
dev_kfree_skb(skb);
break;
}
}
EXPORT_SYMBOL_GPL(mt7921_queue_rx_skb);
static void
mt7921_vif_connect_iter(void *priv, u8 *mac,
struct ieee80211_vif *vif)
{
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
struct mt792x_dev *dev = mvif->phy->dev;
struct ieee80211_hw *hw = mt76_hw(dev);
if (vif->type == NL80211_IFTYPE_STATION)
ieee80211_disconnect(vif, true);
mt76_connac_mcu_uni_add_dev(&dev->mphy, &vif->bss_conf,
&mvif->bss_conf.mt76,
&mvif->sta.deflink.wcid, true);
mt7921_mcu_set_tx(dev, vif);
if (vif->type == NL80211_IFTYPE_AP) {
mt76_connac_mcu_uni_add_bss(dev->phy.mt76, vif, &mvif->sta.deflink.wcid,
true, NULL);
mt7921_mcu_sta_update(dev, NULL, vif, true,
MT76_STA_INFO_STATE_NONE);
mt7921_mcu_uni_add_beacon_offload(dev, hw, vif, true);
}
}
/* system error recovery */
void mt7921_mac_reset_work(struct work_struct *work)
{
struct mt792x_dev *dev = container_of(work, struct mt792x_dev,
reset_work);
struct ieee80211_hw *hw = mt76_hw(dev);
struct mt76_connac_pm *pm = &dev->pm;
int i, ret;
dev_dbg(dev->mt76.dev, "chip reset\n");
set_bit(MT76_RESET, &dev->mphy.state);
dev->hw_full_reset = true;
ieee80211_stop_queues(hw);
cancel_delayed_work_sync(&dev->mphy.mac_work);
cancel_delayed_work_sync(&pm->ps_work);
cancel_work_sync(&pm->wake_work);
for (i = 0; i < 10; i++) {
mutex_lock(&dev->mt76.mutex);
ret = mt792x_dev_reset(dev);
mutex_unlock(&dev->mt76.mutex);
if (!ret)
break;
}
if ((mt76_is_sdio(&dev->mt76) || mt76_is_usb(&dev->mt76)) &&
atomic_read(&dev->mt76.bus_hung))
return;
if (i == 10)
dev_err(dev->mt76.dev, "chip reset failed\n");
if (test_and_clear_bit(MT76_HW_SCANNING, &dev->mphy.state)) {
struct cfg80211_scan_info info = {
.aborted = true,
};
ieee80211_scan_completed(dev->mphy.hw, &info);
}
dev->hw_full_reset = false;
clear_bit(MT76_RESET, &dev->mphy.state);
pm->suspended = false;
ieee80211_wake_queues(hw);
ieee80211_iterate_active_interfaces(hw,
IEEE80211_IFACE_ITER_RESUME_ALL,
mt7921_vif_connect_iter, NULL);
mt76_connac_power_save_sched(&dev->mt76.phy, pm);
mt7921_regd_change(&dev->phy, "00");
}
void mt7921_coredump_work(struct work_struct *work)
{
struct mt792x_dev *dev;
char *dump, *data;
dev = (struct mt792x_dev *)container_of(work, struct mt792x_dev,
coredump.work.work);
if (time_is_after_jiffies(dev->coredump.last_activity +
4 * MT76_CONNAC_COREDUMP_TIMEOUT)) {
queue_delayed_work(dev->mt76.wq, &dev->coredump.work,
MT76_CONNAC_COREDUMP_TIMEOUT);
return;
}
dump = vzalloc(MT76_CONNAC_COREDUMP_SZ);
data = dump;
while (true) {
struct sk_buff *skb;
spin_lock_bh(&dev->mt76.lock);
skb = __skb_dequeue(&dev->coredump.msg_list);
spin_unlock_bh(&dev->mt76.lock);
if (!skb)
break;
skb_pull(skb, sizeof(struct mt76_connac2_mcu_rxd));
if (!dump || data + skb->len - dump > MT76_CONNAC_COREDUMP_SZ) {
dev_kfree_skb(skb);
continue;
}
memcpy(data, skb->data, skb->len);
data += skb->len;
dev_kfree_skb(skb);
}
if (dump)
dev_coredumpv(dev->mt76.dev, dump, MT76_CONNAC_COREDUMP_SZ,
GFP_KERNEL);
mt792x_reset(&dev->mt76);
}
/* usb_sdio */
static void
mt7921_usb_sdio_write_txwi(struct mt792x_dev *dev, struct mt76_wcid *wcid,
enum mt76_txq_id qid, struct ieee80211_sta *sta,
struct ieee80211_key_conf *key, int pid,
struct sk_buff *skb)
{
__le32 *txwi = (__le32 *)(skb->data - MT_SDIO_TXD_SIZE);
memset(txwi, 0, MT_SDIO_TXD_SIZE);
mt76_connac2_mac_write_txwi(&dev->mt76, txwi, skb, wcid, key, pid, qid, 0);
skb_push(skb, MT_SDIO_TXD_SIZE);
}
int mt7921_usb_sdio_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
enum mt76_txq_id qid, struct mt76_wcid *wcid,
struct ieee80211_sta *sta,
struct mt76_tx_info *tx_info)
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb);
struct ieee80211_key_conf *key = info->control.hw_key;
struct sk_buff *skb = tx_info->skb;
int err, pad, pktid, type;
if (unlikely(tx_info->skb->len <= ETH_HLEN))
return -EINVAL;
err = skb_cow_head(skb, MT_SDIO_TXD_SIZE + MT_SDIO_HDR_SIZE);
if (err)
return err;
if (!wcid)
wcid = &dev->mt76.global_wcid;
if (sta) {
struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
if (time_after(jiffies, msta->deflink.last_txs + HZ / 4)) {
info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
msta->deflink.last_txs = jiffies;
}
}
pktid = mt76_tx_status_skb_add(&dev->mt76, wcid, skb);
mt7921_usb_sdio_write_txwi(dev, wcid, qid, sta, key, pktid, skb);
type = mt76_is_sdio(mdev) ? MT7921_SDIO_DATA : 0;
mt792x_skb_add_usb_sdio_hdr(dev, skb, type);
pad = round_up(skb->len, 4) - skb->len;
if (mt76_is_usb(mdev))
pad += 4;
err = mt76_skb_adjust_pad(skb, pad);
if (err)
/* Release pktid in case of error. */
idr_remove(&wcid->pktid, pktid);
return err;
}
EXPORT_SYMBOL_GPL(mt7921_usb_sdio_tx_prepare_skb);
void mt7921_usb_sdio_tx_complete_skb(struct mt76_dev *mdev,
struct mt76_queue_entry *e)
{
__le32 *txwi = (__le32 *)(e->skb->data + MT_SDIO_HDR_SIZE);
unsigned int headroom = MT_SDIO_TXD_SIZE + MT_SDIO_HDR_SIZE;
struct ieee80211_sta *sta;
struct mt76_wcid *wcid;
u16 idx;
idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX);
wcid = __mt76_wcid_ptr(mdev, idx);
sta = wcid_to_sta(wcid);
if (sta && likely(e->skb->protocol != cpu_to_be16(ETH_P_PAE)))
mt76_connac2_tx_check_aggr(sta, txwi);
skb_pull(e->skb, headroom);
mt76_tx_complete_skb(mdev, e->wcid, e->skb);
}
EXPORT_SYMBOL_GPL(mt7921_usb_sdio_tx_complete_skb);
bool mt7921_usb_sdio_tx_status_data(struct mt76_dev *mdev, u8 *update)
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
mt792x_mutex_acquire(dev);
mt7921_mac_sta_poll(dev);
mt792x_mutex_release(dev);
return false;
}
EXPORT_SYMBOL_GPL(mt7921_usb_sdio_tx_status_data);
#if IS_ENABLED(CONFIG_IPV6)
void mt7921_set_ipv6_ns_work(struct work_struct *work)
{
struct mt792x_dev *dev = container_of(work, struct mt792x_dev,
ipv6_ns_work);
struct sk_buff *skb;
int ret = 0;
do {
skb = skb_dequeue(&dev->ipv6_ns_list);
if (!skb)
break;
mt792x_mutex_acquire(dev);
ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
MCU_UNI_CMD(OFFLOAD), true);
mt792x_mutex_release(dev);
} while (!ret);
if (ret)
skb_queue_purge(&dev->ipv6_ns_list);
}
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,120 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2020 MediaTek Inc. */
#ifndef __MT7921_MCU_H
#define __MT7921_MCU_H
#include "../mt76_connac_mcu.h"
struct mt7921_mcu_tx_done_event {
u8 pid;
u8 status;
__le16 seq;
u8 wlan_idx;
u8 tx_cnt;
__le16 tx_rate;
u8 flag;
u8 tid;
u8 rsp_rate;
u8 mcs;
u8 bw;
u8 tx_pwr;
u8 reason;
u8 rsv0[1];
__le32 delay;
__le32 timestamp;
__le32 applied_flag;
u8 txs[28];
u8 rsv1[32];
} __packed;
/* ext event table */
enum {
MCU_EXT_EVENT_RATE_REPORT = 0x87,
};
struct mt7921_mcu_eeprom_info {
__le32 addr;
__le32 valid;
u8 data[MT7921_EEPROM_BLOCK_SIZE];
} __packed;
#define MT_RA_RATE_NSS GENMASK(8, 6)
#define MT_RA_RATE_MCS GENMASK(3, 0)
#define MT_RA_RATE_TX_MODE GENMASK(12, 9)
#define MT_RA_RATE_DCM_EN BIT(4)
#define MT_RA_RATE_BW GENMASK(14, 13)
enum {
MT_EBF = BIT(0), /* explicit beamforming */
MT_IBF = BIT(1) /* implicit beamforming */
};
struct mt7921_mcu_ant_id_config {
u8 ant_id[4];
} __packed;
struct mt7921_txpwr_req {
u8 ver;
u8 action;
__le16 len;
u8 dbdc_idx;
u8 rsv[3];
} __packed;
struct mt7921_txpwr_event {
u8 ver;
u8 action;
__le16 len;
struct mt7921_txpwr txpwr;
} __packed;
struct mt7921_wf_rf_pin_ctrl_event {
u8 result;
u8 value;
} __packed;
enum {
TM_SWITCH_MODE,
TM_SET_AT_CMD,
TM_QUERY_AT_CMD,
};
enum {
MT7921_TM_NORMAL,
MT7921_TM_TESTMODE,
MT7921_TM_ICAP,
MT7921_TM_ICAP_OVERLAP,
MT7921_TM_WIFISPECTRUM,
};
struct mt7921_rftest_cmd {
u8 action;
u8 rsv[3];
__le32 param0;
__le32 param1;
} __packed;
struct mt7921_rftest_evt {
__le32 param0;
__le32 param1;
} __packed;
struct mt7921_clc_info_tlv {
__le16 tag;
__le16 len;
u8 chan_conf; /* BIT(0) : Enable UNII-4
* BIT(1) : Enable UNII-5
* BIT(2) : Enable UNII-6
* BIT(3) : Enable UNII-7
* BIT(4) : Enable UNII-8
*/
u8 rsv[63];
} __packed;
#endif
@@ -0,0 +1,353 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2020 MediaTek Inc. */
#ifndef __MT7921_H
#define __MT7921_H
#include "../mt792x.h"
#include "regs.h"
#define MT7921_MAX_AID 20
#define MT7921_TX_RING_SIZE 2048
#define MT7921_TX_MCU_RING_SIZE 256
#define MT7921_TX_FWDL_RING_SIZE 128
#define MT7921_RX_RING_SIZE 1536
#define MT7921_RX_MCU_RING_SIZE 8
#define MT7921_RX_MCU_WA_RING_SIZE 512
/* MT7902 Rx Ring0 is for both Rx Event and Tx Done Event */
#define MT7902_RX_MCU_RING_SIZE 512
#define MT7921_EEPROM_SIZE 3584
#define MT7921_TOKEN_SIZE 8192
#define MT7921_EEPROM_BLOCK_SIZE 16
#define MT7921_SKU_RATE_NUM 161
#define MT7921_SKU_MAX_DELTA_IDX MT7921_SKU_RATE_NUM
#define MT7921_SKU_TABLE_SIZE (MT7921_SKU_RATE_NUM + 1)
#define MCU_UNI_EVENT_ROC 0x27
#define MCU_UNI_EVENT_CLC 0x80
#define EXT_CMD_RADIO_LED_CTRL_ENABLE 0x1
#define EXT_CMD_RADIO_ON_LED 0x2
#define EXT_CMD_RADIO_OFF_LED 0x3
#define WF_RF_PIN_INIT 0x0
#define WF_RF_PIN_POLL 0x1
enum {
UNI_ROC_ACQUIRE,
UNI_ROC_ABORT,
UNI_ROC_NUM
};
enum mt7921_roc_req {
MT7921_ROC_REQ_JOIN,
MT7921_ROC_REQ_ROC,
MT7921_ROC_REQ_NUM
};
enum {
UNI_EVENT_ROC_GRANT = 0,
UNI_EVENT_ROC_TAG_NUM
};
struct mt7921_realease_info {
__le16 len;
u8 pad_len;
u8 tag;
} __packed;
struct mt7921_fw_features {
u8 segment;
u8 data;
u8 rsv[14];
} __packed;
struct mt7921_roc_grant_tlv {
__le16 tag;
__le16 len;
u8 bss_idx;
u8 tokenid;
u8 status;
u8 primarychannel;
u8 rfsco;
u8 rfband;
u8 channelwidth;
u8 centerfreqseg1;
u8 centerfreqseg2;
u8 reqtype;
u8 dbdcband;
u8 rsv[1];
__le32 max_interval;
} __packed;
enum mt7921_sdio_pkt_type {
MT7921_SDIO_TXD,
MT7921_SDIO_DATA,
MT7921_SDIO_CMD,
MT7921_SDIO_FWDL,
};
struct mt7921_sdio_intr {
u32 isr;
struct {
u32 wtqcr[16];
} tx;
struct {
u16 num[2];
u16 len0[16];
u16 len1[128];
} rx;
u32 rec_mb[2];
} __packed;
#define to_rssi(field, rxv) ((FIELD_GET(field, rxv) - 220) / 2)
#define to_rcpi(rssi) (2 * (rssi) + 220)
enum mt7921_txq_id {
MT7921_TXQ_BAND0,
MT7921_TXQ_BAND1,
MT7921_TXQ_FWDL = 16,
MT7921_TXQ_MCU_WM,
};
enum mt7921_rxq_id {
MT7921_RXQ_BAND0 = 0,
MT7921_RXQ_BAND1,
MT7921_RXQ_MCU_WM = 0,
};
/* MT7902 assigns its MCU-WM TXQ at index 15 */
enum mt7902_txq_id {
MT7902_TXQ_MCU_WM = 15,
};
struct mt7921_dma_layout {
u8 mcu_wm_txq;
u16 mcu_rxdone_ring_size;
bool has_mcu_wa;
};
enum {
MT7921_CLC_POWER,
MT7921_CLC_CHAN,
MT7921_CLC_MAX_NUM,
};
struct mt7921_clc_rule {
u8 alpha2[2];
u8 type[2];
__le16 len;
u8 data[];
} __packed;
struct mt7921_clc {
__le32 len;
u8 idx;
u8 ver;
u8 nr_country;
u8 type;
u8 rsv[8];
u8 data[];
} __packed;
enum mt7921_eeprom_field {
MT_EE_CHIP_ID = 0x000,
MT_EE_VERSION = 0x002,
MT_EE_MAC_ADDR = 0x004,
MT_EE_WIFI_CONF = 0x07c,
MT_EE_HW_TYPE = 0x55b,
__MT_EE_MAX = 0x9ff
};
#define MT_EE_HW_TYPE_ENCAP BIT(0)
enum {
TXPWR_USER,
TXPWR_EEPROM,
TXPWR_MAC,
TXPWR_MAX_NUM,
};
struct mt7921_txpwr {
u8 ch;
u8 rsv[3];
struct {
u8 ch;
u8 cck[4];
u8 ofdm[8];
u8 ht20[8];
u8 ht40[9];
u8 vht20[12];
u8 vht40[12];
u8 vht80[12];
u8 vht160[12];
u8 he26[12];
u8 he52[12];
u8 he106[12];
u8 he242[12];
u8 he484[12];
u8 he996[12];
u8 he996x2[12];
} data[TXPWR_MAX_NUM];
};
extern const struct ieee80211_ops mt7921_ops;
u32 mt7921_reg_map(struct mt792x_dev *dev, u32 addr);
int __mt7921_start(struct mt792x_phy *phy);
int mt7921_register_device(struct mt792x_dev *dev);
void mt7921_unregister_device(struct mt792x_dev *dev);
int mt7921_run_firmware(struct mt792x_dev *dev);
int mt7921_set_channel(struct mt76_phy *mphy);
int mt7921_mcu_set_bss_pm(struct mt792x_dev *dev, struct ieee80211_vif *vif,
bool enable);
int mt7921_mcu_sta_update(struct mt792x_dev *dev, struct ieee80211_sta *sta,
struct ieee80211_vif *vif, bool enable,
enum mt76_sta_info_state state);
int mt7921_mcu_set_chan_info(struct mt792x_phy *phy, int cmd);
int mt7921_mcu_set_tx(struct mt792x_dev *dev, struct ieee80211_vif *vif);
int mt7921_mcu_set_eeprom(struct mt792x_dev *dev);
int mt7921_mcu_get_rx_rate(struct mt792x_phy *phy, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct rate_info *rate);
int mt7921_mcu_fw_log_2_host(struct mt792x_dev *dev, u8 ctrl);
void mt7921_mcu_rx_event(struct mt792x_dev *dev, struct sk_buff *skb);
int mt7921_mcu_set_rxfilter(struct mt792x_dev *dev, u32 fif,
u8 bit_op, u32 bit_map);
int mt7921_mcu_radio_led_ctrl(struct mt792x_dev *dev, u8 value);
int mt7921_mcu_wf_rf_pin_ctrl(struct mt792x_phy *phy, u8 action);
static inline u32
mt7921_reg_map_l1(struct mt792x_dev *dev, u32 addr)
{
u32 offset = FIELD_GET(MT_HIF_REMAP_L1_OFFSET, addr);
u32 base = FIELD_GET(MT_HIF_REMAP_L1_BASE, addr);
mt76_rmw_field(dev, MT_HIF_REMAP_L1, MT_HIF_REMAP_L1_MASK, base);
/* use read to push write */
mt76_rr(dev, MT_HIF_REMAP_L1);
return MT_HIF_REMAP_BASE_L1 + offset;
}
static inline u32
mt7921_l1_rr(struct mt792x_dev *dev, u32 addr)
{
return mt76_rr(dev, mt7921_reg_map_l1(dev, addr));
}
static inline void
mt7921_l1_wr(struct mt792x_dev *dev, u32 addr, u32 val)
{
mt76_wr(dev, mt7921_reg_map_l1(dev, addr), val);
}
static inline u32
mt7921_l1_rmw(struct mt792x_dev *dev, u32 addr, u32 mask, u32 val)
{
val |= mt7921_l1_rr(dev, addr) & ~mask;
mt7921_l1_wr(dev, addr, val);
return val;
}
#define mt7921_l1_set(dev, addr, val) mt7921_l1_rmw(dev, addr, 0, val)
#define mt7921_l1_clear(dev, addr, val) mt7921_l1_rmw(dev, addr, val, 0)
int mt7921_mac_init(struct mt792x_dev *dev);
bool mt7921_mac_wtbl_update(struct mt792x_dev *dev, int idx, u32 mask);
int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
int mt7921_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, enum mt76_sta_event ev);
void mt7921_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
void mt7921_mac_reset_work(struct work_struct *work);
int mt7921e_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
enum mt76_txq_id qid, struct mt76_wcid *wcid,
struct ieee80211_sta *sta,
struct mt76_tx_info *tx_info);
bool mt7921_rx_check(struct mt76_dev *mdev, void *data, int len);
void mt7921_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
struct sk_buff *skb, u32 *info);
void mt7921_stats_work(struct work_struct *work);
void mt7921_set_stream_he_caps(struct mt792x_phy *phy);
int mt7921_init_debugfs(struct mt792x_dev *dev);
int mt7921_mcu_set_beacon_filter(struct mt792x_dev *dev,
struct ieee80211_vif *vif,
bool enable);
int mt7921_mcu_uni_tx_ba(struct mt792x_dev *dev,
struct ieee80211_ampdu_params *params,
bool enable);
int mt7921_mcu_uni_rx_ba(struct mt792x_dev *dev,
struct ieee80211_ampdu_params *params,
bool enable);
void mt7921_scan_work(struct work_struct *work);
void mt7921_roc_work(struct work_struct *work);
void mt7921_csa_work(struct work_struct *work);
int mt7921_mcu_uni_bss_ps(struct mt792x_dev *dev, struct ieee80211_vif *vif);
void mt7921_coredump_work(struct work_struct *work);
int mt7921_get_txpwr_info(struct mt792x_dev *dev, struct mt7921_txpwr *txpwr);
int mt7921_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void *data, int len);
int mt7921_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
struct netlink_callback *cb, void *data, int len);
int mt7921_mcu_parse_response(struct mt76_dev *mdev, int cmd,
struct sk_buff *skb, int seq);
int mt7921e_driver_own(struct mt792x_dev *dev);
int mt7921e_mac_reset(struct mt792x_dev *dev);
int mt7921e_mcu_init(struct mt792x_dev *dev);
int mt7921s_wfsys_reset(struct mt792x_dev *dev);
int mt7921s_mac_reset(struct mt792x_dev *dev);
int mt7921s_init_reset(struct mt792x_dev *dev);
int mt7921s_mcu_init(struct mt792x_dev *dev);
int mt7921s_mcu_drv_pmctrl(struct mt792x_dev *dev);
int mt7921s_mcu_fw_pmctrl(struct mt792x_dev *dev);
void mt7921_mac_add_txs(struct mt792x_dev *dev, void *data);
void mt7921_set_runtime_pm(struct mt792x_dev *dev);
void mt7921_mcu_set_suspend_iter(void *priv, u8 *mac,
struct ieee80211_vif *vif);
void mt7921_set_ipv6_ns_work(struct work_struct *work);
int mt7921_mcu_set_sniffer(struct mt792x_dev *dev, struct ieee80211_vif *vif,
bool enable);
int mt7921_mcu_config_sniffer(struct mt792x_vif *vif,
struct ieee80211_chanctx_conf *ctx);
int mt7921_mcu_get_temperature(struct mt792x_phy *phy);
int mt7921_usb_sdio_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
enum mt76_txq_id qid, struct mt76_wcid *wcid,
struct ieee80211_sta *sta,
struct mt76_tx_info *tx_info);
void mt7921_usb_sdio_tx_complete_skb(struct mt76_dev *mdev,
struct mt76_queue_entry *e);
bool mt7921_usb_sdio_tx_status_data(struct mt76_dev *mdev, u8 *update);
/* usb */
int mt7921_mcu_uni_add_beacon_offload(struct mt792x_dev *dev,
struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
bool enable);
int mt7921_set_tx_sar_pwr(struct ieee80211_hw *hw,
const struct cfg80211_sar_specs *sar);
int mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
enum environment_cap env_cap);
int mt7921_mcu_set_roc(struct mt792x_phy *phy, struct mt792x_vif *vif,
struct ieee80211_channel *chan, int duration,
enum mt7921_roc_req type, u8 token_id);
int mt7921_mcu_abort_roc(struct mt792x_phy *phy, struct mt792x_vif *vif,
u8 token_id);
void mt7921_roc_abort_sync(struct mt792x_dev *dev);
int mt7921_mcu_set_rssimonitor(struct mt792x_dev *dev, struct ieee80211_vif *vif);
#endif
@@ -0,0 +1,633 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/of.h>
#include "mt7921.h"
#include "../mt76_connac2_mac.h"
#include "../dma.h"
#include "mcu.h"
#include "regd.h"
static const struct pci_device_id mt7921_pci_device_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7961),
.driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM },
{ PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7922),
.driver_data = (kernel_ulong_t)MT7922_FIRMWARE_WM },
{ PCI_DEVICE(PCI_VENDOR_ID_ITTIM, 0x7922),
.driver_data = (kernel_ulong_t)MT7922_FIRMWARE_WM },
{ PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0608),
.driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM },
{ PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0616),
.driver_data = (kernel_ulong_t)MT7922_FIRMWARE_WM },
{ PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7920),
.driver_data = (kernel_ulong_t)MT7920_FIRMWARE_WM },
{ PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7902),
.driver_data = (kernel_ulong_t)MT7902_FIRMWARE_WM },
{ },
};
static bool mt7921_disable_aspm;
module_param_named(disable_aspm, mt7921_disable_aspm, bool, 0644);
MODULE_PARM_DESC(disable_aspm, "disable PCI ASPM support");
static int mt7921e_init_reset(struct mt792x_dev *dev)
{
return mt792x_wpdma_reset(dev, true);
}
static void mt7921e_unregister_device(struct mt792x_dev *dev)
{
int i;
struct mt76_connac_pm *pm = &dev->pm;
struct ieee80211_hw *hw = mt76_hw(dev);
if (dev->phy.chip_cap & MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN)
wiphy_rfkill_stop_polling(hw->wiphy);
cancel_work_sync(&dev->init_work);
mt76_unregister_device(&dev->mt76);
mt76_for_each_q_rx(&dev->mt76, i)
napi_disable(&dev->mt76.napi[i]);
cancel_delayed_work_sync(&pm->ps_work);
cancel_work_sync(&pm->wake_work);
cancel_work_sync(&dev->reset_work);
mt76_connac2_tx_token_put(&dev->mt76);
__mt792x_mcu_drv_pmctrl(dev);
mt792x_dma_cleanup(dev);
mt792x_wfsys_reset(dev);
skb_queue_purge(&dev->mt76.mcu.res_q);
tasklet_disable(&dev->mt76.irq_tasklet);
}
static u32 __mt7921_reg_addr(struct mt792x_dev *dev, u32 addr)
{
static const struct mt76_connac_reg_map fixed_map[] = {
{ 0x820d0000, 0x30000, 0x10000 }, /* WF_LMAC_TOP (WF_WTBLON) */
{ 0x820ed000, 0x24800, 0x00800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */
{ 0x820e4000, 0x21000, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */
{ 0x820e7000, 0x21e00, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */
{ 0x820eb000, 0x24200, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */
{ 0x820e2000, 0x20800, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */
{ 0x820e3000, 0x20c00, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */
{ 0x820e5000, 0x21400, 0x00800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */
{ 0x00400000, 0x80000, 0x10000 }, /* WF_MCU_SYSRAM */
{ 0x00410000, 0x90000, 0x10000 }, /* WF_MCU_SYSRAM (configure register) */
{ 0x40000000, 0x70000, 0x10000 }, /* WF_UMAC_SYSRAM */
{ 0x54000000, 0x02000, 0x01000 }, /* WFDMA PCIE0 MCU DMA0 */
{ 0x55000000, 0x03000, 0x01000 }, /* WFDMA PCIE0 MCU DMA1 */
{ 0x58000000, 0x06000, 0x01000 }, /* WFDMA PCIE1 MCU DMA0 (MEM_DMA) */
{ 0x59000000, 0x07000, 0x01000 }, /* WFDMA PCIE1 MCU DMA1 */
{ 0x7c000000, 0xf0000, 0x10000 }, /* CONN_INFRA */
{ 0x7c020000, 0xd0000, 0x10000 }, /* CONN_INFRA, WFDMA */
{ 0x7c060000, 0xe0000, 0x10000 }, /* CONN_INFRA, conn_host_csr_top */
{ 0x80020000, 0xb0000, 0x10000 }, /* WF_TOP_MISC_OFF */
{ 0x81020000, 0xc0000, 0x10000 }, /* WF_TOP_MISC_ON */
{ 0x820c0000, 0x08000, 0x04000 }, /* WF_UMAC_TOP (PLE) */
{ 0x820c8000, 0x0c000, 0x02000 }, /* WF_UMAC_TOP (PSE) */
{ 0x820cc000, 0x0e000, 0x01000 }, /* WF_UMAC_TOP (PP) */
{ 0x820cd000, 0x0f000, 0x01000 }, /* WF_MDP_TOP */
{ 0x74030000, 0x10000, 0x10000 }, /* PCIE_MAC_IREG */
{ 0x820ce000, 0x21c00, 0x00200 }, /* WF_LMAC_TOP (WF_SEC) */
{ 0x820cf000, 0x22000, 0x01000 }, /* WF_LMAC_TOP (WF_PF) */
{ 0x820e0000, 0x20000, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */
{ 0x820e1000, 0x20400, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */
{ 0x820e9000, 0x23400, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */
{ 0x820ea000, 0x24000, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */
{ 0x820ec000, 0x24600, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_INT) */
{ 0x820f0000, 0xa0000, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */
{ 0x820f1000, 0xa0600, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */
{ 0x820f2000, 0xa0800, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */
{ 0x820f3000, 0xa0c00, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */
{ 0x820f4000, 0xa1000, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */
{ 0x820f5000, 0xa1400, 0x00800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */
{ 0x820f7000, 0xa1e00, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */
{ 0x820f9000, 0xa3400, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */
{ 0x820fa000, 0xa4000, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */
{ 0x820fb000, 0xa4200, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */
{ 0x820fc000, 0xa4600, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_INT) */
{ 0x820fd000, 0xa4800, 0x00800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */
};
int i;
if (addr < 0x100000)
return addr;
for (i = 0; i < ARRAY_SIZE(fixed_map); i++) {
u32 ofs;
if (addr < fixed_map[i].phys)
continue;
ofs = addr - fixed_map[i].phys;
if (ofs > fixed_map[i].size)
continue;
return fixed_map[i].maps + ofs;
}
if ((addr >= 0x18000000 && addr < 0x18c00000) ||
(addr >= 0x70000000 && addr < 0x78000000) ||
(addr >= 0x7c000000 && addr < 0x7c400000))
return mt7921_reg_map_l1(dev, addr);
dev_err(dev->mt76.dev, "Access currently unsupported address %08x\n",
addr);
return 0;
}
static u32 mt7921_rr(struct mt76_dev *mdev, u32 offset)
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
u32 addr = __mt7921_reg_addr(dev, offset);
return dev->bus_ops->rr(mdev, addr);
}
static void mt7921_wr(struct mt76_dev *mdev, u32 offset, u32 val)
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
u32 addr = __mt7921_reg_addr(dev, offset);
dev->bus_ops->wr(mdev, addr, val);
}
static u32 mt7921_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val)
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
u32 addr = __mt7921_reg_addr(dev, offset);
return dev->bus_ops->rmw(mdev, addr, mask, val);
}
static int mt7921_dma_init(struct mt792x_dev *dev)
{
struct mt7921_dma_layout layout = {
/* General case: MT7921 / MT7922 /MT7920 */
.mcu_wm_txq = MT7921_TXQ_MCU_WM,
.mcu_rxdone_ring_size = MT7921_RX_MCU_RING_SIZE,
.has_mcu_wa = true,
};
bool is_mt7902;
int ret;
is_mt7902 = mt7921_l1_rr(dev, MT_HW_CHIPID) == 0x7902;
/*
* MT7902 special case:
* - MCU-WM TXQ uses index 15
* - RX Ring0 is larger and shared for event/TX-done
* - MT7902 does not use the MCU_WA ring
*/
if (is_mt7902) {
layout.mcu_wm_txq = MT7902_TXQ_MCU_WM;
layout.mcu_rxdone_ring_size = MT7902_RX_MCU_RING_SIZE;
layout.has_mcu_wa = false;
}
mt76_dma_attach(&dev->mt76);
ret = mt792x_dma_disable(dev, true);
if (ret)
return ret;
/* init tx queue */
ret = mt76_connac_init_tx_queues(dev->phy.mt76, MT7921_TXQ_BAND0,
MT7921_TX_RING_SIZE,
MT_TX_RING_BASE, NULL, 0);
if (ret)
return ret;
mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, 0x4);
/* command to WM */
ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, layout.mcu_wm_txq,
MT7921_TX_MCU_RING_SIZE, MT_TX_RING_BASE);
if (ret)
return ret;
/* firmware download */
ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL, MT7921_TXQ_FWDL,
MT7921_TX_FWDL_RING_SIZE, MT_TX_RING_BASE);
if (ret)
return ret;
/* event from WM before firmware download */
ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU],
MT7921_RXQ_MCU_WM,
layout.mcu_rxdone_ring_size,
MT_RX_BUF_SIZE, MT_RX_EVENT_RING_BASE);
if (ret)
return ret;
if (layout.has_mcu_wa) {
/* Change mcu queue after firmware download */
ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA],
MT7921_RXQ_MCU_WM,
MT7921_RX_MCU_WA_RING_SIZE,
MT_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);
if (ret)
return ret;
ret = mt76_init_queues(dev, mt792x_poll_rx);
if (ret < 0)
return ret;
netif_napi_add_tx(dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
mt792x_poll_tx);
napi_enable(&dev->mt76.tx_napi);
return mt792x_dma_enable(dev);
}
static int mt7921_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
static const struct mt76_driver_ops drv_ops = {
/* txwi_size = txd size + txp size */
.txwi_size = MT_TXD_SIZE + sizeof(struct mt76_connac_hw_txp),
.drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ |
MT_DRV_AMSDU_OFFLOAD,
.survey_flags = SURVEY_INFO_TIME_TX |
SURVEY_INFO_TIME_RX |
SURVEY_INFO_TIME_BSS_RX,
.token_size = MT7921_TOKEN_SIZE,
.tx_prepare_skb = mt7921e_tx_prepare_skb,
.tx_complete_skb = mt76_connac_tx_complete_skb,
.rx_check = mt7921_rx_check,
.rx_skb = mt7921_queue_rx_skb,
.rx_poll_complete = mt792x_rx_poll_complete,
.sta_add = mt7921_mac_sta_add,
.sta_event = mt7921_mac_sta_event,
.sta_remove = mt7921_mac_sta_remove,
.update_survey = mt792x_update_channel,
.set_channel = mt7921_set_channel,
};
static const struct mt792x_hif_ops mt7921_pcie_ops = {
.init_reset = mt7921e_init_reset,
.reset = mt7921e_mac_reset,
.mcu_init = mt7921e_mcu_init,
.drv_own = mt792xe_mcu_drv_pmctrl,
.fw_own = mt792xe_mcu_fw_pmctrl,
};
static const struct mt792x_irq_map irq_map = {
.host_irq_enable = MT_WFDMA0_HOST_INT_ENA,
.tx = {
.all_complete_mask = MT_INT_TX_DONE_ALL,
.mcu_complete_mask = MT_INT_TX_DONE_MCU,
},
.rx = {
.data_complete_mask = MT_INT_RX_DONE_DATA,
.wm_complete_mask = MT_INT_RX_DONE_WM,
.wm2_complete_mask = MT_INT_RX_DONE_WM2,
},
};
struct ieee80211_ops *ops;
struct mt76_bus_ops *bus_ops;
struct mt792x_dev *dev;
struct mt76_dev *mdev;
void __iomem *regs;
u16 cmd, chipid;
u8 features;
int ret;
ret = pcim_enable_device(pdev);
if (ret)
return ret;
pci_read_config_word(pdev, PCI_COMMAND, &cmd);
if (!(cmd & PCI_COMMAND_MEMORY)) {
cmd |= PCI_COMMAND_MEMORY;
pci_write_config_word(pdev, PCI_COMMAND, cmd);
}
pci_set_master(pdev);
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
if (ret < 0)
return ret;
ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
if (ret)
goto err_free_pci_vec;
if (mt7921_disable_aspm)
mt76_pci_disable_aspm(pdev);
ops = mt792x_get_mac80211_ops(&pdev->dev, &mt7921_ops,
(void *)id->driver_data, &features);
if (!ops) {
ret = -ENOMEM;
goto err_free_pci_vec;
}
mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), ops, &drv_ops);
if (!mdev) {
ret = -ENOMEM;
goto err_free_pci_vec;
}
pci_set_drvdata(pdev, mdev);
dev = container_of(mdev, struct mt792x_dev, mt76);
regs = pcim_iomap_region(pdev, 0, pci_name(pdev));
if (IS_ERR(regs)) {
ret = PTR_ERR(regs);
goto err_free_dev;
}
dev->fw_features = features;
dev->hif_ops = &mt7921_pcie_ops;
dev->irq_map = &irq_map;
mt76_mmio_init(&dev->mt76, regs);
if (id->device == 0x7902) {
struct mt792x_irq_map *map;
/* MT7902 needs a mutable copy because wm2_complete_mask differs */
map = devm_kmemdup(&pdev->dev, &irq_map,
sizeof(irq_map), GFP_KERNEL);
if (!map) {
ret = -ENOMEM;
goto err_free_dev;
}
map->rx.wm2_complete_mask = 0;
dev->irq_map = map;
}
tasklet_init(&mdev->irq_tasklet, mt792x_irq_tasklet, (unsigned long)dev);
dev->phy.dev = dev;
dev->phy.mt76 = &dev->mt76.phy;
dev->mt76.phy.priv = &dev->phy;
dev->bus_ops = dev->mt76.bus;
bus_ops = devm_kmemdup(dev->mt76.dev, dev->bus_ops, sizeof(*bus_ops),
GFP_KERNEL);
if (!bus_ops) {
ret = -ENOMEM;
goto err_free_dev;
}
bus_ops->rr = mt7921_rr;
bus_ops->wr = mt7921_wr;
bus_ops->rmw = mt7921_rmw;
dev->mt76.bus = bus_ops;
if (!mt7921_disable_aspm && mt76_pci_aspm_supported(pdev))
dev->aspm_supported = true;
ret = mt792xe_mcu_fw_pmctrl(dev);
if (ret)
goto err_free_dev;
ret = __mt792xe_mcu_drv_pmctrl(dev);
if (ret)
goto err_free_dev;
chipid = mt7921_l1_rr(dev, MT_HW_CHIPID);
if (chipid == 0x7961 && (mt7921_l1_rr(dev, MT_HW_BOUND) & BIT(7)))
chipid = 0x7920;
mdev->rev = (chipid << 16) |
(mt7921_l1_rr(dev, MT_HW_REV) & 0xff);
dev_info(mdev->dev, "ASIC revision: %04x\n", mdev->rev);
ret = mt792x_wfsys_reset(dev);
if (ret)
goto err_free_dev;
mt76_wr(dev, irq_map.host_irq_enable, 0);
mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
ret = devm_request_irq(mdev->dev, pdev->irq, mt792x_irq_handler,
IRQF_SHARED, KBUILD_MODNAME, dev);
if (ret)
goto err_free_dev;
ret = mt7921_dma_init(dev);
if (ret)
goto err_free_irq;
ret = mt7921_register_device(dev);
if (ret)
goto err_free_irq;
if (of_property_read_bool(dev->mt76.dev->of_node, "wakeup-source"))
device_init_wakeup(dev->mt76.dev, true);
return 0;
err_free_irq:
devm_free_irq(&pdev->dev, pdev->irq, dev);
err_free_dev:
mt76_free_device(&dev->mt76);
err_free_pci_vec:
pci_free_irq_vectors(pdev);
return ret;
}
static void mt7921_pci_remove(struct pci_dev *pdev)
{
struct mt76_dev *mdev = pci_get_drvdata(pdev);
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
if (of_property_read_bool(dev->mt76.dev->of_node, "wakeup-source"))
device_init_wakeup(dev->mt76.dev, false);
mt7921e_unregister_device(dev);
set_bit(MT76_REMOVED, &mdev->phy.state);
devm_free_irq(&pdev->dev, pdev->irq, dev);
mt76_free_device(&dev->mt76);
pci_free_irq_vectors(pdev);
}
static int mt7921_pci_suspend(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct mt76_dev *mdev = pci_get_drvdata(pdev);
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
struct mt76_connac_pm *pm = &dev->pm;
int i, err;
pm->suspended = true;
flush_work(&dev->reset_work);
cancel_delayed_work_sync(&pm->ps_work);
cancel_work_sync(&pm->wake_work);
mt7921_roc_abort_sync(dev);
err = mt792x_mcu_drv_pmctrl(dev);
if (err < 0)
goto restore_suspend;
wait_event_timeout(dev->wait,
!dev->regd_in_progress, 5 * HZ);
err = mt7921_mcu_radio_led_ctrl(dev, EXT_CMD_RADIO_OFF_LED);
if (err < 0)
goto restore_suspend;
err = mt76_connac_mcu_set_hif_suspend(mdev, true, true);
if (err)
goto restore_suspend;
/* always enable deep sleep during suspend to reduce
* power consumption
*/
mt76_connac_mcu_set_deep_sleep(&dev->mt76, true);
napi_disable(&mdev->tx_napi);
mt76_worker_disable(&mdev->tx_worker);
mt76_for_each_q_rx(mdev, i) {
napi_disable(&mdev->napi[i]);
}
/* wait until dma is idle */
mt76_poll(dev, MT_WFDMA0_GLO_CFG,
MT_WFDMA0_GLO_CFG_TX_DMA_BUSY |
MT_WFDMA0_GLO_CFG_RX_DMA_BUSY, 0, 1000);
/* put dma disabled */
mt76_clear(dev, MT_WFDMA0_GLO_CFG,
MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN);
/* disable interrupt */
mt76_wr(dev, dev->irq_map->host_irq_enable, 0);
mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0);
synchronize_irq(pdev->irq);
tasklet_kill(&mdev->irq_tasklet);
err = mt792x_mcu_fw_pmctrl(dev);
if (err)
goto restore_napi;
return 0;
restore_napi:
mt76_for_each_q_rx(mdev, i) {
napi_enable(&mdev->napi[i]);
}
napi_enable(&mdev->tx_napi);
if (!pm->ds_enable)
mt76_connac_mcu_set_deep_sleep(&dev->mt76, false);
mt76_connac_mcu_set_hif_suspend(mdev, false, true);
restore_suspend:
pm->suspended = false;
if (err < 0)
mt792x_reset(&dev->mt76);
return err;
}
static int mt7921_pci_resume(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct mt76_dev *mdev = pci_get_drvdata(pdev);
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
struct mt76_connac_pm *pm = &dev->pm;
int i, err;
err = mt792x_mcu_drv_pmctrl(dev);
if (err < 0)
goto failed;
mt792x_wpdma_reinit_cond(dev);
/* enable interrupt */
mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
mt76_connac_irq_enable(&dev->mt76,
dev->irq_map->tx.all_complete_mask |
MT_INT_RX_DONE_ALL | MT_INT_MCU_CMD);
mt76_set(dev, MT_MCU2HOST_SW_INT_ENA, MT_MCU_CMD_WAKE_RX_PCIE);
/* put dma enabled */
mt76_set(dev, MT_WFDMA0_GLO_CFG,
MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN);
mt76_worker_enable(&mdev->tx_worker);
mt76_for_each_q_rx(mdev, i) {
napi_enable(&mdev->napi[i]);
}
napi_enable(&mdev->tx_napi);
local_bh_disable();
mt76_for_each_q_rx(mdev, i) {
napi_schedule(&mdev->napi[i]);
}
napi_schedule(&mdev->tx_napi);
local_bh_enable();
/* restore previous ds setting */
if (!pm->ds_enable)
mt76_connac_mcu_set_deep_sleep(&dev->mt76, false);
err = mt76_connac_mcu_set_hif_suspend(mdev, false, true);
if (err < 0)
goto failed;
mt7921_mcu_regd_update(dev, mdev->alpha2, dev->country_ie_env);
err = mt7921_mcu_radio_led_ctrl(dev, EXT_CMD_RADIO_ON_LED);
failed:
pm->suspended = false;
if (err < 0)
mt792x_reset(&dev->mt76);
return err;
}
static void mt7921_pci_shutdown(struct pci_dev *pdev)
{
mt7921_pci_remove(pdev);
}
static DEFINE_SIMPLE_DEV_PM_OPS(mt7921_pm_ops, mt7921_pci_suspend, mt7921_pci_resume);
static struct pci_driver mt7921_pci_driver = {
.name = KBUILD_MODNAME,
.id_table = mt7921_pci_device_table,
.probe = mt7921_pci_probe,
.remove = mt7921_pci_remove,
.shutdown = mt7921_pci_shutdown,
.driver.pm = pm_sleep_ptr(&mt7921_pm_ops),
};
module_pci_driver(mt7921_pci_driver);
MODULE_DEVICE_TABLE(pci, mt7921_pci_device_table);
MODULE_FIRMWARE(MT7920_FIRMWARE_WM);
MODULE_FIRMWARE(MT7920_ROM_PATCH);
MODULE_FIRMWARE(MT7921_FIRMWARE_WM);
MODULE_FIRMWARE(MT7921_ROM_PATCH);
MODULE_FIRMWARE(MT7922_FIRMWARE_WM);
MODULE_FIRMWARE(MT7922_ROM_PATCH);
MODULE_FIRMWARE(MT7902_FIRMWARE_WM);
MODULE_FIRMWARE(MT7902_ROM_PATCH);
MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
MODULE_DESCRIPTION("MediaTek MT7921E (PCIe) wireless driver");
MODULE_LICENSE("Dual BSD/GPL");
@@ -0,0 +1,129 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2021 MediaTek Inc. */
#include "mt7921.h"
#include "../dma.h"
#include "../mt76_connac2_mac.h"
int mt7921e_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
enum mt76_txq_id qid, struct mt76_wcid *wcid,
struct ieee80211_sta *sta,
struct mt76_tx_info *tx_info)
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb);
struct ieee80211_key_conf *key = info->control.hw_key;
struct mt76_connac_hw_txp *txp;
struct mt76_txwi_cache *t;
int id, pid;
u8 *txwi = (u8 *)txwi_ptr;
if (unlikely(tx_info->skb->len <= ETH_HLEN))
return -EINVAL;
if (!wcid)
wcid = &dev->mt76.global_wcid;
t = (struct mt76_txwi_cache *)(txwi + mdev->drv->txwi_size);
t->skb = tx_info->skb;
id = mt76_token_consume(mdev, &t);
if (id < 0)
return id;
if (sta) {
struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
if (time_after(jiffies, msta->deflink.last_txs + HZ / 4)) {
info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
msta->deflink.last_txs = jiffies;
}
}
pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb);
mt76_connac2_mac_write_txwi(mdev, txwi_ptr, tx_info->skb, wcid, key,
pid, qid, 0);
txp = (struct mt76_connac_hw_txp *)(txwi + MT_TXD_SIZE);
memset(txp, 0, sizeof(struct mt76_connac_hw_txp));
mt76_connac_write_hw_txp(mdev, tx_info, txp, id);
tx_info->skb = NULL;
return 0;
}
int mt7921e_mac_reset(struct mt792x_dev *dev)
{
int i, err;
mt792xe_mcu_drv_pmctrl(dev);
mt76_connac_free_pending_tx_skbs(&dev->pm, NULL);
mt76_wr(dev, dev->irq_map->host_irq_enable, 0);
mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0);
set_bit(MT76_MCU_RESET, &dev->mphy.state);
wake_up(&dev->mt76.mcu.wait);
skb_queue_purge(&dev->mt76.mcu.res_q);
mt76_txq_schedule_all(&dev->mphy);
mt76_worker_disable(&dev->mt76.tx_worker);
mt76_for_each_q_rx(&dev->mt76, i) {
napi_disable(&dev->mt76.napi[i]);
}
napi_disable(&dev->mt76.tx_napi);
mt76_connac2_tx_token_put(&dev->mt76);
idr_init(&dev->mt76.token);
mt792x_wpdma_reset(dev, true);
mt76_for_each_q_rx(&dev->mt76, i) {
napi_enable(&dev->mt76.napi[i]);
}
local_bh_disable();
mt76_for_each_q_rx(&dev->mt76, i) {
napi_schedule(&dev->mt76.napi[i]);
}
local_bh_enable();
dev->fw_assert = false;
clear_bit(MT76_MCU_RESET, &dev->mphy.state);
mt76_wr(dev, dev->irq_map->host_irq_enable,
dev->irq_map->tx.all_complete_mask |
MT_INT_RX_DONE_ALL | MT_INT_MCU_CMD);
mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
err = mt7921e_driver_own(dev);
if (err)
goto out;
err = mt7921_run_firmware(dev);
if (err)
goto out;
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:
napi_enable(&dev->mt76.tx_napi);
local_bh_disable();
napi_schedule(&dev->mt76.tx_napi);
local_bh_enable();
mt76_worker_enable(&dev->mt76.tx_worker);
return err;
}
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2021 MediaTek Inc. */
#include "mt7921.h"
#include "mcu.h"
int mt7921e_driver_own(struct mt792x_dev *dev)
{
u32 reg = mt7921_reg_map_l1(dev, MT_TOP_LPCR_HOST_BAND0);
mt76_wr(dev, reg, MT_TOP_LPCR_HOST_DRV_OWN);
if (!mt76_poll_msec(dev, reg, MT_TOP_LPCR_HOST_FW_OWN,
0, 500)) {
dev_err(dev->mt76.dev, "Timeout for driver own\n");
return -EIO;
}
return 0;
}
static int
mt7921_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
int cmd, int *seq)
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
enum mt76_mcuq_id txq = MT_MCUQ_WM;
int ret;
ret = mt76_connac2_mcu_fill_message(mdev, skb, cmd, seq);
if (ret)
return ret;
mdev->mcu.timeout = 3 * HZ;
if (cmd == MCU_CMD(FW_SCATTER))
txq = MT_MCUQ_FWDL;
return mt76_tx_queue_skb_raw(dev, mdev->q_mcu[txq], skb, 0);
}
int mt7921e_mcu_init(struct mt792x_dev *dev)
{
static const struct mt76_mcu_ops mt7921_mcu_ops = {
.headroom = sizeof(struct mt76_connac2_mcu_txd),
.mcu_skb_send_msg = mt7921_mcu_send_message,
.mcu_parse_response = mt7921_mcu_parse_response,
};
int err;
dev->mt76.mcu_ops = &mt7921_mcu_ops;
err = mt7921e_driver_own(dev);
if (err)
return err;
mt76_rmw_field(dev, MT_PCIE_MAC_PM, MT_PCIE_MAC_PM_L0S_DIS, 1);
err = mt7921_run_firmware(dev);
mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_FWDL], false);
return err;
}
@@ -0,0 +1,214 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2025 MediaTek Inc. */
#include <linux/of.h>
#include "mt7921.h"
#include "regd.h"
#include "mcu.h"
static bool mt7921_disable_clc;
module_param_named(disable_clc, mt7921_disable_clc, bool, 0644);
MODULE_PARM_DESC(disable_clc, "disable CLC support");
bool mt7921_regd_clc_supported(struct mt792x_dev *dev)
{
if (mt7921_disable_clc ||
mt76_is_usb(&dev->mt76))
return false;
return true;
}
static void
mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
{
#define IS_UNII_INVALID(idx, sfreq, efreq) \
(!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq))
struct ieee80211_supported_band *sband;
struct mt76_dev *mdev = &dev->mt76;
struct device_node *np, *band_np;
struct ieee80211_channel *ch;
int i, cfreq;
np = mt76_find_power_limits_node(mdev);
sband = wiphy->bands[NL80211_BAND_5GHZ];
band_np = np ? of_get_child_by_name(np, "txpower-5g") : NULL;
for (i = 0; i < sband->n_channels; i++) {
ch = &sband->channels[i];
cfreq = ch->center_freq;
if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) {
ch->flags |= IEEE80211_CHAN_DISABLED;
continue;
}
/* UNII-4 */
if (IS_UNII_INVALID(0, 5845, 5925))
ch->flags |= IEEE80211_CHAN_DISABLED;
}
sband = wiphy->bands[NL80211_BAND_6GHZ];
if (!sband)
return;
band_np = np ? of_get_child_by_name(np, "txpower-6g") : NULL;
for (i = 0; i < sband->n_channels; i++) {
ch = &sband->channels[i];
cfreq = ch->center_freq;
if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) {
ch->flags |= IEEE80211_CHAN_DISABLED;
continue;
}
/* UNII-5/6/7/8 */
if (IS_UNII_INVALID(1, 5925, 6425) ||
IS_UNII_INVALID(2, 6425, 6525) ||
IS_UNII_INVALID(3, 6525, 6875) ||
IS_UNII_INVALID(4, 6875, 7125))
ch->flags |= IEEE80211_CHAN_DISABLED;
}
}
/* Internal version that assumes mutex is already held by caller */
int __mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
enum environment_cap country_ie_env)
{
struct mt76_dev *mdev = &dev->mt76;
struct ieee80211_hw *hw = mdev->hw;
struct wiphy *wiphy = hw->wiphy;
int ret = 0;
if (!dev->regd_change)
return 0;
ret = mt7921_mcu_set_clc(dev, alpha2, country_ie_env);
if (ret < 0)
return ret;
mt7921_regd_channel_update(wiphy, dev);
ret = mt76_connac_mcu_set_channel_domain(hw->priv);
if (ret < 0)
return ret;
ret = mt7921_set_tx_sar_pwr(hw, NULL);
return ret;
}
int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
enum environment_cap country_ie_env)
{
int ret = 0;
dev->regd_in_progress = true;
mt792x_mutex_acquire(dev);
ret = __mt7921_mcu_regd_update(dev, alpha2, country_ie_env);
mt792x_mutex_release(dev);
dev->regd_change = false;
dev->regd_in_progress = false;
wake_up(&dev->wait);
return ret;
}
EXPORT_SYMBOL_GPL(mt7921_mcu_regd_update);
void mt7921_regd_notifier(struct wiphy *wiphy,
struct regulatory_request *req)
{
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct mt792x_dev *dev = mt792x_hw_dev(hw);
struct mt76_connac_pm *pm = &dev->pm;
struct mt76_dev *mdev = &dev->mt76;
if (req->initiator == NL80211_REGDOM_SET_BY_USER &&
!dev->regd_user)
dev->regd_user = true;
/* do not need to update the same country twice */
if (!memcmp(req->alpha2, mdev->alpha2, 2) &&
dev->country_ie_env == req->country_ie_env)
return;
memcpy(mdev->alpha2, req->alpha2, 2);
mdev->region = req->dfs_region;
dev->country_ie_env = req->country_ie_env;
if (req->initiator == NL80211_REGDOM_SET_BY_USER) {
if (dev->mt76.alpha2[0] == '0' && dev->mt76.alpha2[1] == '0')
wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE;
else
wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
}
dev->regd_change = true;
if (pm->suspended)
return;
mt7921_mcu_regd_update(dev, req->alpha2,
req->country_ie_env);
}
static bool
mt7921_regd_is_valid_alpha2(const char *alpha2)
{
if (!alpha2)
return false;
if (alpha2[0] == '0' && alpha2[1] == '0')
return true;
if (isalpha(alpha2[0]) && isalpha(alpha2[1]))
return true;
return false;
}
int mt7921_regd_change(struct mt792x_phy *phy, char *alpha2)
{
struct wiphy *wiphy = phy->mt76->hw->wiphy;
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct mt792x_dev *dev = mt792x_hw_dev(hw);
struct mt76_dev *mdev = &dev->mt76;
if (dev->hw_full_reset)
return 0;
if (!mt7921_regd_is_valid_alpha2(alpha2) ||
!mt7921_regd_clc_supported(dev) ||
dev->regd_user)
return -EINVAL;
if (mdev->alpha2[0] != '0' && mdev->alpha2[1] != '0')
return 0;
/* do not need to update the same country twice */
if (!memcmp(alpha2, mdev->alpha2, 2))
return 0;
if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN)
return regulatory_hint(wiphy, alpha2);
else
return mt7921_mcu_set_clc(dev, alpha2, ENVIRON_INDOOR);
}
int mt7921_regd_init(struct mt792x_phy *phy)
{
struct wiphy *wiphy = phy->mt76->hw->wiphy;
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct mt792x_dev *dev = mt792x_hw_dev(hw);
struct mt76_dev *mdev = &dev->mt76;
if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN)
wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE |
REGULATORY_DISABLE_BEACON_HINTS;
else
memzero_explicit(&mdev->alpha2, sizeof(mdev->alpha2));
return 0;
}
@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2025 MediaTek Inc. */
#ifndef __MT7921_REGD_H
#define __MT7921_REGD_H
struct mt792x_dev;
struct wiphy;
struct regulatory_request;
int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
enum environment_cap country_ie_env);
int __mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
enum environment_cap country_ie_env);
void mt7921_regd_notifier(struct wiphy *wiphy,
struct regulatory_request *request);
bool mt7921_regd_clc_supported(struct mt792x_dev *dev);
int mt7921_regd_change(struct mt792x_phy *phy, char *alpha2);
int mt7921_regd_init(struct mt792x_phy *phy);
#endif
@@ -0,0 +1,81 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2020 MediaTek Inc. */
#ifndef __MT7921_REGS_H
#define __MT7921_REGS_H
#include "../mt792x_regs.h"
#define MT_MDP_BASE 0x820cd000
#define MT_MDP(ofs) (MT_MDP_BASE + (ofs))
#define MT_MDP_DCR0 MT_MDP(0x000)
#define MT_MDP_DCR0_DAMSDU_EN BIT(15)
#define MT_MDP_DCR0_RX_HDR_TRANS_EN BIT(19)
#define MT_MDP_DCR1 MT_MDP(0x004)
#define MT_MDP_DCR1_MAX_RX_LEN GENMASK(15, 3)
#define MT_MDP_BNRCFR0(_band) MT_MDP(0x070 + ((_band) << 8))
#define MT_MDP_RCFR0_MCU_RX_MGMT GENMASK(5, 4)
#define MT_MDP_RCFR0_MCU_RX_CTL_NON_BAR GENMASK(7, 6)
#define MT_MDP_RCFR0_MCU_RX_CTL_BAR GENMASK(9, 8)
#define MT_MDP_BNRCFR1(_band) MT_MDP(0x074 + ((_band) << 8))
#define MT_MDP_RCFR1_MCU_RX_BYPASS GENMASK(23, 22)
#define MT_MDP_RCFR1_RX_DROPPED_UCAST GENMASK(28, 27)
#define MT_MDP_RCFR1_RX_DROPPED_MCAST GENMASK(30, 29)
#define MT_MDP_TO_HIF 0
#define MT_MDP_TO_WM 1
#define MT_WFDMA0_HOST_INT_ENA MT_WFDMA0(0x204)
#define HOST_TX_DONE_INT_ENA8 BIT(12)
#define HOST_TX_DONE_INT_ENA9 BIT(13)
#define HOST_TX_DONE_INT_ENA10 BIT(14)
#define HOST_TX_DONE_INT_ENA11 BIT(15)
#define HOST_TX_DONE_INT_ENA12 BIT(16)
#define HOST_TX_DONE_INT_ENA13 BIT(17)
#define HOST_TX_DONE_INT_ENA14 BIT(18)
#define HOST_RX_DONE_INT_ENA4 BIT(22)
#define HOST_RX_DONE_INT_ENA5 BIT(23)
#define HOST_TX_DONE_INT_ENA16 BIT(26)
#define HOST_TX_DONE_INT_ENA17 BIT(27)
/* WFDMA interrupt */
#define MT_INT_RX_DONE_DATA HOST_RX_DONE_INT_ENA2
#define MT_INT_RX_DONE_WM HOST_RX_DONE_INT_ENA0
#define MT_INT_RX_DONE_WM2 HOST_RX_DONE_INT_ENA4
#define MT_INT_RX_DONE_ALL (MT_INT_RX_DONE_DATA | \
MT_INT_RX_DONE_WM | \
MT_INT_RX_DONE_WM2)
#define MT_INT_TX_DONE_MCU_WM HOST_TX_DONE_INT_ENA17
#define MT_INT_TX_DONE_FWDL HOST_TX_DONE_INT_ENA16
#define MT_INT_TX_DONE_BAND0 HOST_TX_DONE_INT_ENA0
#define MT_INT_TX_DONE_MCU (MT_INT_TX_DONE_MCU_WM | \
MT_INT_TX_DONE_FWDL)
#define MT_INT_TX_DONE_ALL (MT_INT_TX_DONE_MCU_WM | \
MT_INT_TX_DONE_BAND0 | \
GENMASK(18, 4))
#define MT_RX_DATA_RING_BASE MT_WFDMA0(0x520)
#define MT_INFRA_CFG_BASE 0xfe000
#define MT_INFRA(ofs) (MT_INFRA_CFG_BASE + (ofs))
#define MT_HIF_REMAP_L1 MT_INFRA(0x24c)
#define MT_HIF_REMAP_L1_MASK GENMASK(15, 0)
#define MT_HIF_REMAP_L1_OFFSET GENMASK(15, 0)
#define MT_HIF_REMAP_L1_BASE GENMASK(31, 16)
#define MT_HIF_REMAP_BASE_L1 0x40000
#define MT_WFSYS_SW_RST_B 0x18000140
#define MT_WTBLON_TOP_WDUCR MT_WTBLON_TOP(0x200)
#define MT_WTBLON_TOP_WDUCR_GROUP GENMASK(2, 0)
#define MT_WTBL_UPDATE MT_WTBLON_TOP(0x230)
#define MT_WTBL_UPDATE_WLAN_IDX GENMASK(9, 0)
#define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(12)
#endif
@@ -0,0 +1,337 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2021 MediaTek Inc.
*
*/
#include <linux/kernel.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio_func.h>
#include "mt7921.h"
#include "../sdio.h"
#include "../mt76_connac2_mac.h"
#include "mcu.h"
static const struct sdio_device_id mt7921s_table[] = {
{ SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7901),
.driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM },
{ SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7902),
.driver_data = (kernel_ulong_t)MT7902_FIRMWARE_WM },
{ } /* Terminating entry */
};
static void mt7921s_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 mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
if (!mt76_connac_pm_ref(&dev->mphy, &dev->pm)) {
queue_work(mdev->wq, &dev->pm.wake_work);
return;
}
mt76s_txrx_worker(sdio);
mt76_connac_pm_unref(&dev->mphy, &dev->pm);
}
static void mt7921s_unregister_device(struct mt792x_dev *dev)
{
struct mt76_connac_pm *pm = &dev->pm;
cancel_work_sync(&dev->init_work);
mt76_unregister_device(&dev->mt76);
cancel_delayed_work_sync(&pm->ps_work);
cancel_work_sync(&pm->wake_work);
mt76s_deinit(&dev->mt76);
mt7921s_wfsys_reset(dev);
skb_queue_purge(&dev->mt76.mcu.res_q);
mt76_free_device(&dev->mt76);
}
static int mt7921s_parse_intr(struct mt76_dev *dev, struct mt76s_intr *intr)
{
struct mt76_sdio *sdio = &dev->sdio;
struct mt7921_sdio_intr *irq_data = sdio->intr_data;
int i, err;
sdio_claim_host(sdio->func);
err = sdio_readsb(sdio->func, irq_data, MCR_WHISR, sizeof(*irq_data));
sdio_release_host(sdio->func);
if (err < 0)
return err;
if (irq_data->rx.num[0] > 16 ||
irq_data->rx.num[1] > 128)
return -EINVAL;
intr->isr = irq_data->isr;
intr->rec_mb = irq_data->rec_mb;
intr->tx.wtqcr = irq_data->tx.wtqcr;
intr->rx.num = irq_data->rx.num;
for (i = 0; i < 2 ; i++) {
if (!i)
intr->rx.len[0] = irq_data->rx.len0;
else
intr->rx.len[1] = irq_data->rx.len1;
}
return 0;
}
static int mt7921s_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
static const struct mt76_driver_ops drv_ops = {
.txwi_size = MT_SDIO_TXD_SIZE,
.drv_flags = MT_DRV_AMSDU_OFFLOAD,
.survey_flags = SURVEY_INFO_TIME_TX |
SURVEY_INFO_TIME_RX |
SURVEY_INFO_TIME_BSS_RX,
.tx_prepare_skb = mt7921_usb_sdio_tx_prepare_skb,
.tx_complete_skb = mt7921_usb_sdio_tx_complete_skb,
.tx_status_data = mt7921_usb_sdio_tx_status_data,
.rx_skb = mt7921_queue_rx_skb,
.rx_check = mt7921_rx_check,
.sta_add = mt7921_mac_sta_add,
.sta_event = mt7921_mac_sta_event,
.sta_remove = mt7921_mac_sta_remove,
.update_survey = mt792x_update_channel,
.set_channel = mt7921_set_channel,
};
static const struct mt76_bus_ops mt7921s_ops = {
.rr = mt76s_rr,
.rmw = mt76s_rmw,
.wr = mt76s_wr,
.write_copy = mt76s_write_copy,
.read_copy = mt76s_read_copy,
.wr_rp = mt76s_wr_rp,
.rd_rp = mt76s_rd_rp,
.type = MT76_BUS_SDIO,
};
static const struct mt792x_hif_ops mt7921_sdio_ops = {
.init_reset = mt7921s_init_reset,
.reset = mt7921s_mac_reset,
.mcu_init = mt7921s_mcu_init,
.drv_own = mt7921s_mcu_drv_pmctrl,
.fw_own = mt7921s_mcu_fw_pmctrl,
};
struct ieee80211_ops *ops;
struct mt792x_dev *dev;
struct mt76_dev *mdev;
u8 features;
int ret;
ops = mt792x_get_mac80211_ops(&func->dev, &mt7921_ops,
(void *)id->driver_data, &features);
if (!ops)
return -ENOMEM;
mdev = mt76_alloc_device(&func->dev, sizeof(*dev), ops, &drv_ops);
if (!mdev)
return -ENOMEM;
dev = container_of(mdev, struct mt792x_dev, mt76);
dev->fw_features = features;
dev->hif_ops = &mt7921_sdio_ops;
sdio_set_drvdata(func, dev);
ret = mt76s_init(mdev, func, &mt7921s_ops);
if (ret < 0)
goto error;
ret = mt76s_hw_init(mdev, func, MT76_CONNAC2_SDIO);
if (ret)
goto error;
atomic_set(&mdev->bus_hung, false);
mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) |
(mt76_rr(dev, MT_HW_REV) & 0xff);
dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev);
mdev->sdio.parse_irq = mt7921s_parse_intr;
mdev->sdio.intr_data = devm_kmalloc(mdev->dev,
sizeof(struct mt7921_sdio_intr),
GFP_KERNEL);
if (!mdev->sdio.intr_data) {
ret = -ENOMEM;
goto error;
}
ret = mt76s_alloc_rx_queue(mdev, MT_RXQ_MAIN);
if (ret)
goto error;
ret = mt76s_alloc_rx_queue(mdev, MT_RXQ_MCU);
if (ret)
goto error;
ret = mt76s_alloc_tx(mdev);
if (ret)
goto error;
ret = mt76_worker_setup(mt76_hw(dev), &mdev->sdio.txrx_worker,
mt7921s_txrx_worker, "sdio-txrx");
if (ret)
goto error;
sched_set_fifo_low(mdev->sdio.txrx_worker.task);
ret = mt7921_register_device(dev);
if (ret)
goto error;
return 0;
error:
mt76s_deinit(&dev->mt76);
mt76_free_device(&dev->mt76);
return ret;
}
static void mt7921s_remove(struct sdio_func *func)
{
struct mt792x_dev *dev = sdio_get_drvdata(func);
mt7921s_unregister_device(dev);
}
static int mt7921s_suspend(struct device *__dev)
{
struct sdio_func *func = dev_to_sdio_func(__dev);
struct mt792x_dev *dev = sdio_get_drvdata(func);
struct mt76_connac_pm *pm = &dev->pm;
struct mt76_dev *mdev = &dev->mt76;
int err;
pm->suspended = true;
set_bit(MT76_STATE_SUSPEND, &mdev->phy.state);
flush_work(&dev->reset_work);
cancel_delayed_work_sync(&pm->ps_work);
cancel_work_sync(&pm->wake_work);
mt7921_roc_abort_sync(dev);
err = mt792x_mcu_drv_pmctrl(dev);
if (err < 0)
goto restore_suspend;
/* always enable deep sleep during suspend to reduce
* power consumption
*/
mt76_connac_mcu_set_deep_sleep(mdev, true);
mt76_txq_schedule_all(&dev->mphy);
mt76_worker_disable(&mdev->tx_worker);
mt76_worker_disable(&mdev->sdio.status_worker);
mt76_worker_disable(&mdev->sdio.stat_worker);
clear_bit(MT76_READING_STATS, &dev->mphy.state);
mt76_tx_status_check(mdev, true);
mt76_worker_schedule(&mdev->sdio.txrx_worker);
wait_event_timeout(dev->mt76.sdio.wait,
mt76s_txqs_empty(&dev->mt76), 5 * HZ);
/* It is supposed that SDIO bus is idle at the point */
err = mt76_connac_mcu_set_hif_suspend(mdev, true, true);
if (err)
goto restore_worker;
mt76_worker_disable(&mdev->sdio.txrx_worker);
mt76_worker_disable(&mdev->sdio.net_worker);
err = mt792x_mcu_fw_pmctrl(dev);
if (err)
goto restore_txrx_worker;
sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
return 0;
restore_txrx_worker:
mt76_worker_enable(&mdev->sdio.net_worker);
mt76_worker_enable(&mdev->sdio.txrx_worker);
mt76_connac_mcu_set_hif_suspend(mdev, false, true);
restore_worker:
mt76_worker_enable(&mdev->tx_worker);
mt76_worker_enable(&mdev->sdio.status_worker);
mt76_worker_enable(&mdev->sdio.stat_worker);
if (!pm->ds_enable)
mt76_connac_mcu_set_deep_sleep(mdev, false);
restore_suspend:
clear_bit(MT76_STATE_SUSPEND, &mdev->phy.state);
pm->suspended = false;
if (err < 0)
mt792x_reset(&dev->mt76);
return err;
}
static int mt7921s_resume(struct device *__dev)
{
struct sdio_func *func = dev_to_sdio_func(__dev);
struct mt792x_dev *dev = sdio_get_drvdata(func);
struct mt76_connac_pm *pm = &dev->pm;
struct mt76_dev *mdev = &dev->mt76;
int err;
clear_bit(MT76_STATE_SUSPEND, &mdev->phy.state);
err = mt792x_mcu_drv_pmctrl(dev);
if (err < 0)
goto failed;
mt76_worker_enable(&mdev->tx_worker);
mt76_worker_enable(&mdev->sdio.txrx_worker);
mt76_worker_enable(&mdev->sdio.status_worker);
mt76_worker_enable(&mdev->sdio.net_worker);
mt76_worker_enable(&mdev->sdio.stat_worker);
/* restore previous ds setting */
if (!pm->ds_enable)
mt76_connac_mcu_set_deep_sleep(mdev, false);
err = mt76_connac_mcu_set_hif_suspend(mdev, false, true);
failed:
pm->suspended = false;
if (err < 0)
mt792x_reset(&dev->mt76);
return err;
}
MODULE_DEVICE_TABLE(sdio, mt7921s_table);
MODULE_FIRMWARE(MT7921_FIRMWARE_WM);
MODULE_FIRMWARE(MT7921_ROM_PATCH);
MODULE_FIRMWARE(MT7902_FIRMWARE_WM);
MODULE_FIRMWARE(MT7902_ROM_PATCH);
static DEFINE_SIMPLE_DEV_PM_OPS(mt7921s_pm_ops, mt7921s_suspend, mt7921s_resume);
static struct sdio_driver mt7921s_driver = {
.name = KBUILD_MODNAME,
.probe = mt7921s_probe,
.remove = mt7921s_remove,
.id_table = mt7921s_table,
.drv.pm = pm_sleep_ptr(&mt7921s_pm_ops),
};
module_sdio_driver(mt7921s_driver);
MODULE_DESCRIPTION("MediaTek MT7921S (SDIO) wireless driver");
MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
MODULE_LICENSE("Dual BSD/GPL");
@@ -0,0 +1,199 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2021 MediaTek Inc. */
#include <linux/iopoll.h>
#include <linux/mmc/sdio_func.h>
#include "mt7921.h"
#include "../mt76_connac2_mac.h"
#include "../sdio.h"
#include <linux/mmc/host.h>
#include <linux/kallsyms.h>
static void mt7921s_enable_irq(struct mt76_dev *dev)
{
struct mt76_sdio *sdio = &dev->sdio;
sdio_claim_host(sdio->func);
sdio_writel(sdio->func, WHLPCR_INT_EN_SET, MCR_WHLPCR, NULL);
sdio_release_host(sdio->func);
}
static void mt7921s_disable_irq(struct mt76_dev *dev)
{
struct mt76_sdio *sdio = &dev->sdio;
sdio_claim_host(sdio->func);
sdio_writel(sdio->func, WHLPCR_INT_EN_CLR, MCR_WHLPCR, NULL);
sdio_release_host(sdio->func);
}
static u32 mt7921s_read_whcr(struct mt76_dev *dev)
{
return sdio_readl(dev->sdio.func, MCR_WHCR, NULL);
}
int mt7921s_wfsys_reset(struct mt792x_dev *dev)
{
struct mt76_sdio *sdio = &dev->mt76.sdio;
u32 val, status;
if (atomic_read(&dev->mt76.bus_hung))
return 0;
mt7921s_mcu_drv_pmctrl(dev);
sdio_claim_host(sdio->func);
val = sdio_readl(sdio->func, MCR_WHCR, NULL);
val &= ~WF_WHOLE_PATH_RSTB;
sdio_writel(sdio->func, val, MCR_WHCR, NULL);
msleep(50);
val = sdio_readl(sdio->func, MCR_WHCR, NULL);
val &= ~WF_SDIO_WF_PATH_RSTB;
sdio_writel(sdio->func, val, MCR_WHCR, NULL);
usleep_range(1000, 2000);
val = sdio_readl(sdio->func, MCR_WHCR, NULL);
val |= WF_WHOLE_PATH_RSTB;
sdio_writel(sdio->func, val, MCR_WHCR, NULL);
readx_poll_timeout(mt7921s_read_whcr, &dev->mt76, status,
status & WF_RST_DONE, 50000, 2000000);
sdio_release_host(sdio->func);
clear_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
/* activate mt7921s again */
mt7921s_mcu_drv_pmctrl(dev);
mt76_clear(dev, MT_CONN_STATUS, MT_WIFI_PATCH_DL_STATE);
mt7921s_mcu_fw_pmctrl(dev);
mt7921s_mcu_drv_pmctrl(dev);
return 0;
}
int mt7921s_init_reset(struct mt792x_dev *dev)
{
set_bit(MT76_MCU_RESET, &dev->mphy.state);
wake_up(&dev->mt76.mcu.wait);
skb_queue_purge(&dev->mt76.mcu.res_q);
wait_event_timeout(dev->mt76.sdio.wait,
mt76s_txqs_empty(&dev->mt76), 5 * HZ);
mt76_worker_disable(&dev->mt76.sdio.txrx_worker);
mt7921s_disable_irq(&dev->mt76);
mt7921s_wfsys_reset(dev);
mt76_worker_enable(&dev->mt76.sdio.txrx_worker);
clear_bit(MT76_MCU_RESET, &dev->mphy.state);
mt7921s_enable_irq(&dev->mt76);
return 0;
}
static struct mt76_sdio *msdio;
static void mt7921s_card_reset(struct work_struct *work)
{
struct mmc_host *sdio_host = msdio->func->card->host;
sdio_claim_host(msdio->func);
sdio_release_irq(msdio->func);
sdio_release_host(msdio->func);
mmc_remove_host(sdio_host);
msleep(50);
mmc_add_host(sdio_host);
}
static DECLARE_WORK(sdio_reset_work, mt7921s_card_reset);
static int mt7921s_check_bus(struct mt76_dev *dev)
{
struct mt76_sdio *sdio = &dev->sdio;
int err;
sdio_claim_host(sdio->func);
sdio_readl(dev->sdio.func, MCR_WHCR, &err);
sdio_release_host(sdio->func);
return err;
}
static int mt7921s_host_reset(struct mt792x_dev *dev)
{
struct mt76_dev *mdev = &dev->mt76;
int err = -1;
if (!atomic_read(&mdev->bus_hung))
err = mt7921s_check_bus(&dev->mt76);
if (err) {
atomic_set(&mdev->bus_hung, true);
msdio = &dev->mt76.sdio;
dev_err(mdev->dev, "SDIO bus problem detected(%d), resetting card!!\n", err);
schedule_work(&sdio_reset_work);
return err;
}
atomic_set(&mdev->bus_hung, false);
return 0;
}
int mt7921s_mac_reset(struct mt792x_dev *dev)
{
int err;
mt76_connac_free_pending_tx_skbs(&dev->pm, NULL);
mt7921s_host_reset(dev);
if (atomic_read(&dev->mt76.bus_hung))
return 0;
mt76_txq_schedule_all(&dev->mphy);
mt76_worker_disable(&dev->mt76.tx_worker);
set_bit(MT76_MCU_RESET, &dev->mphy.state);
wake_up(&dev->mt76.mcu.wait);
skb_queue_purge(&dev->mt76.mcu.res_q);
wait_event_timeout(dev->mt76.sdio.wait,
mt76s_txqs_empty(&dev->mt76), 5 * HZ);
mt76_worker_disable(&dev->mt76.sdio.txrx_worker);
mt76_worker_disable(&dev->mt76.sdio.status_worker);
mt76_worker_disable(&dev->mt76.sdio.net_worker);
mt76_worker_disable(&dev->mt76.sdio.stat_worker);
mt7921s_disable_irq(&dev->mt76);
mt7921s_wfsys_reset(dev);
mt76_worker_enable(&dev->mt76.sdio.txrx_worker);
mt76_worker_enable(&dev->mt76.sdio.status_worker);
mt76_worker_enable(&dev->mt76.sdio.net_worker);
mt76_worker_enable(&dev->mt76.sdio.stat_worker);
dev->fw_assert = false;
clear_bit(MT76_MCU_RESET, &dev->mphy.state);
mt7921s_enable_irq(&dev->mt76);
err = mt7921_run_firmware(dev);
if (err)
goto out;
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:
mt76_worker_enable(&dev->mt76.tx_worker);
return err;
}
@@ -0,0 +1,175 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2021 MediaTek Inc. */
#include <linux/kernel.h>
#include <linux/mmc/sdio_func.h>
#include <linux/module.h>
#include <linux/iopoll.h>
#include "mt7921.h"
#include "../sdio.h"
#include "../mt76_connac2_mac.h"
#include "mcu.h"
#include "regs.h"
static int
mt7921s_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
int cmd, int *seq)
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
enum mt7921_sdio_pkt_type type = MT7921_SDIO_CMD;
enum mt76_mcuq_id txq = MT_MCUQ_WM;
int ret, pad;
/* We just return in case firmware assertion to avoid blocking the
* common workqueue to run, for example, the coredump work might be
* blocked by mt792x_mac_work that is excuting register access via sdio
* bus.
*/
if (dev->fw_assert)
return -EBUSY;
ret = mt76_connac2_mcu_fill_message(mdev, skb, cmd, seq);
if (ret)
return ret;
mdev->mcu.timeout = 3 * HZ;
if (cmd == MCU_CMD(FW_SCATTER))
type = MT7921_SDIO_FWDL;
mt792x_skb_add_usb_sdio_hdr(dev, skb, type);
pad = round_up(skb->len, 4) - skb->len;
__skb_put_zero(skb, pad);
ret = mt76_tx_queue_skb_raw(dev, mdev->q_mcu[txq], skb, 0);
if (ret)
return ret;
mt76_queue_kick(dev, mdev->q_mcu[txq]);
return ret;
}
static u32 mt7921s_read_rm3r(struct mt792x_dev *dev)
{
struct mt76_sdio *sdio = &dev->mt76.sdio;
return sdio_readl(sdio->func, MCR_D2HRM3R, NULL);
}
static u32 mt7921s_clear_rm3r_drv_own(struct mt792x_dev *dev)
{
struct mt76_sdio *sdio = &dev->mt76.sdio;
u32 val;
val = sdio_readl(sdio->func, MCR_D2HRM3R, NULL);
if (val)
sdio_writel(sdio->func, H2D_SW_INT_CLEAR_MAILBOX_ACK,
MCR_WSICR, NULL);
return val;
}
int mt7921s_mcu_init(struct mt792x_dev *dev)
{
static const struct mt76_mcu_ops mt7921s_mcu_ops = {
.headroom = MT_SDIO_HDR_SIZE +
sizeof(struct mt76_connac2_mcu_txd),
.tailroom = MT_SDIO_TAIL_SIZE,
.mcu_skb_send_msg = mt7921s_mcu_send_message,
.mcu_parse_response = mt7921_mcu_parse_response,
.mcu_rr = mt76_connac_mcu_reg_rr,
.mcu_wr = mt76_connac_mcu_reg_wr,
};
int ret;
mt7921s_mcu_drv_pmctrl(dev);
dev->mt76.mcu_ops = &mt7921s_mcu_ops;
ret = mt7921_run_firmware(dev);
if (ret)
return ret;
set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
return 0;
}
int mt7921s_mcu_drv_pmctrl(struct mt792x_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 err;
sdio_claim_host(func);
sdio_writel(func, WHLPCR_FW_OWN_REQ_CLR, MCR_WHLPCR, NULL);
err = readx_poll_timeout(mt76s_read_pcr, &dev->mt76, status,
status & WHLPCR_IS_DRIVER_OWN, 2000, 1000000);
if (!err && test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state))
err = readx_poll_timeout(mt7921s_read_rm3r, dev, status,
status & D2HRM3R_IS_DRIVER_OWN,
2000, 1000000);
sdio_release_host(func);
if (err < 0) {
dev_err(dev->mt76.dev, "driver own failed\n");
return -EIO;
}
clear_bit(MT76_STATE_PM, &mphy->state);
pm->stats.last_wake_event = jiffies;
pm->stats.doze_time += pm->stats.last_wake_event -
pm->stats.last_doze_event;
return 0;
}
int mt7921s_mcu_fw_pmctrl(struct mt792x_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 err;
sdio_claim_host(func);
if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) {
err = readx_poll_timeout(mt7921s_clear_rm3r_drv_own,
dev, status,
!(status & D2HRM3R_IS_DRIVER_OWN),
2000, 1000000);
if (err < 0) {
dev_err(dev->mt76.dev, "mailbox ACK not cleared\n");
goto out;
}
}
sdio_writel(func, WHLPCR_FW_OWN_REQ_SET, MCR_WHLPCR, NULL);
err = readx_poll_timeout(mt76s_read_pcr, &dev->mt76, status,
!(status & WHLPCR_IS_DRIVER_OWN), 2000, 1000000);
out:
sdio_release_host(func);
if (err < 0) {
dev_err(dev->mt76.dev, "firmware own failed\n");
clear_bit(MT76_STATE_PM, &mphy->state);
return -EIO;
}
pm->stats.last_doze_event = jiffies;
pm->stats.awake_time += pm->stats.last_doze_event -
pm->stats.last_wake_event;
return 0;
}
@@ -0,0 +1,196 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
#include "mt7921.h"
#include "mcu.h"
enum mt7921_testmode_attr {
MT7921_TM_ATTR_UNSPEC,
MT7921_TM_ATTR_SET,
MT7921_TM_ATTR_QUERY,
MT7921_TM_ATTR_RSP,
/* keep last */
NUM_MT7921_TM_ATTRS,
MT7921_TM_ATTR_MAX = NUM_MT7921_TM_ATTRS - 1,
};
struct mt7921_tm_cmd {
u8 action;
u32 param0;
u32 param1;
};
struct mt7921_tm_evt {
u32 param0;
u32 param1;
};
static const struct nla_policy mt7921_tm_policy[NUM_MT7921_TM_ATTRS] = {
[MT7921_TM_ATTR_SET] = NLA_POLICY_EXACT_LEN(sizeof(struct mt7921_tm_cmd)),
[MT7921_TM_ATTR_QUERY] = NLA_POLICY_EXACT_LEN(sizeof(struct mt7921_tm_cmd)),
};
static int
mt7921_tm_set(struct mt792x_dev *dev, struct mt7921_tm_cmd *req)
{
struct mt7921_rftest_cmd cmd = {
.action = req->action,
.param0 = cpu_to_le32(req->param0),
.param1 = cpu_to_le32(req->param1),
};
bool testmode = false, normal = false;
struct mt76_connac_pm *pm = &dev->pm;
struct mt76_phy *phy = &dev->mphy;
int ret = -ENOTCONN;
mutex_lock(&dev->mt76.mutex);
if (req->action == TM_SWITCH_MODE) {
if (req->param0 == MT7921_TM_NORMAL)
normal = true;
else
testmode = true;
}
if (testmode) {
/* Make sure testmode running on full power mode */
pm->enable = false;
cancel_delayed_work_sync(&pm->ps_work);
cancel_work_sync(&pm->wake_work);
__mt792x_mcu_drv_pmctrl(dev);
phy->test.state = MT76_TM_STATE_ON;
}
if (!mt76_testmode_enabled(phy))
goto out;
ret = mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(TEST_CTRL), &cmd,
sizeof(cmd), false);
if (ret)
goto out;
if (normal) {
/* Switch back to the normal world */
phy->test.state = MT76_TM_STATE_OFF;
pm->enable = true;
}
out:
mutex_unlock(&dev->mt76.mutex);
return ret;
}
static int
mt7921_tm_query(struct mt792x_dev *dev, struct mt7921_tm_cmd *req,
struct mt7921_tm_evt *evt_resp)
{
struct mt7921_rftest_cmd cmd = {
.action = req->action,
.param0 = cpu_to_le32(req->param0),
.param1 = cpu_to_le32(req->param1),
};
struct mt7921_rftest_evt *evt;
struct sk_buff *skb;
int ret;
ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_CE_CMD(TEST_CTRL),
&cmd, sizeof(cmd), true, &skb);
if (ret)
goto out;
evt = (struct mt7921_rftest_evt *)skb->data;
evt_resp->param0 = le32_to_cpu(evt->param0);
evt_resp->param1 = le32_to_cpu(evt->param1);
out:
dev_kfree_skb(skb);
return ret;
}
int mt7921_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void *data, int len)
{
struct nlattr *tb[NUM_MT76_TM_ATTRS];
struct mt76_phy *mphy = hw->priv;
struct mt792x_phy *phy = mphy->priv;
int err;
if (!test_bit(MT76_STATE_RUNNING, &mphy->state) ||
!(hw->conf.flags & IEEE80211_CONF_MONITOR))
return -ENOTCONN;
err = nla_parse_deprecated(tb, MT76_TM_ATTR_MAX, data, len,
mt76_tm_policy, NULL);
if (err)
return err;
if (tb[MT76_TM_ATTR_DRV_DATA]) {
struct nlattr *drv_tb[NUM_MT7921_TM_ATTRS], *data;
int ret;
data = tb[MT76_TM_ATTR_DRV_DATA];
ret = nla_parse_nested_deprecated(drv_tb,
MT7921_TM_ATTR_MAX,
data, mt7921_tm_policy,
NULL);
if (ret)
return ret;
data = drv_tb[MT7921_TM_ATTR_SET];
if (data)
return mt7921_tm_set(phy->dev, nla_data(data));
}
return -EINVAL;
}
int mt7921_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
struct netlink_callback *cb, void *data, int len)
{
struct nlattr *tb[NUM_MT76_TM_ATTRS];
struct mt76_phy *mphy = hw->priv;
struct mt792x_phy *phy = mphy->priv;
int err;
if (!test_bit(MT76_STATE_RUNNING, &mphy->state) ||
!(hw->conf.flags & IEEE80211_CONF_MONITOR) ||
!mt76_testmode_enabled(mphy))
return -ENOTCONN;
if (cb->args[2]++ > 0)
return -ENOENT;
err = nla_parse_deprecated(tb, MT76_TM_ATTR_MAX, data, len,
mt76_tm_policy, NULL);
if (err)
return err;
if (tb[MT76_TM_ATTR_DRV_DATA]) {
struct nlattr *drv_tb[NUM_MT7921_TM_ATTRS], *data;
int ret;
data = tb[MT76_TM_ATTR_DRV_DATA];
ret = nla_parse_nested_deprecated(drv_tb,
MT7921_TM_ATTR_MAX,
data, mt7921_tm_policy,
NULL);
if (ret)
return ret;
data = drv_tb[MT7921_TM_ATTR_QUERY];
if (data) {
struct mt7921_tm_evt evt_resp;
err = mt7921_tm_query(phy->dev, nla_data(data),
&evt_resp);
if (err)
return err;
return nla_put(msg, MT7921_TM_ATTR_RSP,
sizeof(evt_resp), &evt_resp);
}
}
return -EINVAL;
}
@@ -0,0 +1,355 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2022 MediaTek Inc.
*
* Author: Lorenzo Bianconi <lorenzo@kernel.org>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/usb.h>
#include "mt7921.h"
#include "mcu.h"
#include "../mt76_connac2_mac.h"
static const struct usb_device_id mt7921u_device_table[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7902, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)MT7902_FIRMWARE_WM },
{ USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7961, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)MT7921_FIRMWARE_WM },
/* Comfast CF-952AX */
{ USB_DEVICE_AND_INTERFACE_INFO(0x3574, 0x6211, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)MT7921_FIRMWARE_WM },
/* Netgear, Inc. [A8000,AXE3000] */
{ USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0x9060, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)MT7921_FIRMWARE_WM },
/* Netgear, Inc. A7500 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0x9065, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)MT7921_FIRMWARE_WM },
/* TP-Link TXE50UH */
{ USB_DEVICE_AND_INTERFACE_INFO(0x35bc, 0x0107, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)MT7921_FIRMWARE_WM },
{ },
};
static int
mt7921u_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
int cmd, int *seq)
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
u32 pad, ep;
int ret;
ret = mt76_connac2_mcu_fill_message(mdev, skb, cmd, seq);
if (ret)
return ret;
mdev->mcu.timeout = 3 * HZ;
if (cmd != MCU_CMD(FW_SCATTER))
ep = MT_EP_OUT_INBAND_CMD;
else
ep = MT_EP_OUT_AC_BE;
mt792x_skb_add_usb_sdio_hdr(dev, skb, 0);
pad = round_up(skb->len, 4) + 4 - skb->len;
__skb_put_zero(skb, pad);
ret = mt76u_bulk_msg(&dev->mt76, skb->data, skb->len, NULL,
1000, ep);
dev_kfree_skb(skb);
return ret;
}
static int mt7921u_mcu_init(struct mt792x_dev *dev)
{
static const struct mt76_mcu_ops mcu_ops = {
.headroom = MT_SDIO_HDR_SIZE +
sizeof(struct mt76_connac2_mcu_txd),
.tailroom = MT_USB_TAIL_SIZE,
.mcu_skb_send_msg = mt7921u_mcu_send_message,
.mcu_parse_response = mt7921_mcu_parse_response,
};
int ret;
dev->mt76.mcu_ops = &mcu_ops;
mt76_set(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN);
ret = mt7921_run_firmware(dev);
if (ret)
return ret;
set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
mt76_clear(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN);
return 0;
}
static int mt7921u_mac_reset(struct mt792x_dev *dev)
{
int err;
mt792xu_reset_on_bus_error(dev);
if (atomic_read(&dev->mt76.bus_hung))
return 0;
mt76_txq_schedule_all(&dev->mphy);
mt76_worker_disable(&dev->mt76.tx_worker);
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);
mt76u_stop_rx(&dev->mt76);
mt76u_stop_tx(&dev->mt76);
mt792xu_wfsys_reset(dev);
clear_bit(MT76_MCU_RESET, &dev->mphy.state);
err = mt76u_resume_rx(&dev->mt76);
if (err)
goto out;
err = mt792xu_mcu_power_on(dev);
if (err)
goto out;
err = mt792xu_dma_init(dev, false);
if (err)
goto out;
mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE);
mt76_set(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN);
err = mt7921_run_firmware(dev);
if (err)
goto out;
mt76_clear(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN);
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);
mt76_worker_enable(&dev->mt76.tx_worker);
return err;
}
static int mt7921u_probe(struct usb_interface *usb_intf,
const struct usb_device_id *id)
{
static const struct mt76_driver_ops drv_ops = {
.txwi_size = MT_SDIO_TXD_SIZE,
.drv_flags = MT_DRV_RX_DMA_HDR | MT_DRV_HW_MGMT_TXQ |
MT_DRV_AMSDU_OFFLOAD,
.survey_flags = SURVEY_INFO_TIME_TX |
SURVEY_INFO_TIME_RX |
SURVEY_INFO_TIME_BSS_RX,
.tx_prepare_skb = mt7921_usb_sdio_tx_prepare_skb,
.tx_complete_skb = mt7921_usb_sdio_tx_complete_skb,
.tx_status_data = mt7921_usb_sdio_tx_status_data,
.rx_skb = mt7921_queue_rx_skb,
.rx_check = mt7921_rx_check,
.sta_add = mt7921_mac_sta_add,
.sta_event = mt7921_mac_sta_event,
.sta_remove = mt7921_mac_sta_remove,
.update_survey = mt792x_update_channel,
.set_channel = mt7921_set_channel,
};
static const struct mt792x_hif_ops hif_ops = {
.mcu_init = mt7921u_mcu_init,
.init_reset = mt792xu_init_reset,
.reset = mt7921u_mac_reset,
};
static struct mt76_bus_ops bus_ops = {
.rr = mt792xu_rr,
.wr = mt792xu_wr,
.rmw = mt792xu_rmw,
.read_copy = mt76u_read_copy,
.write_copy = mt792xu_copy,
.type = MT76_BUS_USB,
};
struct usb_device *udev = interface_to_usbdev(usb_intf);
struct ieee80211_ops *ops;
struct ieee80211_hw *hw;
struct mt792x_dev *dev;
struct mt76_dev *mdev;
u8 features;
int ret;
ops = mt792x_get_mac80211_ops(&usb_intf->dev, &mt7921_ops,
(void *)id->driver_info, &features);
if (!ops)
return -ENOMEM;
ops->stop = mt792xu_stop;
mdev = mt76_alloc_device(&usb_intf->dev, sizeof(*dev), ops, &drv_ops);
if (!mdev)
return -ENOMEM;
dev = container_of(mdev, struct mt792x_dev, mt76);
dev->fw_features = features;
dev->hif_ops = &hif_ops;
atomic_set(&dev->mt76.bus_hung, false);
mt792xu_reset_work_init(dev);
usb_reset_device(udev);
usb_set_intfdata(usb_intf, dev);
ret = __mt76u_init(mdev, usb_intf, &bus_ops);
if (ret < 0)
goto error;
mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) |
(mt76_rr(dev, MT_HW_REV) & 0xff);
dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev);
if (mt76_get_field(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY)) {
ret = mt792xu_wfsys_reset(dev);
if (ret)
goto error;
}
ret = mt792xu_mcu_power_on(dev);
if (ret)
goto error;
ret = mt76u_alloc_mcu_queue(&dev->mt76);
if (ret)
goto error;
ret = mt76u_alloc_queues(&dev->mt76);
if (ret)
goto error;
ret = mt792xu_dma_init(dev, false);
if (ret)
goto error;
hw = mt76_hw(dev);
/* check hw sg support in order to enable AMSDU */
hw->max_tx_fragments = mdev->usb.sg_en ? MT_HW_TXP_MAX_BUF_NUM : 1;
ret = mt7921_register_device(dev);
if (ret)
goto error;
return 0;
error:
mt76u_queues_deinit(&dev->mt76);
mt792xu_reset_work_cleanup(dev);
usb_set_intfdata(usb_intf, NULL);
mt76_free_device(&dev->mt76);
return ret;
}
#ifdef CONFIG_PM
static int mt7921u_suspend(struct usb_interface *intf, pm_message_t state)
{
struct mt792x_dev *dev = usb_get_intfdata(intf);
struct mt76_connac_pm *pm = &dev->pm;
int err;
pm->suspended = true;
flush_work(&dev->reset_work);
err = mt76_connac_mcu_set_hif_suspend(&dev->mt76, true, true);
if (err)
goto failed;
mt76u_stop_rx(&dev->mt76);
mt76u_stop_tx(&dev->mt76);
return 0;
failed:
pm->suspended = false;
if (err < 0)
mt792x_reset(&dev->mt76);
return err;
}
static int mt7921u_resume(struct usb_interface *intf)
{
struct mt792x_dev *dev = usb_get_intfdata(intf);
struct mt76_connac_pm *pm = &dev->pm;
bool reinit = true;
int err, i;
for (i = 0; i < 10; i++) {
u32 val = mt76_rr(dev, MT_WF_SW_DEF_CR_USB_MCU_EVENT);
if (!(val & MT_WF_SW_SER_TRIGGER_SUSPEND)) {
reinit = false;
break;
}
if (val & MT_WF_SW_SER_DONE_SUSPEND) {
mt76_wr(dev, MT_WF_SW_DEF_CR_USB_MCU_EVENT, 0);
break;
}
msleep(20);
}
if (reinit || mt792x_dma_need_reinit(dev)) {
err = mt792xu_dma_init(dev, true);
if (err)
goto failed;
}
err = mt76u_resume_rx(&dev->mt76);
if (err < 0)
goto failed;
err = mt76_connac_mcu_set_hif_suspend(&dev->mt76, false, true);
failed:
pm->suspended = false;
if (err < 0)
mt792x_reset(&dev->mt76);
return err;
}
#endif /* CONFIG_PM */
MODULE_DEVICE_TABLE(usb, mt7921u_device_table);
MODULE_FIRMWARE(MT7921_FIRMWARE_WM);
MODULE_FIRMWARE(MT7921_ROM_PATCH);
static struct usb_driver mt7921u_driver = {
.name = KBUILD_MODNAME,
.id_table = mt7921u_device_table,
.probe = mt7921u_probe,
.disconnect = mt792xu_disconnect,
#ifdef CONFIG_PM
.suspend = mt7921u_suspend,
.resume = mt7921u_resume,
.reset_resume = mt7921u_resume,
#endif /* CONFIG_PM */
.soft_unbind = 1,
.disable_hub_initiated_lpm = 1,
};
module_usb_driver(mt7921u_driver);
MODULE_DESCRIPTION("MediaTek MT7921U (USB) wireless driver");
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
MODULE_LICENSE("Dual BSD/GPL");