All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/3] mt76: add driver callback for when a sta is associated
@ 2019-01-30 14:07 Felix Fietkau
  2019-01-30 14:07   ` Felix Fietkau
  2019-01-30 14:07 ` [PATCH 3/3] mt76: add driver for MT7603E and MT7628/7688 Felix Fietkau
  0 siblings, 2 replies; 8+ messages in thread
From: Felix Fietkau @ 2019-01-30 14:07 UTC (permalink / raw)
  To: linux-wireless

MT7603 needs this to update the HT/VHT capabilities

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/mac80211.c | 5 +++++
 drivers/net/wireless/mediatek/mt76/mt76.h     | 3 +++
 2 files changed, 8 insertions(+)

diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index ee3b65a14870..489953a47646 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -709,6 +709,11 @@ int mt76_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 	    new_state == IEEE80211_STA_NONE)
 		return mt76_sta_add(dev, vif, sta);
 
+	if (old_state == IEEE80211_STA_AUTH &&
+	    new_state == IEEE80211_STA_ASSOC &&
+	    dev->drv->sta_assoc)
+		dev->drv->sta_assoc(dev, vif, sta);
+
 	if (old_state == IEEE80211_STA_NONE &&
 		 new_state == IEEE80211_STA_NOTEXIST)
 		mt76_sta_remove(dev, vif, sta);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 2bb9db4ed80a..71708b111120 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -300,6 +300,9 @@ struct mt76_driver_ops {
 	int (*sta_add)(struct mt76_dev *dev, struct ieee80211_vif *vif,
 		       struct ieee80211_sta *sta);
 
+	void (*sta_assoc)(struct mt76_dev *dev, struct ieee80211_vif *vif,
+			  struct ieee80211_sta *sta);
+
 	void (*sta_remove)(struct mt76_dev *dev, struct ieee80211_vif *vif,
 			   struct ieee80211_sta *sta);
 };
-- 
2.17.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/3] dt-bindings: net: mt76: update binding for mt7603 driver
@ 2019-01-30 14:07   ` Felix Fietkau
  0 siblings, 0 replies; 8+ messages in thread
From: Felix Fietkau @ 2019-01-30 14:07 UTC (permalink / raw)
  To: linux-wireless; +Cc: devicetree

In addition to MT7603E PCI devices, the driver supports the WLAN core on
MT7628/MT7688, which needs to be defined in DT.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 .../bindings/net/wireless/mediatek,mt76.txt   | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.txt b/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.txt
index 0c17a0ec9b7b..7b9a776230c0 100644
--- a/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.txt
+++ b/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.txt
@@ -4,6 +4,13 @@ This node provides properties for configuring the MediaTek mt76xx wireless
 device. The node is expected to be specified as a child node of the PCI
 controller to which the wireless chip is connected.
 
+Alternatively, it can specify the wireless part of the MT7628/MT7688 SoC.
+For SoC, use the compatible string "mediatek,mt7628-wmac" and the following
+properties:
+
+- reg: Address and length of the register set for the device.
+- interrupts: Main device interrupt
+
 Optional properties:
 
 - mac-address: See ethernet.txt in the parent directory
@@ -30,3 +37,15 @@ Optional nodes:
 		};
 	};
 };
+
+MT7628 example:
+
+wmac: wmac@10300000 {
+	compatible = "mediatek,mt7628-wmac";
+	reg = <0x10300000 0x100000>;
+
+	interrupt-parent = <&cpuintc>;
+	interrupts = <6>;
+
+	mediatek,mtd-eeprom = <&factory 0x0000>;
+};
-- 
2.17.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/3] dt-bindings: net: mt76: update binding for mt7603 driver
@ 2019-01-30 14:07   ` Felix Fietkau
  0 siblings, 0 replies; 8+ messages in thread
From: Felix Fietkau @ 2019-01-30 14:07 UTC (permalink / raw)
  To: linux-wireless-u79uwXL29TY76Z2rM5mHXA; +Cc: devicetree-u79uwXL29TY76Z2rM5mHXA

In addition to MT7603E PCI devices, the driver supports the WLAN core on
MT7628/MT7688, which needs to be defined in DT.

Signed-off-by: Felix Fietkau <nbd-Vt+b4OUoWG0@public.gmane.org>
---
 .../bindings/net/wireless/mediatek,mt76.txt   | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.txt b/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.txt
index 0c17a0ec9b7b..7b9a776230c0 100644
--- a/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.txt
+++ b/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.txt
@@ -4,6 +4,13 @@ This node provides properties for configuring the MediaTek mt76xx wireless
 device. The node is expected to be specified as a child node of the PCI
 controller to which the wireless chip is connected.
 
+Alternatively, it can specify the wireless part of the MT7628/MT7688 SoC.
+For SoC, use the compatible string "mediatek,mt7628-wmac" and the following
+properties:
+
+- reg: Address and length of the register set for the device.
+- interrupts: Main device interrupt
+
 Optional properties:
 
 - mac-address: See ethernet.txt in the parent directory
@@ -30,3 +37,15 @@ Optional nodes:
 		};
 	};
 };
+
+MT7628 example:
+
+wmac: wmac@10300000 {
+	compatible = "mediatek,mt7628-wmac";
+	reg = <0x10300000 0x100000>;
+
+	interrupt-parent = <&cpuintc>;
+	interrupts = <6>;
+
+	mediatek,mtd-eeprom = <&factory 0x0000>;
+};
-- 
2.17.0

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 3/3] mt76: add driver for MT7603E and MT7628/7688
  2019-01-30 14:07 [PATCH 1/3] mt76: add driver callback for when a sta is associated Felix Fietkau
  2019-01-30 14:07   ` Felix Fietkau
@ 2019-01-30 14:07 ` Felix Fietkau
  2019-01-30 15:14   ` Lorenzo Bianconi
  2019-01-31 11:20   ` Kalle Valo
  1 sibling, 2 replies; 8+ messages in thread
From: Felix Fietkau @ 2019-01-30 14:07 UTC (permalink / raw)
  To: linux-wireless

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

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 = &params->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


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 3/3] mt76: add driver for MT7603E and MT7628/7688
  2019-01-30 14:07 ` [PATCH 3/3] mt76: add driver for MT7603E and MT7628/7688 Felix Fietkau
@ 2019-01-30 15:14   ` Lorenzo Bianconi
  2019-01-31 11:20   ` Kalle Valo
  1 sibling, 0 replies; 8+ messages in thread
From: Lorenzo Bianconi @ 2019-01-30 15:14 UTC (permalink / raw)
  To: Felix Fietkau; +Cc: linux-wireless

>
> 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 = &params->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
>


-- 
UNIX is Sexy: who | grep -i blonde | talk; cd ~; wine; talk; touch;
unzip; touch; strip; gasp; finger; gasp; mount; fsck; more; yes; gasp;
umount; make clean; sleep

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 3/3] mt76: add driver for MT7603E and MT7628/7688
  2019-01-30 14:07 ` [PATCH 3/3] mt76: add driver for MT7603E and MT7628/7688 Felix Fietkau
  2019-01-30 15:14   ` Lorenzo Bianconi
@ 2019-01-31 11:20   ` Kalle Valo
  1 sibling, 0 replies; 8+ messages in thread
From: Kalle Valo @ 2019-01-31 11:20 UTC (permalink / raw)
  To: Felix Fietkau; +Cc: linux-wireless

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.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/3] dt-bindings: net: mt76: update binding for mt7603 driver
@ 2019-02-25 16:33     ` Rob Herring
  0 siblings, 0 replies; 8+ messages in thread
From: Rob Herring @ 2019-02-25 16:33 UTC (permalink / raw)
  To: Felix Fietkau; +Cc: linux-wireless, devicetree

On Wed, 30 Jan 2019 15:07:55 +0100, Felix Fietkau wrote:
> In addition to MT7603E PCI devices, the driver supports the WLAN core on
> MT7628/MT7688, which needs to be defined in DT.
> 
> Signed-off-by: Felix Fietkau <nbd@nbd.name>
> ---
>  .../bindings/net/wireless/mediatek,mt76.txt   | 19 +++++++++++++++++++
>  1 file changed, 19 insertions(+)
> 

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/3] dt-bindings: net: mt76: update binding for mt7603 driver
@ 2019-02-25 16:33     ` Rob Herring
  0 siblings, 0 replies; 8+ messages in thread
From: Rob Herring @ 2019-02-25 16:33 UTC (permalink / raw)
  To: Felix Fietkau
  Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA, devicetree-u79uwXL29TY76Z2rM5mHXA

On Wed, 30 Jan 2019 15:07:55 +0100, Felix Fietkau wrote:
> In addition to MT7603E PCI devices, the driver supports the WLAN core on
> MT7628/MT7688, which needs to be defined in DT.
> 
> Signed-off-by: Felix Fietkau <nbd-Vt+b4OUoWG0@public.gmane.org>
> ---
>  .../bindings/net/wireless/mediatek,mt76.txt   | 19 +++++++++++++++++++
>  1 file changed, 19 insertions(+)
> 

Reviewed-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2019-02-25 16:33 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-30 14:07 [PATCH 1/3] mt76: add driver callback for when a sta is associated Felix Fietkau
2019-01-30 14:07 ` [PATCH 2/3] dt-bindings: net: mt76: update binding for mt7603 driver Felix Fietkau
2019-01-30 14:07   ` Felix Fietkau
2019-02-25 16:33   ` Rob Herring
2019-02-25 16:33     ` Rob Herring
2019-01-30 14:07 ` [PATCH 3/3] mt76: add driver for MT7603E and MT7628/7688 Felix Fietkau
2019-01-30 15:14   ` Lorenzo Bianconi
2019-01-31 11:20   ` Kalle Valo

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.