diff options
Diffstat (limited to 'drivers/net/wireless/mediatek/mt76')
45 files changed, 6034 insertions, 288 deletions
diff --git a/drivers/net/wireless/mediatek/mt76/Kconfig b/drivers/net/wireless/mediatek/mt76/Kconfig index c30d8f5bbf2a..dbe8c70a8f73 100644 --- a/drivers/net/wireless/mediatek/mt76/Kconfig +++ b/drivers/net/wireless/mediatek/mt76/Kconfig @@ -21,3 +21,4 @@ config MT76x02_USB source "drivers/net/wireless/mediatek/mt76/mt76x0/Kconfig" source "drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig" +source "drivers/net/wireless/mediatek/mt76/mt7603/Kconfig" diff --git a/drivers/net/wireless/mediatek/mt76/Makefile b/drivers/net/wireless/mediatek/mt76/Makefile index fa7a44edd02d..3fd1b64b4aa7 100644 --- a/drivers/net/wireless/mediatek/mt76/Makefile +++ b/drivers/net/wireless/mediatek/mt76/Makefile @@ -7,7 +7,7 @@ mt76-y := \ mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o \ tx.o agg-rx.o mcu.o -mt76-usb-y := usb.o usb_trace.o usb_mcu.o +mt76-usb-y := usb.o usb_trace.o CFLAGS_trace.o := -I$(src) CFLAGS_usb_trace.o := -I$(src) @@ -22,3 +22,4 @@ mt76x02-usb-y := mt76x02_usb_mcu.o mt76x02_usb_core.o obj-$(CONFIG_MT76x0_COMMON) += mt76x0/ obj-$(CONFIG_MT76x2_COMMON) += mt76x2/ +obj-$(CONFIG_MT7603E) += mt7603/ diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c index 530e5593765c..a1529920d877 100644 --- a/drivers/net/wireless/mediatek/mt76/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/eeprom.c @@ -54,22 +54,30 @@ mt76_get_of_eeprom(struct mt76_dev *dev, int len) part = np->name; mtd = get_mtd_device_nm(part); - if (IS_ERR(mtd)) - return PTR_ERR(mtd); + if (IS_ERR(mtd)) { + ret = PTR_ERR(mtd); + goto out_put_node; + } - if (size <= sizeof(*list)) - return -EINVAL; + if (size <= sizeof(*list)) { + ret = -EINVAL; + goto out_put_node; + } offset = be32_to_cpup(list); ret = mtd_read(mtd, offset, len, &retlen, dev->eeprom.data); put_mtd_device(mtd); if (ret) - return ret; + goto out_put_node; - if (retlen < len) - return -EINVAL; + if (retlen < len) { + ret = -EINVAL; + goto out_put_node; + } - return 0; +out_put_node: + of_node_put(np); + return ret; #else return -ENOENT; #endif diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 82e9f78ef328..a033745adb2f 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -714,6 +714,11 @@ int mt76_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, new_state == IEEE80211_STA_NONE) return mt76_sta_add(dev, vif, sta); + if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC && + dev->drv->sta_assoc) + dev->drv->sta_assoc(dev, vif, sta); + if (old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_NOTEXIST) mt76_sta_remove(dev, vif, sta); diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index f55dc621e060..5dfb0601f101 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -304,6 +304,9 @@ struct mt76_driver_ops { int (*sta_add)(struct mt76_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta); + void (*sta_assoc)(struct mt76_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); + void (*sta_remove)(struct mt76_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta); }; @@ -384,8 +387,7 @@ struct mt76_usb { struct mt76u_mcu { struct mutex mutex; - struct completion cmpl; - struct mt76u_buf res; + u8 *data; u32 msg_seq; /* multiple reads */ @@ -729,16 +731,20 @@ static inline u8 q2ep(u8 qid) } static inline int -mt76u_bulk_msg(struct mt76_dev *dev, void *data, int len, int timeout) +mt76u_bulk_msg(struct mt76_dev *dev, void *data, int len, int *actual_len, + int timeout) { struct usb_interface *intf = to_usb_interface(dev->dev); struct usb_device *udev = interface_to_usbdev(intf); struct mt76_usb *usb = &dev->usb; unsigned int pipe; - int sent; - pipe = usb_sndbulkpipe(udev, usb->out_ep[MT_EP_OUT_INBAND_CMD]); - return usb_bulk_msg(udev, pipe, data, len, &sent, timeout); + if (actual_len) + pipe = usb_rcvbulkpipe(udev, usb->in_ep[MT_EP_IN_CMD_RESP]); + else + pipe = usb_sndbulkpipe(udev, usb->out_ep[MT_EP_OUT_INBAND_CMD]); + + return usb_bulk_msg(udev, pipe, data, len, actual_len, timeout); } int mt76u_vendor_request(struct mt76_dev *dev, u8 req, @@ -747,13 +753,6 @@ int mt76u_vendor_request(struct mt76_dev *dev, u8 req, void mt76u_single_wr(struct mt76_dev *dev, const u8 req, const u16 offset, const u32 val); int mt76u_init(struct mt76_dev *dev, struct usb_interface *intf); -void mt76u_deinit(struct mt76_dev *dev); -int mt76u_buf_alloc(struct mt76_dev *dev, struct mt76u_buf *buf, - int len, int data_len, gfp_t gfp); -void mt76u_buf_free(struct mt76u_buf *buf); -int mt76u_submit_buf(struct mt76_dev *dev, int dir, int index, - struct mt76u_buf *buf, gfp_t gfp, - usb_complete_t complete_fn, void *context); int mt76u_submit_rx_buffers(struct mt76_dev *dev); int mt76u_alloc_queues(struct mt76_dev *dev); void mt76u_stop_queues(struct mt76_dev *dev); @@ -767,8 +766,4 @@ void mt76_mcu_rx_event(struct mt76_dev *dev, struct sk_buff *skb); struct sk_buff *mt76_mcu_get_response(struct mt76_dev *dev, unsigned long expires); -void mt76u_mcu_complete_urb(struct urb *urb); -int mt76u_mcu_init_rx(struct mt76_dev *dev); -void mt76u_mcu_deinit(struct mt76_dev *dev); - #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7603/Kconfig new file mode 100644 index 000000000000..087945c3d8f3 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/Kconfig @@ -0,0 +1,9 @@ +config MT7603E + tristate "MediaTek MT7603E (PCIe) and MT76x8 WLAN support" + select MT76_CORE + depends on MAC80211 + depends on PCI + help + This adds support for MT7603E wireless PCIe devices and the WLAN core on + MT7628/MT7688 SoC devices + diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/Makefile b/drivers/net/wireless/mediatek/mt76/mt7603/Makefile new file mode 100644 index 000000000000..d95a30421c62 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_MT7603E) += mt7603e.o + +mt7603e-y := \ + pci.o soc.o main.o init.o mcu.o \ + core.o dma.o mac.o eeprom.o \ + beacon.o debugfs.o diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c b/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c new file mode 100644 index 000000000000..afcd86f735b4 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: ISC */ + +#include "mt7603.h" + +struct beacon_bc_data { + struct mt7603_dev *dev; + struct sk_buff_head q; + struct sk_buff *tail[MT7603_MAX_INTERFACES]; + int count[MT7603_MAX_INTERFACES]; +}; + +static void +mt7603_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt7603_dev *dev = (struct mt7603_dev *)priv; + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; + struct sk_buff *skb = NULL; + + if (!(dev->beacon_mask & BIT(mvif->idx))) + return; + + skb = ieee80211_beacon_get(mt76_hw(dev), vif); + if (!skb) + return; + + mt76_dma_tx_queue_skb(&dev->mt76, &dev->mt76.q_tx[MT_TXQ_BEACON], skb, + &mvif->sta.wcid, NULL); + + spin_lock_bh(&dev->ps_lock); + mt76_wr(dev, MT_DMA_FQCR0, MT_DMA_FQCR0_BUSY | + FIELD_PREP(MT_DMA_FQCR0_TARGET_WCID, mvif->sta.wcid.idx) | + FIELD_PREP(MT_DMA_FQCR0_TARGET_QID, + dev->mt76.q_tx[MT_TXQ_CAB].hw_idx) | + FIELD_PREP(MT_DMA_FQCR0_DEST_PORT_ID, 3) | + FIELD_PREP(MT_DMA_FQCR0_DEST_QUEUE_ID, 8)); + + if (!mt76_poll(dev, MT_DMA_FQCR0, MT_DMA_FQCR0_BUSY, 0, 5000)) + dev->beacon_check = MT7603_WATCHDOG_TIMEOUT; + + spin_unlock_bh(&dev->ps_lock); +} + +static void +mt7603_add_buffered_bc(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct beacon_bc_data *data = priv; + struct mt7603_dev *dev = data->dev; + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; + struct ieee80211_tx_info *info; + struct sk_buff *skb; + + if (!(dev->beacon_mask & BIT(mvif->idx))) + return; + + skb = ieee80211_get_buffered_bc(mt76_hw(dev), vif); + if (!skb) + return; + + info = IEEE80211_SKB_CB(skb); + info->control.vif = vif; + info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ; + mt76_skb_set_moredata(skb, true); + __skb_queue_tail(&data->q, skb); + data->tail[mvif->idx] = skb; + data->count[mvif->idx]++; +} + +void mt7603_pre_tbtt_tasklet(unsigned long arg) +{ + struct mt7603_dev *dev = (struct mt7603_dev *)arg; + struct mt76_queue *q; + struct beacon_bc_data data = {}; + struct sk_buff *skb; + int i, nframes; + + data.dev = dev; + __skb_queue_head_init(&data.q); + + q = &dev->mt76.q_tx[MT_TXQ_BEACON]; + spin_lock_bh(&q->lock); + ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7603_update_beacon_iter, dev); + mt76_queue_kick(dev, q); + spin_unlock_bh(&q->lock); + + /* Flush all previous CAB queue packets */ + mt76_wr(dev, MT_WF_ARB_CAB_FLUSH, GENMASK(30, 16) | BIT(0)); + + mt76_queue_tx_cleanup(dev, MT_TXQ_CAB, false); + + mt76_csa_check(&dev->mt76); + if (dev->mt76.csa_complete) + goto out; + + q = &dev->mt76.q_tx[MT_TXQ_CAB]; + do { + nframes = skb_queue_len(&data.q); + ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7603_add_buffered_bc, &data); + } while (nframes != skb_queue_len(&data.q) && + skb_queue_len(&data.q) < 8); + + if (skb_queue_empty(&data.q)) + goto out; + + for (i = 0; i < ARRAY_SIZE(data.tail); i++) { + if (!data.tail[i]) + continue; + + mt76_skb_set_moredata(data.tail[i], false); + } + + spin_lock_bh(&q->lock); + while ((skb = __skb_dequeue(&data.q)) != NULL) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; + + mt76_dma_tx_queue_skb(&dev->mt76, q, skb, &mvif->sta.wcid, + NULL); + } + mt76_queue_kick(dev, q); + spin_unlock_bh(&q->lock); + + for (i = 0; i < ARRAY_SIZE(data.count); i++) + mt76_wr(dev, MT_WF_ARB_CAB_COUNT_B0_REG(i), + data.count[i] << MT_WF_ARB_CAB_COUNT_B0_SHIFT(i)); + + mt76_wr(dev, MT_WF_ARB_CAB_START, + MT_WF_ARB_CAB_START_BSSn(0) | + (MT_WF_ARB_CAB_START_BSS0n(1) * + ((1 << (MT7603_MAX_INTERFACES - 1)) - 1))); + +out: + mt76_queue_tx_cleanup(dev, MT_TXQ_BEACON, false); + if (dev->mt76.q_tx[MT_TXQ_BEACON].queued > + __sw_hweight8(dev->beacon_mask)) + dev->beacon_check++; +} + +void mt7603_beacon_set_timer(struct mt7603_dev *dev, int idx, int intval) +{ + u32 pre_tbtt = MT7603_PRE_TBTT_TIME / 64; + + if (idx >= 0) { + if (intval) + dev->beacon_mask |= BIT(idx); + else + dev->beacon_mask &= ~BIT(idx); + } + + if (!dev->beacon_mask || (!intval && idx < 0)) { + mt7603_irq_disable(dev, MT_INT_MAC_IRQ3); + mt76_clear(dev, MT_ARB_SCR, MT_ARB_SCR_BCNQ_OPMODE_MASK); + mt76_wr(dev, MT_HW_INT_MASK(3), 0); + return; + } + + dev->beacon_int = intval; + mt76_wr(dev, MT_TBTT, + FIELD_PREP(MT_TBTT_PERIOD, intval) | MT_TBTT_CAL_ENABLE); + + mt76_wr(dev, MT_TBTT_TIMER_CFG, 0x99); /* start timer */ + + mt76_rmw_field(dev, MT_ARB_SCR, MT_ARB_SCR_BCNQ_OPMODE_MASK, + MT_BCNQ_OPMODE_AP); + mt76_clear(dev, MT_ARB_SCR, MT_ARB_SCR_TBTT_BCN_PRIO); + mt76_set(dev, MT_ARB_SCR, MT_ARB_SCR_TBTT_BCAST_PRIO); + + mt76_wr(dev, MT_PRE_TBTT, pre_tbtt); + + mt76_set(dev, MT_HW_INT_MASK(3), + MT_HW_INT3_PRE_TBTT0 | MT_HW_INT3_TBTT0); + + mt76_set(dev, MT_WF_ARB_BCN_START, + MT_WF_ARB_BCN_START_BSSn(0) | + ((dev->beacon_mask >> 1) * MT_WF_ARB_BCN_START_BSS0n(1))); + mt7603_irq_enable(dev, MT_INT_MAC_IRQ3); + + if (dev->beacon_mask & ~BIT(0)) + mt76_set(dev, MT_LPON_SBTOR(0), MT_LPON_SBTOR_SUB_BSS_EN); + else + mt76_clear(dev, MT_LPON_SBTOR(0), MT_LPON_SBTOR_SUB_BSS_EN); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/core.c b/drivers/net/wireless/mediatek/mt76/mt7603/core.c new file mode 100644 index 000000000000..1086dcd376a0 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/core.c @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: ISC */ + +#include "mt7603.h" + +void mt7603_set_irq_mask(struct mt7603_dev *dev, u32 clear, u32 set) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->mt76.mmio.irq_lock, flags); + dev->mt76.mmio.irqmask &= ~clear; + dev->mt76.mmio.irqmask |= set; + mt76_wr(dev, MT_INT_MASK_CSR, dev->mt76.mmio.irqmask); + spin_unlock_irqrestore(&dev->mt76.mmio.irq_lock, flags); +} + +void mt7603_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + + mt7603_irq_enable(dev, MT_INT_RX_DONE(q)); +} + +irqreturn_t mt7603_irq_handler(int irq, void *dev_instance) +{ + struct mt7603_dev *dev = dev_instance; + u32 intr; + + intr = mt76_rr(dev, MT_INT_SOURCE_CSR); + mt76_wr(dev, MT_INT_SOURCE_CSR, intr); + + if (!test_bit(MT76_STATE_INITIALIZED, &dev->mt76.state)) + return IRQ_NONE; + + intr &= dev->mt76.mmio.irqmask; + + if (intr & MT_INT_MAC_IRQ3) { + u32 hwintr = mt76_rr(dev, MT_HW_INT_STATUS(3)); + + mt76_wr(dev, MT_HW_INT_STATUS(3), hwintr); + if (hwintr & MT_HW_INT3_PRE_TBTT0) + tasklet_schedule(&dev->pre_tbtt_tasklet); + + if ((hwintr & MT_HW_INT3_TBTT0) && dev->mt76.csa_complete) + mt76_csa_finish(&dev->mt76); + } + + if (intr & MT_INT_TX_DONE_ALL) { + mt7603_irq_disable(dev, MT_INT_TX_DONE_ALL); + tasklet_schedule(&dev->tx_tasklet); + } + + if (intr & MT_INT_RX_DONE(0)) { + mt7603_irq_disable(dev, MT_INT_RX_DONE(0)); + napi_schedule(&dev->mt76.napi[0]); + } + + if (intr & MT_INT_RX_DONE(1)) { + mt7603_irq_disable(dev, MT_INT_RX_DONE(1)); + napi_schedule(&dev->mt76.napi[1]); + } + + return IRQ_HANDLED; +} + +u32 mt7603_reg_map(struct mt7603_dev *dev, u32 addr) +{ + u32 base = addr & GENMASK(31, 19); + u32 offset = addr & GENMASK(18, 0); + + dev->bus_ops->wr(&dev->mt76, MT_MCU_PCIE_REMAP_2, base); + + return MT_PCIE_REMAP_BASE_2 + offset; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c new file mode 100644 index 000000000000..f8b3b6ab6297 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: ISC */ + +#include "mt7603.h" + +static int +mt7603_reset_read(struct seq_file *s, void *data) +{ + struct mt7603_dev *dev = dev_get_drvdata(s->private); + static const char * const reset_cause_str[] = { + [RESET_CAUSE_TX_HANG] = "TX hang", + [RESET_CAUSE_TX_BUSY] = "TX DMA busy stuck", + [RESET_CAUSE_RX_BUSY] = "RX DMA busy stuck", + [RESET_CAUSE_RX_PSE_BUSY] = "RX PSE busy stuck", + [RESET_CAUSE_BEACON_STUCK] = "Beacon stuck", + [RESET_CAUSE_MCU_HANG] = "MCU hang", + [RESET_CAUSE_RESET_FAILED] = "PSE reset failed", + }; + int i; + + for (i = 0; i < ARRAY_SIZE(reset_cause_str); i++) { + if (!reset_cause_str[i]) + continue; + + seq_printf(s, "%20s: %u\n", reset_cause_str[i], + dev->reset_cause[i]); + } + + return 0; +} + +static int +mt7603_radio_read(struct seq_file *s, void *data) +{ + struct mt7603_dev *dev = dev_get_drvdata(s->private); + + seq_printf(s, "Sensitivity: %d\n", dev->sensitivity); + seq_printf(s, "False CCA: ofdm=%d cck=%d\n", + dev->false_cca_ofdm, dev->false_cca_cck); + + return 0; +} + +void mt7603_init_debugfs(struct mt7603_dev *dev) +{ + struct dentry *dir; + + dir = mt76_register_debugfs(&dev->mt76); + if (!dir) + return; + + debugfs_create_u32("reset_test", 0600, dir, &dev->reset_test); + debugfs_create_devm_seqfile(dev->mt76.dev, "reset", dir, + mt7603_reset_read); + debugfs_create_devm_seqfile(dev->mt76.dev, "radio", dir, + mt7603_radio_read); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c new file mode 100644 index 000000000000..d69e82c66ab2 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: ISC */ + +#include "mt7603.h" +#include "mac.h" +#include "../dma.h" + +static int +mt7603_init_tx_queue(struct mt7603_dev *dev, struct mt76_queue *q, + int idx, int n_desc) +{ + int ret; + + q->hw_idx = idx; + q->regs = dev->mt76.mmio.regs + MT_TX_RING_BASE + idx * MT_RING_SIZE; + q->ndesc = n_desc; + + ret = mt76_queue_alloc(dev, q); + if (ret) + return ret; + + mt7603_irq_enable(dev, MT_INT_TX_DONE(idx)); + + return 0; +} + +static void +mt7603_rx_loopback_skb(struct mt7603_dev *dev, struct sk_buff *skb) +{ + __le32 *txd = (__le32 *)skb->data; + struct mt7603_sta *msta; + struct mt76_wcid *wcid; + int idx; + u32 val; + + if (skb->len < sizeof(MT_TXD_SIZE) + sizeof(struct ieee80211_hdr)) + goto free; + + val = le32_to_cpu(txd[1]); + idx = FIELD_GET(MT_TXD1_WLAN_IDX, val); + skb->priority = FIELD_GET(MT_TXD1_TID, val); + + if (idx >= MT7603_WTBL_STA - 1) + goto free; + + wcid = rcu_dereference(dev->mt76.wcid[idx]); + if (!wcid) + goto free; + + msta = container_of(wcid, struct mt7603_sta, wcid); + val = le32_to_cpu(txd[0]); + skb_set_queue_mapping(skb, FIELD_GET(MT_TXD0_Q_IDX, val)); + + spin_lock_bh(&dev->ps_lock); + __skb_queue_tail(&msta->psq, skb); + if (skb_queue_len(&msta->psq) >= 64) { + skb = __skb_dequeue(&msta->psq); + dev_kfree_skb(skb); + } + spin_unlock_bh(&dev->ps_lock); + return; + +free: + dev_kfree_skb(skb); +} + +void mt7603_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + __le32 *rxd = (__le32 *)skb->data; + __le32 *end = (__le32 *)&skb->data[skb->len]; + enum rx_pkt_type type; + + type = FIELD_GET(MT_RXD0_PKT_TYPE, le32_to_cpu(rxd[0])); + + if (q == MT_RXQ_MCU) { + if (type == PKT_TYPE_RX_EVENT) + mt76_mcu_rx_event(&dev->mt76, skb); + else + mt7603_rx_loopback_skb(dev, skb); + return; + } + + switch (type) { + case PKT_TYPE_TXS: + for (rxd++; rxd + 5 <= end; rxd += 5) + mt7603_mac_add_txs(dev, rxd); + dev_kfree_skb(skb); + break; + case PKT_TYPE_RX_EVENT: + mt76_mcu_rx_event(&dev->mt76, skb); + return; + case PKT_TYPE_NORMAL: + if (mt7603_mac_fill_rx(dev, skb) == 0) { + mt76_rx(&dev->mt76, q, skb); + return; + } + /* fall through */ + default: + dev_kfree_skb(skb); + break; + } +} + +static int +mt7603_init_rx_queue(struct mt7603_dev *dev, struct mt76_queue *q, + int idx, int n_desc, int bufsize) +{ + int ret; + + q->regs = dev->mt76.mmio.regs + MT_RX_RING_BASE + idx * MT_RING_SIZE; + q->ndesc = n_desc; + q->buf_size = bufsize; + + ret = mt76_queue_alloc(dev, q); + if (ret) + return ret; + + mt7603_irq_enable(dev, MT_INT_RX_DONE(idx)); + + return 0; +} + +static void +mt7603_tx_tasklet(unsigned long data) +{ + struct mt7603_dev *dev = (struct mt7603_dev *)data; + int i; + + dev->tx_dma_check = 0; + for (i = MT_TXQ_MCU; i >= 0; i--) + mt76_queue_tx_cleanup(dev, i, false); + + mt7603_irq_enable(dev, MT_INT_TX_DONE_ALL); +} + +int mt7603_dma_init(struct mt7603_dev *dev) +{ + static const u8 wmm_queue_map[] = { + [IEEE80211_AC_BK] = 0, + [IEEE80211_AC_BE] = 1, + [IEEE80211_AC_VI] = 2, + [IEEE80211_AC_VO] = 3, + }; + int ret; + int i; + + mt76_dma_attach(&dev->mt76); + + init_waitqueue_head(&dev->mt76.mmio.mcu.wait); + skb_queue_head_init(&dev->mt76.mmio.mcu.res_q); + + tasklet_init(&dev->tx_tasklet, mt7603_tx_tasklet, (unsigned long)dev); + + mt76_clear(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_TX_DMA_EN | + MT_WPDMA_GLO_CFG_RX_DMA_EN | + MT_WPDMA_GLO_CFG_DMA_BURST_SIZE | |