diff --git a/Makefile b/Makefile index 71d208be..4de47b9d 100644 --- a/Makefile +++ b/Makefile @@ -15,4 +15,4 @@ mt76x2e-$(CONFIG_OF) += mt76x2_of.o mt7603e-y := \ mt7603_pci.o mt7603_main.o mt7603_init.o mt7603_mcu.o \ - mt7603_core.o + mt7603_core.o mt7603_dma.o diff --git a/dma.h b/dma.h index f9da8b3b..c3af9bf6 100644 --- a/dma.h +++ b/dma.h @@ -13,6 +13,8 @@ #ifndef __MT76_DMA_H #define __MT76_DMA_H +#define MT_RING_SIZE 0x10 + #define MT_DMA_CTL_SD_LEN1 GENMASK(13, 0) #define MT_DMA_CTL_LAST_SEC1 BIT(14) #define MT_DMA_CTL_BURST BIT(15) diff --git a/mt76.h b/mt76.h index 08dd75fd..8f7fb2b8 100644 --- a/mt76.h +++ b/mt76.h @@ -21,6 +21,11 @@ #include #include "util.h" +#define MT_RX_RING_SIZE 128 +#define MT_TX_RING_SIZE 128 +#define MT_MCU_RING_SIZE 32 +#define MT_RX_BUF_SIZE 2048 + struct mt76_dev; struct mt76_bus_ops { diff --git a/mt7603.h b/mt7603.h index 56a2f768..a07f846e 100644 --- a/mt7603.h +++ b/mt7603.h @@ -14,24 +14,58 @@ #ifndef __MT7603_H #define __MT7603_H -#include #include "mt76.h" #include "mt7603_regs.h" #define MT7603_FIRMWARE_E1 "mt7603_e1.bin" #define MT7603_FIRMWARE_E2 "mt7603_e2.bin" +enum mt7603_txq_id { + MT_TXQ_VO = IEEE80211_AC_VO, + MT_TXQ_VI = IEEE80211_AC_VI, + MT_TXQ_BE = IEEE80211_AC_BE, + MT_TXQ_BK = IEEE80211_AC_BK, + MT_TXQ_PSD, + MT_TXQ_MCU, + __MT_TXQ_MAX +}; + +struct mt7603_mcu { + struct mutex mutex; + + wait_queue_head_t wait; + struct sk_buff_head res_q; + + struct mt76_queue q_rx; + u32 msg_seq; +}; + struct mt7603_dev { struct mt76_dev mt76; u32 rev; u32 irqmask; spinlock_t irq_lock; + + struct mt7603_mcu mcu; + struct mt76_queue q_rx; + struct mt76_queue q_tx[__MT_TXQ_MAX]; + + struct net_device napi_dev; + struct napi_struct napi; + + struct tasklet_struct tx_tasklet; + struct tasklet_struct rx_tasklet; + struct tasklet_struct pre_tbtt_tasklet; }; extern const struct ieee80211_ops mt7603_ops; struct mt7603_dev *mt7603_alloc_device(struct device *pdev); irqreturn_t mt7603_irq_handler(int irq, void *dev_instance); + +int mt7603_init_hardware(struct mt7603_dev *dev); +int mt7603_dma_init(struct mt7603_dev *dev); +void mt7603_dma_cleanup(struct mt7603_dev *dev); int mt7603_mcu_init(struct mt7603_dev *dev); void mt7603_set_irq_mask(struct mt7603_dev *dev, u32 clear, u32 set); diff --git a/mt7603_dma.c b/mt7603_dma.c new file mode 100644 index 00000000..f210a390 --- /dev/null +++ b/mt7603_dma.c @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "mt7603.h" +#include "mt7603_dma.h" + +static int +mt7603_dma_add_rx_buf(struct mt7603_dev *dev, struct mt76_queue *q, + dma_addr_t addr, int len) +{ + return mt76_queue_add_buf(dev, q, addr, len, 0, 0, 0); +} + +static int +mt7603_dma_rx_fill(struct mt7603_dev *dev, struct mt76_queue *q) +{ + dma_addr_t addr; + void *buf; + int frames = 0; + int len = SKB_WITH_OVERHEAD(q->buf_size); + int idx; + + spin_lock_bh(&q->lock); + + while (q->queued < q->ndesc - 1) { + buf = kzalloc(q->buf_size, GFP_ATOMIC); + if (!buf) + break; + + addr = dma_map_single(dev->mt76.dev, buf, len, DMA_FROM_DEVICE); + if (dma_mapping_error(dev->mt76.dev, addr)) { + kfree(buf); + break; + } + + idx = mt7603_dma_add_rx_buf(dev, q, addr, len); + q->entry[idx].buf = buf; + frames++; + } + + if (frames) + mt76_queue_kick(dev, q); + + spin_unlock_bh(&q->lock); + + return frames; +} + +int +mt7603_tx_queue_mcu(struct mt7603_dev *dev, enum mt7603_txq_id qid, + struct sk_buff *skb, int cmd, int seq) +{ + struct mt76_queue *q = &dev->q_tx[qid]; + dma_addr_t addr; + int idx; + + addr = dma_map_single(dev->mt76.dev, skb->data, skb->len, DMA_TO_DEVICE); + if (dma_mapping_error(dev->mt76.dev, addr)) + return -ENOMEM; + + spin_lock_bh(&q->lock); + idx = mt76_queue_add_buf(dev, q, addr, skb->len, 0, 0, 0); + q->entry[idx].skb = skb; + mt76_queue_kick(dev, q); + spin_unlock_bh(&q->lock); + + return 0; +} + +static void +mt7603_tx_cleanup_entry(struct mt76_dev *mdev, struct mt76_queue *q, + struct mt76_queue_entry *e) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + + (void) dev; + dev_kfree_skb_any(e->skb); + + if (e->schedule) + q->swq_queued--; +} + +static void +mt7603_rx_cleanup(struct mt7603_dev *dev, struct mt76_queue *q) +{ + void *buf; + + spin_lock_bh(&q->lock); + do { + buf = mt76_queue_dequeue(dev, q, true, NULL, NULL); + if (!buf) + break; + + kfree(buf); + } while (1); + spin_unlock_bh(&q->lock); +} + +static void +mt76_tx_cleanup(struct mt7603_dev *dev, struct mt76_queue *q, bool flush) +{ + spin_lock_bh(&q->lock); + mt76_queue_cleanup(dev, q, flush, mt7603_tx_cleanup_entry); + spin_unlock_bh(&q->lock); +} + +static int +mt7603_init_tx_queue(struct mt7603_dev *dev, struct mt76_queue *q, + int idx, int n_desc, bool mcu) +{ + int ret; + + q->regs = dev->mt76.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_process_rx_skb(struct mt7603_dev *dev, struct mt76_queue *q, + struct sk_buff *skb, u32 info) +{ + if (q == &dev->mcu.q_rx) { + u32 *rxfce; + + rxfce = (u32 *) skb->cb; + *rxfce = info; + + skb_queue_tail(&dev->mcu.res_q, skb); + wake_up(&dev->mcu.wait); + return; + } + + dev_kfree_skb(skb); +} + +static int +mt7603_process_rx_queue(struct mt7603_dev *dev, struct mt76_queue *q, int budget) +{ + struct sk_buff *skb; + unsigned char *data; + int len; + int done = 0; + + while (done < budget) { + u32 info; + + data = mt76_queue_dequeue(dev, q, false, &len, &info); + if (!data) + break; + + skb = build_skb(data, 0); + if (!skb) { + kfree(data); + continue; + } + + if (skb->tail + len > skb->end) { + dev_kfree_skb(skb); + continue; + } + + __skb_put(skb, len); + mt7603_process_rx_skb(dev, q, skb, info); + done++; + } + + mt7603_dma_rx_fill(dev, q); + return done; +} + +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.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; + + for (i = ARRAY_SIZE(dev->q_tx) - 1; i >= 0; i--) + mt76_tx_cleanup(dev, &dev->q_tx[i], false); + + mt7603_irq_enable(dev, MT_INT_TX_DONE_ALL); +} + +static int +mt7603_dma_rx_poll(struct napi_struct *napi, int budget) +{ + struct mt7603_dev *dev = container_of(napi, struct mt7603_dev, napi); + int done; + + done = mt7603_process_rx_queue(dev, &dev->q_rx, budget); + + if (done < budget) { + napi_complete(napi); + mt7603_irq_enable(dev, MT_INT_RX_DONE(0)); + } + + return done; +} + +static void +mt7603_rx_tasklet(unsigned long data) +{ + struct mt7603_dev *dev = (struct mt7603_dev *) data; + + mt7603_process_rx_queue(dev, &dev->mcu.q_rx, dev->mcu.q_rx.ndesc); + mt7603_irq_enable(dev, MT_INT_RX_DONE(1)); +} + +int mt7603_dma_init(struct mt7603_dev *dev) +{ + static const u8 wmm_queue_map[] = { + [IEEE80211_AC_BE] = 0, + [IEEE80211_AC_BK] = 1, + [IEEE80211_AC_VI] = 2, + [IEEE80211_AC_VO] = 3, + }; + int ret; + int i; + + mt76_dma_init(&dev->mt76); + + init_waitqueue_head(&dev->mcu.wait); + skb_queue_head_init(&dev->mcu.res_q); + + init_dummy_netdev(&dev->napi_dev); + netif_napi_add(&dev->napi_dev, &dev->napi, mt7603_dma_rx_poll, 64); + + tasklet_init(&dev->tx_tasklet, mt7603_tx_tasklet, (unsigned long) dev); + tasklet_init(&dev->rx_tasklet, mt7603_rx_tasklet, (unsigned long) dev); + + mt76_wr(dev, MT_WPDMA_RST_IDX, ~0); + + for (i = 0; i < ARRAY_SIZE(wmm_queue_map); i++) { + ret = mt7603_init_tx_queue(dev, &dev->q_tx[i], wmm_queue_map[i], + MT_TX_RING_SIZE, false); + if (ret) + return ret; + } + + ret = mt7603_init_tx_queue(dev, &dev->q_tx[MT_TXQ_PSD], + MT_TX_HW_QUEUE_MGMT, MT_TX_RING_SIZE, false); + if (ret) + return ret; + + ret = mt7603_init_tx_queue(dev, &dev->q_tx[MT_TXQ_MCU], + MT_TX_HW_QUEUE_MCU, MT_MCU_RING_SIZE, true); + if (ret) + return ret; + + ret = mt7603_init_rx_queue(dev, &dev->mcu.q_rx, 1, MT_MCU_RING_SIZE, + MT_RX_BUF_SIZE); + if (ret) + return ret; + + ret = mt7603_init_rx_queue(dev, &dev->q_rx, 0, + MT_RX_RING_SIZE, MT_RX_BUF_SIZE); + if (ret) + return ret; + + mt7603_dma_rx_fill(dev, &dev->q_rx); + mt7603_dma_rx_fill(dev, &dev->mcu.q_rx); + + return 0; +} + +void mt7603_dma_cleanup(struct mt7603_dev *dev) +{ + int i; + + tasklet_kill(&dev->tx_tasklet); + tasklet_kill(&dev->rx_tasklet); + for (i = 0; i < ARRAY_SIZE(dev->q_tx); i++) + mt76_tx_cleanup(dev, &dev->q_tx[i], true); + mt7603_rx_cleanup(dev, &dev->q_rx); + mt7603_rx_cleanup(dev, &dev->mcu.q_rx); +} diff --git a/mt7603_dma.h b/mt7603_dma.h new file mode 100644 index 00000000..690fc68e --- /dev/null +++ b/mt7603_dma.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MT7603_DMA_H +#define __MT7603_DMA_H + +#include "dma.h" + +#endif diff --git a/mt7603_init.c b/mt7603_init.c index 063c3a13..c338effd 100644 --- a/mt7603_init.c +++ b/mt7603_init.c @@ -28,3 +28,18 @@ struct mt7603_dev *mt7603_alloc_device(struct device *pdev) return dev; } + +int mt7603_init_hardware(struct mt7603_dev *dev) +{ + int ret; + + ret = mt7603_dma_init(dev); + if (ret) + return ret; + + ret = mt7603_mcu_init(dev); + if (ret) + return ret; + + return 0; +} diff --git a/mt7603_pci.c b/mt7603_pci.c index bc8601c9..f07e169c 100644 --- a/mt7603_pci.c +++ b/mt7603_pci.c @@ -58,7 +58,7 @@ mt76pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (ret) goto error; - ret = mt7603_mcu_init(dev); + ret = mt7603_init_hardware(dev); if (ret) goto error; @@ -78,6 +78,7 @@ mt76pci_remove(struct pci_dev *pdev) { struct mt7603_dev *dev = pci_get_drvdata(pdev); + mt7603_dma_cleanup(dev); ieee80211_free_hw(mt76_hw(dev)); } diff --git a/mt7603_regs.h b/mt7603_regs.h index 000bc099..7d7af2d2 100644 --- a/mt7603_regs.h +++ b/mt7603_regs.h @@ -31,9 +31,26 @@ #define MT_HIF(ofs) (MT_HIF_BASE + (ofs)) #define MT_ASIC_VERSION MT_HIF(0x000) + #define MT_INT_SOURCE_CSR MT_HIF(0x200) #define MT_INT_MASK_CSR MT_HIF(0x204) +#define MT_INT_RX_DONE(_n) BIT(_n) +#define MT_INT_RX_DONE_ALL GENMASK(1, 0) +#define MT_INT_TX_DONE_ALL GENMASK(19, 4) +#define MT_INT_TX_DONE(_n) BIT(_n + 4) + +#define MT_INT_RX_COHERENT BIT(20) +#define MT_INT_TX_COHERENT BIT(21) +#define MT_INT_PRE_TBTT BIT(27) + +#define MT_INT_MCU_CMD BIT(30) + +#define MT_WPDMA_RST_IDX MT_HIF(0x20c) + +#define MT_TX_RING_BASE MT_HIF(0x300) +#define MT_RX_RING_BASE MT_HIF(0x400) + #define MT_SCH_1 MT_HIF(0x588) #define MT_SCH_2 MT_HIF(0x58c) #define MT_SCH_3 MT_HIF(0x590) @@ -45,4 +62,9 @@ #define MT_PCIE_REMAP_BASE_1 0x40000 #define MT_PCIE_REMAP_BASE_2 0x80000 +#define MT_TX_HW_QUEUE_MGMT 4 +#define MT_TX_HW_QUEUE_MCU 5 +#define MT_TX_HW_QUEUE_BCN 7 +#define MT_TX_HW_QUEUE_BMC 8 + #endif diff --git a/mt76x2.h b/mt76x2.h index 8f8ad768..72f6d297 100644 --- a/mt76x2.h +++ b/mt76x2.h @@ -29,13 +29,7 @@ #define MT7662_ROM_PATCH "mt7662_rom_patch.bin" #define MT7662_EEPROM_SIZE 512 -#define MT_RX_BUF_SIZE 2048 #define MT_RX_HEADROOM 32 -#define MT_RX_RING_SIZE 128 - -#define MT_TX_RING_SIZE 128 - -#define MT_MCU_RING_SIZE 32 #define MT_MAX_CHAINS 2 diff --git a/mt76x2_regs.h b/mt76x2_regs.h index 7be56c00..b2759ad6 100644 --- a/mt76x2_regs.h +++ b/mt76x2_regs.h @@ -143,7 +143,6 @@ #define MT_TX_RING_BASE 0x0300 #define MT_RX_RING_BASE 0x03c0 -#define MT_RING_SIZE 0x10 #define MT_TX_HW_QUEUE_MCU 8 #define MT_TX_HW_QUEUE_MGMT 9