Message ID | 20190130140756.91317-3-nbd@nbd.name (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | Kalle Valo |
Headers | show |
Series | [1/3] mt76: add driver callback for when a sta is associated | expand |
> > This driver is for a newer generation of 2x2 MediaTek 802.11n chipsets. > MT7603E is a PCIe chip. > MT7628 and MT7688 are MIPS SoC devices with built-in WLAN. > MT7688 is limited to 1x1 > > This driver fully supports AP, station, mesh, ad-hoc and monitor mode. > > Signed-off-by: Felix Fietkau <nbd@nbd.name> > --- Tested-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com> > drivers/net/wireless/mediatek/mt76/Kconfig | 1 + > drivers/net/wireless/mediatek/mt76/Makefile | 1 + > .../net/wireless/mediatek/mt76/mt7603/Kconfig | 9 + > .../wireless/mediatek/mt76/mt7603/Makefile | 6 + > .../wireless/mediatek/mt76/mt7603/beacon.c | 201 ++ > .../net/wireless/mediatek/mt76/mt7603/core.c | 88 + > .../wireless/mediatek/mt76/mt7603/debugfs.c | 71 + > .../net/wireless/mediatek/mt76/mt7603/dma.c | 253 +++ > .../wireless/mediatek/mt76/mt7603/eeprom.c | 183 ++ > .../wireless/mediatek/mt76/mt7603/eeprom.h | 101 + > .../net/wireless/mediatek/mt76/mt7603/init.c | 608 ++++++ > .../net/wireless/mediatek/mt76/mt7603/mac.c | 1764 +++++++++++++++++ > .../net/wireless/mediatek/mt76/mt7603/mac.h | 257 +++ > .../net/wireless/mediatek/mt76/mt7603/main.c | 730 +++++++ > .../net/wireless/mediatek/mt76/mt7603/mcu.c | 536 +++++ > .../net/wireless/mediatek/mt76/mt7603/mcu.h | 118 ++ > .../wireless/mediatek/mt76/mt7603/mt7603.h | 271 +++ > .../net/wireless/mediatek/mt76/mt7603/pci.c | 92 + > .../net/wireless/mediatek/mt76/mt7603/regs.h | 789 ++++++++ > .../net/wireless/mediatek/mt76/mt7603/soc.c | 97 + > 20 files changed, 6176 insertions(+) > create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/Kconfig > create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/Makefile > create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/beacon.c > create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/core.c > create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c > create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/dma.c > create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c > create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h > create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/init.c > create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/mac.c > create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/mac.h > create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/main.c > create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/mcu.c > create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/mcu.h > create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h > create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/pci.c > create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/regs.h > create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/soc.c > > 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 1a45cb30f39f..40615327ab68 100644 > --- a/drivers/net/wireless/mediatek/mt76/Makefile > +++ b/drivers/net/wireless/mediatek/mt76/Makefile > @@ -21,3 +21,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/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..0f6f679bbe73 > --- /dev/null > +++ b/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c > @@ -0,0 +1,201 @@ > +/* SPDX-License-Identifier: ISC */ > +/* > + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> > + * > + * Permission to use, copy, modify, and/or distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#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..30ffc68b0208 > --- /dev/null > +++ b/drivers/net/wireless/mediatek/mt76/mt7603/core.c > @@ -0,0 +1,88 @@ > +/* SPDX-License-Identifier: ISC */ > +/* > + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> > + * > + * Permission to use, copy, modify, and/or distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#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..ca7b109c78a6 > --- /dev/null > +++ b/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c > @@ -0,0 +1,71 @@ > +/* SPDX-License-Identifier: ISC */ > +/* > + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> > + * > + * Permission to use, copy, modify, and/or distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#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..75ec077d8d74 > --- /dev/null > +++ b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c > @@ -0,0 +1,253 @@ > +/* SPDX-License-Identifier: ISC */ > +/* > + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> > + * > + * Permission to use, copy, modify, and/or distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#include "mt7603.h" > +#include "mac.h" > +#include "../dma.h" > + > +int > +mt7603_tx_queue_mcu(struct mt7603_dev *dev, enum mt76_txq_id qid, > + struct sk_buff *skb) > +{ > + struct mt76_queue *q = &dev->mt76.q_tx[qid]; > + struct mt76_queue_buf buf; > + dma_addr_t addr; > + > + addr = dma_map_single(dev->mt76.dev, skb->data, skb->len, > + DMA_TO_DEVICE); > + if (dma_mapping_error(dev->mt76.dev, addr)) > + return -ENOMEM; > + > + buf.addr = addr; > + buf.len = skb->len; > + spin_lock_bh(&q->lock); > + mt76_queue_add_buf(dev, q, &buf, 1, 0, skb, NULL); > + mt76_queue_kick(dev, q); > + spin_unlock_bh(&q->lock); > + > + return 0; > +} > + > +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) > + mt7603_mcu_rx_event(dev, 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: > + mt7603_mcu_rx_event(dev, 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 | > + MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE); > + > + mt76_wr(dev, MT_WPDMA_RST_IDX, ~0); > + mt7603_pse_client_reset(dev); > + > + for (i = 0; i < ARRAY_SIZE(wmm_queue_map); i++) { > + ret = mt7603_init_tx_queue(dev, &dev->mt76.q_tx[i], > + wmm_queue_map[i], > + MT_TX_RING_SIZE); > + if (ret) > + return ret; > + } > + > + ret = mt7603_init_tx_queue(dev, &dev->mt76.q_tx[MT_TXQ_PSD], > + MT_TX_HW_QUEUE_MGMT, MT_TX_RING_SIZE); > + if (ret) > + return ret; > + > + ret = mt7603_init_tx_queue(dev, &dev->mt76.q_tx[MT_TXQ_MCU], > + MT_TX_HW_QUEUE_MCU, MT_MCU_RING_SIZE); > + if (ret) > + return ret; > + > + ret = mt7603_init_tx_queue(dev, &dev->mt76.q_tx[MT_TXQ_BEACON], > + MT_TX_HW_QUEUE_BCN, MT_MCU_RING_SIZE); > + if (ret) > + return ret; > + > + ret = mt7603_init_tx_queue(dev, &dev->mt76.q_tx[MT_TXQ_CAB], > + MT_TX_HW_QUEUE_BMC, MT_MCU_RING_SIZE); > + if (ret) > + return ret; > + > + ret = mt7603_init_rx_queue(dev, &dev->mt76.q_rx[MT_RXQ_MCU], 1, > + MT_MCU_RING_SIZE, MT_RX_BUF_SIZE); > + if (ret) > + return ret; > + > + ret = mt7603_init_rx_queue(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], 0, > + MT7603_RX_RING_SIZE, MT_RX_BUF_SIZE); > + if (ret) > + return ret; > + > + mt76_wr(dev, MT_DELAY_INT_CFG, 0); > + return mt76_init_queues(dev); > +} > + > +void mt7603_dma_cleanup(struct mt7603_dev *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_TX_WRITEBACK_DONE); > + > + tasklet_kill(&dev->tx_tasklet); > + mt76_dma_cleanup(&dev->mt76); > +} > diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c > new file mode 100644 > index 000000000000..6d7df67de494 > --- /dev/null > +++ b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c > @@ -0,0 +1,183 @@ > +/* SPDX-License-Identifier: ISC */ > +/* > + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> > + * > + * Permission to use, copy, modify, and/or distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#include "mt7603.h" > +#include "eeprom.h" > + > +static int > +mt7603_efuse_read(struct mt7603_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, 0xff, 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 > +mt7603_efuse_init(struct mt7603_dev *dev) > +{ > + u32 base = mt7603_reg_map(dev, MT_EFUSE_BASE); > + int len = MT7603_EEPROM_SIZE; > + void *buf; > + int ret, i; > + > + if (mt76_rr(dev, base + MT_EFUSE_BASE_CTRL) & MT_EFUSE_BASE_CTRL_EMPTY) > + return 0; > + > + 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 = mt7603_efuse_read(dev, base, i, buf + i); > + if (ret) > + return ret; > + } > + > + return 0; > +} > + > +static bool > +mt7603_has_cal_free_data(struct mt7603_dev *dev, u8 *efuse) > +{ > + if (!efuse[MT_EE_TEMP_SENSOR_CAL]) > + return false; > + > + if (get_unaligned_le16(efuse + MT_EE_TX_POWER_0_START_2G) == 0) > + return false; > + > + if (get_unaligned_le16(efuse + MT_EE_TX_POWER_1_START_2G) == 0) > + return false; > + > + if (!efuse[MT_EE_CP_FT_VERSION]) > + return false; > + > + if (!efuse[MT_EE_XTAL_FREQ_OFFSET]) > + return false; > + > + if (!efuse[MT_EE_XTAL_WF_RFCAL]) > + return false; > + > + return true; > +} > + > +static void > +mt7603_apply_cal_free_data(struct mt7603_dev *dev, u8 *efuse) > +{ > + static const u8 cal_free_bytes[] = { > + MT_EE_TEMP_SENSOR_CAL, > + MT_EE_CP_FT_VERSION, > + MT_EE_XTAL_FREQ_OFFSET, > + MT_EE_XTAL_WF_RFCAL, > + /* Skip for MT7628 */ > + MT_EE_TX_POWER_0_START_2G, > + MT_EE_TX_POWER_0_START_2G + 1, > + MT_EE_TX_POWER_1_START_2G, > + MT_EE_TX_POWER_1_START_2G + 1, > + }; > + u8 *eeprom = dev->mt76.eeprom.data; > + int n = ARRAY_SIZE(cal_free_bytes); > + int i; > + > + if (!mt7603_has_cal_free_data(dev, efuse)) > + return; > + > + if (is_mt7628(dev)) > + n -= 4; > + > + for (i = 0; i < n; i++) { > + int offset = cal_free_bytes[i]; > + > + eeprom[offset] = efuse[offset]; > + } > +} > + > +static int > +mt7603_eeprom_load(struct mt7603_dev *dev) > +{ > + int ret; > + > + ret = mt76_eeprom_init(&dev->mt76, MT7603_EEPROM_SIZE); > + if (ret < 0) > + return ret; > + > + return mt7603_efuse_init(dev); > +} > + > +static int mt7603_check_eeprom(struct mt76_dev *dev) > +{ > + u16 val = get_unaligned_le16(dev->eeprom.data); > + > + switch (val) { > + case 0x7628: > + case 0x7603: > + return 0; > + default: > + return -EINVAL; > + } > +} > + > +int mt7603_eeprom_init(struct mt7603_dev *dev) > +{ > + int ret; > + > + ret = mt7603_eeprom_load(dev); > + if (ret < 0) > + return ret; > + > + if (dev->mt76.otp.data) { > + if (mt7603_check_eeprom(&dev->mt76) == 0) > + mt7603_apply_cal_free_data(dev, dev->mt76.otp.data); > + else > + memcpy(dev->mt76.eeprom.data, dev->mt76.otp.data, > + MT7603_EEPROM_SIZE); > + } > + > + dev->mt76.cap.has_2ghz = true; > + memcpy(dev->mt76.macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR, > + ETH_ALEN); > + > + mt76_eeprom_override(&dev->mt76); > + > + return 0; > +} > diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h > new file mode 100644 > index 000000000000..18e649493bfd > --- /dev/null > +++ b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h > @@ -0,0 +1,101 @@ > +/* SPDX-License-Identifier: ISC */ > +/* > + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> > + * > + * Permission to use, copy, modify, and/or distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#ifndef __MT7603_EEPROM_H > +#define __MT7603_EEPROM_H > + > +#include "mt7603.h" > + > +enum mt7603_eeprom_field { > + MT_EE_CHIP_ID = 0x000, > + MT_EE_VERSION = 0x002, > + MT_EE_MAC_ADDR = 0x004, > + MT_EE_NIC_CONF_0 = 0x034, > + MT_EE_NIC_CONF_1 = 0x036, > + MT_EE_NIC_CONF_2 = 0x042, > + > + MT_EE_XTAL_TRIM_1 = 0x03a, > + > + MT_EE_RSSI_OFFSET_2G = 0x046, > + MT_EE_WIFI_RF_SETTING = 0x048, > + MT_EE_RSSI_OFFSET_5G = 0x04a, > + > + MT_EE_TX_POWER_DELTA_BW40 = 0x050, > + MT_EE_TX_POWER_DELTA_BW80 = 0x052, > + > + MT_EE_TX_POWER_EXT_PA_5G = 0x054, > + > + MT_EE_TEMP_SENSOR_CAL = 0x055, > + > + MT_EE_TX_POWER_0_START_2G = 0x056, > + MT_EE_TX_POWER_1_START_2G = 0x05c, > + > + /* used as byte arrays */ > +#define MT_TX_POWER_GROUP_SIZE_5G 5 > +#define MT_TX_POWER_GROUPS_5G 6 > + MT_EE_TX_POWER_0_START_5G = 0x062, > + > + MT_EE_TX_POWER_0_GRP3_TX_POWER_DELTA = 0x074, > + MT_EE_TX_POWER_0_GRP4_TSSI_SLOPE = 0x076, > + > + MT_EE_TX_POWER_1_START_5G = 0x080, > + > + MT_EE_TX_POWER_CCK = 0x0a0, > + MT_EE_TX_POWER_OFDM_2G_6M = 0x0a2, > + MT_EE_TX_POWER_OFDM_2G_24M = 0x0a4, > + MT_EE_TX_POWER_OFDM_2G_54M = 0x0a6, > + MT_EE_TX_POWER_HT_BPSK_QPSK = 0x0a8, > + MT_EE_TX_POWER_HT_16_64_QAM = 0x0aa, > + MT_EE_TX_POWER_HT_64_QAM = 0x0ac, > + > + MT_EE_ELAN_RX_MODE_GAIN = 0x0c0, > + MT_EE_ELAN_RX_MODE_NF = 0x0c1, > + MT_EE_ELAN_RX_MODE_P1DB = 0x0c2, > + > + MT_EE_ELAN_BYPASS_MODE_GAIN = 0x0c3, > + MT_EE_ELAN_BYPASS_MODE_NF = 0x0c4, > + MT_EE_ELAN_BYPASS_MODE_P1DB = 0x0c5, > + > + MT_EE_STEP_NUM_NEG_6_7 = 0x0c6, > + MT_EE_STEP_NUM_NEG_4_5 = 0x0c8, > + MT_EE_STEP_NUM_NEG_2_3 = 0x0ca, > + MT_EE_STEP_NUM_NEG_0_1 = 0x0cc, > + > + MT_EE_REF_STEP_24G = 0x0ce, > + > + MT_EE_STEP_NUM_PLUS_1_2 = 0x0d0, > + MT_EE_STEP_NUM_PLUS_3_4 = 0x0d2, > + MT_EE_STEP_NUM_PLUS_5_6 = 0x0d4, > + MT_EE_STEP_NUM_PLUS_7 = 0x0d6, > + > + MT_EE_CP_FT_VERSION = 0x0f0, > + > + MT_EE_XTAL_FREQ_OFFSET = 0x0f4, > + MT_EE_XTAL_TRIM_2_COMP = 0x0f5, > + MT_EE_XTAL_TRIM_3_COMP = 0x0f6, > + MT_EE_XTAL_WF_RFCAL = 0x0f7, > + > + __MT_EE_MAX > +}; > + > +enum mt7603_eeprom_source { > + MT_EE_SRC_PROM, > + MT_EE_SRC_EFUSE, > + MT_EE_SRC_FLASH, > +}; > + > +#endif > diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/init.c b/drivers/net/wireless/mediatek/mt76/mt7603/init.c > new file mode 100644 > index 000000000000..41d92fb9c280 > --- /dev/null > +++ b/drivers/net/wireless/mediatek/mt76/mt7603/init.c > @@ -0,0 +1,608 @@ > +/* SPDX-License-Identifier: ISC */ > +/* > + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> > + * > + * Permission to use, copy, modify, and/or distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#include <linux/etherdevice.h> > +#include "mt7603.h" > +#include "mac.h" > +#include "eeprom.h" > + > +struct mt7603_dev *mt7603_alloc_device(struct device *pdev) > +{ > + static const struct mt76_driver_ops drv_ops = { > + .txwi_size = MT_TXD_SIZE, > + .tx_prepare_skb = mt7603_tx_prepare_skb, > + .tx_complete_skb = mt7603_tx_complete_skb, > + .rx_skb = mt7603_queue_rx_skb, > + .rx_poll_complete = mt7603_rx_poll_complete, > + .sta_ps = mt7603_sta_ps, > + .sta_add = mt7603_sta_add, > + .sta_assoc = mt7603_sta_assoc, > + .sta_remove = mt7603_sta_remove, > + .update_survey = mt7603_update_channel, > + }; > + struct mt7603_dev *dev; > + struct mt76_dev *mdev; > + > + mdev = mt76_alloc_device(sizeof(*dev), &mt7603_ops); > + if (!mdev) > + return NULL; > + > + dev = container_of(mdev, struct mt7603_dev, mt76); > + mdev->dev = pdev; > + mdev->drv = &drv_ops; > + > + return dev; > +} > + > +static void > +mt7603_set_tmac_template(struct mt7603_dev *dev) > +{ > + u32 desc[5] = { > + [1] = FIELD_PREP(MT_TXD3_REM_TX_COUNT, 0xf), > + [3] = MT_TXD5_SW_POWER_MGMT > + }; > + u32 addr; > + int i; > + > + addr = mt7603_reg_map(dev, MT_CLIENT_BASE_PHYS_ADDR); > + addr += MT_CLIENT_TMAC_INFO_TEMPLATE; > + for (i = 0; i < ARRAY_SIZE(desc); i++) > + mt76_wr(dev, addr + 4 * i, desc[i]); > +} > + > +static void > +mt7603_dma_sched_init(struct mt7603_dev *dev) > +{ > + int page_size = 128; > + int page_count; > + int max_len = 1792; > + int max_amsdu_pages = 4096 / page_size; > + int max_mcu_len = 4096; > + int max_beacon_len = 512 * 4 + max_len; > + int max_mcast_pages = 4 * max_len / page_size; > + int reserved_count = 0; > + int beacon_pages; > + int mcu_pages; > + int i; > + > + page_count = mt76_get_field(dev, MT_PSE_FC_P0, > + MT_PSE_FC_P0_MAX_QUOTA); > + beacon_pages = 4 * (max_beacon_len / page_size); > + mcu_pages = max_mcu_len / page_size; > + > + mt76_wr(dev, MT_PSE_FRP, > + FIELD_PREP(MT_PSE_FRP_P0, 7) | > + FIELD_PREP(MT_PSE_FRP_P1, 6) | > + FIELD_PREP(MT_PSE_FRP_P2_RQ2, 4)); > + > + mt76_wr(dev, MT_HIGH_PRIORITY_1, 0x55555553); > + mt76_wr(dev, MT_HIGH_PRIORITY_2, 0x78555555); > + > + mt76_wr(dev, MT_QUEUE_PRIORITY_1, 0x2b1a096e); > + mt76_wr(dev, MT_QUEUE_PRIORITY_2, 0x785f4d3c); > + > + mt76_wr(dev, MT_PRIORITY_MASK, 0xffffffff); > + > + mt76_wr(dev, MT_SCH_1, page_count | (2 << 28)); > + mt76_wr(dev, MT_SCH_2, max_amsdu_pages); > + > + for (i = 0; i <= 4; i++) > + mt76_wr(dev, MT_PAGE_COUNT(i), max_amsdu_pages); > + reserved_count += 5 * max_amsdu_pages; > + > + mt76_wr(dev, MT_PAGE_COUNT(5), mcu_pages); > + reserved_count += mcu_pages; > + > + mt76_wr(dev, MT_PAGE_COUNT(7), beacon_pages); > + reserved_count += beacon_pages; > + > + mt76_wr(dev, MT_PAGE_COUNT(8), max_mcast_pages); > + reserved_count += max_mcast_pages; > + > + if (is_mt7603(dev)) > + reserved_count = 0; > + > + mt76_wr(dev, MT_RSV_MAX_THRESH, page_count - reserved_count); > + > + if (is_mt7603(dev) && mt76xx_rev(dev) >= MT7603_REV_E2) { > + mt76_wr(dev, MT_GROUP_THRESH(0), > + page_count - beacon_pages - mcu_pages); > + mt76_wr(dev, MT_GROUP_THRESH(1), beacon_pages); > + mt76_wr(dev, MT_BMAP_0, 0x0080ff5f); > + mt76_wr(dev, MT_GROUP_THRESH(2), mcu_pages); > + mt76_wr(dev, MT_BMAP_1, 0x00000020); > + } else { > + mt76_wr(dev, MT_GROUP_THRESH(0), page_count); > + mt76_wr(dev, MT_BMAP_0, 0xffff); > + } > + > + mt76_wr(dev, MT_SCH_4, 0); > + > + for (i = 0; i <= 15; i++) > + mt76_wr(dev, MT_TXTIME_THRESH(i), 0xfffff); > + > + mt76_set(dev, MT_SCH_4, BIT(6)); > +} > + > +static void > +mt7603_phy_init(struct mt7603_dev *dev) > +{ > + int rx_chains = dev->mt76.antenna_mask; > + int tx_chains = __sw_hweight8(rx_chains) - 1; > + > + mt76_rmw(dev, MT_WF_RMAC_RMCR, > + (MT_WF_RMAC_RMCR_SMPS_MODE | > + MT_WF_RMAC_RMCR_RX_STREAMS), > + (FIELD_PREP(MT_WF_RMAC_RMCR_SMPS_MODE, 3) | > + FIELD_PREP(MT_WF_RMAC_RMCR_RX_STREAMS, rx_chains))); > + > + mt76_rmw_field(dev, MT_TMAC_TCR, MT_TMAC_TCR_TX_STREAMS, > + tx_chains); > + > + dev->agc0 = mt76_rr(dev, MT_AGC(0)); > + dev->agc3 = mt76_rr(dev, MT_AGC(3)); > +} > + > +static void > +mt7603_mac_init(struct mt7603_dev *dev) > +{ > + u8 bc_addr[ETH_ALEN]; > + u32 addr; > + int i; > + > + mt76_wr(dev, MT_AGG_BA_SIZE_LIMIT_0, > + (MT_AGG_SIZE_LIMIT(0) << 0 * MT_AGG_BA_SIZE_LIMIT_SHIFT) | > + (MT_AGG_SIZE_LIMIT(1) << 1 * MT_AGG_BA_SIZE_LIMIT_SHIFT) | > + (MT_AGG_SIZE_LIMIT(2) << 2 * MT_AGG_BA_SIZE_LIMIT_SHIFT) | > + (MT_AGG_SIZE_LIMIT(3) << 3 * MT_AGG_BA_SIZE_LIMIT_SHIFT)); > + > + mt76_wr(dev, MT_AGG_BA_SIZE_LIMIT_1, > + (MT_AGG_SIZE_LIMIT(4) << 0 * MT_AGG_BA_SIZE_LIMIT_SHIFT) | > + (MT_AGG_SIZE_LIMIT(5) << 1 * MT_AGG_BA_SIZE_LIMIT_SHIFT) | > + (MT_AGG_SIZE_LIMIT(6) << 2 * MT_AGG_BA_SIZE_LIMIT_SHIFT) | > + (MT_AGG_SIZE_LIMIT(7) << 3 * MT_AGG_BA_SIZE_LIMIT_SHIFT)); > + > + mt76_wr(dev, MT_AGG_LIMIT, > + FIELD_PREP(MT_AGG_LIMIT_AC(0), 24) | > + FIELD_PREP(MT_AGG_LIMIT_AC(1), 24) | > + FIELD_PREP(MT_AGG_LIMIT_AC(2), 24) | > + FIELD_PREP(MT_AGG_LIMIT_AC(3), 24)); > + > + mt76_wr(dev, MT_AGG_LIMIT_1, > + FIELD_PREP(MT_AGG_LIMIT_AC(0), 24) | > + FIELD_PREP(MT_AGG_LIMIT_AC(1), 24) | > + FIELD_PREP(MT_AGG_LIMIT_AC(2), 24) | > + FIELD_PREP(MT_AGG_LIMIT_AC(3), 24)); > + > + mt76_wr(dev, MT_AGG_CONTROL, > + FIELD_PREP(MT_AGG_CONTROL_BAR_RATE, 0x4b) | > + FIELD_PREP(MT_AGG_CONTROL_CFEND_RATE, 0x69) | > + MT_AGG_CONTROL_NO_BA_AR_RULE); > + > + mt76_wr(dev, MT_AGG_RETRY_CONTROL, > + FIELD_PREP(MT_AGG_RETRY_CONTROL_BAR_LIMIT, 1) | > + FIELD_PREP(MT_AGG_RETRY_CONTROL_RTS_LIMIT, 15)); > + > + mt76_rmw(dev, MT_DMA_DCR0, ~0xfffc, 4096); > + > + mt76_rmw(dev, MT_DMA_VCFR0, BIT(0), BIT(13)); > + mt76_rmw(dev, MT_DMA_TMCFR0, BIT(0) | BIT(1), BIT(13)); > + > + mt76_clear(dev, MT_WF_RMAC_TMR_PA, BIT(31)); > + > + mt76_set(dev, MT_WF_RMACDR, MT_WF_RMACDR_MAXLEN_20BIT); > + mt76_rmw(dev, MT_WF_RMAC_MAXMINLEN, 0xffffff, 0x19000); > + > + mt76_wr(dev, MT_WF_RFCR1, 0); > + > + mt76_set(dev, MT_TMAC_TCR, MT_TMAC_TCR_RX_RIFS_MODE); > + > + mt7603_set_tmac_template(dev); > + > + /* Enable RX group to HIF */ > + addr = mt7603_reg_map(dev, MT_CLIENT_BASE_PHYS_ADDR); > + mt76_set(dev, addr + MT_CLIENT_RXINF, MT_CLIENT_RXINF_RXSH_GROUPS); > + > + /* Enable RX group to MCU */ > + mt76_set(dev, MT_DMA_DCR1, GENMASK(13, 11)); > + > + mt76_rmw_field(dev, MT_AGG_PCR_RTS, MT_AGG_PCR_RTS_PKT_THR, 3); > + mt76_set(dev, MT_TMAC_PCR, MT_TMAC_PCR_SPE_EN); > + > + /* include preamble detection in CCA trigger signal */ > + mt76_rmw_field(dev, MT_TXREQ, MT_TXREQ_CCA_SRC_SEL, 2); > + > + mt76_wr(dev, MT_RXREQ, 4); > + > + /* Configure all rx packets to HIF */ > + mt76_wr(dev, MT_DMA_RCFR0, 0xc0000000); > + > + /* Configure MCU txs selection with aggregation */ > + mt76_wr(dev, MT_DMA_TCFR0, > + FIELD_PREP(MT_DMA_TCFR_TXS_AGGR_TIMEOUT, 1) | /* 32 us */ > + MT_DMA_TCFR_TXS_AGGR_COUNT); > + > + /* Configure HIF txs selection with aggregation */ > + mt76_wr(dev, MT_DMA_TCFR1, > + FIELD_PREP(MT_DMA_TCFR_TXS_AGGR_TIMEOUT, 1) | /* 32 us */ > + MT_DMA_TCFR_TXS_AGGR_COUNT | /* Maximum count */ > + MT_DMA_TCFR_TXS_BIT_MAP); > + > + mt76_wr(dev, MT_MCU_PCIE_REMAP_1, MT_PSE_WTBL_2_PHYS_ADDR); > + > + for (i = 0; i < MT7603_WTBL_SIZE; i++) > + mt7603_wtbl_clear(dev, i); > + > + eth_broadcast_addr(bc_addr); > + mt7603_wtbl_init(dev, MT7603_WTBL_RESERVED, -1, bc_addr); > + dev->global_sta.wcid.idx = MT7603_WTBL_RESERVED; > + rcu_assign_pointer(dev->mt76.wcid[MT7603_WTBL_RESERVED], > + &dev->global_sta.wcid); > + > + mt76_rmw_field(dev, MT_LPON_BTEIR, MT_LPON_BTEIR_MBSS_MODE, 2); > + mt76_rmw_field(dev, MT_WF_RMACDR, MT_WF_RMACDR_MBSSID_MASK, 2); > + > + mt76_wr(dev, MT_AGG_ARUCR, FIELD_PREP(MT_AGG_ARxCR_LIMIT(0), 7)); > + mt76_wr(dev, MT_AGG_ARDCR, > + FIELD_PREP(MT_AGG_ARxCR_LIMIT(0), 0) | > + FIELD_PREP(MT_AGG_ARxCR_LIMIT(1), > + max_t(int, 0, MT7603_RATE_RETRY - 2)) | > + FIELD_PREP(MT_AGG_ARxCR_LIMIT(2), MT7603_RATE_RETRY - 1) | > + FIELD_PREP(MT_AGG_ARxCR_LIMIT(3), MT7603_RATE_RETRY - 1) | > + FIELD_PREP(MT_AGG_ARxCR_LIMIT(4), MT7603_RATE_RETRY - 1) | > + FIELD_PREP(MT_AGG_ARxCR_LIMIT(5), MT7603_RATE_RETRY - 1) | > + FIELD_PREP(MT_AGG_ARxCR_LIMIT(6), MT7603_RATE_RETRY - 1) | > + FIELD_PREP(MT_AGG_ARxCR_LIMIT(7), MT7603_RATE_RETRY - 1)); > + > + mt76_wr(dev, MT_AGG_ARCR, > + (MT_AGG_ARCR_INIT_RATE1 | > + FIELD_PREP(MT_AGG_ARCR_RTS_RATE_THR, 2) | > + MT_AGG_ARCR_RATE_DOWN_RATIO_EN | > + FIELD_PREP(MT_AGG_ARCR_RATE_DOWN_RATIO, 1) | > + FIELD_PREP(MT_AGG_ARCR_RATE_UP_EXTRA_TH, 4))); > + > + mt76_set(dev, MT_WTBL_RMVTCR, MT_WTBL_RMVTCR_RX_MV_MODE); > + > + mt76_clear(dev, MT_SEC_SCR, MT_SEC_SCR_MASK_ORDER); > + mt76_clear(dev, MT_SEC_SCR, BIT(18)); > + > + /* Set secondary beacon time offsets */ > + for (i = 0; i <= 4; i++) > + mt76_rmw_field(dev, MT_LPON_SBTOR(i), MT_LPON_SBTOR_TIME_OFFSET, > + (i + 1) * (20 + 4096)); > +} > + > +static int > +mt7603_init_hardware(struct mt7603_dev *dev) > +{ > + int i, ret; > + > + mt76_wr(dev, MT_INT_SOURCE_CSR, ~0); > + > + ret = mt7603_eeprom_init(dev); > + if (ret < 0) > + return ret; > + > + ret = mt7603_dma_init(dev); > + if (ret) > + return ret; > + > + mt76_wr(dev, MT_WPDMA_GLO_CFG, 0x52000850); > + mt7603_mac_dma_start(dev); > + dev->rxfilter = mt76_rr(dev, MT_WF_RFCR); > + set_bit(MT76_STATE_INITIALIZED, &dev->mt76.state); > + > + for (i = 0; i < MT7603_WTBL_SIZE; i++) { > + mt76_wr(dev, MT_PSE_RTA, MT_PSE_RTA_BUSY | MT_PSE_RTA_WRITE | > + FIELD_PREP(MT_PSE_RTA_TAG_ID, i)); > + mt76_poll(dev, MT_PSE_RTA, MT_PSE_RTA_BUSY, 0, 5000); > + } > + > + ret = mt7603_mcu_init(dev); > + if (ret) > + return ret; > + > + mt7603_dma_sched_init(dev); > + mt7603_mcu_set_eeprom(dev); > + mt7603_phy_init(dev); > + mt7603_mac_init(dev); > + > + return 0; > +} > + > +#define CCK_RATE(_idx, _rate) { \ > + .bitrate = _rate, \ > + .flags = IEEE80211_RATE_SHORT_PREAMBLE, \ > + .hw_value = (MT_PHY_TYPE_CCK << 8) | (_idx), \ > + .hw_value_short = (MT_PHY_TYPE_CCK << 8) | (4 + _idx), \ > +} > + > +#define OFDM_RATE(_idx, _rate) { \ > + .bitrate = _rate, \ > + .hw_value = (MT_PHY_TYPE_OFDM << 8) | (_idx), \ > + .hw_value_short = (MT_PHY_TYPE_OFDM << 8) | (_idx), \ > +} > + > +static struct ieee80211_rate mt7603_rates[] = { > + CCK_RATE(0, 10), > + CCK_RATE(1, 20), > + CCK_RATE(2, 55), > + CCK_RATE(3, 110), > + OFDM_RATE(11, 60), > + OFDM_RATE(15, 90), > + OFDM_RATE(10, 120), > + OFDM_RATE(14, 180), > + OFDM_RATE(9, 240), > + OFDM_RATE(13, 360), > + OFDM_RATE(8, 480), > + OFDM_RATE(12, 540), > +}; > + > +static const struct ieee80211_iface_limit if_limits[] = { > + { > + .max = 1, > + .types = BIT(NL80211_IFTYPE_ADHOC) > + }, { > + .max = MT7603_MAX_INTERFACES, > + .types = BIT(NL80211_IFTYPE_STATION) | > +#ifdef CONFIG_MAC80211_MESH > + BIT(NL80211_IFTYPE_MESH_POINT) | > +#endif > + BIT(NL80211_IFTYPE_AP) > + }, > +}; > + > +static const struct ieee80211_iface_combination if_comb[] = { > + { > + .limits = if_limits, > + .n_limits = ARRAY_SIZE(if_limits), > + .max_interfaces = 4, > + .num_different_channels = 1, > + .beacon_int_infra_match = true, > + } > +}; > + > +static void mt7603_led_set_config(struct mt76_dev *mt76, u8 delay_on, > + u8 delay_off) > +{ > + struct mt7603_dev *dev = container_of(mt76, struct mt7603_dev, > + mt76); > + u32 val, addr; > + > + val = MT_LED_STATUS_DURATION(0xffff) | > + MT_LED_STATUS_OFF(delay_off) | > + MT_LED_STATUS_ON(delay_on); > + > + addr = mt7603_reg_map(dev, MT_LED_STATUS_0(mt76->led_pin)); > + mt76_wr(dev, addr, val); > + addr = mt7603_reg_map(dev, MT_LED_STATUS_1(mt76->led_pin)); > + mt76_wr(dev, addr, val); > + > + val = MT_LED_CTRL_REPLAY(mt76->led_pin) | > + MT_LED_CTRL_KICK(mt76->led_pin); > + if (mt76->led_al) > + val |= MT_LED_CTRL_POLARITY(mt76->led_pin); > + addr = mt7603_reg_map(dev, MT_LED_CTRL); > + mt76_wr(dev, addr, val); > +} > + > +static int mt7603_led_set_blink(struct led_classdev *led_cdev, > + unsigned long *delay_on, > + unsigned long *delay_off) > +{ > + struct mt76_dev *mt76 = container_of(led_cdev, struct mt76_dev, > + led_cdev); > + u8 delta_on, delta_off; > + > + delta_off = max_t(u8, *delay_off / 10, 1); > + delta_on = max_t(u8, *delay_on / 10, 1); > + > + mt7603_led_set_config(mt76, delta_on, delta_off); > + return 0; > +} > + > +static void mt7603_led_set_brightness(struct led_classdev *led_cdev, > + enum led_brightness brightness) > +{ > + struct mt76_dev *mt76 = container_of(led_cdev, struct mt76_dev, > + led_cdev); > + > + if (!brightness) > + mt7603_led_set_config(mt76, 0, 0xff); > + else > + mt7603_led_set_config(mt76, 0xff, 0); > +} > + > +static u32 __mt7603_reg_addr(struct mt7603_dev *dev, u32 addr) > +{ > + if (addr < 0x100000) > + return addr; > + > + return mt7603_reg_map(dev, addr); > +} > + > +static u32 mt7603_rr(struct mt76_dev *mdev, u32 offset) > +{ > + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); > + u32 addr = __mt7603_reg_addr(dev, offset); > + > + return dev->bus_ops->rr(mdev, addr); > +} > + > +static void mt7603_wr(struct mt76_dev *mdev, u32 offset, u32 val) > +{ > + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); > + u32 addr = __mt7603_reg_addr(dev, offset); > + > + dev->bus_ops->wr(mdev, addr, val); > +} > + > +static u32 mt7603_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) > +{ > + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); > + u32 addr = __mt7603_reg_addr(dev, offset); > + > + return dev->bus_ops->rmw(mdev, addr, mask, val); > +} > + > +static void > +mt7603_regd_notifier(struct wiphy *wiphy, > + struct regulatory_request *request) > +{ > + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); > + struct mt7603_dev *dev = hw->priv; > + > + dev->ed_monitor = request->dfs_region == NL80211_DFS_ETSI; > +} > + > +static int > +mt7603_txpower_signed(int val) > +{ > + bool sign = val & BIT(6); > + > + if (!(val & BIT(7))) > + return 0; > + > + val &= GENMASK(5, 0); > + if (!sign) > + val = -val; > + > + return val; > +} > + > +static void > +mt7603_init_txpower(struct mt7603_dev *dev, > + struct ieee80211_supported_band *sband) > +{ > + struct ieee80211_channel *chan; > + u8 *eeprom = (u8 *)dev->mt76.eeprom.data; > + int target_power = eeprom[MT_EE_TX_POWER_0_START_2G + 2] & ~BIT(7); > + u8 *rate_power = &eeprom[MT_EE_TX_POWER_CCK]; > + int max_offset, cur_offset; > + int i; > + > + if (target_power & BIT(6)) > + target_power = -(target_power & GENMASK(5, 0)); > + > + max_offset = 0; > + for (i = 0; i < 14; i++) { > + cur_offset = mt7603_txpower_signed(rate_power[i]); > + max_offset = max(max_offset, cur_offset); > + } > + > + target_power += max_offset; > + > + dev->tx_power_limit = target_power; > + dev->mt76.txpower_cur = target_power; > + > + target_power = DIV_ROUND_UP(target_power, 2); > + > + /* add 3 dBm for 2SS devices (combined output) */ > + if (dev->mt76.antenna_mask & BIT(1)) > + target_power += 3; > + > + for (i = 0; i < sband->n_channels; i++) { > + chan = &sband->channels[i]; > + chan->max_power = target_power; > + } > +} > + > + > +int mt7603_register_device(struct mt7603_dev *dev) > +{ > + struct mt76_bus_ops *bus_ops; > + struct ieee80211_hw *hw = mt76_hw(dev); > + struct wiphy *wiphy = hw->wiphy; > + int ret; > + > + dev->bus_ops = dev->mt76.bus; > + bus_ops = devm_kmemdup(dev->mt76.dev, dev->bus_ops, sizeof(*bus_ops), > + GFP_KERNEL); > + if (!bus_ops) > + return -ENOMEM; > + > + bus_ops->rr = mt7603_rr; > + bus_ops->wr = mt7603_wr; > + bus_ops->rmw = mt7603_rmw; > + dev->mt76.bus = bus_ops; > + > + INIT_DELAYED_WORK(&dev->mac_work, mt7603_mac_work); > + tasklet_init(&dev->pre_tbtt_tasklet, mt7603_pre_tbtt_tasklet, > + (unsigned long)dev); > + > + /* Check for 7688, which only has 1SS */ > + dev->mt76.antenna_mask = 3; > + if (mt76_rr(dev, MT_EFUSE_BASE + 0x64) & BIT(4)) > + dev->mt76.antenna_mask = 1; > + > + dev->slottime = 9; > + > + ret = mt7603_init_hardware(dev); > + if (ret) > + return ret; > + > + hw->queues = 4; > + hw->max_rates = 3; > + hw->max_report_rates = 7; > + hw->max_rate_tries = 11; > + > + hw->sta_data_size = sizeof(struct mt7603_sta); > + hw->vif_data_size = sizeof(struct mt7603_vif); > + > + wiphy->iface_combinations = if_comb; > + wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); > + > + ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER); > + ieee80211_hw_set(hw, TX_STATUS_NO_AMPDU_LEN); > + > + /* init led callbacks */ > + if (IS_ENABLED(CONFIG_MT76_LEDS)) { > + dev->mt76.led_cdev.brightness_set = mt7603_led_set_brightness; > + dev->mt76.led_cdev.blink_set = mt7603_led_set_blink; > + } > + > + wiphy->interface_modes = > + BIT(NL80211_IFTYPE_STATION) | > + BIT(NL80211_IFTYPE_AP) | > +#ifdef CONFIG_MAC80211_MESH > + BIT(NL80211_IFTYPE_MESH_POINT) | > +#endif > + BIT(NL80211_IFTYPE_ADHOC); > + > + wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; > + > + wiphy->reg_notifier = mt7603_regd_notifier; > + > + ret = mt76_register_device(&dev->mt76, true, mt7603_rates, > + ARRAY_SIZE(mt7603_rates)); > + if (ret) > + return ret; > + > + mt7603_init_debugfs(dev); > + mt7603_init_txpower(dev, &dev->mt76.sband_2g.sband); > + > + return 0; > +} > + > +void mt7603_unregister_device(struct mt7603_dev *dev) > +{ > + tasklet_disable(&dev->pre_tbtt_tasklet); > + mt76_unregister_device(&dev->mt76); > + mt7603_mcu_exit(dev); > + mt7603_dma_cleanup(dev); > + ieee80211_free_hw(mt76_hw(dev)); > +} > diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c > new file mode 100644 > index 000000000000..d1ea0e32b2ca > --- /dev/null > +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c > @@ -0,0 +1,1764 @@ > +/* SPDX-License-Identifier: ISC */ > +/* > + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> > + * > + * Permission to use, copy, modify, and/or distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#include <linux/etherdevice.h> > +#include <linux/timekeeping.h> > +#include "mt7603.h" > +#include "mac.h" > + > +#define MT_PSE_PAGE_SIZE 128 > + > +static u32 > +mt7603_ac_queue_mask0(u32 mask) > +{ > + u32 ret = 0; > + > + ret |= GENMASK(3, 0) * !!(mask & BIT(0)); > + ret |= GENMASK(8, 5) * !!(mask & BIT(1)); > + ret |= GENMASK(13, 10) * !!(mask & BIT(2)); > + ret |= GENMASK(19, 16) * !!(mask & BIT(3)); > + return ret; > +} > + > +static void > +mt76_stop_tx_ac(struct mt7603_dev *dev, u32 mask) > +{ > + mt76_set(dev, MT_WF_ARB_TX_STOP_0, mt7603_ac_queue_mask0(mask)); > +} > + > +static void > +mt76_start_tx_ac(struct mt7603_dev *dev, u32 mask) > +{ > + mt76_set(dev, MT_WF_ARB_TX_START_0, mt7603_ac_queue_mask0(mask)); > +} > + > +void mt7603_mac_set_timing(struct mt7603_dev *dev) > +{ > + u32 cck = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 231) | > + FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48); > + u32 ofdm = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 60) | > + FIELD_PREP(MT_TIMEOUT_VAL_CCA, 24); > + int offset = 3 * dev->coverage_class; > + u32 reg_offset = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, offset) | > + FIELD_PREP(MT_TIMEOUT_VAL_CCA, offset); > + int sifs; > + u32 val; > + > + if (dev->mt76.chandef.chan->band == NL80211_BAND_5GHZ) > + sifs = 16; > + else > + sifs = 10; > + > + mt76_set(dev, MT_ARB_SCR, > + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); > + udelay(1); > + > + mt76_wr(dev, MT_TIMEOUT_CCK, cck + reg_offset); > + mt76_wr(dev, MT_TIMEOUT_OFDM, ofdm + reg_offset); > + mt76_wr(dev, MT_IFS, > + FIELD_PREP(MT_IFS_EIFS, 360) | > + FIELD_PREP(MT_IFS_RIFS, 2) | > + FIELD_PREP(MT_IFS_SIFS, sifs) | > + FIELD_PREP(MT_IFS_SLOT, dev->slottime)); > + > + if (dev->slottime < 20) > + val = MT7603_CFEND_RATE_DEFAULT; > + else > + val = MT7603_CFEND_RATE_11B; > + > + mt76_rmw_field(dev, MT_AGG_CONTROL, MT_AGG_CONTROL_CFEND_RATE, val); > + > + mt76_clear(dev, MT_ARB_SCR, > + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); > +} > + > +static void > +mt7603_wtbl_update(struct mt7603_dev *dev, int idx, u32 mask) > +{ > + mt76_rmw(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX, > + FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, idx) | mask); > + > + mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000); > +} > + > +static u32 > +mt7603_wtbl1_addr(int idx) > +{ > + return MT_WTBL1_BASE + idx * MT_WTBL1_SIZE; > +} > + > +static u32 > +mt7603_wtbl2_addr(int idx) > +{ > + /* Mapped to WTBL2 */ > + return MT_PCIE_REMAP_BASE_1 + idx * MT_WTBL2_SIZE; > +} > + > +static u32 > +mt7603_wtbl3_addr(int idx) > +{ > + u32 base = mt7603_wtbl2_addr(MT7603_WTBL_SIZE); > + > + return base + idx * MT_WTBL3_SIZE; > +} > + > +static u32 > +mt7603_wtbl4_addr(int idx) > +{ > + u32 base = mt7603_wtbl3_addr(MT7603_WTBL_SIZE); > + > + return base + idx * MT_WTBL4_SIZE; > +} > + > +void mt7603_wtbl_init(struct mt7603_dev *dev, int idx, int vif, > + const u8 *mac_addr) > +{ > + const void *_mac = mac_addr; > + u32 addr = mt7603_wtbl1_addr(idx); > + u32 w0 = 0, w1 = 0; > + int i; > + > + if (_mac) { > + w0 = FIELD_PREP(MT_WTBL1_W0_ADDR_HI, > + get_unaligned_le16(_mac + 4)); > + w1 = FIELD_PREP(MT_WTBL1_W1_ADDR_LO, > + get_unaligned_le32(_mac)); > + } > + > + if (vif < 0) > + vif = 0; > + else > + w0 |= MT_WTBL1_W0_RX_CHECK_A1; > + w0 |= FIELD_PREP(MT_WTBL1_W0_MUAR_IDX, vif); > + > + mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000); > + > + mt76_set(dev, addr + 0 * 4, w0); > + mt76_set(dev, addr + 1 * 4, w1); > + mt76_set(dev, addr + 2 * 4, MT_WTBL1_W2_ADMISSION_CONTROL); > + > + mt76_stop_tx_ac(dev, GENMASK(3, 0)); > + addr = mt7603_wtbl2_addr(idx); > + for (i = 0; i < MT_WTBL2_SIZE; i += 4) > + mt76_wr(dev, addr + i, 0); > + mt7603_wtbl_update(dev, idx, MT_WTBL_UPDATE_WTBL2); > + mt76_start_tx_ac(dev, GENMASK(3, 0)); > + > + addr = mt7603_wtbl3_addr(idx); > + for (i = 0; i < MT_WTBL3_SIZE; i += 4) > + mt76_wr(dev, addr + i, 0); > + > + addr = mt7603_wtbl4_addr(idx); > + for (i = 0; i < MT_WTBL4_SIZE; i += 4) > + mt76_wr(dev, addr + i, 0); > +} > + > +static void > +mt7603_wtbl_set_skip_tx(struct mt7603_dev *dev, int idx, bool enabled) > +{ > + u32 addr = mt7603_wtbl1_addr(idx); > + u32 val = mt76_rr(dev, addr + 3 * 4); > + > + val &= ~MT_WTBL1_W3_SKIP_TX; > + val |= enabled * MT_WTBL1_W3_SKIP_TX; > + > + mt76_wr(dev, addr + 3 * 4, val); > +} > + > +void mt7603_filter_tx(struct mt7603_dev *dev, int idx, bool abort) > +{ > + int i, port, queue; > + > + if (abort) { > + port = 3; /* PSE */ > + queue = 8; /* free queue */ > + } else { > + port = 0; /* HIF */ > + queue = 1; /* MCU queue */ > + } > + > + mt7603_wtbl_set_skip_tx(dev, idx, true); > + > + mt76_wr(dev, MT_TX_ABORT, MT_TX_ABORT_EN | > + FIELD_PREP(MT_TX_ABORT_WCID, idx)); > + > + for (i = 0; i < 4; i++) { > + mt76_wr(dev, MT_DMA_FQCR0, MT_DMA_FQCR0_BUSY | > + FIELD_PREP(MT_DMA_FQCR0_TARGET_WCID, idx) | > + FIELD_PREP(MT_DMA_FQCR0_TARGET_QID, i) | > + FIELD_PREP(MT_DMA_FQCR0_DEST_PORT_ID, port) | > + FIELD_PREP(MT_DMA_FQCR0_DEST_QUEUE_ID, queue)); > + > + WARN_ON_ONCE(!mt76_poll(dev, MT_DMA_FQCR0, MT_DMA_FQCR0_BUSY, > + 0, 5000)); > + } > + > + mt76_wr(dev, MT_TX_ABORT, 0); > + > + mt7603_wtbl_set_skip_tx(dev, idx, false); > +} > + > +void mt7603_wtbl_set_smps(struct mt7603_dev *dev, struct mt7603_sta *sta, > + bool enabled) > +{ > + u32 addr = mt7603_wtbl1_addr(sta->wcid.idx); > + > + if (sta->smps == enabled) > + return; > + > + mt76_rmw_field(dev, addr + 2 * 4, MT_WTBL1_W2_SMPS, enabled); > + sta->smps = enabled; > +} > + > +void mt7603_wtbl_set_ps(struct mt7603_dev *dev, struct mt7603_sta *sta, > + bool enabled) > +{ > + int idx = sta->wcid.idx; > + u32 addr; > + > + spin_lock_bh(&dev->ps_lock); > + > + if (sta->ps == enabled) > + goto out; > + > + mt76_wr(dev, MT_PSE_RTA, > + FIELD_PREP(MT_PSE_RTA_TAG_ID, idx) | > + FIELD_PREP(MT_PSE_RTA_PORT_ID, 0) | > + FIELD_PREP(MT_PSE_RTA_QUEUE_ID, 1) | > + FIELD_PREP(MT_PSE_RTA_REDIRECT_EN, enabled) | > + MT_PSE_RTA_WRITE | MT_PSE_RTA_BUSY); > + > + mt76_poll(dev, MT_PSE_RTA, MT_PSE_RTA_BUSY, 0, 5000); > + > + if (enabled) > + mt7603_filter_tx(dev, idx, false); > + > + addr = mt7603_wtbl1_addr(idx); > + mt76_set(dev, MT_WTBL1_OR, MT_WTBL1_OR_PSM_WRITE); > + mt76_rmw(dev, addr + 3 * 4, MT_WTBL1_W3_POWER_SAVE, > + enabled * MT_WTBL1_W3_POWER_SAVE); > + mt76_clear(dev, MT_WTBL1_OR, MT_WTBL1_OR_PSM_WRITE); > + sta->ps = enabled; > + > +out: > + spin_unlock_bh(&dev->ps_lock); > +} > + > +void mt7603_wtbl_clear(struct mt7603_dev *dev, int idx) > +{ > + int wtbl2_frame_size = MT_PSE_PAGE_SIZE / MT_WTBL2_SIZE; > + int wtbl2_frame = idx / wtbl2_frame_size; > + int wtbl2_entry = idx % wtbl2_frame_size; > + > + int wtbl3_base_frame = MT_WTBL3_OFFSET / MT_PSE_PAGE_SIZE; > + int wtbl3_frame_size = MT_PSE_PAGE_SIZE / MT_WTBL3_SIZE; > + int wtbl3_frame = wtbl3_base_frame + idx / wtbl3_frame_size; > + int wtbl3_entry = (idx % wtbl3_frame_size) * 2; > + > + int wtbl4_base_frame = MT_WTBL4_OFFSET / MT_PSE_PAGE_SIZE; > + int wtbl4_frame_size = MT_PSE_PAGE_SIZE / MT_WTBL4_SIZE; > + int wtbl4_frame = wtbl4_base_frame + idx / wtbl4_frame_size; > + int wtbl4_entry = idx % wtbl4_frame_size; > + > + u32 addr = MT_WTBL1_BASE + idx * MT_WTBL1_SIZE; > + int i; > + > + mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000); > + > + mt76_wr(dev, addr + 0 * 4, > + MT_WTBL1_W0_RX_CHECK_A1 | > + MT_WTBL1_W0_RX_CHECK_A2 | > + MT_WTBL1_W0_RX_VALID); > + mt76_wr(dev, addr + 1 * 4, 0); > + mt76_wr(dev, addr + 2 * 4, 0); > + > + mt76_set(dev, MT_WTBL1_OR, MT_WTBL1_OR_PSM_WRITE); > + > + mt76_wr(dev, addr + 3 * 4, > + FIELD_PREP(MT_WTBL1_W3_WTBL2_FRAME_ID, wtbl2_frame) | > + FIELD_PREP(MT_WTBL1_W3_WTBL2_ENTRY_ID, wtbl2_entry) | > + FIELD_PREP(MT_WTBL1_W3_WTBL4_FRAME_ID, wtbl4_frame) | > + MT_WTBL1_W3_I_PSM | MT_WTBL1_W3_KEEP_I_PSM); > + mt76_wr(dev, addr + 4 * 4, > + FIELD_PREP(MT_WTBL1_W4_WTBL3_FRAME_ID, wtbl3_frame) | > + FIELD_PREP(MT_WTBL1_W4_WTBL3_ENTRY_ID, wtbl3_entry) | > + FIELD_PREP(MT_WTBL1_W4_WTBL4_ENTRY_ID, wtbl4_entry)); > + > + mt76_clear(dev, MT_WTBL1_OR, MT_WTBL1_OR_PSM_WRITE); > + > + addr = mt7603_wtbl2_addr(idx); > + > + /* Clear BA information */ > + mt76_wr(dev, addr + (15 * 4), 0); > + > + mt76_stop_tx_ac(dev, GENMASK(3, 0)); > + for (i = 2; i <= 4; i++) > + mt76_wr(dev, addr + (i * 4), 0); > + mt7603_wtbl_update(dev, idx, MT_WTBL_UPDATE_WTBL2); > + mt76_start_tx_ac(dev, GENMASK(3, 0)); > + > + mt7603_wtbl_update(dev, idx, MT_WTBL_UPDATE_RX_COUNT_CLEAR); > + mt7603_wtbl_update(dev, idx, MT_WTBL_UPDATE_TX_COUNT_CLEAR); > + mt7603_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); > +} > + > +void mt7603_wtbl_update_cap(struct mt7603_dev *dev, struct ieee80211_sta *sta) > +{ > + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; > + int idx = msta->wcid.idx; > + u32 addr; > + u32 val; > + > + addr = mt7603_wtbl1_addr(idx); > + > + val = mt76_rr(dev, addr + 2 * 4); > + val &= MT_WTBL1_W2_KEY_TYPE | MT_WTBL1_W2_ADMISSION_CONTROL; > + val |= FIELD_PREP(MT_WTBL1_W2_AMPDU_FACTOR, sta->ht_cap.ampdu_factor) | > + FIELD_PREP(MT_WTBL1_W2_MPDU_DENSITY, sta->ht_cap.ampdu_density) | > + MT_WTBL1_W2_TXS_BAF_REPORT; > + > + if (sta->ht_cap.cap) > + val |= MT_WTBL1_W2_HT; > + if (sta->vht_cap.cap) > + val |= MT_WTBL1_W2_VHT; > + > + mt76_wr(dev, addr + 2 * 4, val); > + > + addr = mt7603_wtbl2_addr(idx); > + val = mt76_rr(dev, addr + 9 * 4); > + val &= ~(MT_WTBL2_W9_SHORT_GI_20 | MT_WTBL2_W9_SHORT_GI_40 | > + MT_WTBL2_W9_SHORT_GI_80); > + if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) > + val |= MT_WTBL2_W9_SHORT_GI_20; > + if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) > + val |= MT_WTBL2_W9_SHORT_GI_40; > + mt76_wr(dev, addr + 9 * 4, val); > +} > + > +void mt7603_mac_rx_ba_reset(struct mt7603_dev *dev, void *addr, u8 tid) > +{ > + mt76_wr(dev, MT_BA_CONTROL_0, get_unaligned_le32(addr)); > + mt76_wr(dev, MT_BA_CONTROL_1, > + (get_unaligned_le16(addr + 4) | > + FIELD_PREP(MT_BA_CONTROL_1_TID, tid) | > + MT_BA_CONTROL_1_RESET)); > +} > + > +void mt7603_mac_tx_ba_reset(struct mt7603_dev *dev, int wcid, int tid, int ssn, > + int ba_size) > +{ > + u32 addr = mt7603_wtbl2_addr(wcid); > + u32 tid_mask = FIELD_PREP(MT_WTBL2_W15_BA_EN_TIDS, BIT(tid)) | > + (MT_WTBL2_W15_BA_WIN_SIZE << > + (tid * MT_WTBL2_W15_BA_WIN_SIZE_SHIFT)); > + u32 tid_val; > + int i; > + > + if (ba_size < 0) { > + /* disable */ > + mt76_clear(dev, addr + (15 * 4), tid_mask); > + return; > + } > + mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000); > + > + mt7603_mac_stop(dev); > + switch (tid) { > + case 0: > + mt76_rmw_field(dev, addr + (2 * 4), MT_WTBL2_W2_TID0_SN, ssn); > + break; > + case 1: > + mt76_rmw_field(dev, addr + (2 * 4), MT_WTBL2_W2_TID1_SN, ssn); > + break; > + case 2: > + mt76_rmw_field(dev, addr + (2 * 4), MT_WTBL2_W2_TID2_SN_LO, > + ssn); > + mt76_rmw_field(dev, addr + (3 * 4), MT_WTBL2_W3_TID2_SN_HI, > + ssn >> 8); > + break; > + case 3: > + mt76_rmw_field(dev, addr + (3 * 4), MT_WTBL2_W3_TID3_SN, ssn); > + break; > + case 4: > + mt76_rmw_field(dev, addr + (3 * 4), MT_WTBL2_W3_TID4_SN, ssn); > + break; > + case 5: > + mt76_rmw_field(dev, addr + (3 * 4), MT_WTBL2_W3_TID5_SN_LO, > + ssn); > + mt76_rmw_field(dev, addr + (4 * 4), MT_WTBL2_W4_TID5_SN_HI, > + ssn >> 4); > + break; > + case 6: > + mt76_rmw_field(dev, addr + (4 * 4), MT_WTBL2_W4_TID6_SN, ssn); > + break; > + case 7: > + mt76_rmw_field(dev, addr + (4 * 4), MT_WTBL2_W4_TID7_SN, ssn); > + break; > + } > + mt7603_wtbl_update(dev, wcid, MT_WTBL_UPDATE_WTBL2); > + mt7603_mac_start(dev); > + > + for (i = 7; i > 0; i--) { > + if (ba_size < MT_AGG_SIZE_LIMIT(i)) > + break; > + } > + > + tid_val = FIELD_PREP(MT_WTBL2_W15_BA_EN_TIDS, BIT(tid)) | > + i << (tid * MT_WTBL2_W15_BA_WIN_SIZE_SHIFT); > + > + mt76_rmw(dev, addr + (15 * 4), tid_mask, tid_val); > +} > + > +static int > +mt7603_get_rate(struct mt7603_dev *dev, struct ieee80211_supported_band *sband, > + int idx, bool cck) > +{ > + int offset = 0; > + int len = sband->n_bitrates; > + int i; > + > + if (cck) { > + if (sband == &dev->mt76.sband_5g.sband) > + return 0; > + > + idx &= ~BIT(2); /* short preamble */ > + } else if (sband == &dev->mt76.sband_2g.sband) { > + offset = 4; > + } > + > + for (i = offset; i < len; i++) { > + if ((sband->bitrates[i].hw_value & GENMASK(7, 0)) == idx) > + return i; > + } > + > + return 0; > +} > + > +static struct mt76_wcid * > +mt7603_rx_get_wcid(struct mt7603_dev *dev, u8 idx, bool unicast) > +{ > + struct mt7603_sta *sta; > + struct mt76_wcid *wcid; > + > + if (idx >= ARRAY_SIZE(dev->mt76.wcid)) > + return NULL; > + > + wcid = rcu_dereference(dev->mt76.wcid[idx]); > + if (unicast || !wcid) > + return wcid; > + > + if (!wcid->sta) > + return NULL; > + > + sta = container_of(wcid, struct mt7603_sta, wcid); > + if (!sta->vif) > + return NULL; > + > + return &sta->vif->sta.wcid; > +} > + > +static void > +mt7603_insert_ccmp_hdr(struct sk_buff *skb, u8 key_id) > +{ > + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; > + int hdr_len = ieee80211_get_hdrlen_from_skb(skb); > + u8 *pn = status->iv; > + u8 *hdr; > + > + __skb_push(skb, 8); > + memmove(skb->data, skb->data + 8, hdr_len); > + hdr = skb->data + hdr_len; > + > + hdr[0] = pn[5]; > + hdr[1] = pn[4]; > + hdr[2] = 0; > + hdr[3] = 0x20 | (key_id << 6); > + hdr[4] = pn[3]; > + hdr[5] = pn[2]; > + hdr[6] = pn[1]; > + hdr[7] = pn[0]; > + > + status->flag &= ~RX_FLAG_IV_STRIPPED; > +} > + > +int > +mt7603_mac_fill_rx(struct mt7603_dev *dev, struct sk_buff *skb) > +{ > + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; > + struct ieee80211_supported_band *sband; > + struct ieee80211_hdr *hdr; > + __le32 *rxd = (__le32 *)skb->data; > + u32 rxd0 = le32_to_cpu(rxd[0]); > + u32 rxd1 = le32_to_cpu(rxd[1]); > + u32 rxd2 = le32_to_cpu(rxd[2]); > + bool unicast = rxd1 & MT_RXD1_NORMAL_U2M; > + bool insert_ccmp_hdr = false; > + bool remove_pad; > + int idx; > + int i; > + > + memset(status, 0, sizeof(*status)); > + > + i = FIELD_GET(MT_RXD1_NORMAL_CH_FREQ, rxd1); > + sband = (i & 1) ? &dev->mt76.sband_5g.sband : &dev->mt76.sband_2g.sband; > + i >>= 1; > + > + idx = FIELD_GET(MT_RXD2_NORMAL_WLAN_IDX, rxd2); > + status->wcid = mt7603_rx_get_wcid(dev, idx, unicast); > + > + status->band = sband->band; > + if (i < sband->n_channels) > + status->freq = sband->channels[i].center_freq; > + > + if (rxd2 & MT_RXD2_NORMAL_FCS_ERR) > + status->flag |= RX_FLAG_FAILED_FCS_CRC; > + > + if (rxd2 & MT_RXD2_NORMAL_TKIP_MIC_ERR) > + status->flag |= RX_FLAG_MMIC_ERROR; > + > + if (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2) != 0 && > + !(rxd2 & (MT_RXD2_NORMAL_CLM | MT_RXD2_NORMAL_CM))) { > + status->flag |= RX_FLAG_DECRYPTED; > + status->flag |= RX_FLAG_IV_STRIPPED; > + status->flag |= RX_FLAG_MMIC_STRIPPED | RX_FLAG_MIC_STRIPPED; > + } > + > + remove_pad = rxd1 & MT_RXD1_NORMAL_HDR_OFFSET; > + > + if (rxd2 & MT_RXD2_NORMAL_MAX_LEN_ERROR) > + return -EINVAL; > + > + if (!sband->channels) > + return -EINVAL; > + > + rxd += 4; > + if (rxd0 & MT_RXD0_NORMAL_GROUP_4) { > + rxd += 4; > + if ((u8 *)rxd - skb->data >= skb->len) > + return -EINVAL; > + } > + if (rxd0 & MT_RXD0_NORMAL_GROUP_1) { > + u8 *data = (u8 *)rxd; > + > + if (status->flag & RX_FLAG_DECRYPTED) { > + status->iv[0] = data[5]; > + status->iv[1] = data[4]; > + status->iv[2] = data[3]; > + status->iv[3] = data[2]; > + status->iv[4] = data[1]; > + status->iv[5] = data[0]; > + > + insert_ccmp_hdr = FIELD_GET(MT_RXD2_NORMAL_FRAG, rxd2); > + } > + > + rxd += 4; > + if ((u8 *)rxd - skb->data >= skb->len) > + return -EINVAL; > + } > + if (rxd0 & MT_RXD0_NORMAL_GROUP_2) { > + rxd += 2; > + if ((u8 *)rxd - skb->data >= skb->len) > + return -EINVAL; > + } > + if (rxd0 & MT_RXD0_NORMAL_GROUP_3) { > + u32 rxdg0 = le32_to_cpu(rxd[0]); > + u32 rxdg3 = le32_to_cpu(rxd[3]); > + bool cck = false; > + > + i = FIELD_GET(MT_RXV1_TX_RATE, rxdg0); > + switch (FIELD_GET(MT_RXV1_TX_MODE, rxdg0)) { > + case MT_PHY_TYPE_CCK: > + cck = true; > + /* fall through */ > + case MT_PHY_TYPE_OFDM: > + i = mt7603_get_rate(dev, sband, i, cck); > + break; > + case MT_PHY_TYPE_HT_GF: > + case MT_PHY_TYPE_HT: > + status->encoding = RX_ENC_HT; > + if (i > 15) > + return -EINVAL; > + break; > + default: > + return -EINVAL; > + } > + > + if (rxdg0 & MT_RXV1_HT_SHORT_GI) > + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; > + if (rxdg0 & MT_RXV1_HT_AD_CODE) > + status->enc_flags |= RX_ENC_FLAG_LDPC; > + > + status->enc_flags |= RX_ENC_FLAG_STBC_MASK * > + FIELD_GET(MT_RXV1_HT_STBC, rxdg0); > + > + status->rate_idx = i; > + > + status->chains = dev->mt76.antenna_mask; > + status->chain_signal[0] = FIELD_GET(MT_RXV4_IB_RSSI0, rxdg3) + > + dev->rssi_offset[0]; > + status->chain_signal[1] = FIELD_GET(MT_RXV4_IB_RSSI1, rxdg3) + > + dev->rssi_offset[1]; > + > + status->signal = status->chain_signal[0]; > + if (status->chains & BIT(1)) > + status->signal = max(status->signal, > + status->chain_signal[1]); > + > + if (FIELD_GET(MT_RXV1_FRAME_MODE, rxdg0) == 1) > + status->bw = RATE_INFO_BW_40; > + > + rxd += 6; > + if ((u8 *)rxd - skb->data >= skb->len) > + return -EINVAL; > + } else { > + return -EINVAL; > + } > + > + skb_pull(skb, (u8 *)rxd - skb->data + 2 * remove_pad); > + > + if (insert_ccmp_hdr) { > + u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1); > + > + mt7603_insert_ccmp_hdr(skb, key_id); > + } > + > + hdr = (struct ieee80211_hdr *)skb->data; > + if (!status->wcid || !ieee80211_is_data_qos(hdr->frame_control)) > + return 0; > + > + status->aggr = unicast && > + !ieee80211_is_qos_nullfunc(hdr->frame_control); > + status->tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; > + status->seqno = hdr->seq_ctrl >> 4; > + > + return 0; > +} > + > +static u16 > +mt7603_mac_tx_rate_val(struct mt7603_dev *dev, > + const struct ieee80211_tx_rate *rate, bool stbc, u8 *bw) > +{ > + u8 phy, nss, rate_idx; > + u16 rateval; > + > + *bw = 0; > + if (rate->flags & IEEE80211_TX_RC_MCS) { > + rate_idx = rate->idx; > + nss = 1 + (rate->idx >> 3); > + phy = MT_PHY_TYPE_HT; > + if (rate->flags & IEEE80211_TX_RC_GREEN_FIELD) > + phy = MT_PHY_TYPE_HT_GF; > + if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) > + *bw = 1; > + } else { > + const struct ieee80211_rate *r; > + int band = dev->mt76.chandef.chan->band; > + u16 val; > + > + nss = 1; > + r = &mt76_hw(dev)->wiphy->bands[band]->bitrates[rate->idx]; > + if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) > + val = r->hw_value_short; > + else > + val = r->hw_value; > + > + phy = val >> 8; > + rate_idx = val & 0xff; > + } > + > + rateval = (FIELD_PREP(MT_TX_RATE_IDX, rate_idx) | > + FIELD_PREP(MT_TX_RATE_MODE, phy)); > + > + if (stbc && nss == 1) > + rateval |= MT_TX_RATE_STBC; > + > + return rateval; > +} > + > +void mt7603_wtbl_set_rates(struct mt7603_dev *dev, struct mt7603_sta *sta, > + struct ieee80211_tx_rate *probe_rate, > + struct ieee80211_tx_rate *rates) > +{ > + int wcid = sta->wcid.idx; > + u32 addr = mt7603_wtbl2_addr(wcid); > + bool stbc = false; > + int n_rates = sta->n_rates; > + u8 bw, bw_prev, bw_idx = 0; > + u16 val[4]; > + u16 probe_val; > + u32 w9 = mt76_rr(dev, addr + 9 * 4); > + int i; > + > + if (!mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000)) > + return; > + > + for (i = n_rates; i < 4; i++) > + rates[i] = rates[n_rates - 1]; > + > + w9 &= MT_WTBL2_W9_SHORT_GI_20 | MT_WTBL2_W9_SHORT_GI_40 | > + MT_WTBL2_W9_SHORT_GI_80; > + > + val[0] = mt7603_mac_tx_rate_val(dev, &rates[0], stbc, &bw); > + bw_prev = bw; > + > + if (probe_rate) { > + probe_val = mt7603_mac_tx_rate_val(dev, probe_rate, stbc, &bw); > + if (bw) > + bw_idx = 1; > + else > + bw_prev = 0; > + } else { > + probe_val = val[0]; > + } > + > + w9 |= FIELD_PREP(MT_WTBL2_W9_CC_BW_SEL, bw); > + w9 |= FIELD_PREP(MT_WTBL2_W9_BW_CAP, bw); > + > + val[1] = mt7603_mac_tx_rate_val(dev, &rates[1], stbc, &bw); > + if (bw_prev) { > + bw_idx = 3; > + bw_prev = bw; > + } > + > + val[2] = mt7603_mac_tx_rate_val(dev, &rates[2], stbc, &bw); > + if (bw_prev) { > + bw_idx = 5; > + bw_prev = bw; > + } > + > + val[3] = mt7603_mac_tx_rate_val(dev, &rates[3], stbc, &bw); > + if (bw_prev) > + bw_idx = 7; > + > + w9 |= FIELD_PREP(MT_WTBL2_W9_CHANGE_BW_RATE, > + bw_idx ? bw_idx - 1 : 7); > + > + mt76_wr(dev, MT_WTBL_RIUCR0, w9); > + > + mt76_wr(dev, MT_WTBL_RIUCR1, > + FIELD_PREP(MT_WTBL_RIUCR1_RATE0, probe_val) | > + FIELD_PREP(MT_WTBL_RIUCR1_RATE1, val[0]) | > + FIELD_PREP(MT_WTBL_RIUCR1_RATE2_LO, val[0])); > + > + mt76_wr(dev, MT_WTBL_RIUCR2, > + FIELD_PREP(MT_WTBL_RIUCR2_RATE2_HI, val[0] >> 8) | > + FIELD_PREP(MT_WTBL_RIUCR2_RATE3, val[1]) | > + FIELD_PREP(MT_WTBL_RIUCR2_RATE4, val[1]) | > + FIELD_PREP(MT_WTBL_RIUCR2_RATE5_LO, val[2])); > + > + mt76_wr(dev, MT_WTBL_RIUCR3, > + FIELD_PREP(MT_WTBL_RIUCR3_RATE5_HI, val[2] >> 4) | > + FIELD_PREP(MT_WTBL_RIUCR3_RATE6, val[2]) | > + FIELD_PREP(MT_WTBL_RIUCR3_RATE7, val[3])); > + > + mt76_wr(dev, MT_WTBL_UPDATE, > + FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, wcid) | > + MT_WTBL_UPDATE_RATE_UPDATE | > + MT_WTBL_UPDATE_TX_COUNT_CLEAR); > + > + if (!sta->wcid.tx_rate_set) > + mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000); > + > + sta->rate_count = 2 * MT7603_RATE_RETRY * n_rates; > + sta->wcid.tx_rate_set = true; > +} > + > +static enum mt7603_cipher_type > +mt7603_mac_get_key_info(struct ieee80211_key_conf *key, u8 *key_data) > +{ > + memset(key_data, 0, 32); > + if (!key) > + return MT_CIPHER_NONE; > + > + if (key->keylen > 32) > + return MT_CIPHER_NONE; > + > + memcpy(key_data, key->key, key->keylen); > + > + switch (key->cipher) { > + case WLAN_CIPHER_SUITE_WEP40: > + return MT_CIPHER_WEP40; > + case WLAN_CIPHER_SUITE_WEP104: > + return MT_CIPHER_WEP104; > + case WLAN_CIPHER_SUITE_TKIP: > + /* Rx/Tx MIC keys are swapped */ > + memcpy(key_data + 16, key->key + 24, 8); > + memcpy(key_data + 24, key->key + 16, 8); > + return MT_CIPHER_TKIP; > + case WLAN_CIPHER_SUITE_CCMP: > + return MT_CIPHER_AES_CCMP; > + default: > + return MT_CIPHER_NONE; > + } > +} > + > +int mt7603_wtbl_set_key(struct mt7603_dev *dev, int wcid, > + struct ieee80211_key_conf *key) > +{ > + enum mt7603_cipher_type cipher; > + u32 addr = mt7603_wtbl3_addr(wcid); > + u8 key_data[32]; > + int key_len = sizeof(key_data); > + > + cipher = mt7603_mac_get_key_info(key, key_data); > + if (cipher == MT_CIPHER_NONE && key) > + return -EOPNOTSUPP; > + > + if (key && (cipher == MT_CIPHER_WEP40 || cipher == MT_CIPHER_WEP104)) { > + addr += key->keyidx * 16; > + key_len = 16; > + } > + > + mt76_wr_copy(dev, addr, key_data, key_len); > + > + addr = mt7603_wtbl1_addr(wcid); > + mt76_rmw_field(dev, addr + 2 * 4, MT_WTBL1_W2_KEY_TYPE, cipher); > + if (key) > + mt76_rmw_field(dev, addr, MT_WTBL1_W0_KEY_IDX, key->keyidx); > + mt76_rmw_field(dev, addr, MT_WTBL1_W0_RX_KEY_VALID, !!key); > + > + return 0; > +} > + > +static int > +mt7603_mac_write_txwi(struct mt7603_dev *dev, __le32 *txwi, > + struct sk_buff *skb, struct mt76_queue *q, > + struct mt76_wcid *wcid, struct ieee80211_sta *sta, > + int pid, struct ieee80211_key_conf *key) > +{ > + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); > + struct ieee80211_tx_rate *rate = &info->control.rates[0]; > + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; > + struct ieee80211_vif *vif = info->control.vif; > + struct mt7603_vif *mvif; > + int wlan_idx; > + int hdr_len = ieee80211_get_hdrlen_from_skb(skb); > + int tx_count = 8; > + u8 frame_type, frame_subtype; > + u16 fc = le16_to_cpu(hdr->frame_control); > + u8 vif_idx = 0; > + u32 val; > + u8 bw; > + > + if (vif) { > + mvif = (struct mt7603_vif *)vif->drv_priv; > + vif_idx = mvif->idx; > + if (vif_idx && q >= &dev->mt76.q_tx[MT_TXQ_BEACON]) > + vif_idx += 0x10; > + } > + > + if (sta) { > + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; > + > + tx_count = msta->rate_count; > + } > + > + if (wcid) > + wlan_idx = wcid->idx; > + else > + wlan_idx = MT7603_WTBL_RESERVED; > + > + frame_type = (fc & IEEE80211_FCTL_FTYPE) >> 2; > + frame_subtype = (fc & IEEE80211_FCTL_STYPE) >> 4; > + > + val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len + MT_TXD_SIZE) | > + FIELD_PREP(MT_TXD0_Q_IDX, q->hw_idx); > + txwi[0] = cpu_to_le32(val); > + > + val = MT_TXD1_LONG_FORMAT | > + FIELD_PREP(MT_TXD1_OWN_MAC, vif_idx) | > + FIELD_PREP(MT_TXD1_TID, > + skb->priority & IEEE80211_QOS_CTL_TID_MASK) | > + FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_11) | > + FIELD_PREP(MT_TXD1_HDR_INFO, hdr_len / 2) | > + FIELD_PREP(MT_TXD1_WLAN_IDX, wlan_idx) | > + FIELD_PREP(MT_TXD1_PROTECTED, !!key); > + txwi[1] = cpu_to_le32(val); > + > + if (info->flags & IEEE80211_TX_CTL_NO_ACK) > + txwi[1] |= cpu_to_le32(MT_TXD1_NO_ACK); > + > + val = FIELD_PREP(MT_TXD2_FRAME_TYPE, frame_type) | > + FIELD_PREP(MT_TXD2_SUB_TYPE, frame_subtype) | > + FIELD_PREP(MT_TXD2_MULTICAST, > + is_multicast_ether_addr(hdr->addr1)); > + txwi[2] = cpu_to_le32(val); > + > + if (!(info->flags & IEEE80211_TX_CTL_AMPDU)) > + txwi[2] |= cpu_to_le32(MT_TXD2_BA_DISABLE); > + > + txwi[4] = 0; > + > + val = MT_TXD5_TX_STATUS_HOST | MT_TXD5_SW_POWER_MGMT | > + FIELD_PREP(MT_TXD5_PID, pid); > + txwi[5] = cpu_to_le32(val); > + > + txwi[6] = 0; > + > + if (rate->idx >= 0 && rate->count && > + !(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)) { > + bool stbc = info->flags & IEEE80211_TX_CTL_STBC; > + u16 rateval = mt7603_mac_tx_rate_val(dev, rate, stbc, &bw); > + > + txwi[2] |= cpu_to_le32(MT_TXD2_FIX_RATE); > + > + val = MT_TXD6_FIXED_BW | > + FIELD_PREP(MT_TXD6_BW, bw) | > + FIELD_PREP(MT_TXD6_TX_RATE, rateval); > + txwi[6] |= cpu_to_le32(val); > + > + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) > + txwi[6] |= cpu_to_le32(MT_TXD6_SGI); > + > + if (!(rate->flags & IEEE80211_TX_RC_MCS)) > + txwi[2] |= cpu_to_le32(MT_TXD2_BA_DISABLE); > + > + tx_count = rate->count; > + } > + > + /* use maximum tx count for beacons and buffered multicast */ > + if (q >= &dev->mt76.q_tx[MT_TXQ_BEACON]) > + tx_count = 0x1f; > + > + val = FIELD_PREP(MT_TXD3_REM_TX_COUNT, tx_count) | > + FIELD_PREP(MT_TXD3_SEQ, le16_to_cpu(hdr->seq_ctrl)); > + txwi[3] = cpu_to_le32(val); > + > + if (key) { > + u64 pn = atomic64_inc_return(&key->tx_pn); > + > + txwi[3] |= cpu_to_le32(MT_TXD3_PN_VALID); > + txwi[4] = cpu_to_le32(pn & GENMASK(31, 0)); > + txwi[5] |= cpu_to_le32(FIELD_PREP(MT_TXD5_PN_HIGH, pn >> 32)); > + } > + > + txwi[7] = 0; > + > + return 0; > +} > + > +int mt7603_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, > + struct sk_buff *skb, struct mt76_queue *q, > + struct mt76_wcid *wcid, struct ieee80211_sta *sta, > + u32 *tx_info) > +{ > + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); > + struct mt7603_sta *msta = container_of(wcid, struct mt7603_sta, wcid); > + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); > + struct ieee80211_key_conf *key = info->control.hw_key; > + int pid; > + > + if (!wcid) > + wcid = &dev->global_sta.wcid; > + > + if (sta) { > + msta = (struct mt7603_sta *)sta->drv_priv; > + > + if ((info->flags & (IEEE80211_TX_CTL_NO_PS_BUFFER | > + IEEE80211_TX_CTL_CLEAR_PS_FILT)) || > + (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE)) > + mt7603_wtbl_set_ps(dev, msta, false); > + } > + > + pid = mt76_tx_status_skb_add(mdev, wcid, skb); > + > + if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) { > + spin_lock_bh(&dev->mt76.lock); > + msta->rate_probe = true; > + mt7603_wtbl_set_rates(dev, msta, &info->control.rates[0], > + msta->rates); > + spin_unlock_bh(&dev->mt76.lock); > + } > + > + mt7603_mac_write_txwi(dev, txwi_ptr, skb, q, wcid, sta, pid, key); > + > + return 0; > +} > + > +static bool > +mt7603_fill_txs(struct mt7603_dev *dev, struct mt7603_sta *sta, > + struct ieee80211_tx_info *info, __le32 *txs_data) > +{ > + struct ieee80211_supported_band *sband; > + int final_idx = 0; > + u32 final_rate; > + u32 final_rate_flags; > + bool final_mpdu; > + bool ack_timeout; > + bool fixed_rate; > + bool probe; > + bool ampdu; > + bool cck = false; > + int count; > + u32 txs; > + u8 pid; > + int idx; > + int i; > + > + fixed_rate = info->status.rates[0].count; > + probe = !!(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE); > + > + txs = le32_to_cpu(txs_data[4]); > + final_mpdu = txs & MT_TXS4_ACKED_MPDU; > + ampdu = !fixed_rate && (txs & MT_TXS4_AMPDU); > + pid = FIELD_GET(MT_TXS4_PID, txs); > + count = FIELD_GET(MT_TXS4_TX_COUNT, txs); > + > + txs = le32_to_cpu(txs_data[0]); > + final_rate = FIELD_GET(MT_TXS0_TX_RATE, txs); > + ack_timeout = txs & MT_TXS0_ACK_TIMEOUT; > + > + if (!ampdu && (txs & MT_TXS0_RTS_TIMEOUT)) > + return false; > + > + if (txs & MT_TXS0_QUEUE_TIMEOUT) > + return false; > + > + if (!ack_timeout) > + info->flags |= IEEE80211_TX_STAT_ACK; > + > + info->status.ampdu_len = 1; > + info->status.ampdu_ack_len = !!(info->flags & > + IEEE80211_TX_STAT_ACK); > + > + if (ampdu || (info->flags & IEEE80211_TX_CTL_AMPDU)) > + info->flags |= IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_CTL_AMPDU; > + > + if (fixed_rate && !probe) { > + info->status.rates[0].count = count; > + goto out; > + } > + > + for (i = 0, idx = 0; i < ARRAY_SIZE(info->status.rates); i++) { > + int cur_count = min_t(int, count, 2 * MT7603_RATE_RETRY); > + > + if (!i && probe) { > + cur_count = 1; > + } else { > + info->status.rates[i] = sta->rates[idx]; > + idx++; > + } > + > + if (i && info->status.rates[i].idx < 0) { > + info->status.rates[i - 1].count += count; > + break; > + } > + > + if (!count) { > + info->status.rates[i].idx = -1; > + break; > + } > + > + info->status.rates[i].count = cur_count; > + final_idx = i; > + count -= cur_count; > + } > + > +out: > + final_rate_flags = info->status.rates[final_idx].flags; > + > + switch (FIELD_GET(MT_TX_RATE_MODE, final_rate)) { > + case MT_PHY_TYPE_CCK: > + cck = true; > + /* fall through */ > + case MT_PHY_TYPE_OFDM: > + if (dev->mt76.chandef.chan->band == NL80211_BAND_5GHZ) > + sband = &dev->mt76.sband_5g.sband; > + else > + sband = &dev->mt76.sband_2g.sband; > + final_rate &= GENMASK(5, 0); > + final_rate = mt7603_get_rate(dev, sband, final_rate, cck); > + final_rate_flags = 0; > + break; > + case MT_PHY_TYPE_HT_GF: > + case MT_PHY_TYPE_HT: > + final_rate_flags |= IEEE80211_TX_RC_MCS; > + final_rate &= GENMASK(5, 0); > + if (i > 15) > + return false; > + break; > + default: > + return false; > + } > + > + info->status.rates[final_idx].idx = final_rate; > + info->status.rates[final_idx].flags = final_rate_flags; > + > + return true; > +} > + > +static bool > +mt7603_mac_add_txs_skb(struct mt7603_dev *dev, struct mt7603_sta *sta, int pid, > + __le32 *txs_data) > +{ > + struct mt76_dev *mdev = &dev->mt76; > + struct sk_buff_head list; > + struct sk_buff *skb; > + > + if (pid < MT_PACKET_ID_FIRST) > + return false; > + > + mt76_tx_status_lock(mdev, &list); > + skb = mt76_tx_status_skb_get(mdev, &sta->wcid, pid, &list); > + if (skb) { > + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); > + > + if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) { > + spin_lock_bh(&dev->mt76.lock); > + if (sta->rate_probe) { > + mt7603_wtbl_set_rates(dev, sta, NULL, > + sta->rates); > + sta->rate_probe = false; > + } > + spin_unlock_bh(&dev->mt76.lock); > + } > + > + if (!mt7603_fill_txs(dev, sta, info, txs_data)) { > + ieee80211_tx_info_clear_status(info); > + info->status.rates[0].idx = -1; > + } > + > + mt76_tx_status_skb_done(mdev, skb, &list); > + } > + mt76_tx_status_unlock(mdev, &list); > + > + return !!skb; > +} > + > +void mt7603_mac_add_txs(struct mt7603_dev *dev, void *data) > +{ > + struct ieee80211_tx_info info = {}; > + struct ieee80211_sta *sta = NULL; > + struct mt7603_sta *msta = NULL; > + struct mt76_wcid *wcid; > + __le32 *txs_data = data; > + u32 txs; > + u8 wcidx; > + u8 pid; > + > + txs = le32_to_cpu(txs_data[4]); > + pid = FIELD_GET(MT_TXS4_PID, txs); > + txs = le32_to_cpu(txs_data[3]); > + wcidx = FIELD_GET(MT_TXS3_WCID, txs); > + > + if (pid == MT_PACKET_ID_NO_ACK) > + return; > + > + if (wcidx >= ARRAY_SIZE(dev->mt76.wcid)) > + return; > + > + rcu_read_lock(); > + > + wcid = rcu_dereference(dev->mt76.wcid[wcidx]); > + if (!wcid) > + goto out; > + > + msta = container_of(wcid, struct mt7603_sta, wcid); > + sta = wcid_to_sta(wcid); > + > + if (mt7603_mac_add_txs_skb(dev, msta, pid, txs_data)) > + goto out; > + > + if (wcidx >= MT7603_WTBL_STA || !sta) > + goto out; > + > + if (mt7603_fill_txs(dev, msta, &info, txs_data)) > + ieee80211_tx_status_noskb(mt76_hw(dev), sta, &info); > + > +out: > + rcu_read_unlock(); > +} > + > +void mt7603_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue *q, > + struct mt76_queue_entry *e, bool flush) > +{ > + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); > + struct sk_buff *skb = e->skb; > + > + if (!e->txwi) { > + dev_kfree_skb_any(skb); > + return; > + } > + > + if (q - dev->mt76.q_tx < 4) > + dev->tx_hang_check = 0; > + > + mt76_tx_complete_skb(mdev, skb); > +} > + > +static bool > +wait_for_wpdma(struct mt7603_dev *dev) > +{ > + return mt76_poll(dev, MT_WPDMA_GLO_CFG, > + MT_WPDMA_GLO_CFG_TX_DMA_BUSY | > + MT_WPDMA_GLO_CFG_RX_DMA_BUSY, > + 0, 1000); > +} > + > +static void mt7603_pse_reset(struct mt7603_dev *dev) > +{ > + /* Clear previous reset result */ > + if (!dev->reset_cause[RESET_CAUSE_RESET_FAILED]) > + mt76_clear(dev, MT_MCU_DEBUG_RESET, MT_MCU_DEBUG_RESET_PSE_S); > + > + /* Reset PSE */ > + mt76_set(dev, MT_MCU_DEBUG_RESET, MT_MCU_DEBUG_RESET_PSE); > + > + if (!mt76_poll_msec(dev, MT_MCU_DEBUG_RESET, > + MT_MCU_DEBUG_RESET_PSE_S, > + MT_MCU_DEBUG_RESET_PSE_S, 500)) { > + dev->reset_cause[RESET_CAUSE_RESET_FAILED]++; > + mt76_clear(dev, MT_MCU_DEBUG_RESET, MT_MCU_DEBUG_RESET_PSE); > + } else { > + dev->reset_cause[RESET_CAUSE_RESET_FAILED] = 0; > + mt76_clear(dev, MT_MCU_DEBUG_RESET, MT_MCU_DEBUG_RESET_QUEUES); > + } > + > + if (dev->reset_cause[RESET_CAUSE_RESET_FAILED] >= 3) > + dev->reset_cause[RESET_CAUSE_RESET_FAILED] = 0; > +} > + > +void mt7603_mac_dma_start(struct mt7603_dev *dev) > +{ > + mt7603_mac_start(dev); > + > + wait_for_wpdma(dev); > + usleep_range(50, 100); > + > + mt76_set(dev, MT_WPDMA_GLO_CFG, > + (MT_WPDMA_GLO_CFG_TX_DMA_EN | > + MT_WPDMA_GLO_CFG_RX_DMA_EN | > + FIELD_PREP(MT_WPDMA_GLO_CFG_DMA_BURST_SIZE, 3) | > + MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE)); > + > + mt7603_irq_enable(dev, MT_INT_RX_DONE_ALL | MT_INT_TX_DONE_ALL); > +} > + > +void mt7603_mac_start(struct mt7603_dev *dev) > +{ > + mt76_clear(dev, MT_ARB_SCR, > + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); > + mt76_wr(dev, MT_WF_ARB_TX_START_0, ~0); > + mt76_set(dev, MT_WF_ARB_RQCR, MT_WF_ARB_RQCR_RX_START); > +} > + > +void mt7603_mac_stop(struct mt7603_dev *dev) > +{ > + mt76_set(dev, MT_ARB_SCR, > + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); > + mt76_wr(dev, MT_WF_ARB_TX_START_0, 0); > + mt76_clear(dev, MT_WF_ARB_RQCR, MT_WF_ARB_RQCR_RX_START); > +} > + > +void mt7603_pse_client_reset(struct mt7603_dev *dev) > +{ > + u32 addr; > + > + addr = mt7603_reg_map(dev, MT_CLIENT_BASE_PHYS_ADDR + > + MT_CLIENT_RESET_TX); > + > + /* Clear previous reset state */ > + mt76_clear(dev, addr, > + MT_CLIENT_RESET_TX_R_E_1 | > + MT_CLIENT_RESET_TX_R_E_2 | > + MT_CLIENT_RESET_TX_R_E_1_S | > + MT_CLIENT_RESET_TX_R_E_2_S); > + > + /* Start PSE client TX abort */ > + mt76_set(dev, addr, MT_CLIENT_RESET_TX_R_E_1); > + mt76_poll_msec(dev, addr, MT_CLIENT_RESET_TX_R_E_1_S, > + MT_CLIENT_RESET_TX_R_E_1_S, 500); > + > + mt76_set(dev, addr, MT_CLIENT_RESET_TX_R_E_2); > + mt76_set(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_SW_RESET); > + > + /* Wait for PSE client to clear TX FIFO */ > + mt76_poll_msec(dev, addr, MT_CLIENT_RESET_TX_R_E_2_S, > + MT_CLIENT_RESET_TX_R_E_2_S, 500); > + > + /* Clear PSE client TX abort state */ > + mt76_clear(dev, addr, > + MT_CLIENT_RESET_TX_R_E_1 | > + MT_CLIENT_RESET_TX_R_E_2); > +} > + > +static void mt7603_dma_sched_reset(struct mt7603_dev *dev) > +{ > + if (!is_mt7628(dev)) > + return; > + > + mt76_set(dev, MT_SCH_4, MT_SCH_4_RESET); > + mt76_clear(dev, MT_SCH_4, MT_SCH_4_RESET); > +} > + > +static void mt7603_mac_watchdog_reset(struct mt7603_dev *dev) > +{ > + int beacon_int = dev->beacon_int; > + u32 mask = dev->mt76.mmio.irqmask; > + int i; > + > + ieee80211_stop_queues(dev->mt76.hw); > + set_bit(MT76_RESET, &dev->mt76.state); > + > + /* lock/unlock all queues to ensure that no tx is pending */ > + mt76_txq_schedule_all(&dev->mt76); > + > + tasklet_disable(&dev->tx_tasklet); > + tasklet_disable(&dev->pre_tbtt_tasklet); > + napi_disable(&dev->mt76.napi[0]); > + napi_disable(&dev->mt76.napi[1]); > + > + mutex_lock(&dev->mt76.mutex); > + > + mt7603_beacon_set_timer(dev, -1, 0); > + > + if (dev->reset_cause[RESET_CAUSE_RESET_FAILED] || > + dev->cur_reset_cause == RESET_CAUSE_RX_PSE_BUSY || > + dev->cur_reset_cause == RESET_CAUSE_BEACON_STUCK || > + dev->cur_reset_cause == RESET_CAUSE_TX_HANG) > + mt7603_pse_reset(dev); > + > + if (dev->reset_cause[RESET_CAUSE_RESET_FAILED]) > + goto skip_dma_reset; > + > + mt7603_mac_stop(dev); > + > + mt76_clear(dev, MT_WPDMA_GLO_CFG, > + MT_WPDMA_GLO_CFG_RX_DMA_EN | MT_WPDMA_GLO_CFG_TX_DMA_EN | > + MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE); > + usleep_range(1000, 2000); > + > + mt7603_irq_disable(dev, mask); > + > + mt76_set(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_FORCE_TX_EOF); > + > + mt7603_pse_client_reset(dev); > + > + for (i = 0; i < ARRAY_SIZE(dev->mt76.q_tx); i++) > + mt76_queue_tx_cleanup(dev, i, true); > + > + for (i = 0; i < ARRAY_SIZE(dev->mt76.q_rx); i++) > + mt76_queue_rx_reset(dev, i); > + > + mt7603_dma_sched_reset(dev); > + > + mt7603_mac_dma_start(dev); > + > + mt7603_irq_enable(dev, mask); > + > +skip_dma_reset: > + clear_bit(MT76_RESET, &dev->mt76.state); > + mutex_unlock(&dev->mt76.mutex); > + > + tasklet_enable(&dev->tx_tasklet); > + tasklet_schedule(&dev->tx_tasklet); > + > + tasklet_enable(&dev->pre_tbtt_tasklet); > + mt7603_beacon_set_timer(dev, -1, beacon_int); > + > + napi_enable(&dev->mt76.napi[0]); > + napi_schedule(&dev->mt76.napi[0]); > + > + napi_enable(&dev->mt76.napi[1]); > + napi_schedule(&dev->mt76.napi[1]); > + > + ieee80211_wake_queues(dev->mt76.hw); > + mt76_txq_schedule_all(&dev->mt76); > +} > + > +static u32 mt7603_dma_debug(struct mt7603_dev *dev, u8 index) > +{ > + u32 val; > + > + mt76_wr(dev, MT_WPDMA_DEBUG, > + FIELD_PREP(MT_WPDMA_DEBUG_IDX, index) | > + MT_WPDMA_DEBUG_SEL); > + > + val = mt76_rr(dev, MT_WPDMA_DEBUG); > + return FIELD_GET(MT_WPDMA_DEBUG_VALUE, val); > +} > + > +static bool mt7603_rx_fifo_busy(struct mt7603_dev *dev) > +{ > + if (is_mt7628(dev)) > + return mt7603_dma_debug(dev, 9) & BIT(9); > + > + return mt7603_dma_debug(dev, 2) & BIT(8); > +} > + > +static bool mt7603_rx_dma_busy(struct mt7603_dev *dev) > +{ > + if (!(mt76_rr(dev, MT_WPDMA_GLO_CFG) & MT_WPDMA_GLO_CFG_RX_DMA_BUSY)) > + return false; > + > + return mt7603_rx_fifo_busy(dev); > +} > + > +static bool mt7603_tx_dma_busy(struct mt7603_dev *dev) > +{ > + u32 val; > + > + if (!(mt76_rr(dev, MT_WPDMA_GLO_CFG) & MT_WPDMA_GLO_CFG_TX_DMA_BUSY)) > + return false; > + > + val = mt7603_dma_debug(dev, 9); > + return (val & BIT(8)) && (val & 0xf) != 0xf; > +} > + > +static bool mt7603_tx_hang(struct mt7603_dev *dev) > +{ > + struct mt76_queue *q; > + u32 dma_idx, prev_dma_idx; > + int i; > + > + for (i = 0; i < 4; i++) { > + q = &dev->mt76.q_tx[i]; > + > + if (!q->queued) > + continue; > + > + prev_dma_idx = dev->tx_dma_idx[i]; > + dma_idx = ioread32(&q->regs->dma_idx); > + dev->tx_dma_idx[i] = dma_idx; > + > + if (dma_idx == prev_dma_idx && > + dma_idx != ioread32(&q->regs->cpu_idx)) > + break; > + } > + > + return i < 4; > +} > + > +static bool mt7603_rx_pse_busy(struct mt7603_dev *dev) > +{ > + u32 addr, val; > + > + if (mt76_rr(dev, MT_MCU_DEBUG_RESET) & MT_MCU_DEBUG_RESET_QUEUES) > + return true; > + > + if (mt7603_rx_fifo_busy(dev)) > + return false; > + > + addr = mt7603_reg_map(dev, MT_CLIENT_BASE_PHYS_ADDR + MT_CLIENT_STATUS); > + mt76_wr(dev, addr, 3); > + val = mt76_rr(dev, addr) >> 16; > + > + if (is_mt7628(dev) && (val & 0x4001) == 0x4001) > + return true; > + > + return (val & 0x8001) == 0x8001 || (val & 0xe001) == 0xe001; > +} > + > +static bool > +mt7603_watchdog_check(struct mt7603_dev *dev, u8 *counter, > + enum mt7603_reset_cause cause, > + bool (*check)(struct mt7603_dev *dev)) > +{ > + if (dev->reset_test == cause + 1) { > + dev->reset_test = 0; > + goto trigger; > + } > + > + if (check) { > + if (!check(dev) && *counter < MT7603_WATCHDOG_TIMEOUT) { > + *counter = 0; > + return false; > + } > + > + (*counter)++; > + } > + > + if (*counter < MT7603_WATCHDOG_TIMEOUT) > + return false; > +trigger: > + dev->cur_reset_cause = cause; > + dev->reset_cause[cause]++; > + return true; > +} > + > +void mt7603_update_channel(struct mt76_dev *mdev) > +{ > + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); > + struct mt76_channel_state *state; > + ktime_t cur_time; > + u32 busy; > + > + if (!test_bit(MT76_STATE_RUNNING, &dev->mt76.state)) > + return; > + > + state = mt76_channel_state(&dev->mt76, dev->mt76.chandef.chan); > + busy = mt76_rr(dev, MT_MIB_STAT_PSCCA); > + > + spin_lock_bh(&dev->mt76.cc_lock); > + cur_time = ktime_get_boottime(); > + state->cc_busy += busy; > + state->cc_active += ktime_to_us(ktime_sub(cur_time, dev->survey_time)); > + dev->survey_time = cur_time; > + spin_unlock_bh(&dev->mt76.cc_lock); > +} > + > +void > +mt7603_edcca_set_strict(struct mt7603_dev *dev, bool val) > +{ > + u32 rxtd_6 = 0xd7c80000; > + > + if (val == dev->ed_strict_mode) > + return; > + > + dev->ed_strict_mode = val; > + > + /* Ensure that ED/CCA does not trigger if disabled */ > + if (!dev->ed_monitor) > + rxtd_6 |= FIELD_PREP(MT_RXTD_6_CCAED_TH, 0x34); > + else > + rxtd_6 |= FIELD_PREP(MT_RXTD_6_CCAED_TH, 0x7d); > + > + if (dev->ed_monitor && !dev->ed_strict_mode) > + rxtd_6 |= FIELD_PREP(MT_RXTD_6_ACI_TH, 0x0f); > + else > + rxtd_6 |= FIELD_PREP(MT_RXTD_6_ACI_TH, 0x10); > + > + mt76_wr(dev, MT_RXTD(6), rxtd_6); > + > + mt76_rmw_field(dev, MT_RXTD(13), MT_RXTD_13_ACI_TH_EN, > + dev->ed_monitor && !dev->ed_strict_mode); > +} > + > +static void > +mt7603_edcca_check(struct mt7603_dev *dev) > +{ > + u32 val = mt76_rr(dev, MT_AGC(41)); > + ktime_t cur_time; > + int rssi0, rssi1; > + u32 active; > + u32 ed_busy; > + > + if (!dev->ed_monitor) > + return; > + > + rssi0 = FIELD_GET(MT_AGC_41_RSSI_0, val); > + if (rssi0 > 128) > + rssi0 -= 256; > + > + rssi1 = FIELD_GET(MT_AGC_41_RSSI_1, val); > + if (rssi1 > 128) > + rssi1 -= 256; > + > + if (max(rssi0, rssi1) >= -40 && > + dev->ed_strong_signal < MT7603_EDCCA_BLOCK_TH) > + dev->ed_strong_signal++; > + else if (dev->ed_strong_signal > 0) > + dev->ed_strong_signal--; > + > + cur_time = ktime_get_boottime(); > + ed_busy = mt76_rr(dev, MT_MIB_STAT_ED) & MT_MIB_STAT_ED_MASK; > + > + active = ktime_to_us(ktime_sub(cur_time, dev->ed_time)); > + dev->ed_time = cur_time; > + > + if (!active) > + return; > + > + if (100 * ed_busy / active > 90) { > + if (dev->ed_trigger < 0) > + dev->ed_trigger = 0; > + dev->ed_trigger++; > + } else { > + if (dev->ed_trigger > 0) > + dev->ed_trigger = 0; > + dev->ed_trigger--; > + } > + > + if (dev->ed_trigger > MT7603_EDCCA_BLOCK_TH || > + dev->ed_strong_signal < MT7603_EDCCA_BLOCK_TH / 2) { > + mt7603_edcca_set_strict(dev, true); > + } else if (dev->ed_trigger < -MT7603_EDCCA_BLOCK_TH) { > + mt7603_edcca_set_strict(dev, false); > + } > + > + if (dev->ed_trigger > MT7603_EDCCA_BLOCK_TH) > + dev->ed_trigger = MT7603_EDCCA_BLOCK_TH; > + else if (dev->ed_trigger < -MT7603_EDCCA_BLOCK_TH) > + dev->ed_trigger = -MT7603_EDCCA_BLOCK_TH; > +} > + > +void mt7603_cca_stats_reset(struct mt7603_dev *dev) > +{ > + mt76_set(dev, MT_PHYCTRL(2), MT_PHYCTRL_2_STATUS_RESET); > + mt76_clear(dev, MT_PHYCTRL(2), MT_PHYCTRL_2_STATUS_RESET); > + mt76_set(dev, MT_PHYCTRL(2), MT_PHYCTRL_2_STATUS_EN); > +} > + > +static void > +mt7603_adjust_sensitivity(struct mt7603_dev *dev) > +{ > + u32 agc0 = dev->agc0, agc3 = dev->agc3; > + u32 adj; > + > + if (!dev->sensitivity || dev->sensitivity < -100) { > + dev->sensitivity = 0; > + } else if (dev->sensitivity <= -84) { > + adj = 7 + (dev->sensitivity + 92) / 2; > + > + agc0 = 0x56f0076f; > + agc0 |= adj << 12; > + agc0 |= adj << 16; > + agc3 = 0x81d0d5e3; > + } else if (dev->sensitivity <= -72) { > + adj = 7 + (dev->sensitivity + 80) / 2; > + > + agc0 = 0x6af0006f; > + agc0 |= adj << 8; > + agc0 |= adj << 12; > + agc0 |= adj << 16; > + > + agc3 = 0x8181d5e3; > + } else { > + if (dev->sensitivity > -54) > + dev->sensitivity = -54; > + > + adj = 7 + (dev->sensitivity + 80) / 2; > + > + agc0 = 0x7ff0000f; > + agc0 |= adj << 4; > + agc0 |= adj << 8; > + agc0 |= adj << 12; > + agc0 |= adj << 16; > + > + agc3 = 0x818181e3; > + } > + > + mt76_wr(dev, MT_AGC(0), agc0); > + mt76_wr(dev, MT_AGC1(0), agc0); > + > + mt76_wr(dev, MT_AGC(3), agc3); > + mt76_wr(dev, MT_AGC1(3), agc3); > +} > + > +static void > +mt7603_false_cca_check(struct mt7603_dev *dev) > +{ > + int pd_cck, pd_ofdm, mdrdy_cck, mdrdy_ofdm; > + int false_cca; > + int min_signal; > + u32 val; > + > + val = mt76_rr(dev, MT_PHYCTRL_STAT_PD); > + pd_cck = FIELD_GET(MT_PHYCTRL_STAT_PD_CCK, val); > + pd_ofdm = FIELD_GET(MT_PHYCTRL_STAT_PD_OFDM, val); > + > + val = mt76_rr(dev, MT_PHYCTRL_STAT_MDRDY); > + mdrdy_cck = FIELD_GET(MT_PHYCTRL_STAT_MDRDY_CCK, val); > + mdrdy_ofdm = FIELD_GET(MT_PHYCTRL_STAT_MDRDY_OFDM, val); > + > + dev->false_cca_ofdm = pd_ofdm - mdrdy_ofdm; > + dev->false_cca_cck = pd_cck - mdrdy_cck; > + > + mt7603_cca_stats_reset(dev); > + > + min_signal = mt76_get_min_avg_rssi(&dev->mt76); > + if (!min_signal) { > + dev->sensitivity = 0; > + dev->last_cca_adj = jiffies; > + goto out; > + } > + > + min_signal -= 15; > + > + false_cca = dev->false_cca_ofdm + dev->false_cca_cck; > + if (false_cca > 600) { > + if (!dev->sensitivity) > + dev->sensitivity = -92; > + else > + dev->sensitivity += 2; > + dev->last_cca_adj = jiffies; > + } else if (false_cca < 100 || > + time_after(jiffies, dev->last_cca_adj + 10 * HZ)) { > + dev->last_cca_adj = jiffies; > + if (!dev->sensitivity) > + goto out; > + > + dev->sensitivity -= 2; > + } > + > + if (dev->sensitivity && dev->sensitivity > min_signal) { > + dev->sensitivity = min_signal; > + dev->last_cca_adj = jiffies; > + } > + > +out: > + mt7603_adjust_sensitivity(dev); > +} > + > +void mt7603_mac_work(struct work_struct *work) > +{ > + struct mt7603_dev *dev = container_of(work, struct mt7603_dev, > + mac_work.work); > + bool reset = false; > + > + mt76_tx_status_check(&dev->mt76, NULL, false); > + > + mutex_lock(&dev->mt76.mutex); > + > + dev->mac_work_count++; > + mt7603_update_channel(&dev->mt76); > + mt7603_edcca_check(dev); > + > + if (dev->mac_work_count == 10) > + mt7603_false_cca_check(dev); > + > + if (mt7603_watchdog_check(dev, &dev->rx_pse_check, > + RESET_CAUSE_RX_PSE_BUSY, > + mt7603_rx_pse_busy) || > + mt7603_watchdog_check(dev, &dev->beacon_check, > + RESET_CAUSE_BEACON_STUCK, > + NULL) || > + mt7603_watchdog_check(dev, &dev->tx_hang_check, > + RESET_CAUSE_TX_HANG, > + mt7603_tx_hang) || > + mt7603_watchdog_check(dev, &dev->tx_dma_check, > + RESET_CAUSE_TX_BUSY, > + mt7603_tx_dma_busy) || > + mt7603_watchdog_check(dev, &dev->rx_dma_check, > + RESET_CAUSE_RX_BUSY, > + mt7603_rx_dma_busy) || > + mt7603_watchdog_check(dev, &dev->mcu_hang, > + RESET_CAUSE_MCU_HANG, > + NULL) || > + dev->reset_cause[RESET_CAUSE_RESET_FAILED]) { > + dev->beacon_check = 0; > + dev->tx_dma_check = 0; > + dev->tx_hang_check = 0; > + dev->rx_dma_check = 0; > + dev->rx_pse_check = 0; > + dev->mcu_hang = 0; > + dev->rx_dma_idx = ~0; > + memset(dev->tx_dma_idx, 0xff, sizeof(dev->tx_dma_idx)); > + reset = true; > + dev->mac_work_count = 0; > + } > + > + if (dev->mac_work_count >= 10) > + dev->mac_work_count = 0; > + > + mutex_unlock(&dev->mt76.mutex); > + > + if (reset) > + mt7603_mac_watchdog_reset(dev); > + > + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mac_work, > + msecs_to_jiffies(MT7603_WATCHDOG_TIME)); > +} > diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.h b/drivers/net/wireless/mediatek/mt76/mt7603/mac.h > new file mode 100644 > index 000000000000..1704e6f932a3 > --- /dev/null > +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.h > @@ -0,0 +1,257 @@ > +/* SPDX-License-Identifier: ISC */ > +/* > + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> > + * > + * Permission to use, copy, modify, and/or distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#ifndef __MT7603_MAC_H > +#define __MT7603_MAC_H > + > +#define MT_RXD0_LENGTH GENMASK(15, 0) > +#define MT_RXD0_PKT_TYPE GENMASK(31, 29) > + > +#define MT_RXD0_NORMAL_ETH_TYPE_OFS GENMASK(22, 16) > +#define MT_RXD0_NORMAL_IP_SUM BIT(23) > +#define MT_RXD0_NORMAL_UDP_TCP_SUM BIT(24) > +#define MT_RXD0_NORMAL_GROUP_1 BIT(25) > +#define MT_RXD0_NORMAL_GROUP_2 BIT(26) > +#define MT_RXD0_NORMAL_GROUP_3 BIT(27) > +#define MT_RXD0_NORMAL_GROUP_4 BIT(28) > + > +enum rx_pkt_type { > + PKT_TYPE_TXS = 0, > + PKT_TYPE_TXRXV = 1, > + PKT_TYPE_NORMAL = 2, > + PKT_TYPE_RX_DUP_RFB = 3, > + PKT_TYPE_RX_TMR = 4, > + PKT_TYPE_RETRIEVE = 5, > + PKT_TYPE_RX_EVENT = 7, > +}; > + > +#define MT_RXD1_NORMAL_BSSID GENMASK(31, 26) > +#define MT_RXD1_NORMAL_PAYLOAD_FORMAT GENMASK(25, 24) > +#define MT_RXD1_NORMAL_HDR_TRANS BIT(23) > +#define MT_RXD1_NORMAL_HDR_OFFSET BIT(22) > +#define MT_RXD1_NORMAL_MAC_HDR_LEN GENMASK(21, 16) > +#define MT_RXD1_NORMAL_CH_FREQ GENMASK(15, 8) > +#define MT_RXD1_NORMAL_KEY_ID GENMASK(7, 6) > +#define MT_RXD1_NORMAL_BEACON_UC BIT(5) > +#define MT_RXD1_NORMAL_BEACON_MC BIT(4) > +#define MT_RXD1_NORMAL_BCAST BIT(3) > +#define MT_RXD1_NORMAL_MCAST BIT(2) > +#define MT_RXD1_NORMAL_U2M BIT(1) > +#define MT_RXD1_NORMAL_HTC_VLD BIT(0) > + > +#define MT_RXD2_NORMAL_NON_AMPDU BIT(31) > +#define MT_RXD2_NORMAL_NON_AMPDU_SUB BIT(30) > +#define MT_RXD2_NORMAL_NDATA BIT(29) > +#define MT_RXD2_NORMAL_NULL_FRAME BIT(28) > +#define MT_RXD2_NORMAL_FRAG BIT(27) > +#define MT_RXD2_NORMAL_UDF_VALID BIT(26) > +#define MT_RXD2_NORMAL_LLC_MIS BIT(25) > +#define MT_RXD2_NORMAL_MAX_LEN_ERROR BIT(24) > +#define MT_RXD2_NORMAL_AMSDU_ERR BIT(23) > +#define MT_RXD2_NORMAL_LEN_MISMATCH BIT(22) > +#define MT_RXD2_NORMAL_TKIP_MIC_ERR BIT(21) > +#define MT_RXD2_NORMAL_ICV_ERR BIT(20) > +#define MT_RXD2_NORMAL_CLM BIT(19) > +#define MT_RXD2_NORMAL_CM BIT(18) > +#define MT_RXD2_NORMAL_FCS_ERR BIT(17) > +#define MT_RXD2_NORMAL_SW_BIT BIT(16) > +#define MT_RXD2_NORMAL_SEC_MODE GENMASK(15, 12) > +#define MT_RXD2_NORMAL_TID GENMASK(11, 8) > +#define MT_RXD2_NORMAL_WLAN_IDX GENMASK(7, 0) > + > +#define MT_RXD3_NORMAL_PF_STS GENMASK(31, 30) > +#define MT_RXD3_NORMAL_PF_MODE BIT(29) > +#define MT_RXD3_NORMAL_CLS_BITMAP GENMASK(28, 19) > +#define MT_RXD3_NORMAL_WOL GENMASK(18, 14) > +#define MT_RXD3_NORMAL_MAGIC_PKT BIT(13) > +#define MT_RXD3_NORMAL_OFLD GENMASK(12, 11) > +#define MT_RXD3_NORMAL_CLS BIT(10) > +#define MT_RXD3_NORMAL_PATTERN_DROP BIT(9) > +#define MT_RXD3_NORMAL_TSF_COMPARE_LOSS BIT(8) > +#define MT_RXD3_NORMAL_RXV_SEQ GENMASK(7, 0) > + > +#define MT_RXV1_VHTA1_B5_B4 GENMASK(31, 30) > +#define MT_RXV1_VHTA2_B8_B1 GENMASK(29, 22) > +#define MT_RXV1_HT_NO_SOUND BIT(21) > +#define MT_RXV1_HT_SMOOTH BIT(20) > +#define MT_RXV1_HT_SHORT_GI BIT(19) > +#define MT_RXV1_HT_AGGR BIT(18) > +#define MT_RXV1_VHTA1_B22 BIT(17) > +#define MT_RXV1_FRAME_MODE GENMASK(16, 15) > +#define MT_RXV1_TX_MODE GENMASK(14, 12) > +#define MT_RXV1_HT_EXT_LTF GENMASK(11, 10) > +#define MT_RXV1_HT_AD_CODE BIT(9) > +#define MT_RXV1_HT_STBC GENMASK(8, 7) > +#define MT_RXV1_TX_RATE GENMASK(6, 0) > + > +#define MT_RXV2_VHTA1_B16_B6 GENMASK(31, 21) > +#define MT_RXV2_LENGTH GENMASK(20, 0) > + > +#define MT_RXV3_F_AGC1_CAL_GAIN GENMASK(31, 29) > +#define MT_RXV3_F_AGC1_EQ_CAL BIT(28) > +#define MT_RXV3_RCPI1 GENMASK(27, 20) > +#define MT_RXV3_F_AGC0_CAL_GAIN GENMASK(19, 17) > +#define MT_RXV3_F_AGC0_EQ_CAL BIT(16) > +#define MT_RXV3_RCPI0 GENMASK(15, 8) > +#define MT_RXV3_SEL_ANT BIT(7) > +#define MT_RXV3_ACI_DET_X BIT(6) > +#define MT_RXV3_OFDM_FREQ_TRANS_DETECT BIT(5) > +#define MT_RXV3_VHTA1_B21_B17 GENMASK(4, 0) > + > +#define MT_RXV4_F_AGC_CAL_GAIN GENMASK(31, 29) > +#define MT_RXV4_F_AGC2_EQ_CAL BIT(28) > +#define MT_RXV4_IB_RSSI1 GENMASK(27, 20) > +#define MT_RXV4_F_AGC_LPF_GAIN_X GENMASK(19, 16) > +#define MT_RXV4_WB_RSSI_X GENMASK(15, 8) > +#define MT_RXV4_IB_RSSI0 GENMASK(7, 0) > + > +#define MT_RXV5_LTF_SNR0 GENMASK(31, 26) > +#define MT_RXV5_LTF_PROC_TIME GENMASK(25, 19) > +#define MT_RXV5_FOE GENMASK(18, 7) > +#define MT_RXV5_C_AGC_SATE GENMASK(6, 4) > +#define MT_RXV5_F_AGC_LNA_GAIN_0 GENMASK(3, 2) > +#define MT_RXV5_F_AGC_LNA_GAIN_1 GENMASK(1, 0) > + > +#define MT_RXV6_C_AGC_STATE GENMASK(30, 28) > +#define MT_RXV6_NS_TS_FIELD GENMASK(27, 25) > +#define MT_RXV6_RX_VALID BIT(24) > +#define MT_RXV6_NF2 GENMASK(23, 16) > +#define MT_RXV6_NF1 GENMASK(15, 8) > +#define MT_RXV6_NF0 GENMASK(7, 0) > + > +enum mt7603_tx_header_format { > + MT_HDR_FORMAT_802_3, > + MT_HDR_FORMAT_CMD, > + MT_HDR_FORMAT_802_11, > + MT_HDR_FORMAT_802_11_EXT, > +}; > + > +#define MT_TXD_SIZE (8 * 4) > + > +#define MT_TXD0_P_IDX BIT(31) > +#define MT_TXD0_Q_IDX GENMASK(30, 27) > +#define MT_TXD0_UTXB BIT(26) > +#define MT_TXD0_UNXV BIT(25) > +#define MT_TXD0_UDP_TCP_SUM BIT(24) > +#define MT_TXD0_IP_SUM BIT(23) > +#define MT_TXD0_ETH_TYPE_OFFSET GENMASK(22, 16) > +#define MT_TXD0_TX_BYTES GENMASK(15, 0) > + > +#define MT_TXD1_OWN_MAC GENMASK(31, 26) > +#define MT_TXD1_PROTECTED BIT(23) > +#define MT_TXD1_TID GENMASK(22, 20) > +#define MT_TXD1_NO_ACK BIT(19) > +#define MT_TXD1_HDR_PAD GENMASK(18, 16) > +#define MT_TXD1_LONG_FORMAT BIT(15) > +#define MT_TXD1_HDR_FORMAT GENMASK(14, 13) > +#define MT_TXD1_HDR_INFO GENMASK(12, 8) > +#define MT_TXD1_WLAN_IDX GENMASK(7, 0) > + > +#define MT_TXD2_FIX_RATE BIT(31) > +#define MT_TXD2_TIMING_MEASURE BIT(30) > +#define MT_TXD2_BA_DISABLE BIT(29) > +#define MT_TXD2_POWER_OFFSET GENMASK(28, 24) > +#define MT_TXD2_MAX_TX_TIME GENMASK(23, 16) > +#define MT_TXD2_FRAG GENMASK(15, 14) > +#define MT_TXD2_HTC_VLD BIT(13) > +#define MT_TXD2_DURATION BIT(12) > +#define MT_TXD2_BIP BIT(11) > +#define MT_TXD2_MULTICAST BIT(10) > +#define MT_TXD2_RTS BIT(9) > +#define MT_TXD2_SOUNDING BIT(8) > +#define MT_TXD2_NDPA BIT(7) > +#define MT_TXD2_NDP BIT(6) > +#define MT_TXD2_FRAME_TYPE GENMASK(5, 4) > +#define MT_TXD2_SUB_TYPE GENMASK(3, 0) > + > +#define MT_TXD3_SN_VALID BIT(31) > +#define MT_TXD3_PN_VALID BIT(30) > +#define MT_TXD3_SEQ GENMASK(27, 16) > +#define MT_TXD3_REM_TX_COUNT GENMASK(15, 11) > +#define MT_TXD3_TX_COUNT GENMASK(10, 6) > + > +#define MT_TXD4_PN_LOW GENMASK(31, 0) > + > +#define MT_TXD5_PN_HIGH GENMASK(31, 16) > +#define MT_TXD5_SW_POWER_MGMT BIT(13) > +#define MT_TXD5_BA_SEQ_CTRL BIT(12) > +#define MT_TXD5_DA_SELECT BIT(11) > +#define MT_TXD5_TX_STATUS_HOST BIT(10) > +#define MT_TXD5_TX_STATUS_MCU BIT(9) > +#define MT_TXD5_TX_STATUS_FMT BIT(8) > +#define MT_TXD5_PID GENMASK(7, 0) > + > +#define MT_TXD6_SGI BIT(31) > +#define MT_TXD6_LDPC BIT(30) > +#define MT_TXD6_TX_RATE GENMASK(29, 18) > +#define MT_TXD6_I_TXBF BIT(17) > +#define MT_TXD6_E_TXBF BIT(16) > +#define MT_TXD6_DYN_BW BIT(15) > +#define MT_TXD6_ANT_PRI GENMASK(14, 12) > +#define MT_TXD6_SPE_EN BIT(11) > +#define MT_TXD6_FIXED_BW BIT(10) > +#define MT_TXD6_BW GENMASK(9, 8) > +#define MT_TXD6_ANT_ID GENMASK(7, 2) > +#define MT_TXD6_FIXED_RATE BIT(0) > + > +#define MT_TX_RATE_STBC BIT(11) > +#define MT_TX_RATE_NSS GENMASK(10, 9) > +#define MT_TX_RATE_MODE GENMASK(8, 6) > +#define MT_TX_RATE_IDX GENMASK(5, 0) > + > +#define MT_TXS0_ANTENNA GENMASK(31, 26) > +#define MT_TXS0_TID GENMASK(25, 22) > +#define MT_TXS0_BA_ERROR BIT(22) > +#define MT_TXS0_PS_FLAG BIT(21) > +#define MT_TXS0_TXOP_TIMEOUT BIT(20) > +#define MT_TXS0_BIP_ERROR BIT(19) > + > +#define MT_TXS0_QUEUE_TIMEOUT BIT(18) > +#define MT_TXS0_RTS_TIMEOUT BIT(17) > +#define MT_TXS0_ACK_TIMEOUT BIT(16) > +#define MT_TXS0_ACK_ERROR_MASK GENMASK(18, 16) > + > +#define MT_TXS0_TX_STATUS_HOST BIT(15) > +#define MT_TXS0_TX_STATUS_MCU BIT(14) > +#define MT_TXS0_TXS_FORMAT BIT(13) > +#define MT_TXS0_FIXED_RATE BIT(12) > +#define MT_TXS0_TX_RATE GENMASK(11, 0) > + > +#define MT_TXS1_F0_TIMESTAMP GENMASK(31, 0) > +#define MT_TXS1_F1_NOISE_2 GENMASK(23, 16) > +#define MT_TXS1_F1_NOISE_1 GENMASK(15, 8) > +#define MT_TXS1_F1_NOISE_0 GENMASK(7, 0) > + > +#define MT_TXS2_F0_FRONT_TIME GENMASK(24, 0) > +#define MT_TXS2_F1_RCPI_2 GENMASK(23, 16) > +#define MT_TXS2_F1_RCPI_1 GENMASK(15, 8) > +#define MT_TXS2_F1_RCPI_0 GENMASK(7, 0) > + > +#define MT_TXS3_WCID GENMASK(31, 24) > +#define MT_TXS3_RXV_SEQNO GENMASK(23, 16) > +#define MT_TXS3_TX_DELAY GENMASK(15, 0) > + > +#define MT_TXS4_LAST_TX_RATE GENMASK(31, 29) > +#define MT_TXS4_TX_COUNT GENMASK(28, 24) > +#define MT_TXS4_AMPDU BIT(23) > +#define MT_TXS4_ACKED_MPDU BIT(22) > +#define MT_TXS4_PID GENMASK(21, 14) > +#define MT_TXS4_BW GENMASK(13, 12) > +#define MT_TXS4_F0_SEQNO GENMASK(11, 0) > +#define MT_TXS4_F1_TSSI GENMASK(11, 0) > + > +#endif > diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/main.c b/drivers/net/wireless/mediatek/mt76/mt7603/main.c > new file mode 100644 > index 000000000000..74ccd6edbb9f > --- /dev/null > +++ b/drivers/net/wireless/mediatek/mt76/mt7603/main.c > @@ -0,0 +1,730 @@ > +/* SPDX-License-Identifier: ISC */ > +/* > + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> > + * > + * Permission to use, copy, modify, and/or distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#include <linux/etherdevice.h> > +#include <linux/platform_device.h> > +#include <linux/pci.h> > +#include <linux/module.h> > +#include "mt7603.h" > +#include "eeprom.h" > + > +static int > +mt7603_start(struct ieee80211_hw *hw) > +{ > + struct mt7603_dev *dev = hw->priv; > + > + mt7603_mac_start(dev); > + dev->survey_time = ktime_get_boottime(); > + set_bit(MT76_STATE_RUNNING, &dev->mt76.state); > + mt7603_mac_work(&dev->mac_work.work); > + > + return 0; > +} > + > +static void > +mt7603_stop(struct ieee80211_hw *hw) > +{ > + struct mt7603_dev *dev = hw->priv; > + > + clear_bit(MT76_STATE_RUNNING, &dev->mt76.state); > + cancel_delayed_work_sync(&dev->mac_work); > + mt7603_mac_stop(dev); > +} > + > +static int > +mt7603_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) > +{ > + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; > + struct mt7603_dev *dev = hw->priv; > + struct mt76_txq *mtxq; > + u8 bc_addr[ETH_ALEN]; > + int idx; > + int ret = 0; > + > + mutex_lock(&dev->mt76.mutex); > + > + mvif->idx = ffs(~dev->vif_mask) - 1; > + if (mvif->idx >= MT7603_MAX_INTERFACES) { > + ret = -ENOSPC; > + goto out; > + } > + > + mt76_wr(dev, MT_MAC_ADDR0(mvif->idx), > + get_unaligned_le32(vif->addr)); > + mt76_wr(dev, MT_MAC_ADDR1(mvif->idx), > + (get_unaligned_le16(vif->addr + 4) | > + MT_MAC_ADDR1_VALID)); > + > + if (vif->type == NL80211_IFTYPE_AP) { > + mt76_wr(dev, MT_BSSID0(mvif->idx), > + get_unaligned_le32(vif->addr)); > + mt76_wr(dev, MT_BSSID1(mvif->idx), > + (get_unaligned_le16(vif->addr + 4) | > + MT_BSSID1_VALID)); > + } > + > + idx = MT7603_WTBL_RESERVED - 1 - mvif->idx; > + dev->vif_mask |= BIT(mvif->idx); > + mvif->sta.wcid.idx = idx; > + mvif->sta.wcid.hw_key_idx = -1; > + > + eth_broadcast_addr(bc_addr); > + mt7603_wtbl_init(dev, idx, mvif->idx, bc_addr); > + > + mtxq = (struct mt76_txq *)vif->txq->drv_priv; > + mtxq->wcid = &mvif->sta.wcid; > + mt76_txq_init(&dev->mt76, vif->txq); > + rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid); > + > +out: > + mutex_unlock(&dev->mt76.mutex); > + > + return ret; > +} > + > +static void > +mt7603_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) > +{ > + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; > + struct mt7603_dev *dev = hw->priv; > + int idx = mvif->sta.wcid.idx; > + > + mt76_wr(dev, MT_MAC_ADDR0(mvif->idx), 0); > + mt76_wr(dev, MT_MAC_ADDR1(mvif->idx), 0); > + mt76_wr(dev, MT_BSSID0(mvif->idx), 0); > + mt76_wr(dev, MT_BSSID1(mvif->idx), 0); > + mt7603_beacon_set_timer(dev, mvif->idx, 0); > + > + rcu_assign_pointer(dev->mt76.wcid[idx], NULL); > + mt76_txq_remove(&dev->mt76, vif->txq); > + > + mutex_lock(&dev->mt76.mutex); > + dev->vif_mask &= ~BIT(mvif->idx); > + mutex_unlock(&dev->mt76.mutex); > +} > + > +static void > +mt7603_init_edcca(struct mt7603_dev *dev) > +{ > + /* Set lower signal level to -65dBm */ > + mt76_rmw_field(dev, MT_RXTD(8), MT_RXTD_8_LOWER_SIGNAL, 0x23); > + > + /* clear previous energy detect monitor results */ > + mt76_rr(dev, MT_MIB_STAT_ED); > + > + if (dev->ed_monitor) > + mt76_set(dev, MT_MIB_CTL, MT_MIB_CTL_ED_TIME); > + else > + mt76_clear(dev, MT_MIB_CTL, MT_MIB_CTL_ED_TIME); > + > + dev->ed_strict_mode = 0xff; > + dev->ed_strong_signal = 0; > + dev->ed_time = ktime_get_boottime(); > + > + mt7603_edcca_set_strict(dev, false); > +} > + > +static int > +mt7603_set_channel(struct mt7603_dev *dev, struct cfg80211_chan_def *def) > +{ > + u8 *rssi_data = (u8 *)dev->mt76.eeprom.data; > + int idx, ret; > + u8 bw = MT_BW_20; > + bool failed = false; > + > + cancel_delayed_work_sync(&dev->mac_work); > + > + mutex_lock(&dev->mt76.mutex); > + set_bit(MT76_RESET, &dev->mt76.state); > + > + mt76_set_channel(&dev->mt76); > + mt7603_mac_stop(dev); > + > + if (def->width == NL80211_CHAN_WIDTH_40) > + bw = MT_BW_40; > + > + dev->mt76.chandef = *def; > + mt76_rmw_field(dev, MT_AGG_BWCR, MT_AGG_BWCR_BW, bw); > + ret = mt7603_mcu_set_channel(dev); > + if (ret) { > + failed = true; > + goto out; > + } > + > + if (def->chan->band == NL80211_BAND_5GHZ) { > + idx = 1; > + rssi_data += MT_EE_RSSI_OFFSET_5G; > + } else { > + idx = 0; > + rssi_data += MT_EE_RSSI_OFFSET_2G; > + } > + > + memcpy(dev->rssi_offset, rssi_data, sizeof(dev->rssi_offset)); > + > + idx |= (def->chan - > + mt76_hw(dev)->wiphy->bands[def->chan->band]->channels) << 1; > + mt76_wr(dev, MT_WF_RMAC_CH_FREQ, idx); > + mt7603_mac_set_timing(dev); > + mt7603_mac_start(dev); > + > + clear_bit(MT76_RESET, &dev->mt76.state); > + > + mt76_txq_schedule_all(&dev->mt76); > + > + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mac_work, > + MT7603_WATCHDOG_TIME); > + > + /* reset channel stats */ > + mt76_clear(dev, MT_MIB_CTL, MT_MIB_CTL_READ_CLR_DIS); > + mt76_set(dev, MT_MIB_CTL, > + MT_MIB_CTL_CCA_NAV_TX | MT_MIB_CTL_PSCCA_TIME); > + mt76_rr(dev, MT_MIB_STAT_PSCCA); > + mt7603_cca_stats_reset(dev); > + > + dev->survey_time = ktime_get_boottime(); > + > + mt7603_init_edcca(dev); > + > +out: > + mutex_unlock(&dev->mt76.mutex); > + > + if (failed) > + mt7603_mac_work(&dev->mac_work.work); > + > + return ret; > +} > + > +static int > +mt7603_config(struct ieee80211_hw *hw, u32 changed) > +{ > + struct mt7603_dev *dev = hw->priv; > + int ret = 0; > + > + if (changed & (IEEE80211_CONF_CHANGE_CHANNEL | > + IEEE80211_CONF_CHANGE_POWER)) > + ret = mt7603_set_channel(dev, &hw->conf.chandef); > + > + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { > + mutex_lock(&dev->mt76.mutex); > + > + if (!(hw->conf.flags & IEEE80211_CONF_MONITOR)) > + dev->rxfilter |= MT_WF_RFCR_DROP_OTHER_UC; > + else > + dev->rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC; > + > + mt76_wr(dev, MT_WF_RFCR, dev->rxfilter); > + > + mutex_unlock(&dev->mt76.mutex); > + } > + > + return ret; > +} > + > +static void > +mt7603_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, > + unsigned int *total_flags, u64 multicast) > +{ > + struct mt7603_dev *dev = hw->priv; > + u32 flags = 0; > + > +#define MT76_FILTER(_flag, _hw) do { \ > + flags |= *total_flags & FIF_##_flag; \ > + dev->rxfilter &= ~(_hw); \ > + dev->rxfilter |= !(flags & FIF_##_flag) * (_hw); \ > + } while (0) > + > + dev->rxfilter &= ~(MT_WF_RFCR_DROP_OTHER_BSS | > + MT_WF_RFCR_DROP_OTHER_BEACON | > + MT_WF_RFCR_DROP_FRAME_REPORT | > + MT_WF_RFCR_DROP_PROBEREQ | > + MT_WF_RFCR_DROP_MCAST_FILTERED | > + MT_WF_RFCR_DROP_MCAST | > + MT_WF_RFCR_DROP_BCAST | > + MT_WF_RFCR_DROP_DUPLICATE | > + MT_WF_RFCR_DROP_A2_BSSID | > + MT_WF_RFCR_DROP_UNWANTED_CTL | > + MT_WF_RFCR_DROP_STBC_MULTI); > + > + MT76_FILTER(OTHER_BSS, MT_WF_RFCR_DROP_OTHER_TIM | > + MT_WF_RFCR_DROP_A3_MAC | > + MT_WF_RFCR_DROP_A3_BSSID); > + > + MT76_FILTER(FCSFAIL, MT_WF_RFCR_DROP_FCSFAIL); > + > + MT76_FILTER(CONTROL, MT_WF_RFCR_DROP_CTS | > + MT_WF_RFCR_DROP_RTS | > + MT_WF_RFCR_DROP_CTL_RSV | > + MT_WF_RFCR_DROP_NDPA); > + > + *total_flags = flags; > + mt76_wr(dev, MT_WF_RFCR, dev->rxfilter); > +} > + > +static void > +mt7603_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, > + struct ieee80211_bss_conf *info, u32 changed) > +{ > + struct mt7603_dev *dev = hw->priv; > + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; > + > + mutex_lock(&dev->mt76.mutex); > + > + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BSSID)) { > + if (info->assoc || info->ibss_joined) { > + mt76_wr(dev, MT_BSSID0(mvif->idx), > + get_unaligned_le32(info->bssid)); > + mt76_wr(dev, MT_BSSID1(mvif->idx), > + (get_unaligned_le16(info->bssid + 4) | > + MT_BSSID1_VALID)); > + } else { > + mt76_wr(dev, MT_BSSID0(mvif->idx), 0); > + mt76_wr(dev, MT_BSSID1(mvif->idx), 0); > + } > + } > + > + if (changed & BSS_CHANGED_ERP_SLOT) { > + int slottime = info->use_short_slot ? 9 : 20; > + > + if (slottime != dev->slottime) { > + dev->slottime = slottime; > + mt7603_mac_set_timing(dev); > + } > + } > + > + if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON_INT)) { > + int beacon_int = !!info->enable_beacon * info->beacon_int; > + > + tasklet_disable(&dev->pre_tbtt_tasklet); > + mt7603_beacon_set_timer(dev, mvif->idx, beacon_int); > + tasklet_enable(&dev->pre_tbtt_tasklet); > + } > + > + mutex_unlock(&dev->mt76.mutex); > +} > + > +int > +mt7603_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, > + struct ieee80211_sta *sta) > +{ > + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); > + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; > + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; > + int idx; > + int ret = 0; > + > + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7603_WTBL_STA - 1); > + if (idx < 0) > + return -ENOSPC; > + > + __skb_queue_head_init(&msta->psq); > + msta->ps = ~0; > + msta->smps = ~0; > + msta->wcid.sta = 1; > + msta->wcid.idx = idx; > + mt7603_wtbl_init(dev, idx, mvif->idx, sta->addr); > + mt7603_wtbl_set_ps(dev, msta, false); > + > + if (vif->type == NL80211_IFTYPE_AP) > + set_bit(MT_WCID_FLAG_CHECK_PS, &msta->wcid.flags); > + > + return ret; > +} > + > +void > +mt7603_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif, > + struct ieee80211_sta *sta) > +{ > + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); > + > + mt7603_wtbl_update_cap(dev, sta); > +} > + > +void > +mt7603_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, > + struct ieee80211_sta *sta) > +{ > + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); > + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; > + struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv; > + > + spin_lock_bh(&dev->ps_lock); > + __skb_queue_purge(&msta->psq); > + mt7603_filter_tx(dev, wcid->idx, true); > + spin_unlock_bh(&dev->ps_lock); > + > + mt7603_wtbl_clear(dev, wcid->idx); > +} > + > +static void > +mt7603_ps_tx_list(struct mt7603_dev *dev, struct sk_buff_head *list) > +{ > + struct sk_buff *skb; > + > + while ((skb = __skb_dequeue(list)) != NULL) > + mt7603_tx_queue_mcu(dev, skb_get_queue_mapping(skb), skb); > +} > + > +void > +mt7603_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps) > +{ > + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); > + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; > + struct sk_buff_head list; > + > + mt76_stop_tx_queues(&dev->mt76, sta, false); > + mt7603_wtbl_set_ps(dev, msta, ps); > + if (ps) > + return; > + > + __skb_queue_head_init(&list); > + > + spin_lock_bh(&dev->ps_lock); > + skb_queue_splice_tail_init(&msta->psq, &list); > + spin_unlock_bh(&dev->ps_lock); > + > + mt7603_ps_tx_list(dev, &list); > +} > + > +static void > +mt7603_release_buffered_frames(struct ieee80211_hw *hw, > + struct ieee80211_sta *sta, > + u16 tids, int nframes, > + enum ieee80211_frame_release_type reason, > + bool more_data) > +{ > + struct mt7603_dev *dev = hw->priv; > + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; > + struct sk_buff_head list; > + struct sk_buff *skb, *tmp; > + > + __skb_queue_head_init(&list); > + > + spin_lock_bh(&dev->ps_lock); > + skb_queue_walk_safe(&msta->psq, skb, tmp) { > + if (!nframes) > + break; > + > + if (!(tids & BIT(skb->priority))) > + continue; > + > + skb_set_queue_mapping(skb, MT_TXQ_PSD); > + __skb_unlink(skb, &msta->psq); > + __skb_queue_tail(&list, skb); > + nframes--; > + } > + spin_unlock_bh(&dev->ps_lock); > + > + mt7603_ps_tx_list(dev, &list); > + > + if (nframes) > + mt76_release_buffered_frames(hw, sta, tids, nframes, reason, > + more_data); > +} > + > +static int > +mt7603_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, > + struct ieee80211_vif *vif, struct ieee80211_sta *sta, > + struct ieee80211_key_conf *key) > +{ > + struct mt7603_dev *dev = hw->priv; > + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; > + struct mt7603_sta *msta = sta ? (struct mt7603_sta *)sta->drv_priv : > + &mvif->sta; > + struct mt76_wcid *wcid = &msta->wcid; > + int idx = key->keyidx; > + > + /* fall back to sw encryption for unsupported ciphers */ > + switch (key->cipher) { > + case WLAN_CIPHER_SUITE_TKIP: > + case WLAN_CIPHER_SUITE_CCMP: > + break; > + default: > + return -EOPNOTSUPP; > + } > + > + /* > + * The hardware does not support per-STA RX GTK, fall back > + * to software mode for these. > + */ > + if ((vif->type == NL80211_IFTYPE_ADHOC || > + vif->type == NL80211_IFTYPE_MESH_POINT) && > + (key->cipher == WLAN_CIPHER_SUITE_TKIP || > + key->cipher == WLAN_CIPHER_SUITE_CCMP) && > + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) > + return -EOPNOTSUPP; > + > + if (cmd == SET_KEY) { > + key->hw_key_idx = wcid->idx; > + wcid->hw_key_idx = idx; > + } else { > + if (idx == wcid->hw_key_idx) > + wcid->hw_key_idx = -1; > + > + key = NULL; > + } > + mt76_wcid_key_setup(&dev->mt76, wcid, key); > + > + return mt7603_wtbl_set_key(dev, wcid->idx, key); > +} > + > +static int > +mt7603_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, > + const struct ieee80211_tx_queue_params *params) > +{ > + struct mt7603_dev *dev = hw->priv; > + u16 cw_min = (1 << 5) - 1; > + u16 cw_max = (1 << 10) - 1; > + u32 val; > + > + queue = dev->mt76.q_tx[queue].hw_idx; > + > + if (params->cw_min) > + cw_min = params->cw_min; > + if (params->cw_max) > + cw_max = params->cw_max; > + > + mutex_lock(&dev->mt76.mutex); > + mt7603_mac_stop(dev); > + > + val = mt76_rr(dev, MT_WMM_TXOP(queue)); > + val &= ~(MT_WMM_TXOP_MASK << MT_WMM_TXOP_SHIFT(queue)); > + val |= params->txop << MT_WMM_TXOP_SHIFT(queue); > + mt76_wr(dev, MT_WMM_TXOP(queue), val); > + > + val = mt76_rr(dev, MT_WMM_AIFSN); > + val &= ~(MT_WMM_AIFSN_MASK << MT_WMM_AIFSN_SHIFT(queue)); > + val |= params->aifs << MT_WMM_AIFSN_SHIFT(queue); > + mt76_wr(dev, MT_WMM_AIFSN, val); > + > + val = mt76_rr(dev, MT_WMM_CWMIN); > + val &= ~(MT_WMM_CWMIN_MASK << MT_WMM_CWMIN_SHIFT(queue)); > + val |= cw_min << MT_WMM_CWMIN_SHIFT(queue); > + mt76_wr(dev, MT_WMM_CWMIN, val); > + > + val = mt76_rr(dev, MT_WMM_CWMAX(queue)); > + val &= ~(MT_WMM_CWMAX_MASK << MT_WMM_CWMAX_SHIFT(queue)); > + val |= cw_max << MT_WMM_CWMAX_SHIFT(queue); > + mt76_wr(dev, MT_WMM_CWMAX(queue), val); > + > + mt7603_mac_start(dev); > + mutex_unlock(&dev->mt76.mutex); > + > + return 0; > +} > + > +static void > +mt7603_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, > + const u8 *mac) > +{ > + struct mt7603_dev *dev = hw->priv; > + > + set_bit(MT76_SCANNING, &dev->mt76.state); > + mt7603_beacon_set_timer(dev, -1, 0); > +} > + > +static void > +mt7603_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif) > +{ > + struct mt7603_dev *dev = hw->priv; > + > + clear_bit(MT76_SCANNING, &dev->mt76.state); > + mt7603_beacon_set_timer(dev, -1, dev->beacon_int); > +} > + > +static void > +mt7603_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, > + u32 queues, bool drop) > +{ > +} > + > +static int > +mt7603_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, > + struct ieee80211_ampdu_params *params) > +{ > + enum ieee80211_ampdu_mlme_action action = params->action; > + struct mt7603_dev *dev = hw->priv; > + struct ieee80211_sta *sta = params->sta; > + struct ieee80211_txq *txq = sta->txq[params->tid]; > + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; > + u16 tid = params->tid; > + u16 *ssn = ¶ms->ssn; > + u8 ba_size = params->buf_size; > + struct mt76_txq *mtxq; > + > + if (!txq) > + return -EINVAL; > + > + mtxq = (struct mt76_txq *)txq->drv_priv; > + > + switch (action) { > + case IEEE80211_AMPDU_RX_START: > + mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, *ssn, > + params->buf_size); > + mt7603_mac_rx_ba_reset(dev, sta->addr, tid); > + break; > + case IEEE80211_AMPDU_RX_STOP: > + mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid); > + break; > + case IEEE80211_AMPDU_TX_OPERATIONAL: > + mtxq->aggr = true; > + mtxq->send_bar = false; > + mt7603_mac_tx_ba_reset(dev, msta->wcid.idx, tid, *ssn, ba_size); > + break; > + case IEEE80211_AMPDU_TX_STOP_FLUSH: > + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: > + mtxq->aggr = false; > + ieee80211_send_bar(vif, sta->addr, tid, mtxq->agg_ssn); > + mt7603_mac_tx_ba_reset(dev, msta->wcid.idx, tid, *ssn, -1); > + break; > + case IEEE80211_AMPDU_TX_START: > + mtxq->agg_ssn = *ssn << 4; > + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); > + break; > + case IEEE80211_AMPDU_TX_STOP_CONT: > + mtxq->aggr = false; > + mt7603_mac_tx_ba_reset(dev, msta->wcid.idx, tid, *ssn, -1); > + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); > + break; > + } > + > + return 0; > +} > + > +static void > +mt7603_sta_rate_tbl_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, > + struct ieee80211_sta *sta) > +{ > + struct mt7603_dev *dev = hw->priv; > + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; > + struct ieee80211_sta_rates *sta_rates = rcu_dereference(sta->rates); > + int i; > + > + spin_lock_bh(&dev->mt76.lock); > + for (i = 0; i < ARRAY_SIZE(msta->rates); i++) { > + msta->rates[i].idx = sta_rates->rate[i].idx; > + msta->rates[i].count = sta_rates->rate[i].count; > + msta->rates[i].flags = sta_rates->rate[i].flags; > + > + if (msta->rates[i].idx < 0 || !msta->rates[i].count) > + break; > + } > + msta->n_rates = i; > + mt7603_wtbl_set_rates(dev, msta, NULL, msta->rates); > + msta->rate_probe = false; > + mt7603_wtbl_set_smps(dev, msta, > + sta->smps_mode == IEEE80211_SMPS_DYNAMIC); > + spin_unlock_bh(&dev->mt76.lock); > +} > + > +static void > +mt7603_set_coverage_class(struct ieee80211_hw *hw, s16 coverage_class) > +{ > + struct mt7603_dev *dev = hw->priv; > + > + dev->coverage_class = coverage_class; > + mt7603_mac_set_timing(dev); > +} > + > +static void mt7603_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, > + struct sk_buff *skb) > +{ > + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; > + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); > + struct ieee80211_vif *vif = info->control.vif; > + struct mt7603_dev *dev = hw->priv; > + struct mt76_wcid *wcid = &dev->global_sta.wcid; > + > + if (control->sta) { > + struct mt7603_sta *msta; > + > + msta = (struct mt7603_sta *)control->sta->drv_priv; > + wcid = &msta->wcid; > + /* sw encrypted frames */ > + if (!info->control.hw_key && wcid->hw_key_idx != 0xff && > + ieee80211_has_protected(hdr->frame_control)) > + control->sta = NULL; > + } > + > + if (vif && !control->sta) { > + struct mt7603_vif *mvif; > + > + mvif = (struct mt7603_vif *)vif->drv_priv; > + wcid = &mvif->sta.wcid; > + } > + > + mt76_tx(&dev->mt76, control->sta, wcid, skb); > +} > + > +static int > +mt7603_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) > +{ > + return 0; > +} > + > +const struct ieee80211_ops mt7603_ops = { > + .tx = mt7603_tx, > + .start = mt7603_start, > + .stop = mt7603_stop, > + .add_interface = mt7603_add_interface, > + .remove_interface = mt7603_remove_interface, > + .config = mt7603_config, > + .configure_filter = mt7603_configure_filter, > + .bss_info_changed = mt7603_bss_info_changed, > + .sta_state = mt76_sta_state, > + .set_key = mt7603_set_key, > + .conf_tx = mt7603_conf_tx, > + .sw_scan_start = mt7603_sw_scan, > + .sw_scan_complete = mt7603_sw_scan_complete, > + .flush = mt7603_flush, > + .ampdu_action = mt7603_ampdu_action, > + .get_txpower = mt76_get_txpower, > + .wake_tx_queue = mt76_wake_tx_queue, > + .sta_rate_tbl_update = mt7603_sta_rate_tbl_update, > + .release_buffered_frames = mt7603_release_buffered_frames, > + .set_coverage_class = mt7603_set_coverage_class, > + .set_tim = mt7603_set_tim, > + .get_survey = mt76_get_survey, > +}; > + > +MODULE_LICENSE("Dual BSD/GPL"); > + > +static int __init mt7603_init(void) > +{ > + int ret; > + > + ret = platform_driver_register(&mt76_wmac_driver); > + if (ret) > + return ret; > + > +#ifdef CONFIG_PCI > + ret = pci_register_driver(&mt7603_pci_driver); > + if (ret) > + platform_driver_unregister(&mt76_wmac_driver); > +#endif > + return ret; > +} > + > +static void __exit mt7603_exit(void) > +{ > +#ifdef CONFIG_PCI > + pci_unregister_driver(&mt7603_pci_driver); > +#endif > + platform_driver_unregister(&mt76_wmac_driver); > +} > + > +module_init(mt7603_init); > +module_exit(mt7603_exit); > diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c > new file mode 100644 > index 000000000000..bd5b60ac7a24 > --- /dev/null > +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c > @@ -0,0 +1,536 @@ > +/* SPDX-License-Identifier: ISC */ > +/* > + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> > + * > + * Permission to use, copy, modify, and/or distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#include <linux/firmware.h> > +#include "mt7603.h" > +#include "mcu.h" > +#include "eeprom.h" > + > +#define MCU_SKB_RESERVE 8 > + > +struct mt7603_fw_trailer { > + char fw_ver[10]; > + char build_date[15]; > + __le32 dl_len; > +} __packed; > + > +static struct sk_buff *mt7603_mcu_msg_alloc(const void *data, int len) > +{ > + struct sk_buff *skb; > + > + skb = alloc_skb(len + sizeof(struct mt7603_mcu_txd), > + GFP_KERNEL); > + if (!skb) > + return NULL; > + > + skb_reserve(skb, sizeof(struct mt7603_mcu_txd)); > + if (data && len) > + memcpy(skb_put(skb, len), data, len); > + > + return skb; > +} > + > +void mt7603_mcu_rx_event(struct mt7603_dev *dev, struct sk_buff *skb) > +{ > + skb_queue_tail(&dev->mt76.mmio.mcu.res_q, skb); > + wake_up(&dev->mt76.mmio.mcu.wait); > +} > + > +static struct sk_buff * > +mt7603_mcu_get_response(struct mt7603_dev *dev, unsigned long expires) > +{ > + struct mt76_dev *mdev = &dev->mt76; > + unsigned long timeout; > + > + if (!time_is_after_jiffies(expires)) > + return NULL; > + > + timeout = expires - jiffies; > + wait_event_timeout(mdev->mmio.mcu.wait, > + !skb_queue_empty(&mdev->mmio.mcu.res_q), > + timeout); > + return skb_dequeue(&mdev->mmio.mcu.res_q); > +} > + > +static int > +__mt7603_mcu_msg_send(struct mt7603_dev *dev, struct sk_buff *skb, int cmd, > + int query, int *wait_seq) > +{ > + int hdrlen = dev->mcu_running ? sizeof(struct mt7603_mcu_txd) : 12; > + struct mt76_dev *mdev = &dev->mt76; > + struct mt7603_mcu_txd *txd; > + u8 seq; > + > + if (!skb) > + return -EINVAL; > + > + seq = ++mdev->mmio.mcu.msg_seq & 0xf; > + if (!seq) > + seq = ++mdev->mmio.mcu.msg_seq & 0xf; > + > + txd = (struct mt7603_mcu_txd *)skb_push(skb, hdrlen); > + memset(txd, 0, hdrlen); > + > + txd->len = cpu_to_le16(skb->len); > + if (cmd == -MCU_CMD_FW_SCATTER) > + txd->pq_id = cpu_to_le16(MCU_PORT_QUEUE_FW); > + else > + txd->pq_id = cpu_to_le16(MCU_PORT_QUEUE); > + txd->pkt_type = MCU_PKT_ID; > + txd->seq = seq; > + > + if (cmd < 0) { > + txd->cid = -cmd; > + } else { > + txd->cid = MCU_CMD_EXT_CID; > + txd->ext_cid = cmd; > + if (query != MCU_Q_NA) > + txd->ext_cid_ack = 1; > + } > + > + txd->set_query = query; > + > + if (wait_seq) > + *wait_seq = seq; > + > + return mt7603_tx_queue_mcu(dev, MT_TXQ_MCU, skb); > +} > + > +static int > +mt7603_mcu_msg_send(struct mt7603_dev *dev, struct sk_buff *skb, int cmd, > + int query) > +{ > + struct mt76_dev *mdev = &dev->mt76; > + unsigned long expires = jiffies + 3 * HZ; > + struct mt7603_mcu_rxd *rxd; > + int ret, seq; > + > + mutex_lock(&mdev->mmio.mcu.mutex); > + > + ret = __mt7603_mcu_msg_send(dev, skb, cmd, query, &seq); > + if (ret) > + goto out; > + > + while (1) { > + bool check_seq = false; > + > + skb = mt7603_mcu_get_response(dev, expires); > + if (!skb) { > + dev_err(mdev->dev, > + "MCU message %d (seq %d) timed out\n", > + cmd, seq); > + dev->mcu_hang = MT7603_WATCHDOG_TIMEOUT; > + ret = -ETIMEDOUT; > + break; > + } > + > + rxd = (struct mt7603_mcu_rxd *)skb->data; > + if (seq == rxd->seq) > + check_seq = true; > + > + dev_kfree_skb(skb); > + > + if (check_seq) > + break; > + } > + > +out: > + mutex_unlock(&mdev->mmio.mcu.mutex); > + > + return ret; > +} > + > +static int > +mt7603_mcu_init_download(struct mt7603_dev *dev, u32 addr, u32 len) > +{ > + struct { > + __le32 addr; > + __le32 len; > + __le32 mode; > + } req = { > + .addr = cpu_to_le32(addr), > + .len = cpu_to_le32(len), > + .mode = cpu_to_le32(BIT(31)), > + }; > + struct sk_buff *skb = mt7603_mcu_msg_alloc(&req, sizeof(req)); > + > + return mt7603_mcu_msg_send(dev, skb, -MCU_CMD_TARGET_ADDRESS_LEN_REQ, > + MCU_Q_NA); > +} > + > +static int > +mt7603_mcu_send_firmware(struct mt7603_dev *dev, const void *data, int len) > +{ > + struct sk_buff *skb; > + int ret = 0; > + > + while (len > 0) { > + int cur_len = min_t(int, 4096 - sizeof(struct mt7603_mcu_txd), > + len); > + > + skb = mt7603_mcu_msg_alloc(data, cur_len); > + if (!skb) > + return -ENOMEM; > + > + ret = __mt7603_mcu_msg_send(dev, skb, -MCU_CMD_FW_SCATTER, > + MCU_Q_NA, NULL); > + if (ret) > + break; > + > + data += cur_len; > + len -= cur_len; > + } > + > + return ret; > +} > + > +static int > +mt7603_mcu_start_firmware(struct mt7603_dev *dev, u32 addr) > +{ > + struct { > + __le32 override; > + __le32 addr; > + } req = { > + .override = cpu_to_le32(addr ? 1 : 0), > + .addr = cpu_to_le32(addr), > + }; > + struct sk_buff *skb = mt7603_mcu_msg_alloc(&req, sizeof(req)); > + > + return mt7603_mcu_msg_send(dev, skb, -MCU_CMD_FW_START_REQ, > + MCU_Q_NA); > +} > + > +static int > +mt7603_mcu_restart(struct mt7603_dev *dev) > +{ > + struct sk_buff *skb = mt7603_mcu_msg_alloc(NULL, 0); > + > + return mt7603_mcu_msg_send(dev, skb, -MCU_CMD_RESTART_DL_REQ, > + MCU_Q_NA); > +} > + > +static int > +mt7603_load_firmware(struct mt7603_dev *dev) > +{ > + const struct firmware *fw; > + const struct mt7603_fw_trailer *hdr; > + const char *firmware; > + int dl_len; > + u32 addr, val; > + int ret; > + > + if (is_mt7628(dev)) { > + if (mt76xx_rev(dev) == MT7628_REV_E1) > + firmware = MT7628_FIRMWARE_E1; > + else > + firmware = MT7628_FIRMWARE_E2; > + } else { > + if (mt76xx_rev(dev) < MT7603_REV_E2) > + firmware = MT7603_FIRMWARE_E1; > + else > + firmware = MT7603_FIRMWARE_E2; > + } > + > + ret = request_firmware(&fw, firmware, dev->mt76.dev); > + if (ret) > + return ret; > + > + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { > + dev_err(dev->mt76.dev, "Invalid firmware\n"); > + ret = -EINVAL; > + goto out; > + } > + > + hdr = (const struct mt7603_fw_trailer *)(fw->data + fw->size - > + sizeof(*hdr)); > + > + dev_info(dev->mt76.dev, "Firmware Version: %.10s\n", hdr->fw_ver); > + dev_info(dev->mt76.dev, "Build Time: %.15s\n", hdr->build_date); > + > + addr = mt7603_reg_map(dev, 0x50012498); > + mt76_wr(dev, addr, 0x5); > + mt76_wr(dev, addr, 0x5); > + udelay(1); > + > + /* switch to bypass mode */ > + mt76_rmw(dev, MT_SCH_4, MT_SCH_4_FORCE_QID, > + MT_SCH_4_BYPASS | FIELD_PREP(MT_SCH_4_FORCE_QID, 5)); > + > + val = mt76_rr(dev, MT_TOP_MISC2); > + if (val & BIT(1)) { > + dev_info(dev->mt76.dev, "Firmware already running...\n"); > + goto running; > + } > + > + if (!mt76_poll_msec(dev, MT_TOP_MISC2, BIT(0) | BIT(1), BIT(0), 500)) { > + dev_err(dev->mt76.dev, "Timeout waiting for ROM code to become ready\n"); > + ret = -EIO; > + goto out; > + } > + > + dl_len = le32_to_cpu(hdr->dl_len) + 4; > + ret = mt7603_mcu_init_download(dev, MCU_FIRMWARE_ADDRESS, dl_len); > + if (ret) { > + dev_err(dev->mt76.dev, "Download request failed\n"); > + goto out; > + } > + > + ret = mt7603_mcu_send_firmware(dev, fw->data, dl_len); > + if (ret) { > + dev_err(dev->mt76.dev, "Failed to send firmware to device\n"); > + goto out; > + } > + > + ret = mt7603_mcu_start_firmware(dev, MCU_FIRMWARE_ADDRESS); > + if (ret) { > + dev_err(dev->mt76.dev, "Failed to start firmware\n"); > + goto out; > + } > + > + if (!mt76_poll_msec(dev, MT_TOP_MISC2, BIT(1), BIT(1), 500)) { > + dev_err(dev->mt76.dev, "Timeout waiting for firmware to initialize\n"); > + ret = -EIO; > + goto out; > + } > + > +running: > + mt76_clear(dev, MT_SCH_4, MT_SCH_4_FORCE_QID | MT_SCH_4_BYPASS); > + > + mt76_set(dev, MT_SCH_4, BIT(8)); > + mt76_clear(dev, MT_SCH_4, BIT(8)); > + > + dev->mcu_running = true; > + dev_info(dev->mt76.dev, "firmware init done\n"); > + > +out: > + release_firmware(fw); > + > + return ret; > +} > + > +int mt7603_mcu_init(struct mt7603_dev *dev) > +{ > + mutex_init(&dev->mt76.mmio.mcu.mutex); > + > + return mt7603_load_firmware(dev); > +} > + > +void mt7603_mcu_exit(struct mt7603_dev *dev) > +{ > + mt7603_mcu_restart(dev); > + skb_queue_purge(&dev->mt76.mmio.mcu.res_q); > +} > + > +int mt7603_mcu_set_eeprom(struct mt7603_dev *dev) > +{ > + static const u16 req_fields[] = { > +#define WORD(_start) \ > + _start, \ > + _start + 1 > +#define GROUP_2G(_start) \ > + WORD(_start), \ > + WORD(_start + 2), \ > + WORD(_start + 4) > + > + MT_EE_NIC_CONF_0 + 1, > + WORD(MT_EE_NIC_CONF_1), > + MT_EE_WIFI_RF_SETTING, > + MT_EE_TX_POWER_DELTA_BW40, > + MT_EE_TX_POWER_DELTA_BW80 + 1, > + MT_EE_TX_POWER_EXT_PA_5G, > + MT_EE_TEMP_SENSOR_CAL, > + GROUP_2G(MT_EE_TX_POWER_0_START_2G), > + GROUP_2G(MT_EE_TX_POWER_1_START_2G), > + WORD(MT_EE_TX_POWER_CCK), > + WORD(MT_EE_TX_POWER_OFDM_2G_6M), > + WORD(MT_EE_TX_POWER_OFDM_2G_24M), > + WORD(MT_EE_TX_POWER_OFDM_2G_54M), > + WORD(MT_EE_TX_POWER_HT_BPSK_QPSK), > + WORD(MT_EE_TX_POWER_HT_16_64_QAM), > + WORD(MT_EE_TX_POWER_HT_64_QAM), > + MT_EE_ELAN_RX_MODE_GAIN, > + MT_EE_ELAN_RX_MODE_NF, > + MT_EE_ELAN_RX_MODE_P1DB, > + MT_EE_ELAN_BYPASS_MODE_GAIN, > + MT_EE_ELAN_BYPASS_MODE_NF, > + MT_EE_ELAN_BYPASS_MODE_P1DB, > + WORD(MT_EE_STEP_NUM_NEG_6_7), > + WORD(MT_EE_STEP_NUM_NEG_4_5), > + WORD(MT_EE_STEP_NUM_NEG_2_3), > + WORD(MT_EE_STEP_NUM_NEG_0_1), > + WORD(MT_EE_REF_STEP_24G), > + WORD(MT_EE_STEP_NUM_PLUS_1_2), > + WORD(MT_EE_STEP_NUM_PLUS_3_4), > + WORD(MT_EE_STEP_NUM_PLUS_5_6), > + MT_EE_STEP_NUM_PLUS_7, > + MT_EE_XTAL_FREQ_OFFSET, > + MT_EE_XTAL_TRIM_2_COMP, > + MT_EE_XTAL_TRIM_3_COMP, > + MT_EE_XTAL_WF_RFCAL, > + > + /* unknown fields below */ > + WORD(0x24), > + 0x34, > + 0x39, > + 0x3b, > + WORD(0x42), > + WORD(0x9e), > + 0xf2, > + WORD(0xf8), > + 0xfa, > + 0x12e, > + WORD(0x130), WORD(0x132), WORD(0x134), WORD(0x136), > + WORD(0x138), WORD(0x13a), WORD(0x13c), WORD(0x13e), > + > +#undef GROUP_2G > +#undef WORD > + > + }; > + struct req_data { > + u16 addr; > + u8 val; > + u8 pad; > + } __packed; > + struct { > + u8 buffer_mode; > + u8 len; > + u8 pad[2]; > + } req_hdr = { > + .buffer_mode = 1, > + .len = ARRAY_SIZE(req_fields) - 1, > + }; > + struct sk_buff *skb; > + struct req_data *data; > + const int size = 0xff * sizeof(struct req_data); > + u8 *eep = (u8 *)dev->mt76.eeprom.data; > + int i; > + > + BUILD_BUG_ON(ARRAY_SIZE(req_fields) * sizeof(*data) > size); > + > + skb = mt7603_mcu_msg_alloc(NULL, size + sizeof(req_hdr)); > + memcpy(skb_put(skb, sizeof(req_hdr)), &req_hdr, sizeof(req_hdr)); > + data = (struct req_data *)skb_put(skb, size); > + memset(data, 0, size); > + > + for (i = 0; i < ARRAY_SIZE(req_fields); i++) { > + data[i].addr = cpu_to_le16(req_fields[i]); > + data[i].val = eep[req_fields[i]]; > + data[i].pad = 0; > + } > + > + return mt7603_mcu_msg_send(dev, skb, MCU_EXT_CMD_EFUSE_BUFFER_MODE, > + MCU_Q_SET); > +} > + > +static int mt7603_mcu_set_tx_power(struct mt7603_dev *dev) > +{ > + struct { > + u8 center_channel; > + u8 tssi; > + u8 temp_comp; > + u8 target_power[2]; > + u8 rate_power_delta[14]; > + u8 bw_power_delta; > + u8 ch_power_delta[6]; > + u8 temp_comp_power[17]; > + u8 reserved; > + } req = { > + .center_channel = dev->mt76.chandef.chan->hw_value, > +#define EEP_VAL(n) ((u8 *)dev->mt76.eeprom.data)[n] > + .tssi = EEP_VAL(MT_EE_NIC_CONF_1 + 1), > + .temp_comp = EEP_VAL(MT_EE_NIC_CONF_1), > + .target_power = { > + EEP_VAL(MT_EE_TX_POWER_0_START_2G + 2), > + EEP_VAL(MT_EE_TX_POWER_1_START_2G + 2) > + }, > + .bw_power_delta = EEP_VAL(MT_EE_TX_POWER_DELTA_BW40), > + .ch_power_delta = { > + EEP_VAL(MT_EE_TX_POWER_0_START_2G + 3), > + EEP_VAL(MT_EE_TX_POWER_0_START_2G + 4), > + EEP_VAL(MT_EE_TX_POWER_0_START_2G + 5), > + EEP_VAL(MT_EE_TX_POWER_1_START_2G + 3), > + EEP_VAL(MT_EE_TX_POWER_1_START_2G + 4), > + EEP_VAL(MT_EE_TX_POWER_1_START_2G + 5) > + }, > +#undef EEP_VAL > + }; > + struct sk_buff *skb; > + u8 *eep = (u8 *)dev->mt76.eeprom.data; > + > + memcpy(req.rate_power_delta, eep + MT_EE_TX_POWER_CCK, > + sizeof(req.rate_power_delta)); > + > + memcpy(req.temp_comp_power, eep + MT_EE_STEP_NUM_NEG_6_7, > + sizeof(req.temp_comp_power)); > + > + skb = mt7603_mcu_msg_alloc(&req, sizeof(req)); > + return mt7603_mcu_msg_send(dev, skb, MCU_EXT_CMD_SET_TX_POWER_CTRL, > + MCU_Q_SET); > +} > + > +int mt7603_mcu_set_channel(struct mt7603_dev *dev) > +{ > + struct cfg80211_chan_def *chandef = &dev->mt76.chandef; > + struct ieee80211_hw *hw = mt76_hw(dev); > + int n_chains = __sw_hweight8(dev->mt76.antenna_mask); > + struct { > + u8 control_chan; > + u8 center_chan; > + u8 bw; > + u8 tx_streams; > + u8 rx_streams; > + u8 _res0[7]; > + u8 txpower[21]; > + u8 _res1[3]; > + } req = { > + .control_chan = chandef->chan->hw_value, > + .center_chan = chandef->chan->hw_value, > + .bw = MT_BW_20, > + .tx_streams = n_chains, > + .rx_streams = n_chains, > + }; > + struct sk_buff *skb; > + s8 tx_power; > + int ret; > + int i; > + > + if (dev->mt76.chandef.width == NL80211_CHAN_WIDTH_40) { > + req.bw = MT_BW_40; > + if (chandef->center_freq1 > chandef->chan->center_freq) > + req.center_chan += 2; > + else > + req.center_chan -= 2; > + } > + > + tx_power = hw->conf.power_level * 2; > + if (dev->mt76.antenna_mask == 3) > + tx_power -= 6; > + tx_power = min(tx_power, dev->tx_power_limit); > + > + dev->mt76.txpower_cur = tx_power; > + > + for (i = 0; i < ARRAY_SIZE(req.txpower); i++) > + req.txpower[i] = tx_power; > + > + skb = mt7603_mcu_msg_alloc(&req, sizeof(req)); > + ret = mt7603_mcu_msg_send(dev, skb, MCU_EXT_CMD_CHANNEL_SWITCH, > + MCU_Q_SET); > + if (ret) > + return ret; > + > + return mt7603_mcu_set_tx_power(dev); > +} > diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.h > new file mode 100644 > index 000000000000..3cdb4e6e0c9d > --- /dev/null > +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.h > @@ -0,0 +1,118 @@ > +/* SPDX-License-Identifier: ISC */ > +/* > + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> > + * > + * Permission to use, copy, modify, and/or distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#ifndef __MT7603_MCU_H > +#define __MT7603_MCU_H > + > +struct mt7603_mcu_txd { > + __le16 len; > + __le16 pq_id; > + > + u8 cid; > + u8 pkt_type; > + u8 set_query; > + u8 seq; > + > + u8 uc_d2b0_rev; > + u8 ext_cid; > + u8 uc_d2b2_rev; > + u8 ext_cid_ack; > + > + u32 au4_d3_to_d7_rev[5]; > +} __packed __aligned(4); > + > +struct mt7603_mcu_rxd { > + __le16 len; > + __le16 pkt_type_id; > + > + u8 eid; > + u8 seq; > + __le16 __rsv; > + > + u8 ext_eid; > + u8 __rsv1[3]; > +}; > + > +#define MCU_PKT_ID 0xa0 > +#define MCU_PORT_QUEUE 0x8000 > +#define MCU_PORT_QUEUE_FW 0xc000 > + > +#define MCU_FIRMWARE_ADDRESS 0x100000 > + > +enum { > + MCU_Q_QUERY, > + MCU_Q_SET, > + MCU_Q_RESERVED, > + MCU_Q_NA > +}; > + > +enum { > + MCU_CMD_TARGET_ADDRESS_LEN_REQ = 0x01, > + MCU_CMD_FW_START_REQ = 0x02, > + MCU_CMD_INIT_ACCESS_REG = 0x3, > + MCU_CMD_PATCH_START_REQ = 0x05, > + MCU_CMD_PATCH_FINISH_REQ = 0x07, > + MCU_CMD_PATCH_SEM_CONTROL = 0x10, > + MCU_CMD_HIF_LOOPBACK = 0x20, > + MCU_CMD_CH_PRIVILEGE = 0x20, > + MCU_CMD_ACCESS_REG = 0xC2, > + MCU_CMD_EXT_CID = 0xED, > + MCU_CMD_FW_SCATTER = 0xEE, > + MCU_CMD_RESTART_DL_REQ = 0xEF, > +}; > + > +enum { > + MCU_EXT_CMD_RF_REG_ACCESS = 0x02, > + MCU_EXT_CMD_RF_TEST = 0x04, > + MCU_EXT_CMD_RADIO_ON_OFF_CTRL = 0x05, > + MCU_EXT_CMD_WIFI_RX_DISABLE = 0x06, > + MCU_EXT_CMD_PM_STATE_CTRL = 0x07, > + MCU_EXT_CMD_CHANNEL_SWITCH = 0x08, > + MCU_EXT_CMD_NIC_CAPABILITY = 0x09, > + MCU_EXT_CMD_PWR_SAVING = 0x0A, > + MCU_EXT_CMD_MULTIPLE_REG_ACCESS = 0x0E, > + MCU_EXT_CMD_AP_PWR_SAVING_CAPABILITY = 0xF, > + MCU_EXT_CMD_SEC_ADDREMOVE_KEY = 0x10, > + MCU_EXT_CMD_SET_TX_POWER_CTRL = 0x11, > + MCU_EXT_CMD_FW_LOG_2_HOST = 0x13, > + MCU_EXT_CMD_PS_RETRIEVE_START = 0x14, > + MCU_EXT_CMD_LED_CTRL = 0x17, > + MCU_EXT_CMD_PACKET_FILTER = 0x18, > + MCU_EXT_CMD_PWR_MGT_BIT_WIFI = 0x1B, > + MCU_EXT_CMD_EFUSE_BUFFER_MODE = 0x21, > + MCU_EXT_CMD_THERMAL_PROTECT = 0x23, > + MCU_EXT_CMD_EDCA_SET = 0x27, > + MCU_EXT_CMD_SLOT_TIME_SET = 0x28, > + MCU_EXT_CMD_CONFIG_INTERNAL_SETTING = 0x29, > + MCU_EXT_CMD_NOA_OFFLOAD_CTRL = 0x2B, > + MCU_EXT_CMD_GET_THEMAL_SENSOR = 0x2C, > + MCU_EXT_CMD_WAKEUP_OPTION = 0x2E, > + MCU_EXT_CMD_AC_QUEUE_CONTROL = 0x31, > + MCU_EXT_CMD_BCN_UPDATE = 0x33 > +}; > + > +enum { > + MCU_EXT_EVENT_CMD_RESULT = 0x0, > + MCU_EXT_EVENT_RF_REG_ACCESS = 0x2, > + MCU_EXT_EVENT_MULTI_CR_ACCESS = 0x0E, > + MCU_EXT_EVENT_FW_LOG_2_HOST = 0x13, > + MCU_EXT_EVENT_BEACON_LOSS = 0x1A, > + MCU_EXT_EVENT_THERMAL_PROTECT = 0x22, > + MCU_EXT_EVENT_BCN_UPDATE = 0x31, > +}; > + > +#endif > diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h > new file mode 100644 > index 000000000000..8b9297d0ec89 > --- /dev/null > +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h > @@ -0,0 +1,271 @@ > +/* SPDX-License-Identifier: ISC */ > +/* > + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> > + * > + * Permission to use, copy, modify, and/or distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#ifndef __MT7603_H > +#define __MT7603_H > + > +#include <linux/interrupt.h> > +#include <linux/ktime.h> > +#include "../mt76.h" > +#include "regs.h" > + > +#define MT7603_MAX_INTERFACES 4 > +#define MT7603_WTBL_SIZE 128 > +#define MT7603_WTBL_RESERVED (MT7603_WTBL_SIZE - 1) > +#define MT7603_WTBL_STA (MT7603_WTBL_RESERVED - MT7603_MAX_INTERFACES) > + > +#define MT7603_RATE_RETRY 2 > + > +#define MT7603_RX_RING_SIZE 128 > + > +#define MT7603_FIRMWARE_E1 "mt7603_e1.bin" > +#define MT7603_FIRMWARE_E2 "mt7603_e2.bin" > +#define MT7628_FIRMWARE_E1 "mt7628_e1.bin" > +#define MT7628_FIRMWARE_E2 "mt7628_e2.bin" > + > +#define MT7603_EEPROM_SIZE 1024 > + > +#define MT_AGG_SIZE_LIMIT(_n) (((_n) + 1) * 4) > + > +#define MT7603_PRE_TBTT_TIME 5000 /* ms */ > + > +#define MT7603_WATCHDOG_TIME 100 /* ms */ > +#define MT7603_WATCHDOG_TIMEOUT 10 /* number of checks */ > + > +#define MT7603_EDCCA_BLOCK_TH 10 > + > +#define MT7603_CFEND_RATE_DEFAULT 0x69 /* chip default (24M) */ > +#define MT7603_CFEND_RATE_11B 0x03 /* 11B LP, 11M */ > + > +struct mt7603_vif; > +struct mt7603_sta; > + > +enum { > + MT7603_REV_E1 = 0x00, > + MT7603_REV_E2 = 0x10, > + MT7628_REV_E1 = 0x8a00, > +}; > + > +enum mt7603_bw { > + MT_BW_20, > + MT_BW_40, > + MT_BW_80, > +}; > + > +struct mt7603_sta { > + struct mt76_wcid wcid; /* must be first */ > + > + struct mt7603_vif *vif; > + > + struct sk_buff_head psq; > + > + struct ieee80211_tx_rate rates[8]; > + u8 rate_count; > + u8 n_rates; > + > + u8 rate_probe; > + u8 smps; > + > + u8 ps; > +}; > + > +struct mt7603_vif { > + struct mt7603_sta sta; /* must be first */ > + > + u8 idx; > +}; > + > +enum mt7603_reset_cause { > + RESET_CAUSE_TX_HANG, > + RESET_CAUSE_TX_BUSY, > + RESET_CAUSE_RX_BUSY, > + RESET_CAUSE_BEACON_STUCK, > + RESET_CAUSE_RX_PSE_BUSY, > + RESET_CAUSE_MCU_HANG, > + RESET_CAUSE_RESET_FAILED, > + __RESET_CAUSE_MAX > +}; > + > +struct mt7603_dev { > + struct mt76_dev mt76; /* must be first */ > + > + const struct mt76_bus_ops *bus_ops; > + > + u32 rxfilter; > + > + u8 vif_mask; > + > + struct mt7603_sta global_sta; > + > + u32 agc0, agc3; > + u32 false_cca_ofdm, false_cca_cck; > + unsigned long last_cca_adj; > + > + u8 rssi_offset[3]; > + > + u8 slottime; > + s16 coverage_class; > + > + s8 tx_power_limit; > + > + ktime_t survey_time; > + ktime_t ed_time; > + int beacon_int; > + > + struct mt76_queue q_rx; > + > + spinlock_t ps_lock; > + > + u8 mac_work_count; > + > + u8 mcu_running; > + u8 ed_monitor; > + > + s8 ed_trigger; > + u8 ed_strict_mode; > + u8 ed_strong_signal; > + > + s8 sensitivity; > + > + u8 beacon_mask; > + > + u8 beacon_check; > + u8 tx_hang_check; > + u8 tx_dma_check; > + u8 rx_dma_check; > + u8 rx_pse_check; > + u8 mcu_hang; > + > + enum mt7603_reset_cause cur_reset_cause; > + > + u16 tx_dma_idx[4]; > + u16 rx_dma_idx; > + > + u32 reset_test; > + > + unsigned int reset_cause[__RESET_CAUSE_MAX]; > + > + struct delayed_work mac_work; > + struct tasklet_struct tx_tasklet; > + struct tasklet_struct pre_tbtt_tasklet; > +}; > + > +extern const struct ieee80211_ops mt7603_ops; > +extern struct pci_driver mt7603_pci_driver; > +extern struct platform_driver mt76_wmac_driver; > + > +static inline bool is_mt7603(struct mt7603_dev *dev) > +{ > + return mt76xx_chip(dev) == 0x7603; > +} > + > +static inline bool is_mt7628(struct mt7603_dev *dev) > +{ > + return mt76xx_chip(dev) == 0x7628; > +} > + > +/* need offset to prevent conflict with ampdu_ack_len */ > +#define MT_RATE_DRIVER_DATA_OFFSET 4 > + > +u32 mt7603_reg_map(struct mt7603_dev *dev, u32 addr); > + > +struct mt7603_dev *mt7603_alloc_device(struct device *pdev); > +irqreturn_t mt7603_irq_handler(int irq, void *dev_instance); > + > +int mt7603_register_device(struct mt7603_dev *dev); > +void mt7603_unregister_device(struct mt7603_dev *dev); > +int mt7603_eeprom_init(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); > +int mt7603_tx_queue_mcu(struct mt7603_dev *dev, enum mt76_txq_id qid, > + struct sk_buff *skb); > +void mt7603_mcu_rx_event(struct mt7603_dev *dev, struct sk_buff *skb); > +void mt7603_init_debugfs(struct mt7603_dev *dev); > + > +void mt7603_set_irq_mask(struct mt7603_dev *dev, u32 clear, u32 set); > + > +static inline void mt7603_irq_enable(struct mt7603_dev *dev, u32 mask) > +{ > + mt7603_set_irq_mask(dev, 0, mask); > +} > + > +static inline void mt7603_irq_disable(struct mt7603_dev *dev, u32 mask) > +{ > + mt7603_set_irq_mask(dev, mask, 0); > +} > + > +void mt7603_mac_dma_start(struct mt7603_dev *dev); > +void mt7603_mac_start(struct mt7603_dev *dev); > +void mt7603_mac_stop(struct mt7603_dev *dev); > +void mt7603_mac_work(struct work_struct *work); > +void mt7603_mac_set_timing(struct mt7603_dev *dev); > +void mt7603_beacon_set_timer(struct mt7603_dev *dev, int idx, int intval); > +int mt7603_mac_fill_rx(struct mt7603_dev *dev, struct sk_buff *skb); > +void mt7603_mac_add_txs(struct mt7603_dev *dev, void *data); > +void mt7603_mac_rx_ba_reset(struct mt7603_dev *dev, void *addr, u8 tid); > +void mt7603_mac_tx_ba_reset(struct mt7603_dev *dev, int wcid, int tid, int ssn, > + int ba_size); > + > +void mt7603_pse_client_reset(struct mt7603_dev *dev); > + > +int mt7603_mcu_set_channel(struct mt7603_dev *dev); > +int mt7603_mcu_set_eeprom(struct mt7603_dev *dev); > +void mt7603_mcu_exit(struct mt7603_dev *dev); > + > +void mt7603_wtbl_init(struct mt7603_dev *dev, int idx, int vif, > + const u8 *mac_addr); > +void mt7603_wtbl_clear(struct mt7603_dev *dev, int idx); > +void mt7603_wtbl_update_cap(struct mt7603_dev *dev, struct ieee80211_sta *sta); > +void mt7603_wtbl_set_rates(struct mt7603_dev *dev, struct mt7603_sta *sta, > + struct ieee80211_tx_rate *probe_rate, > + struct ieee80211_tx_rate *rates); > +int mt7603_wtbl_set_key(struct mt7603_dev *dev, int wcid, > + struct ieee80211_key_conf *key); > +void mt7603_wtbl_set_ps(struct mt7603_dev *dev, struct mt7603_sta *sta, > + bool enabled); > +void mt7603_wtbl_set_smps(struct mt7603_dev *dev, struct mt7603_sta *sta, > + bool enabled); > +void mt7603_filter_tx(struct mt7603_dev *dev, int idx, bool abort); > + > +int mt7603_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, > + struct sk_buff *skb, struct mt76_queue *q, > + struct mt76_wcid *wcid, struct ieee80211_sta *sta, > + u32 *tx_info); > + > +void mt7603_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue *q, > + struct mt76_queue_entry *e, bool flush); > + > +void mt7603_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, > + struct sk_buff *skb); > +void mt7603_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q); > +void mt7603_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps); > +int mt7603_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, > + struct ieee80211_sta *sta); > +void mt7603_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif, > + struct ieee80211_sta *sta); > +void mt7603_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, > + struct ieee80211_sta *sta); > + > +void mt7603_pre_tbtt_tasklet(unsigned long arg); > + > +void mt7603_update_channel(struct mt76_dev *mdev); > + > +void mt7603_edcca_set_strict(struct mt7603_dev *dev, bool val); > +void mt7603_cca_stats_reset(struct mt7603_dev *dev); > + > +#endif > diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/pci.c b/drivers/net/wireless/mediatek/mt76/mt7603/pci.c > new file mode 100644 > index 000000000000..cc740bcf1e07 > --- /dev/null > +++ b/drivers/net/wireless/mediatek/mt76/mt7603/pci.c > @@ -0,0 +1,92 @@ > +/* SPDX-License-Identifier: ISC */ > +/* > + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> > + * > + * Permission to use, copy, modify, and/or distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/pci.h> > + > +#include "mt7603.h" > + > +static const struct pci_device_id mt76pci_device_table[] = { > + { PCI_DEVICE(0x14c3, 0x7603) }, > + { }, > +}; > + > +static int > +mt76pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) > +{ > + struct mt7603_dev *dev; > + int ret; > + > + ret = pcim_enable_device(pdev); > + if (ret) > + return ret; > + > + ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); > + if (ret) > + return ret; > + > + pci_set_master(pdev); > + > + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); > + if (ret) > + return ret; > + > + dev = mt7603_alloc_device(&pdev->dev); > + if (!dev) > + return -ENOMEM; > + > + mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]); > + > + dev->mt76.rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | > + (mt76_rr(dev, MT_HW_REV) & 0xff); > + dev_info(dev->mt76.dev, "ASIC revision: %04x\n", dev->mt76.rev); > + > + ret = devm_request_irq(dev->mt76.dev, pdev->irq, mt7603_irq_handler, > + IRQF_SHARED, KBUILD_MODNAME, dev); > + if (ret) > + goto error; > + > + ret = mt7603_register_device(dev); > + if (ret) > + goto error; > + > + return 0; > +error: > + ieee80211_free_hw(mt76_hw(dev)); > + return ret; > +} > + > +static void > +mt76pci_remove(struct pci_dev *pdev) > +{ > + struct mt76_dev *mdev = pci_get_drvdata(pdev); > + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); > + > + mt7603_unregister_device(dev); > +} > + > +MODULE_DEVICE_TABLE(pci, mt76pci_device_table); > +MODULE_FIRMWARE(MT7603_FIRMWARE_E1); > +MODULE_FIRMWARE(MT7603_FIRMWARE_E2); > + > +struct pci_driver mt7603_pci_driver = { > + .name = KBUILD_MODNAME, > + .id_table = mt76pci_device_table, > + .probe = mt76pci_probe, > + .remove = mt76pci_remove, > +}; > diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/regs.h b/drivers/net/wireless/mediatek/mt76/mt7603/regs.h > new file mode 100644 > index 000000000000..5d61e222f227 > --- /dev/null > +++ b/drivers/net/wireless/mediatek/mt76/mt7603/regs.h > @@ -0,0 +1,789 @@ > +/* SPDX-License-Identifier: ISC */ > +/* > + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> > + * > + * Permission to use, copy, modify, and/or distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#ifndef __MT7603_REGS_H > +#define __MT7603_REGS_H > + > +#define MT_HW_REV 0x1000 > +#define MT_HW_CHIPID 0x1008 > +#define MT_TOP_MISC2 0x1134 > + > +#define MT_MCU_BASE 0x2000 > +#define MT_MCU(ofs) (MT_MCU_BASE + (ofs)) > + > +#define MT_MCU_PCIE_REMAP_1 MT_MCU(0x500) > +#define MT_MCU_PCIE_REMAP_1_OFFSET GENMASK(17, 0) > +#define MT_MCU_PCIE_REMAP_1_BASE GENMASK(31, 18) > + > +#define MT_MCU_PCIE_REMAP_2 MT_MCU(0x504) > +#define MT_MCU_PCIE_REMAP_2_OFFSET GENMASK(18, 0) > +#define MT_MCU_PCIE_REMAP_2_BASE GENMASK(31, 19) > + > +#define MT_HIF_BASE 0x4000 > +#define MT_HIF(ofs) (MT_HIF_BASE + (ofs)) > + > +#define MT_INT_SOURCE_CSR MT_HIF(0x200) > +#define MT_INT_MASK_CSR MT_HIF(0x204) > +#define MT_DELAY_INT_CFG MT_HIF(0x210) > + > +#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_MAC_IRQ3 BIT(27) > + > +#define MT_INT_MCU_CMD BIT(30) > + > +#define MT_WPDMA_GLO_CFG MT_HIF(0x208) > +#define MT_WPDMA_GLO_CFG_TX_DMA_EN BIT(0) > +#define MT_WPDMA_GLO_CFG_TX_DMA_BUSY BIT(1) > +#define MT_WPDMA_GLO_CFG_RX_DMA_EN BIT(2) > +#define MT_WPDMA_GLO_CFG_RX_DMA_BUSY BIT(3) > +#define MT_WPDMA_GLO_CFG_DMA_BURST_SIZE GENMASK(5, 4) > +#define MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE BIT(6) > +#define MT_WPDMA_GLO_CFG_BIG_ENDIAN BIT(7) > +#define MT_WPDMA_GLO_CFG_HDR_SEG_LEN GENMASK(15, 8) > +#define MT_WPDMA_GLO_CFG_SW_RESET BIT(24) > +#define MT_WPDMA_GLO_CFG_FORCE_TX_EOF BIT(25) > +#define MT_WPDMA_GLO_CFG_CLK_GATE_DIS BIT(30) > +#define MT_WPDMA_GLO_CFG_RX_2B_OFFSET BIT(31) > + > +#define MT_WPDMA_RST_IDX MT_HIF(0x20c) > + > +#define MT_WPDMA_DEBUG MT_HIF(0x244) > +#define MT_WPDMA_DEBUG_VALUE GENMASK(17, 0) > +#define MT_WPDMA_DEBUG_SEL BIT(27) > +#define MT_WPDMA_DEBUG_IDX GENMASK(31, 28) > + > +#define MT_TX_RING_BASE MT_HIF(0x300) > +#define MT_RX_RING_BASE MT_HIF(0x400) > + > +#define MT_TXTIME_THRESH_BASE MT_HIF(0x500) > +#define MT_TXTIME_THRESH(n) (MT_TXTIME_THRESH_BASE + ((n) * 4)) > + > +#define MT_PAGE_COUNT_BASE MT_HIF(0x540) > +#define MT_PAGE_COUNT(n) (MT_PAGE_COUNT_BASE + ((n) * 4)) > + > +#define MT_SCH_1 MT_HIF(0x588) > +#define MT_SCH_2 MT_HIF(0x58c) > +#define MT_SCH_3 MT_HIF(0x590) > + > +#define MT_SCH_4 MT_HIF(0x594) > +#define MT_SCH_4_FORCE_QID GENMASK(4, 0) > +#define MT_SCH_4_BYPASS BIT(5) > +#define MT_SCH_4_RESET BIT(8) > + > +#define MT_GROUP_THRESH_BASE MT_HIF(0x598) > +#define MT_GROUP_THRESH(n) (MT_GROUP_THRESH_BASE + ((n) * 4)) > + > +#define MT_QUEUE_PRIORITY_1 MT_HIF(0x580) > +#define MT_QUEUE_PRIORITY_2 MT_HIF(0x584) > + > +#define MT_BMAP_0 MT_HIF(0x5b0) > +#define MT_BMAP_1 MT_HIF(0x5b4) > +#define MT_BMAP_2 MT_HIF(0x5b8) > + > +#define MT_HIGH_PRIORITY_1 MT_HIF(0x5bc) > +#define MT_HIGH_PRIORITY_2 MT_HIF(0x5c0) > + > +#define MT_PRIORITY_MASK MT_HIF(0x5c4) > + > +#define MT_RSV_MAX_THRESH MT_HIF(0x5c8) > + > +#define MT_PSE_BASE 0x8000 > +#define MT_PSE(ofs) (MT_PSE_BASE + (ofs)) > + > +#define MT_MCU_DEBUG_RESET MT_PSE(0x16c) > +#define MT_MCU_DEBUG_RESET_PSE BIT(0) > +#define MT_MCU_DEBUG_RESET_PSE_S BIT(1) > +#define MT_MCU_DEBUG_RESET_QUEUES GENMASK(6, 2) > + > +#define MT_PSE_FC_P0 MT_PSE(0x120) > +#define MT_PSE_FC_P0_MIN_RESERVE GENMASK(11, 0) > +#define MT_PSE_FC_P0_MAX_QUOTA GENMASK(27, 16) > + > +#define MT_PSE_FRP MT_PSE(0x138) > +#define MT_PSE_FRP_P0 GENMASK(2, 0) > +#define MT_PSE_FRP_P1 GENMASK(5, 3) > +#define MT_PSE_FRP_P2_RQ0 GENMASK(8, 6) > +#define MT_PSE_FRP_P2_RQ1 GENMASK(11, 9) > +#define MT_PSE_FRP_P2_RQ2 GENMASK(14, 12) > + > +#define MT_FC_RSV_COUNT_0 MT_PSE(0x13c) > +#define MT_FC_RSV_COUNT_0_P0 GENMASK(11, 0) > +#define MT_FC_RSV_COUNT_0_P1 GENMASK(27, 16) > + > +#define MT_FC_SP2_Q0Q1 MT_PSE(0x14c) > +#define MT_FC_SP2_Q0Q1_SRC_COUNT_Q0 GENMASK(11, 0) > +#define MT_FC_SP2_Q0Q1_SRC_COUNT_Q1 GENMASK(27, 16) > + > +#define MT_PSE_FW_SHARED MT_PSE(0x17c) > + > +#define MT_PSE_RTA MT_PSE(0x194) > +#define MT_PSE_RTA_QUEUE_ID GENMASK(4, 0) > +#define MT_PSE_RTA_PORT_ID GENMASK(6, 5) > +#define MT_PSE_RTA_REDIRECT_EN BIT(7) > +#define MT_PSE_RTA_TAG_ID GENMASK(15, 8) > +#define MT_PSE_RTA_WRITE BIT(16) > +#define MT_PSE_RTA_BUSY BIT(31) > + > +#define MT_WF_PHY_BASE 0x10000 > +#define MT_WF_PHY_OFFSET 0x1000 > +#define MT_WF_PHY(ofs) (MT_WF_PHY_BASE + (ofs)) > + > +#define MT_AGC_BASE MT_WF_PHY(0x500) > +#define MT_AGC(n) (MT_AGC_BASE + ((n) * 4)) > + > +#define MT_AGC1_BASE MT_WF_PHY(0x1500) > +#define MT_AGC1(n) (MT_AGC1_BASE + ((n) * 4)) > + > +#define MT_AGC_41_RSSI_0 GENMASK(23, 16) > +#define MT_AGC_41_RSSI_1 GENMASK(7, 0) > + > +#define MT_RXTD_BASE MT_WF_PHY(0x600) > +#define MT_RXTD(n) (MT_RXTD_BASE + ((n) * 4)) > + > +#define MT_RXTD_6_ACI_TH GENMASK(4, 0) > +#define MT_RXTD_6_CCAED_TH GENMASK(14, 8) > + > +#define MT_RXTD_8_LOWER_SIGNAL GENMASK(5, 0) > + > +#define MT_RXTD_13_ACI_TH_EN BIT(0) > + > +#define MT_WF_PHY_CR_TSSI_BASE MT_WF_PHY(0xd00) > +#define MT_WF_PHY_CR_TSSI(phy, n) (MT_WF_PHY_CR_TSSI_BASE + \ > + ((phy) * MT_WF_PHY_OFFSET) + \ > + ((n) * 4)) > + > +#define MT_PHYCTRL_BASE MT_WF_PHY(0x4100) > +#define MT_PHYCTRL(n) (MT_PHYCTRL_BASE + ((n) * 4)) > + > +#define MT_PHYCTRL_2_STATUS_RESET BIT(6) > +#define MT_PHYCTRL_2_STATUS_EN BIT(7) > + > +#define MT_PHYCTRL_STAT_PD MT_PHYCTRL(3) > +#define MT_PHYCTRL_STAT_PD_OFDM GENMASK(31, 16) > +#define MT_PHYCTRL_STAT_PD_CCK GENMASK(15, 0) > + > +#define MT_PHYCTRL_STAT_MDRDY MT_PHYCTRL(8) > +#define MT_PHYCTRL_STAT_MDRDY_OFDM GENMASK(31, 16) > +#define MT_PHYCTRL_STAT_MDRDY_CCK GENMASK(15, 0) > + > +#define MT_WF_AGG_BASE 0x21200 > +#define MT_WF_AGG(ofs) (MT_WF_AGG_BASE + (ofs)) > + > +#define MT_AGG_ARCR MT_WF_AGG(0x010) > +#define MT_AGG_ARCR_INIT_RATE1 BIT(0) > +#define MT_AGG_ARCR_FB_SGI_DISABLE BIT(1) > +#define MT_AGG_ARCR_RATE8_DOWN_WRAP BIT(2) > +#define MT_AGG_ARCR_RTS_RATE_THR GENMASK(12, 8) > +#define MT_AGG_ARCR_RATE_DOWN_RATIO GENMASK(17, 16) > +#define MT_AGG_ARCR_RATE_DOWN_RATIO_EN BIT(19) > +#define MT_AGG_ARCR_RATE_UP_EXTRA_TH GENMASK(22, 20) > +#define MT_AGG_ARCR_SPE_DIS_TH GENMASK(27, 24) > + > +#define MT_AGG_ARUCR MT_WF_AGG(0x014) > +#define MT_AGG_ARDCR MT_WF_AGG(0x018) > +#define MT_AGG_ARxCR_LIMIT_SHIFT(_n) (4 * (_n)) > +#define MT_AGG_ARxCR_LIMIT(_n) GENMASK(2 + \ > + MT_AGG_ARxCR_LIMIT_SHIFT(_n), \ > + MT_AGG_ARxCR_LIMIT_SHIFT(_n)) > + > +#define MT_AGG_LIMIT MT_WF_AGG(0x040) > +#define MT_AGG_LIMIT_1 MT_WF_AGG(0x044) > +#define MT_AGG_LIMIT_AC(_n) GENMASK(((_n) + 1) * 8 - 1, (_n) * 8) > + > +#define MT_AGG_BA_SIZE_LIMIT_0 MT_WF_AGG(0x048) > +#define MT_AGG_BA_SIZE_LIMIT_1 MT_WF_AGG(0x04c) > +#define MT_AGG_BA_SIZE_LIMIT_SHIFT 8 > + > +#define MT_AGG_PCR MT_WF_AGG(0x050) > +#define MT_AGG_PCR_MM BIT(16) > +#define MT_AGG_PCR_GF BIT(17) > +#define MT_AGG_PCR_BW40 BIT(18) > +#define MT_AGG_PCR_RIFS BIT(19) > +#define MT_AGG_PCR_BW80 BIT(20) > +#define MT_AGG_PCR_BW160 BIT(21) > +#define MT_AGG_PCR_ERP BIT(22) > + > +#define MT_AGG_PCR_RTS MT_WF_AGG(0x054) > +#define MT_AGG_PCR_RTS_THR GENMASK(19, 0) > +#define MT_AGG_PCR_RTS_PKT_THR GENMASK(31, 25) > + > +#define MT_AGG_CONTROL MT_WF_AGG(0x070) > +#define MT_AGG_CONTROL_NO_BA_RULE BIT(0) > +#define MT_AGG_CONTROL_NO_BA_AR_RULE BIT(1) > +#define MT_AGG_CONTROL_CFEND_SPE_EN BIT(3) > +#define MT_AGG_CONTROL_CFEND_RATE GENMASK(15, 4) > +#define MT_AGG_CONTROL_BAR_SPE_EN BIT(19) > +#define MT_AGG_CONTROL_BAR_RATE GENMASK(31, 20) > + > +#define MT_AGG_TMP MT_WF_AGG(0x0d8) > + > +#define MT_AGG_BWCR MT_WF_AGG(0x0ec) > +#define MT_AGG_BWCR_BW GENMASK(3, 2) > + > +#define MT_AGG_RETRY_CONTROL MT_WF_AGG(0x0f4) > +#define MT_AGG_RETRY_CONTROL_RTS_LIMIT GENMASK(11, 7) > +#define MT_AGG_RETRY_CONTROL_BAR_LIMIT GENMASK(15, 12) > + > +#define MT_WF_DMA_BASE 0x21c00 > +#define MT_WF_DMA(ofs) (MT_WF_DMA_BASE + (ofs)) > + > +#define MT_DMA_DCR0 MT_WF_DMA(0x000) > +#define MT_DMA_DCR1 MT_WF_DMA(0x004) > + > +#define MT_DMA_FQCR0 MT_WF_DMA(0x008) > +#define MT_DMA_FQCR0_TARGET_WCID GENMASK(7, 0) > +#define MT_DMA_FQCR0_TARGET_BSS GENMASK(13, 8) > +#define MT_DMA_FQCR0_TARGET_QID GENMASK(20, 16) > +#define MT_DMA_FQCR0_DEST_PORT_ID GENMASK(23, 22) > +#define MT_DMA_FQCR0_DEST_QUEUE_ID GENMASK(28, 24) > +#define MT_DMA_FQCR0_MODE BIT(29) > +#define MT_DMA_FQCR0_STATUS BIT(30) > +#define MT_DMA_FQCR0_BUSY BIT(31) > + > +#define MT_DMA_RCFR0 MT_WF_DMA(0x070) > +#define MT_DMA_VCFR0 MT_WF_DMA(0x07c) > + > +#define MT_DMA_TCFR0 MT_WF_DMA(0x080) > +#define MT_DMA_TCFR1 MT_WF_DMA(0x084) > +#define MT_DMA_TCFR_TXS_AGGR_TIMEOUT GENMASK(27, 16) > +#define MT_DMA_TCFR_TXS_QUEUE BIT(14) > +#define MT_DMA_TCFR_TXS_AGGR_COUNT GENMASK(12, 8) > +#define MT_DMA_TCFR_TXS_BIT_MAP GENMASK(6, 0) > + > +#define MT_DMA_TMCFR0 MT_WF_DMA(0x088) > + > +#define MT_WF_ARB_BASE 0x21400 > +#define MT_WF_ARB(ofs) (MT_WF_ARB_BASE + (ofs)) > + > +#define MT_WMM_AIFSN MT_WF_ARB(0x020) > +#define MT_WMM_AIFSN_MASK GENMASK(3, 0) > +#define MT_WMM_AIFSN_SHIFT(_n) ((_n) * 4) > + > +#define MT_WMM_CWMAX_BASE MT_WF_ARB(0x028) > +#define MT_WMM_CWMAX(_n) (MT_WMM_CWMAX_BASE + (((_n) / 2) << 2)) > +#define MT_WMM_CWMAX_SHIFT(_n) (((_n) & 1) * 16) > +#define MT_WMM_CWMAX_MASK GENMASK(15, 0) > + > +#define MT_WMM_CWMIN MT_WF_ARB(0x040) > +#define MT_WMM_CWMIN_MASK GENMASK(7, 0) > +#define MT_WMM_CWMIN_SHIFT(_n) ((_n) * 8) > + > +#define MT_WF_ARB_RQCR MT_WF_ARB(0x070) > +#define MT_WF_ARB_RQCR_RX_START BIT(0) > +#define MT_WF_ARB_RQCR_RXV_START BIT(4) > +#define MT_WF_ARB_RQCR_RXV_R_EN BIT(7) > +#define MT_WF_ARB_RQCR_RXV_T_EN BIT(8) > + > +#define MT_ARB_SCR MT_WF_ARB(0x080) > +#define MT_ARB_SCR_BCNQ_OPMODE_MASK GENMASK(1, 0) > +#define MT_ARB_SCR_BCNQ_OPMODE_SHIFT(n) ((n) * 2) > +#define MT_ARB_SCR_TX_DISABLE BIT(8) > +#define MT_ARB_SCR_RX_DISABLE BIT(9) > +#define MT_ARB_SCR_BCNQ_EMPTY_SKIP BIT(28) > +#define MT_ARB_SCR_TTTT_BTIM_PRIO BIT(29) > +#define MT_ARB_SCR_TBTT_BCN_PRIO BIT(30) > +#define MT_ARB_SCR_TBTT_BCAST_PRIO BIT(31) > + > +enum { > + MT_BCNQ_OPMODE_STA = 0, > + MT_BCNQ_OPMODE_AP = 1, > + MT_BCNQ_OPMODE_ADHOC = 2, > +}; > + > +#define MT_WF_ARB_TX_START_0 MT_WF_ARB(0x100) > +#define MT_WF_ARB_TX_START_1 MT_WF_ARB(0x104) > +#define MT_WF_ARB_TX_FLUSH_0 MT_WF_ARB(0x108) > +#define MT_WF_ARB_TX_FLUSH_1 MT_WF_ARB(0x10c) > +#define MT_WF_ARB_TX_STOP_0 MT_WF_ARB(0x110) > +#define MT_WF_ARB_TX_STOP_1 MT_WF_ARB(0x114) > + > +#define MT_WF_ARB_BCN_START MT_WF_ARB(0x118) > +#define MT_WF_ARB_BCN_START_BSSn(n) BIT(0 + (n)) > +#define MT_WF_ARB_BCN_START_T_PRE_TTTT BIT(10) > +#define MT_WF_ARB_BCN_START_T_TTTT BIT(11) > +#define MT_WF_ARB_BCN_START_T_PRE_TBTT BIT(12) > +#define MT_WF_ARB_BCN_START_T_TBTT BIT(13) > +#define MT_WF_ARB_BCN_START_T_SLOT_IDLE BIT(14) > +#define MT_WF_ARB_BCN_START_T_TX_START BIT(15) > +#define MT_WF_ARB_BCN_START_BSS0n(n) BIT((n) ? 16 + ((n) - 1) : 0) > + > +#define MT_WF_ARB_BCN_FLUSH MT_WF_ARB(0x11c) > +#define MT_WF_ARB_BCN_FLUSH_BSSn(n) BIT(0 + (n)) > +#define MT_WF_ARB_BCN_FLUSH_BSS0n(n) BIT((n) ? 16 + ((n) - 1) : 0) > + > +#define MT_WF_ARB_CAB_START MT_WF_ARB(0x120) > +#define MT_WF_ARB_CAB_START_BSSn(n) BIT(0 + (n)) > +#define MT_WF_ARB_CAB_START_BSS0n(n) BIT((n) ? 16 + ((n) - 1) : 0) > + > +#define MT_WF_ARB_CAB_FLUSH MT_WF_ARB(0x124) > +#define MT_WF_ARB_CAB_FLUSH_BSSn(n) BIT(0 + (n)) > +#define MT_WF_ARB_CAB_FLUSH_BSS0n(n) BIT((n) ? 16 + ((n) - 1) : 0) > + > +#define MT_WF_ARB_CAB_COUNT(n) MT_WF_ARB(0x128 + (n) * 4) > +#define MT_WF_ARB_CAB_COUNT_SHIFT 4 > +#define MT_WF_ARB_CAB_COUNT_MASK GENMASK(3, 0) > +#define MT_WF_ARB_CAB_COUNT_B0_REG(n) MT_WF_ARB_CAB_COUNT(((n) > 12 ? 2 : \ > + ((n) > 4 ? 1 : 0))) > +#define MT_WF_ARB_CAB_COUNT_B0_SHIFT(n) (((n) > 12 ? (n) - 12 : \ > + ((n) > 4 ? (n) - 4 : \ > + (n) ? (n) + 3 : 0)) * 4) > + > +#define MT_TX_ABORT MT_WF_ARB(0x134) > +#define MT_TX_ABORT_EN BIT(0) > +#define MT_TX_ABORT_WCID GENMASK(15, 8) > + > +#define MT_WF_TMAC_BASE 0x21600 > +#define MT_WF_TMAC(ofs) (MT_WF_TMAC_BASE + (ofs)) > + > +#define MT_TMAC_TCR MT_WF_TMAC(0x000) > +#define MT_TMAC_TCR_BLINK_SEL GENMASK(7, 6) > +#define MT_TMAC_TCR_PRE_RTS_GUARD GENMASK(11, 8) > +#define MT_TMAC_TCR_PRE_RTS_SEC_IDLE GENMASK(13, 12) > +#define MT_TMAC_TCR_RTS_SIGTA BIT(14) > +#define MT_TMAC_TCR_LDPC_OFS BIT(15) > +#define MT_TMAC_TCR_TX_STREAMS GENMASK(17, 16) > +#define MT_TMAC_TCR_SCH_IDLE_SEL GENMASK(19, 18) > +#define MT_TMAC_TCR_SCH_DET_PER_IOD BIT(20) > +#define MT_TMAC_TCR_DCH_DET_DISABLE BIT(21) > +#define MT_TMAC_TCR_TX_RIFS BIT(22) > +#define MT_TMAC_TCR_RX_RIFS_MODE BIT(23) > +#define MT_TMAC_TCR_TXOP_TBTT_CTL BIT(24) > +#define MT_TMAC_TCR_TBTT_TX_STOP_CTL BIT(25) > +#define MT_TMAC_TCR_TXOP_BURST_STOP BIT(26) > +#define MT_TMAC_TCR_RDG_RA_MODE BIT(27) > +#define MT_TMAC_TCR_RDG_RESP BIT(29) > +#define MT_TMAC_TCR_RDG_NO_PENDING BIT(30) > +#define MT_TMAC_TCR_SMOOTHING BIT(31) > + > +#define MT_WMM_TXOP_BASE MT_WF_TMAC(0x010) > +#define MT_WMM_TXOP(_n) (MT_WMM_TXOP_BASE + \ > + ((((_n) / 2) ^ 0x1) << 2)) > +#define MT_WMM_TXOP_SHIFT(_n) (((_n) & 1) * 16) > +#define MT_WMM_TXOP_MASK GENMASK(15, 0) > + > +#define MT_TIMEOUT_CCK MT_WF_TMAC(0x090) > +#define MT_TIMEOUT_OFDM MT_WF_TMAC(0x094) > +#define MT_TIMEOUT_VAL_PLCP GENMASK(15, 0) > +#define MT_TIMEOUT_VAL_CCA GENMASK(31, 16) > + > +#define MT_TXREQ MT_WF_TMAC(0x09c) > +#define MT_TXREQ_CCA_SRC_SEL GENMASK(31, 30) > + > +#define MT_RXREQ MT_WF_TMAC(0x0a0) > +#define MT_RXREQ_DELAY GENMASK(8, 0) > + > +#define MT_IFS MT_WF_TMAC(0x0a4) > +#define MT_IFS_EIFS GENMASK(8, 0) > +#define MT_IFS_RIFS GENMASK(14, 10) > +#define MT_IFS_SIFS GENMASK(22, 16) > +#define MT_IFS_SLOT GENMASK(30, 24) > + > +#define MT_TMAC_PCR MT_WF_TMAC(0x0b4) > +#define MT_TMAC_PCR_RATE GENMASK(8, 0) > +#define MT_TMAC_PCR_RATE_FIXED BIT(15) > +#define MT_TMAC_PCR_ANT_ID GENMASK(21, 16) > +#define MT_TMAC_PCR_ANT_ID_SEL BIT(22) > +#define MT_TMAC_PCR_SPE_EN BIT(23) > +#define MT_TMAC_PCR_ANT_PRI GENMASK(26, 24) > +#define MT_TMAC_PCR_ANT_PRI_SEL GENMASK(27) > + > +#define MT_WF_RMAC_BASE 0x21800 > +#define MT_WF_RMAC(ofs) (MT_WF_RMAC_BASE + (ofs)) > + > +#define MT_WF_RFCR MT_WF_RMAC(0x000) > +#define MT_WF_RFCR_DROP_STBC_MULTI BIT(0) > +#define MT_WF_RFCR_DROP_FCSFAIL BIT(1) > +#define MT_WF_RFCR_DROP_VERSION BIT(3) > +#define MT_WF_RFCR_DROP_PROBEREQ BIT(4) > +#define MT_WF_RFCR_DROP_MCAST BIT(5) > +#define MT_WF_RFCR_DROP_BCAST BIT(6) > +#define MT_WF_RFCR_DROP_MCAST_FILTERED BIT(7) > +#define MT_WF_RFCR_DROP_A3_MAC BIT(8) > +#define MT_WF_RFCR_DROP_A3_BSSID BIT(9) > +#define MT_WF_RFCR_DROP_A2_BSSID BIT(10) > +#define MT_WF_RFCR_DROP_OTHER_BEACON BIT(11) > +#define MT_WF_RFCR_DROP_FRAME_REPORT BIT(12) > +#define MT_WF_RFCR_DROP_CTL_RSV BIT(13) > +#define MT_WF_RFCR_DROP_CTS BIT(14) > +#define MT_WF_RFCR_DROP_RTS BIT(15) > +#define MT_WF_RFCR_DROP_DUPLICATE BIT(16) > +#define MT_WF_RFCR_DROP_OTHER_BSS BIT(17) > +#define MT_WF_RFCR_DROP_OTHER_UC BIT(18) > +#define MT_WF_RFCR_DROP_OTHER_TIM BIT(19) > +#define MT_WF_RFCR_DROP_NDPA BIT(20) > +#define MT_WF_RFCR_DROP_UNWANTED_CTL BIT(21) > + > +#define MT_BSSID0(idx) MT_WF_RMAC(0x004 + (idx) * 8) > +#define MT_BSSID1(idx) MT_WF_RMAC(0x008 + (idx) * 8) > +#define MT_BSSID1_VALID BIT(16) > + > +#define MT_MAC_ADDR0(idx) MT_WF_RMAC(0x024 + (idx) * 8) > +#define MT_MAC_ADDR1(idx) MT_WF_RMAC(0x028 + (idx) * 8) > +#define MT_MAC_ADDR1_ADDR GENMASK(15, 0) > +#define MT_MAC_ADDR1_VALID BIT(16) > + > +#define MT_BA_CONTROL_0 MT_WF_RMAC(0x068) > +#define MT_BA_CONTROL_1 MT_WF_RMAC(0x06c) > +#define MT_BA_CONTROL_1_ADDR GENMASK(15, 0) > +#define MT_BA_CONTROL_1_TID GENMASK(19, 16) > +#define MT_BA_CONTROL_1_IGNORE_TID BIT(20) > +#define MT_BA_CONTROL_1_IGNORE_ALL BIT(21) > +#define MT_BA_CONTROL_1_RESET BIT(22) > + > +#define MT_WF_RMACDR MT_WF_RMAC(0x078) > +#define MT_WF_RMACDR_TSF_PROBERSP_DIS BIT(0) > +#define MT_WF_RMACDR_TSF_TIM BIT(4) > +#define MT_WF_RMACDR_MBSSID_MASK GENMASK(25, 24) > +#define MT_WF_RMACDR_CHECK_HTC_BY_RATE BIT(26) > +#define MT_WF_RMACDR_MAXLEN_20BIT BIT(30) > + > +#define MT_WF_RMAC_RMCR MT_WF_RMAC(0x080) > +#define MT_WF_RMAC_RMCR_SMPS_MODE GENMASK(21, 20) > +#define MT_WF_RMAC_RMCR_RX_STREAMS GENMASK(24, 22) > +#define MT_WF_RMAC_RMCR_SMPS_RTS BIT(25) > + > +#define MT_WF_RMAC_CH_FREQ MT_WF_RMAC(0x090) > +#define MT_WF_RMAC_MAXMINLEN MT_WF_RMAC(0x098) > +#define MT_WF_RFCR1 MT_WF_RMAC(0x0a4) > +#define MT_WF_RMAC_TMR_PA MT_WF_RMAC(0x0e0) > + > +#define MT_WF_SEC_BASE 0x21a00 > +#define MT_WF_SEC(ofs) (MT_WF_SEC_BASE + (ofs)) > + > +#define MT_SEC_SCR MT_WF_SEC(0x004) > +#define MT_SEC_SCR_MASK_ORDER GENMASK(1, 0) > + > +#define MT_WTBL_OFF_BASE 0x23000 > +#define MT_WTBL_OFF(n) (MT_WTBL_OFF_BASE + (n)) > + > +#define MT_WTBL_UPDATE MT_WTBL_OFF(0x000) > +#define MT_WTBL_UPDATE_WLAN_IDX GENMASK(7, 0) > +#define MT_WTBL_UPDATE_WTBL2 BIT(11) > +#define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(12) > +#define MT_WTBL_UPDATE_RATE_UPDATE BIT(13) > +#define MT_WTBL_UPDATE_TX_COUNT_CLEAR BIT(14) > +#define MT_WTBL_UPDATE_RX_COUNT_CLEAR BIT(15) > +#define MT_WTBL_UPDATE_BUSY BIT(16) > + > +#define MT_WTBL_RMVTCR MT_WTBL_OFF(0x008) > +#define MT_WTBL_RMVTCR_RX_MV_MODE BIT(23) > + > +#define MT_LPON_BASE 0x24000 > +#define MT_LPON(n) (MT_LPON_BASE + (n)) > + > +#define MT_LPON_BTEIR MT_LPON(0x020) > +#define MT_LPON_BTEIR_MBSS_MODE GENMASK(31, 29) > + > +#define MT_PRE_TBTT MT_LPON(0x030) > +#define MT_PRE_TBTT_MASK GENMASK(7, 0) > +#define MT_PRE_TBTT_SHIFT 8 > + > +#define MT_TBTT MT_LPON(0x034) > +#define MT_TBTT_PERIOD GENMASK(15, 0) > +#define MT_TBTT_DTIM_PERIOD GENMASK(23, 16) > +#define MT_TBTT_TBTT_WAKE_PERIOD GENMASK(27, 24) > +#define MT_TBTT_DTIM_WAKE_PERIOD GENMASK(30, 28) > +#define MT_TBTT_CAL_ENABLE BIT(31) > + > +#define MT_TBTT_TIMER_CFG MT_LPON(0x05c) > + > +#define MT_LPON_SBTOR(n) MT_LPON(0x0a0) > +#define MT_LPON_SBTOR_SUB_BSS_EN BIT(29) > +#define MT_LPON_SBTOR_TIME_OFFSET GENMASK(19, 0) > + > +#define MT_INT_WAKEUP_BASE 0x24400 > +#define MT_INT_WAKEUP(n) (MT_INT_WAKEUP_BASE + (n)) > + > +#define MT_HW_INT_STATUS(n) MT_INT_WAKEUP(0x3c + (n) * 8) > +#define MT_HW_INT_MASK(n) MT_INT_WAKEUP(0x40 + (n) * 8) > + > +#define MT_HW_INT3_TBTT0 BIT(15) > +#define MT_HW_INT3_PRE_TBTT0 BIT(31) > + > +#define MT_WTBL1_BASE 0x28000 > + > +#define MT_WTBL_ON_BASE (MT_WTBL1_BASE + 0x2000) > +#define MT_WTBL_ON(_n) (MT_WTBL_ON_BASE + (_n)) > + > +#define MT_WTBL_RIUCR0 MT_WTBL_ON(0x200) > + > +#define MT_WTBL_RIUCR1 MT_WTBL_ON(0x204) > +#define MT_WTBL_RIUCR1_RATE0 GENMASK(11, 0) > +#define MT_WTBL_RIUCR1_RATE1 GENMASK(23, 12) > +#define MT_WTBL_RIUCR1_RATE2_LO GENMASK(31, 24) > + > +#define MT_WTBL_RIUCR2 MT_WTBL_ON(0x208) > +#define MT_WTBL_RIUCR2_RATE2_HI GENMASK(3, 0) > +#define MT_WTBL_RIUCR2_RATE3 GENMASK(15, 4) > +#define MT_WTBL_RIUCR2_RATE4 GENMASK(27, 16) > +#define MT_WTBL_RIUCR2_RATE5_LO GENMASK(31, 28) > + > +#define MT_WTBL_RIUCR3 MT_WTBL_ON(0x20c) > +#define MT_WTBL_RIUCR3_RATE5_HI GENMASK(7, 0) > +#define MT_WTBL_RIUCR3_RATE6 GENMASK(19, 8) > +#define MT_WTBL_RIUCR3_RATE7 GENMASK(31, 20) > + > +#define MT_MIB_BASE 0x2c000 > +#define MT_MIB(_n) (MT_MIB_BASE + (_n)) > + > +#define MT_MIB_CTL MT_MIB(0x00) > +#define MT_MIB_CTL_PSCCA_TIME GENMASK(13, 11) > +#define MT_MIB_CTL_CCA_NAV_TX GENMASK(16, 14) > +#define MT_MIB_CTL_ED_TIME GENMASK(30, 28) > +#define MT_MIB_CTL_READ_CLR_DIS BIT(31) > + > +#define MT_MIB_STAT(_n) MT_MIB(0x08 + (_n) * 4) > + > +#define MT_MIB_STAT_CCA MT_MIB_STAT(9) > +#define MT_MIB_STAT_CCA_MASK GENMASK(23, 0) > + > +#define MT_MIB_STAT_PSCCA MT_MIB_STAT(16) > +#define MT_MIB_STAT_PSCCA_MASK GENMASK(23, 0) > + > +#define MT_MIB_STAT_ED MT_MIB_STAT(18) > +#define MT_MIB_STAT_ED_MASK GENMASK(23, 0) > + > +#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 > + > +#define MT_LED_BASE_PHYS 0x80024000 > +#define MT_LED_PHYS(_n) (MT_LED_BASE_PHYS + (_n)) > + > +#define MT_LED_CTRL MT_LED_PHYS(0x00) > + > +#define MT_LED_CTRL_REPLAY(_n) BIT(0 + (8 * (_n))) > +#define MT_LED_CTRL_POLARITY(_n) BIT(1 + (8 * (_n))) > +#define MT_LED_CTRL_TX_BLINK_MODE(_n) BIT(2 + (8 * (_n))) > +#define MT_LED_CTRL_TX_MANUAL_BLINK(_n) BIT(3 + (8 * (_n))) > +#define MT_LED_CTRL_TX_OVER_BLINK(_n) BIT(5 + (8 * (_n))) > +#define MT_LED_CTRL_KICK(_n) BIT(7 + (8 * (_n))) > + > +#define MT_LED_STATUS_0(_n) MT_LED_PHYS(0x10 + ((_n) * 8)) > +#define MT_LED_STATUS_1(_n) MT_LED_PHYS(0x14 + ((_n) * 8)) > +#define MT_LED_STATUS_OFF_MASK GENMASK(31, 24) > +#define MT_LED_STATUS_OFF(_v) (((_v) << \ > + __ffs(MT_LED_STATUS_OFF_MASK)) & \ > + MT_LED_STATUS_OFF_MASK) > +#define MT_LED_STATUS_ON_MASK GENMASK(23, 16) > +#define MT_LED_STATUS_ON(_v) (((_v) << \ > + __ffs(MT_LED_STATUS_ON_MASK)) & \ > + MT_LED_STATUS_ON_MASK) > +#define MT_LED_STATUS_DURATION_MASK GENMASK(15, 0) > +#define MT_LED_STATUS_DURATION(_v) (((_v) << \ > + __ffs(MT_LED_STATUS_DURATION_MASK)) &\ > + MT_LED_STATUS_DURATION_MASK) > + > +#define MT_CLIENT_BASE_PHYS_ADDR 0x800c0000 > + > +#define MT_CLIENT_TMAC_INFO_TEMPLATE 0x040 > + > +#define MT_CLIENT_STATUS 0x06c > + > +#define MT_CLIENT_RESET_TX 0x070 > +#define MT_CLIENT_RESET_TX_R_E_1 BIT(16) > +#define MT_CLIENT_RESET_TX_R_E_2 BIT(17) > +#define MT_CLIENT_RESET_TX_R_E_1_S BIT(20) > +#define MT_CLIENT_RESET_TX_R_E_2_S BIT(21) > + > +#define MT_EFUSE_BASE 0x81070000 > + > +#define MT_EFUSE_BASE_CTRL 0x000 > +#define MT_EFUSE_BASE_CTRL_EMPTY BIT(30) > + > +#define MT_EFUSE_CTRL 0x008 > +#define MT_EFUSE_CTRL_AOUT GENMASK(5, 0) > +#define MT_EFUSE_CTRL_MODE GENMASK(7, 6) > +#define MT_EFUSE_CTRL_LDO_OFF_TIME GENMASK(13, 8) > +#define MT_EFUSE_CTRL_LDO_ON_TIME GENMASK(15, 14) > +#define MT_EFUSE_CTRL_AIN GENMASK(25, 16) > +#define MT_EFUSE_CTRL_VALID BIT(29) > +#define MT_EFUSE_CTRL_KICK BIT(30) > +#define MT_EFUSE_CTRL_SEL BIT(31) > + > +#define MT_EFUSE_WDATA(_i) (0x010 + ((_i) * 4)) > +#define MT_EFUSE_RDATA(_i) (0x030 + ((_i) * 4)) > + > +#define MT_CLIENT_RXINF 0x068 > +#define MT_CLIENT_RXINF_RXSH_GROUPS GENMASK(2, 0) > + > +#define MT_PSE_BASE_PHYS_ADDR 0xa0000000 > + > +#define MT_PSE_WTBL_2_PHYS_ADDR 0xa5000000 > + > +#define MT_WTBL1_SIZE (8 * 4) > +#define MT_WTBL2_SIZE (16 * 4) > +#define MT_WTBL3_OFFSET (MT7603_WTBL_SIZE * MT_WTBL2_SIZE) > +#define MT_WTBL3_SIZE (16 * 4) > +#define MT_WTBL4_OFFSET (MT7603_WTBL_SIZE * MT_WTBL3_SIZE + \ > + MT_WTBL3_OFFSET) > +#define MT_WTBL4_SIZE (8 * 4) > + > +#define MT_WTBL1_W0_ADDR_HI GENMASK(15, 0) > +#define MT_WTBL1_W0_MUAR_IDX GENMASK(21, 16) > +#define MT_WTBL1_W0_RX_CHECK_A1 BIT(22) > +#define MT_WTBL1_W0_KEY_IDX GENMASK(24, 23) > +#define MT_WTBL1_W0_RX_CHECK_KEY_IDX BIT(25) > +#define MT_WTBL1_W0_RX_KEY_VALID BIT(26) > +#define MT_WTBL1_W0_RX_IK_VALID BIT(27) > +#define MT_WTBL1_W0_RX_VALID BIT(28) > +#define MT_WTBL1_W0_RX_CHECK_A2 BIT(29) > +#define MT_WTBL1_W0_RX_DATA_VALID BIT(30) > +#define MT_WTBL1_W0_WRITE_BURST BIT(31) > + > +#define MT_WTBL1_W1_ADDR_LO GENMASK(31, 0) > + > +#define MT_WTBL1_W2_MPDU_DENSITY GENMASK(2, 0) > +#define MT_WTBL1_W2_KEY_TYPE GENMASK(6, 3) > +#define MT_WTBL1_W2_EVEN_PN BIT(7) > +#define MT_WTBL1_W2_TO_DS BIT(8) > +#define MT_WTBL1_W2_FROM_DS BIT(9) > +#define MT_WTBL1_W2_HEADER_TRANS BIT(10) > +#define MT_WTBL1_W2_AMPDU_FACTOR GENMASK(13, 11) > +#define MT_WTBL1_W2_PWR_MGMT BIT(14) > +#define MT_WTBL1_W2_RDG BIT(15) > +#define MT_WTBL1_W2_RTS BIT(16) > +#define MT_WTBL1_W2_CFACK BIT(17) > +#define MT_WTBL1_W2_RDG_BA BIT(18) > +#define MT_WTBL1_W2_SMPS BIT(19) > +#define MT_WTBL1_W2_TXS_BAF_REPORT BIT(20) > +#define MT_WTBL1_W2_DYN_BW BIT(21) > +#define MT_WTBL1_W2_LDPC BIT(22) > +#define MT_WTBL1_W2_ITXBF BIT(23) > +#define MT_WTBL1_W2_ETXBF BIT(24) > +#define MT_WTBL1_W2_TXOP_PS BIT(25) > +#define MT_WTBL1_W2_MESH BIT(26) > +#define MT_WTBL1_W2_QOS BIT(27) > +#define MT_WTBL1_W2_HT BIT(28) > +#define MT_WTBL1_W2_VHT BIT(29) > +#define MT_WTBL1_W2_ADMISSION_CONTROL BIT(30) > +#define MT_WTBL1_W2_GROUP_ID BIT(31) > + > +#define MT_WTBL1_W3_WTBL2_FRAME_ID GENMASK(10, 0) > +#define MT_WTBL1_W3_WTBL2_ENTRY_ID GENMASK(15, 11) > +#define MT_WTBL1_W3_WTBL4_FRAME_ID GENMASK(26, 16) > +#define MT_WTBL1_W3_CHECK_PER BIT(27) > +#define MT_WTBL1_W3_KEEP_I_PSM BIT(28) > +#define MT_WTBL1_W3_I_PSM BIT(29) > +#define MT_WTBL1_W3_POWER_SAVE BIT(30) > +#define MT_WTBL1_W3_SKIP_TX BIT(31) > + > +#define MT_WTBL1_W4_WTBL3_FRAME_ID GENMASK(10, 0) > +#define MT_WTBL1_W4_WTBL3_ENTRY_ID GENMASK(16, 11) > +#define MT_WTBL1_W4_WTBL4_ENTRY_ID GENMASK(22, 17) > +#define MT_WTBL1_W4_PARTIAL_AID GENMASK(31, 23) > + > +#define MT_WTBL2_W0_PN_LO GENMASK(31, 0) > + > +#define MT_WTBL2_W1_PN_HI GENMASK(15, 0) > +#define MT_WTBL2_W1_NON_QOS_SEQNO GENMASK(27, 16) > + > +#define MT_WTBL2_W2_TID0_SN GENMASK(11, 0) > +#define MT_WTBL2_W2_TID1_SN GENMASK(23, 12) > +#define MT_WTBL2_W2_TID2_SN_LO GENMASK(31, 24) > + > +#define MT_WTBL2_W3_TID2_SN_HI GENMASK(3, 0) > +#define MT_WTBL2_W3_TID3_SN GENMASK(15, 4) > +#define MT_WTBL2_W3_TID4_SN GENMASK(27, 16) > +#define MT_WTBL2_W3_TID5_SN_LO GENMASK(31, 28) > + > +#define MT_WTBL2_W4_TID5_SN_HI GENMASK(7, 0) > +#define MT_WTBL2_W4_TID6_SN GENMASK(19, 8) > +#define MT_WTBL2_W4_TID7_SN GENMASK(31, 20) > + > +#define MT_WTBL2_W5_TX_COUNT_RATE1 GENMASK(15, 0) > +#define MT_WTBL2_W5_FAIL_COUNT_RATE1 GENAMSK(31, 16) > + > +#define MT_WTBL2_W6_TX_COUNT_RATE2 GENMASK(7, 0) > +#define MT_WTBL2_W6_TX_COUNT_RATE3 GENMASK(15, 8) > +#define MT_WTBL2_W6_TX_COUNT_RATE4 GENMASK(23, 16) > +#define MT_WTBL2_W6_TX_COUNT_RATE5 GENMASK(31, 24) > + > +#define MT_WTBL2_W7_TX_COUNT_CUR_BW GENMASK(15, 0) > +#define MT_WTBL2_W7_FAIL_COUNT_CUR_BW GENMASK(31, 16) > + > +#define MT_WTBL2_W8_TX_COUNT_OTHER_BW GENMASK(15, 0) > +#define MT_WTBL2_W8_FAIL_COUNT_OTHER_BW GENMASK(31, 16) > + > +#define MT_WTBL2_W9_POWER_OFFSET GENMASK(4, 0) > +#define MT_WTBL2_W9_SPATIAL_EXT BIT(5) > +#define MT_WTBL2_W9_ANT_PRIORITY GENMASK(8, 6) > +#define MT_WTBL2_W9_CC_BW_SEL GENMASK(10, 9) > +#define MT_WTBL2_W9_CHANGE_BW_RATE GENMASK(13, 11) > +#define MT_WTBL2_W9_BW_CAP GENMASK(15, 14) > +#define MT_WTBL2_W9_SHORT_GI_20 BIT(16) > +#define MT_WTBL2_W9_SHORT_GI_40 BIT(17) > +#define MT_WTBL2_W9_SHORT_GI_80 BIT(18) > +#define MT_WTBL2_W9_SHORT_GI_160 BIT(19) > +#define MT_WTBL2_W9_MPDU_FAIL_COUNT GENMASK(25, 23) > +#define MT_WTBL2_W9_MPDU_OK_COUNT GENMASK(28, 26) > +#define MT_WTBL2_W9_RATE_IDX GENMASK(31, 29) > + > +#define MT_WTBL2_W10_RATE1 GENMASK(11, 0) > +#define MT_WTBL2_W10_RATE2 GENMASK(23, 12) > +#define MT_WTBL2_W10_RATE3_LO GENMASK(31, 24) > + > +#define MT_WTBL2_W11_RATE3_HI GENMASK(3, 0) > +#define MT_WTBL2_W11_RATE4 GENMASK(15, 4) > +#define MT_WTBL2_W11_RATE5 GENMASK(27, 16) > +#define MT_WTBL2_W11_RATE6_LO GENMASK(31, 28) > + > +#define MT_WTBL2_W12_RATE6_HI GENMASK(7, 0) > +#define MT_WTBL2_W12_RATE7 GENMASK(19, 8) > +#define MT_WTBL2_W12_RATE8 GENMASK(31, 20) > + > +#define MT_WTBL2_W13_AVG_RCPI0 GENMASK(7, 0) > +#define MT_WTBL2_W13_AVG_RCPI1 GENMASK(15, 8) > +#define MT_WTBL2_W13_AVG_RCPI2 GENAMSK(23, 16) > + > +#define MT_WTBL2_W14_CC_NOISE_1S GENMASK(6, 0) > +#define MT_WTBL2_W14_CC_NOISE_2S GENMASK(13, 7) > +#define MT_WTBL2_W14_CC_NOISE_3S GENMASK(20, 14) > +#define MT_WTBL2_W14_CHAN_EST_RMS GENMASK(24, 21) > +#define MT_WTBL2_W14_CC_NOISE_SEL BIT(15) > +#define MT_WTBL2_W14_ANT_SEL GENMASK(31, 26) > + > +#define MT_WTBL2_W15_BA_WIN_SIZE GENMASK(2, 0) > +#define MT_WTBL2_W15_BA_WIN_SIZE_SHIFT 3 > +#define MT_WTBL2_W15_BA_EN_TIDS GENMASK(31, 24) > + > +#define MT_WTBL1_OR (MT_WTBL1_BASE + 0x2300) > +#define MT_WTBL1_OR_PSM_WRITE BIT(31) > + > +enum mt7603_cipher_type { > + MT_CIPHER_NONE, > + MT_CIPHER_WEP40, > + MT_CIPHER_TKIP, > + MT_CIPHER_TKIP_NO_MIC, > + MT_CIPHER_AES_CCMP, > + MT_CIPHER_WEP104, > + MT_CIPHER_BIP_CMAC_128, > + MT_CIPHER_WEP128, > + MT_CIPHER_WAPI, > +}; > + > +#endif > diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/soc.c b/drivers/net/wireless/mediatek/mt76/mt7603/soc.c > new file mode 100644 > index 000000000000..5d19b4e7edc6 > --- /dev/null > +++ b/drivers/net/wireless/mediatek/mt76/mt7603/soc.c > @@ -0,0 +1,97 @@ > +/* SPDX-License-Identifier: ISC */ > +/* > + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> > + * > + * Permission to use, copy, modify, and/or distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/platform_device.h> > + > +#include "mt7603.h" > + > +static int > +mt76_wmac_probe(struct platform_device *pdev) > +{ > + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + struct mt7603_dev *dev; > + void __iomem *mem_base; > + int irq; > + int ret; > + > + irq = platform_get_irq(pdev, 0); > + if (irq < 0) { > + dev_err(&pdev->dev, "Failed to get device IRQ\n"); > + return irq; > + } > + > + mem_base = devm_ioremap_resource(&pdev->dev, res); > + if (!mem_base) { > + dev_err(&pdev->dev, "Failed to get memory resource\n"); > + return -EINVAL; > + } > + > + dev = mt7603_alloc_device(&pdev->dev); > + if (!dev) > + return -ENOMEM; > + > + mt76_mmio_init(&dev->mt76, mem_base); > + > + dev->mt76.rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | > + (mt76_rr(dev, MT_HW_REV) & 0xff); > + dev_info(dev->mt76.dev, "ASIC revision: %04x\n", dev->mt76.rev); > + > + ret = devm_request_irq(dev->mt76.dev, irq, mt7603_irq_handler, > + IRQF_SHARED, KBUILD_MODNAME, dev); > + if (ret) > + goto error; > + > + ret = mt7603_register_device(dev); > + if (ret) > + goto error; > + > + return 0; > +error: > + ieee80211_free_hw(mt76_hw(dev)); > + return ret; > +} > + > +static int > +mt76_wmac_remove(struct platform_device *pdev) > +{ > + struct mt76_dev *mdev = platform_get_drvdata(pdev); > + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); > + > + mt7603_unregister_device(dev); > + > + return 0; > +} > + > +static const struct of_device_id of_wmac_match[] = { > + { .compatible = "mediatek,mt7628-wmac" }, > + {}, > +}; > + > +MODULE_DEVICE_TABLE(of, of_wmac_match); > +MODULE_FIRMWARE(MT7628_FIRMWARE_E1); > +MODULE_FIRMWARE(MT7628_FIRMWARE_E2); > + > +struct platform_driver mt76_wmac_driver = { > + .probe = mt76_wmac_probe, > + .remove = mt76_wmac_remove, > + .driver = { > + .name = "mt76_wmac", > + .of_match_table = of_wmac_match, > + }, > +}; > -- > 2.17.0 >
Felix Fietkau <nbd@nbd.name> writes: > This driver is for a newer generation of 2x2 MediaTek 802.11n chipsets. > MT7603E is a PCIe chip. > MT7628 and MT7688 are MIPS SoC devices with built-in WLAN. > MT7688 is limited to 1x1 > > This driver fully supports AP, station, mesh, ad-hoc and monitor mode. > > Signed-off-by: Felix Fietkau <nbd@nbd.name> [...] > --- /dev/null > +++ b/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c > @@ -0,0 +1,201 @@ > +/* SPDX-License-Identifier: ISC */ > +/* > + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> > + * > + * Permission to use, copy, modify, and/or distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ If you have the SPDX tag I don't think you need to have the license text anymore. My understanding is that the SPDX identifier and copyright is enough. This comment is for all files in this patch.
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 1a45cb30f39f..40615327ab68 100644 --- a/drivers/net/wireless/mediatek/mt76/Makefile +++ b/drivers/net/wireless/mediatek/mt76/Makefile @@ -21,3 +21,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/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..0f6f679bbe73 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c @@ -0,0 +1,201 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#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..30ffc68b0208 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/core.c @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#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..ca7b109c78a6 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#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..75ec077d8d74 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c @@ -0,0 +1,253 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "mt7603.h" +#include "mac.h" +#include "../dma.h" + +int +mt7603_tx_queue_mcu(struct mt7603_dev *dev, enum mt76_txq_id qid, + struct sk_buff *skb) +{ + struct mt76_queue *q = &dev->mt76.q_tx[qid]; + struct mt76_queue_buf buf; + dma_addr_t addr; + + addr = dma_map_single(dev->mt76.dev, skb->data, skb->len, + DMA_TO_DEVICE); + if (dma_mapping_error(dev->mt76.dev, addr)) + return -ENOMEM; + + buf.addr = addr; + buf.len = skb->len; + spin_lock_bh(&q->lock); + mt76_queue_add_buf(dev, q, &buf, 1, 0, skb, NULL); + mt76_queue_kick(dev, q); + spin_unlock_bh(&q->lock); + + return 0; +} + +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) + mt7603_mcu_rx_event(dev, 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: + mt7603_mcu_rx_event(dev, 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 | + MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE); + + mt76_wr(dev, MT_WPDMA_RST_IDX, ~0); + mt7603_pse_client_reset(dev); + + for (i = 0; i < ARRAY_SIZE(wmm_queue_map); i++) { + ret = mt7603_init_tx_queue(dev, &dev->mt76.q_tx[i], + wmm_queue_map[i], + MT_TX_RING_SIZE); + if (ret) + return ret; + } + + ret = mt7603_init_tx_queue(dev, &dev->mt76.q_tx[MT_TXQ_PSD], + MT_TX_HW_QUEUE_MGMT, MT_TX_RING_SIZE); + if (ret) + return ret; + + ret = mt7603_init_tx_queue(dev, &dev->mt76.q_tx[MT_TXQ_MCU], + MT_TX_HW_QUEUE_MCU, MT_MCU_RING_SIZE); + if (ret) + return ret; + + ret = mt7603_init_tx_queue(dev, &dev->mt76.q_tx[MT_TXQ_BEACON], + MT_TX_HW_QUEUE_BCN, MT_MCU_RING_SIZE); + if (ret) + return ret; + + ret = mt7603_init_tx_queue(dev, &dev->mt76.q_tx[MT_TXQ_CAB], + MT_TX_HW_QUEUE_BMC, MT_MCU_RING_SIZE); + if (ret) + return ret; + + ret = mt7603_init_rx_queue(dev, &dev->mt76.q_rx[MT_RXQ_MCU], 1, + MT_MCU_RING_SIZE, MT_RX_BUF_SIZE); + if (ret) + return ret; + + ret = mt7603_init_rx_queue(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], 0, + MT7603_RX_RING_SIZE, MT_RX_BUF_SIZE); + if (ret) + return ret; + + mt76_wr(dev, MT_DELAY_INT_CFG, 0); + return mt76_init_queues(dev); +} + +void mt7603_dma_cleanup(struct mt7603_dev *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_TX_WRITEBACK_DONE); + + tasklet_kill(&dev->tx_tasklet); + mt76_dma_cleanup(&dev->mt76); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c new file mode 100644 index 000000000000..6d7df67de494 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "mt7603.h" +#include "eeprom.h" + +static int +mt7603_efuse_read(struct mt7603_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, 0xff, 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 +mt7603_efuse_init(struct mt7603_dev *dev) +{ + u32 base = mt7603_reg_map(dev, MT_EFUSE_BASE); + int len = MT7603_EEPROM_SIZE; + void *buf; + int ret, i; + + if (mt76_rr(dev, base + MT_EFUSE_BASE_CTRL) & MT_EFUSE_BASE_CTRL_EMPTY) + return 0; + + 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 = mt7603_efuse_read(dev, base, i, buf + i); + if (ret) + return ret; + } + + return 0; +} + +static bool +mt7603_has_cal_free_data(struct mt7603_dev *dev, u8 *efuse) +{ + if (!efuse[MT_EE_TEMP_SENSOR_CAL]) + return false; + + if (get_unaligned_le16(efuse + MT_EE_TX_POWER_0_START_2G) == 0) + return false; + + if (get_unaligned_le16(efuse + MT_EE_TX_POWER_1_START_2G) == 0) + return false; + + if (!efuse[MT_EE_CP_FT_VERSION]) + return false; + + if (!efuse[MT_EE_XTAL_FREQ_OFFSET]) + return false; + + if (!efuse[MT_EE_XTAL_WF_RFCAL]) + return false; + + return true; +} + +static void +mt7603_apply_cal_free_data(struct mt7603_dev *dev, u8 *efuse) +{ + static const u8 cal_free_bytes[] = { + MT_EE_TEMP_SENSOR_CAL, + MT_EE_CP_FT_VERSION, + MT_EE_XTAL_FREQ_OFFSET, + MT_EE_XTAL_WF_RFCAL, + /* Skip for MT7628 */ + MT_EE_TX_POWER_0_START_2G, + MT_EE_TX_POWER_0_START_2G + 1, + MT_EE_TX_POWER_1_START_2G, + MT_EE_TX_POWER_1_START_2G + 1, + }; + u8 *eeprom = dev->mt76.eeprom.data; + int n = ARRAY_SIZE(cal_free_bytes); + int i; + + if (!mt7603_has_cal_free_data(dev, efuse)) + return; + + if (is_mt7628(dev)) + n -= 4; + + for (i = 0; i < n; i++) { + int offset = cal_free_bytes[i]; + + eeprom[offset] = efuse[offset]; + } +} + +static int +mt7603_eeprom_load(struct mt7603_dev *dev) +{ + int ret; + + ret = mt76_eeprom_init(&dev->mt76, MT7603_EEPROM_SIZE); + if (ret < 0) + return ret; + + return mt7603_efuse_init(dev); +} + +static int mt7603_check_eeprom(struct mt76_dev *dev) +{ + u16 val = get_unaligned_le16(dev->eeprom.data); + + switch (val) { + case 0x7628: + case 0x7603: + return 0; + default: + return -EINVAL; + } +} + +int mt7603_eeprom_init(struct mt7603_dev *dev) +{ + int ret; + + ret = mt7603_eeprom_load(dev); + if (ret < 0) + return ret; + + if (dev->mt76.otp.data) { + if (mt7603_check_eeprom(&dev->mt76) == 0) + mt7603_apply_cal_free_data(dev, dev->mt76.otp.data); + else + memcpy(dev->mt76.eeprom.data, dev->mt76.otp.data, + MT7603_EEPROM_SIZE); + } + + dev->mt76.cap.has_2ghz = true; + memcpy(dev->mt76.macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR, + ETH_ALEN); + + mt76_eeprom_override(&dev->mt76); + + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h new file mode 100644 index 000000000000..18e649493bfd --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __MT7603_EEPROM_H +#define __MT7603_EEPROM_H + +#include "mt7603.h" + +enum mt7603_eeprom_field { + MT_EE_CHIP_ID = 0x000, + MT_EE_VERSION = 0x002, + MT_EE_MAC_ADDR = 0x004, + MT_EE_NIC_CONF_0 = 0x034, + MT_EE_NIC_CONF_1 = 0x036, + MT_EE_NIC_CONF_2 = 0x042, + + MT_EE_XTAL_TRIM_1 = 0x03a, + + MT_EE_RSSI_OFFSET_2G = 0x046, + MT_EE_WIFI_RF_SETTING = 0x048, + MT_EE_RSSI_OFFSET_5G = 0x04a, + + MT_EE_TX_POWER_DELTA_BW40 = 0x050, + MT_EE_TX_POWER_DELTA_BW80 = 0x052, + + MT_EE_TX_POWER_EXT_PA_5G = 0x054, + + MT_EE_TEMP_SENSOR_CAL = 0x055, + + MT_EE_TX_POWER_0_START_2G = 0x056, + MT_EE_TX_POWER_1_START_2G = 0x05c, + + /* used as byte arrays */ +#define MT_TX_POWER_GROUP_SIZE_5G 5 +#define MT_TX_POWER_GROUPS_5G 6 + MT_EE_TX_POWER_0_START_5G = 0x062, + + MT_EE_TX_POWER_0_GRP3_TX_POWER_DELTA = 0x074, + MT_EE_TX_POWER_0_GRP4_TSSI_SLOPE = 0x076, + + MT_EE_TX_POWER_1_START_5G = 0x080, + + MT_EE_TX_POWER_CCK = 0x0a0, + MT_EE_TX_POWER_OFDM_2G_6M = 0x0a2, + MT_EE_TX_POWER_OFDM_2G_24M = 0x0a4, + MT_EE_TX_POWER_OFDM_2G_54M = 0x0a6, + MT_EE_TX_POWER_HT_BPSK_QPSK = 0x0a8, + MT_EE_TX_POWER_HT_16_64_QAM = 0x0aa, + MT_EE_TX_POWER_HT_64_QAM = 0x0ac, + + MT_EE_ELAN_RX_MODE_GAIN = 0x0c0, + MT_EE_ELAN_RX_MODE_NF = 0x0c1, + MT_EE_ELAN_RX_MODE_P1DB = 0x0c2, + + MT_EE_ELAN_BYPASS_MODE_GAIN = 0x0c3, + MT_EE_ELAN_BYPASS_MODE_NF = 0x0c4, + MT_EE_ELAN_BYPASS_MODE_P1DB = 0x0c5, + + MT_EE_STEP_NUM_NEG_6_7 = 0x0c6, + MT_EE_STEP_NUM_NEG_4_5 = 0x0c8, + MT_EE_STEP_NUM_NEG_2_3 = 0x0ca, + MT_EE_STEP_NUM_NEG_0_1 = 0x0cc, + + MT_EE_REF_STEP_24G = 0x0ce, + + MT_EE_STEP_NUM_PLUS_1_2 = 0x0d0, + MT_EE_STEP_NUM_PLUS_3_4 = 0x0d2, + MT_EE_STEP_NUM_PLUS_5_6 = 0x0d4, + MT_EE_STEP_NUM_PLUS_7 = 0x0d6, + + MT_EE_CP_FT_VERSION = 0x0f0, + + MT_EE_XTAL_FREQ_OFFSET = 0x0f4, + MT_EE_XTAL_TRIM_2_COMP = 0x0f5, + MT_EE_XTAL_TRIM_3_COMP = 0x0f6, + MT_EE_XTAL_WF_RFCAL = 0x0f7, + + __MT_EE_MAX +}; + +enum mt7603_eeprom_source { + MT_EE_SRC_PROM, + MT_EE_SRC_EFUSE, + MT_EE_SRC_FLASH, +}; + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/init.c b/drivers/net/wireless/mediatek/mt76/mt7603/init.c new file mode 100644 index 000000000000..41d92fb9c280 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/init.c @@ -0,0 +1,608 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/etherdevice.h> +#include "mt7603.h" +#include "mac.h" +#include "eeprom.h" + +struct mt7603_dev *mt7603_alloc_device(struct device *pdev) +{ + static const struct mt76_driver_ops drv_ops = { + .txwi_size = MT_TXD_SIZE, + .tx_prepare_skb = mt7603_tx_prepare_skb, + .tx_complete_skb = mt7603_tx_complete_skb, + .rx_skb = mt7603_queue_rx_skb, + .rx_poll_complete = mt7603_rx_poll_complete, + .sta_ps = mt7603_sta_ps, + .sta_add = mt7603_sta_add, + .sta_assoc = mt7603_sta_assoc, + .sta_remove = mt7603_sta_remove, + .update_survey = mt7603_update_channel, + }; + struct mt7603_dev *dev; + struct mt76_dev *mdev; + + mdev = mt76_alloc_device(sizeof(*dev), &mt7603_ops); + if (!mdev) + return NULL; + + dev = container_of(mdev, struct mt7603_dev, mt76); + mdev->dev = pdev; + mdev->drv = &drv_ops; + + return dev; +} + +static void +mt7603_set_tmac_template(struct mt7603_dev *dev) +{ + u32 desc[5] = { + [1] = FIELD_PREP(MT_TXD3_REM_TX_COUNT, 0xf), + [3] = MT_TXD5_SW_POWER_MGMT + }; + u32 addr; + int i; + + addr = mt7603_reg_map(dev, MT_CLIENT_BASE_PHYS_ADDR); + addr += MT_CLIENT_TMAC_INFO_TEMPLATE; + for (i = 0; i < ARRAY_SIZE(desc); i++) + mt76_wr(dev, addr + 4 * i, desc[i]); +} + +static void +mt7603_dma_sched_init(struct mt7603_dev *dev) +{ + int page_size = 128; + int page_count; + int max_len = 1792; + int max_amsdu_pages = 4096 / page_size; + int max_mcu_len = 4096; + int max_beacon_len = 512 * 4 + max_len; + int max_mcast_pages = 4 * max_len / page_size; + int reserved_count = 0; + int beacon_pages; + int mcu_pages; + int i; + + page_count = mt76_get_field(dev, MT_PSE_FC_P0, + MT_PSE_FC_P0_MAX_QUOTA); + beacon_pages = 4 * (max_beacon_len / page_size); + mcu_pages = max_mcu_len / page_size; + + mt76_wr(dev, MT_PSE_FRP, + FIELD_PREP(MT_PSE_FRP_P0, 7) | + FIELD_PREP(MT_PSE_FRP_P1, 6) | + FIELD_PREP(MT_PSE_FRP_P2_RQ2, 4)); + + mt76_wr(dev, MT_HIGH_PRIORITY_1, 0x55555553); + mt76_wr(dev, MT_HIGH_PRIORITY_2, 0x78555555); + + mt76_wr(dev, MT_QUEUE_PRIORITY_1, 0x2b1a096e); + mt76_wr(dev, MT_QUEUE_PRIORITY_2, 0x785f4d3c); + + mt76_wr(dev, MT_PRIORITY_MASK, 0xffffffff); + + mt76_wr(dev, MT_SCH_1, page_count | (2 << 28)); + mt76_wr(dev, MT_SCH_2, max_amsdu_pages); + + for (i = 0; i <= 4; i++) + mt76_wr(dev, MT_PAGE_COUNT(i), max_amsdu_pages); + reserved_count += 5 * max_amsdu_pages; + + mt76_wr(dev, MT_PAGE_COUNT(5), mcu_pages); + reserved_count += mcu_pages; + + mt76_wr(dev, MT_PAGE_COUNT(7), beacon_pages); + reserved_count += beacon_pages; + + mt76_wr(dev, MT_PAGE_COUNT(8), max_mcast_pages); + reserved_count += max_mcast_pages; + + if (is_mt7603(dev)) + reserved_count = 0; + + mt76_wr(dev, MT_RSV_MAX_THRESH, page_count - reserved_count); + + if (is_mt7603(dev) && mt76xx_rev(dev) >= MT7603_REV_E2) { + mt76_wr(dev, MT_GROUP_THRESH(0), + page_count - beacon_pages - mcu_pages); + mt76_wr(dev, MT_GROUP_THRESH(1), beacon_pages); + mt76_wr(dev, MT_BMAP_0, 0x0080ff5f); + mt76_wr(dev, MT_GROUP_THRESH(2), mcu_pages); + mt76_wr(dev, MT_BMAP_1, 0x00000020); + } else { + mt76_wr(dev, MT_GROUP_THRESH(0), page_count); + mt76_wr(dev, MT_BMAP_0, 0xffff); + } + + mt76_wr(dev, MT_SCH_4, 0); + + for (i = 0; i <= 15; i++) + mt76_wr(dev, MT_TXTIME_THRESH(i), 0xfffff); + + mt76_set(dev, MT_SCH_4, BIT(6)); +} + +static void +mt7603_phy_init(struct mt7603_dev *dev) +{ + int rx_chains = dev->mt76.antenna_mask; + int tx_chains = __sw_hweight8(rx_chains) - 1; + + mt76_rmw(dev, MT_WF_RMAC_RMCR, + (MT_WF_RMAC_RMCR_SMPS_MODE | + MT_WF_RMAC_RMCR_RX_STREAMS), + (FIELD_PREP(MT_WF_RMAC_RMCR_SMPS_MODE, 3) | + FIELD_PREP(MT_WF_RMAC_RMCR_RX_STREAMS, rx_chains))); + + mt76_rmw_field(dev, MT_TMAC_TCR, MT_TMAC_TCR_TX_STREAMS, + tx_chains); + + dev->agc0 = mt76_rr(dev, MT_AGC(0)); + dev->agc3 = mt76_rr(dev, MT_AGC(3)); +} + +static void +mt7603_mac_init(struct mt7603_dev *dev) +{ + u8 bc_addr[ETH_ALEN]; + u32 addr; + int i; + + mt76_wr(dev, MT_AGG_BA_SIZE_LIMIT_0, + (MT_AGG_SIZE_LIMIT(0) << 0 * MT_AGG_BA_SIZE_LIMIT_SHIFT) | + (MT_AGG_SIZE_LIMIT(1) << 1 * MT_AGG_BA_SIZE_LIMIT_SHIFT) | + (MT_AGG_SIZE_LIMIT(2) << 2 * MT_AGG_BA_SIZE_LIMIT_SHIFT) | + (MT_AGG_SIZE_LIMIT(3) << 3 * MT_AGG_BA_SIZE_LIMIT_SHIFT)); + + mt76_wr(dev, MT_AGG_BA_SIZE_LIMIT_1, + (MT_AGG_SIZE_LIMIT(4) << 0 * MT_AGG_BA_SIZE_LIMIT_SHIFT) | + (MT_AGG_SIZE_LIMIT(5) << 1 * MT_AGG_BA_SIZE_LIMIT_SHIFT) | + (MT_AGG_SIZE_LIMIT(6) << 2 * MT_AGG_BA_SIZE_LIMIT_SHIFT) | + (MT_AGG_SIZE_LIMIT(7) << 3 * MT_AGG_BA_SIZE_LIMIT_SHIFT)); + + mt76_wr(dev, MT_AGG_LIMIT, + FIELD_PREP(MT_AGG_LIMIT_AC(0), 24) | + FIELD_PREP(MT_AGG_LIMIT_AC(1), 24) | + FIELD_PREP(MT_AGG_LIMIT_AC(2), 24) | + FIELD_PREP(MT_AGG_LIMIT_AC(3), 24)); + + mt76_wr(dev, MT_AGG_LIMIT_1, + FIELD_PREP(MT_AGG_LIMIT_AC(0), 24) | + FIELD_PREP(MT_AGG_LIMIT_AC(1), 24) | + FIELD_PREP(MT_AGG_LIMIT_AC(2), 24) | + FIELD_PREP(MT_AGG_LIMIT_AC(3), 24)); + + mt76_wr(dev, MT_AGG_CONTROL, + FIELD_PREP(MT_AGG_CONTROL_BAR_RATE, 0x4b) | + FIELD_PREP(MT_AGG_CONTROL_CFEND_RATE, 0x69) | + MT_AGG_CONTROL_NO_BA_AR_RULE); + + mt76_wr(dev, MT_AGG_RETRY_CONTROL, + FIELD_PREP(MT_AGG_RETRY_CONTROL_BAR_LIMIT, 1) | + FIELD_PREP(MT_AGG_RETRY_CONTROL_RTS_LIMIT, 15)); + + mt76_rmw(dev, MT_DMA_DCR0, ~0xfffc, 4096); + + mt76_rmw(dev, MT_DMA_VCFR0, BIT(0), BIT(13)); + mt76_rmw(dev, MT_DMA_TMCFR0, BIT(0) | BIT(1), BIT(13)); + + mt76_clear(dev, MT_WF_RMAC_TMR_PA, BIT(31)); + + mt76_set(dev, MT_WF_RMACDR, MT_WF_RMACDR_MAXLEN_20BIT); + mt76_rmw(dev, MT_WF_RMAC_MAXMINLEN, 0xffffff, 0x19000); + + mt76_wr(dev, MT_WF_RFCR1, 0); + + mt76_set(dev, MT_TMAC_TCR, MT_TMAC_TCR_RX_RIFS_MODE); + + mt7603_set_tmac_template(dev); + + /* Enable RX group to HIF */ + addr = mt7603_reg_map(dev, MT_CLIENT_BASE_PHYS_ADDR); + mt76_set(dev, addr + MT_CLIENT_RXINF, MT_CLIENT_RXINF_RXSH_GROUPS); + + /* Enable RX group to MCU */ + mt76_set(dev, MT_DMA_DCR1, GENMASK(13, 11)); + + mt76_rmw_field(dev, MT_AGG_PCR_RTS, MT_AGG_PCR_RTS_PKT_THR, 3); + mt76_set(dev, MT_TMAC_PCR, MT_TMAC_PCR_SPE_EN); + + /* include preamble detection in CCA trigger signal */ + mt76_rmw_field(dev, MT_TXREQ, MT_TXREQ_CCA_SRC_SEL, 2); + + mt76_wr(dev, MT_RXREQ, 4); + + /* Configure all rx packets to HIF */ + mt76_wr(dev, MT_DMA_RCFR0, 0xc0000000); + + /* Configure MCU txs selection with aggregation */ + mt76_wr(dev, MT_DMA_TCFR0, + FIELD_PREP(MT_DMA_TCFR_TXS_AGGR_TIMEOUT, 1) | /* 32 us */ + MT_DMA_TCFR_TXS_AGGR_COUNT); + + /* Configure HIF txs selection with aggregation */ + mt76_wr(dev, MT_DMA_TCFR1, + FIELD_PREP(MT_DMA_TCFR_TXS_AGGR_TIMEOUT, 1) | /* 32 us */ + MT_DMA_TCFR_TXS_AGGR_COUNT | /* Maximum count */ + MT_DMA_TCFR_TXS_BIT_MAP); + + mt76_wr(dev, MT_MCU_PCIE_REMAP_1, MT_PSE_WTBL_2_PHYS_ADDR); + + for (i = 0; i < MT7603_WTBL_SIZE; i++) + mt7603_wtbl_clear(dev, i); + + eth_broadcast_addr(bc_addr); + mt7603_wtbl_init(dev, MT7603_WTBL_RESERVED, -1, bc_addr); + dev->global_sta.wcid.idx = MT7603_WTBL_RESERVED; + rcu_assign_pointer(dev->mt76.wcid[MT7603_WTBL_RESERVED], + &dev->global_sta.wcid); + + mt76_rmw_field(dev, MT_LPON_BTEIR, MT_LPON_BTEIR_MBSS_MODE, 2); + mt76_rmw_field(dev, MT_WF_RMACDR, MT_WF_RMACDR_MBSSID_MASK, 2); + + mt76_wr(dev, MT_AGG_ARUCR, FIELD_PREP(MT_AGG_ARxCR_LIMIT(0), 7)); + mt76_wr(dev, MT_AGG_ARDCR, + FIELD_PREP(MT_AGG_ARxCR_LIMIT(0), 0) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(1), + max_t(int, 0, MT7603_RATE_RETRY - 2)) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(2), MT7603_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(3), MT7603_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(4), MT7603_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(5), MT7603_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(6), MT7603_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(7), MT7603_RATE_RETRY - 1)); + + mt76_wr(dev, MT_AGG_ARCR, + (MT_AGG_ARCR_INIT_RATE1 | + FIELD_PREP(MT_AGG_ARCR_RTS_RATE_THR, 2) | + MT_AGG_ARCR_RATE_DOWN_RATIO_EN | + FIELD_PREP(MT_AGG_ARCR_RATE_DOWN_RATIO, 1) | + FIELD_PREP(MT_AGG_ARCR_RATE_UP_EXTRA_TH, 4))); + + mt76_set(dev, MT_WTBL_RMVTCR, MT_WTBL_RMVTCR_RX_MV_MODE); + + mt76_clear(dev, MT_SEC_SCR, MT_SEC_SCR_MASK_ORDER); + mt76_clear(dev, MT_SEC_SCR, BIT(18)); + + /* Set secondary beacon time offsets */ + for (i = 0; i <= 4; i++) + mt76_rmw_field(dev, MT_LPON_SBTOR(i), MT_LPON_SBTOR_TIME_OFFSET, + (i + 1) * (20 + 4096)); +} + +static int +mt7603_init_hardware(struct mt7603_dev *dev) +{ + int i, ret; + + mt76_wr(dev, MT_INT_SOURCE_CSR, ~0); + + ret = mt7603_eeprom_init(dev); + if (ret < 0) + return ret; + + ret = mt7603_dma_init(dev); + if (ret) + return ret; + + mt76_wr(dev, MT_WPDMA_GLO_CFG, 0x52000850); + mt7603_mac_dma_start(dev); + dev->rxfilter = mt76_rr(dev, MT_WF_RFCR); + set_bit(MT76_STATE_INITIALIZED, &dev->mt76.state); + + for (i = 0; i < MT7603_WTBL_SIZE; i++) { + mt76_wr(dev, MT_PSE_RTA, MT_PSE_RTA_BUSY | MT_PSE_RTA_WRITE | + FIELD_PREP(MT_PSE_RTA_TAG_ID, i)); + mt76_poll(dev, MT_PSE_RTA, MT_PSE_RTA_BUSY, 0, 5000); + } + + ret = mt7603_mcu_init(dev); + if (ret) + return ret; + + mt7603_dma_sched_init(dev); + mt7603_mcu_set_eeprom(dev); + mt7603_phy_init(dev); + mt7603_mac_init(dev); + + return 0; +} + +#define CCK_RATE(_idx, _rate) { \ + .bitrate = _rate, \ + .flags = IEEE80211_RATE_SHORT_PREAMBLE, \ + .hw_value = (MT_PHY_TYPE_CCK << 8) | (_idx), \ + .hw_value_short = (MT_PHY_TYPE_CCK << 8) | (4 + _idx), \ +} + +#define OFDM_RATE(_idx, _rate) { \ + .bitrate = _rate, \ + .hw_value = (MT_PHY_TYPE_OFDM << 8) | (_idx), \ + .hw_value_short = (MT_PHY_TYPE_OFDM << 8) | (_idx), \ +} + +static struct ieee80211_rate mt7603_rates[] = { + CCK_RATE(0, 10), + CCK_RATE(1, 20), + CCK_RATE(2, 55), + CCK_RATE(3, 110), + OFDM_RATE(11, 60), + OFDM_RATE(15, 90), + OFDM_RATE(10, 120), + OFDM_RATE(14, 180), + OFDM_RATE(9, 240), + OFDM_RATE(13, 360), + OFDM_RATE(8, 480), + OFDM_RATE(12, 540), +}; + +static const struct ieee80211_iface_limit if_limits[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_ADHOC) + }, { + .max = MT7603_MAX_INTERFACES, + .types = BIT(NL80211_IFTYPE_STATION) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_AP) + }, +}; + +static const struct ieee80211_iface_combination if_comb[] = { + { + .limits = if_limits, + .n_limits = ARRAY_SIZE(if_limits), + .max_interfaces = 4, + .num_different_channels = 1, + .beacon_int_infra_match = true, + } +}; + +static void mt7603_led_set_config(struct mt76_dev *mt76, u8 delay_on, + u8 delay_off) +{ + struct mt7603_dev *dev = container_of(mt76, struct mt7603_dev, + mt76); + u32 val, addr; + + val = MT_LED_STATUS_DURATION(0xffff) | + MT_LED_STATUS_OFF(delay_off) | + MT_LED_STATUS_ON(delay_on); + + addr = mt7603_reg_map(dev, MT_LED_STATUS_0(mt76->led_pin)); + mt76_wr(dev, addr, val); + addr = mt7603_reg_map(dev, MT_LED_STATUS_1(mt76->led_pin)); + mt76_wr(dev, addr, val); + + val = MT_LED_CTRL_REPLAY(mt76->led_pin) | + MT_LED_CTRL_KICK(mt76->led_pin); + if (mt76->led_al) + val |= MT_LED_CTRL_POLARITY(mt76->led_pin); + addr = mt7603_reg_map(dev, MT_LED_CTRL); + mt76_wr(dev, addr, val); +} + +static int mt7603_led_set_blink(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct mt76_dev *mt76 = container_of(led_cdev, struct mt76_dev, + led_cdev); + u8 delta_on, delta_off; + + delta_off = max_t(u8, *delay_off / 10, 1); + delta_on = max_t(u8, *delay_on / 10, 1); + + mt7603_led_set_config(mt76, delta_on, delta_off); + return 0; +} + +static void mt7603_led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct mt76_dev *mt76 = container_of(led_cdev, struct mt76_dev, + led_cdev); + + if (!brightness) + mt7603_led_set_config(mt76, 0, 0xff); + else + mt7603_led_set_config(mt76, 0xff, 0); +} + +static u32 __mt7603_reg_addr(struct mt7603_dev *dev, u32 addr) +{ + if (addr < 0x100000) + return addr; + + return mt7603_reg_map(dev, addr); +} + +static u32 mt7603_rr(struct mt76_dev *mdev, u32 offset) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + u32 addr = __mt7603_reg_addr(dev, offset); + + return dev->bus_ops->rr(mdev, addr); +} + +static void mt7603_wr(struct mt76_dev *mdev, u32 offset, u32 val) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + u32 addr = __mt7603_reg_addr(dev, offset); + + dev->bus_ops->wr(mdev, addr, val); +} + +static u32 mt7603_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + u32 addr = __mt7603_reg_addr(dev, offset); + + return dev->bus_ops->rmw(mdev, addr, mask, val); +} + +static void +mt7603_regd_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt7603_dev *dev = hw->priv; + + dev->ed_monitor = request->dfs_region == NL80211_DFS_ETSI; +} + +static int +mt7603_txpower_signed(int val) +{ + bool sign = val & BIT(6); + + if (!(val & BIT(7))) + return 0; + + val &= GENMASK(5, 0); + if (!sign) + val = -val; + + return val; +} + +static void +mt7603_init_txpower(struct mt7603_dev *dev, + struct ieee80211_supported_band *sband) +{ + struct ieee80211_channel *chan; + u8 *eeprom = (u8 *)dev->mt76.eeprom.data; + int target_power = eeprom[MT_EE_TX_POWER_0_START_2G + 2] & ~BIT(7); + u8 *rate_power = &eeprom[MT_EE_TX_POWER_CCK]; + int max_offset, cur_offset; + int i; + + if (target_power & BIT(6)) + target_power = -(target_power & GENMASK(5, 0)); + + max_offset = 0; + for (i = 0; i < 14; i++) { + cur_offset = mt7603_txpower_signed(rate_power[i]); + max_offset = max(max_offset, cur_offset); + } + + target_power += max_offset; + + dev->tx_power_limit = target_power; + dev->mt76.txpower_cur = target_power; + + target_power = DIV_ROUND_UP(target_power, 2); + + /* add 3 dBm for 2SS devices (combined output) */ + if (dev->mt76.antenna_mask & BIT(1)) + target_power += 3; + + for (i = 0; i < sband->n_channels; i++) { + chan = &sband->channels[i]; + chan->max_power = target_power; + } +} + + +int mt7603_register_device(struct mt7603_dev *dev) +{ + struct mt76_bus_ops *bus_ops; + struct ieee80211_hw *hw = mt76_hw(dev); + struct wiphy *wiphy = hw->wiphy; + int ret; + + dev->bus_ops = dev->mt76.bus; + bus_ops = devm_kmemdup(dev->mt76.dev, dev->bus_ops, sizeof(*bus_ops), + GFP_KERNEL); + if (!bus_ops) + return -ENOMEM; + + bus_ops->rr = mt7603_rr; + bus_ops->wr = mt7603_wr; + bus_ops->rmw = mt7603_rmw; + dev->mt76.bus = bus_ops; + + INIT_DELAYED_WORK(&dev->mac_work, mt7603_mac_work); + tasklet_init(&dev->pre_tbtt_tasklet, mt7603_pre_tbtt_tasklet, + (unsigned long)dev); + + /* Check for 7688, which only has 1SS */ + dev->mt76.antenna_mask = 3; + if (mt76_rr(dev, MT_EFUSE_BASE + 0x64) & BIT(4)) + dev->mt76.antenna_mask = 1; + + dev->slottime = 9; + + ret = mt7603_init_hardware(dev); + if (ret) + return ret; + + hw->queues = 4; + hw->max_rates = 3; + hw->max_report_rates = 7; + hw->max_rate_tries = 11; + + hw->sta_data_size = sizeof(struct mt7603_sta); + hw->vif_data_size = sizeof(struct mt7603_vif); + + wiphy->iface_combinations = if_comb; + wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + + ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER); + ieee80211_hw_set(hw, TX_STATUS_NO_AMPDU_LEN); + + /* init led callbacks */ + if (IS_ENABLED(CONFIG_MT76_LEDS)) { + dev->mt76.led_cdev.brightness_set = mt7603_led_set_brightness; + dev->mt76.led_cdev.blink_set = mt7603_led_set_blink; + } + + wiphy->interface_modes = + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_ADHOC); + + wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; + + wiphy->reg_notifier = mt7603_regd_notifier; + + ret = mt76_register_device(&dev->mt76, true, mt7603_rates, + ARRAY_SIZE(mt7603_rates)); + if (ret) + return ret; + + mt7603_init_debugfs(dev); + mt7603_init_txpower(dev, &dev->mt76.sband_2g.sband); + + return 0; +} + +void mt7603_unregister_device(struct mt7603_dev *dev) +{ + tasklet_disable(&dev->pre_tbtt_tasklet); + mt76_unregister_device(&dev->mt76); + mt7603_mcu_exit(dev); + mt7603_dma_cleanup(dev); + ieee80211_free_hw(mt76_hw(dev)); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c new file mode 100644 index 000000000000..d1ea0e32b2ca --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c @@ -0,0 +1,1764 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/etherdevice.h> +#include <linux/timekeeping.h> +#include "mt7603.h" +#include "mac.h" + +#define MT_PSE_PAGE_SIZE 128 + +static u32 +mt7603_ac_queue_mask0(u32 mask) +{ + u32 ret = 0; + + ret |= GENMASK(3, 0) * !!(mask & BIT(0)); + ret |= GENMASK(8, 5) * !!(mask & BIT(1)); + ret |= GENMASK(13, 10) * !!(mask & BIT(2)); + ret |= GENMASK(19, 16) * !!(mask & BIT(3)); + return ret; +} + +static void +mt76_stop_tx_ac(struct mt7603_dev *dev, u32 mask) +{ + mt76_set(dev, MT_WF_ARB_TX_STOP_0, mt7603_ac_queue_mask0(mask)); +} + +static void +mt76_start_tx_ac(struct mt7603_dev *dev, u32 mask) +{ + mt76_set(dev, MT_WF_ARB_TX_START_0, mt7603_ac_queue_mask0(mask)); +} + +void mt7603_mac_set_timing(struct mt7603_dev *dev) +{ + u32 cck = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 231) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48); + u32 ofdm = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 60) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, 24); + int offset = 3 * dev->coverage_class; + u32 reg_offset = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, offset) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, offset); + int sifs; + u32 val; + + if (dev->mt76.chandef.chan->band == NL80211_BAND_5GHZ) + sifs = 16; + else + sifs = 10; + + mt76_set(dev, MT_ARB_SCR, + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); + udelay(1); + + mt76_wr(dev, MT_TIMEOUT_CCK, cck + reg_offset); + mt76_wr(dev, MT_TIMEOUT_OFDM, ofdm + reg_offset); + mt76_wr(dev, MT_IFS, + FIELD_PREP(MT_IFS_EIFS, 360) | + FIELD_PREP(MT_IFS_RIFS, 2) | + FIELD_PREP(MT_IFS_SIFS, sifs) | + FIELD_PREP(MT_IFS_SLOT, dev->slottime)); + + if (dev->slottime < 20) + val = MT7603_CFEND_RATE_DEFAULT; + else + val = MT7603_CFEND_RATE_11B; + + mt76_rmw_field(dev, MT_AGG_CONTROL, MT_AGG_CONTROL_CFEND_RATE, val); + + mt76_clear(dev, MT_ARB_SCR, + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); +} + +static void +mt7603_wtbl_update(struct mt7603_dev *dev, int idx, u32 mask) +{ + mt76_rmw(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX, + FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, idx) | mask); + + mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000); +} + +static u32 +mt7603_wtbl1_addr(int idx) +{ + return MT_WTBL1_BASE + idx * MT_WTBL1_SIZE; +} + +static u32 +mt7603_wtbl2_addr(int idx) +{ + /* Mapped to WTBL2 */ + return MT_PCIE_REMAP_BASE_1 + idx * MT_WTBL2_SIZE; +} + +static u32 +mt7603_wtbl3_addr(int idx) +{ + u32 base = mt7603_wtbl2_addr(MT7603_WTBL_SIZE); + + return base + idx * MT_WTBL3_SIZE; +} + +static u32 +mt7603_wtbl4_addr(int idx) +{ + u32 base = mt7603_wtbl3_addr(MT7603_WTBL_SIZE); + + return base + idx * MT_WTBL4_SIZE; +} + +void mt7603_wtbl_init(struct mt7603_dev *dev, int idx, int vif, + const u8 *mac_addr) +{ + const void *_mac = mac_addr; + u32 addr = mt7603_wtbl1_addr(idx); + u32 w0 = 0, w1 = 0; + int i; + + if (_mac) { + w0 = FIELD_PREP(MT_WTBL1_W0_ADDR_HI, + get_unaligned_le16(_mac + 4)); + w1 = FIELD_PREP(MT_WTBL1_W1_ADDR_LO, + get_unaligned_le32(_mac)); + } + + if (vif < 0) + vif = 0; + else + w0 |= MT_WTBL1_W0_RX_CHECK_A1; + w0 |= FIELD_PREP(MT_WTBL1_W0_MUAR_IDX, vif); + + mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000); + + mt76_set(dev, addr + 0 * 4, w0); + mt76_set(dev, addr + 1 * 4, w1); + mt76_set(dev, addr + 2 * 4, MT_WTBL1_W2_ADMISSION_CONTROL); + + mt76_stop_tx_ac(dev, GENMASK(3, 0)); + addr = mt7603_wtbl2_addr(idx); + for (i = 0; i < MT_WTBL2_SIZE; i += 4) + mt76_wr(dev, addr + i, 0); + mt7603_wtbl_update(dev, idx, MT_WTBL_UPDATE_WTBL2); + mt76_start_tx_ac(dev, GENMASK(3, 0)); + + addr = mt7603_wtbl3_addr(idx); + for (i = 0; i < MT_WTBL3_SIZE; i += 4) + mt76_wr(dev, addr + i, 0); + + addr = mt7603_wtbl4_addr(idx); + for (i = 0; i < MT_WTBL4_SIZE; i += 4) + mt76_wr(dev, addr + i, 0); +} + +static void +mt7603_wtbl_set_skip_tx(struct mt7603_dev *dev, int idx, bool enabled) +{ + u32 addr = mt7603_wtbl1_addr(idx); + u32 val = mt76_rr(dev, addr + 3 * 4); + + val &= ~MT_WTBL1_W3_SKIP_TX; + val |= enabled * MT_WTBL1_W3_SKIP_TX; + + mt76_wr(dev, addr + 3 * 4, val); +} + +void mt7603_filter_tx(struct mt7603_dev *dev, int idx, bool abort) +{ + int i, port, queue; + + if (abort) { + port = 3; /* PSE */ + queue = 8; /* free queue */ + } else { + port = 0; /* HIF */ + queue = 1; /* MCU queue */ + } + + mt7603_wtbl_set_skip_tx(dev, idx, true); + + mt76_wr(dev, MT_TX_ABORT, MT_TX_ABORT_EN | + FIELD_PREP(MT_TX_ABORT_WCID, idx)); + + for (i = 0; i < 4; i++) { + mt76_wr(dev, MT_DMA_FQCR0, MT_DMA_FQCR0_BUSY | + FIELD_PREP(MT_DMA_FQCR0_TARGET_WCID, idx) | + FIELD_PREP(MT_DMA_FQCR0_TARGET_QID, i) | + FIELD_PREP(MT_DMA_FQCR0_DEST_PORT_ID, port) | + FIELD_PREP(MT_DMA_FQCR0_DEST_QUEUE_ID, queue)); + + WARN_ON_ONCE(!mt76_poll(dev, MT_DMA_FQCR0, MT_DMA_FQCR0_BUSY, + 0, 5000)); + } + + mt76_wr(dev, MT_TX_ABORT, 0); + + mt7603_wtbl_set_skip_tx(dev, idx, false); +} + +void mt7603_wtbl_set_smps(struct mt7603_dev *dev, struct mt7603_sta *sta, + bool enabled) +{ + u32 addr = mt7603_wtbl1_addr(sta->wcid.idx); + + if (sta->smps == enabled) + return; + + mt76_rmw_field(dev, addr + 2 * 4, MT_WTBL1_W2_SMPS, enabled); + sta->smps = enabled; +} + +void mt7603_wtbl_set_ps(struct mt7603_dev *dev, struct mt7603_sta *sta, + bool enabled) +{ + int idx = sta->wcid.idx; + u32 addr; + + spin_lock_bh(&dev->ps_lock); + + if (sta->ps == enabled) + goto out; + + mt76_wr(dev, MT_PSE_RTA, + FIELD_PREP(MT_PSE_RTA_TAG_ID, idx) | + FIELD_PREP(MT_PSE_RTA_PORT_ID, 0) | + FIELD_PREP(MT_PSE_RTA_QUEUE_ID, 1) | + FIELD_PREP(MT_PSE_RTA_REDIRECT_EN, enabled) | + MT_PSE_RTA_WRITE | MT_PSE_RTA_BUSY); + + mt76_poll(dev, MT_PSE_RTA, MT_PSE_RTA_BUSY, 0, 5000); + + if (enabled) + mt7603_filter_tx(dev, idx, false); + + addr = mt7603_wtbl1_addr(idx); + mt76_set(dev, MT_WTBL1_OR, MT_WTBL1_OR_PSM_WRITE); + mt76_rmw(dev, addr + 3 * 4, MT_WTBL1_W3_POWER_SAVE, + enabled * MT_WTBL1_W3_POWER_SAVE); + mt76_clear(dev, MT_WTBL1_OR, MT_WTBL1_OR_PSM_WRITE); + sta->ps = enabled; + +out: + spin_unlock_bh(&dev->ps_lock); +} + +void mt7603_wtbl_clear(struct mt7603_dev *dev, int idx) +{ + int wtbl2_frame_size = MT_PSE_PAGE_SIZE / MT_WTBL2_SIZE; + int wtbl2_frame = idx / wtbl2_frame_size; + int wtbl2_entry = idx % wtbl2_frame_size; + + int wtbl3_base_frame = MT_WTBL3_OFFSET / MT_PSE_PAGE_SIZE; + int wtbl3_frame_size = MT_PSE_PAGE_SIZE / MT_WTBL3_SIZE; + int wtbl3_frame = wtbl3_base_frame + idx / wtbl3_frame_size; + int wtbl3_entry = (idx % wtbl3_frame_size) * 2; + + int wtbl4_base_frame = MT_WTBL4_OFFSET / MT_PSE_PAGE_SIZE; + int wtbl4_frame_size = MT_PSE_PAGE_SIZE / MT_WTBL4_SIZE; + int wtbl4_frame = wtbl4_base_frame + idx / wtbl4_frame_size; + int wtbl4_entry = idx % wtbl4_frame_size; + + u32 addr = MT_WTBL1_BASE + idx * MT_WTBL1_SIZE; + int i; + + mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000); + + mt76_wr(dev, addr + 0 * 4, + MT_WTBL1_W0_RX_CHECK_A1 | + MT_WTBL1_W0_RX_CHECK_A2 | + MT_WTBL1_W0_RX_VALID); + mt76_wr(dev, addr + 1 * 4, 0); + mt76_wr(dev, addr + 2 * 4, 0); + + mt76_set(dev, MT_WTBL1_OR, MT_WTBL1_OR_PSM_WRITE); + + mt76_wr(dev, addr + 3 * 4, + FIELD_PREP(MT_WTBL1_W3_WTBL2_FRAME_ID, wtbl2_frame) | + FIELD_PREP(MT_WTBL1_W3_WTBL2_ENTRY_ID, wtbl2_entry) | + FIELD_PREP(MT_WTBL1_W3_WTBL4_FRAME_ID, wtbl4_frame) | + MT_WTBL1_W3_I_PSM | MT_WTBL1_W3_KEEP_I_PSM); + mt76_wr(dev, addr + 4 * 4, + FIELD_PREP(MT_WTBL1_W4_WTBL3_FRAME_ID, wtbl3_frame) | + FIELD_PREP(MT_WTBL1_W4_WTBL3_ENTRY_ID, wtbl3_entry) | + FIELD_PREP(MT_WTBL1_W4_WTBL4_ENTRY_ID, wtbl4_entry)); + + mt76_clear(dev, MT_WTBL1_OR, MT_WTBL1_OR_PSM_WRITE); + + addr = mt7603_wtbl2_addr(idx); + + /* Clear BA information */ + mt76_wr(dev, addr + (15 * 4), 0); + + mt76_stop_tx_ac(dev, GENMASK(3, 0)); + for (i = 2; i <= 4; i++) + mt76_wr(dev, addr + (i * 4), 0); + mt7603_wtbl_update(dev, idx, MT_WTBL_UPDATE_WTBL2); + mt76_start_tx_ac(dev, GENMASK(3, 0)); + + mt7603_wtbl_update(dev, idx, MT_WTBL_UPDATE_RX_COUNT_CLEAR); + mt7603_wtbl_update(dev, idx, MT_WTBL_UPDATE_TX_COUNT_CLEAR); + mt7603_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); +} + +void mt7603_wtbl_update_cap(struct mt7603_dev *dev, struct ieee80211_sta *sta) +{ + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; + int idx = msta->wcid.idx; + u32 addr; + u32 val; + + addr = mt7603_wtbl1_addr(idx); + + val = mt76_rr(dev, addr + 2 * 4); + val &= MT_WTBL1_W2_KEY_TYPE | MT_WTBL1_W2_ADMISSION_CONTROL; + val |= FIELD_PREP(MT_WTBL1_W2_AMPDU_FACTOR, sta->ht_cap.ampdu_factor) | + FIELD_PREP(MT_WTBL1_W2_MPDU_DENSITY, sta->ht_cap.ampdu_density) | + MT_WTBL1_W2_TXS_BAF_REPORT; + + if (sta->ht_cap.cap) + val |= MT_WTBL1_W2_HT; + if (sta->vht_cap.cap) + val |= MT_WTBL1_W2_VHT; + + mt76_wr(dev, addr + 2 * 4, val); + + addr = mt7603_wtbl2_addr(idx); + val = mt76_rr(dev, addr + 9 * 4); + val &= ~(MT_WTBL2_W9_SHORT_GI_20 | MT_WTBL2_W9_SHORT_GI_40 | + MT_WTBL2_W9_SHORT_GI_80); + if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) + val |= MT_WTBL2_W9_SHORT_GI_20; + if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) + val |= MT_WTBL2_W9_SHORT_GI_40; + mt76_wr(dev, addr + 9 * 4, val); +} + +void mt7603_mac_rx_ba_reset(struct mt7603_dev *dev, void *addr, u8 tid) +{ + mt76_wr(dev, MT_BA_CONTROL_0, get_unaligned_le32(addr)); + mt76_wr(dev, MT_BA_CONTROL_1, + (get_unaligned_le16(addr + 4) | + FIELD_PREP(MT_BA_CONTROL_1_TID, tid) | + MT_BA_CONTROL_1_RESET)); +} + +void mt7603_mac_tx_ba_reset(struct mt7603_dev *dev, int wcid, int tid, int ssn, + int ba_size) +{ + u32 addr = mt7603_wtbl2_addr(wcid); + u32 tid_mask = FIELD_PREP(MT_WTBL2_W15_BA_EN_TIDS, BIT(tid)) | + (MT_WTBL2_W15_BA_WIN_SIZE << + (tid * MT_WTBL2_W15_BA_WIN_SIZE_SHIFT)); + u32 tid_val; + int i; + + if (ba_size < 0) { + /* disable */ + mt76_clear(dev, addr + (15 * 4), tid_mask); + return; + } + mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000); + + mt7603_mac_stop(dev); + switch (tid) { + case 0: + mt76_rmw_field(dev, addr + (2 * 4), MT_WTBL2_W2_TID0_SN, ssn); + break; + case 1: + mt76_rmw_field(dev, addr + (2 * 4), MT_WTBL2_W2_TID1_SN, ssn); + break; + case 2: + mt76_rmw_field(dev, addr + (2 * 4), MT_WTBL2_W2_TID2_SN_LO, + ssn); + mt76_rmw_field(dev, addr + (3 * 4), MT_WTBL2_W3_TID2_SN_HI, + ssn >> 8); + break; + case 3: + mt76_rmw_field(dev, addr + (3 * 4), MT_WTBL2_W3_TID3_SN, ssn); + break; + case 4: + mt76_rmw_field(dev, addr + (3 * 4), MT_WTBL2_W3_TID4_SN, ssn); + break; + case 5: + mt76_rmw_field(dev, addr + (3 * 4), MT_WTBL2_W3_TID5_SN_LO, + ssn); + mt76_rmw_field(dev, addr + (4 * 4), MT_WTBL2_W4_TID5_SN_HI, + ssn >> 4); + break; + case 6: + mt76_rmw_field(dev, addr + (4 * 4), MT_WTBL2_W4_TID6_SN, ssn); + break; + case 7: + mt76_rmw_field(dev, addr + (4 * 4), MT_WTBL2_W4_TID7_SN, ssn); + break; + } + mt7603_wtbl_update(dev, wcid, MT_WTBL_UPDATE_WTBL2); + mt7603_mac_start(dev); + + for (i = 7; i > 0; i--) { + if (ba_size < MT_AGG_SIZE_LIMIT(i)) + break; + } + + tid_val = FIELD_PREP(MT_WTBL2_W15_BA_EN_TIDS, BIT(tid)) | + i << (tid * MT_WTBL2_W15_BA_WIN_SIZE_SHIFT); + + mt76_rmw(dev, addr + (15 * 4), tid_mask, tid_val); +} + +static int +mt7603_get_rate(struct mt7603_dev *dev, struct ieee80211_supported_band *sband, + int idx, bool cck) +{ + int offset = 0; + int len = sband->n_bitrates; + int i; + + if (cck) { + if (sband == &dev->mt76.sband_5g.sband) + return 0; + + idx &= ~BIT(2); /* short preamble */ + } else if (sband == &dev->mt76.sband_2g.sband) { + offset = 4; + } + + for (i = offset; i < len; i++) { + if ((sband->bitrates[i].hw_value & GENMASK(7, 0)) == idx) + return i; + } + + return 0; +} + +static struct mt76_wcid * +mt7603_rx_get_wcid(struct mt7603_dev *dev, u8 idx, bool unicast) +{ + struct mt7603_sta *sta; + struct mt76_wcid *wcid; + + if (idx >= ARRAY_SIZE(dev->mt76.wcid)) + return NULL; + + wcid = rcu_dereference(dev->mt76.wcid[idx]); + if (unicast || !wcid) + return wcid; + + if (!wcid->sta) + return NULL; + + sta = container_of(wcid, struct mt7603_sta, wcid); + if (!sta->vif) + return NULL; + + return &sta->vif->sta.wcid; +} + +static void +mt7603_insert_ccmp_hdr(struct sk_buff *skb, u8 key_id) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + int hdr_len = ieee80211_get_hdrlen_from_skb(skb); + u8 *pn = status->iv; + u8 *hdr; + + __skb_push(skb, 8); + memmove(skb->data, skb->data + 8, hdr_len); + hdr = skb->data + hdr_len; + + hdr[0] = pn[5]; + hdr[1] = pn[4]; + hdr[2] = 0; + hdr[3] = 0x20 | (key_id << 6); + hdr[4] = pn[3]; + hdr[5] = pn[2]; + hdr[6] = pn[1]; + hdr[7] = pn[0]; + + status->flag &= ~RX_FLAG_IV_STRIPPED; +} + +int +mt7603_mac_fill_rx(struct mt7603_dev *dev, struct sk_buff *skb) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct ieee80211_supported_band *sband; + struct ieee80211_hdr *hdr; + __le32 *rxd = (__le32 *)skb->data; + u32 rxd0 = le32_to_cpu(rxd[0]); + u32 rxd1 = le32_to_cpu(rxd[1]); + u32 rxd2 = le32_to_cpu(rxd[2]); + bool unicast = rxd1 & MT_RXD1_NORMAL_U2M; + bool insert_ccmp_hdr = false; + bool remove_pad; + int idx; + int i; + + memset(status, 0, sizeof(*status)); + + i = FIELD_GET(MT_RXD1_NORMAL_CH_FREQ, rxd1); + sband = (i & 1) ? &dev->mt76.sband_5g.sband : &dev->mt76.sband_2g.sband; + i >>= 1; + + idx = FIELD_GET(MT_RXD2_NORMAL_WLAN_IDX, rxd2); + status->wcid = mt7603_rx_get_wcid(dev, idx, unicast); + + status->band = sband->band; + if (i < sband->n_channels) + status->freq = sband->channels[i].center_freq; + + if (rxd2 & MT_RXD2_NORMAL_FCS_ERR) + status->flag |= RX_FLAG_FAILED_FCS_CRC; + + if (rxd2 & MT_RXD2_NORMAL_TKIP_MIC_ERR) + status->flag |= RX_FLAG_MMIC_ERROR; + + if (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2) != 0 && + !(rxd2 & (MT_RXD2_NORMAL_CLM | MT_RXD2_NORMAL_CM))) { + status->flag |= RX_FLAG_DECRYPTED; + status->flag |= RX_FLAG_IV_STRIPPED; + status->flag |= RX_FLAG_MMIC_STRIPPED | RX_FLAG_MIC_STRIPPED; + } + + remove_pad = rxd1 & MT_RXD1_NORMAL_HDR_OFFSET; + + if (rxd2 & MT_RXD2_NORMAL_MAX_LEN_ERROR) + return -EINVAL; + + if (!sband->channels) + return -EINVAL; + + rxd += 4; + if (rxd0 & MT_RXD0_NORMAL_GROUP_4) { + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + if (rxd0 & MT_RXD0_NORMAL_GROUP_1) { + u8 *data = (u8 *)rxd; + + if (status->flag & RX_FLAG_DECRYPTED) { + status->iv[0] = data[5]; + status->iv[1] = data[4]; + status->iv[2] = data[3]; + status->iv[3] = data[2]; + status->iv[4] = data[1]; + status->iv[5] = data[0]; + + insert_ccmp_hdr = FIELD_GET(MT_RXD2_NORMAL_FRAG, rxd2); + } + + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + if (rxd0 & MT_RXD0_NORMAL_GROUP_2) { + rxd += 2; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + if (rxd0 & MT_RXD0_NORMAL_GROUP_3) { + u32 rxdg0 = le32_to_cpu(rxd[0]); + u32 rxdg3 = le32_to_cpu(rxd[3]); + bool cck = false; + + i = FIELD_GET(MT_RXV1_TX_RATE, rxdg0); + switch (FIELD_GET(MT_RXV1_TX_MODE, rxdg0)) { + case MT_PHY_TYPE_CCK: + cck = true; + /* fall through */ + case MT_PHY_TYPE_OFDM: + i = mt7603_get_rate(dev, sband, i, cck); + break; + case MT_PHY_TYPE_HT_GF: + case MT_PHY_TYPE_HT: + status->encoding = RX_ENC_HT; + if (i > 15) + return -EINVAL; + break; + default: + return -EINVAL; + } + + if (rxdg0 & MT_RXV1_HT_SHORT_GI) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + if (rxdg0 & MT_RXV1_HT_AD_CODE) + status->enc_flags |= RX_ENC_FLAG_LDPC; + + status->enc_flags |= RX_ENC_FLAG_STBC_MASK * + FIELD_GET(MT_RXV1_HT_STBC, rxdg0); + + status->rate_idx = i; + + status->chains = dev->mt76.antenna_mask; + status->chain_signal[0] = FIELD_GET(MT_RXV4_IB_RSSI0, rxdg3) + + dev->rssi_offset[0]; + status->chain_signal[1] = FIELD_GET(MT_RXV4_IB_RSSI1, rxdg3) + + dev->rssi_offset[1]; + + status->signal = status->chain_signal[0]; + if (status->chains & BIT(1)) + status->signal = max(status->signal, + status->chain_signal[1]); + + if (FIELD_GET(MT_RXV1_FRAME_MODE, rxdg0) == 1) + status->bw = RATE_INFO_BW_40; + + rxd += 6; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } else { + return -EINVAL; + } + + skb_pull(skb, (u8 *)rxd - skb->data + 2 * remove_pad); + + if (insert_ccmp_hdr) { + u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1); + + mt7603_insert_ccmp_hdr(skb, key_id); + } + + hdr = (struct ieee80211_hdr *)skb->data; + if (!status->wcid || !ieee80211_is_data_qos(hdr->frame_control)) + return 0; + + status->aggr = unicast && + !ieee80211_is_qos_nullfunc(hdr->frame_control); + status->tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; + status->seqno = hdr->seq_ctrl >> 4; + + return 0; +} + +static u16 +mt7603_mac_tx_rate_val(struct mt7603_dev *dev, + const struct ieee80211_tx_rate *rate, bool stbc, u8 *bw) +{ + u8 phy, nss, rate_idx; + u16 rateval; + + *bw = 0; + if (rate->flags & IEEE80211_TX_RC_MCS) { + rate_idx = rate->idx; + nss = 1 + (rate->idx >> 3); + phy = MT_PHY_TYPE_HT; + if (rate->flags & IEEE80211_TX_RC_GREEN_FIELD) + phy = MT_PHY_TYPE_HT_GF; + if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + *bw = 1; + } else { + const struct ieee80211_rate *r; + int band = dev->mt76.chandef.chan->band; + u16 val; + + nss = 1; + r = &mt76_hw(dev)->wiphy->bands[band]->bitrates[rate->idx]; + if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + val = r->hw_value_short; + else + val = r->hw_value; + + phy = val >> 8; + rate_idx = val & 0xff; + } + + rateval = (FIELD_PREP(MT_TX_RATE_IDX, rate_idx) | + FIELD_PREP(MT_TX_RATE_MODE, phy)); + + if (stbc && nss == 1) + rateval |= MT_TX_RATE_STBC; + + return rateval; +} + +void mt7603_wtbl_set_rates(struct mt7603_dev *dev, struct mt7603_sta *sta, + struct ieee80211_tx_rate *probe_rate, + struct ieee80211_tx_rate *rates) +{ + int wcid = sta->wcid.idx; + u32 addr = mt7603_wtbl2_addr(wcid); + bool stbc = false; + int n_rates = sta->n_rates; + u8 bw, bw_prev, bw_idx = 0; + u16 val[4]; + u16 probe_val; + u32 w9 = mt76_rr(dev, addr + 9 * 4); + int i; + + if (!mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000)) + return; + + for (i = n_rates; i < 4; i++) + rates[i] = rates[n_rates - 1]; + + w9 &= MT_WTBL2_W9_SHORT_GI_20 | MT_WTBL2_W9_SHORT_GI_40 | + MT_WTBL2_W9_SHORT_GI_80; + + val[0] = mt7603_mac_tx_rate_val(dev, &rates[0], stbc, &bw); + bw_prev = bw; + + if (probe_rate) { + probe_val = mt7603_mac_tx_rate_val(dev, probe_rate, stbc, &bw); + if (bw) + bw_idx = 1; + else + bw_prev = 0; + } else { + probe_val = val[0]; + } + + w9 |= FIELD_PREP(MT_WTBL2_W9_CC_BW_SEL, bw); + w9 |= FIELD_PREP(MT_WTBL2_W9_BW_CAP, bw); + + val[1] = mt7603_mac_tx_rate_val(dev, &rates[1], stbc, &bw); + if (bw_prev) { + bw_idx = 3; + bw_prev = bw; + } + + val[2] = mt7603_mac_tx_rate_val(dev, &rates[2], stbc, &bw); + if (bw_prev) { + bw_idx = 5; + bw_prev = bw; + } + + val[3] = mt7603_mac_tx_rate_val(dev, &rates[3], stbc, &bw); + if (bw_prev) + bw_idx = 7; + + w9 |= FIELD_PREP(MT_WTBL2_W9_CHANGE_BW_RATE, + bw_idx ? bw_idx - 1 : 7); + + mt76_wr(dev, MT_WTBL_RIUCR0, w9); + + mt76_wr(dev, MT_WTBL_RIUCR1, + FIELD_PREP(MT_WTBL_RIUCR1_RATE0, probe_val) | + FIELD_PREP(MT_WTBL_RIUCR1_RATE1, val[0]) | + FIELD_PREP(MT_WTBL_RIUCR1_RATE2_LO, val[0])); + + mt76_wr(dev, MT_WTBL_RIUCR2, + FIELD_PREP(MT_WTBL_RIUCR2_RATE2_HI, val[0] >> 8) | + FIELD_PREP(MT_WTBL_RIUCR2_RATE3, val[1]) | + FIELD_PREP(MT_WTBL_RIUCR2_RATE4, val[1]) | + FIELD_PREP(MT_WTBL_RIUCR2_RATE5_LO, val[2])); + + mt76_wr(dev, MT_WTBL_RIUCR3, + FIELD_PREP(MT_WTBL_RIUCR3_RATE5_HI, val[2] >> 4) | + FIELD_PREP(MT_WTBL_RIUCR3_RATE6, val[2]) | + FIELD_PREP(MT_WTBL_RIUCR3_RATE7, val[3])); + + mt76_wr(dev, MT_WTBL_UPDATE, + FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, wcid) | + MT_WTBL_UPDATE_RATE_UPDATE | + MT_WTBL_UPDATE_TX_COUNT_CLEAR); + + if (!sta->wcid.tx_rate_set) + mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000); + + sta->rate_count = 2 * MT7603_RATE_RETRY * n_rates; + sta->wcid.tx_rate_set = true; +} + +static enum mt7603_cipher_type +mt7603_mac_get_key_info(struct ieee80211_key_conf *key, u8 *key_data) +{ + memset(key_data, 0, 32); + if (!key) + return MT_CIPHER_NONE; + + if (key->keylen > 32) + return MT_CIPHER_NONE; + + memcpy(key_data, key->key, key->keylen); + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + return MT_CIPHER_WEP40; + case WLAN_CIPHER_SUITE_WEP104: + return MT_CIPHER_WEP104; + case WLAN_CIPHER_SUITE_TKIP: + /* Rx/Tx MIC keys are swapped */ + memcpy(key_data + 16, key->key + 24, 8); + memcpy(key_data + 24, key->key + 16, 8); + return MT_CIPHER_TKIP; + case WLAN_CIPHER_SUITE_CCMP: + return MT_CIPHER_AES_CCMP; + default: + return MT_CIPHER_NONE; + } +} + +int mt7603_wtbl_set_key(struct mt7603_dev *dev, int wcid, + struct ieee80211_key_conf *key) +{ + enum mt7603_cipher_type cipher; + u32 addr = mt7603_wtbl3_addr(wcid); + u8 key_data[32]; + int key_len = sizeof(key_data); + + cipher = mt7603_mac_get_key_info(key, key_data); + if (cipher == MT_CIPHER_NONE && key) + return -EOPNOTSUPP; + + if (key && (cipher == MT_CIPHER_WEP40 || cipher == MT_CIPHER_WEP104)) { + addr += key->keyidx * 16; + key_len = 16; + } + + mt76_wr_copy(dev, addr, key_data, key_len); + + addr = mt7603_wtbl1_addr(wcid); + mt76_rmw_field(dev, addr + 2 * 4, MT_WTBL1_W2_KEY_TYPE, cipher); + if (key) + mt76_rmw_field(dev, addr, MT_WTBL1_W0_KEY_IDX, key->keyidx); + mt76_rmw_field(dev, addr, MT_WTBL1_W0_RX_KEY_VALID, !!key); + + return 0; +} + +static int +mt7603_mac_write_txwi(struct mt7603_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct mt76_queue *q, + struct mt76_wcid *wcid, struct ieee80211_sta *sta, + int pid, struct ieee80211_key_conf *key) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_rate *rate = &info->control.rates[0]; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_vif *vif = info->control.vif; + struct mt7603_vif *mvif; + int wlan_idx; + int hdr_len = ieee80211_get_hdrlen_from_skb(skb); + int tx_count = 8; + u8 frame_type, frame_subtype; + u16 fc = le16_to_cpu(hdr->frame_control); + u8 vif_idx = 0; + u32 val; + u8 bw; + + if (vif) { + mvif = (struct mt7603_vif *)vif->drv_priv; + vif_idx = mvif->idx; + if (vif_idx && q >= &dev->mt76.q_tx[MT_TXQ_BEACON]) + vif_idx += 0x10; + } + + if (sta) { + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; + + tx_count = msta->rate_count; + } + + if (wcid) + wlan_idx = wcid->idx; + else + wlan_idx = MT7603_WTBL_RESERVED; + + frame_type = (fc & IEEE80211_FCTL_FTYPE) >> 2; + frame_subtype = (fc & IEEE80211_FCTL_STYPE) >> 4; + + val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len + MT_TXD_SIZE) | + FIELD_PREP(MT_TXD0_Q_IDX, q->hw_idx); + txwi[0] = cpu_to_le32(val); + + val = MT_TXD1_LONG_FORMAT | + FIELD_PREP(MT_TXD1_OWN_MAC, vif_idx) | + FIELD_PREP(MT_TXD1_TID, + skb->priority & IEEE80211_QOS_CTL_TID_MASK) | + FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_11) | + FIELD_PREP(MT_TXD1_HDR_INFO, hdr_len / 2) | + FIELD_PREP(MT_TXD1_WLAN_IDX, wlan_idx) | + FIELD_PREP(MT_TXD1_PROTECTED, !!key); + txwi[1] = cpu_to_le32(val); + + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + txwi[1] |= cpu_to_le32(MT_TXD1_NO_ACK); + + val = FIELD_PREP(MT_TXD2_FRAME_TYPE, frame_type) | + FIELD_PREP(MT_TXD2_SUB_TYPE, frame_subtype) | + FIELD_PREP(MT_TXD2_MULTICAST, + is_multicast_ether_addr(hdr->addr1)); + txwi[2] = cpu_to_le32(val); + + if (!(info->flags & IEEE80211_TX_CTL_AMPDU)) + txwi[2] |= cpu_to_le32(MT_TXD2_BA_DISABLE); + + txwi[4] = 0; + + val = MT_TXD5_TX_STATUS_HOST | MT_TXD5_SW_POWER_MGMT | + FIELD_PREP(MT_TXD5_PID, pid); + txwi[5] = cpu_to_le32(val); + + txwi[6] = 0; + + if (rate->idx >= 0 && rate->count && + !(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)) { + bool stbc = info->flags & IEEE80211_TX_CTL_STBC; + u16 rateval = mt7603_mac_tx_rate_val(dev, rate, stbc, &bw); + + txwi[2] |= cpu_to_le32(MT_TXD2_FIX_RATE); + + val = MT_TXD6_FIXED_BW | + FIELD_PREP(MT_TXD6_BW, bw) | + FIELD_PREP(MT_TXD6_TX_RATE, rateval); + txwi[6] |= cpu_to_le32(val); + + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) + txwi[6] |= cpu_to_le32(MT_TXD6_SGI); + + if (!(rate->flags & IEEE80211_TX_RC_MCS)) + txwi[2] |= cpu_to_le32(MT_TXD2_BA_DISABLE); + + tx_count = rate->count; + } + + /* use maximum tx count for beacons and buffered multicast */ + if (q >= &dev->mt76.q_tx[MT_TXQ_BEACON]) + tx_count = 0x1f; + + val = FIELD_PREP(MT_TXD3_REM_TX_COUNT, tx_count) | + FIELD_PREP(MT_TXD3_SEQ, le16_to_cpu(hdr->seq_ctrl)); + txwi[3] = cpu_to_le32(val); + + if (key) { + u64 pn = atomic64_inc_return(&key->tx_pn); + + txwi[3] |= cpu_to_le32(MT_TXD3_PN_VALID); + txwi[4] = cpu_to_le32(pn & GENMASK(31, 0)); + txwi[5] |= cpu_to_le32(FIELD_PREP(MT_TXD5_PN_HIGH, pn >> 32)); + } + + txwi[7] = 0; + + return 0; +} + +int mt7603_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + struct sk_buff *skb, struct mt76_queue *q, + struct mt76_wcid *wcid, struct ieee80211_sta *sta, + u32 *tx_info) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + struct mt7603_sta *msta = container_of(wcid, struct mt7603_sta, wcid); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_key_conf *key = info->control.hw_key; + int pid; + + if (!wcid) + wcid = &dev->global_sta.wcid; + + if (sta) { + msta = (struct mt7603_sta *)sta->drv_priv; + + if ((info->flags & (IEEE80211_TX_CTL_NO_PS_BUFFER | + IEEE80211_TX_CTL_CLEAR_PS_FILT)) || + (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE)) + mt7603_wtbl_set_ps(dev, msta, false); + } + + pid = mt76_tx_status_skb_add(mdev, wcid, skb); + + if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) { + spin_lock_bh(&dev->mt76.lock); + msta->rate_probe = true; + mt7603_wtbl_set_rates(dev, msta, &info->control.rates[0], + msta->rates); + spin_unlock_bh(&dev->mt76.lock); + } + + mt7603_mac_write_txwi(dev, txwi_ptr, skb, q, wcid, sta, pid, key); + + return 0; +} + +static bool +mt7603_fill_txs(struct mt7603_dev *dev, struct mt7603_sta *sta, + struct ieee80211_tx_info *info, __le32 *txs_data) +{ + struct ieee80211_supported_band *sband; + int final_idx = 0; + u32 final_rate; + u32 final_rate_flags; + bool final_mpdu; + bool ack_timeout; + bool fixed_rate; + bool probe; + bool ampdu; + bool cck = false; + int count; + u32 txs; + u8 pid; + int idx; + int i; + + fixed_rate = info->status.rates[0].count; + probe = !!(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE); + + txs = le32_to_cpu(txs_data[4]); + final_mpdu = txs & MT_TXS4_ACKED_MPDU; + ampdu = !fixed_rate && (txs & MT_TXS4_AMPDU); + pid = FIELD_GET(MT_TXS4_PID, txs); + count = FIELD_GET(MT_TXS4_TX_COUNT, txs); + + txs = le32_to_cpu(txs_data[0]); + final_rate = FIELD_GET(MT_TXS0_TX_RATE, txs); + ack_timeout = txs & MT_TXS0_ACK_TIMEOUT; + + if (!ampdu && (txs & MT_TXS0_RTS_TIMEOUT)) + return false; + + if (txs & MT_TXS0_QUEUE_TIMEOUT) + return false; + + if (!ack_timeout) + info->flags |= IEEE80211_TX_STAT_ACK; + + info->status.ampdu_len = 1; + info->status.ampdu_ack_len = !!(info->flags & + IEEE80211_TX_STAT_ACK); + + if (ampdu || (info->flags & IEEE80211_TX_CTL_AMPDU)) + info->flags |= IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_CTL_AMPDU; + + if (fixed_rate && !probe) { + info->status.rates[0].count = count; + goto out; + } + + for (i = 0, idx = 0; i < ARRAY_SIZE(info->status.rates); i++) { + int cur_count = min_t(int, count, 2 * MT7603_RATE_RETRY); + + if (!i && probe) { + cur_count = 1; + } else { + info->status.rates[i] = sta->rates[idx]; + idx++; + } + + if (i && info->status.rates[i].idx < 0) { + info->status.rates[i - 1].count += count; + break; + } + + if (!count) { + info->status.rates[i].idx = -1; + break; + } + + info->status.rates[i].count = cur_count; + final_idx = i; + count -= cur_count; + } + +out: + final_rate_flags = info->status.rates[final_idx].flags; + + switch (FIELD_GET(MT_TX_RATE_MODE, final_rate)) { + case MT_PHY_TYPE_CCK: + cck = true; + /* fall through */ + case MT_PHY_TYPE_OFDM: + if (dev->mt76.chandef.chan->band == NL80211_BAND_5GHZ) + sband = &dev->mt76.sband_5g.sband; + else + sband = &dev->mt76.sband_2g.sband; + final_rate &= GENMASK(5, 0); + final_rate = mt7603_get_rate(dev, sband, final_rate, cck); + final_rate_flags = 0; + break; + case MT_PHY_TYPE_HT_GF: + case MT_PHY_TYPE_HT: + final_rate_flags |= IEEE80211_TX_RC_MCS; + final_rate &= GENMASK(5, 0); + if (i > 15) + return false; + break; + default: + return false; + } + + info->status.rates[final_idx].idx = final_rate; + info->status.rates[final_idx].flags = final_rate_flags; + + return true; +} + +static bool +mt7603_mac_add_txs_skb(struct mt7603_dev *dev, struct mt7603_sta *sta, int pid, + __le32 *txs_data) +{ + struct mt76_dev *mdev = &dev->mt76; + struct sk_buff_head list; + struct sk_buff *skb; + + if (pid < MT_PACKET_ID_FIRST) + return false; + + mt76_tx_status_lock(mdev, &list); + skb = mt76_tx_status_skb_get(mdev, &sta->wcid, pid, &list); + if (skb) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) { + spin_lock_bh(&dev->mt76.lock); + if (sta->rate_probe) { + mt7603_wtbl_set_rates(dev, sta, NULL, + sta->rates); + sta->rate_probe = false; + } + spin_unlock_bh(&dev->mt76.lock); + } + + if (!mt7603_fill_txs(dev, sta, info, txs_data)) { + ieee80211_tx_info_clear_status(info); + info->status.rates[0].idx = -1; + } + + mt76_tx_status_skb_done(mdev, skb, &list); + } + mt76_tx_status_unlock(mdev, &list); + + return !!skb; +} + +void mt7603_mac_add_txs(struct mt7603_dev *dev, void *data) +{ + struct ieee80211_tx_info info = {}; + struct ieee80211_sta *sta = NULL; + struct mt7603_sta *msta = NULL; + struct mt76_wcid *wcid; + __le32 *txs_data = data; + u32 txs; + u8 wcidx; + u8 pid; + + txs = le32_to_cpu(txs_data[4]); + pid = FIELD_GET(MT_TXS4_PID, txs); + txs = le32_to_cpu(txs_data[3]); + wcidx = FIELD_GET(MT_TXS3_WCID, txs); + + if (pid == MT_PACKET_ID_NO_ACK) + return; + + if (wcidx >= ARRAY_SIZE(dev->mt76.wcid)) + return; + + rcu_read_lock(); + + wcid = rcu_dereference(dev->mt76.wcid[wcidx]); + if (!wcid) + goto out; + + msta = container_of(wcid, struct mt7603_sta, wcid); + sta = wcid_to_sta(wcid); + + if (mt7603_mac_add_txs_skb(dev, msta, pid, txs_data)) + goto out; + + if (wcidx >= MT7603_WTBL_STA || !sta) + goto out; + + if (mt7603_fill_txs(dev, msta, &info, txs_data)) + ieee80211_tx_status_noskb(mt76_hw(dev), sta, &info); + +out: + rcu_read_unlock(); +} + +void mt7603_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue *q, + struct mt76_queue_entry *e, bool flush) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + struct sk_buff *skb = e->skb; + + if (!e->txwi) { + dev_kfree_skb_any(skb); + return; + } + + if (q - dev->mt76.q_tx < 4) + dev->tx_hang_check = 0; + + mt76_tx_complete_skb(mdev, skb); +} + +static bool +wait_for_wpdma(struct mt7603_dev *dev) +{ + return mt76_poll(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_TX_DMA_BUSY | + MT_WPDMA_GLO_CFG_RX_DMA_BUSY, + 0, 1000); +} + +static void mt7603_pse_reset(struct mt7603_dev *dev) +{ + /* Clear previous reset result */ + if (!dev->reset_cause[RESET_CAUSE_RESET_FAILED]) + mt76_clear(dev, MT_MCU_DEBUG_RESET, MT_MCU_DEBUG_RESET_PSE_S); + + /* Reset PSE */ + mt76_set(dev, MT_MCU_DEBUG_RESET, MT_MCU_DEBUG_RESET_PSE); + + if (!mt76_poll_msec(dev, MT_MCU_DEBUG_RESET, + MT_MCU_DEBUG_RESET_PSE_S, + MT_MCU_DEBUG_RESET_PSE_S, 500)) { + dev->reset_cause[RESET_CAUSE_RESET_FAILED]++; + mt76_clear(dev, MT_MCU_DEBUG_RESET, MT_MCU_DEBUG_RESET_PSE); + } else { + dev->reset_cause[RESET_CAUSE_RESET_FAILED] = 0; + mt76_clear(dev, MT_MCU_DEBUG_RESET, MT_MCU_DEBUG_RESET_QUEUES); + } + + if (dev->reset_cause[RESET_CAUSE_RESET_FAILED] >= 3) + dev->reset_cause[RESET_CAUSE_RESET_FAILED] = 0; +} + +void mt7603_mac_dma_start(struct mt7603_dev *dev) +{ + mt7603_mac_start(dev); + + wait_for_wpdma(dev); + usleep_range(50, 100); + + mt76_set(dev, MT_WPDMA_GLO_CFG, + (MT_WPDMA_GLO_CFG_TX_DMA_EN | + MT_WPDMA_GLO_CFG_RX_DMA_EN | + FIELD_PREP(MT_WPDMA_GLO_CFG_DMA_BURST_SIZE, 3) | + MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE)); + + mt7603_irq_enable(dev, MT_INT_RX_DONE_ALL | MT_INT_TX_DONE_ALL); +} + +void mt7603_mac_start(struct mt7603_dev *dev) +{ + mt76_clear(dev, MT_ARB_SCR, + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); + mt76_wr(dev, MT_WF_ARB_TX_START_0, ~0); + mt76_set(dev, MT_WF_ARB_RQCR, MT_WF_ARB_RQCR_RX_START); +} + +void mt7603_mac_stop(struct mt7603_dev *dev) +{ + mt76_set(dev, MT_ARB_SCR, + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); + mt76_wr(dev, MT_WF_ARB_TX_START_0, 0); + mt76_clear(dev, MT_WF_ARB_RQCR, MT_WF_ARB_RQCR_RX_START); +} + +void mt7603_pse_client_reset(struct mt7603_dev *dev) +{ + u32 addr; + + addr = mt7603_reg_map(dev, MT_CLIENT_BASE_PHYS_ADDR + + MT_CLIENT_RESET_TX); + + /* Clear previous reset state */ + mt76_clear(dev, addr, + MT_CLIENT_RESET_TX_R_E_1 | + MT_CLIENT_RESET_TX_R_E_2 | + MT_CLIENT_RESET_TX_R_E_1_S | + MT_CLIENT_RESET_TX_R_E_2_S); + + /* Start PSE client TX abort */ + mt76_set(dev, addr, MT_CLIENT_RESET_TX_R_E_1); + mt76_poll_msec(dev, addr, MT_CLIENT_RESET_TX_R_E_1_S, + MT_CLIENT_RESET_TX_R_E_1_S, 500); + + mt76_set(dev, addr, MT_CLIENT_RESET_TX_R_E_2); + mt76_set(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_SW_RESET); + + /* Wait for PSE client to clear TX FIFO */ + mt76_poll_msec(dev, addr, MT_CLIENT_RESET_TX_R_E_2_S, + MT_CLIENT_RESET_TX_R_E_2_S, 500); + + /* Clear PSE client TX abort state */ + mt76_clear(dev, addr, + MT_CLIENT_RESET_TX_R_E_1 | + MT_CLIENT_RESET_TX_R_E_2); +} + +static void mt7603_dma_sched_reset(struct mt7603_dev *dev) +{ + if (!is_mt7628(dev)) + return; + + mt76_set(dev, MT_SCH_4, MT_SCH_4_RESET); + mt76_clear(dev, MT_SCH_4, MT_SCH_4_RESET); +} + +static void mt7603_mac_watchdog_reset(struct mt7603_dev *dev) +{ + int beacon_int = dev->beacon_int; + u32 mask = dev->mt76.mmio.irqmask; + int i; + + ieee80211_stop_queues(dev->mt76.hw); + set_bit(MT76_RESET, &dev->mt76.state); + + /* lock/unlock all queues to ensure that no tx is pending */ + mt76_txq_schedule_all(&dev->mt76); + + tasklet_disable(&dev->tx_tasklet); + tasklet_disable(&dev->pre_tbtt_tasklet); + napi_disable(&dev->mt76.napi[0]); + napi_disable(&dev->mt76.napi[1]); + + mutex_lock(&dev->mt76.mutex); + + mt7603_beacon_set_timer(dev, -1, 0); + + if (dev->reset_cause[RESET_CAUSE_RESET_FAILED] || + dev->cur_reset_cause == RESET_CAUSE_RX_PSE_BUSY || + dev->cur_reset_cause == RESET_CAUSE_BEACON_STUCK || + dev->cur_reset_cause == RESET_CAUSE_TX_HANG) + mt7603_pse_reset(dev); + + if (dev->reset_cause[RESET_CAUSE_RESET_FAILED]) + goto skip_dma_reset; + + mt7603_mac_stop(dev); + + mt76_clear(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_RX_DMA_EN | MT_WPDMA_GLO_CFG_TX_DMA_EN | + MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE); + usleep_range(1000, 2000); + + mt7603_irq_disable(dev, mask); + + mt76_set(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_FORCE_TX_EOF); + + mt7603_pse_client_reset(dev); + + for (i = 0; i < ARRAY_SIZE(dev->mt76.q_tx); i++) + mt76_queue_tx_cleanup(dev, i, true); + + for (i = 0; i < ARRAY_SIZE(dev->mt76.q_rx); i++) + mt76_queue_rx_reset(dev, i); + + mt7603_dma_sched_reset(dev); + + mt7603_mac_dma_start(dev); + + mt7603_irq_enable(dev, mask); + +skip_dma_reset: + clear_bit(MT76_RESET, &dev->mt76.state); + mutex_unlock(&dev->mt76.mutex); + + tasklet_enable(&dev->tx_tasklet); + tasklet_schedule(&dev->tx_tasklet); + + tasklet_enable(&dev->pre_tbtt_tasklet); + mt7603_beacon_set_timer(dev, -1, beacon_int); + + napi_enable(&dev->mt76.napi[0]); + napi_schedule(&dev->mt76.napi[0]); + + napi_enable(&dev->mt76.napi[1]); + napi_schedule(&dev->mt76.napi[1]); + + ieee80211_wake_queues(dev->mt76.hw); + mt76_txq_schedule_all(&dev->mt76); +} + +static u32 mt7603_dma_debug(struct mt7603_dev *dev, u8 index) +{ + u32 val; + + mt76_wr(dev, MT_WPDMA_DEBUG, + FIELD_PREP(MT_WPDMA_DEBUG_IDX, index) | + MT_WPDMA_DEBUG_SEL); + + val = mt76_rr(dev, MT_WPDMA_DEBUG); + return FIELD_GET(MT_WPDMA_DEBUG_VALUE, val); +} + +static bool mt7603_rx_fifo_busy(struct mt7603_dev *dev) +{ + if (is_mt7628(dev)) + return mt7603_dma_debug(dev, 9) & BIT(9); + + return mt7603_dma_debug(dev, 2) & BIT(8); +} + +static bool mt7603_rx_dma_busy(struct mt7603_dev *dev) +{ + if (!(mt76_rr(dev, MT_WPDMA_GLO_CFG) & MT_WPDMA_GLO_CFG_RX_DMA_BUSY)) + return false; + + return mt7603_rx_fifo_busy(dev); +} + +static bool mt7603_tx_dma_busy(struct mt7603_dev *dev) +{ + u32 val; + + if (!(mt76_rr(dev, MT_WPDMA_GLO_CFG) & MT_WPDMA_GLO_CFG_TX_DMA_BUSY)) + return false; + + val = mt7603_dma_debug(dev, 9); + return (val & BIT(8)) && (val & 0xf) != 0xf; +} + +static bool mt7603_tx_hang(struct mt7603_dev *dev) +{ + struct mt76_queue *q; + u32 dma_idx, prev_dma_idx; + int i; + + for (i = 0; i < 4; i++) { + q = &dev->mt76.q_tx[i]; + + if (!q->queued) + continue; + + prev_dma_idx = dev->tx_dma_idx[i]; + dma_idx = ioread32(&q->regs->dma_idx); + dev->tx_dma_idx[i] = dma_idx; + + if (dma_idx == prev_dma_idx && + dma_idx != ioread32(&q->regs->cpu_idx)) + break; + } + + return i < 4; +} + +static bool mt7603_rx_pse_busy(struct mt7603_dev *dev) +{ + u32 addr, val; + + if (mt76_rr(dev, MT_MCU_DEBUG_RESET) & MT_MCU_DEBUG_RESET_QUEUES) + return true; + + if (mt7603_rx_fifo_busy(dev)) + return false; + + addr = mt7603_reg_map(dev, MT_CLIENT_BASE_PHYS_ADDR + MT_CLIENT_STATUS); + mt76_wr(dev, addr, 3); + val = mt76_rr(dev, addr) >> 16; + + if (is_mt7628(dev) && (val & 0x4001) == 0x4001) + return true; + + return (val & 0x8001) == 0x8001 || (val & 0xe001) == 0xe001; +} + +static bool +mt7603_watchdog_check(struct mt7603_dev *dev, u8 *counter, + enum mt7603_reset_cause cause, + bool (*check)(struct mt7603_dev *dev)) +{ + if (dev->reset_test == cause + 1) { + dev->reset_test = 0; + goto trigger; + } + + if (check) { + if (!check(dev) && *counter < MT7603_WATCHDOG_TIMEOUT) { + *counter = 0; + return false; + } + + (*counter)++; + } + + if (*counter < MT7603_WATCHDOG_TIMEOUT) + return false; +trigger: + dev->cur_reset_cause = cause; + dev->reset_cause[cause]++; + return true; +} + +void mt7603_update_channel(struct mt76_dev *mdev) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + struct mt76_channel_state *state; + ktime_t cur_time; + u32 busy; + + if (!test_bit(MT76_STATE_RUNNING, &dev->mt76.state)) + return; + + state = mt76_channel_state(&dev->mt76, dev->mt76.chandef.chan); + busy = mt76_rr(dev, MT_MIB_STAT_PSCCA); + + spin_lock_bh(&dev->mt76.cc_lock); + cur_time = ktime_get_boottime(); + state->cc_busy += busy; + state->cc_active += ktime_to_us(ktime_sub(cur_time, dev->survey_time)); + dev->survey_time = cur_time; + spin_unlock_bh(&dev->mt76.cc_lock); +} + +void +mt7603_edcca_set_strict(struct mt7603_dev *dev, bool val) +{ + u32 rxtd_6 = 0xd7c80000; + + if (val == dev->ed_strict_mode) + return; + + dev->ed_strict_mode = val; + + /* Ensure that ED/CCA does not trigger if disabled */ + if (!dev->ed_monitor) + rxtd_6 |= FIELD_PREP(MT_RXTD_6_CCAED_TH, 0x34); + else + rxtd_6 |= FIELD_PREP(MT_RXTD_6_CCAED_TH, 0x7d); + + if (dev->ed_monitor && !dev->ed_strict_mode) + rxtd_6 |= FIELD_PREP(MT_RXTD_6_ACI_TH, 0x0f); + else + rxtd_6 |= FIELD_PREP(MT_RXTD_6_ACI_TH, 0x10); + + mt76_wr(dev, MT_RXTD(6), rxtd_6); + + mt76_rmw_field(dev, MT_RXTD(13), MT_RXTD_13_ACI_TH_EN, + dev->ed_monitor && !dev->ed_strict_mode); +} + +static void +mt7603_edcca_check(struct mt7603_dev *dev) +{ + u32 val = mt76_rr(dev, MT_AGC(41)); + ktime_t cur_time; + int rssi0, rssi1; + u32 active; + u32 ed_busy; + + if (!dev->ed_monitor) + return; + + rssi0 = FIELD_GET(MT_AGC_41_RSSI_0, val); + if (rssi0 > 128) + rssi0 -= 256; + + rssi1 = FIELD_GET(MT_AGC_41_RSSI_1, val); + if (rssi1 > 128) + rssi1 -= 256; + + if (max(rssi0, rssi1) >= -40 && + dev->ed_strong_signal < MT7603_EDCCA_BLOCK_TH) + dev->ed_strong_signal++; + else if (dev->ed_strong_signal > 0) + dev->ed_strong_signal--; + + cur_time = ktime_get_boottime(); + ed_busy = mt76_rr(dev, MT_MIB_STAT_ED) & MT_MIB_STAT_ED_MASK; + + active = ktime_to_us(ktime_sub(cur_time, dev->ed_time)); + dev->ed_time = cur_time; + + if (!active) + return; + + if (100 * ed_busy / active > 90) { + if (dev->ed_trigger < 0) + dev->ed_trigger = 0; + dev->ed_trigger++; + } else { + if (dev->ed_trigger > 0) + dev->ed_trigger = 0; + dev->ed_trigger--; + } + + if (dev->ed_trigger > MT7603_EDCCA_BLOCK_TH || + dev->ed_strong_signal < MT7603_EDCCA_BLOCK_TH / 2) { + mt7603_edcca_set_strict(dev, true); + } else if (dev->ed_trigger < -MT7603_EDCCA_BLOCK_TH) { + mt7603_edcca_set_strict(dev, false); + } + + if (dev->ed_trigger > MT7603_EDCCA_BLOCK_TH) + dev->ed_trigger = MT7603_EDCCA_BLOCK_TH; + else if (dev->ed_trigger < -MT7603_EDCCA_BLOCK_TH) + dev->ed_trigger = -MT7603_EDCCA_BLOCK_TH; +} + +void mt7603_cca_stats_reset(struct mt7603_dev *dev) +{ + mt76_set(dev, MT_PHYCTRL(2), MT_PHYCTRL_2_STATUS_RESET); + mt76_clear(dev, MT_PHYCTRL(2), MT_PHYCTRL_2_STATUS_RESET); + mt76_set(dev, MT_PHYCTRL(2), MT_PHYCTRL_2_STATUS_EN); +} + +static void +mt7603_adjust_sensitivity(struct mt7603_dev *dev) +{ + u32 agc0 = dev->agc0, agc3 = dev->agc3; + u32 adj; + + if (!dev->sensitivity || dev->sensitivity < -100) { + dev->sensitivity = 0; + } else if (dev->sensitivity <= -84) { + adj = 7 + (dev->sensitivity + 92) / 2; + + agc0 = 0x56f0076f; + agc0 |= adj << 12; + agc0 |= adj << 16; + agc3 = 0x81d0d5e3; + } else if (dev->sensitivity <= -72) { + adj = 7 + (dev->sensitivity + 80) / 2; + + agc0 = 0x6af0006f; + agc0 |= adj << 8; + agc0 |= adj << 12; + agc0 |= adj << 16; + + agc3 = 0x8181d5e3; + } else { + if (dev->sensitivity > -54) + dev->sensitivity = -54; + + adj = 7 + (dev->sensitivity + 80) / 2; + + agc0 = 0x7ff0000f; + agc0 |= adj << 4; + agc0 |= adj << 8; + agc0 |= adj << 12; + agc0 |= adj << 16; + + agc3 = 0x818181e3; + } + + mt76_wr(dev, MT_AGC(0), agc0); + mt76_wr(dev, MT_AGC1(0), agc0); + + mt76_wr(dev, MT_AGC(3), agc3); + mt76_wr(dev, MT_AGC1(3), agc3); +} + +static void +mt7603_false_cca_check(struct mt7603_dev *dev) +{ + int pd_cck, pd_ofdm, mdrdy_cck, mdrdy_ofdm; + int false_cca; + int min_signal; + u32 val; + + val = mt76_rr(dev, MT_PHYCTRL_STAT_PD); + pd_cck = FIELD_GET(MT_PHYCTRL_STAT_PD_CCK, val); + pd_ofdm = FIELD_GET(MT_PHYCTRL_STAT_PD_OFDM, val); + + val = mt76_rr(dev, MT_PHYCTRL_STAT_MDRDY); + mdrdy_cck = FIELD_GET(MT_PHYCTRL_STAT_MDRDY_CCK, val); + mdrdy_ofdm = FIELD_GET(MT_PHYCTRL_STAT_MDRDY_OFDM, val); + + dev->false_cca_ofdm = pd_ofdm - mdrdy_ofdm; + dev->false_cca_cck = pd_cck - mdrdy_cck; + + mt7603_cca_stats_reset(dev); + + min_signal = mt76_get_min_avg_rssi(&dev->mt76); + if (!min_signal) { + dev->sensitivity = 0; + dev->last_cca_adj = jiffies; + goto out; + } + + min_signal -= 15; + + false_cca = dev->false_cca_ofdm + dev->false_cca_cck; + if (false_cca > 600) { + if (!dev->sensitivity) + dev->sensitivity = -92; + else + dev->sensitivity += 2; + dev->last_cca_adj = jiffies; + } else if (false_cca < 100 || + time_after(jiffies, dev->last_cca_adj + 10 * HZ)) { + dev->last_cca_adj = jiffies; + if (!dev->sensitivity) + goto out; + + dev->sensitivity -= 2; + } + + if (dev->sensitivity && dev->sensitivity > min_signal) { + dev->sensitivity = min_signal; + dev->last_cca_adj = jiffies; + } + +out: + mt7603_adjust_sensitivity(dev); +} + +void mt7603_mac_work(struct work_struct *work) +{ + struct mt7603_dev *dev = container_of(work, struct mt7603_dev, + mac_work.work); + bool reset = false; + + mt76_tx_status_check(&dev->mt76, NULL, false); + + mutex_lock(&dev->mt76.mutex); + + dev->mac_work_count++; + mt7603_update_channel(&dev->mt76); + mt7603_edcca_check(dev); + + if (dev->mac_work_count == 10) + mt7603_false_cca_check(dev); + + if (mt7603_watchdog_check(dev, &dev->rx_pse_check, + RESET_CAUSE_RX_PSE_BUSY, + mt7603_rx_pse_busy) || + mt7603_watchdog_check(dev, &dev->beacon_check, + RESET_CAUSE_BEACON_STUCK, + NULL) || + mt7603_watchdog_check(dev, &dev->tx_hang_check, + RESET_CAUSE_TX_HANG, + mt7603_tx_hang) || + mt7603_watchdog_check(dev, &dev->tx_dma_check, + RESET_CAUSE_TX_BUSY, + mt7603_tx_dma_busy) || + mt7603_watchdog_check(dev, &dev->rx_dma_check, + RESET_CAUSE_RX_BUSY, + mt7603_rx_dma_busy) || + mt7603_watchdog_check(dev, &dev->mcu_hang, + RESET_CAUSE_MCU_HANG, + NULL) || + dev->reset_cause[RESET_CAUSE_RESET_FAILED]) { + dev->beacon_check = 0; + dev->tx_dma_check = 0; + dev->tx_hang_check = 0; + dev->rx_dma_check = 0; + dev->rx_pse_check = 0; + dev->mcu_hang = 0; + dev->rx_dma_idx = ~0; + memset(dev->tx_dma_idx, 0xff, sizeof(dev->tx_dma_idx)); + reset = true; + dev->mac_work_count = 0; + } + + if (dev->mac_work_count >= 10) + dev->mac_work_count = 0; + + mutex_unlock(&dev->mt76.mutex); + + if (reset) + mt7603_mac_watchdog_reset(dev); + + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mac_work, + msecs_to_jiffies(MT7603_WATCHDOG_TIME)); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.h b/drivers/net/wireless/mediatek/mt76/mt7603/mac.h new file mode 100644 index 000000000000..1704e6f932a3 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.h @@ -0,0 +1,257 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __MT7603_MAC_H +#define __MT7603_MAC_H + +#define MT_RXD0_LENGTH GENMASK(15, 0) +#define MT_RXD0_PKT_TYPE GENMASK(31, 29) + +#define MT_RXD0_NORMAL_ETH_TYPE_OFS GENMASK(22, 16) +#define MT_RXD0_NORMAL_IP_SUM BIT(23) +#define MT_RXD0_NORMAL_UDP_TCP_SUM BIT(24) +#define MT_RXD0_NORMAL_GROUP_1 BIT(25) +#define MT_RXD0_NORMAL_GROUP_2 BIT(26) +#define MT_RXD0_NORMAL_GROUP_3 BIT(27) +#define MT_RXD0_NORMAL_GROUP_4 BIT(28) + +enum rx_pkt_type { + PKT_TYPE_TXS = 0, + PKT_TYPE_TXRXV = 1, + PKT_TYPE_NORMAL = 2, + PKT_TYPE_RX_DUP_RFB = 3, + PKT_TYPE_RX_TMR = 4, + PKT_TYPE_RETRIEVE = 5, + PKT_TYPE_RX_EVENT = 7, +}; + +#define MT_RXD1_NORMAL_BSSID GENMASK(31, 26) +#define MT_RXD1_NORMAL_PAYLOAD_FORMAT GENMASK(25, 24) +#define MT_RXD1_NORMAL_HDR_TRANS BIT(23) +#define MT_RXD1_NORMAL_HDR_OFFSET BIT(22) +#define MT_RXD1_NORMAL_MAC_HDR_LEN GENMASK(21, 16) +#define MT_RXD1_NORMAL_CH_FREQ GENMASK(15, 8) +#define MT_RXD1_NORMAL_KEY_ID GENMASK(7, 6) +#define MT_RXD1_NORMAL_BEACON_UC BIT(5) +#define MT_RXD1_NORMAL_BEACON_MC BIT(4) +#define MT_RXD1_NORMAL_BCAST BIT(3) +#define MT_RXD1_NORMAL_MCAST BIT(2) +#define MT_RXD1_NORMAL_U2M BIT(1) +#define MT_RXD1_NORMAL_HTC_VLD BIT(0) + +#define MT_RXD2_NORMAL_NON_AMPDU BIT(31) +#define MT_RXD2_NORMAL_NON_AMPDU_SUB BIT(30) +#define MT_RXD2_NORMAL_NDATA BIT(29) +#define MT_RXD2_NORMAL_NULL_FRAME BIT(28) +#define MT_RXD2_NORMAL_FRAG BIT(27) +#define MT_RXD2_NORMAL_UDF_VALID BIT(26) +#define MT_RXD2_NORMAL_LLC_MIS BIT(25) +#define MT_RXD2_NORMAL_MAX_LEN_ERROR BIT(24) +#define MT_RXD2_NORMAL_AMSDU_ERR BIT(23) +#define MT_RXD2_NORMAL_LEN_MISMATCH BIT(22) +#define MT_RXD2_NORMAL_TKIP_MIC_ERR BIT(21) +#define MT_RXD2_NORMAL_ICV_ERR BIT(20) +#define MT_RXD2_NORMAL_CLM BIT(19) +#define MT_RXD2_NORMAL_CM BIT(18) +#define MT_RXD2_NORMAL_FCS_ERR BIT(17) +#define MT_RXD2_NORMAL_SW_BIT BIT(16) +#define MT_RXD2_NORMAL_SEC_MODE GENMASK(15, 12) +#define MT_RXD2_NORMAL_TID GENMASK(11, 8) +#define MT_RXD2_NORMAL_WLAN_IDX GENMASK(7, 0) + +#define MT_RXD3_NORMAL_PF_STS GENMASK(31, 30) +#define MT_RXD3_NORMAL_PF_MODE BIT(29) +#define MT_RXD3_NORMAL_CLS_BITMAP GENMASK(28, 19) +#define MT_RXD3_NORMAL_WOL GENMASK(18, 14) +#define MT_RXD3_NORMAL_MAGIC_PKT BIT(13) +#define MT_RXD3_NORMAL_OFLD GENMASK(12, 11) +#define MT_RXD3_NORMAL_CLS BIT(10) +#define MT_RXD3_NORMAL_PATTERN_DROP BIT(9) +#define MT_RXD3_NORMAL_TSF_COMPARE_LOSS BIT(8) +#define MT_RXD3_NORMAL_RXV_SEQ GENMASK(7, 0) + +#define MT_RXV1_VHTA1_B5_B4 GENMASK(31, 30) +#define MT_RXV1_VHTA2_B8_B1 GENMASK(29, 22) +#define MT_RXV1_HT_NO_SOUND BIT(21) +#define MT_RXV1_HT_SMOOTH BIT(20) +#define MT_RXV1_HT_SHORT_GI BIT(19) +#define MT_RXV1_HT_AGGR BIT(18) +#define MT_RXV1_VHTA1_B22 BIT(17) +#define MT_RXV1_FRAME_MODE GENMASK(16, 15) +#define MT_RXV1_TX_MODE GENMASK(14, 12) +#define MT_RXV1_HT_EXT_LTF GENMASK(11, 10) +#define MT_RXV1_HT_AD_CODE BIT(9) +#define MT_RXV1_HT_STBC GENMASK(8, 7) +#define MT_RXV1_TX_RATE GENMASK(6, 0) + +#define MT_RXV2_VHTA1_B16_B6 GENMASK(31, 21) +#define MT_RXV2_LENGTH GENMASK(20, 0) + +#define MT_RXV3_F_AGC1_CAL_GAIN GENMASK(31, 29) +#define MT_RXV3_F_AGC1_EQ_CAL BIT(28) +#define MT_RXV3_RCPI1 GENMASK(27, 20) +#define MT_RXV3_F_AGC0_CAL_GAIN GENMASK(19, 17) +#define MT_RXV3_F_AGC0_EQ_CAL BIT(16) +#define MT_RXV3_RCPI0 GENMASK(15, 8) +#define MT_RXV3_SEL_ANT BIT(7) +#define MT_RXV3_ACI_DET_X BIT(6) +#define MT_RXV3_OFDM_FREQ_TRANS_DETECT BIT(5) +#define MT_RXV3_VHTA1_B21_B17 GENMASK(4, 0) + +#define MT_RXV4_F_AGC_CAL_GAIN GENMASK(31, 29) +#define MT_RXV4_F_AGC2_EQ_CAL BIT(28) +#define MT_RXV4_IB_RSSI1 GENMASK(27, 20) +#define MT_RXV4_F_AGC_LPF_GAIN_X GENMASK(19, 16) +#define MT_RXV4_WB_RSSI_X GENMASK(15, 8) +#define MT_RXV4_IB_RSSI0 GENMASK(7, 0) + +#define MT_RXV5_LTF_SNR0 GENMASK(31, 26) +#define MT_RXV5_LTF_PROC_TIME GENMASK(25, 19) +#define MT_RXV5_FOE GENMASK(18, 7) +#define MT_RXV5_C_AGC_SATE GENMASK(6, 4) +#define MT_RXV5_F_AGC_LNA_GAIN_0 GENMASK(3, 2) +#define MT_RXV5_F_AGC_LNA_GAIN_1 GENMASK(1, 0) + +#define MT_RXV6_C_AGC_STATE GENMASK(30, 28) +#define MT_RXV6_NS_TS_FIELD GENMASK(27, 25) +#define MT_RXV6_RX_VALID BIT(24) +#define MT_RXV6_NF2 GENMASK(23, 16) +#define MT_RXV6_NF1 GENMASK(15, 8) +#define MT_RXV6_NF0 GENMASK(7, 0) + +enum mt7603_tx_header_format { + MT_HDR_FORMAT_802_3, + MT_HDR_FORMAT_CMD, + MT_HDR_FORMAT_802_11, + MT_HDR_FORMAT_802_11_EXT, +}; + +#define MT_TXD_SIZE (8 * 4) + +#define MT_TXD0_P_IDX BIT(31) +#define MT_TXD0_Q_IDX GENMASK(30, 27) +#define MT_TXD0_UTXB BIT(26) +#define MT_TXD0_UNXV BIT(25) +#define MT_TXD0_UDP_TCP_SUM BIT(24) +#define MT_TXD0_IP_SUM BIT(23) +#define MT_TXD0_ETH_TYPE_OFFSET GENMASK(22, 16) +#define MT_TXD0_TX_BYTES GENMASK(15, 0) + +#define MT_TXD1_OWN_MAC GENMASK(31, 26) +#define MT_TXD1_PROTECTED BIT(23) +#define MT_TXD1_TID GENMASK(22, 20) +#define MT_TXD1_NO_ACK BIT(19) +#define MT_TXD1_HDR_PAD GENMASK(18, 16) +#define MT_TXD1_LONG_FORMAT BIT(15) +#define MT_TXD1_HDR_FORMAT GENMASK(14, 13) +#define MT_TXD1_HDR_INFO GENMASK(12, 8) +#define MT_TXD1_WLAN_IDX GENMASK(7, 0) + +#define MT_TXD2_FIX_RATE BIT(31) +#define MT_TXD2_TIMING_MEASURE BIT(30) +#define MT_TXD2_BA_DISABLE BIT(29) +#define MT_TXD2_POWER_OFFSET GENMASK(28, 24) +#define MT_TXD2_MAX_TX_TIME GENMASK(23, 16) +#define MT_TXD2_FRAG GENMASK(15, 14) +#define MT_TXD2_HTC_VLD BIT(13) +#define MT_TXD2_DURATION BIT(12) +#define MT_TXD2_BIP BIT(11) +#define MT_TXD2_MULTICAST BIT(10) +#define MT_TXD2_RTS BIT(9) +#define MT_TXD2_SOUNDING BIT(8) +#define MT_TXD2_NDPA BIT(7) +#define MT_TXD2_NDP BIT(6) +#define MT_TXD2_FRAME_TYPE GENMASK(5, 4) +#define MT_TXD2_SUB_TYPE GENMASK(3, 0) + +#define MT_TXD3_SN_VALID BIT(31) +#define MT_TXD3_PN_VALID BIT(30) +#define MT_TXD3_SEQ GENMASK(27, 16) +#define MT_TXD3_REM_TX_COUNT GENMASK(15, 11) +#define MT_TXD3_TX_COUNT GENMASK(10, 6) + +#define MT_TXD4_PN_LOW GENMASK(31, 0) + +#define MT_TXD5_PN_HIGH GENMASK(31, 16) +#define MT_TXD5_SW_POWER_MGMT BIT(13) +#define MT_TXD5_BA_SEQ_CTRL BIT(12) +#define MT_TXD5_DA_SELECT BIT(11) +#define MT_TXD5_TX_STATUS_HOST BIT(10) +#define MT_TXD5_TX_STATUS_MCU BIT(9) +#define MT_TXD5_TX_STATUS_FMT BIT(8) +#define MT_TXD5_PID GENMASK(7, 0) + +#define MT_TXD6_SGI BIT(31) +#define MT_TXD6_LDPC BIT(30) +#define MT_TXD6_TX_RATE GENMASK(29, 18) +#define MT_TXD6_I_TXBF BIT(17) +#define MT_TXD6_E_TXBF BIT(16) +#define MT_TXD6_DYN_BW BIT(15) +#define MT_TXD6_ANT_PRI GENMASK(14, 12) +#define MT_TXD6_SPE_EN BIT(11) +#define MT_TXD6_FIXED_BW BIT(10) +#define MT_TXD6_BW GENMASK(9, 8) +#define MT_TXD6_ANT_ID GENMASK(7, 2) +#define MT_TXD6_FIXED_RATE BIT(0) + +#define MT_TX_RATE_STBC BIT(11) +#define MT_TX_RATE_NSS GENMASK(10, 9) +#define MT_TX_RATE_MODE GENMASK(8, 6) +#define MT_TX_RATE_IDX GENMASK(5, 0) + +#define MT_TXS0_ANTENNA GENMASK(31, 26) +#define MT_TXS0_TID GENMASK(25, 22) +#define MT_TXS0_BA_ERROR BIT(22) +#define MT_TXS0_PS_FLAG BIT(21) +#define MT_TXS0_TXOP_TIMEOUT BIT(20) +#define MT_TXS0_BIP_ERROR BIT(19) + +#define MT_TXS0_QUEUE_TIMEOUT BIT(18) +#define MT_TXS0_RTS_TIMEOUT BIT(17) +#define MT_TXS0_ACK_TIMEOUT BIT(16) +#define MT_TXS0_ACK_ERROR_MASK GENMASK(18, 16) + +#define MT_TXS0_TX_STATUS_HOST BIT(15) +#define MT_TXS0_TX_STATUS_MCU BIT(14) +#define MT_TXS0_TXS_FORMAT BIT(13) +#define MT_TXS0_FIXED_RATE BIT(12) +#define MT_TXS0_TX_RATE GENMASK(11, 0) + +#define MT_TXS1_F0_TIMESTAMP GENMASK(31, 0) +#define MT_TXS1_F1_NOISE_2 GENMASK(23, 16) +#define MT_TXS1_F1_NOISE_1 GENMASK(15, 8) +#define MT_TXS1_F1_NOISE_0 GENMASK(7, 0) + +#define MT_TXS2_F0_FRONT_TIME GENMASK(24, 0) +#define MT_TXS2_F1_RCPI_2 GENMASK(23, 16) +#define MT_TXS2_F1_RCPI_1 GENMASK(15, 8) +#define MT_TXS2_F1_RCPI_0 GENMASK(7, 0) + +#define MT_TXS3_WCID GENMASK(31, 24) +#define MT_TXS3_RXV_SEQNO GENMASK(23, 16) +#define MT_TXS3_TX_DELAY GENMASK(15, 0) + +#define MT_TXS4_LAST_TX_RATE GENMASK(31, 29) +#define MT_TXS4_TX_COUNT GENMASK(28, 24) +#define MT_TXS4_AMPDU BIT(23) +#define MT_TXS4_ACKED_MPDU BIT(22) +#define MT_TXS4_PID GENMASK(21, 14) +#define MT_TXS4_BW GENMASK(13, 12) +#define MT_TXS4_F0_SEQNO GENMASK(11, 0) +#define MT_TXS4_F1_TSSI GENMASK(11, 0) + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/main.c b/drivers/net/wireless/mediatek/mt76/mt7603/main.c new file mode 100644 index 000000000000..74ccd6edbb9f --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/main.c @@ -0,0 +1,730 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/etherdevice.h> +#include <linux/platform_device.h> +#include <linux/pci.h> +#include <linux/module.h> +#include "mt7603.h" +#include "eeprom.h" + +static int +mt7603_start(struct ieee80211_hw *hw) +{ + struct mt7603_dev *dev = hw->priv; + + mt7603_mac_start(dev); + dev->survey_time = ktime_get_boottime(); + set_bit(MT76_STATE_RUNNING, &dev->mt76.state); + mt7603_mac_work(&dev->mac_work.work); + + return 0; +} + +static void +mt7603_stop(struct ieee80211_hw *hw) +{ + struct mt7603_dev *dev = hw->priv; + + clear_bit(MT76_STATE_RUNNING, &dev->mt76.state); + cancel_delayed_work_sync(&dev->mac_work); + mt7603_mac_stop(dev); +} + +static int +mt7603_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; + struct mt7603_dev *dev = hw->priv; + struct mt76_txq *mtxq; + u8 bc_addr[ETH_ALEN]; + int idx; + int ret = 0; + + mutex_lock(&dev->mt76.mutex); + + mvif->idx = ffs(~dev->vif_mask) - 1; + if (mvif->idx >= MT7603_MAX_INTERFACES) { + ret = -ENOSPC; + goto out; + } + + mt76_wr(dev, MT_MAC_ADDR0(mvif->idx), + get_unaligned_le32(vif->addr)); + mt76_wr(dev, MT_MAC_ADDR1(mvif->idx), + (get_unaligned_le16(vif->addr + 4) | + MT_MAC_ADDR1_VALID)); + + if (vif->type == NL80211_IFTYPE_AP) { + mt76_wr(dev, MT_BSSID0(mvif->idx), + get_unaligned_le32(vif->addr)); + mt76_wr(dev, MT_BSSID1(mvif->idx), + (get_unaligned_le16(vif->addr + 4) | + MT_BSSID1_VALID)); + } + + idx = MT7603_WTBL_RESERVED - 1 - mvif->idx; + dev->vif_mask |= BIT(mvif->idx); + mvif->sta.wcid.idx = idx; + mvif->sta.wcid.hw_key_idx = -1; + + eth_broadcast_addr(bc_addr); + mt7603_wtbl_init(dev, idx, mvif->idx, bc_addr); + + mtxq = (struct mt76_txq *)vif->txq->drv_priv; + mtxq->wcid = &mvif->sta.wcid; + mt76_txq_init(&dev->mt76, vif->txq); + rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid); + +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static void +mt7603_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; + struct mt7603_dev *dev = hw->priv; + int idx = mvif->sta.wcid.idx; + + mt76_wr(dev, MT_MAC_ADDR0(mvif->idx), 0); + mt76_wr(dev, MT_MAC_ADDR1(mvif->idx), 0); + mt76_wr(dev, MT_BSSID0(mvif->idx), 0); + mt76_wr(dev, MT_BSSID1(mvif->idx), 0); + mt7603_beacon_set_timer(dev, mvif->idx, 0); + + rcu_assign_pointer(dev->mt76.wcid[idx], NULL); + mt76_txq_remove(&dev->mt76, vif->txq); + + mutex_lock(&dev->mt76.mutex); + dev->vif_mask &= ~BIT(mvif->idx); + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7603_init_edcca(struct mt7603_dev *dev) +{ + /* Set lower signal level to -65dBm */ + mt76_rmw_field(dev, MT_RXTD(8), MT_RXTD_8_LOWER_SIGNAL, 0x23); + + /* clear previous energy detect monitor results */ + mt76_rr(dev, MT_MIB_STAT_ED); + + if (dev->ed_monitor) + mt76_set(dev, MT_MIB_CTL, MT_MIB_CTL_ED_TIME); + else + mt76_clear(dev, MT_MIB_CTL, MT_MIB_CTL_ED_TIME); + + dev->ed_strict_mode = 0xff; + dev->ed_strong_signal = 0; + dev->ed_time = ktime_get_boottime(); + + mt7603_edcca_set_strict(dev, false); +} + +static int +mt7603_set_channel(struct mt7603_dev *dev, struct cfg80211_chan_def *def) +{ + u8 *rssi_data = (u8 *)dev->mt76.eeprom.data; + int idx, ret; + u8 bw = MT_BW_20; + bool failed = false; + + cancel_delayed_work_sync(&dev->mac_work); + + mutex_lock(&dev->mt76.mutex); + set_bit(MT76_RESET, &dev->mt76.state); + + mt76_set_channel(&dev->mt76); + mt7603_mac_stop(dev); + + if (def->width == NL80211_CHAN_WIDTH_40) + bw = MT_BW_40; + + dev->mt76.chandef = *def; + mt76_rmw_field(dev, MT_AGG_BWCR, MT_AGG_BWCR_BW, bw); + ret = mt7603_mcu_set_channel(dev); + if (ret) { + failed = true; + goto out; + } + + if (def->chan->band == NL80211_BAND_5GHZ) { + idx = 1; + rssi_data += MT_EE_RSSI_OFFSET_5G; + } else { + idx = 0; + rssi_data += MT_EE_RSSI_OFFSET_2G; + } + + memcpy(dev->rssi_offset, rssi_data, sizeof(dev->rssi_offset)); + + idx |= (def->chan - + mt76_hw(dev)->wiphy->bands[def->chan->band]->channels) << 1; + mt76_wr(dev, MT_WF_RMAC_CH_FREQ, idx); + mt7603_mac_set_timing(dev); + mt7603_mac_start(dev); + + clear_bit(MT76_RESET, &dev->mt76.state); + + mt76_txq_schedule_all(&dev->mt76); + + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mac_work, + MT7603_WATCHDOG_TIME); + + /* reset channel stats */ + mt76_clear(dev, MT_MIB_CTL, MT_MIB_CTL_READ_CLR_DIS); + mt76_set(dev, MT_MIB_CTL, + MT_MIB_CTL_CCA_NAV_TX | MT_MIB_CTL_PSCCA_TIME); + mt76_rr(dev, MT_MIB_STAT_PSCCA); + mt7603_cca_stats_reset(dev); + + dev->survey_time = ktime_get_boottime(); + + mt7603_init_edcca(dev); + +out: + mutex_unlock(&dev->mt76.mutex); + + if (failed) + mt7603_mac_work(&dev->mac_work.work); + + return ret; +} + +static int +mt7603_config(struct ieee80211_hw *hw, u32 changed) +{ + struct mt7603_dev *dev = hw->priv; + int ret = 0; + + if (changed & (IEEE80211_CONF_CHANGE_CHANNEL | + IEEE80211_CONF_CHANGE_POWER)) + ret = mt7603_set_channel(dev, &hw->conf.chandef); + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + mutex_lock(&dev->mt76.mutex); + + if (!(hw->conf.flags & IEEE80211_CONF_MONITOR)) + dev->rxfilter |= MT_WF_RFCR_DROP_OTHER_UC; + else + dev->rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC; + + mt76_wr(dev, MT_WF_RFCR, dev->rxfilter); + + mutex_unlock(&dev->mt76.mutex); + } + + return ret; +} + +static void +mt7603_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, + unsigned int *total_flags, u64 multicast) +{ + struct mt7603_dev *dev = hw->priv; + u32 flags = 0; + +#define MT76_FILTER(_flag, _hw) do { \ + flags |= *total_flags & FIF_##_flag; \ + dev->rxfilter &= ~(_hw); \ + dev->rxfilter |= !(flags & FIF_##_flag) * (_hw); \ + } while (0) + + dev->rxfilter &= ~(MT_WF_RFCR_DROP_OTHER_BSS | + MT_WF_RFCR_DROP_OTHER_BEACON | + MT_WF_RFCR_DROP_FRAME_REPORT | + MT_WF_RFCR_DROP_PROBEREQ | + MT_WF_RFCR_DROP_MCAST_FILTERED | + MT_WF_RFCR_DROP_MCAST | + MT_WF_RFCR_DROP_BCAST | + MT_WF_RFCR_DROP_DUPLICATE | + MT_WF_RFCR_DROP_A2_BSSID | + MT_WF_RFCR_DROP_UNWANTED_CTL | + MT_WF_RFCR_DROP_STBC_MULTI); + + MT76_FILTER(OTHER_BSS, MT_WF_RFCR_DROP_OTHER_TIM | + MT_WF_RFCR_DROP_A3_MAC | + MT_WF_RFCR_DROP_A3_BSSID); + + MT76_FILTER(FCSFAIL, MT_WF_RFCR_DROP_FCSFAIL); + + MT76_FILTER(CONTROL, MT_WF_RFCR_DROP_CTS | + MT_WF_RFCR_DROP_RTS | + MT_WF_RFCR_DROP_CTL_RSV | + MT_WF_RFCR_DROP_NDPA); + + *total_flags = flags; + mt76_wr(dev, MT_WF_RFCR, dev->rxfilter); +} + +static void +mt7603_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u32 changed) +{ + struct mt7603_dev *dev = hw->priv; + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; + + mutex_lock(&dev->mt76.mutex); + + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BSSID)) { + if (info->assoc || info->ibss_joined) { + mt76_wr(dev, MT_BSSID0(mvif->idx), + get_unaligned_le32(info->bssid)); + mt76_wr(dev, MT_BSSID1(mvif->idx), + (get_unaligned_le16(info->bssid + 4) | + MT_BSSID1_VALID)); + } else { + mt76_wr(dev, MT_BSSID0(mvif->idx), 0); + mt76_wr(dev, MT_BSSID1(mvif->idx), 0); + } + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + int slottime = info->use_short_slot ? 9 : 20; + + if (slottime != dev->slottime) { + dev->slottime = slottime; + mt7603_mac_set_timing(dev); + } + } + + if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON_INT)) { + int beacon_int = !!info->enable_beacon * info->beacon_int; + + tasklet_disable(&dev->pre_tbtt_tasklet); + mt7603_beacon_set_timer(dev, mvif->idx, beacon_int); + tasklet_enable(&dev->pre_tbtt_tasklet); + } + + mutex_unlock(&dev->mt76.mutex); +} + +int +mt7603_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; + int idx; + int ret = 0; + + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7603_WTBL_STA - 1); + if (idx < 0) + return -ENOSPC; + + __skb_queue_head_init(&msta->psq); + msta->ps = ~0; + msta->smps = ~0; + msta->wcid.sta = 1; + msta->wcid.idx = idx; + mt7603_wtbl_init(dev, idx, mvif->idx, sta->addr); + mt7603_wtbl_set_ps(dev, msta, false); + + if (vif->type == NL80211_IFTYPE_AP) + set_bit(MT_WCID_FLAG_CHECK_PS, &msta->wcid.flags); + + return ret; +} + +void +mt7603_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + + mt7603_wtbl_update_cap(dev, sta); +} + +void +mt7603_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; + struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv; + + spin_lock_bh(&dev->ps_lock); + __skb_queue_purge(&msta->psq); + mt7603_filter_tx(dev, wcid->idx, true); + spin_unlock_bh(&dev->ps_lock); + + mt7603_wtbl_clear(dev, wcid->idx); +} + +static void +mt7603_ps_tx_list(struct mt7603_dev *dev, struct sk_buff_head *list) +{ + struct sk_buff *skb; + + while ((skb = __skb_dequeue(list)) != NULL) + mt7603_tx_queue_mcu(dev, skb_get_queue_mapping(skb), skb); +} + +void +mt7603_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; + struct sk_buff_head list; + + mt76_stop_tx_queues(&dev->mt76, sta, false); + mt7603_wtbl_set_ps(dev, msta, ps); + if (ps) + return; + + __skb_queue_head_init(&list); + + spin_lock_bh(&dev->ps_lock); + skb_queue_splice_tail_init(&msta->psq, &list); + spin_unlock_bh(&dev->ps_lock); + + mt7603_ps_tx_list(dev, &list); +} + +static void +mt7603_release_buffered_frames(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u16 tids, int nframes, + enum ieee80211_frame_release_type reason, + bool more_data) +{ + struct mt7603_dev *dev = hw->priv; + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; + struct sk_buff_head list; + struct sk_buff *skb, *tmp; + + __skb_queue_head_init(&list); + + spin_lock_bh(&dev->ps_lock); + skb_queue_walk_safe(&msta->psq, skb, tmp) { + if (!nframes) + break; + + if (!(tids & BIT(skb->priority))) + continue; + + skb_set_queue_mapping(skb, MT_TXQ_PSD); + __skb_unlink(skb, &msta->psq); + __skb_queue_tail(&list, skb); + nframes--; + } + spin_unlock_bh(&dev->ps_lock); + + mt7603_ps_tx_list(dev, &list); + + if (nframes) + mt76_release_buffered_frames(hw, sta, tids, nframes, reason, + more_data); +} + +static int +mt7603_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct mt7603_dev *dev = hw->priv; + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; + struct mt7603_sta *msta = sta ? (struct mt7603_sta *)sta->drv_priv : + &mvif->sta; + struct mt76_wcid *wcid = &msta->wcid; + int idx = key->keyidx; + + /* fall back to sw encryption for unsupported ciphers */ + switch (key->cipher) { + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + break; + default: + return -EOPNOTSUPP; + } + + /* + * The hardware does not support per-STA RX GTK, fall back + * to software mode for these. + */ + if ((vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT) && + (key->cipher == WLAN_CIPHER_SUITE_TKIP || + key->cipher == WLAN_CIPHER_SUITE_CCMP) && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return -EOPNOTSUPP; + + if (cmd == SET_KEY) { + key->hw_key_idx = wcid->idx; + wcid->hw_key_idx = idx; + } else { + if (idx == wcid->hw_key_idx) + wcid->hw_key_idx = -1; + + key = NULL; + } + mt76_wcid_key_setup(&dev->mt76, wcid, key); + + return mt7603_wtbl_set_key(dev, wcid->idx, key); +} + +static int +mt7603_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct mt7603_dev *dev = hw->priv; + u16 cw_min = (1 << 5) - 1; + u16 cw_max = (1 << 10) - 1; + u32 val; + + queue = dev->mt76.q_tx[queue].hw_idx; + + if (params->cw_min) + cw_min = params->cw_min; + if (params->cw_max) + cw_max = params->cw_max; + + mutex_lock(&dev->mt76.mutex); + mt7603_mac_stop(dev); + + val = mt76_rr(dev, MT_WMM_TXOP(queue)); + val &= ~(MT_WMM_TXOP_MASK << MT_WMM_TXOP_SHIFT(queue)); + val |= params->txop << MT_WMM_TXOP_SHIFT(queue); + mt76_wr(dev, MT_WMM_TXOP(queue), val); + + val = mt76_rr(dev, MT_WMM_AIFSN); + val &= ~(MT_WMM_AIFSN_MASK << MT_WMM_AIFSN_SHIFT(queue)); + val |= params->aifs << MT_WMM_AIFSN_SHIFT(queue); + mt76_wr(dev, MT_WMM_AIFSN, val); + + val = mt76_rr(dev, MT_WMM_CWMIN); + val &= ~(MT_WMM_CWMIN_MASK << MT_WMM_CWMIN_SHIFT(queue)); + val |= cw_min << MT_WMM_CWMIN_SHIFT(queue); + mt76_wr(dev, MT_WMM_CWMIN, val); + + val = mt76_rr(dev, MT_WMM_CWMAX(queue)); + val &= ~(MT_WMM_CWMAX_MASK << MT_WMM_CWMAX_SHIFT(queue)); + val |= cw_max << MT_WMM_CWMAX_SHIFT(queue); + mt76_wr(dev, MT_WMM_CWMAX(queue), val); + + mt7603_mac_start(dev); + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +static void +mt7603_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + const u8 *mac) +{ + struct mt7603_dev *dev = hw->priv; + + set_bit(MT76_SCANNING, &dev->mt76.state); + mt7603_beacon_set_timer(dev, -1, 0); +} + +static void +mt7603_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt7603_dev *dev = hw->priv; + + clear_bit(MT76_SCANNING, &dev->mt76.state); + mt7603_beacon_set_timer(dev, -1, dev->beacon_int); +} + +static void +mt7603_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ +} + +static int +mt7603_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + enum ieee80211_ampdu_mlme_action action = params->action; + struct mt7603_dev *dev = hw->priv; + struct ieee80211_sta *sta = params->sta; + struct ieee80211_txq *txq = sta->txq[params->tid]; + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; + u8 ba_size = params->buf_size; + struct mt76_txq *mtxq; + + if (!txq) + return -EINVAL; + + mtxq = (struct mt76_txq *)txq->drv_priv; + + switch (action) { + case IEEE80211_AMPDU_RX_START: + mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, *ssn, + params->buf_size); + mt7603_mac_rx_ba_reset(dev, sta->addr, tid); + break; + case IEEE80211_AMPDU_RX_STOP: + mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + mtxq->aggr = true; + mtxq->send_bar = false; + mt7603_mac_tx_ba_reset(dev, msta->wcid.idx, tid, *ssn, ba_size); + break; + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + mtxq->aggr = false; + ieee80211_send_bar(vif, sta->addr, tid, mtxq->agg_ssn); + mt7603_mac_tx_ba_reset(dev, msta->wcid.idx, tid, *ssn, -1); + break; + case IEEE80211_AMPDU_TX_START: + mtxq->agg_ssn = *ssn << 4; + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + mtxq->aggr = false; + mt7603_mac_tx_ba_reset(dev, msta->wcid.idx, tid, *ssn, -1); + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + } + + return 0; +} + +static void +mt7603_sta_rate_tbl_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7603_dev *dev = hw->priv; + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; + struct ieee80211_sta_rates *sta_rates = rcu_dereference(sta->rates); + int i; + + spin_lock_bh(&dev->mt76.lock); + for (i = 0; i < ARRAY_SIZE(msta->rates); i++) { + msta->rates[i].idx = sta_rates->rate[i].idx; + msta->rates[i].count = sta_rates->rate[i].count; + msta->rates[i].flags = sta_rates->rate[i].flags; + + if (msta->rates[i].idx < 0 || !msta->rates[i].count) + break; + } + msta->n_rates = i; + mt7603_wtbl_set_rates(dev, msta, NULL, msta->rates); + msta->rate_probe = false; + mt7603_wtbl_set_smps(dev, msta, + sta->smps_mode == IEEE80211_SMPS_DYNAMIC); + spin_unlock_bh(&dev->mt76.lock); +} + +static void +mt7603_set_coverage_class(struct ieee80211_hw *hw, s16 coverage_class) +{ + struct mt7603_dev *dev = hw->priv; + + dev->coverage_class = coverage_class; + mt7603_mac_set_timing(dev); +} + +static void mt7603_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct mt7603_dev *dev = hw->priv; + struct mt76_wcid *wcid = &dev->global_sta.wcid; + + if (control->sta) { + struct mt7603_sta *msta; + + msta = (struct mt7603_sta *)control->sta->drv_priv; + wcid = &msta->wcid; + /* sw encrypted frames */ + if (!info->control.hw_key && wcid->hw_key_idx != 0xff && + ieee80211_has_protected(hdr->frame_control)) + control->sta = NULL; + } + + if (vif && !control->sta) { + struct mt7603_vif *mvif; + + mvif = (struct mt7603_vif *)vif->drv_priv; + wcid = &mvif->sta.wcid; + } + + mt76_tx(&dev->mt76, control->sta, wcid, skb); +} + +static int +mt7603_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) +{ + return 0; +} + +const struct ieee80211_ops mt7603_ops = { + .tx = mt7603_tx, + .start = mt7603_start, + .stop = mt7603_stop, + .add_interface = mt7603_add_interface, + .remove_interface = mt7603_remove_interface, + .config = mt7603_config, + .configure_filter = mt7603_configure_filter, + .bss_info_changed = mt7603_bss_info_changed, + .sta_state = mt76_sta_state, + .set_key = mt7603_set_key, + .conf_tx = mt7603_conf_tx, + .sw_scan_start = mt7603_sw_scan, + .sw_scan_complete = mt7603_sw_scan_complete, + .flush = mt7603_flush, + .ampdu_action = mt7603_ampdu_action, + .get_txpower = mt76_get_txpower, + .wake_tx_queue = mt76_wake_tx_queue, + .sta_rate_tbl_update = mt7603_sta_rate_tbl_update, + .release_buffered_frames = mt7603_release_buffered_frames, + .set_coverage_class = mt7603_set_coverage_class, + .set_tim = mt7603_set_tim, + .get_survey = mt76_get_survey, +}; + +MODULE_LICENSE("Dual BSD/GPL"); + +static int __init mt7603_init(void) +{ + int ret; + + ret = platform_driver_register(&mt76_wmac_driver); + if (ret) + return ret; + +#ifdef CONFIG_PCI + ret = pci_register_driver(&mt7603_pci_driver); + if (ret) + platform_driver_unregister(&mt76_wmac_driver); +#endif + return ret; +} + +static void __exit mt7603_exit(void) +{ +#ifdef CONFIG_PCI + pci_unregister_driver(&mt7603_pci_driver); +#endif + platform_driver_unregister(&mt76_wmac_driver); +} + +module_init(mt7603_init); +module_exit(mt7603_exit); diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c new file mode 100644 index 000000000000..bd5b60ac7a24 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c @@ -0,0 +1,536 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/firmware.h> +#include "mt7603.h" +#include "mcu.h" +#include "eeprom.h" + +#define MCU_SKB_RESERVE 8 + +struct mt7603_fw_trailer { + char fw_ver[10]; + char build_date[15]; + __le32 dl_len; +} __packed; + +static struct sk_buff *mt7603_mcu_msg_alloc(const void *data, int len) +{ + struct sk_buff *skb; + + skb = alloc_skb(len + sizeof(struct mt7603_mcu_txd), + GFP_KERNEL); + if (!skb) + return NULL; + + skb_reserve(skb, sizeof(struct mt7603_mcu_txd)); + if (data && len) + memcpy(skb_put(skb, len), data, len); + + return skb; +} + +void mt7603_mcu_rx_event(struct mt7603_dev *dev, struct sk_buff *skb) +{ + skb_queue_tail(&dev->mt76.mmio.mcu.res_q, skb); + wake_up(&dev->mt76.mmio.mcu.wait); +} + +static struct sk_buff * +mt7603_mcu_get_response(struct mt7603_dev *dev, unsigned long expires) +{ + struct mt76_dev *mdev = &dev->mt76; + unsigned long timeout; + + if (!time_is_after_jiffies(expires)) + return NULL; + + timeout = expires - jiffies; + wait_event_timeout(mdev->mmio.mcu.wait, + !skb_queue_empty(&mdev->mmio.mcu.res_q), + timeout); + return skb_dequeue(&mdev->mmio.mcu.res_q); +} + +static int +__mt7603_mcu_msg_send(struct mt7603_dev *dev, struct sk_buff *skb, int cmd, + int query, int *wait_seq) +{ + int hdrlen = dev->mcu_running ? sizeof(struct mt7603_mcu_txd) : 12; + struct mt76_dev *mdev = &dev->mt76; + struct mt7603_mcu_txd *txd; + u8 seq; + + if (!skb) + return -EINVAL; + + seq = ++mdev->mmio.mcu.msg_seq & 0xf; + if (!seq) + seq = ++mdev->mmio.mcu.msg_seq & 0xf; + + txd = (struct mt7603_mcu_txd *)skb_push(skb, hdrlen); + memset(txd, 0, hdrlen); + + txd->len = cpu_to_le16(skb->len); + if (cmd == -MCU_CMD_FW_SCATTER) + txd->pq_id = cpu_to_le16(MCU_PORT_QUEUE_FW); + else + txd->pq_id = cpu_to_le16(MCU_PORT_QUEUE); + txd->pkt_type = MCU_PKT_ID; + txd->seq = seq; + + if (cmd < 0) { + txd->cid = -cmd; + } else { + txd->cid = MCU_CMD_EXT_CID; + txd->ext_cid = cmd; + if (query != MCU_Q_NA) + txd->ext_cid_ack = 1; + } + + txd->set_query = query; + + if (wait_seq) + *wait_seq = seq; + + return mt7603_tx_queue_mcu(dev, MT_TXQ_MCU, skb); +} + +static int +mt7603_mcu_msg_send(struct mt7603_dev *dev, struct sk_buff *skb, int cmd, + int query) +{ + struct mt76_dev *mdev = &dev->mt76; + unsigned long expires = jiffies + 3 * HZ; + struct mt7603_mcu_rxd *rxd; + int ret, seq; + + mutex_lock(&mdev->mmio.mcu.mutex); + + ret = __mt7603_mcu_msg_send(dev, skb, cmd, query, &seq); + if (ret) + goto out; + + while (1) { + bool check_seq = false; + + skb = mt7603_mcu_get_response(dev, expires); + if (!skb) { + dev_err(mdev->dev, + "MCU message %d (seq %d) timed out\n", + cmd, seq); + dev->mcu_hang = MT7603_WATCHDOG_TIMEOUT; + ret = -ETIMEDOUT; + break; + } + + rxd = (struct mt7603_mcu_rxd *)skb->data; + if (seq == rxd->seq) + check_seq = true; + + dev_kfree_skb(skb); + + if (check_seq) + break; + } + +out: + mutex_unlock(&mdev->mmio.mcu.mutex); + + return ret; +} + +static int +mt7603_mcu_init_download(struct mt7603_dev *dev, u32 addr, u32 len) +{ + struct { + __le32 addr; + __le32 len; + __le32 mode; + } req = { + .addr = cpu_to_le32(addr), + .len = cpu_to_le32(len), + .mode = cpu_to_le32(BIT(31)), + }; + struct sk_buff *skb = mt7603_mcu_msg_alloc(&req, sizeof(req)); + + return mt7603_mcu_msg_send(dev, skb, -MCU_CMD_TARGET_ADDRESS_LEN_REQ, + MCU_Q_NA); +} + +static int +mt7603_mcu_send_firmware(struct mt7603_dev *dev, const void *data, int len) +{ + struct sk_buff *skb; + int ret = 0; + + while (len > 0) { + int cur_len = min_t(int, 4096 - sizeof(struct mt7603_mcu_txd), + len); + + skb = mt7603_mcu_msg_alloc(data, cur_len); + if (!skb) + return -ENOMEM; + + ret = __mt7603_mcu_msg_send(dev, skb, -MCU_CMD_FW_SCATTER, + MCU_Q_NA, NULL); + if (ret) + break; + + data += cur_len; + len -= cur_len; + } + + return ret; +} + +static int +mt7603_mcu_start_firmware(struct mt7603_dev *dev, u32 addr) +{ + struct { + __le32 override; + __le32 addr; + } req = { + .override = cpu_to_le32(addr ? 1 : 0), + .addr = cpu_to_le32(addr), + }; + struct sk_buff *skb = mt7603_mcu_msg_alloc(&req, sizeof(req)); + + return mt7603_mcu_msg_send(dev, skb, -MCU_CMD_FW_START_REQ, + MCU_Q_NA); +} + +static int +mt7603_mcu_restart(struct mt7603_dev *dev) +{ + struct sk_buff *skb = mt7603_mcu_msg_alloc(NULL, 0); + + return mt7603_mcu_msg_send(dev, skb, -MCU_CMD_RESTART_DL_REQ, + MCU_Q_NA); +} + +static int +mt7603_load_firmware(struct mt7603_dev *dev) +{ + const struct firmware *fw; + const struct mt7603_fw_trailer *hdr; + const char *firmware; + int dl_len; + u32 addr, val; + int ret; + + if (is_mt7628(dev)) { + if (mt76xx_rev(dev) == MT7628_REV_E1) + firmware = MT7628_FIRMWARE_E1; + else + firmware = MT7628_FIRMWARE_E2; + } else { + if (mt76xx_rev(dev) < MT7603_REV_E2) + firmware = MT7603_FIRMWARE_E1; + else + firmware = MT7603_FIRMWARE_E2; + } + + ret = request_firmware(&fw, firmware, dev->mt76.dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev->mt76.dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const struct mt7603_fw_trailer *)(fw->data + fw->size - + sizeof(*hdr)); + + dev_info(dev->mt76.dev, "Firmware Version: %.10s\n", hdr->fw_ver); + dev_info(dev->mt76.dev, "Build Time: %.15s\n", hdr->build_date); + + addr = mt7603_reg_map(dev, 0x50012498); + mt76_wr(dev, addr, 0x5); + mt76_wr(dev, addr, 0x5); + udelay(1); + + /* switch to bypass mode */ + mt76_rmw(dev, MT_SCH_4, MT_SCH_4_FORCE_QID, + MT_SCH_4_BYPASS | FIELD_PREP(MT_SCH_4_FORCE_QID, 5)); + + val = mt76_rr(dev, MT_TOP_MISC2); + if (val & BIT(1)) { + dev_info(dev->mt76.dev, "Firmware already running...\n"); + goto running; + } + + if (!mt76_poll_msec(dev, MT_TOP_MISC2, BIT(0) | BIT(1), BIT(0), 500)) { + dev_err(dev->mt76.dev, "Timeout waiting for ROM code to become ready\n"); + ret = -EIO; + goto out; + } + + dl_len = le32_to_cpu(hdr->dl_len) + 4; + ret = mt7603_mcu_init_download(dev, MCU_FIRMWARE_ADDRESS, dl_len); + if (ret) { + dev_err(dev->mt76.dev, "Download request failed\n"); + goto out; + } + + ret = mt7603_mcu_send_firmware(dev, fw->data, dl_len); + if (ret) { + dev_err(dev->mt76.dev, "Failed to send firmware to device\n"); + goto out; + } + + ret = mt7603_mcu_start_firmware(dev, MCU_FIRMWARE_ADDRESS); + if (ret) { + dev_err(dev->mt76.dev, "Failed to start firmware\n"); + goto out; + } + + if (!mt76_poll_msec(dev, MT_TOP_MISC2, BIT(1), BIT(1), 500)) { + dev_err(dev->mt76.dev, "Timeout waiting for firmware to initialize\n"); + ret = -EIO; + goto out; + } + +running: + mt76_clear(dev, MT_SCH_4, MT_SCH_4_FORCE_QID | MT_SCH_4_BYPASS); + + mt76_set(dev, MT_SCH_4, BIT(8)); + mt76_clear(dev, MT_SCH_4, BIT(8)); + + dev->mcu_running = true; + dev_info(dev->mt76.dev, "firmware init done\n"); + +out: + release_firmware(fw); + + return ret; +} + +int mt7603_mcu_init(struct mt7603_dev *dev) +{ + mutex_init(&dev->mt76.mmio.mcu.mutex); + + return mt7603_load_firmware(dev); +} + +void mt7603_mcu_exit(struct mt7603_dev *dev) +{ + mt7603_mcu_restart(dev); + skb_queue_purge(&dev->mt76.mmio.mcu.res_q); +} + +int mt7603_mcu_set_eeprom(struct mt7603_dev *dev) +{ + static const u16 req_fields[] = { +#define WORD(_start) \ + _start, \ + _start + 1 +#define GROUP_2G(_start) \ + WORD(_start), \ + WORD(_start + 2), \ + WORD(_start + 4) + + MT_EE_NIC_CONF_0 + 1, + WORD(MT_EE_NIC_CONF_1), + MT_EE_WIFI_RF_SETTING, + MT_EE_TX_POWER_DELTA_BW40, + MT_EE_TX_POWER_DELTA_BW80 + 1, + MT_EE_TX_POWER_EXT_PA_5G, + MT_EE_TEMP_SENSOR_CAL, + GROUP_2G(MT_EE_TX_POWER_0_START_2G), + GROUP_2G(MT_EE_TX_POWER_1_START_2G), + WORD(MT_EE_TX_POWER_CCK), + WORD(MT_EE_TX_POWER_OFDM_2G_6M), + WORD(MT_EE_TX_POWER_OFDM_2G_24M), + WORD(MT_EE_TX_POWER_OFDM_2G_54M), + WORD(MT_EE_TX_POWER_HT_BPSK_QPSK), + WORD(MT_EE_TX_POWER_HT_16_64_QAM), + WORD(MT_EE_TX_POWER_HT_64_QAM), + MT_EE_ELAN_RX_MODE_GAIN, + MT_EE_ELAN_RX_MODE_NF, + MT_EE_ELAN_RX_MODE_P1DB, + MT_EE_ELAN_BYPASS_MODE_GAIN, + MT_EE_ELAN_BYPASS_MODE_NF, + MT_EE_ELAN_BYPASS_MODE_P1DB, + WORD(MT_EE_STEP_NUM_NEG_6_7), + WORD(MT_EE_STEP_NUM_NEG_4_5), + WORD(MT_EE_STEP_NUM_NEG_2_3), + WORD(MT_EE_STEP_NUM_NEG_0_1), + WORD(MT_EE_REF_STEP_24G), + WORD(MT_EE_STEP_NUM_PLUS_1_2), + WORD(MT_EE_STEP_NUM_PLUS_3_4), + WORD(MT_EE_STEP_NUM_PLUS_5_6), + MT_EE_STEP_NUM_PLUS_7, + MT_EE_XTAL_FREQ_OFFSET, + MT_EE_XTAL_TRIM_2_COMP, + MT_EE_XTAL_TRIM_3_COMP, + MT_EE_XTAL_WF_RFCAL, + + /* unknown fields below */ + WORD(0x24), + 0x34, + 0x39, + 0x3b, + WORD(0x42), + WORD(0x9e), + 0xf2, + WORD(0xf8), + 0xfa, + 0x12e, + WORD(0x130), WORD(0x132), WORD(0x134), WORD(0x136), + WORD(0x138), WORD(0x13a), WORD(0x13c), WORD(0x13e), + +#undef GROUP_2G +#undef WORD + + }; + struct req_data { + u16 addr; + u8 val; + u8 pad; + } __packed; + struct { + u8 buffer_mode; + u8 len; + u8 pad[2]; + } req_hdr = { + .buffer_mode = 1, + .len = ARRAY_SIZE(req_fields) - 1, + }; + struct sk_buff *skb; + struct req_data *data; + const int size = 0xff * sizeof(struct req_data); + u8 *eep = (u8 *)dev->mt76.eeprom.data; + int i; + + BUILD_BUG_ON(ARRAY_SIZE(req_fields) * sizeof(*data) > size); + + skb = mt7603_mcu_msg_alloc(NULL, size + sizeof(req_hdr)); + memcpy(skb_put(skb, sizeof(req_hdr)), &req_hdr, sizeof(req_hdr)); + data = (struct req_data *)skb_put(skb, size); + memset(data, 0, size); + + for (i = 0; i < ARRAY_SIZE(req_fields); i++) { + data[i].addr = cpu_to_le16(req_fields[i]); + data[i].val = eep[req_fields[i]]; + data[i].pad = 0; + } + + return mt7603_mcu_msg_send(dev, skb, MCU_EXT_CMD_EFUSE_BUFFER_MODE, + MCU_Q_SET); +} + +static int mt7603_mcu_set_tx_power(struct mt7603_dev *dev) +{ + struct { + u8 center_channel; + u8 tssi; + u8 temp_comp; + u8 target_power[2]; + u8 rate_power_delta[14]; + u8 bw_power_delta; + u8 ch_power_delta[6]; + u8 temp_comp_power[17]; + u8 reserved; + } req = { + .center_channel = dev->mt76.chandef.chan->hw_value, +#define EEP_VAL(n) ((u8 *)dev->mt76.eeprom.data)[n] + .tssi = EEP_VAL(MT_EE_NIC_CONF_1 + 1), + .temp_comp = EEP_VAL(MT_EE_NIC_CONF_1), + .target_power = { + EEP_VAL(MT_EE_TX_POWER_0_START_2G + 2), + EEP_VAL(MT_EE_TX_POWER_1_START_2G + 2) + }, + .bw_power_delta = EEP_VAL(MT_EE_TX_POWER_DELTA_BW40), + .ch_power_delta = { + EEP_VAL(MT_EE_TX_POWER_0_START_2G + 3), + EEP_VAL(MT_EE_TX_POWER_0_START_2G + 4), + EEP_VAL(MT_EE_TX_POWER_0_START_2G + 5), + EEP_VAL(MT_EE_TX_POWER_1_START_2G + 3), + EEP_VAL(MT_EE_TX_POWER_1_START_2G + 4), + EEP_VAL(MT_EE_TX_POWER_1_START_2G + 5) + }, +#undef EEP_VAL + }; + struct sk_buff *skb; + u8 *eep = (u8 *)dev->mt76.eeprom.data; + + memcpy(req.rate_power_delta, eep + MT_EE_TX_POWER_CCK, + sizeof(req.rate_power_delta)); + + memcpy(req.temp_comp_power, eep + MT_EE_STEP_NUM_NEG_6_7, + sizeof(req.temp_comp_power)); + + skb = mt7603_mcu_msg_alloc(&req, sizeof(req)); + return mt7603_mcu_msg_send(dev, skb, MCU_EXT_CMD_SET_TX_POWER_CTRL, + MCU_Q_SET); +} + +int mt7603_mcu_set_channel(struct mt7603_dev *dev) +{ + struct cfg80211_chan_def *chandef = &dev->mt76.chandef; + struct ieee80211_hw *hw = mt76_hw(dev); + int n_chains = __sw_hweight8(dev->mt76.antenna_mask); + struct { + u8 control_chan; + u8 center_chan; + u8 bw; + u8 tx_streams; + u8 rx_streams; + u8 _res0[7]; + u8 txpower[21]; + u8 _res1[3]; + } req = { + .control_chan = chandef->chan->hw_value, + .center_chan = chandef->chan->hw_value, + .bw = MT_BW_20, + .tx_streams = n_chains, + .rx_streams = n_chains, + }; + struct sk_buff *skb; + s8 tx_power; + int ret; + int i; + + if (dev->mt76.chandef.width == NL80211_CHAN_WIDTH_40) { + req.bw = MT_BW_40; + if (chandef->center_freq1 > chandef->chan->center_freq) + req.center_chan += 2; + else + req.center_chan -= 2; + } + + tx_power = hw->conf.power_level * 2; + if (dev->mt76.antenna_mask == 3) + tx_power -= 6; + tx_power = min(tx_power, dev->tx_power_limit); + + dev->mt76.txpower_cur = tx_power; + + for (i = 0; i < ARRAY_SIZE(req.txpower); i++) + req.txpower[i] = tx_power; + + skb = mt7603_mcu_msg_alloc(&req, sizeof(req)); + ret = mt7603_mcu_msg_send(dev, skb, MCU_EXT_CMD_CHANNEL_SWITCH, + MCU_Q_SET); + if (ret) + return ret; + + return mt7603_mcu_set_tx_power(dev); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.h new file mode 100644 index 000000000000..3cdb4e6e0c9d --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __MT7603_MCU_H +#define __MT7603_MCU_H + +struct mt7603_mcu_txd { + __le16 len; + __le16 pq_id; + + u8 cid; + u8 pkt_type; + u8 set_query; + u8 seq; + + u8 uc_d2b0_rev; + u8 ext_cid; + u8 uc_d2b2_rev; + u8 ext_cid_ack; + + u32 au4_d3_to_d7_rev[5]; +} __packed __aligned(4); + +struct mt7603_mcu_rxd { + __le16 len; + __le16 pkt_type_id; + + u8 eid; + u8 seq; + __le16 __rsv; + + u8 ext_eid; + u8 __rsv1[3]; +}; + +#define MCU_PKT_ID 0xa0 +#define MCU_PORT_QUEUE 0x8000 +#define MCU_PORT_QUEUE_FW 0xc000 + +#define MCU_FIRMWARE_ADDRESS 0x100000 + +enum { + MCU_Q_QUERY, + MCU_Q_SET, + MCU_Q_RESERVED, + MCU_Q_NA +}; + +enum { + MCU_CMD_TARGET_ADDRESS_LEN_REQ = 0x01, + MCU_CMD_FW_START_REQ = 0x02, + MCU_CMD_INIT_ACCESS_REG = 0x3, + MCU_CMD_PATCH_START_REQ = 0x05, + MCU_CMD_PATCH_FINISH_REQ = 0x07, + MCU_CMD_PATCH_SEM_CONTROL = 0x10, + MCU_CMD_HIF_LOOPBACK = 0x20, + MCU_CMD_CH_PRIVILEGE = 0x20, + MCU_CMD_ACCESS_REG = 0xC2, + MCU_CMD_EXT_CID = 0xED, + MCU_CMD_FW_SCATTER = 0xEE, + MCU_CMD_RESTART_DL_REQ = 0xEF, +}; + +enum { + MCU_EXT_CMD_RF_REG_ACCESS = 0x02, + MCU_EXT_CMD_RF_TEST = 0x04, + MCU_EXT_CMD_RADIO_ON_OFF_CTRL = 0x05, + MCU_EXT_CMD_WIFI_RX_DISABLE = 0x06, + MCU_EXT_CMD_PM_STATE_CTRL = 0x07, + MCU_EXT_CMD_CHANNEL_SWITCH = 0x08, + MCU_EXT_CMD_NIC_CAPABILITY = 0x09, + MCU_EXT_CMD_PWR_SAVING = 0x0A, + MCU_EXT_CMD_MULTIPLE_REG_ACCESS = 0x0E, + MCU_EXT_CMD_AP_PWR_SAVING_CAPABILITY = 0xF, + MCU_EXT_CMD_SEC_ADDREMOVE_KEY = 0x10, + MCU_EXT_CMD_SET_TX_POWER_CTRL = 0x11, + MCU_EXT_CMD_FW_LOG_2_HOST = 0x13, + MCU_EXT_CMD_PS_RETRIEVE_START = 0x14, + MCU_EXT_CMD_LED_CTRL = 0x17, + MCU_EXT_CMD_PACKET_FILTER = 0x18, + MCU_EXT_CMD_PWR_MGT_BIT_WIFI = 0x1B, + MCU_EXT_CMD_EFUSE_BUFFER_MODE = 0x21, + MCU_EXT_CMD_THERMAL_PROTECT = 0x23, + MCU_EXT_CMD_EDCA_SET = 0x27, + MCU_EXT_CMD_SLOT_TIME_SET = 0x28, + MCU_EXT_CMD_CONFIG_INTERNAL_SETTING = 0x29, + MCU_EXT_CMD_NOA_OFFLOAD_CTRL = 0x2B, + MCU_EXT_CMD_GET_THEMAL_SENSOR = 0x2C, + MCU_EXT_CMD_WAKEUP_OPTION = 0x2E, + MCU_EXT_CMD_AC_QUEUE_CONTROL = 0x31, + MCU_EXT_CMD_BCN_UPDATE = 0x33 +}; + +enum { + MCU_EXT_EVENT_CMD_RESULT = 0x0, + MCU_EXT_EVENT_RF_REG_ACCESS = 0x2, + MCU_EXT_EVENT_MULTI_CR_ACCESS = 0x0E, + MCU_EXT_EVENT_FW_LOG_2_HOST = 0x13, + MCU_EXT_EVENT_BEACON_LOSS = 0x1A, + MCU_EXT_EVENT_THERMAL_PROTECT = 0x22, + MCU_EXT_EVENT_BCN_UPDATE = 0x31, +}; + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h new file mode 100644 index 000000000000..8b9297d0ec89 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h @@ -0,0 +1,271 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __MT7603_H +#define __MT7603_H + +#include <linux/interrupt.h> +#include <linux/ktime.h> +#include "../mt76.h" +#include "regs.h" + +#define MT7603_MAX_INTERFACES 4 +#define MT7603_WTBL_SIZE 128 +#define MT7603_WTBL_RESERVED (MT7603_WTBL_SIZE - 1) +#define MT7603_WTBL_STA (MT7603_WTBL_RESERVED - MT7603_MAX_INTERFACES) + +#define MT7603_RATE_RETRY 2 + +#define MT7603_RX_RING_SIZE 128 + +#define MT7603_FIRMWARE_E1 "mt7603_e1.bin" +#define MT7603_FIRMWARE_E2 "mt7603_e2.bin" +#define MT7628_FIRMWARE_E1 "mt7628_e1.bin" +#define MT7628_FIRMWARE_E2 "mt7628_e2.bin" + +#define MT7603_EEPROM_SIZE 1024 + +#define MT_AGG_SIZE_LIMIT(_n) (((_n) + 1) * 4) + +#define MT7603_PRE_TBTT_TIME 5000 /* ms */ + +#define MT7603_WATCHDOG_TIME 100 /* ms */ +#define MT7603_WATCHDOG_TIMEOUT 10 /* number of checks */ + +#define MT7603_EDCCA_BLOCK_TH 10 + +#define MT7603_CFEND_RATE_DEFAULT 0x69 /* chip default (24M) */ +#define MT7603_CFEND_RATE_11B 0x03 /* 11B LP, 11M */ + +struct mt7603_vif; +struct mt7603_sta; + +enum { + MT7603_REV_E1 = 0x00, + MT7603_REV_E2 = 0x10, + MT7628_REV_E1 = 0x8a00, +}; + +enum mt7603_bw { + MT_BW_20, + MT_BW_40, + MT_BW_80, +}; + +struct mt7603_sta { + struct mt76_wcid wcid; /* must be first */ + + struct mt7603_vif *vif; + + struct sk_buff_head psq; + + struct ieee80211_tx_rate rates[8]; + u8 rate_count; + u8 n_rates; + + u8 rate_probe; + u8 smps; + + u8 ps; +}; + +struct mt7603_vif { + struct mt7603_sta sta; /* must be first */ + + u8 idx; +}; + +enum mt7603_reset_cause { + RESET_CAUSE_TX_HANG, + RESET_CAUSE_TX_BUSY, + RESET_CAUSE_RX_BUSY, + RESET_CAUSE_BEACON_STUCK, + RESET_CAUSE_RX_PSE_BUSY, + RESET_CAUSE_MCU_HANG, + RESET_CAUSE_RESET_FAILED, + __RESET_CAUSE_MAX +}; + +struct mt7603_dev { + struct mt76_dev mt76; /* must be first */ + + const struct mt76_bus_ops *bus_ops; + + u32 rxfilter; + + u8 vif_mask; + + struct mt7603_sta global_sta; + + u32 agc0, agc3; + u32 false_cca_ofdm, false_cca_cck; + unsigned long last_cca_adj; + + u8 rssi_offset[3]; + + u8 slottime; + s16 coverage_class; + + s8 tx_power_limit; + + ktime_t survey_time; + ktime_t ed_time; + int beacon_int; + + struct mt76_queue q_rx; + + spinlock_t ps_lock; + + u8 mac_work_count; + + u8 mcu_running; + u8 ed_monitor; + + s8 ed_trigger; + u8 ed_strict_mode; + u8 ed_strong_signal; + + s8 sensitivity; + + u8 beacon_mask; + + u8 beacon_check; + u8 tx_hang_check; + u8 tx_dma_check; + u8 rx_dma_check; + u8 rx_pse_check; + u8 mcu_hang; + + enum mt7603_reset_cause cur_reset_cause; + + u16 tx_dma_idx[4]; + u16 rx_dma_idx; + + u32 reset_test; + + unsigned int reset_cause[__RESET_CAUSE_MAX]; + + struct delayed_work mac_work; + struct tasklet_struct tx_tasklet; + struct tasklet_struct pre_tbtt_tasklet; +}; + +extern const struct ieee80211_ops mt7603_ops; +extern struct pci_driver mt7603_pci_driver; +extern struct platform_driver mt76_wmac_driver; + +static inline bool is_mt7603(struct mt7603_dev *dev) +{ + return mt76xx_chip(dev) == 0x7603; +} + +static inline bool is_mt7628(struct mt7603_dev *dev) +{ + return mt76xx_chip(dev) == 0x7628; +} + +/* need offset to prevent conflict with ampdu_ack_len */ +#define MT_RATE_DRIVER_DATA_OFFSET 4 + +u32 mt7603_reg_map(struct mt7603_dev *dev, u32 addr); + +struct mt7603_dev *mt7603_alloc_device(struct device *pdev); +irqreturn_t mt7603_irq_handler(int irq, void *dev_instance); + +int mt7603_register_device(struct mt7603_dev *dev); +void mt7603_unregister_device(struct mt7603_dev *dev); +int mt7603_eeprom_init(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); +int mt7603_tx_queue_mcu(struct mt7603_dev *dev, enum mt76_txq_id qid, + struct sk_buff *skb); +void mt7603_mcu_rx_event(struct mt7603_dev *dev, struct sk_buff *skb); +void mt7603_init_debugfs(struct mt7603_dev *dev); + +void mt7603_set_irq_mask(struct mt7603_dev *dev, u32 clear, u32 set); + +static inline void mt7603_irq_enable(struct mt7603_dev *dev, u32 mask) +{ + mt7603_set_irq_mask(dev, 0, mask); +} + +static inline void mt7603_irq_disable(struct mt7603_dev *dev, u32 mask) +{ + mt7603_set_irq_mask(dev, mask, 0); +} + +void mt7603_mac_dma_start(struct mt7603_dev *dev); +void mt7603_mac_start(struct mt7603_dev *dev); +void mt7603_mac_stop(struct mt7603_dev *dev); +void mt7603_mac_work(struct work_struct *work); +void mt7603_mac_set_timing(struct mt7603_dev *dev); +void mt7603_beacon_set_timer(struct mt7603_dev *dev, int idx, int intval); +int mt7603_mac_fill_rx(struct mt7603_dev *dev, struct sk_buff *skb); +void mt7603_mac_add_txs(struct mt7603_dev *dev, void *data); +void mt7603_mac_rx_ba_reset(struct mt7603_dev *dev, void *addr, u8 tid); +void mt7603_mac_tx_ba_reset(struct mt7603_dev *dev, int wcid, int tid, int ssn, + int ba_size); + +void mt7603_pse_client_reset(struct mt7603_dev *dev); + +int mt7603_mcu_set_channel(struct mt7603_dev *dev); +int mt7603_mcu_set_eeprom(struct mt7603_dev *dev); +void mt7603_mcu_exit(struct mt7603_dev *dev); + +void mt7603_wtbl_init(struct mt7603_dev *dev, int idx, int vif, + const u8 *mac_addr); +void mt7603_wtbl_clear(struct mt7603_dev *dev, int idx); +void mt7603_wtbl_update_cap(struct mt7603_dev *dev, struct ieee80211_sta *sta); +void mt7603_wtbl_set_rates(struct mt7603_dev *dev, struct mt7603_sta *sta, + struct ieee80211_tx_rate *probe_rate, + struct ieee80211_tx_rate *rates); +int mt7603_wtbl_set_key(struct mt7603_dev *dev, int wcid, + struct ieee80211_key_conf *key); +void mt7603_wtbl_set_ps(struct mt7603_dev *dev, struct mt7603_sta *sta, + bool enabled); +void mt7603_wtbl_set_smps(struct mt7603_dev *dev, struct mt7603_sta *sta, + bool enabled); +void mt7603_filter_tx(struct mt7603_dev *dev, int idx, bool abort); + +int mt7603_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + struct sk_buff *skb, struct mt76_queue *q, + struct mt76_wcid *wcid, struct ieee80211_sta *sta, + u32 *tx_info); + +void mt7603_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue *q, + struct mt76_queue_entry *e, bool flush); + +void mt7603_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb); +void mt7603_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q); +void mt7603_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps); +int mt7603_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void mt7603_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void mt7603_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); + +void mt7603_pre_tbtt_tasklet(unsigned long arg); + +void mt7603_update_channel(struct mt76_dev *mdev); + +void mt7603_edcca_set_strict(struct mt7603_dev *dev, bool val); +void mt7603_cca_stats_reset(struct mt7603_dev *dev); + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/pci.c b/drivers/net/wireless/mediatek/mt76/mt7603/pci.c new file mode 100644 index 000000000000..cc740bcf1e07 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/pci.c @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> + +#include "mt7603.h" + +static const struct pci_device_id mt76pci_device_table[] = { + { PCI_DEVICE(0x14c3, 0x7603) }, + { }, +}; + +static int +mt76pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct mt7603_dev *dev; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); + if (ret) + return ret; + + pci_set_master(pdev); + + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + dev = mt7603_alloc_device(&pdev->dev); + if (!dev) + return -ENOMEM; + + mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]); + + dev->mt76.rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | + (mt76_rr(dev, MT_HW_REV) & 0xff); + dev_info(dev->mt76.dev, "ASIC revision: %04x\n", dev->mt76.rev); + + ret = devm_request_irq(dev->mt76.dev, pdev->irq, mt7603_irq_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (ret) + goto error; + + ret = mt7603_register_device(dev); + if (ret) + goto error; + + return 0; +error: + ieee80211_free_hw(mt76_hw(dev)); + return ret; +} + +static void +mt76pci_remove(struct pci_dev *pdev) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + + mt7603_unregister_device(dev); +} + +MODULE_DEVICE_TABLE(pci, mt76pci_device_table); +MODULE_FIRMWARE(MT7603_FIRMWARE_E1); +MODULE_FIRMWARE(MT7603_FIRMWARE_E2); + +struct pci_driver mt7603_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = mt76pci_device_table, + .probe = mt76pci_probe, + .remove = mt76pci_remove, +}; diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/regs.h b/drivers/net/wireless/mediatek/mt76/mt7603/regs.h new file mode 100644 index 000000000000..5d61e222f227 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/regs.h @@ -0,0 +1,789 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __MT7603_REGS_H +#define __MT7603_REGS_H + +#define MT_HW_REV 0x1000 +#define MT_HW_CHIPID 0x1008 +#define MT_TOP_MISC2 0x1134 + +#define MT_MCU_BASE 0x2000 +#define MT_MCU(ofs) (MT_MCU_BASE + (ofs)) + +#define MT_MCU_PCIE_REMAP_1 MT_MCU(0x500) +#define MT_MCU_PCIE_REMAP_1_OFFSET GENMASK(17, 0) +#define MT_MCU_PCIE_REMAP_1_BASE GENMASK(31, 18) + +#define MT_MCU_PCIE_REMAP_2 MT_MCU(0x504) +#define MT_MCU_PCIE_REMAP_2_OFFSET GENMASK(18, 0) +#define MT_MCU_PCIE_REMAP_2_BASE GENMASK(31, 19) + +#define MT_HIF_BASE 0x4000 +#define MT_HIF(ofs) (MT_HIF_BASE + (ofs)) + +#define MT_INT_SOURCE_CSR MT_HIF(0x200) +#define MT_INT_MASK_CSR MT_HIF(0x204) +#define MT_DELAY_INT_CFG MT_HIF(0x210) + +#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_MAC_IRQ3 BIT(27) + +#define MT_INT_MCU_CMD BIT(30) + +#define MT_WPDMA_GLO_CFG MT_HIF(0x208) +#define MT_WPDMA_GLO_CFG_TX_DMA_EN BIT(0) +#define MT_WPDMA_GLO_CFG_TX_DMA_BUSY BIT(1) +#define MT_WPDMA_GLO_CFG_RX_DMA_EN BIT(2) +#define MT_WPDMA_GLO_CFG_RX_DMA_BUSY BIT(3) +#define MT_WPDMA_GLO_CFG_DMA_BURST_SIZE GENMASK(5, 4) +#define MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE BIT(6) +#define MT_WPDMA_GLO_CFG_BIG_ENDIAN BIT(7) +#define MT_WPDMA_GLO_CFG_HDR_SEG_LEN GENMASK(15, 8) +#define MT_WPDMA_GLO_CFG_SW_RESET BIT(24) +#define MT_WPDMA_GLO_CFG_FORCE_TX_EOF BIT(25) +#define MT_WPDMA_GLO_CFG_CLK_GATE_DIS BIT(30) +#define MT_WPDMA_GLO_CFG_RX_2B_OFFSET BIT(31) + +#define MT_WPDMA_RST_IDX MT_HIF(0x20c) + +#define MT_WPDMA_DEBUG MT_HIF(0x244) +#define MT_WPDMA_DEBUG_VALUE GENMASK(17, 0) +#define MT_WPDMA_DEBUG_SEL BIT(27) +#define MT_WPDMA_DEBUG_IDX GENMASK(31, 28) + +#define MT_TX_RING_BASE MT_HIF(0x300) +#define MT_RX_RING_BASE MT_HIF(0x400) + +#define MT_TXTIME_THRESH_BASE MT_HIF(0x500) +#define MT_TXTIME_THRESH(n) (MT_TXTIME_THRESH_BASE + ((n) * 4)) + +#define MT_PAGE_COUNT_BASE MT_HIF(0x540) +#define MT_PAGE_COUNT(n) (MT_PAGE_COUNT_BASE + ((n) * 4)) + +#define MT_SCH_1 MT_HIF(0x588) +#define MT_SCH_2 MT_HIF(0x58c) +#define MT_SCH_3 MT_HIF(0x590) + +#define MT_SCH_4 MT_HIF(0x594) +#define MT_SCH_4_FORCE_QID GENMASK(4, 0) +#define MT_SCH_4_BYPASS BIT(5) +#define MT_SCH_4_RESET BIT(8) + +#define MT_GROUP_THRESH_BASE MT_HIF(0x598) +#define MT_GROUP_THRESH(n) (MT_GROUP_THRESH_BASE + ((n) * 4)) + +#define MT_QUEUE_PRIORITY_1 MT_HIF(0x580) +#define MT_QUEUE_PRIORITY_2 MT_HIF(0x584) + +#define MT_BMAP_0 MT_HIF(0x5b0) +#define MT_BMAP_1 MT_HIF(0x5b4) +#define MT_BMAP_2 MT_HIF(0x5b8) + +#define MT_HIGH_PRIORITY_1 MT_HIF(0x5bc) +#define MT_HIGH_PRIORITY_2 MT_HIF(0x5c0) + +#define MT_PRIORITY_MASK MT_HIF(0x5c4) + +#define MT_RSV_MAX_THRESH MT_HIF(0x5c8) + +#define MT_PSE_BASE 0x8000 +#define MT_PSE(ofs) (MT_PSE_BASE + (ofs)) + +#define MT_MCU_DEBUG_RESET MT_PSE(0x16c) +#define MT_MCU_DEBUG_RESET_PSE BIT(0) +#define MT_MCU_DEBUG_RESET_PSE_S BIT(1) +#define MT_MCU_DEBUG_RESET_QUEUES GENMASK(6, 2) + +#define MT_PSE_FC_P0 MT_PSE(0x120) +#define MT_PSE_FC_P0_MIN_RESERVE GENMASK(11, 0) +#define MT_PSE_FC_P0_MAX_QUOTA GENMASK(27, 16) + +#define MT_PSE_FRP MT_PSE(0x138) +#define MT_PSE_FRP_P0 GENMASK(2, 0) +#define MT_PSE_FRP_P1 GENMASK(5, 3) +#define MT_PSE_FRP_P2_RQ0 GENMASK(8, 6) +#define MT_PSE_FRP_P2_RQ1 GENMASK(11, 9) +#define MT_PSE_FRP_P2_RQ2 GENMASK(14, 12) + +#define MT_FC_RSV_COUNT_0 MT_PSE(0x13c) +#define MT_FC_RSV_COUNT_0_P0 GENMASK(11, 0) +#define MT_FC_RSV_COUNT_0_P1 GENMASK(27, 16) + +#define MT_FC_SP2_Q0Q1 MT_PSE(0x14c) +#define MT_FC_SP2_Q0Q1_SRC_COUNT_Q0 GENMASK(11, 0) +#define MT_FC_SP2_Q0Q1_SRC_COUNT_Q1 GENMASK(27, 16) + +#define MT_PSE_FW_SHARED MT_PSE(0x17c) + +#define MT_PSE_RTA MT_PSE(0x194) +#define MT_PSE_RTA_QUEUE_ID GENMASK(4, 0) +#define MT_PSE_RTA_PORT_ID GENMASK(6, 5) +#define MT_PSE_RTA_REDIRECT_EN BIT(7) +#define MT_PSE_RTA_TAG_ID GENMASK(15, 8) +#define MT_PSE_RTA_WRITE BIT(16) +#define MT_PSE_RTA_BUSY BIT(31) + +#define MT_WF_PHY_BASE 0x10000 +#define MT_WF_PHY_OFFSET 0x1000 +#define MT_WF_PHY(ofs) (MT_WF_PHY_BASE + (ofs)) + +#define MT_AGC_BASE MT_WF_PHY(0x500) +#define MT_AGC(n) (MT_AGC_BASE + ((n) * 4)) + +#define MT_AGC1_BASE MT_WF_PHY(0x1500) +#define MT_AGC1(n) (MT_AGC1_BASE + ((n) * 4)) + +#define MT_AGC_41_RSSI_0 GENMASK(23, 16) +#define MT_AGC_41_RSSI_1 GENMASK(7, 0) + +#define MT_RXTD_BASE MT_WF_PHY(0x600) +#define MT_RXTD(n) (MT_RXTD_BASE + ((n) * 4)) + +#define MT_RXTD_6_ACI_TH GENMASK(4, 0) +#define MT_RXTD_6_CCAED_TH GENMASK(14, 8) + +#define MT_RXTD_8_LOWER_SIGNAL GENMASK(5, 0) + +#define MT_RXTD_13_ACI_TH_EN BIT(0) + +#define MT_WF_PHY_CR_TSSI_BASE MT_WF_PHY(0xd00) +#define MT_WF_PHY_CR_TSSI(phy, n) (MT_WF_PHY_CR_TSSI_BASE + \ + ((phy) * MT_WF_PHY_OFFSET) + \ + ((n) * 4)) + +#define MT_PHYCTRL_BASE MT_WF_PHY(0x4100) +#define MT_PHYCTRL(n) (MT_PHYCTRL_BASE + ((n) * 4)) + +#define MT_PHYCTRL_2_STATUS_RESET BIT(6) +#define MT_PHYCTRL_2_STATUS_EN BIT(7) + +#define MT_PHYCTRL_STAT_PD MT_PHYCTRL(3) +#define MT_PHYCTRL_STAT_PD_OFDM GENMASK(31, 16) +#define MT_PHYCTRL_STAT_PD_CCK GENMASK(15, 0) + +#define MT_PHYCTRL_STAT_MDRDY MT_PHYCTRL(8) +#define MT_PHYCTRL_STAT_MDRDY_OFDM GENMASK(31, 16) +#define MT_PHYCTRL_STAT_MDRDY_CCK GENMASK(15, 0) + +#define MT_WF_AGG_BASE 0x21200 +#define MT_WF_AGG(ofs) (MT_WF_AGG_BASE + (ofs)) + +#define MT_AGG_ARCR MT_WF_AGG(0x010) +#define MT_AGG_ARCR_INIT_RATE1 BIT(0) +#define MT_AGG_ARCR_FB_SGI_DISABLE BIT(1) +#define MT_AGG_ARCR_RATE8_DOWN_WRAP BIT(2) +#define MT_AGG_ARCR_RTS_RATE_THR GENMASK(12, 8) +#define MT_AGG_ARCR_RATE_DOWN_RATIO GENMASK(17, 16) +#define MT_AGG_ARCR_RATE_DOWN_RATIO_EN BIT(19) +#define MT_AGG_ARCR_RATE_UP_EXTRA_TH GENMASK(22, 20) +#define MT_AGG_ARCR_SPE_DIS_TH GENMASK(27, 24) + +#define MT_AGG_ARUCR MT_WF_AGG(0x014) +#define MT_AGG_ARDCR MT_WF_AGG(0x018) +#define MT_AGG_ARxCR_LIMIT_SHIFT(_n) (4 * (_n)) +#define MT_AGG_ARxCR_LIMIT(_n) GENMASK(2 + \ + MT_AGG_ARxCR_LIMIT_SHIFT(_n), \ + MT_AGG_ARxCR_LIMIT_SHIFT(_n)) + +#define MT_AGG_LIMIT MT_WF_AGG(0x040) +#define MT_AGG_LIMIT_1 MT_WF_AGG(0x044) +#define MT_AGG_LIMIT_AC(_n) GENMASK(((_n) + 1) * 8 - 1, (_n) * 8) + +#define MT_AGG_BA_SIZE_LIMIT_0 MT_WF_AGG(0x048) +#define MT_AGG_BA_SIZE_LIMIT_1 MT_WF_AGG(0x04c) +#define MT_AGG_BA_SIZE_LIMIT_SHIFT 8 + +#define MT_AGG_PCR MT_WF_AGG(0x050) +#define MT_AGG_PCR_MM BIT(16) +#define MT_AGG_PCR_GF BIT(17) +#define MT_AGG_PCR_BW40 BIT(18) +#define MT_AGG_PCR_RIFS BIT(19) +#define MT_AGG_PCR_BW80 BIT(20) +#define MT_AGG_PCR_BW160 BIT(21) +#define MT_AGG_PCR_ERP BIT(22) + +#define MT_AGG_PCR_RTS MT_WF_AGG(0x054) +#define MT_AGG_PCR_RTS_THR GENMASK(19, 0) +#define MT_AGG_PCR_RTS_PKT_THR GENMASK(31, 25) + +#define MT_AGG_CONTROL MT_WF_AGG(0x070) +#define MT_AGG_CONTROL_NO_BA_RULE BIT(0) +#define MT_AGG_CONTROL_NO_BA_AR_RULE BIT(1) +#define MT_AGG_CONTROL_CFEND_SPE_EN BIT(3) +#define MT_AGG_CONTROL_CFEND_RATE GENMASK(15, 4) +#define MT_AGG_CONTROL_BAR_SPE_EN BIT(19) +#define MT_AGG_CONTROL_BAR_RATE GENMASK(31, 20) + +#define MT_AGG_TMP MT_WF_AGG(0x0d8) + +#define MT_AGG_BWCR MT_WF_AGG(0x0ec) +#define MT_AGG_BWCR_BW GENMASK(3, 2) + +#define MT_AGG_RETRY_CONTROL MT_WF_AGG(0x0f4) +#define MT_AGG_RETRY_CONTROL_RTS_LIMIT GENMASK(11, 7) +#define MT_AGG_RETRY_CONTROL_BAR_LIMIT GENMASK(15, 12) + +#define MT_WF_DMA_BASE 0x21c00 +#define MT_WF_DMA(ofs) (MT_WF_DMA_BASE + (ofs)) + +#define MT_DMA_DCR0 MT_WF_DMA(0x000) +#define MT_DMA_DCR1 MT_WF_DMA(0x004) + +#define MT_DMA_FQCR0 MT_WF_DMA(0x008) +#define MT_DMA_FQCR0_TARGET_WCID GENMASK(7, 0) +#define MT_DMA_FQCR0_TARGET_BSS GENMASK(13, 8) +#define MT_DMA_FQCR0_TARGET_QID GENMASK(20, 16) +#define MT_DMA_FQCR0_DEST_PORT_ID GENMASK(23, 22) +#define MT_DMA_FQCR0_DEST_QUEUE_ID GENMASK(28, 24) +#define MT_DMA_FQCR0_MODE BIT(29) +#define MT_DMA_FQCR0_STATUS BIT(30) +#define MT_DMA_FQCR0_BUSY BIT(31) + +#define MT_DMA_RCFR0 MT_WF_DMA(0x070) +#define MT_DMA_VCFR0 MT_WF_DMA(0x07c) + +#define MT_DMA_TCFR0 MT_WF_DMA(0x080) +#define MT_DMA_TCFR1 MT_WF_DMA(0x084) +#define MT_DMA_TCFR_TXS_AGGR_TIMEOUT GENMASK(27, 16) +#define MT_DMA_TCFR_TXS_QUEUE BIT(14) +#define MT_DMA_TCFR_TXS_AGGR_COUNT GENMASK(12, 8) +#define MT_DMA_TCFR_TXS_BIT_MAP GENMASK(6, 0) + +#define MT_DMA_TMCFR0 MT_WF_DMA(0x088) + +#define MT_WF_ARB_BASE 0x21400 +#define MT_WF_ARB(ofs) (MT_WF_ARB_BASE + (ofs)) + +#define MT_WMM_AIFSN MT_WF_ARB(0x020) +#define MT_WMM_AIFSN_MASK GENMASK(3, 0) +#define MT_WMM_AIFSN_SHIFT(_n) ((_n) * 4) + +#define MT_WMM_CWMAX_BASE MT_WF_ARB(0x028) +#define MT_WMM_CWMAX(_n) (MT_WMM_CWMAX_BASE + (((_n) / 2) << 2)) +#define MT_WMM_CWMAX_SHIFT(_n) (((_n) & 1) * 16) +#define MT_WMM_CWMAX_MASK GENMASK(15, 0) + +#define MT_WMM_CWMIN MT_WF_ARB(0x040) +#define MT_WMM_CWMIN_MASK GENMASK(7, 0) +#define MT_WMM_CWMIN_SHIFT(_n) ((_n) * 8) + +#define MT_WF_ARB_RQCR MT_WF_ARB(0x070) +#define MT_WF_ARB_RQCR_RX_START BIT(0) +#define MT_WF_ARB_RQCR_RXV_START BIT(4) +#define MT_WF_ARB_RQCR_RXV_R_EN BIT(7) +#define MT_WF_ARB_RQCR_RXV_T_EN BIT(8) + +#define MT_ARB_SCR MT_WF_ARB(0x080) +#define MT_ARB_SCR_BCNQ_OPMODE_MASK GENMASK(1, 0) +#define MT_ARB_SCR_BCNQ_OPMODE_SHIFT(n) ((n) * 2) +#define MT_ARB_SCR_TX_DISABLE BIT(8) +#define MT_ARB_SCR_RX_DISABLE BIT(9) +#define MT_ARB_SCR_BCNQ_EMPTY_SKIP BIT(28) +#define MT_ARB_SCR_TTTT_BTIM_PRIO BIT(29) +#define MT_ARB_SCR_TBTT_BCN_PRIO BIT(30) +#define MT_ARB_SCR_TBTT_BCAST_PRIO BIT(31) + +enum { + MT_BCNQ_OPMODE_STA = 0, + MT_BCNQ_OPMODE_AP = 1, + MT_BCNQ_OPMODE_ADHOC = 2, +}; + +#define MT_WF_ARB_TX_START_0 MT_WF_ARB(0x100) +#define MT_WF_ARB_TX_START_1 MT_WF_ARB(0x104) +#define MT_WF_ARB_TX_FLUSH_0 MT_WF_ARB(0x108) +#define MT_WF_ARB_TX_FLUSH_1 MT_WF_ARB(0x10c) +#define MT_WF_ARB_TX_STOP_0 MT_WF_ARB(0x110) +#define MT_WF_ARB_TX_STOP_1 MT_WF_ARB(0x114) + +#define MT_WF_ARB_BCN_START MT_WF_ARB(0x118) +#define MT_WF_ARB_BCN_START_BSSn(n) BIT(0 + (n)) +#define MT_WF_ARB_BCN_START_T_PRE_TTTT BIT(10) +#define MT_WF_ARB_BCN_START_T_TTTT BIT(11) +#define MT_WF_ARB_BCN_START_T_PRE_TBTT BIT(12) +#define MT_WF_ARB_BCN_START_T_TBTT BIT(13) +#define MT_WF_ARB_BCN_START_T_SLOT_IDLE BIT(14) +#define MT_WF_ARB_BCN_START_T_TX_START BIT(15) +#define MT_WF_ARB_BCN_START_BSS0n(n) BIT((n) ? 16 + ((n) - 1) : 0) + +#define MT_WF_ARB_BCN_FLUSH MT_WF_ARB(0x11c) +#define MT_WF_ARB_BCN_FLUSH_BSSn(n) BIT(0 + (n)) +#define MT_WF_ARB_BCN_FLUSH_BSS0n(n) BIT((n) ? 16 + ((n) - 1) : 0) + +#define MT_WF_ARB_CAB_START MT_WF_ARB(0x120) +#define MT_WF_ARB_CAB_START_BSSn(n) BIT(0 + (n)) +#define MT_WF_ARB_CAB_START_BSS0n(n) BIT((n) ? 16 + ((n) - 1) : 0) + +#define MT_WF_ARB_CAB_FLUSH MT_WF_ARB(0x124) +#define MT_WF_ARB_CAB_FLUSH_BSSn(n) BIT(0 + (n)) +#define MT_WF_ARB_CAB_FLUSH_BSS0n(n) BIT((n) ? 16 + ((n) - 1) : 0) + +#define MT_WF_ARB_CAB_COUNT(n) MT_WF_ARB(0x128 + (n) * 4) +#define MT_WF_ARB_CAB_COUNT_SHIFT 4 +#define MT_WF_ARB_CAB_COUNT_MASK GENMASK(3, 0) +#define MT_WF_ARB_CAB_COUNT_B0_REG(n) MT_WF_ARB_CAB_COUNT(((n) > 12 ? 2 : \ + ((n) > 4 ? 1 : 0))) +#define MT_WF_ARB_CAB_COUNT_B0_SHIFT(n) (((n) > 12 ? (n) - 12 : \ + ((n) > 4 ? (n) - 4 : \ + (n) ? (n) + 3 : 0)) * 4) + +#define MT_TX_ABORT MT_WF_ARB(0x134) +#define MT_TX_ABORT_EN BIT(0) +#define MT_TX_ABORT_WCID GENMASK(15, 8) + +#define MT_WF_TMAC_BASE 0x21600 +#define MT_WF_TMAC(ofs) (MT_WF_TMAC_BASE + (ofs)) + +#define MT_TMAC_TCR MT_WF_TMAC(0x000) +#define MT_TMAC_TCR_BLINK_SEL GENMASK(7, 6) +#define MT_TMAC_TCR_PRE_RTS_GUARD GENMASK(11, 8) +#define MT_TMAC_TCR_PRE_RTS_SEC_IDLE GENMASK(13, 12) +#define MT_TMAC_TCR_RTS_SIGTA BIT(14) +#define MT_TMAC_TCR_LDPC_OFS BIT(15) +#define MT_TMAC_TCR_TX_STREAMS GENMASK(17, 16) +#define MT_TMAC_TCR_SCH_IDLE_SEL GENMASK(19, 18) +#define MT_TMAC_TCR_SCH_DET_PER_IOD BIT(20) +#define MT_TMAC_TCR_DCH_DET_DISABLE BIT(21) +#define MT_TMAC_TCR_TX_RIFS BIT(22) +#define MT_TMAC_TCR_RX_RIFS_MODE BIT(23) +#define MT_TMAC_TCR_TXOP_TBTT_CTL BIT(24) +#define MT_TMAC_TCR_TBTT_TX_STOP_CTL BIT(25) +#define MT_TMAC_TCR_TXOP_BURST_STOP BIT(26) +#define MT_TMAC_TCR_RDG_RA_MODE BIT(27) +#define MT_TMAC_TCR_RDG_RESP BIT(29) +#define MT_TMAC_TCR_RDG_NO_PENDING BIT(30) +#define MT_TMAC_TCR_SMOOTHING BIT(31) + +#define MT_WMM_TXOP_BASE MT_WF_TMAC(0x010) +#define MT_WMM_TXOP(_n) (MT_WMM_TXOP_BASE + \ + ((((_n) / 2) ^ 0x1) << 2)) +#define MT_WMM_TXOP_SHIFT(_n) (((_n) & 1) * 16) +#define MT_WMM_TXOP_MASK GENMASK(15, 0) + +#define MT_TIMEOUT_CCK MT_WF_TMAC(0x090) +#define MT_TIMEOUT_OFDM MT_WF_TMAC(0x094) +#define MT_TIMEOUT_VAL_PLCP GENMASK(15, 0) +#define MT_TIMEOUT_VAL_CCA GENMASK(31, 16) + +#define MT_TXREQ MT_WF_TMAC(0x09c) +#define MT_TXREQ_CCA_SRC_SEL GENMASK(31, 30) + +#define MT_RXREQ MT_WF_TMAC(0x0a0) +#define MT_RXREQ_DELAY GENMASK(8, 0) + +#define MT_IFS MT_WF_TMAC(0x0a4) +#define MT_IFS_EIFS GENMASK(8, 0) +#define MT_IFS_RIFS GENMASK(14, 10) +#define MT_IFS_SIFS GENMASK(22, 16) +#define MT_IFS_SLOT GENMASK(30, 24) + +#define MT_TMAC_PCR MT_WF_TMAC(0x0b4) +#define MT_TMAC_PCR_RATE GENMASK(8, 0) +#define MT_TMAC_PCR_RATE_FIXED BIT(15) +#define MT_TMAC_PCR_ANT_ID GENMASK(21, 16) +#define MT_TMAC_PCR_ANT_ID_SEL BIT(22) +#define MT_TMAC_PCR_SPE_EN BIT(23) +#define MT_TMAC_PCR_ANT_PRI GENMASK(26, 24) +#define MT_TMAC_PCR_ANT_PRI_SEL GENMASK(27) + +#define MT_WF_RMAC_BASE 0x21800 +#define MT_WF_RMAC(ofs) (MT_WF_RMAC_BASE + (ofs)) + +#define MT_WF_RFCR MT_WF_RMAC(0x000) +#define MT_WF_RFCR_DROP_STBC_MULTI BIT(0) +#define MT_WF_RFCR_DROP_FCSFAIL BIT(1) +#define MT_WF_RFCR_DROP_VERSION BIT(3) +#define MT_WF_RFCR_DROP_PROBEREQ BIT(4) +#define MT_WF_RFCR_DROP_MCAST BIT(5) +#define MT_WF_RFCR_DROP_BCAST BIT(6) +#define MT_WF_RFCR_DROP_MCAST_FILTERED BIT(7) +#define MT_WF_RFCR_DROP_A3_MAC BIT(8) +#define MT_WF_RFCR_DROP_A3_BSSID BIT(9) +#define MT_WF_RFCR_DROP_A2_BSSID BIT(10) +#define MT_WF_RFCR_DROP_OTHER_BEACON BIT(11) +#define MT_WF_RFCR_DROP_FRAME_REPORT BIT(12) +#define MT_WF_RFCR_DROP_CTL_RSV BIT(13) +#define MT_WF_RFCR_DROP_CTS BIT(14) +#define MT_WF_RFCR_DROP_RTS BIT(15) +#define MT_WF_RFCR_DROP_DUPLICATE BIT(16) +#define MT_WF_RFCR_DROP_OTHER_BSS BIT(17) +#define MT_WF_RFCR_DROP_OTHER_UC BIT(18) +#define MT_WF_RFCR_DROP_OTHER_TIM BIT(19) +#define MT_WF_RFCR_DROP_NDPA BIT(20) +#define MT_WF_RFCR_DROP_UNWANTED_CTL BIT(21) + +#define MT_BSSID0(idx) MT_WF_RMAC(0x004 + (idx) * 8) +#define MT_BSSID1(idx) MT_WF_RMAC(0x008 + (idx) * 8) +#define MT_BSSID1_VALID BIT(16) + +#define MT_MAC_ADDR0(idx) MT_WF_RMAC(0x024 + (idx) * 8) +#define MT_MAC_ADDR1(idx) MT_WF_RMAC(0x028 + (idx) * 8) +#define MT_MAC_ADDR1_ADDR GENMASK(15, 0) +#define MT_MAC_ADDR1_VALID BIT(16) + +#define MT_BA_CONTROL_0 MT_WF_RMAC(0x068) +#define MT_BA_CONTROL_1 MT_WF_RMAC(0x06c) +#define MT_BA_CONTROL_1_ADDR GENMASK(15, 0) +#define MT_BA_CONTROL_1_TID GENMASK(19, 16) +#define MT_BA_CONTROL_1_IGNORE_TID BIT(20) +#define MT_BA_CONTROL_1_IGNORE_ALL BIT(21) +#define MT_BA_CONTROL_1_RESET BIT(22) + +#define MT_WF_RMACDR MT_WF_RMAC(0x078) +#define MT_WF_RMACDR_TSF_PROBERSP_DIS BIT(0) +#define MT_WF_RMACDR_TSF_TIM BIT(4) +#define MT_WF_RMACDR_MBSSID_MASK GENMASK(25, 24) +#define MT_WF_RMACDR_CHECK_HTC_BY_RATE BIT(26) +#define MT_WF_RMACDR_MAXLEN_20BIT BIT(30) + +#define MT_WF_RMAC_RMCR MT_WF_RMAC(0x080) +#define MT_WF_RMAC_RMCR_SMPS_MODE GENMASK(21, 20) +#define MT_WF_RMAC_RMCR_RX_STREAMS GENMASK(24, 22) +#define MT_WF_RMAC_RMCR_SMPS_RTS BIT(25) + +#define MT_WF_RMAC_CH_FREQ MT_WF_RMAC(0x090) +#define MT_WF_RMAC_MAXMINLEN MT_WF_RMAC(0x098) +#define MT_WF_RFCR1 MT_WF_RMAC(0x0a4) +#define MT_WF_RMAC_TMR_PA MT_WF_RMAC(0x0e0) + +#define MT_WF_SEC_BASE 0x21a00 +#define MT_WF_SEC(ofs) (MT_WF_SEC_BASE + (ofs)) + +#define MT_SEC_SCR MT_WF_SEC(0x004) +#define MT_SEC_SCR_MASK_ORDER GENMASK(1, 0) + +#define MT_WTBL_OFF_BASE 0x23000 +#define MT_WTBL_OFF(n) (MT_WTBL_OFF_BASE + (n)) + +#define MT_WTBL_UPDATE MT_WTBL_OFF(0x000) +#define MT_WTBL_UPDATE_WLAN_IDX GENMASK(7, 0) +#define MT_WTBL_UPDATE_WTBL2 BIT(11) +#define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(12) +#define MT_WTBL_UPDATE_RATE_UPDATE BIT(13) +#define MT_WTBL_UPDATE_TX_COUNT_CLEAR BIT(14) +#define MT_WTBL_UPDATE_RX_COUNT_CLEAR BIT(15) +#define MT_WTBL_UPDATE_BUSY BIT(16) + +#define MT_WTBL_RMVTCR MT_WTBL_OFF(0x008) +#define MT_WTBL_RMVTCR_RX_MV_MODE BIT(23) + +#define MT_LPON_BASE 0x24000 +#define MT_LPON(n) (MT_LPON_BASE + (n)) + +#define MT_LPON_BTEIR MT_LPON(0x020) +#define MT_LPON_BTEIR_MBSS_MODE GENMASK(31, 29) + +#define MT_PRE_TBTT MT_LPON(0x030) +#define MT_PRE_TBTT_MASK GENMASK(7, 0) +#define MT_PRE_TBTT_SHIFT 8 + +#define MT_TBTT MT_LPON(0x034) +#define MT_TBTT_PERIOD GENMASK(15, 0) +#define MT_TBTT_DTIM_PERIOD GENMASK(23, 16) +#define MT_TBTT_TBTT_WAKE_PERIOD GENMASK(27, 24) +#define MT_TBTT_DTIM_WAKE_PERIOD GENMASK(30, 28) +#define MT_TBTT_CAL_ENABLE BIT(31) + +#define MT_TBTT_TIMER_CFG MT_LPON(0x05c) + +#define MT_LPON_SBTOR(n) MT_LPON(0x0a0) +#define MT_LPON_SBTOR_SUB_BSS_EN BIT(29) +#define MT_LPON_SBTOR_TIME_OFFSET GENMASK(19, 0) + +#define MT_INT_WAKEUP_BASE 0x24400 +#define MT_INT_WAKEUP(n) (MT_INT_WAKEUP_BASE + (n)) + +#define MT_HW_INT_STATUS(n) MT_INT_WAKEUP(0x3c + (n) * 8) +#define MT_HW_INT_MASK(n) MT_INT_WAKEUP(0x40 + (n) * 8) + +#define MT_HW_INT3_TBTT0 BIT(15) +#define MT_HW_INT3_PRE_TBTT0 BIT(31) + +#define MT_WTBL1_BASE 0x28000 + +#define MT_WTBL_ON_BASE (MT_WTBL1_BASE + 0x2000) +#define MT_WTBL_ON(_n) (MT_WTBL_ON_BASE + (_n)) + +#define MT_WTBL_RIUCR0 MT_WTBL_ON(0x200) + +#define MT_WTBL_RIUCR1 MT_WTBL_ON(0x204) +#define MT_WTBL_RIUCR1_RATE0 GENMASK(11, 0) +#define MT_WTBL_RIUCR1_RATE1 GENMASK(23, 12) +#define MT_WTBL_RIUCR1_RATE2_LO GENMASK(31, 24) + +#define MT_WTBL_RIUCR2 MT_WTBL_ON(0x208) +#define MT_WTBL_RIUCR2_RATE2_HI GENMASK(3, 0) +#define MT_WTBL_RIUCR2_RATE3 GENMASK(15, 4) +#define MT_WTBL_RIUCR2_RATE4 GENMASK(27, 16) +#define MT_WTBL_RIUCR2_RATE5_LO GENMASK(31, 28) + +#define MT_WTBL_RIUCR3 MT_WTBL_ON(0x20c) +#define MT_WTBL_RIUCR3_RATE5_HI GENMASK(7, 0) +#define MT_WTBL_RIUCR3_RATE6 GENMASK(19, 8) +#define MT_WTBL_RIUCR3_RATE7 GENMASK(31, 20) + +#define MT_MIB_BASE 0x2c000 +#define MT_MIB(_n) (MT_MIB_BASE + (_n)) + +#define MT_MIB_CTL MT_MIB(0x00) +#define MT_MIB_CTL_PSCCA_TIME GENMASK(13, 11) +#define MT_MIB_CTL_CCA_NAV_TX GENMASK(16, 14) +#define MT_MIB_CTL_ED_TIME GENMASK(30, 28) +#define MT_MIB_CTL_READ_CLR_DIS BIT(31) + +#define MT_MIB_STAT(_n) MT_MIB(0x08 + (_n) * 4) + +#define MT_MIB_STAT_CCA MT_MIB_STAT(9) +#define MT_MIB_STAT_CCA_MASK GENMASK(23, 0) + +#define MT_MIB_STAT_PSCCA MT_MIB_STAT(16) +#define MT_MIB_STAT_PSCCA_MASK GENMASK(23, 0) + +#define MT_MIB_STAT_ED MT_MIB_STAT(18) +#define MT_MIB_STAT_ED_MASK GENMASK(23, 0) + +#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 + +#define MT_LED_BASE_PHYS 0x80024000 +#define MT_LED_PHYS(_n) (MT_LED_BASE_PHYS + (_n)) + +#define MT_LED_CTRL MT_LED_PHYS(0x00) + +#define MT_LED_CTRL_REPLAY(_n) BIT(0 + (8 * (_n))) +#define MT_LED_CTRL_POLARITY(_n) BIT(1 + (8 * (_n))) +#define MT_LED_CTRL_TX_BLINK_MODE(_n) BIT(2 + (8 * (_n))) +#define MT_LED_CTRL_TX_MANUAL_BLINK(_n) BIT(3 + (8 * (_n))) +#define MT_LED_CTRL_TX_OVER_BLINK(_n) BIT(5 + (8 * (_n))) +#define MT_LED_CTRL_KICK(_n) BIT(7 + (8 * (_n))) + +#define MT_LED_STATUS_0(_n) MT_LED_PHYS(0x10 + ((_n) * 8)) +#define MT_LED_STATUS_1(_n) MT_LED_PHYS(0x14 + ((_n) * 8)) +#define MT_LED_STATUS_OFF_MASK GENMASK(31, 24) +#define MT_LED_STATUS_OFF(_v) (((_v) << \ + __ffs(MT_LED_STATUS_OFF_MASK)) & \ + MT_LED_STATUS_OFF_MASK) +#define MT_LED_STATUS_ON_MASK GENMASK(23, 16) +#define MT_LED_STATUS_ON(_v) (((_v) << \ + __ffs(MT_LED_STATUS_ON_MASK)) & \ + MT_LED_STATUS_ON_MASK) +#define MT_LED_STATUS_DURATION_MASK GENMASK(15, 0) +#define MT_LED_STATUS_DURATION(_v) (((_v) << \ + __ffs(MT_LED_STATUS_DURATION_MASK)) &\ + MT_LED_STATUS_DURATION_MASK) + +#define MT_CLIENT_BASE_PHYS_ADDR 0x800c0000 + +#define MT_CLIENT_TMAC_INFO_TEMPLATE 0x040 + +#define MT_CLIENT_STATUS 0x06c + +#define MT_CLIENT_RESET_TX 0x070 +#define MT_CLIENT_RESET_TX_R_E_1 BIT(16) +#define MT_CLIENT_RESET_TX_R_E_2 BIT(17) +#define MT_CLIENT_RESET_TX_R_E_1_S BIT(20) +#define MT_CLIENT_RESET_TX_R_E_2_S BIT(21) + +#define MT_EFUSE_BASE 0x81070000 + +#define MT_EFUSE_BASE_CTRL 0x000 +#define MT_EFUSE_BASE_CTRL_EMPTY BIT(30) + +#define MT_EFUSE_CTRL 0x008 +#define MT_EFUSE_CTRL_AOUT GENMASK(5, 0) +#define MT_EFUSE_CTRL_MODE GENMASK(7, 6) +#define MT_EFUSE_CTRL_LDO_OFF_TIME GENMASK(13, 8) +#define MT_EFUSE_CTRL_LDO_ON_TIME GENMASK(15, 14) +#define MT_EFUSE_CTRL_AIN GENMASK(25, 16) +#define MT_EFUSE_CTRL_VALID BIT(29) +#define MT_EFUSE_CTRL_KICK BIT(30) +#define MT_EFUSE_CTRL_SEL BIT(31) + +#define MT_EFUSE_WDATA(_i) (0x010 + ((_i) * 4)) +#define MT_EFUSE_RDATA(_i) (0x030 + ((_i) * 4)) + +#define MT_CLIENT_RXINF 0x068 +#define MT_CLIENT_RXINF_RXSH_GROUPS GENMASK(2, 0) + +#define MT_PSE_BASE_PHYS_ADDR 0xa0000000 + +#define MT_PSE_WTBL_2_PHYS_ADDR 0xa5000000 + +#define MT_WTBL1_SIZE (8 * 4) +#define MT_WTBL2_SIZE (16 * 4) +#define MT_WTBL3_OFFSET (MT7603_WTBL_SIZE * MT_WTBL2_SIZE) +#define MT_WTBL3_SIZE (16 * 4) +#define MT_WTBL4_OFFSET (MT7603_WTBL_SIZE * MT_WTBL3_SIZE + \ + MT_WTBL3_OFFSET) +#define MT_WTBL4_SIZE (8 * 4) + +#define MT_WTBL1_W0_ADDR_HI GENMASK(15, 0) +#define MT_WTBL1_W0_MUAR_IDX GENMASK(21, 16) +#define MT_WTBL1_W0_RX_CHECK_A1 BIT(22) +#define MT_WTBL1_W0_KEY_IDX GENMASK(24, 23) +#define MT_WTBL1_W0_RX_CHECK_KEY_IDX BIT(25) +#define MT_WTBL1_W0_RX_KEY_VALID BIT(26) +#define MT_WTBL1_W0_RX_IK_VALID BIT(27) +#define MT_WTBL1_W0_RX_VALID BIT(28) +#define MT_WTBL1_W0_RX_CHECK_A2 BIT(29) +#define MT_WTBL1_W0_RX_DATA_VALID BIT(30) +#define MT_WTBL1_W0_WRITE_BURST BIT(31) + +#define MT_WTBL1_W1_ADDR_LO GENMASK(31, 0) + +#define MT_WTBL1_W2_MPDU_DENSITY GENMASK(2, 0) +#define MT_WTBL1_W2_KEY_TYPE GENMASK(6, 3) +#define MT_WTBL1_W2_EVEN_PN BIT(7) +#define MT_WTBL1_W2_TO_DS BIT(8) +#define MT_WTBL1_W2_FROM_DS BIT(9) +#define MT_WTBL1_W2_HEADER_TRANS BIT(10) +#define MT_WTBL1_W2_AMPDU_FACTOR GENMASK(13, 11) +#define MT_WTBL1_W2_PWR_MGMT BIT(14) +#define MT_WTBL1_W2_RDG BIT(15) +#define MT_WTBL1_W2_RTS BIT(16) +#define MT_WTBL1_W2_CFACK BIT(17) +#define MT_WTBL1_W2_RDG_BA BIT(18) +#define MT_WTBL1_W2_SMPS BIT(19) +#define MT_WTBL1_W2_TXS_BAF_REPORT BIT(20) +#define MT_WTBL1_W2_DYN_BW BIT(21) +#define MT_WTBL1_W2_LDPC BIT(22) +#define MT_WTBL1_W2_ITXBF BIT(23) +#define MT_WTBL1_W2_ETXBF BIT(24) +#define MT_WTBL1_W2_TXOP_PS BIT(25) +#define MT_WTBL1_W2_MESH BIT(26) +#define MT_WTBL1_W2_QOS BIT(27) +#define MT_WTBL1_W2_HT BIT(28) +#define MT_WTBL1_W2_VHT BIT(29) +#define MT_WTBL1_W2_ADMISSION_CONTROL BIT(30) +#define MT_WTBL1_W2_GROUP_ID BIT(31) + +#define MT_WTBL1_W3_WTBL2_FRAME_ID GENMASK(10, 0) +#define MT_WTBL1_W3_WTBL2_ENTRY_ID GENMASK(15, 11) +#define MT_WTBL1_W3_WTBL4_FRAME_ID GENMASK(26, 16) +#define MT_WTBL1_W3_CHECK_PER BIT(27) +#define MT_WTBL1_W3_KEEP_I_PSM BIT(28) +#define MT_WTBL1_W3_I_PSM BIT(29) +#define MT_WTBL1_W3_POWER_SAVE BIT(30) +#define MT_WTBL1_W3_SKIP_TX BIT(31) + +#define MT_WTBL1_W4_WTBL3_FRAME_ID GENMASK(10, 0) +#define MT_WTBL1_W4_WTBL3_ENTRY_ID GENMASK(16, 11) +#define MT_WTBL1_W4_WTBL4_ENTRY_ID GENMASK(22, 17) +#define MT_WTBL1_W4_PARTIAL_AID GENMASK(31, 23) + +#define MT_WTBL2_W0_PN_LO GENMASK(31, 0) + +#define MT_WTBL2_W1_PN_HI GENMASK(15, 0) +#define MT_WTBL2_W1_NON_QOS_SEQNO GENMASK(27, 16) + +#define MT_WTBL2_W2_TID0_SN GENMASK(11, 0) +#define MT_WTBL2_W2_TID1_SN GENMASK(23, 12) +#define MT_WTBL2_W2_TID2_SN_LO GENMASK(31, 24) + +#define MT_WTBL2_W3_TID2_SN_HI GENMASK(3, 0) +#define MT_WTBL2_W3_TID3_SN GENMASK(15, 4) +#define MT_WTBL2_W3_TID4_SN GENMASK(27, 16) +#define MT_WTBL2_W3_TID5_SN_LO GENMASK(31, 28) + +#define MT_WTBL2_W4_TID5_SN_HI GENMASK(7, 0) +#define MT_WTBL2_W4_TID6_SN GENMASK(19, 8) +#define MT_WTBL2_W4_TID7_SN GENMASK(31, 20) + +#define MT_WTBL2_W5_TX_COUNT_RATE1 GENMASK(15, 0) +#define MT_WTBL2_W5_FAIL_COUNT_RATE1 GENAMSK(31, 16) + +#define MT_WTBL2_W6_TX_COUNT_RATE2 GENMASK(7, 0) +#define MT_WTBL2_W6_TX_COUNT_RATE3 GENMASK(15, 8) +#define MT_WTBL2_W6_TX_COUNT_RATE4 GENMASK(23, 16) +#define MT_WTBL2_W6_TX_COUNT_RATE5 GENMASK(31, 24) + +#define MT_WTBL2_W7_TX_COUNT_CUR_BW GENMASK(15, 0) +#define MT_WTBL2_W7_FAIL_COUNT_CUR_BW GENMASK(31, 16) + +#define MT_WTBL2_W8_TX_COUNT_OTHER_BW GENMASK(15, 0) +#define MT_WTBL2_W8_FAIL_COUNT_OTHER_BW GENMASK(31, 16) + +#define MT_WTBL2_W9_POWER_OFFSET GENMASK(4, 0) +#define MT_WTBL2_W9_SPATIAL_EXT BIT(5) +#define MT_WTBL2_W9_ANT_PRIORITY GENMASK(8, 6) +#define MT_WTBL2_W9_CC_BW_SEL GENMASK(10, 9) +#define MT_WTBL2_W9_CHANGE_BW_RATE GENMASK(13, 11) +#define MT_WTBL2_W9_BW_CAP GENMASK(15, 14) +#define MT_WTBL2_W9_SHORT_GI_20 BIT(16) +#define MT_WTBL2_W9_SHORT_GI_40 BIT(17) +#define MT_WTBL2_W9_SHORT_GI_80 BIT(18) +#define MT_WTBL2_W9_SHORT_GI_160 BIT(19) +#define MT_WTBL2_W9_MPDU_FAIL_COUNT GENMASK(25, 23) +#define MT_WTBL2_W9_MPDU_OK_COUNT GENMASK(28, 26) +#define MT_WTBL2_W9_RATE_IDX GENMASK(31, 29) + +#define MT_WTBL2_W10_RATE1 GENMASK(11, 0) +#define MT_WTBL2_W10_RATE2 GENMASK(23, 12) +#define MT_WTBL2_W10_RATE3_LO GENMASK(31, 24) + +#define MT_WTBL2_W11_RATE3_HI GENMASK(3, 0) +#define MT_WTBL2_W11_RATE4 GENMASK(15, 4) +#define MT_WTBL2_W11_RATE5 GENMASK(27, 16) +#define MT_WTBL2_W11_RATE6_LO GENMASK(31, 28) + +#define MT_WTBL2_W12_RATE6_HI GENMASK(7, 0) +#define MT_WTBL2_W12_RATE7 GENMASK(19, 8) +#define MT_WTBL2_W12_RATE8 GENMASK(31, 20) + +#define MT_WTBL2_W13_AVG_RCPI0 GENMASK(7, 0) +#define MT_WTBL2_W13_AVG_RCPI1 GENMASK(15, 8) +#define MT_WTBL2_W13_AVG_RCPI2 GENAMSK(23, 16) + +#define MT_WTBL2_W14_CC_NOISE_1S GENMASK(6, 0) +#define MT_WTBL2_W14_CC_NOISE_2S GENMASK(13, 7) +#define MT_WTBL2_W14_CC_NOISE_3S GENMASK(20, 14) +#define MT_WTBL2_W14_CHAN_EST_RMS GENMASK(24, 21) +#define MT_WTBL2_W14_CC_NOISE_SEL BIT(15) +#define MT_WTBL2_W14_ANT_SEL GENMASK(31, 26) + +#define MT_WTBL2_W15_BA_WIN_SIZE GENMASK(2, 0) +#define MT_WTBL2_W15_BA_WIN_SIZE_SHIFT 3 +#define MT_WTBL2_W15_BA_EN_TIDS GENMASK(31, 24) + +#define MT_WTBL1_OR (MT_WTBL1_BASE + 0x2300) +#define MT_WTBL1_OR_PSM_WRITE BIT(31) + +enum mt7603_cipher_type { + MT_CIPHER_NONE, + MT_CIPHER_WEP40, + MT_CIPHER_TKIP, + MT_CIPHER_TKIP_NO_MIC, + MT_CIPHER_AES_CCMP, + MT_CIPHER_WEP104, + MT_CIPHER_BIP_CMAC_128, + MT_CIPHER_WEP128, + MT_CIPHER_WAPI, +}; + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/soc.c b/drivers/net/wireless/mediatek/mt76/mt7603/soc.c new file mode 100644 index 000000000000..5d19b4e7edc6 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/soc.c @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "mt7603.h" + +static int +mt76_wmac_probe(struct platform_device *pdev) +{ + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct mt7603_dev *dev; + void __iomem *mem_base; + int irq; + int ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "Failed to get device IRQ\n"); + return irq; + } + + mem_base = devm_ioremap_resource(&pdev->dev, res); + if (!mem_base) { + dev_err(&pdev->dev, "Failed to get memory resource\n"); + return -EINVAL; + } + + dev = mt7603_alloc_device(&pdev->dev); + if (!dev) + return -ENOMEM; + + mt76_mmio_init(&dev->mt76, mem_base); + + dev->mt76.rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | + (mt76_rr(dev, MT_HW_REV) & 0xff); + dev_info(dev->mt76.dev, "ASIC revision: %04x\n", dev->mt76.rev); + + ret = devm_request_irq(dev->mt76.dev, irq, mt7603_irq_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (ret) + goto error; + + ret = mt7603_register_device(dev); + if (ret) + goto error; + + return 0; +error: + ieee80211_free_hw(mt76_hw(dev)); + return ret; +} + +static int +mt76_wmac_remove(struct platform_device *pdev) +{ + struct mt76_dev *mdev = platform_get_drvdata(pdev); + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + + mt7603_unregister_device(dev); + + return 0; +} + +static const struct of_device_id of_wmac_match[] = { + { .compatible = "mediatek,mt7628-wmac" }, + {}, +}; + +MODULE_DEVICE_TABLE(of, of_wmac_match); +MODULE_FIRMWARE(MT7628_FIRMWARE_E1); +MODULE_FIRMWARE(MT7628_FIRMWARE_E2); + +struct platform_driver mt76_wmac_driver = { + .probe = mt76_wmac_probe, + .remove = mt76_wmac_remove, + .driver = { + .name = "mt76_wmac", + .of_match_table = of_wmac_match, + }, +};
This driver is for a newer generation of 2x2 MediaTek 802.11n chipsets. MT7603E is a PCIe chip. MT7628 and MT7688 are MIPS SoC devices with built-in WLAN. MT7688 is limited to 1x1 This driver fully supports AP, station, mesh, ad-hoc and monitor mode. Signed-off-by: Felix Fietkau <nbd@nbd.name> --- drivers/net/wireless/mediatek/mt76/Kconfig | 1 + drivers/net/wireless/mediatek/mt76/Makefile | 1 + .../net/wireless/mediatek/mt76/mt7603/Kconfig | 9 + .../wireless/mediatek/mt76/mt7603/Makefile | 6 + .../wireless/mediatek/mt76/mt7603/beacon.c | 201 ++ .../net/wireless/mediatek/mt76/mt7603/core.c | 88 + .../wireless/mediatek/mt76/mt7603/debugfs.c | 71 + .../net/wireless/mediatek/mt76/mt7603/dma.c | 253 +++ .../wireless/mediatek/mt76/mt7603/eeprom.c | 183 ++ .../wireless/mediatek/mt76/mt7603/eeprom.h | 101 + .../net/wireless/mediatek/mt76/mt7603/init.c | 608 ++++++ .../net/wireless/mediatek/mt76/mt7603/mac.c | 1764 +++++++++++++++++ .../net/wireless/mediatek/mt76/mt7603/mac.h | 257 +++ .../net/wireless/mediatek/mt76/mt7603/main.c | 730 +++++++ .../net/wireless/mediatek/mt76/mt7603/mcu.c | 536 +++++ .../net/wireless/mediatek/mt76/mt7603/mcu.h | 118 ++ .../wireless/mediatek/mt76/mt7603/mt7603.h | 271 +++ .../net/wireless/mediatek/mt76/mt7603/pci.c | 92 + .../net/wireless/mediatek/mt76/mt7603/regs.h | 789 ++++++++ .../net/wireless/mediatek/mt76/mt7603/soc.c | 97 + 20 files changed, 6176 insertions(+) create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/Kconfig create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/Makefile create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/beacon.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/core.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/dma.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/init.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/mac.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/main.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/mcu.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/pci.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/regs.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/soc.c