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,30 @@
# SPDX-License-Identifier: BSD-3-Clause-Clear
config MT7925_COMMON
tristate
select MT792x_LIB
select WANT_DEV_COREDUMP
config MT7925E
tristate "MediaTek MT7925E (PCIe) support"
select MT7925_COMMON
depends on MAC80211
depends on PCI
help
This adds support for MT7925-based wireless PCIe devices,
which support operation at 6GHz, 5GHz, and 2.4GHz IEEE 802.11be
2x2:2SS 4096-QAM, 160MHz channels.
To compile this driver as a module, choose M here.
config MT7925U
tristate "MediaTek MT7925U (USB) support"
select MT792x_USB
select MT7925_COMMON
depends on MAC80211
depends on USB
help
This adds support for MT7925-based wireless USB devices,
which support operation at 6GHz, 5GHz, and 2.4GHz IEEE 802.11be
2x2:2SS 4096-QAM, 160MHz channels.
To compile this driver as a module, choose M here.
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: BSD-3-Clause-Clear
obj-$(CONFIG_MT7925_COMMON) += mt7925-common.o
obj-$(CONFIG_MT7925E) += mt7925e.o
obj-$(CONFIG_MT7925U) += mt7925u.o
mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o
mt7925-common-$(CONFIG_NL80211_TESTMODE) += testmode.o
mt7925e-y := pci.o pci_mac.o pci_mcu.o
mt7925u-y := usb.o
@@ -0,0 +1,317 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include "mt7925.h"
#include "mcu.h"
static int
mt7925_reg_set(void *data, u64 val)
{
struct mt792x_dev *dev = data;
u32 regval = val;
mt792x_mutex_acquire(dev);
mt7925_mcu_regval(dev, dev->mt76.debugfs_reg, &regval, true);
mt792x_mutex_release(dev);
return 0;
}
static int
mt7925_reg_get(void *data, u64 *val)
{
struct mt792x_dev *dev = data;
u32 regval;
int ret;
mt792x_mutex_acquire(dev);
ret = mt7925_mcu_regval(dev, dev->mt76.debugfs_reg, &regval, false);
mt792x_mutex_release(dev);
if (!ret)
*val = regval;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_regval, mt7925_reg_get, mt7925_reg_set,
"0x%08llx\n");
static int
mt7925_fw_debug_set(void *data, u64 val)
{
struct mt792x_dev *dev = data;
mt792x_mutex_acquire(dev);
dev->fw_debug = (u8)val;
mt7925_mcu_fw_log_2_host(dev, dev->fw_debug);
mt792x_mutex_release(dev);
return 0;
}
static int
mt7925_fw_debug_get(void *data, u64 *val)
{
struct mt792x_dev *dev = data;
*val = dev->fw_debug;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug, mt7925_fw_debug_get,
mt7925_fw_debug_set, "%lld\n");
DEFINE_SHOW_ATTRIBUTE(mt792x_tx_stats);
static void
mt7925_seq_puts_array(struct seq_file *file, const char *str,
s8 val[][2], int len, u8 band_idx)
{
int i;
seq_printf(file, "%-22s:", str);
for (i = 0; i < len; i++)
if (val[i][band_idx] == 127)
seq_printf(file, " %6s", "N.A");
else
seq_printf(file, " %6d", val[i][band_idx]);
seq_puts(file, "\n");
}
#define mt7925_print_txpwr_entry(prefix, rate, idx) \
({ \
mt7925_seq_puts_array(s, #prefix " (tmac)", \
txpwr->rate, \
ARRAY_SIZE(txpwr->rate), \
idx); \
})
static inline void
mt7925_eht_txpwr(struct seq_file *s, struct mt7925_txpwr *txpwr, u8 band_idx)
{
seq_printf(s, "%-22s %6s %6s %6s %6s %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",
"mcs12", "mcs13", "mcs14", "mcs15");
mt7925_print_txpwr_entry(EHT26, eht26, band_idx);
mt7925_print_txpwr_entry(EHT52, eht52, band_idx);
mt7925_print_txpwr_entry(EHT106, eht106, band_idx);
mt7925_print_txpwr_entry(EHT242, eht242, band_idx);
mt7925_print_txpwr_entry(EHT484, eht484, band_idx);
mt7925_print_txpwr_entry(EHT996, eht996, band_idx);
mt7925_print_txpwr_entry(EHT996x2, eht996x2, band_idx);
mt7925_print_txpwr_entry(EHT996x4, eht996x4, band_idx);
mt7925_print_txpwr_entry(EHT26_52, eht26_52, band_idx);
mt7925_print_txpwr_entry(EHT26_106, eht26_106, band_idx);
mt7925_print_txpwr_entry(EHT484_242, eht484_242, band_idx);
mt7925_print_txpwr_entry(EHT996_484, eht996_484, band_idx);
mt7925_print_txpwr_entry(EHT996_484_242, eht996_484_242, band_idx);
mt7925_print_txpwr_entry(EHT996x2_484, eht996x2_484, band_idx);
mt7925_print_txpwr_entry(EHT996x3, eht996x3, band_idx);
mt7925_print_txpwr_entry(EHT996x3_484, eht996x3_484, band_idx);
}
static int
mt7925_txpwr(struct seq_file *s, void *data)
{
struct mt792x_dev *dev = dev_get_drvdata(s->private);
struct mt7925_txpwr *txpwr = NULL;
u8 band_idx = dev->mphy.band_idx;
int ret = 0;
txpwr = devm_kmalloc(dev->mt76.dev, sizeof(*txpwr), GFP_KERNEL);
if (!txpwr)
return -ENOMEM;
mt792x_mutex_acquire(dev);
ret = mt7925_get_txpwr_info(dev, band_idx, txpwr);
mt792x_mutex_release(dev);
if (ret)
goto out;
seq_printf(s, "%-22s %6s %6s %6s %6s\n",
" ", "1m", "2m", "5m", "11m");
mt7925_print_txpwr_entry(CCK, cck, band_idx);
seq_printf(s, "%-22s %6s %6s %6s %6s %6s %6s %6s %6s\n",
" ", "6m", "9m", "12m", "18m", "24m", "36m",
"48m", "54m");
mt7925_print_txpwr_entry(OFDM, ofdm, band_idx);
seq_printf(s, "%-22s %6s %6s %6s %6s %6s %6s %6s %6s\n",
" ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
"mcs6", "mcs7");
mt7925_print_txpwr_entry(HT20, ht20, band_idx);
seq_printf(s, "%-22s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
" ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
"mcs6", "mcs7", "mcs32");
mt7925_print_txpwr_entry(HT40, ht40, band_idx);
seq_printf(s, "%-22s %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");
mt7925_print_txpwr_entry(VHT20, vht20, band_idx);
mt7925_print_txpwr_entry(VHT40, vht40, band_idx);
mt7925_print_txpwr_entry(VHT80, vht80, band_idx);
mt7925_print_txpwr_entry(VHT160, vht160, band_idx);
mt7925_print_txpwr_entry(HE26, he26, band_idx);
mt7925_print_txpwr_entry(HE52, he52, band_idx);
mt7925_print_txpwr_entry(HE106, he106, band_idx);
mt7925_print_txpwr_entry(HE242, he242, band_idx);
mt7925_print_txpwr_entry(HE484, he484, band_idx);
mt7925_print_txpwr_entry(HE996, he996, band_idx);
mt7925_print_txpwr_entry(HE996x2, he996x2, band_idx);
mt7925_eht_txpwr(s, txpwr, band_idx);
out:
devm_kfree(dev->mt76.dev, txpwr);
return ret;
}
static int
mt7925_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;
mt7925_set_runtime_pm(dev);
mt76_connac_power_save_sched(&dev->mphy, pm);
out:
mutex_unlock(&dev->mt76.mutex);
return 0;
}
static int
mt7925_pm_get(void *data, u64 *val)
{
struct mt792x_dev *dev = data;
*val = dev->pm.enable_user;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_pm, mt7925_pm_get, mt7925_pm_set, "%lld\n");
static int
mt7925_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;
mt7925_mcu_set_deep_sleep(dev, pm->ds_enable);
out:
mt792x_mutex_release(dev);
return 0;
}
static int
mt7925_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, mt7925_deep_sleep_get,
mt7925_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 mt7925_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 = mt7925_mcu_chip_config(dev, "assert");
mt792x_mutex_release(dev);
break;
}
return ret;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_reset, NULL, mt7925_chip_reset, "%lld\n");
int mt7925_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,
mt7925_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);
return 0;
}
@@ -0,0 +1,293 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include <linux/etherdevice.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/thermal.h>
#include <linux/firmware.h>
#include "mt7925.h"
#include "regd.h"
#include "mac.h"
#include "mcu.h"
static ssize_t mt7925_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 = mt7925_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, mt7925_thermal_temp, 0);
static struct attribute *mt7925_hwmon_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
NULL,
};
ATTRIBUTE_GROUPS(mt7925_hwmon);
static int mt7925_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, "mt7925_%s",
wiphy_name(wiphy));
if (!name)
return -ENOMEM;
hwmon = devm_hwmon_device_register_with_groups(&wiphy->dev, name, phy,
mt7925_hwmon_groups);
return PTR_ERR_OR_ZERO(hwmon);
}
static void mt7925_mac_init_basic_rates(struct mt792x_dev *dev)
{
int i;
for (i = 0; i < ARRAY_SIZE(mt76_rates); i++) {
u16 rate = mt76_rates[i].hw_value;
u16 idx = MT792x_BASIC_RATES_TBL + i;
rate = FIELD_PREP(MT_TX_RATE_MODE, rate >> 8) |
FIELD_PREP(MT_TX_RATE_IDX, rate & GENMASK(7, 0));
mt7925_mac_set_fixed_rate_table(dev, idx, rate);
}
}
int mt7925_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);
for (i = 0; i < MT792x_WTBL_SIZE; i++)
mt7925_mac_wtbl_update(dev, i,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
for (i = 0; i < 2; i++)
mt792x_mac_init_band(dev, i);
mt7925_mac_init_basic_rates(dev);
memzero_explicit(&dev->mt76.alpha2, sizeof(dev->mt76.alpha2));
return 0;
}
EXPORT_SYMBOL_GPL(mt7925_mac_init);
static int __mt7925_init_hardware(struct mt792x_dev *dev)
{
int ret;
ret = mt792x_mcu_init(dev);
if (ret)
goto out;
ret = mt76_eeprom_override(&dev->mphy);
if (ret)
goto out;
ret = mt7925_mcu_set_eeprom(dev);
if (ret)
goto out;
ret = mt7925_mac_init(dev);
if (ret)
goto out;
if (is_mt7927(&dev->mt76)) {
ret = mt7925_mcu_set_dbdc(&dev->mphy, true);
if (ret) {
dev_warn(dev->mt76.dev,
"MT7927 DBDC enable failed: %d\n", ret);
ret = 0;
}
}
out:
return ret;
}
static int mt7925_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 = __mt7925_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 mt7925_init_work(struct work_struct *work)
{
struct mt792x_dev *dev = container_of(work, struct mt792x_dev,
init_work);
int ret;
ret = mt7925_init_hardware(dev);
if (ret)
return;
mt76_set_stream_caps(&dev->mphy, true);
mt7925_set_stream_he_eht_caps(&dev->phy);
mt792x_config_mac_addr_list(dev);
ret = mt7925_init_mlo_caps(&dev->phy);
if (ret) {
dev_err(dev->mt76.dev, "MLO init failed\n");
return;
}
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 = mt7925_init_debugfs(dev);
if (ret) {
dev_err(dev->mt76.dev, "register debugfs failed\n");
return;
}
ret = mt7925_thermal_init(&dev->phy);
if (ret) {
dev_err(dev->mt76.dev, "thermal init failed\n");
return;
}
ret = mt7925_mcu_set_thermal_protect(dev);
if (ret) {
dev_err(dev->mt76.dev, "thermal protection enable failed\n");
return;
}
/* we support chip reset now */
dev->hw_init_done = true;
mt7925_mcu_set_deep_sleep(dev, dev->pm.ds_enable);
}
int mt7925_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_DELAYED_WORK(&dev->mlo_pm_work, mt7925_mlo_pm_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);
spin_lock_init(&dev->pm.txq_lock);
INIT_DELAYED_WORK(&dev->mphy.mac_work, mt792x_mac_work);
INIT_DELAYED_WORK(&dev->phy.scan_work, mt7925_scan_work);
INIT_DELAYED_WORK(&dev->coredump.work, mt7925_coredump_work);
#if IS_ENABLED(CONFIG_IPV6)
INIT_WORK(&dev->ipv6_ns_work, mt7925_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, mt7925_mac_reset_work);
INIT_WORK(&dev->init_work, mt7925_init_work);
INIT_WORK(&dev->phy.roc_work, mt7925_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;
/* MT7927: runtime PM crashes BT firmware on the shared CONNINFRA domain */
if (!mt76_is_usb(&dev->mt76) && !is_mt7927(&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 = mt7925_regd_notifier;
dev->mphy.sband_2g.sband.ht_cap.cap |=
IEEE80211_HT_CAP_LDPC_CODING |
IEEE80211_HT_CAP_MAX_AMSDU;
dev->mphy.sband_2g.sband.ht_cap.ampdu_density =
IEEE80211_HT_MPDU_DENSITY_2;
dev->mphy.sband_5g.sband.ht_cap.cap |=
IEEE80211_HT_CAP_LDPC_CODING |
IEEE80211_HT_CAP_MAX_AMSDU;
dev->mphy.sband_2g.sband.ht_cap.ampdu_density =
IEEE80211_HT_MPDU_DENSITY_1;
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);
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(mt7925_register_device);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2023 MediaTek Inc. */
#ifndef __MT7925_MAC_H
#define __MT7925_MAC_H
#include "../mt76_connac3_mac.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
static inline u32 mt7925_mac_wtbl_lmac_addr(struct mt792x_dev *dev, u16 wcid, u8 dw)
{
mt76_wr(dev, MT_WTBLON_TOP_WDUCR,
FIELD_PREP(MT_WTBLON_TOP_WDUCR_GROUP, (wcid >> 7)));
return MT_WTBL_LMAC_OFFS(wcid, dw);
}
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,722 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2023 MediaTek Inc. */
#ifndef __MT7925_MCU_H
#define __MT7925_MCU_H
#include "../mt76_connac_mcu.h"
/* ext event table */
enum {
MCU_EXT_EVENT_RATE_REPORT = 0x87,
};
struct mt7925_mcu_eeprom_info {
__le32 addr;
__le32 valid;
u8 data[MT7925_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)
struct mt7925_mcu_rxd {
__le32 rxd[8];
__le16 len;
__le16 pkt_type_id;
u8 eid;
u8 seq;
u8 option;
u8 __rsv;
u8 ext_eid;
u8 __rsv1[2];
u8 s2d_index;
u8 tlv[];
};
struct mt7925_mcu_uni_event {
u8 cid;
u8 pad[3];
__le32 status; /* 0: success, others: fail */
} __packed;
enum {
MT_EBF = BIT(0), /* explicit beamforming */
MT_IBF = BIT(1) /* implicit beamforming */
};
struct mt7925_mcu_reg_event {
__le32 reg;
__le32 val;
} __packed;
struct mt7925_mcu_ant_id_config {
u8 ant_id[4];
} __packed;
struct mt7925_txpwr_req {
u8 _rsv[4];
__le16 tag;
__le16 len;
u8 format_id;
u8 catg;
u8 band_idx;
u8 _rsv1;
} __packed;
struct mt7925_txpwr_event {
u8 rsv[4];
__le16 tag;
__le16 len;
u8 catg;
u8 band_idx;
u8 ch_band;
u8 format; /* 0:Legacy, 1:HE */
/* Rate power info */
struct mt7925_txpwr txpwr;
s8 pwr_max;
s8 pwr_min;
u8 rsv1;
} __packed;
enum {
TM_SWITCH_MODE,
TM_SET_AT_CMD,
TM_QUERY_AT_CMD,
};
enum {
MT7925_TM_NORMAL,
MT7925_TM_TESTMODE,
MT7925_TM_ICAP,
MT7925_TM_ICAP_OVERLAP,
MT7925_TM_WIFISPECTRUM,
};
struct mt7925_rftest_evt {
__le32 param0;
__le32 param1;
} __packed;
enum {
UNI_CHANNEL_SWITCH,
UNI_CHANNEL_RX_PATH,
};
enum {
UNI_CHIP_CONFIG_CHIP_CFG = 0x2,
UNI_CHIP_CONFIG_NIC_CAPA = 0x3,
};
enum {
UNI_BAND_CONFIG_RADIO_ENABLE,
UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08,
UNI_BAND_CONFIG_SET_MAC80211_RX_FILTER = 0x0C,
};
enum {
UNI_WSYS_CONFIG_FW_LOG_CTRL,
UNI_WSYS_CONFIG_FW_DBG_CTRL,
};
enum {
UNI_EFUSE_ACCESS = 1,
UNI_EFUSE_BUFFER_MODE,
UNI_EFUSE_FREE_BLOCK,
UNI_EFUSE_BUFFER_RD,
};
enum {
UNI_CMD_ACCESS_REG_BASIC = 0x0,
UNI_CMD_ACCESS_RF_REG_BASIC,
};
enum {
UNI_MBMC_SETTING = 0,
UNI_MBMC_NO_RESP_SETTING = 1,
};
enum {
UNI_EVENT_SCAN_DONE_BASIC = 0,
UNI_EVENT_SCAN_DONE_CHNLINFO = 2,
UNI_EVENT_SCAN_DONE_NLO = 3,
};
enum {
UNI_CMD_RSSI_MONITOR_SET = 0,
};
enum {
UNI_EVENT_RSSI_MONITOR_INFO = 0,
};
enum connac3_mcu_cipher_type {
CONNAC3_CIPHER_NONE = 0,
CONNAC3_CIPHER_WEP40 = 1,
CONNAC3_CIPHER_TKIP = 2,
CONNAC3_CIPHER_AES_CCMP = 4,
CONNAC3_CIPHER_WEP104 = 5,
CONNAC3_CIPHER_BIP_CMAC_128 = 6,
CONNAC3_CIPHER_WEP128 = 7,
CONNAC3_CIPHER_WAPI = 8,
CONNAC3_CIPHER_CCMP_256 = 10,
CONNAC3_CIPHER_GCMP = 11,
CONNAC3_CIPHER_GCMP_256 = 12,
};
struct mt7925_mcu_scan_chinfo_event {
u8 nr_chan;
u8 alpha2[3];
} __packed;
enum {
UNI_SCAN_REQ = 1,
UNI_SCAN_CANCEL = 2,
UNI_SCAN_SCHED_REQ = 3,
UNI_SCAN_SCHED_ENABLE = 4,
UNI_SCAN_SSID = 10,
UNI_SCAN_BSSID,
UNI_SCAN_CHANNEL,
UNI_SCAN_IE,
UNI_SCAN_MISC,
UNI_SCAN_SSID_MATCH_SETS,
};
enum {
UNI_SNIFFER_ENABLE,
UNI_SNIFFER_CONFIG,
};
#define MT7925_RNR_SCAN_MAX_BSSIDS 10
struct scan_hdr_tlv {
/* fixed field */
u8 seq_num;
u8 bss_idx;
u8 pad[2];
/* tlv */
u8 data[];
} __packed;
struct scan_req_tlv {
__le16 tag;
__le16 len;
u8 scan_type; /* 0: PASSIVE SCAN
* 1: ACTIVE SCAN
*/
u8 probe_req_num; /* Number of probe request for each SSID */
u8 scan_func; /* BIT(0) Enable random MAC scan
* BIT(1) Disable DBDC scan type 1~3.
* BIT(2) Use DBDC scan type 3 (dedicated one RF to scan).
*/
u8 src_mask;
__le16 channel_min_dwell_time;
__le16 channel_dwell_time; /* channel Dwell interval */
__le16 timeout_value;
__le16 probe_delay_time;
__le32 func_mask_ext;
} __packed;
struct scan_ssid_tlv {
__le16 tag;
__le16 len;
u8 ssid_type; /* BIT(0) wildcard SSID
* BIT(1) P2P wildcard SSID
* BIT(2) specified SSID + wildcard SSID
* BIT(2) + ssid_type_ext BIT(0) specified SSID only
*/
u8 ssids_num;
u8 is_short_ssid;
u8 pad;
struct mt76_connac_mcu_scan_ssid ssids[MT7925_RNR_SCAN_MAX_BSSIDS];
} __packed;
struct scan_bssid_tlv {
__le16 tag;
__le16 len;
u8 bssid[ETH_ALEN];
u8 match_ch;
u8 match_ssid_ind;
u8 rcpi;
u8 match_short_ssid_ind;
u8 pad[2];
} __packed;
struct scan_chan_info_tlv {
__le16 tag;
__le16 len;
u8 channel_type; /* 0: Full channels
* 1: Only 2.4GHz channels
* 2: Only 5GHz channels
* 3: P2P social channel only (channel #1, #6 and #11)
* 4: Specified channels
* Others: Reserved
*/
u8 channels_num; /* valid when channel_type is 4 */
u8 pad[2];
struct mt76_connac_mcu_scan_channel channels[64];
} __packed;
struct scan_ie_tlv {
__le16 tag;
__le16 len;
__le16 ies_len;
u8 band;
u8 pad;
u8 ies[];
};
struct scan_misc_tlv {
__le16 tag;
__le16 len;
u8 random_mac[ETH_ALEN];
u8 rsv[2];
};
struct scan_sched_req {
__le16 tag;
__le16 len;
u8 version;
u8 stop_on_match;
u8 intervals_num;
u8 scan_func;
__le16 intervals[MT76_CONNAC_MAX_NUM_SCHED_SCAN_INTERVAL];
};
struct scan_sched_ssid_match_sets {
__le16 tag;
__le16 len;
u8 match_num;
u8 rsv[3];
struct mt76_connac_mcu_scan_match match[MT76_CONNAC_MAX_SCAN_MATCH];
};
struct scan_sched_enable {
__le16 tag;
__le16 len;
u8 active;
u8 rsv[3];
};
struct mbmc_set_req {
u8 pad[4];
u8 data[];
} __packed;
struct mbmc_conf_tlv {
__le16 tag;
__le16 len;
u8 mbmc_en;
u8 band;
u8 pad[2];
} __packed;
struct edca {
__le16 tag;
__le16 len;
u8 queue;
u8 set;
u8 cw_min;
u8 cw_max;
__le16 txop;
u8 aifs;
u8 __rsv;
};
struct bss_req_hdr {
u8 bss_idx;
u8 __rsv[3];
} __packed;
struct bss_rate_tlv {
__le16 tag;
__le16 len;
u8 __rsv1[2];
__le16 basic_rate;
__le16 bc_trans;
__le16 mc_trans;
u8 short_preamble;
u8 bc_fixed_rate;
u8 mc_fixed_rate;
u8 __rsv2;
} __packed;
struct bss_mld_tlv {
__le16 tag;
__le16 len;
u8 group_mld_id;
u8 own_mld_id;
u8 mac_addr[ETH_ALEN];
u8 remap_idx;
u8 link_id;
u8 eml_enable;
u8 max_link_num;
u8 hybrid_mode;
u8 __rsv[3];
} __packed;
struct bss_eht_tlv {
__le16 tag;
__le16 len;
u8 is_eht_op_present;
u8 is_eth_dscb_present;
u8 eht_ctrl;
u8 eht_ccfs0;
u8 eht_ccfs1;
u8 pad1;
__le16 eht_dis_sub_chan_bitmap;
u8 pad2[4];
} __packed;
struct sta_rec_ba_uni {
__le16 tag;
__le16 len;
u8 tid;
u8 ba_type;
u8 amsdu;
u8 ba_en;
__le16 ssn;
__le16 winsize;
u8 ba_rdd_rro;
u8 __rsv[3];
} __packed;
struct sta_rec_eht {
__le16 tag;
__le16 len;
u8 tid_bitmap;
u8 _rsv;
__le16 mac_cap;
__le64 phy_cap;
__le64 phy_cap_ext;
u8 mcs_map_bw20[4];
u8 mcs_map_bw80[3];
u8 mcs_map_bw160[3];
u8 mcs_map_bw320[3];
u8 _rsv2[3];
} __packed;
struct sta_rec_sec_uni {
__le16 tag;
__le16 len;
u8 add;
u8 tx_key;
u8 key_type;
u8 is_authenticator;
u8 peer_addr[6];
u8 bss_idx;
u8 cipher_id;
u8 key_id;
u8 key_len;
u8 wlan_idx;
u8 mgmt_prot;
u8 key[32];
u8 key_rsc[16];
} __packed;
struct sta_rec_hdr_trans {
__le16 tag;
__le16 len;
u8 from_ds;
u8 to_ds;
u8 dis_rx_hdr_tran;
u8 rsv;
} __packed;
struct sta_rec_mld {
__le16 tag;
__le16 len;
u8 mac_addr[ETH_ALEN];
__le16 primary_id;
__le16 secondary_id;
__le16 wlan_id;
u8 link_num;
u8 rsv[3];
struct {
__le16 wlan_id;
u8 bss_idx;
u8 rsv;
} __packed link[2];
} __packed;
struct sta_rec_eht_mld {
__le16 tag;
__le16 len;
u8 nsep;
u8 mld_type;
u8 __rsv1[1];
u8 str_cap[3];
u8 eml_cap[3];
u8 __rsv2[3];
} __packed;
struct bss_ifs_time_tlv {
__le16 tag;
__le16 len;
u8 slot_valid;
u8 sifs_valid;
u8 rifs_valid;
u8 eifs_valid;
__le16 slot_time;
__le16 sifs_time;
__le16 rifs_time;
__le16 eifs_time;
u8 eifs_cck_valid;
u8 rsv;
__le16 eifs_cck_time;
} __packed;
struct bss_rlm_tlv {
__le16 tag;
__le16 len;
u8 control_channel;
u8 center_chan;
u8 center_chan2;
u8 bw;
u8 tx_streams;
u8 rx_streams;
u8 ht_op_info;
u8 sco;
u8 band;
u8 pad[3];
} __packed;
#define MT7925_STA_UPDATE_MAX_SIZE (sizeof(struct sta_req_hdr) + \
sizeof(struct sta_rec_basic) + \
sizeof(struct sta_rec_bf) + \
sizeof(struct sta_rec_ht) + \
sizeof(struct sta_rec_he_v2) + \
sizeof(struct sta_rec_ba_uni) + \
sizeof(struct sta_rec_vht) + \
sizeof(struct sta_rec_uapsd) + \
sizeof(struct sta_rec_amsdu) + \
sizeof(struct sta_rec_bfee) + \
sizeof(struct sta_rec_phy) + \
sizeof(struct sta_rec_ra) + \
sizeof(struct sta_rec_sec_uni) + \
sizeof(struct sta_rec_ra_fixed) + \
sizeof(struct sta_rec_he_6g_capa) + \
sizeof(struct sta_rec_eht) + \
sizeof(struct sta_rec_hdr_trans) + \
sizeof(struct sta_rec_mld) + \
sizeof(struct tlv) * 2 + \
sizeof(struct sta_rec_remove))
#define MT7925_BSS_UPDATE_MAX_SIZE (sizeof(struct bss_req_hdr) + \
sizeof(struct mt76_connac_bss_basic_tlv) + \
sizeof(struct mt76_connac_bss_qos_tlv) + \
sizeof(struct bss_rate_tlv) + \
sizeof(struct bss_mld_tlv) + \
sizeof(struct bss_info_uni_he) + \
sizeof(struct bss_info_uni_bss_color) + \
sizeof(struct bss_ifs_time_tlv) + \
sizeof(struct bss_rlm_tlv) + \
sizeof(struct tlv))
#define MT_CONNAC3_SKU_POWER_LIMIT 449
struct mt7925_sku_tlv {
u8 channel;
s8 pwr_limit[MT_CONNAC3_SKU_POWER_LIMIT];
} __packed;
struct mt7925_tx_power_limit_tlv {
u8 rsv[4];
__le16 tag;
__le16 len;
/* DW0 - common info*/
u8 ver;
u8 pad0;
__le16 rsv1;
/* DW1 - cmd hint */
u8 n_chan; /* # channel */
u8 band; /* 2.4GHz - 5GHz - 6GHz */
u8 last_msg;
u8 limit_type;
/* DW3 */
u8 alpha2[4]; /* regulatory_request.alpha2 */
u8 pad2[32];
u8 data[];
} __packed;
struct mt7925_arpns_tlv {
__le16 tag;
__le16 len;
u8 enable;
u8 ips_num;
u8 rsv[2];
} __packed;
struct mt7925_wow_pattern_tlv {
__le16 tag;
__le16 len;
u8 bss_idx;
u8 index; /* pattern index */
u8 enable; /* 0: disable
* 1: enable
*/
u8 data_len; /* pattern length */
u8 offset;
u8 mask[MT76_CONNAC_WOW_MASK_MAX_LEN];
u8 pattern[MT76_CONNAC_WOW_PATTEN_MAX_LEN];
u8 rsv[4];
};
struct roc_acquire_tlv {
__le16 tag;
__le16 len;
u8 bss_idx;
u8 tokenid;
u8 control_channel;
u8 sco;
u8 band;
u8 bw;
u8 center_chan;
u8 center_chan2;
u8 bw_from_ap;
u8 center_chan_from_ap;
u8 center_chan2_from_ap;
u8 reqtype;
__le32 maxinterval;
u8 dbdcband;
u8 rsv[3];
} __packed;
enum ENUM_CMD_TEST_CTRL_ACT {
CMD_TEST_CTRL_ACT_SWITCH_MODE = 0,
CMD_TEST_CTRL_ACT_SET_AT = 1,
CMD_TEST_CTRL_ACT_GET_AT = 2,
CMD_TEST_CTRL_ACT_SET_AT_ENG = 3,
CMD_TEST_CTRL_ACT_GET_AT_ENG = 4,
CMD_TEST_CTRL_ACT_NUM
};
enum ENUM_CMD_TEST_CTRL_ACT_SWITCH_MODE_OP {
CMD_TEST_CTRL_ACT_SWITCH_MODE_NORMAL = 0,
CMD_TEST_CTRL_ACT_SWITCH_MODE_RF_TEST = 1,
CMD_TEST_CTRL_ACT_SWITCH_MODE_ICAP = 2,
CMD_TEST_CTRL_ACT_SWITCH_MODE_NUM
};
union testmode_data {
__le32 op_mode;
__le32 channel_freq;
u8 rf_at_info[84];
};
union testmode_evt {
__le32 op_mode;
__le32 channel_freq;
u8 rf_at_info[1024];
};
struct uni_cmd_testmode_ctrl {
u16 tag;
u16 length;
u8 action;
u8 reserved[3];
union testmode_data data;
} __packed;
struct mt7925_rftest_cmd {
u8 padding[4];
struct uni_cmd_testmode_ctrl ctrl;
} __packed;
static inline enum connac3_mcu_cipher_type
mt7925_mcu_get_cipher(int cipher)
{
switch (cipher) {
case WLAN_CIPHER_SUITE_WEP40:
return CONNAC3_CIPHER_WEP40;
case WLAN_CIPHER_SUITE_WEP104:
return CONNAC3_CIPHER_WEP104;
case WLAN_CIPHER_SUITE_TKIP:
return CONNAC3_CIPHER_TKIP;
case WLAN_CIPHER_SUITE_AES_CMAC:
return CONNAC3_CIPHER_BIP_CMAC_128;
case WLAN_CIPHER_SUITE_CCMP:
return CONNAC3_CIPHER_AES_CCMP;
case WLAN_CIPHER_SUITE_CCMP_256:
return CONNAC3_CIPHER_CCMP_256;
case WLAN_CIPHER_SUITE_GCMP:
return CONNAC3_CIPHER_GCMP;
case WLAN_CIPHER_SUITE_GCMP_256:
return CONNAC3_CIPHER_GCMP_256;
case WLAN_CIPHER_SUITE_SMS4:
return CONNAC3_CIPHER_WAPI;
default:
return CONNAC3_CIPHER_NONE;
}
}
int mt7925_mcu_set_dbdc(struct mt76_phy *phy, bool enable);
int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
struct ieee80211_scan_request *scan_req);
int mt7925_mcu_cancel_hw_scan(struct mt76_phy *phy,
struct ieee80211_vif *vif);
int mt7925_mcu_sched_scan_req(struct mt76_phy *phy,
struct ieee80211_vif *vif,
struct cfg80211_sched_scan_request *sreq,
struct ieee80211_scan_ies *ies);
int mt7925_mcu_sched_scan_enable(struct mt76_phy *phy,
struct ieee80211_vif *vif,
bool enable);
void mt7925_mcu_del_dev(struct mt76_dev *mdev,
struct ieee80211_vif *vif);
int mt7925_mcu_add_bss_info(struct mt792x_phy *phy,
struct ieee80211_chanctx_conf *ctx,
struct ieee80211_bss_conf *link_conf,
struct ieee80211_link_sta *link_sta,
int enable);
int mt7925_mcu_add_bss_info_sta(struct mt792x_phy *phy,
struct ieee80211_chanctx_conf *ctx,
struct ieee80211_bss_conf *link_conf,
struct ieee80211_link_sta *link_sta,
u16 bmc_tx_wlan_idx,
u16 sta_wlan_idx,
int enable);
int mt7925_mcu_set_timing(struct mt792x_phy *phy,
struct ieee80211_bss_conf *link_conf);
int mt7925_mcu_set_deep_sleep(struct mt792x_dev *dev, bool enable);
int mt7925_mcu_set_thermal_protect(struct mt792x_dev *dev);
int mt7925_mcu_set_channel_domain(struct mt76_phy *phy);
int mt7925_mcu_set_radio_en(struct mt792x_phy *phy, bool enable);
int mt7925_mcu_set_chctx(struct mt76_phy *phy, struct mt76_vif_link *mvif,
struct ieee80211_bss_conf *link_conf,
struct ieee80211_chanctx_conf *ctx);
int mt7925_mcu_set_eht_pp(struct mt76_phy *phy, struct mt76_vif_link *mvif,
struct ieee80211_bss_conf *link_conf,
struct ieee80211_chanctx_conf *ctx);
int mt7925_mcu_set_rate_txpower(struct mt76_phy *phy);
int mt7925_mcu_update_arp_filter(struct mt76_dev *dev,
struct ieee80211_bss_conf *link_conf);
int
mt7925_mcu_uni_bss_bcnft(struct mt792x_dev *dev,
struct ieee80211_bss_conf *link_conf, bool enable);
#endif
@@ -0,0 +1,408 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2023 MediaTek Inc. */
#ifndef __MT7925_H
#define __MT7925_H
#include "../mt792x.h"
#include "regs.h"
#define MT7925_BEACON_RATES_TBL 25
#define MT7925_TX_RING_SIZE 2048
#define MT7925_TX_MCU_RING_SIZE 256
#define MT7925_TX_FWDL_RING_SIZE 128
#define MT7925_RX_RING_SIZE 1536
#define MT7925_RX_MCU_RING_SIZE 512
#define MT7925_EEPROM_SIZE 3584
#define MT7925_TOKEN_SIZE 8192
#define MT7925_EEPROM_BLOCK_SIZE 16
#define MT7925_SKU_RATE_NUM 161
#define MT7925_SKU_MAX_DELTA_IDX MT7925_SKU_RATE_NUM
#define MT7925_SKU_TABLE_SIZE (MT7925_SKU_RATE_NUM + 1)
#define MCU_UNI_EVENT_ROC 0x27
#define HIF_TRAFFIC_IDLE 0x2
enum {
UNI_EVENT_HIF_CTRL_BASIC = 0,
UNI_EVENT_HIF_CTRL_TAG_NUM
};
struct mt7925_mcu_hif_ctrl_basic_tlv {
__le16 tag;
__le16 len;
u8 cid;
u8 pad[3];
u32 status;
u8 hif_type;
u8 hif_tx_traffic_status;
u8 hif_rx_traffic_status;
u8 hifsuspend;
u8 rsv[4];
} __packed;
enum {
UNI_ROC_ACQUIRE,
UNI_ROC_ABORT,
UNI_ROC_SUB_LINK = 3,
UNI_ROC_NUM
};
enum mt7925_roc_req {
MT7925_ROC_REQ_JOIN,
MT7925_ROC_REQ_ROC,
MT7925_ROC_REQ_SUB_LINK,
MT7925_ROC_REQ_MLSR_AG = 10,
MT7925_ROC_REQ_MLSR_AA,
MT7925_ROC_REQ_NUM
};
enum {
UNI_EVENT_ROC_GRANT = 0,
UNI_EVENT_ROC_GRANT_SUB_LINK = 4,
UNI_EVENT_ROC_TAG_NUM
};
struct mt7925_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;
struct mt7925_beacon_loss_tlv {
__le16 tag;
__le16 len;
u8 reason;
u8 nr_btolink;
u8 pad[2];
} __packed;
struct mt7925_uni_beacon_loss_event {
struct {
u8 bss_idx;
u8 pad[3];
} __packed hdr;
struct mt7925_beacon_loss_tlv beacon_loss;
} __packed;
struct mt7925_uni_rssi_monitor_event {
__le16 tag;
__le16 len;
__le32 rssi;
} __packed;
#define to_rssi(field, rxv) ((FIELD_GET(field, rxv) - 220) / 2)
#define to_rcpi(rssi) (2 * (rssi) + 220)
enum mt7925_txq_id {
MT7925_TXQ_BAND0,
MT7925_TXQ_BAND1,
MT7925_TXQ_MCU_WM = 15,
MT7925_TXQ_FWDL,
};
enum mt7925_rxq_id {
MT7925_RXQ_BAND0 = 2,
MT7925_RXQ_BAND1,
MT7925_RXQ_MCU_WM = 0,
MT7925_RXQ_MCU_WM2, /* for tx done */
};
enum mt7927_txq_id {
MT7927_TXQ_BAND0 = MT7925_TXQ_BAND0,
MT7927_TXQ_BAND1 = MT7925_TXQ_BAND1,
MT7927_TXQ_MCU_WM = MT7925_TXQ_MCU_WM,
MT7927_TXQ_FWDL = MT7925_TXQ_FWDL,
};
enum mt7927_rxq_id {
MT7927_RXQ_BAND0 = 4,
MT7927_RXQ_MCU_WM = 6,
MT7927_RXQ_DATA2 = 7,
};
enum {
MODE_OPEN = 0,
MODE_SHARED = 1,
MODE_WPA = 3,
MODE_WPA_PSK = 4,
MODE_WPA_NONE = 5,
MODE_WPA2 = 6,
MODE_WPA2_PSK = 7,
MODE_WPA3_SAE = 11,
};
enum {
MT7925_CLC_POWER,
MT7925_CLC_CHAN,
MT7925_CLC_MAX_NUM,
};
struct mt7925_clc_rule_v2 {
u32 flag;
u8 alpha2[2];
u8 rsv[10];
} __packed;
struct mt7925_clc_rule {
u8 alpha2[2];
u8 type[2];
u8 seg_idx;
u8 flag; /* UNII4~8 ctrl flag */
u8 rsv[2];
} __packed;
struct mt7925_clc_segment {
u8 idx;
u8 rsv1[3];
u32 offset;
u32 len;
u8 rsv2[4];
} __packed;
struct mt7925_clc_type0 {
u8 nr_country;
u8 type;
u8 nr_seg;
u8 rsv[7];
} __packed;
struct mt7925_clc_type2 {
u8 type;
u8 rsv[9];
} __packed;
struct mt7925_clc {
__le32 len;
u8 idx;
u8 ver;
union {
struct mt7925_clc_type0 t0;
struct mt7925_clc_type2 t2;
};
u8 data[];
} __packed;
enum mt7925_eeprom_field {
MT_EE_CHIP_ID = 0x000,
MT_EE_VERSION = 0x002,
MT_EE_MAC_ADDR = 0x004,
MT_EE_HW_TYPE = 0xa71,
__MT_EE_MAX = 0x9ff
};
#define MT_EE_HW_TYPE_ENCAP GENMASK(1, 0)
enum {
TXPWR_USER,
TXPWR_EEPROM,
TXPWR_MAC,
TXPWR_MAX_NUM,
};
struct mt7925_txpwr {
s8 cck[4][2];
s8 ofdm[8][2];
s8 ht20[8][2];
s8 ht40[9][2];
s8 vht20[12][2];
s8 vht40[12][2];
s8 vht80[12][2];
s8 vht160[12][2];
s8 he26[12][2];
s8 he52[12][2];
s8 he106[12][2];
s8 he242[12][2];
s8 he484[12][2];
s8 he996[12][2];
s8 he996x2[12][2];
s8 eht26[16][2];
s8 eht52[16][2];
s8 eht106[16][2];
s8 eht242[16][2];
s8 eht484[16][2];
s8 eht996[16][2];
s8 eht996x2[16][2];
s8 eht996x4[16][2];
s8 eht26_52[16][2];
s8 eht26_106[16][2];
s8 eht484_242[16][2];
s8 eht996_484[16][2];
s8 eht996_484_242[16][2];
s8 eht996x2_484[16][2];
s8 eht996x3[16][2];
s8 eht996x3_484[16][2];
};
static inline u8 mt7927_band_idx(enum nl80211_band band)
{
switch (band) {
case NL80211_BAND_2GHZ:
return 0;
case NL80211_BAND_5GHZ:
case NL80211_BAND_6GHZ:
default:
return 1;
}
}
extern const struct ieee80211_ops mt7925_ops;
int __mt7925_start(struct mt792x_phy *phy);
int mt7925_register_device(struct mt792x_dev *dev);
void mt7925_unregister_device(struct mt792x_dev *dev);
int mt7925_run_firmware(struct mt792x_dev *dev);
int mt7925_mcu_set_bss_pm(struct mt792x_dev *dev,
struct ieee80211_bss_conf *link_conf,
bool enable);
int mt7925_mcu_sta_update(struct mt792x_dev *dev,
struct ieee80211_link_sta *link_sta,
struct ieee80211_vif *vif,
struct mt792x_link_sta *mlink,
bool enable,
enum mt76_sta_info_state state);
int mt7925_mcu_set_chan_info(struct mt792x_phy *phy, u16 tag);
int mt7925_mcu_set_tx(struct mt792x_dev *dev, struct ieee80211_bss_conf *bss_conf);
int mt7925_mcu_set_eeprom(struct mt792x_dev *dev);
int mt7925_mcu_get_rx_rate(struct mt792x_phy *phy, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct rate_info *rate);
int mt7925_mcu_fw_log_2_host(struct mt792x_dev *dev, u8 ctrl);
void mt7925_mcu_rx_event(struct mt792x_dev *dev, struct sk_buff *skb);
int mt7925_mcu_chip_config(struct mt792x_dev *dev, const char *cmd);
int mt7925_mcu_set_rxfilter(struct mt792x_dev *dev, u32 fif,
u8 bit_op, u32 bit_map);
int mt7925_mac_init(struct mt792x_dev *dev);
int mt7925_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
bool mt7925_mac_wtbl_update(struct mt792x_dev *dev, int idx, u32 mask);
int mt7925_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, enum mt76_sta_event ev);
void mt7925_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
void mt7925_mac_reset_work(struct work_struct *work);
int mt7925e_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 mt7925_tx_token_put(struct mt792x_dev *dev);
bool mt7925_rx_check(struct mt76_dev *mdev, void *data, int len);
void mt7925_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
struct sk_buff *skb, u32 *info);
void mt7925_stats_work(struct work_struct *work);
void mt7925_set_stream_he_eht_caps(struct mt792x_phy *phy);
int mt7925_init_mlo_caps(struct mt792x_phy *phy);
int mt7925_init_debugfs(struct mt792x_dev *dev);
int mt7925_mcu_set_beacon_filter(struct mt792x_dev *dev,
struct ieee80211_vif *vif,
bool enable);
int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev,
struct ieee80211_ampdu_params *params,
struct ieee80211_vif *vif, bool enable);
int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev,
struct ieee80211_ampdu_params *params,
struct ieee80211_vif *vif, bool enable);
void mt7925_mlo_pm_work(struct work_struct *work);
void mt7925_scan_work(struct work_struct *work);
void mt7925_roc_work(struct work_struct *work);
void mt7925_csa_work(struct work_struct *work);
int mt7925_mcu_uni_bss_ps(struct mt792x_dev *dev,
struct ieee80211_bss_conf *link_conf);
void mt7925_coredump_work(struct work_struct *work);
int mt7925_get_txpwr_info(struct mt792x_dev *dev, u8 band_idx,
struct mt7925_txpwr *txpwr);
void mt7925_mac_set_fixed_rate_table(struct mt792x_dev *dev,
u8 tbl_idx, u16 rate_idx);
void mt7925_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
struct sk_buff *skb, struct mt76_wcid *wcid,
struct ieee80211_key_conf *key, int pid,
enum mt76_txq_id qid, u32 changed);
void mt7925_txwi_free(struct mt792x_dev *dev, struct mt76_txwi_cache *t,
struct ieee80211_sta *sta, struct mt76_wcid *wcid,
struct list_head *free_list);
int mt7925_mcu_parse_response(struct mt76_dev *mdev, int cmd,
struct sk_buff *skb, int seq);
int mt7925e_mac_reset(struct mt792x_dev *dev);
int mt7925e_mcu_init(struct mt792x_dev *dev);
void mt7925_mac_add_txs(struct mt792x_dev *dev, void *data);
void mt7925_set_runtime_pm(struct mt792x_dev *dev);
void mt7925_mcu_set_suspend_iter(void *priv, u8 *mac,
struct ieee80211_vif *vif);
void mt7925_connac_mcu_set_suspend_iter(void *priv, u8 *mac,
struct ieee80211_vif *vif);
void mt7925_set_ipv6_ns_work(struct work_struct *work);
int mt7925_mcu_set_sniffer(struct mt792x_dev *dev, struct ieee80211_vif *vif,
bool enable);
int mt7925_mcu_config_sniffer(struct mt792x_vif *vif,
struct ieee80211_chanctx_conf *ctx);
int mt7925_mcu_get_temperature(struct mt792x_phy *phy);
int mt7925_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 mt7925_usb_sdio_tx_complete_skb(struct mt76_dev *mdev,
struct mt76_queue_entry *e);
bool mt7925_usb_sdio_tx_status_data(struct mt76_dev *mdev, u8 *update);
int mt7925_mcu_uni_add_beacon_offload(struct mt792x_dev *dev,
struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
bool enable);
int mt7925_set_tx_sar_pwr(struct ieee80211_hw *hw,
const struct cfg80211_sar_specs *sar);
int mt7925_mcu_regval(struct mt792x_dev *dev, u32 regidx, u32 *val, bool set);
int mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
enum environment_cap env_cap);
int mt7925_mcu_set_mlo_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf,
u16 sel_links, int duration, u8 token_id);
int mt7925_mcu_set_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf,
struct ieee80211_channel *chan, int duration,
enum mt7925_roc_req type, u8 token_id);
int mt7925_mcu_abort_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf,
u8 token_id);
void mt7925_roc_abort_sync(struct mt792x_dev *dev);
int mt7925_mcu_fill_message(struct mt76_dev *mdev, struct sk_buff *skb,
int cmd, int *wait_seq);
int mt7925_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
struct mt76_connac_sta_key_conf *sta_key_conf,
struct ieee80211_key_conf *key, int mcu_cmd,
struct mt76_wcid *wcid, enum set_key_cmd cmd,
struct mt792x_sta *msta);
int mt7925_mcu_set_rts_thresh(struct mt792x_phy *phy, u32 val);
int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev,
struct ieee80211_vif *vif,
struct mt792x_bss_conf *mconf,
struct mt792x_link_sta *mlink);
int mt7925_mcu_wf_rf_pin_ctrl(struct mt792x_phy *phy);
int mt7925_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void *data, int len);
int mt7925_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
struct netlink_callback *cb, void *data, int len);
int mt7925_mcu_set_rssimonitor(struct mt792x_dev *dev, struct ieee80211_vif *vif);
#endif
@@ -0,0 +1,708 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include "mt7925.h"
#include "mac.h"
#include "mcu.h"
#include "regd.h"
#include "../dma.h"
static const struct pci_device_id mt7925_pci_device_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7925),
.driver_data = (kernel_ulong_t)MT7925_FIRMWARE_WM },
{ PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0717),
.driver_data = (kernel_ulong_t)MT7925_FIRMWARE_WM },
{ PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7927),
.driver_data = (kernel_ulong_t)MT7927_FIRMWARE_WM },
{ PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x6639),
.driver_data = (kernel_ulong_t)MT7927_FIRMWARE_WM },
{ PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0738),
.driver_data = (kernel_ulong_t)MT7927_FIRMWARE_WM },
{ },
};
static bool mt7925_disable_aspm;
module_param_named(disable_aspm, mt7925_disable_aspm, bool, 0644);
MODULE_PARM_DESC(disable_aspm, "disable PCI ASPM support");
static int mt7925e_init_reset(struct mt792x_dev *dev)
{
return mt792x_wpdma_reset(dev, true);
}
static void mt7925e_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);
mt7925_tx_token_put(dev);
__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 void mt7925_reg_remap_restore(struct mt792x_dev *dev)
{
/* remap to ori status */
if (unlikely(dev->backup_l1)) {
dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L1, dev->backup_l1);
dev->backup_l1 = 0;
}
if (dev->backup_l2) {
dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L2, dev->backup_l2);
dev->backup_l2 = 0;
}
}
static u32 mt7925_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);
dev->backup_l1 = dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1);
dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L1,
MT_HIF_REMAP_L1_MASK,
FIELD_PREP(MT_HIF_REMAP_L1_MASK, base));
/* use read to push write */
dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1);
return MT_HIF_REMAP_BASE_L1 + offset;
}
static u32 mt7925_reg_map_l2(struct mt792x_dev *dev, u32 addr)
{
u32 base = FIELD_GET(MT_HIF_REMAP_L1_BASE, MT_HIF_REMAP_BASE_L2);
dev->backup_l2 = dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1);
dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L1,
MT_HIF_REMAP_L1_MASK,
FIELD_PREP(MT_HIF_REMAP_L1_MASK, base));
dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L2, addr);
/* use read to push write */
dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1);
return MT_HIF_REMAP_BASE_L1;
}
static u32 __mt7925_reg_addr(struct mt792x_dev *dev, u32 addr)
{
static const struct mt76_connac_reg_map fixed_map[] = {
{ 0x830c0000, 0x000000, 0x0001000 }, /* WF_MCU_BUS_CR_REMAP */
{ 0x54000000, 0x002000, 0x0001000 }, /* WFDMA PCIE0 MCU DMA0 */
{ 0x55000000, 0x003000, 0x0001000 }, /* WFDMA PCIE0 MCU DMA1 */
{ 0x56000000, 0x004000, 0x0001000 }, /* WFDMA reserved */
{ 0x57000000, 0x005000, 0x0001000 }, /* WFDMA MCU wrap CR */
{ 0x58000000, 0x006000, 0x0001000 }, /* WFDMA PCIE1 MCU DMA0 (MEM_DMA) */
{ 0x59000000, 0x007000, 0x0001000 }, /* WFDMA PCIE1 MCU DMA1 */
{ 0x820c0000, 0x008000, 0x0004000 }, /* WF_UMAC_TOP (PLE) */
{ 0x820c8000, 0x00c000, 0x0002000 }, /* WF_UMAC_TOP (PSE) */
{ 0x820cc000, 0x00e000, 0x0002000 }, /* WF_UMAC_TOP (PP) */
{ 0x74030000, 0x010000, 0x0001000 }, /* PCIe MAC */
{ 0x820e0000, 0x020000, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */
{ 0x820e1000, 0x020400, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */
{ 0x820e2000, 0x020800, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */
{ 0x820e3000, 0x020c00, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */
{ 0x820e4000, 0x021000, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */
{ 0x820e5000, 0x021400, 0x0000800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */
{ 0x820ce000, 0x021c00, 0x0000200 }, /* WF_LMAC_TOP (WF_SEC) */
{ 0x820e7000, 0x021e00, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */
{ 0x820cf000, 0x022000, 0x0001000 }, /* WF_LMAC_TOP (WF_PF) */
{ 0x820e9000, 0x023400, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */
{ 0x820ea000, 0x024000, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */
{ 0x820eb000, 0x024200, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */
{ 0x820ec000, 0x024600, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_INT) */
{ 0x820ed000, 0x024800, 0x0000800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */
{ 0x820ca000, 0x026000, 0x0002000 }, /* WF_LMAC_TOP BN0 (WF_MUCOP) */
{ 0x820d0000, 0x030000, 0x0010000 }, /* WF_LMAC_TOP (WF_WTBLON) */
{ 0x40000000, 0x070000, 0x0010000 }, /* WF_UMAC_SYSRAM */
{ 0x00400000, 0x080000, 0x0010000 }, /* WF_MCU_SYSRAM */
{ 0x00410000, 0x090000, 0x0010000 }, /* WF_MCU_SYSRAM (configure register) */
{ 0x820f0000, 0x0a0000, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */
{ 0x820f1000, 0x0a0600, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */
{ 0x820f2000, 0x0a0800, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */
{ 0x820f3000, 0x0a0c00, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */
{ 0x820f4000, 0x0a1000, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */
{ 0x820f5000, 0x0a1400, 0x0000800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */
{ 0x820f7000, 0x0a1e00, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */
{ 0x820f9000, 0x0a3400, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */
{ 0x820fa000, 0x0a4000, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */
{ 0x820fb000, 0x0a4200, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */
{ 0x820fc000, 0x0a4600, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_INT) */
{ 0x820fd000, 0x0a4800, 0x0000800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */
{ 0x820c4000, 0x0a8000, 0x0004000 }, /* WF_LMAC_TOP BN1 (WF_MUCOP) */
{ 0x820b0000, 0x0ae000, 0x0001000 }, /* [APB2] WFSYS_ON */
{ 0x80020000, 0x0b0000, 0x0010000 }, /* WF_TOP_MISC_OFF */
{ 0x81020000, 0x0c0000, 0x0010000 }, /* WF_TOP_MISC_ON */
{ 0x7c020000, 0x0d0000, 0x0010000 }, /* CONN_INFRA, wfdma */
{ 0x7c060000, 0x0e0000, 0x0010000 }, /* CONN_INFRA, conn_host_csr_top */
{ 0x7c000000, 0x0f0000, 0x0010000 }, /* CONN_INFRA */
{ 0x70020000, 0x1f0000, 0x0010000 }, /* Reserved for CBTOP, can't switch */
{ 0x7c500000, 0x060000, 0x2000000 }, /* remap */
{ 0x0, 0x0, 0x0 } /* End */
};
int i;
if (addr < 0x200000)
return addr;
mt7925_reg_remap_restore(dev);
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 mt7925_reg_map_l1(dev, addr);
return mt7925_reg_map_l2(dev, addr);
}
static u32 mt7925_rr(struct mt76_dev *mdev, u32 offset)
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
u32 addr = __mt7925_reg_addr(dev, offset);
return dev->bus_ops->rr(mdev, addr);
}
static void mt7925_wr(struct mt76_dev *mdev, u32 offset, u32 val)
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
u32 addr = __mt7925_reg_addr(dev, offset);
dev->bus_ops->wr(mdev, addr, val);
}
static u32 mt7925_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val)
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
u32 addr = __mt7925_reg_addr(dev, offset);
return dev->bus_ops->rmw(mdev, addr, mask, val);
}
static const struct mt792x_dma_layout mt7925_dma_layout = {
.tx_data0 = mt792x_dma_ring(MT7925_TXQ_BAND0,
MT7925_TX_RING_SIZE,
MT_TX_RING_BASE),
.tx_mcu = mt792x_dma_ring(MT7925_TXQ_MCU_WM,
MT7925_TX_MCU_RING_SIZE,
MT_TX_RING_BASE),
.tx_fwdl = mt792x_dma_ring(MT7925_TXQ_FWDL,
MT7925_TX_FWDL_RING_SIZE,
MT_TX_RING_BASE),
.rx_mcu = mt792x_dma_ring(MT7925_RXQ_MCU_WM,
MT7925_RX_MCU_RING_SIZE,
MT_RX_EVENT_RING_BASE),
.rx_data = mt792x_dma_ring(MT7925_RXQ_BAND0,
MT7925_RX_RING_SIZE,
MT_RX_DATA_RING_BASE),
};
static const struct mt792x_dma_layout mt7927_dma_layout = {
.tx_data0 = mt792x_dma_ring(MT7927_TXQ_BAND0,
MT7925_TX_RING_SIZE,
MT_TX_RING_BASE),
.tx_mcu = mt792x_dma_ring(MT7927_TXQ_MCU_WM,
MT7925_TX_MCU_RING_SIZE,
MT_TX_RING_BASE),
.tx_fwdl = mt792x_dma_ring(MT7927_TXQ_FWDL,
MT7925_TX_FWDL_RING_SIZE,
MT_TX_RING_BASE),
.rx_mcu = mt792x_dma_ring(MT7927_RXQ_MCU_WM,
MT7925_RX_MCU_RING_SIZE,
MT_RX_EVENT_RING_BASE),
.rx_data = mt792x_dma_ring(MT7927_RXQ_BAND0,
MT7925_RX_RING_SIZE,
MT_RX_DATA_RING_BASE),
};
static int mt7927_dma_init(struct mt792x_dev *dev)
{
int ret;
ret = mt792x_dma_alloc_queues(dev, &mt7927_dma_layout);
if (ret)
return ret;
ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA],
MT7927_RXQ_DATA2,
MT7925_RX_MCU_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 mt7925_dma_init(struct mt792x_dev *dev)
{
int ret;
ret = mt792x_dma_alloc_queues(dev, &mt7925_dma_layout);
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 const struct mt792x_irq_map mt7927_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 = MT7927_RX_DONE_INT_ENA4,
.wm_complete_mask = MT7927_RX_DONE_INT_ENA6,
.wm2_complete_mask = MT7927_RX_DONE_INT_ENA7,
},
};
static int mt7925_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 = MT7925_TOKEN_SIZE,
.tx_prepare_skb = mt7925e_tx_prepare_skb,
.tx_complete_skb = mt76_connac_tx_complete_skb,
.rx_check = mt7925_rx_check,
.rx_skb = mt7925_queue_rx_skb,
.rx_poll_complete = mt792x_rx_poll_complete,
.sta_add = mt7925_mac_sta_add,
.sta_event = mt7925_mac_sta_event,
.sta_remove = mt7925_mac_sta_remove,
.update_survey = mt792x_update_channel,
};
static const struct mt792x_hif_ops mt7925_pcie_ops = {
.init_reset = mt7925e_init_reset,
.reset = mt7925e_mac_reset,
.mcu_init = mt7925e_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 = HOST_RX_DONE_INT_ENA2,
.wm_complete_mask = HOST_RX_DONE_INT_ENA0,
},
};
struct ieee80211_ops *ops;
struct mt76_bus_ops *bus_ops;
struct mt792x_dev *dev;
struct mt76_dev *mdev;
bool is_mt7927_hw;
u8 features;
int ret;
u16 cmd;
ret = pcim_enable_device(pdev);
if (ret)
return ret;
ret = pcim_iomap_regions(pdev, BIT(0), pci_name(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;
is_mt7927_hw = (pdev->device == 0x6639 || pdev->device == 0x7927 ||
pdev->device == 0x0738);
/* MT7927: ASPM L1 causes unreliable WFDMA register access */
if (mt7925_disable_aspm || is_mt7927_hw)
mt76_pci_disable_aspm(pdev);
ops = mt792x_get_mac80211_ops(&pdev->dev, &mt7925_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);
dev->fw_features = features;
dev->hif_ops = &mt7925_pcie_ops;
dev->irq_map = is_mt7927_hw ? &mt7927_irq_map : &irq_map;
mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]);
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 = mt7925_rr;
bus_ops->wr = mt7925_wr;
bus_ops->rmw = mt7925_rmw;
dev->mt76.bus = bus_ops;
if (!mt7925_disable_aspm && mt76_pci_aspm_supported(pdev))
dev->aspm_supported = true;
ret = __mt792x_mcu_fw_pmctrl(dev);
if (ret)
goto err_free_dev;
ret = __mt792xe_mcu_drv_pmctrl(dev);
if (ret)
goto err_free_dev;
mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) |
(mt76_rr(dev, MT_HW_REV) & 0xff);
dev_info(mdev->dev, "ASIC revision: %04x\n", mdev->rev);
if (is_mt7927_hw && mt76_chip(mdev) != 0x7927) {
dev_info(mdev->dev,
"MT7927 raw CHIPID=0x%04x, forcing chip=0x7927\n",
mt76_chip(mdev));
mdev->rev = (0x7927 << 16) | (mdev->rev & 0xff);
}
mt76_rmw_field(dev, MT_HW_EMI_CTL, MT_HW_EMI_CTL_SLPPROT_EN, 1);
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;
if (is_mt7927(&dev->mt76))
ret = mt7927_dma_init(dev);
else if (is_mt7925(&dev->mt76))
ret = mt7925_dma_init(dev);
else
ret = -EINVAL;
if (ret)
goto err_free_irq;
ret = mt7925_register_device(dev);
if (ret)
goto err_free_dma;
return 0;
err_free_dma:
mt792x_dma_cleanup(dev);
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 mt7925_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);
mt7925e_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 mt7925_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, ret;
pm->suspended = true;
dev->hif_resumed = false;
flush_work(&dev->reset_work);
cancel_delayed_work_sync(&pm->ps_work);
cancel_work_sync(&pm->wake_work);
mt7925_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);
/* always enable deep sleep during suspend to reduce
* power consumption
*/
mt7925_mcu_set_deep_sleep(dev, true);
mt76_connac_mcu_set_hif_suspend(mdev, true, false);
ret = wait_event_timeout(dev->wait,
dev->hif_idle, 3 * HZ);
if (!ret) {
err = -ETIMEDOUT;
goto restore_suspend;
}
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)
mt7925_mcu_set_deep_sleep(dev, false);
mt76_connac_mcu_set_hif_suspend(mdev, false, false);
ret = wait_event_timeout(dev->wait,
dev->hif_resumed, 3 * HZ);
if (!ret)
err = -ETIMEDOUT;
restore_suspend:
pm->suspended = false;
if (err < 0)
mt792x_reset(&dev->mt76);
return err;
}
static int _mt7925_pci_resume(struct device *device, bool restore)
{
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, ret;
dev->hif_idle = false;
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 |
dev->irq_map->rx.data_complete_mask |
dev->irq_map->rx.wm_complete_mask |
dev->irq_map->rx.wm2_complete_mask |
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();
if (restore)
goto failed;
mt76_connac_mcu_set_hif_suspend(mdev, false, false);
ret = wait_event_timeout(dev->wait,
dev->hif_resumed, 3 * HZ);
if (!ret) {
err = -ETIMEDOUT;
goto failed;
}
/* restore previous ds setting */
if (!pm->ds_enable)
mt7925_mcu_set_deep_sleep(dev, false);
mt7925_mcu_regd_update(dev, mdev->alpha2, dev->country_ie_env);
failed:
pm->suspended = false;
if (err < 0 || restore)
mt792x_reset(&dev->mt76);
return err;
}
static void mt7925_pci_shutdown(struct pci_dev *pdev)
{
mt7925_pci_remove(pdev);
}
static int mt7925_pci_resume(struct device *device)
{
return _mt7925_pci_resume(device, false);
}
static int mt7925_pci_restore(struct device *device)
{
return _mt7925_pci_resume(device, true);
}
static const struct dev_pm_ops mt7925_pm_ops = {
.suspend = pm_sleep_ptr(mt7925_pci_suspend),
.resume = pm_sleep_ptr(mt7925_pci_resume),
.freeze = pm_sleep_ptr(mt7925_pci_suspend),
.thaw = pm_sleep_ptr(mt7925_pci_resume),
.poweroff = pm_sleep_ptr(mt7925_pci_suspend),
.restore = pm_sleep_ptr(mt7925_pci_restore),
};
static struct pci_driver mt7925_pci_driver = {
.name = KBUILD_MODNAME,
.id_table = mt7925_pci_device_table,
.probe = mt7925_pci_probe,
.remove = mt7925_pci_remove,
.shutdown = mt7925_pci_shutdown,
.driver.pm = pm_sleep_ptr(&mt7925_pm_ops),
};
module_pci_driver(mt7925_pci_driver);
MODULE_DEVICE_TABLE(pci, mt7925_pci_device_table);
MODULE_FIRMWARE(MT7925_FIRMWARE_WM);
MODULE_FIRMWARE(MT7925_ROM_PATCH);
MODULE_FIRMWARE(MT7927_FIRMWARE_WM);
MODULE_FIRMWARE(MT7927_ROM_PATCH);
MODULE_AUTHOR("Deren Wu <deren.wu@mediatek.com>");
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
MODULE_DESCRIPTION("MediaTek MT7925E (PCIe) wireless driver");
MODULE_LICENSE("Dual BSD/GPL");
@@ -0,0 +1,161 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include "mt7925.h"
#include "../dma.h"
#include "mcu.h"
#include "mac.h"
int mt7925e_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);
mt7925_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;
}
void mt7925_tx_token_put(struct mt792x_dev *dev)
{
struct mt76_txwi_cache *txwi;
int id;
spin_lock_bh(&dev->mt76.token_lock);
idr_for_each_entry(&dev->mt76.token, txwi, id) {
mt7925_txwi_free(dev, txwi, NULL, NULL, NULL);
dev->mt76.token_count--;
}
spin_unlock_bh(&dev->mt76.token_lock);
idr_destroy(&dev->mt76.token);
}
int mt7925e_mac_reset(struct mt792x_dev *dev)
{
const struct mt792x_irq_map *irq_map = dev->irq_map;
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_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);
mt76_txq_schedule_all(&dev->mphy);
mt76_worker_disable(&dev->mt76.tx_worker);
if (irq_map->rx.data_complete_mask)
napi_disable(&dev->mt76.napi[MT_RXQ_MAIN]);
if (irq_map->rx.wm_complete_mask)
napi_disable(&dev->mt76.napi[MT_RXQ_MCU]);
if (irq_map->rx.wm2_complete_mask)
napi_disable(&dev->mt76.napi[MT_RXQ_MCU_WA]);
if (irq_map->tx.all_complete_mask)
napi_disable(&dev->mt76.tx_napi);
mt7925_tx_token_put(dev);
idr_init(&dev->mt76.token);
mt792x_wpdma_reset(dev, true);
mt76_for_each_q_rx(&dev->mt76, i) {
napi_enable(&dev->mt76.napi[i]);
}
napi_enable(&dev->mt76.tx_napi);
local_bh_disable();
mt76_for_each_q_rx(&dev->mt76, i) {
napi_schedule(&dev->mt76.napi[i]);
}
napi_schedule(&dev->mt76.tx_napi);
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 |
dev->irq_map->rx.data_complete_mask |
dev->irq_map->rx.wm_complete_mask |
dev->irq_map->rx.wm2_complete_mask |
MT_INT_MCU_CMD);
mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
err = mt792xe_mcu_fw_pmctrl(dev);
if (err)
return err;
err = __mt792xe_mcu_drv_pmctrl(dev);
if (err)
goto out;
err = mt7925_run_firmware(dev);
if (err)
goto out;
err = mt7925_mcu_set_eeprom(dev);
if (err)
goto out;
err = mt7925_mac_init(dev);
if (err)
goto out;
if (is_mt7927(&dev->mt76)) {
err = mt7925_mcu_set_dbdc(&dev->mphy, true);
if (err)
goto out;
}
err = __mt7925_start(&dev->phy);
out:
clear_bit(MT76_RESET, &dev->mphy.state);
mt76_worker_enable(&dev->mt76.tx_worker);
return err;
}
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include "mt7925.h"
#include "mcu.h"
static int
mt7925_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 = mt7925_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 mt7925e_mcu_init(struct mt792x_dev *dev)
{
static const struct mt76_mcu_ops mt7925_mcu_ops = {
.headroom = sizeof(struct mt76_connac2_mcu_txd),
.mcu_skb_send_msg = mt7925_mcu_send_message,
.mcu_parse_response = mt7925_mcu_parse_response,
};
int err;
dev->mt76.mcu_ops = &mt7925_mcu_ops;
err = mt792xe_mcu_fw_pmctrl(dev);
if (err)
return err;
err = __mt792xe_mcu_drv_pmctrl(dev);
if (err)
return err;
mt76_rmw_field(dev, MT_PCIE_MAC_PM, MT_PCIE_MAC_PM_L0S_DIS, 1);
err = mt7925_run_firmware(dev);
mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_FWDL], false);
return err;
}
@@ -0,0 +1,266 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2025 MediaTek Inc. */
#include "mt7925.h"
#include "regd.h"
#include "mcu.h"
static bool mt7925_disable_clc;
module_param_named(disable_clc, mt7925_disable_clc, bool, 0644);
MODULE_PARM_DESC(disable_clc, "disable CLC support");
bool mt7925_regd_clc_supported(struct mt792x_dev *dev)
{
if (mt7925_disable_clc ||
mt76_is_usb(&dev->mt76))
return false;
return true;
}
void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2)
{
struct mt792x_phy *phy = &dev->phy;
struct mt7925_clc_rule_v2 *rule;
struct mt7925_clc *clc;
bool old = dev->has_eht, new = true;
u32 mtcl_conf = mt792x_acpi_get_mtcl_conf(&dev->phy, alpha2);
u8 *pos;
if (mtcl_conf != MT792X_ACPI_MTCL_INVALID &&
(((mtcl_conf >> 4) & 0x3) == 0)) {
new = false;
goto out;
}
if (!phy->clc[MT792x_CLC_BE_CTRL])
goto out;
clc = (struct mt7925_clc *)phy->clc[MT792x_CLC_BE_CTRL];
pos = clc->data;
while (1) {
rule = (struct mt7925_clc_rule_v2 *)pos;
if (rule->alpha2[0] == alpha2[0] &&
rule->alpha2[1] == alpha2[1]) {
new = false;
break;
}
/* Check the last one */
if (rule->flag & BIT(0))
break;
pos += sizeof(*rule);
}
out:
if (old == new)
return;
dev->has_eht = new;
mt7925_set_stream_he_eht_caps(phy);
}
static void
mt7925_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
{
#define IS_UNII_INVALID(idx, sfreq, efreq, cfreq) \
(!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq))
#define MT7925_UNII_59G_IS_VALID 0x1
#define MT7925_UNII_6G_IS_VALID 0x1e
struct ieee80211_supported_band *sband;
struct mt76_dev *mdev = &dev->mt76;
struct ieee80211_channel *ch;
u32 mtcl_conf = mt792x_acpi_get_mtcl_conf(&dev->phy, mdev->alpha2);
int i;
if (mtcl_conf != MT792X_ACPI_MTCL_INVALID) {
if ((mtcl_conf & 0x3) == 0)
dev->phy.clc_chan_conf &= ~MT7925_UNII_59G_IS_VALID;
if (((mtcl_conf >> 2) & 0x3) == 0)
dev->phy.clc_chan_conf &= ~MT7925_UNII_6G_IS_VALID;
}
sband = wiphy->bands[NL80211_BAND_2GHZ];
if (!sband)
return;
for (i = 0; i < sband->n_channels; i++) {
ch = &sband->channels[i];
if (!dev->has_eht)
ch->flags |= IEEE80211_CHAN_NO_EHT;
}
sband = wiphy->bands[NL80211_BAND_5GHZ];
if (!sband)
return;
for (i = 0; i < sband->n_channels; i++) {
ch = &sband->channels[i];
/* UNII-4 */
if (IS_UNII_INVALID(0, 5845, 5925, ch->center_freq))
ch->flags |= IEEE80211_CHAN_DISABLED;
if (!dev->has_eht)
ch->flags |= IEEE80211_CHAN_NO_EHT;
}
sband = wiphy->bands[NL80211_BAND_6GHZ];
if (!sband)
return;
for (i = 0; i < sband->n_channels; i++) {
ch = &sband->channels[i];
/* UNII-5/6/7/8 */
if (IS_UNII_INVALID(1, 5925, 6425, ch->center_freq) ||
IS_UNII_INVALID(2, 6425, 6525, ch->center_freq) ||
IS_UNII_INVALID(3, 6525, 6875, ch->center_freq) ||
IS_UNII_INVALID(4, 6875, 7125, ch->center_freq))
ch->flags |= IEEE80211_CHAN_DISABLED;
if (!dev->has_eht)
ch->flags |= IEEE80211_CHAN_NO_EHT;
}
}
int mt7925_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
enum environment_cap country_ie_env)
{
struct ieee80211_hw *hw = mt76_hw(dev);
struct wiphy *wiphy = hw->wiphy;
int ret = 0;
dev->regd_in_progress = true;
mt792x_mutex_acquire(dev);
if (!dev->regd_change)
goto err;
ret = mt7925_mcu_set_clc(dev, alpha2, country_ie_env);
if (ret < 0)
goto err;
mt7925_regd_be_ctrl(dev, alpha2);
mt7925_regd_channel_update(wiphy, dev);
ret = mt7925_mcu_set_channel_domain(hw->priv);
if (ret < 0)
goto err;
ret = mt7925_set_tx_sar_pwr(hw, NULL);
if (ret < 0)
goto err;
err:
mt792x_mutex_release(dev);
dev->regd_change = false;
dev->regd_in_progress = false;
wake_up(&dev->wait);
return ret;
}
EXPORT_SYMBOL_GPL(mt7925_mcu_regd_update);
void mt7925_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;
/* allow world regdom at the first boot only */
if (!memcmp(req->alpha2, "00", 2) &&
mdev->alpha2[0] && mdev->alpha2[1])
return;
/* 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;
dev->regd_change = true;
if (pm->suspended)
/* postpone the mcu update to resume */
return;
mt7925_mcu_regd_update(dev, req->alpha2,
req->country_ie_env);
return;
}
static bool
mt7925_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 mt7925_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 (!mt7925_regd_is_valid_alpha2(alpha2) ||
!mt7925_regd_clc_supported(dev) ||
dev->regd_user)
return -EINVAL;
if ((mdev->alpha2[0] && mdev->alpha2[0] != '0') &&
(mdev->alpha2[1] && 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 mt7925_mcu_set_clc(dev, alpha2, ENVIRON_INDOOR);
}
}
EXPORT_SYMBOL_GPL(mt7925_regd_change);
int mt7925_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,19 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2025 MediaTek Inc. */
#ifndef __MT7925_REGD_H
#define __MT7925_REGD_H
#include "mt7925.h"
int mt7925_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
enum environment_cap country_ie_env);
void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2);
void mt7925_regd_notifier(struct wiphy *wiphy, struct regulatory_request *req);
bool mt7925_regd_clc_supported(struct mt792x_dev *dev);
int mt7925_regd_change(struct mt792x_phy *phy, char *alpha2);
int mt7925_regd_init(struct mt792x_phy *phy);
#endif
@@ -0,0 +1,92 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2023 MediaTek Inc. */
#ifndef __MT7925_REGS_H
#define __MT7925_REGS_H
#include "../mt792x_regs.h"
#define MT_MDP_BASE 0x820cc800
#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(0x090 + ((_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(0x094 + ((_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 MT_WFDMA0_HOST_INT_DIS MT_WFDMA0(0x22c)
#define HOST_RX_DONE_INT_ENA4 BIT(12)
#define HOST_RX_DONE_INT_ENA5 BIT(13)
#define HOST_RX_DONE_INT_ENA6 BIT(14)
#define HOST_RX_DONE_INT_ENA7 BIT(15)
#define HOST_RX_DONE_INT_ENA8 BIT(16)
#define HOST_RX_DONE_INT_ENA9 BIT(17)
#define HOST_RX_DONE_INT_ENA10 BIT(18)
#define HOST_RX_DONE_INT_ENA11 BIT(19)
#define HOST_TX_DONE_INT_ENA15 BIT(25)
#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_ENA1
#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_ENA15 | \
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 | \
MT_INT_TX_DONE_BAND0 | \
GENMASK(18, 4))
#define MT_RX_DATA_RING_BASE MT_WFDMA0(0x500)
#define MT_INFRA_CFG_BASE 0xd1000
#define MT_INFRA(ofs) (MT_INFRA_CFG_BASE + (ofs))
#define MT_HIF_REMAP_L1 0x155024
#define MT_HIF_REMAP_L1_MASK GENMASK(31, 16)
#define MT_HIF_REMAP_L1_OFFSET GENMASK(15, 0)
#define MT_HIF_REMAP_L1_BASE GENMASK(31, 16)
#define MT_HIF_REMAP_BASE_L1 0x130000
#define MT_HIF_REMAP_L2 0x0120
#if IS_ENABLED(CONFIG_MT76_DEV)
#define MT_HIF_REMAP_BASE_L2 (0x7c500000 - (0x7c000000 - 0x18000000))
#else
#define MT_HIF_REMAP_BASE_L2 0x18500000
#endif
#define MT_WFSYS_SW_RST_B 0x7c000140
#define MT_WTBLON_TOP_WDUCR MT_WTBLON_TOP(0x370)
#define MT_WTBLON_TOP_WDUCR_GROUP GENMASK(4, 0)
#define MT_WTBL_UPDATE MT_WTBLON_TOP(0x380)
#define MT_WTBL_UPDATE_WLAN_IDX GENMASK(11, 0)
#define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(14)
#endif
@@ -0,0 +1,206 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
#include "mt7925.h"
#include "mcu.h"
#define MT7925_EVT_RSP_LEN 512
enum mt7925_testmode_attr {
MT7925_TM_ATTR_UNSPEC,
MT7925_TM_ATTR_SET,
MT7925_TM_ATTR_QUERY,
MT7925_TM_ATTR_RSP,
/* keep last */
NUM_MT7925_TM_ATTRS,
MT7925_TM_ATTR_MAX = NUM_MT7925_TM_ATTRS - 1,
};
struct mt7925_tm_cmd {
u8 padding[4];
struct uni_cmd_testmode_ctrl c;
} __packed;
struct mt7925_tm_evt {
u32 param0;
u32 param1;
} __packed;
static const struct nla_policy mt7925_tm_policy[NUM_MT7925_TM_ATTRS] = {
[MT7925_TM_ATTR_SET] = NLA_POLICY_EXACT_LEN(sizeof(struct mt7925_tm_cmd)),
[MT7925_TM_ATTR_QUERY] = NLA_POLICY_EXACT_LEN(sizeof(struct mt7925_tm_cmd)),
};
static int
mt7925_tm_set(struct mt792x_dev *dev, struct mt7925_tm_cmd *req)
{
struct mt7925_rftest_cmd cmd;
struct mt7925_rftest_cmd *pcmd = &cmd;
bool testmode = false, normal = false;
struct mt76_connac_pm *pm = &dev->pm;
struct mt76_phy *phy = &dev->mphy;
int ret = -ENOTCONN;
memset(pcmd, 0, sizeof(*pcmd));
memcpy(pcmd, req, sizeof(struct mt7925_tm_cmd));
mutex_lock(&dev->mt76.mutex);
if (pcmd->ctrl.action == CMD_TEST_CTRL_ACT_SWITCH_MODE) {
if (pcmd->ctrl.data.op_mode == CMD_TEST_CTRL_ACT_SWITCH_MODE_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_UNI_CMD(TESTMODE_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
mt7925_tm_query(struct mt792x_dev *dev, struct mt7925_tm_cmd *req,
char *evt_resp)
{
struct mt7925_rftest_cmd cmd;
char *pcmd = (char *)&cmd;
struct sk_buff *skb = NULL;
int ret = 1;
memset(pcmd, 0, sizeof(*pcmd));
memcpy(pcmd + 4, (char *)&req->c, sizeof(struct uni_cmd_testmode_ctrl));
if (*((uint16_t *)req->padding) == MCU_UNI_CMD_TESTMODE_CTRL)
ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_QUERY(TESTMODE_CTRL),
&cmd, sizeof(cmd), true, &skb);
else if (*((uint16_t *)req->padding) == MCU_UNI_CMD_TESTMODE_RX_STAT)
ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_QUERY(TESTMODE_RX_STAT),
&cmd, sizeof(cmd), true, &skb);
if (ret)
goto out;
if (skb->len < MT7925_EVT_RSP_LEN + 8) {
ret = -EINVAL;
goto out;
}
memcpy((char *)evt_resp, (char *)skb->data + 8, MT7925_EVT_RSP_LEN);
out:
dev_kfree_skb(skb);
return ret;
}
int mt7925_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_MT7925_TM_ATTRS], *data;
int ret;
data = tb[MT76_TM_ATTR_DRV_DATA];
ret = nla_parse_nested_deprecated(drv_tb,
MT7925_TM_ATTR_MAX,
data, mt7925_tm_policy,
NULL);
if (ret)
return ret;
data = drv_tb[MT7925_TM_ATTR_SET];
if (data)
return mt7925_tm_set(phy->dev, nla_data(data));
}
return -EINVAL;
}
int mt7925_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_MT7925_TM_ATTRS], *data;
int ret;
data = tb[MT76_TM_ATTR_DRV_DATA];
ret = nla_parse_nested_deprecated(drv_tb,
MT7925_TM_ATTR_MAX,
data, mt7925_tm_policy,
NULL);
if (ret)
return ret;
data = drv_tb[MT7925_TM_ATTR_QUERY];
if (data) {
char evt_resp[MT7925_EVT_RSP_LEN];
err = mt7925_tm_query(phy->dev, nla_data(data),
evt_resp);
if (err)
return err;
return nla_put(msg, MT7925_TM_ATTR_RSP,
sizeof(evt_resp), evt_resp);
}
}
return -EINVAL;
}
@@ -0,0 +1,364 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/usb.h>
#include "mt7925.h"
#include "mcu.h"
#include "mac.h"
static const struct usb_device_id mt7925u_device_table[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x6639, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM },
{ USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7925, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM },
/* Netgear, Inc. A8500 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0x9050, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM },
/* Netgear, Inc. A9000 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0x9072, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM },
{ },
};
static int
mt7925u_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 = mt7925_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 mt7925u_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 = mt7925u_mcu_send_message,
.mcu_parse_response = mt7925_mcu_parse_response,
};
int ret;
dev->mt76.mcu_ops = &mcu_ops;
mt76_set(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN);
ret = mt7925_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 mt7925u_mac_reset(struct mt792x_dev *dev)
{
int err;
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 = mt7925_run_firmware(dev);
if (err)
goto out;
mt76_clear(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN);
err = mt7925_mcu_set_eeprom(dev);
if (err)
goto out;
err = mt7925_mac_init(dev);
if (err)
goto out;
if (is_mt7927(&dev->mt76)) {
err = mt7925_mcu_set_dbdc(&dev->mphy, true);
if (err)
goto out;
}
err = __mt7925_start(&dev->phy);
out:
clear_bit(MT76_RESET, &dev->mphy.state);
mt76_worker_enable(&dev->mt76.tx_worker);
return err;
}
static int mt7925u_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 = mt7925_usb_sdio_tx_prepare_skb,
.tx_complete_skb = mt7925_usb_sdio_tx_complete_skb,
.tx_status_data = mt7925_usb_sdio_tx_status_data,
.rx_skb = mt7925_queue_rx_skb,
.rx_check = mt7925_rx_check,
.sta_add = mt7925_mac_sta_add,
.sta_event = mt7925_mac_sta_event,
.sta_remove = mt7925_mac_sta_remove,
.update_survey = mt792x_update_channel,
};
static const struct mt792x_hif_ops hif_ops = {
.mcu_init = mt7925u_mcu_init,
.init_reset = mt792xu_init_reset,
.reset = mt7925u_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, &mt7925_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;
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_chip(mdev) == 0x6639) {
dev_dbg(mdev->dev,
"MT7927 raw CHIPID=0x%04x, forcing chip=0x7927\n",
mt76_chip(mdev));
mdev->rev = (0x7927 << 16) | (mdev->rev & 0xff);
}
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 = mt7925_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 mt7925u_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, ret;
pm->suspended = true;
dev->hif_resumed = false;
flush_work(&dev->reset_work);
mt76_connac_mcu_set_hif_suspend(&dev->mt76, true, false);
ret = wait_event_timeout(dev->wait,
dev->hif_idle, 3 * HZ);
if (!ret) {
err = -ETIMEDOUT;
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 mt7925u_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, ret;
dev->hif_idle = false;
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;
mt76_connac_mcu_set_hif_suspend(&dev->mt76, false, false);
ret = wait_event_timeout(dev->wait,
dev->hif_resumed, 3 * HZ);
if (!ret)
err = -ETIMEDOUT;
failed:
pm->suspended = false;
if (err < 0)
mt792x_reset(&dev->mt76);
return err;
}
#endif /* CONFIG_PM */
MODULE_DEVICE_TABLE(usb, mt7925u_device_table);
MODULE_FIRMWARE(MT7925_FIRMWARE_WM);
MODULE_FIRMWARE(MT7925_ROM_PATCH);
static struct usb_driver mt7925u_driver = {
.name = KBUILD_MODNAME,
.id_table = mt7925u_device_table,
.probe = mt7925u_probe,
.disconnect = mt792xu_disconnect,
#ifdef CONFIG_PM
.suspend = mt7925u_suspend,
.resume = mt7925u_resume,
.reset_resume = mt7925u_resume,
#endif /* CONFIG_PM */
.soft_unbind = 1,
.disable_hub_initiated_lpm = 1,
};
module_usb_driver(mt7925u_driver);
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
MODULE_DESCRIPTION("MediaTek MT7925U (USB) wireless driver");
MODULE_LICENSE("Dual BSD/GPL");