-
Notifications
You must be signed in to change notification settings - Fork 102
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
mt76: add mac80211 driver for MT7615 PCIe-based chipsets
This driver is for a newer generation of MediaTek MT7615 4x4 802.11ac PCIe-based chipsets, which support wave2 MU-MIMO up to 4 users/group and also support up to 160MHz bandwidth. The driver fully supports AP, station and monitor mode. Signed-off-by: Ryder Lee <ryder.lee@mediatek.com> Signed-off-by: Roy Luo <royluo@google.com> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> Signed-off-by: Felix Fietkau <nbd@nbd.name>
- Loading branch information
1 parent
598da38
commit 04b8e65
Showing
17 changed files
with
4,863 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -69,6 +69,7 @@ enum mt76_txq_id { | |
MT_TXQ_MCU, | ||
MT_TXQ_BEACON, | ||
MT_TXQ_CAB, | ||
MT_TXQ_FWDL, | ||
__MT_TXQ_MAX | ||
}; | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
config MT7615E | ||
tristate "MediaTek MT7615E (PCIe) support" | ||
select MT76_CORE | ||
depends on MAC80211 | ||
depends on PCI | ||
help | ||
This adds support for MT7615-based wireless PCIe devices. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#SPDX-License-Identifier: ISC | ||
|
||
obj-$(CONFIG_MT7615E) += mt7615e.o | ||
|
||
mt7615e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
// SPDX-License-Identifier: ISC | ||
/* Copyright (C) 2019 MediaTek Inc. | ||
* | ||
* Author: Ryder Lee <ryder.lee@mediatek.com> | ||
* Roy Luo <royluo@google.com> | ||
* Lorenzo Bianconi <lorenzo@kernel.org> | ||
* Felix Fietkau <nbd@nbd.name> | ||
*/ | ||
|
||
#include "mt7615.h" | ||
#include "../dma.h" | ||
#include "mac.h" | ||
|
||
static int | ||
mt7615_init_tx_queues(struct mt7615_dev *dev, int n_desc) | ||
{ | ||
struct mt76_sw_queue *q; | ||
struct mt76_queue *hwq; | ||
int err, i; | ||
|
||
hwq = devm_kzalloc(dev->mt76.dev, sizeof(*hwq), GFP_KERNEL); | ||
if (!hwq) | ||
return -ENOMEM; | ||
|
||
err = mt76_queue_alloc(dev, hwq, 0, n_desc, 0, MT_TX_RING_BASE); | ||
if (err < 0) | ||
return err; | ||
|
||
for (i = 0; i < MT_TXQ_MCU; i++) { | ||
q = &dev->mt76.q_tx[i]; | ||
INIT_LIST_HEAD(&q->swq); | ||
q->q = hwq; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static int | ||
mt7615_init_mcu_queue(struct mt7615_dev *dev, struct mt76_sw_queue *q, | ||
int idx, int n_desc) | ||
{ | ||
struct mt76_queue *hwq; | ||
int err; | ||
|
||
hwq = devm_kzalloc(dev->mt76.dev, sizeof(*hwq), GFP_KERNEL); | ||
if (!hwq) | ||
return -ENOMEM; | ||
|
||
err = mt76_queue_alloc(dev, hwq, idx, n_desc, 0, MT_TX_RING_BASE); | ||
if (err < 0) | ||
return err; | ||
|
||
INIT_LIST_HEAD(&q->swq); | ||
q->q = hwq; | ||
|
||
return 0; | ||
} | ||
|
||
void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, | ||
struct sk_buff *skb) | ||
{ | ||
struct mt7615_dev *dev = container_of(mdev, struct mt7615_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])); | ||
|
||
switch (type) { | ||
case PKT_TYPE_TXS: | ||
for (rxd++; rxd + 7 <= end; rxd += 7) | ||
mt7615_mac_add_txs(dev, rxd); | ||
dev_kfree_skb(skb); | ||
break; | ||
case PKT_TYPE_TXRX_NOTIFY: | ||
mt7615_mac_tx_free(dev, skb); | ||
break; | ||
case PKT_TYPE_RX_EVENT: | ||
mt76_mcu_rx_event(&dev->mt76, skb); | ||
break; | ||
case PKT_TYPE_NORMAL: | ||
if (!mt7615_mac_fill_rx(dev, skb)) { | ||
mt76_rx(&dev->mt76, q, skb); | ||
return; | ||
} | ||
/* fall through */ | ||
default: | ||
dev_kfree_skb(skb); | ||
break; | ||
} | ||
} | ||
|
||
static void mt7615_tx_tasklet(unsigned long data) | ||
{ | ||
struct mt7615_dev *dev = (struct mt7615_dev *)data; | ||
static const u8 queue_map[] = { | ||
MT_TXQ_MCU, | ||
MT_TXQ_BE | ||
}; | ||
int i; | ||
|
||
for (i = 0; i < ARRAY_SIZE(queue_map); i++) | ||
mt76_queue_tx_cleanup(dev, queue_map[i], false); | ||
|
||
mt76_txq_schedule_all(&dev->mt76); | ||
|
||
mt7615_irq_enable(dev, MT_INT_TX_DONE_ALL); | ||
} | ||
|
||
int mt7615_dma_init(struct mt7615_dev *dev) | ||
{ | ||
int ret; | ||
|
||
mt76_dma_attach(&dev->mt76); | ||
|
||
tasklet_init(&dev->mt76.tx_tasklet, mt7615_tx_tasklet, | ||
(unsigned long)dev); | ||
|
||
mt76_wr(dev, MT_WPDMA_GLO_CFG, | ||
MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE | | ||
MT_WPDMA_GLO_CFG_FIFO_LITTLE_ENDIAN | | ||
MT_WPDMA_GLO_CFG_FIRST_TOKEN_ONLY | | ||
MT_WPDMA_GLO_CFG_OMIT_TX_INFO); | ||
|
||
mt76_rmw_field(dev, MT_WPDMA_GLO_CFG, | ||
MT_WPDMA_GLO_CFG_TX_BT_SIZE_BIT0, 0x1); | ||
|
||
mt76_rmw_field(dev, MT_WPDMA_GLO_CFG, | ||
MT_WPDMA_GLO_CFG_TX_BT_SIZE_BIT21, 0x1); | ||
|
||
mt76_rmw_field(dev, MT_WPDMA_GLO_CFG, | ||
MT_WPDMA_GLO_CFG_DMA_BURST_SIZE, 0x3); | ||
|
||
mt76_rmw_field(dev, MT_WPDMA_GLO_CFG, | ||
MT_WPDMA_GLO_CFG_MULTI_DMA_EN, 0x3); | ||
|
||
mt76_wr(dev, MT_WPDMA_GLO_CFG1, 0x1); | ||
mt76_wr(dev, MT_WPDMA_TX_PRE_CFG, 0xf0000); | ||
mt76_wr(dev, MT_WPDMA_RX_PRE_CFG, 0xf7f0000); | ||
mt76_wr(dev, MT_WPDMA_ABT_CFG, 0x4000026); | ||
mt76_wr(dev, MT_WPDMA_ABT_CFG1, 0x18811881); | ||
mt76_set(dev, 0x7158, BIT(16)); | ||
mt76_clear(dev, 0x7000, BIT(23)); | ||
mt76_wr(dev, MT_WPDMA_RST_IDX, ~0); | ||
|
||
ret = mt7615_init_tx_queues(dev, MT7615_TX_RING_SIZE); | ||
if (ret) | ||
return ret; | ||
|
||
ret = mt7615_init_mcu_queue(dev, &dev->mt76.q_tx[MT_TXQ_MCU], | ||
MT7615_TXQ_MCU, | ||
MT7615_TX_MCU_RING_SIZE); | ||
if (ret) | ||
return ret; | ||
|
||
ret = mt7615_init_mcu_queue(dev, &dev->mt76.q_tx[MT_TXQ_FWDL], | ||
MT7615_TXQ_FWDL, | ||
MT7615_TX_FWDL_RING_SIZE); | ||
if (ret) | ||
return ret; | ||
|
||
/* init rx queues */ | ||
ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU], 1, | ||
MT7615_RX_MCU_RING_SIZE, MT_RX_BUF_SIZE, | ||
MT_RX_RING_BASE); | ||
if (ret) | ||
return ret; | ||
|
||
ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], 0, | ||
MT7615_RX_RING_SIZE, MT_RX_BUF_SIZE, | ||
MT_RX_RING_BASE); | ||
if (ret) | ||
return ret; | ||
|
||
mt76_wr(dev, MT_DELAY_INT_CFG, 0); | ||
|
||
ret = mt76_init_queues(dev); | ||
if (ret < 0) | ||
return ret; | ||
|
||
mt76_poll(dev, MT_WPDMA_GLO_CFG, | ||
MT_WPDMA_GLO_CFG_TX_DMA_BUSY | | ||
MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 1000); | ||
|
||
/* start dma engine */ | ||
mt76_set(dev, MT_WPDMA_GLO_CFG, | ||
MT_WPDMA_GLO_CFG_TX_DMA_EN | | ||
MT_WPDMA_GLO_CFG_RX_DMA_EN); | ||
|
||
/* enable interrupts for TX/RX rings */ | ||
mt7615_irq_enable(dev, MT_INT_RX_DONE_ALL | MT_INT_TX_DONE_ALL); | ||
|
||
return 0; | ||
} | ||
|
||
void mt7615_dma_cleanup(struct mt7615_dev *dev) | ||
{ | ||
mt76_clear(dev, MT_WPDMA_GLO_CFG, | ||
MT_WPDMA_GLO_CFG_TX_DMA_EN | | ||
MT_WPDMA_GLO_CFG_RX_DMA_EN); | ||
mt76_set(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_SW_RESET); | ||
|
||
tasklet_kill(&dev->mt76.tx_tasklet); | ||
mt76_dma_cleanup(&dev->mt76); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
// SPDX-License-Identifier: ISC | ||
/* Copyright (C) 2019 MediaTek Inc. | ||
* | ||
* Author: Ryder Lee <ryder.lee@mediatek.com> | ||
* Felix Fietkau <nbd@nbd.name> | ||
*/ | ||
|
||
#include "mt7615.h" | ||
#include "eeprom.h" | ||
|
||
static int mt7615_efuse_read(struct mt7615_dev *dev, u32 base, | ||
u16 addr, u8 *data) | ||
{ | ||
u32 val; | ||
int i; | ||
|
||
val = mt76_rr(dev, base + MT_EFUSE_CTRL); | ||
val &= ~(MT_EFUSE_CTRL_AIN | MT_EFUSE_CTRL_MODE); | ||
val |= FIELD_PREP(MT_EFUSE_CTRL_AIN, addr & ~0xf); | ||
val |= MT_EFUSE_CTRL_KICK; | ||
mt76_wr(dev, base + MT_EFUSE_CTRL, val); | ||
|
||
if (!mt76_poll(dev, base + MT_EFUSE_CTRL, MT_EFUSE_CTRL_KICK, 0, 1000)) | ||
return -ETIMEDOUT; | ||
|
||
udelay(2); | ||
|
||
val = mt76_rr(dev, base + MT_EFUSE_CTRL); | ||
if ((val & MT_EFUSE_CTRL_AOUT) == MT_EFUSE_CTRL_AOUT || | ||
WARN_ON_ONCE(!(val & MT_EFUSE_CTRL_VALID))) { | ||
memset(data, 0x0, 16); | ||
return 0; | ||
} | ||
|
||
for (i = 0; i < 4; i++) { | ||
val = mt76_rr(dev, base + MT_EFUSE_RDATA(i)); | ||
put_unaligned_le32(val, data + 4 * i); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static int mt7615_efuse_init(struct mt7615_dev *dev) | ||
{ | ||
u32 base = mt7615_reg_map(dev, MT_EFUSE_BASE); | ||
int len = MT7615_EEPROM_SIZE; | ||
int ret, i; | ||
void *buf; | ||
|
||
if (mt76_rr(dev, base + MT_EFUSE_BASE_CTRL) & MT_EFUSE_BASE_CTRL_EMPTY) | ||
return -EINVAL; | ||
|
||
dev->mt76.otp.data = devm_kzalloc(dev->mt76.dev, len, GFP_KERNEL); | ||
dev->mt76.otp.size = len; | ||
if (!dev->mt76.otp.data) | ||
return -ENOMEM; | ||
|
||
buf = dev->mt76.otp.data; | ||
for (i = 0; i + 16 <= len; i += 16) { | ||
ret = mt7615_efuse_read(dev, base, i, buf + i); | ||
if (ret) | ||
return ret; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static int mt7615_eeprom_load(struct mt7615_dev *dev) | ||
{ | ||
int ret; | ||
|
||
ret = mt76_eeprom_init(&dev->mt76, MT7615_EEPROM_SIZE); | ||
if (ret < 0) | ||
return ret; | ||
|
||
return mt7615_efuse_init(dev); | ||
} | ||
|
||
int mt7615_eeprom_init(struct mt7615_dev *dev) | ||
{ | ||
int ret; | ||
|
||
ret = mt7615_eeprom_load(dev); | ||
if (ret < 0) | ||
return ret; | ||
|
||
memcpy(dev->mt76.eeprom.data, dev->mt76.otp.data, MT7615_EEPROM_SIZE); | ||
|
||
dev->mt76.cap.has_2ghz = true; | ||
dev->mt76.cap.has_5ghz = true; | ||
|
||
memcpy(dev->mt76.macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR, | ||
ETH_ALEN); | ||
|
||
mt76_eeprom_override(&dev->mt76); | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* SPDX-License-Identifier: ISC */ | ||
/* Copyright (C) 2019 MediaTek Inc. */ | ||
|
||
#ifndef __MT7615_EEPROM_H | ||
#define __MT7615_EEPROM_H | ||
|
||
#include "mt7615.h" | ||
|
||
enum mt7615_eeprom_field { | ||
MT_EE_CHIP_ID = 0x000, | ||
MT_EE_VERSION = 0x002, | ||
MT_EE_MAC_ADDR = 0x004, | ||
MT_EE_NIC_CONF_0 = 0x034, | ||
|
||
__MT_EE_MAX = 0x3bf | ||
}; | ||
|
||
#endif |
Oops, something went wrong.