mt76: mt7921: introduce mt7921s support

Introduce support for mt7921s 802.11ax (Wi-Fi 6) 2x2:2SS chipset.

Tested-by: Deren Wu <deren.wu@mediatek.com>
Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
Sean Wang 2021-10-19 07:11:46 +08:00 committed by Felix Fietkau
parent fe0195f756
commit 48fab5bbef
11 changed files with 637 additions and 15 deletions

View File

@ -13,3 +13,13 @@ config MT7921E
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
help
This adds support for MT7921S 802.11ax 2x2:2SS wireless devices.
To compile this driver as a module, choose M here.

View File

@ -2,9 +2,11 @@
obj-$(CONFIG_MT7921_COMMON) += mt7921-common.o
obj-$(CONFIG_MT7921E) += mt7921e.o
obj-$(CONFIG_MT7921S) += mt7921s.o
CFLAGS_trace.o := -I$(src)
mt7921-common-y := mac.o mcu.o main.o init.o debugfs.o trace.o
mt7921-common-$(CONFIG_NL80211_TESTMODE) += testmode.o
mt7921e-y := pci.o pci_mac.o pci_mcu.o dma.o
mt7921s-y := sdio.o sdio_mac.o sdio_mcu.o

View File

@ -413,6 +413,20 @@ static int mt7921_chip_reset(void *data, u64 val)
DEFINE_DEBUGFS_ATTRIBUTE(fops_reset, NULL, mt7921_chip_reset, "%lld\n");
static int
mt7921s_sched_quota_read(struct seq_file *s, void *data)
{
struct mt7921_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 mt7921_dev *dev)
{
struct dentry *dir;
@ -436,6 +450,8 @@ int mt7921_init_debugfs(struct mt7921_dev *dev)
debugfs_create_devm_seqfile(dev->mt76.dev, "runtime_pm_stats", dir,
mt7921_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;
}

View File

@ -231,8 +231,15 @@ int mt7921_register_device(struct mt7921_dev *dev)
dev->pm.idle_timeout = MT7921_PM_TIMEOUT;
dev->pm.stats.last_wake_event = jiffies;
dev->pm.stats.last_doze_event = jiffies;
dev->pm.enable = true;
dev->pm.ds_enable = true;
/* TODO: mt7921s run sleep mode on default */
if (mt76_is_mmio(&dev->mt76)) {
dev->pm.enable = true;
dev->pm.ds_enable = true;
}
if (mt76_is_sdio(&dev->mt76))
hw->extra_tx_headroom += MT_SDIO_TXD_SIZE + MT_SDIO_HDR_SIZE;
ret = mt7921_init_hardware(dev);
if (ret)

View File

@ -843,6 +843,8 @@ void mt7921_mac_write_txwi(struct mt7921_dev *dev, __le32 *txwi,
struct ieee80211_vif *vif = info->control.vif;
struct mt76_phy *mphy = &dev->mphy;
u8 p_fmt, q_idx, omac_idx = 0, wmm_idx = 0;
bool is_mmio = mt76_is_mmio(&dev->mt76);
u32 sz_txd = is_mmio ? MT_TXD_SIZE : MT_SDIO_TXD_SIZE;
bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
u16 tx_count = 15;
u32 val;
@ -858,15 +860,15 @@ void mt7921_mac_write_txwi(struct mt7921_dev *dev, __le32 *txwi,
p_fmt = MT_TX_TYPE_FW;
q_idx = MT_LMAC_BCN0;
} else if (skb_get_queue_mapping(skb) >= MT_TXQ_PSD) {
p_fmt = MT_TX_TYPE_CT;
p_fmt = is_mmio ? MT_TX_TYPE_CT : MT_TX_TYPE_SF;
q_idx = MT_LMAC_ALTX0;
} else {
p_fmt = MT_TX_TYPE_CT;
p_fmt = is_mmio ? MT_TX_TYPE_CT : MT_TX_TYPE_SF;
q_idx = wmm_idx * MT7921_MAX_WMM_SETS +
mt7921_lmac_mapping(dev, skb_get_queue_mapping(skb));
}
val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len + MT_TXD_SIZE) |
val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len + sz_txd) |
FIELD_PREP(MT_TXD0_PKT_FMT, p_fmt) |
FIELD_PREP(MT_TXD0_Q_IDX, q_idx);
txwi[0] = cpu_to_le32(val);
@ -1384,12 +1386,18 @@ void mt7921_pm_wake_work(struct work_struct *work)
mphy = dev->phy.mt76;
if (!mt7921_mcu_drv_pmctrl(dev)) {
struct mt76_dev *mdev = &dev->mt76;
int i;
mt76_for_each_q_rx(&dev->mt76, i)
napi_schedule(&dev->mt76.napi[i]);
mt76_connac_pm_dequeue_skbs(mphy, &dev->pm);
mt7921_mcu_tx_cleanup(dev);
if (mt76_is_sdio(mdev)) {
mt76_connac_pm_dequeue_skbs(mphy, &dev->pm);
mt76_worker_schedule(&mdev->sdio.txrx_worker);
} else {
mt76_for_each_q_rx(mdev, i)
napi_schedule(&mdev->napi[i]);
mt76_connac_pm_dequeue_skbs(mphy, &dev->pm);
mt7921_mcu_tx_cleanup(dev);
}
if (test_bit(MT76_STATE_RUNNING, &mphy->state))
ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
MT7921_WATCHDOG_TIME);

View File

@ -199,6 +199,10 @@ enum tx_mcu_port_q_idx {
#define MT_TXD_SIZE (8 * 4)
#define MT_SDIO_TXD_SIZE (MT_TXD_SIZE + 8 * 4)
#define MT_SDIO_TAIL_SIZE 8
#define MT_SDIO_HDR_SIZE 4
#define MT_TXD0_Q_IDX GENMASK(31, 25)
#define MT_TXD0_PKT_FMT GENMASK(24, 23)
#define MT_TXD0_ETH_TYPE_OFFSET GENMASK(22, 16)

View File

@ -650,7 +650,9 @@ static int mt7921_load_patch(struct mt7921_dev *dev)
{
const struct mt7921_patch_hdr *hdr;
const struct firmware *fw = NULL;
int i, ret, sem;
int i, ret, sem, max_len;
max_len = mt76_is_sdio(&dev->mt76) ? 2048 : 4096;
sem = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, true);
switch (sem) {
@ -706,7 +708,7 @@ static int mt7921_load_patch(struct mt7921_dev *dev)
}
ret = __mt76_mcu_send_firmware(&dev->mt76, MCU_CMD_FW_SCATTER,
dl, len, 4096);
dl, len, max_len);
if (ret) {
dev_err(dev->mt76.dev, "Failed to send patch\n");
goto out;
@ -753,9 +755,11 @@ mt7921_mcu_send_ram_firmware(struct mt7921_dev *dev,
const struct mt7921_fw_trailer *hdr,
const u8 *data, bool is_wa)
{
int i, offset = 0;
int i, offset = 0, max_len;
u32 override = 0, option = 0;
max_len = mt76_is_sdio(&dev->mt76) ? 2048 : 4096;
for (i = 0; i < hdr->n_region; i++) {
const struct mt7921_fw_region *region;
int err;
@ -778,7 +782,7 @@ mt7921_mcu_send_ram_firmware(struct mt7921_dev *dev,
}
err = __mt76_mcu_send_firmware(&dev->mt76, MCU_CMD_FW_SCATTER,
data + offset, len, 4096);
data + offset, len, max_len);
if (err) {
dev_err(dev->mt76.dev, "Failed to send firmware.\n");
return err;
@ -851,7 +855,7 @@ static int mt7921_load_firmware(struct mt7921_dev *dev)
int ret;
ret = mt76_get_field(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY);
if (ret) {
if (ret && mt76_is_mmio(&dev->mt76)) {
dev_dbg(dev->mt76.dev, "Firmware is already download\n");
goto fw_loaded;
}

View File

@ -47,6 +47,29 @@
#define MT7921_SKU_MAX_DELTA_IDX MT7921_SKU_RATE_NUM
#define MT7921_SKU_TABLE_SIZE (MT7921_SKU_RATE_NUM + 1)
#define MT7921_SDIO_HDR_TX_BYTES GENMASK(15, 0)
#define MT7921_SDIO_HDR_PKT_TYPE GENMASK(17, 16)
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)
@ -321,6 +344,17 @@ static inline void mt7921_mcu_tx_cleanup(struct mt7921_dev *dev)
mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_WA], false);
}
static inline void mt7921_skb_add_sdio_hdr(struct sk_buff *skb,
enum mt7921_sdio_pkt_type type)
{
u32 hdr;
hdr = FIELD_PREP(MT7921_SDIO_HDR_TX_BYTES, skb->len + sizeof(hdr)) |
FIELD_PREP(MT7921_SDIO_HDR_PKT_TYPE, type);
put_unaligned_le32(hdr, skb_push(skb, sizeof(hdr)));
}
int mt7921_mac_init(struct mt7921_dev *dev);
bool mt7921_mac_wtbl_update(struct mt7921_dev *dev, int idx, u32 mask);
void mt7921_mac_reset_counters(struct mt7921_phy *phy);
@ -394,4 +428,13 @@ int mt7921e_mcu_init(struct mt7921_dev *dev);
int mt7921e_mcu_drv_pmctrl(struct mt7921_dev *dev);
int mt7921e_mcu_fw_pmctrl(struct mt7921_dev *dev);
int mt7921s_mcu_init(struct mt7921_dev *dev);
int mt7921s_mcu_drv_pmctrl(struct mt7921_dev *dev);
int mt7921s_mcu_fw_pmctrl(struct mt7921_dev *dev);
int mt7921s_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 mt7921s_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e);
bool mt7921s_tx_status_data(struct mt76_dev *mdev, u8 *update);
#endif

View File

@ -0,0 +1,314 @@
// SPDX-License-Identifier: ISC
/* 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 "mac.h"
#include "mcu.h"
static const struct sdio_device_id mt7921s_table[] = {
{ SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7901) },
{ } /* 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 mt7921_dev *dev = container_of(mdev, struct mt7921_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 mt7921_dev *dev)
{
struct mt76_connac_pm *pm = &dev->pm;
mt76_unregister_device(&dev->mt76);
cancel_delayed_work_sync(&pm->ps_work);
cancel_work_sync(&pm->wake_work);
mt76s_deinit(&dev->mt76);
mt7921_mcu_exit(dev);
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;
err = sdio_readsb(sdio->func, irq_data, MCR_WHISR, sizeof(*irq_data));
if (err < 0)
return err;
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,
.survey_flags = SURVEY_INFO_TIME_TX |
SURVEY_INFO_TIME_RX |
SURVEY_INFO_TIME_BSS_RX,
.tx_prepare_skb = mt7921s_tx_prepare_skb,
.tx_complete_skb = mt7921s_tx_complete_skb,
.tx_status_data = mt7921s_tx_status_data,
.rx_skb = mt7921_queue_rx_skb,
.sta_ps = mt7921_sta_ps,
.sta_add = mt7921_mac_sta_add,
.sta_assoc = mt7921_mac_sta_assoc,
.sta_remove = mt7921_mac_sta_remove,
.update_survey = mt7921_update_channel,
};
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 mt7921_hif_ops mt7921_sdio_ops = {
.mcu_init = mt7921s_mcu_init,
.drv_own = mt7921s_mcu_drv_pmctrl,
.fw_own = mt7921s_mcu_fw_pmctrl,
};
struct mt7921_dev *dev;
struct mt76_dev *mdev;
int ret, i;
mdev = mt76_alloc_device(&func->dev, sizeof(*dev), &mt7921_ops,
&drv_ops);
if (!mdev)
return -ENOMEM;
dev = container_of(mdev, struct mt7921_dev, mt76);
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;
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;
}
for (i = 0; i < ARRAY_SIZE(mdev->sdio.xmit_buf); i++) {
mdev->sdio.xmit_buf[i] = devm_kmalloc(mdev->dev,
MT76S_XMIT_BUF_SZ,
GFP_KERNEL);
if (!mdev->sdio.xmit_buf[i]) {
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 mt7921_dev *dev = sdio_get_drvdata(func);
mt7921s_unregister_device(dev);
}
#ifdef CONFIG_PM
static int mt7921s_suspend(struct device *__dev)
{
struct sdio_func *func = dev_to_sdio_func(__dev);
struct mt7921_dev *dev = sdio_get_drvdata(func);
struct mt76_connac_pm *pm = &dev->pm;
struct mt76_dev *mdev = &dev->mt76;
bool hif_suspend;
int err;
pm->suspended = true;
cancel_delayed_work_sync(&pm->ps_work);
cancel_work_sync(&pm->wake_work);
err = mt7921_mcu_drv_pmctrl(dev);
if (err < 0)
goto restore_suspend;
hif_suspend = !test_bit(MT76_STATE_SUSPEND, &dev->mphy.state);
if (hif_suspend) {
err = mt76_connac_mcu_set_hif_suspend(mdev, true);
if (err)
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.txrx_worker);
mt76_worker_disable(&mdev->sdio.status_worker);
mt76_worker_disable(&mdev->sdio.net_worker);
cancel_work_sync(&mdev->sdio.stat_work);
clear_bit(MT76_READING_STATS, &dev->mphy.state);
mt76_tx_status_check(mdev, true);
err = mt7921_mcu_fw_pmctrl(dev);
if (err)
goto restore_worker;
sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
return 0;
restore_worker:
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);
if (!pm->ds_enable)
mt76_connac_mcu_set_deep_sleep(mdev, false);
if (hif_suspend)
mt76_connac_mcu_set_hif_suspend(mdev, false);
restore_suspend:
pm->suspended = false;
return err;
}
static int mt7921s_resume(struct device *__dev)
{
struct sdio_func *func = dev_to_sdio_func(__dev);
struct mt7921_dev *dev = sdio_get_drvdata(func);
struct mt76_connac_pm *pm = &dev->pm;
struct mt76_dev *mdev = &dev->mt76;
int err;
pm->suspended = false;
err = mt7921_mcu_drv_pmctrl(dev);
if (err < 0)
return err;
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);
/* restore previous ds setting */
if (!pm->ds_enable)
mt76_connac_mcu_set_deep_sleep(mdev, false);
if (!test_bit(MT76_STATE_SUSPEND, &dev->mphy.state))
err = mt76_connac_mcu_set_hif_suspend(mdev, false);
return err;
}
static const struct dev_pm_ops mt7921s_pm_ops = {
.suspend = mt7921s_suspend,
.resume = mt7921s_resume,
};
#endif
MODULE_DEVICE_TABLE(sdio, mt7921s_table);
MODULE_FIRMWARE(MT7921_FIRMWARE_WM);
MODULE_FIRMWARE(MT7921_ROM_PATCH);
static struct sdio_driver mt7921s_driver = {
.name = KBUILD_MODNAME,
.probe = mt7921s_probe,
.remove = mt7921s_remove,
.id_table = mt7921s_table,
#ifdef CONFIG_PM
.drv = {
.pm = &mt7921s_pm_ops,
}
#endif
};
module_sdio_driver(mt7921s_driver);
MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
MODULE_LICENSE("Dual BSD/GPL");

View File

@ -0,0 +1,87 @@
// SPDX-License-Identifier: ISC
/* Copyright (C) 2021 MediaTek Inc. */
#include <linux/iopoll.h>
#include <linux/mmc/sdio_func.h>
#include "mt7921.h"
#include "mac.h"
static void
mt7921s_write_txwi(struct mt7921_dev *dev, struct mt76_wcid *wcid,
enum mt76_txq_id qid, struct ieee80211_sta *sta,
struct sk_buff *skb)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_key_conf *key = info->control.hw_key;
__le32 *txwi;
int pid;
pid = mt76_tx_status_skb_add(&dev->mt76, wcid, skb);
txwi = (__le32 *)(skb->data - MT_SDIO_TXD_SIZE);
memset(txwi, 0, MT_SDIO_TXD_SIZE);
mt7921_mac_write_txwi(dev, txwi, skb, wcid, key, pid, false);
skb_push(skb, MT_SDIO_TXD_SIZE);
}
int mt7921s_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 mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb);
struct sk_buff *skb = tx_info->skb;
int pad;
if (unlikely(tx_info->skb->len <= ETH_HLEN))
return -EINVAL;
if (!wcid)
wcid = &dev->mt76.global_wcid;
if (sta) {
struct mt7921_sta *msta = (struct mt7921_sta *)sta->drv_priv;
if (time_after(jiffies, msta->last_txs + HZ / 4)) {
info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
msta->last_txs = jiffies;
}
}
mt7921s_write_txwi(dev, wcid, qid, sta, skb);
mt7921_skb_add_sdio_hdr(skb, MT7921_SDIO_DATA);
pad = round_up(skb->len, 4) - skb->len;
return mt76_skb_adjust_pad(skb, pad);
}
void mt7921s_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 = FIELD_GET(MT_TXD1_WLAN_IDX, le32_to_cpu(txwi[1]));
wcid = rcu_dereference(mdev->wcid[idx]);
sta = wcid_to_sta(wcid);
if (sta && likely(e->skb->protocol != cpu_to_be16(ETH_P_PAE)))
mt7921_tx_check_aggr(sta, txwi);
skb_pull(e->skb, headroom);
mt76_tx_complete_skb(mdev, e->wcid, e->skb);
}
bool mt7921s_tx_status_data(struct mt76_dev *mdev, u8 *update)
{
struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76);
mt7921_mutex_acquire(dev);
mt7921_mac_sta_poll(dev);
mt7921_mutex_release(dev);
return 0;
}

View File

@ -0,0 +1,127 @@
// SPDX-License-Identifier: ISC
/* 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 "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 mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76);
enum mt7921_sdio_pkt_type type = MT7921_SDIO_CMD;
enum mt76_mcuq_id txq = MT_MCUQ_WM;
int ret, pad;
ret = mt7921_mcu_fill_message(mdev, skb, cmd, seq);
if (ret)
return ret;
if (cmd == MCU_CMD_FW_SCATTER)
type = MT7921_SDIO_FWDL;
mt7921_skb_add_sdio_hdr(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;
}
int mt7921s_mcu_init(struct mt7921_dev *dev)
{
static const struct mt76_mcu_ops mt7921s_mcu_ops = {
.headroom = MT_SDIO_HDR_SIZE + sizeof(struct mt7921_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 mt7921_dev *dev)
{
struct sdio_func *func = dev->mt76.sdio.func;
struct mt76_phy *mphy = &dev->mt76.phy;
struct mt76_connac_pm *pm = &dev->pm;
int err = 0;
u32 status;
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);
sdio_release_host(func);
if (err < 0) {
dev_err(dev->mt76.dev, "driver own failed\n");
err = -EIO;
goto out;
}
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;
out:
return err;
}
int mt7921s_mcu_fw_pmctrl(struct mt7921_dev *dev)
{
struct sdio_func *func = dev->mt76.sdio.func;
struct mt76_phy *mphy = &dev->mt76.phy;
struct mt76_connac_pm *pm = &dev->pm;
int err = 0;
u32 status;
sdio_claim_host(func);
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);
sdio_release_host(func);
if (err < 0) {
dev_err(dev->mt76.dev, "firmware own failed\n");
clear_bit(MT76_STATE_PM, &mphy->state);
err = -EIO;
}
pm->stats.last_doze_event = jiffies;
pm->stats.awake_time += pm->stats.last_doze_event -
pm->stats.last_wake_event;
return err;
}