Commit 6065bb8a authored by Kalle Valo's avatar Kalle Valo
Browse files

Merge tag 'mt76-for-kvalo-2020-02-14' of https://github.com/nbd168/wireless

mt76 patches for 5.7

* dual-band concurrent support for MT7615
* fixes for rx path race conditions
* EEPROM fixes
* MAC address handling fixes
* coverage class support for MT7615
* beacon fixes for USB devices
* MT7615 LED support
* minor cleanups/fixes for all drivers
* set_antenna support for MT7615
* tracing improvements
* preparation for supporting new USB devices
* tx power fixes
parents 932183aa 09872957
Loading
Loading
Loading
Loading
+26 −3
Original line number Diff line number Diff line
@@ -4,17 +4,27 @@ 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:
Alternatively, it can specify the wireless part of the MT7628/MT7688 or
MT7622 SoC. For SoC, use the following compatible strings:

compatible:
- "mediatek,mt7628-wmac" for MT7628/MT7688
- "mediatek,mt7622-wmac" for MT7622

properties:
- reg: Address and length of the register set for the device.
- interrupts: Main device interrupt

MT7622 specific properties:
- power-domains: phandle to the power domain that the WMAC is part of
- mediatek,infracfg: phandle to the infrastructure bus fabric syscon node

Optional properties:

- ieee80211-freq-limit: See ieee80211.txt
- mediatek,mtd-eeprom: Specify a MTD partition + offset containing EEPROM data
- big-endian: if the radio eeprom partition is written in big-endian, specify
  this property

The MAC address can as well be set with corresponding optional properties
defined in net/ethernet.txt.
@@ -31,6 +41,7 @@ Optional nodes:
			reg = <0x0000 0 0 0 0>;
			ieee80211-freq-limit = <5000000 6000000>;
			mediatek,mtd-eeprom = <&factory 0x8000>;
			big-endian;

			led {
				led-sources = <2>;
@@ -50,3 +61,15 @@ wmac: wmac@10300000 {

	mediatek,mtd-eeprom = <&factory 0x0000>;
};

MT7622 example:

wmac: wmac@18000000 {
	compatible = "mediatek,mt7622-wmac";
	reg = <0 0x18000000 0 0x100000>;
	interrupts = <GIC_SPI 211 IRQ_TYPE_LEVEL_LOW>;

	mediatek,infracfg = <&infracfg>;

	power-domains = <&scpsys MT7622_POWER_DOMAIN_WB>;
};
+1 −1
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@ obj-$(CONFIG_MT76x02_USB) += mt76x02-usb.o

mt76-y := \
	mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o \
	tx.o agg-rx.o mcu.o airtime.o
	tx.o agg-rx.o mcu.o

mt76-$(CONFIG_PCI) += pci.o

+13 −4
Original line number Diff line number Diff line
@@ -4,7 +4,13 @@
 */
#include "mt76.h"

#define REORDER_TIMEOUT (HZ / 10)
static unsigned long mt76_aggr_tid_to_timeo(u8 tidno)
{
	/* Currently voice traffic (AC_VO) always runs without aggregation,
	 * no special handling is needed. AC_BE/AC_BK use tids 0-3. Just check
	 * for non AC_BK/AC_BE and set smaller timeout for it. */
	return HZ / (tidno >= 4 ? 25 : 10);
}

static void
mt76_aggr_release(struct mt76_rx_tid *tid, struct sk_buff_head *frames, int idx)
@@ -71,7 +77,8 @@ mt76_rx_aggr_check_release(struct mt76_rx_tid *tid, struct sk_buff_head *frames)
		nframes--;
		status = (struct mt76_rx_status *)skb->cb;
		if (!time_after(jiffies,
				status->reorder_time + REORDER_TIMEOUT))
				status->reorder_time +
				mt76_aggr_tid_to_timeo(tid->num)))
			continue;

		mt76_rx_aggr_release_frames(tid, frames, status->seqno);
@@ -101,7 +108,7 @@ mt76_rx_aggr_reorder_work(struct work_struct *work)

	if (nframes)
		ieee80211_queue_delayed_work(tid->dev->hw, &tid->reorder_work,
					     REORDER_TIMEOUT);
					     mt76_aggr_tid_to_timeo(tid->num));
	mt76_rx_complete(dev, &frames, NULL);

	rcu_read_unlock();
@@ -225,7 +232,7 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
	mt76_rx_aggr_release_head(tid, frames);

	ieee80211_queue_delayed_work(tid->dev->hw, &tid->reorder_work,
				     REORDER_TIMEOUT);
				     mt76_aggr_tid_to_timeo(tid->num));

out:
	spin_unlock_bh(&tid->lock);
@@ -245,6 +252,7 @@ int mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tidno,
	tid->dev = dev;
	tid->head = ssn;
	tid->size = size;
	tid->num = tidno;
	INIT_DELAYED_WORK(&tid->reorder_work, mt76_rx_aggr_reorder_work);
	spin_lock_init(&tid->lock);

@@ -268,6 +276,7 @@ static void mt76_rx_aggr_shutdown(struct mt76_dev *dev, struct mt76_rx_tid *tid)
		if (!skb)
			continue;

		tid->reorder_buf[i] = NULL;
		tid->nframes--;
		dev_kfree_skb(skb);
	}
+0 −326
Original line number Diff line number Diff line
// SPDX-License-Identifier: ISC
/*
 * Copyright (C) 2019 Felix Fietkau <nbd@nbd.name>
 */

#include "mt76.h"

#define AVG_PKT_SIZE	1024

/* Number of bits for an average sized packet */
#define MCS_NBITS (AVG_PKT_SIZE << 3)

/* Number of symbols for a packet with (bps) bits per symbol */
#define MCS_NSYMS(bps) DIV_ROUND_UP(MCS_NBITS, (bps))

/* Transmission time (1024 usec) for a packet containing (syms) * symbols */
#define MCS_SYMBOL_TIME(sgi, syms)					\
	(sgi ?								\
	  ((syms) * 18 * 1024 + 4 * 1024) / 5 :	/* syms * 3.6 us */	\
	  ((syms) * 1024) << 2			/* syms * 4 us */	\
	)

/* Transmit duration for the raw data part of an average sized packet */
#define MCS_DURATION(streams, sgi, bps) \
	MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps)))

#define BW_20			0
#define BW_40			1
#define BW_80			2

/*
 * Define group sort order: HT40 -> SGI -> #streams
 */
#define MT_MAX_STREAMS		4
#define MT_HT_STREAM_GROUPS	4 /* BW(=2) * SGI(=2) */
#define MT_VHT_STREAM_GROUPS	6 /* BW(=3) * SGI(=2) */

#define MT_HT_GROUPS_NB	(MT_MAX_STREAMS *		\
				 MT_HT_STREAM_GROUPS)
#define MT_VHT_GROUPS_NB	(MT_MAX_STREAMS *		\
				 MT_VHT_STREAM_GROUPS)
#define MT_GROUPS_NB	(MT_HT_GROUPS_NB +	\
				 MT_VHT_GROUPS_NB)

#define MT_HT_GROUP_0	0
#define MT_VHT_GROUP_0	(MT_HT_GROUP_0 + MT_HT_GROUPS_NB)

#define MCS_GROUP_RATES		10

#define HT_GROUP_IDX(_streams, _sgi, _ht40)	\
	MT_HT_GROUP_0 +			\
	MT_MAX_STREAMS * 2 * _ht40 +	\
	MT_MAX_STREAMS * _sgi +	\
	_streams - 1

#define _MAX(a, b) (((a)>(b))?(a):(b))

#define GROUP_SHIFT(duration)						\
	_MAX(0, 16 - __builtin_clz(duration))

/* MCS rate information for an MCS group */
#define __MCS_GROUP(_streams, _sgi, _ht40, _s)				\
	[HT_GROUP_IDX(_streams, _sgi, _ht40)] = {			\
	.shift = _s,							\
	.duration = {							\
		MCS_DURATION(_streams, _sgi, _ht40 ? 54 : 26) >> _s,	\
		MCS_DURATION(_streams, _sgi, _ht40 ? 108 : 52) >> _s,	\
		MCS_DURATION(_streams, _sgi, _ht40 ? 162 : 78) >> _s,	\
		MCS_DURATION(_streams, _sgi, _ht40 ? 216 : 104) >> _s,	\
		MCS_DURATION(_streams, _sgi, _ht40 ? 324 : 156) >> _s,	\
		MCS_DURATION(_streams, _sgi, _ht40 ? 432 : 208) >> _s,	\
		MCS_DURATION(_streams, _sgi, _ht40 ? 486 : 234) >> _s,	\
		MCS_DURATION(_streams, _sgi, _ht40 ? 540 : 260) >> _s	\
	}								\
}

#define MCS_GROUP_SHIFT(_streams, _sgi, _ht40)				\
	GROUP_SHIFT(MCS_DURATION(_streams, _sgi, _ht40 ? 54 : 26))

#define MCS_GROUP(_streams, _sgi, _ht40)				\
	__MCS_GROUP(_streams, _sgi, _ht40,				\
		    MCS_GROUP_SHIFT(_streams, _sgi, _ht40))

#define VHT_GROUP_IDX(_streams, _sgi, _bw)				\
	(MT_VHT_GROUP_0 +						\
	 MT_MAX_STREAMS * 2 * (_bw) +				\
	 MT_MAX_STREAMS * (_sgi) +				\
	 (_streams) - 1)

#define BW2VBPS(_bw, r3, r2, r1)					\
	(_bw == BW_80 ? r3 : _bw == BW_40 ? r2 : r1)

#define __VHT_GROUP(_streams, _sgi, _bw, _s)				\
	[VHT_GROUP_IDX(_streams, _sgi, _bw)] = {			\
	.shift = _s,							\
	.duration = {							\
		MCS_DURATION(_streams, _sgi,				\
			     BW2VBPS(_bw,  117,  54,  26)) >> _s,	\
		MCS_DURATION(_streams, _sgi,				\
			     BW2VBPS(_bw,  234, 108,  52)) >> _s,	\
		MCS_DURATION(_streams, _sgi,				\
			     BW2VBPS(_bw,  351, 162,  78)) >> _s,	\
		MCS_DURATION(_streams, _sgi,				\
			     BW2VBPS(_bw,  468, 216, 104)) >> _s,	\
		MCS_DURATION(_streams, _sgi,				\
			     BW2VBPS(_bw,  702, 324, 156)) >> _s,	\
		MCS_DURATION(_streams, _sgi,				\
			     BW2VBPS(_bw,  936, 432, 208)) >> _s,	\
		MCS_DURATION(_streams, _sgi,				\
			     BW2VBPS(_bw, 1053, 486, 234)) >> _s,	\
		MCS_DURATION(_streams, _sgi,				\
			     BW2VBPS(_bw, 1170, 540, 260)) >> _s,	\
		MCS_DURATION(_streams, _sgi,				\
			     BW2VBPS(_bw, 1404, 648, 312)) >> _s,	\
		MCS_DURATION(_streams, _sgi,				\
			     BW2VBPS(_bw, 1560, 720, 346)) >> _s	\
	}								\
}

#define VHT_GROUP_SHIFT(_streams, _sgi, _bw)				\
	GROUP_SHIFT(MCS_DURATION(_streams, _sgi,			\
				 BW2VBPS(_bw,  117,  54,  26)))

#define VHT_GROUP(_streams, _sgi, _bw)					\
	__VHT_GROUP(_streams, _sgi, _bw,				\
		    VHT_GROUP_SHIFT(_streams, _sgi, _bw))

struct mcs_group {
	u8 shift;
	u16 duration[MCS_GROUP_RATES];
};

static const struct mcs_group airtime_mcs_groups[] = {
	MCS_GROUP(1, 0, BW_20),
	MCS_GROUP(2, 0, BW_20),
	MCS_GROUP(3, 0, BW_20),
	MCS_GROUP(4, 0, BW_20),

	MCS_GROUP(1, 1, BW_20),
	MCS_GROUP(2, 1, BW_20),
	MCS_GROUP(3, 1, BW_20),
	MCS_GROUP(4, 1, BW_20),

	MCS_GROUP(1, 0, BW_40),
	MCS_GROUP(2, 0, BW_40),
	MCS_GROUP(3, 0, BW_40),
	MCS_GROUP(4, 0, BW_40),

	MCS_GROUP(1, 1, BW_40),
	MCS_GROUP(2, 1, BW_40),
	MCS_GROUP(3, 1, BW_40),
	MCS_GROUP(4, 1, BW_40),

	VHT_GROUP(1, 0, BW_20),
	VHT_GROUP(2, 0, BW_20),
	VHT_GROUP(3, 0, BW_20),
	VHT_GROUP(4, 0, BW_20),

	VHT_GROUP(1, 1, BW_20),
	VHT_GROUP(2, 1, BW_20),
	VHT_GROUP(3, 1, BW_20),
	VHT_GROUP(4, 1, BW_20),

	VHT_GROUP(1, 0, BW_40),
	VHT_GROUP(2, 0, BW_40),
	VHT_GROUP(3, 0, BW_40),
	VHT_GROUP(4, 0, BW_40),

	VHT_GROUP(1, 1, BW_40),
	VHT_GROUP(2, 1, BW_40),
	VHT_GROUP(3, 1, BW_40),
	VHT_GROUP(4, 1, BW_40),

	VHT_GROUP(1, 0, BW_80),
	VHT_GROUP(2, 0, BW_80),
	VHT_GROUP(3, 0, BW_80),
	VHT_GROUP(4, 0, BW_80),

	VHT_GROUP(1, 1, BW_80),
	VHT_GROUP(2, 1, BW_80),
	VHT_GROUP(3, 1, BW_80),
	VHT_GROUP(4, 1, BW_80),
};

static u32
mt76_calc_legacy_rate_duration(const struct ieee80211_rate *rate, bool short_pre,
			       int len)
{
	u32 duration;

	switch (rate->hw_value >> 8) {
	case MT_PHY_TYPE_CCK:
		duration = 144 + 48; /* preamble + PLCP */
		if (short_pre)
			duration >>= 1;

		duration += 10; /* SIFS */
		break;
	case MT_PHY_TYPE_OFDM:
		duration = 20 + 16; /* premable + SIFS */
		break;
	default:
		WARN_ON_ONCE(1);
		return 0;
	}

	len <<= 3;
	duration += (len * 10) / rate->bitrate;

	return duration;
}

u32 mt76_calc_rx_airtime(struct mt76_dev *dev, struct mt76_rx_status *status,
			 int len)
{
	struct ieee80211_supported_band *sband;
	const struct ieee80211_rate *rate;
	bool sgi = status->enc_flags & RX_ENC_FLAG_SHORT_GI;
	bool sp = status->enc_flags & RX_ENC_FLAG_SHORTPRE;
	int bw, streams;
	u32 duration;
	int group, idx;

	switch (status->bw) {
	case RATE_INFO_BW_20:
		bw = BW_20;
		break;
	case RATE_INFO_BW_40:
		bw = BW_40;
		break;
	case RATE_INFO_BW_80:
		bw = BW_80;
		break;
	default:
		WARN_ON_ONCE(1);
		return 0;
	}

	switch (status->encoding) {
	case RX_ENC_LEGACY:
		if (WARN_ON_ONCE(status->band > NL80211_BAND_5GHZ))
			return 0;

		sband = dev->hw->wiphy->bands[status->band];
		if (!sband || status->rate_idx >= sband->n_bitrates)
			return 0;

		rate = &sband->bitrates[status->rate_idx];

		return mt76_calc_legacy_rate_duration(rate, sp, len);
	case RX_ENC_VHT:
		streams = status->nss;
		idx = status->rate_idx;
		group = VHT_GROUP_IDX(streams, sgi, bw);
		break;
	case RX_ENC_HT:
		streams = ((status->rate_idx >> 3) & 3) + 1;
		idx = status->rate_idx & 7;
		group = HT_GROUP_IDX(streams, sgi, bw);
		break;
	default:
		WARN_ON_ONCE(1);
		return 0;
	}

	if (WARN_ON_ONCE(streams > 4))
		return 0;

	duration = airtime_mcs_groups[group].duration[idx];
	duration <<= airtime_mcs_groups[group].shift;
	duration *= len;
	duration /= AVG_PKT_SIZE;
	duration /= 1024;

	duration += 36 + (streams << 2);

	return duration;
}

u32 mt76_calc_tx_airtime(struct mt76_dev *dev, struct ieee80211_tx_info *info,
			 int len)
{
	struct mt76_rx_status stat = {
		.band = info->band,
	};
	u32 duration = 0;
	int i;

	for (i = 0; i < ARRAY_SIZE(info->status.rates); i++) {
		struct ieee80211_tx_rate *rate = &info->status.rates[i];
		u32 cur_duration;

		if (rate->idx < 0 || !rate->count)
			break;

		if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
			stat.bw = RATE_INFO_BW_80;
		else if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
			stat.bw = RATE_INFO_BW_40;
		else
			stat.bw = RATE_INFO_BW_20;

		stat.enc_flags = 0;
		if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
			stat.enc_flags |= RX_ENC_FLAG_SHORTPRE;
		if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
			stat.enc_flags |= RX_ENC_FLAG_SHORT_GI;

		stat.rate_idx = rate->idx;
		if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
			stat.encoding = RX_ENC_VHT;
			stat.rate_idx = ieee80211_rate_get_vht_mcs(rate);
			stat.nss = ieee80211_rate_get_vht_nss(rate);
		} else if (rate->flags & IEEE80211_TX_RC_MCS) {
			stat.encoding = RX_ENC_HT;
		} else {
			stat.encoding = RX_ENC_LEGACY;
		}

		cur_duration = mt76_calc_rx_airtime(dev, &stat, len);
		duration += cur_duration * rate->count;
	}

	return duration;
}
EXPORT_SYMBOL_GPL(mt76_calc_tx_airtime);
+35 −14
Original line number Diff line number Diff line
@@ -132,6 +132,11 @@ mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q)
	writel(q->ndesc, &q->regs->ring_size);
	q->head = readl(&q->regs->dma_idx);
	q->tail = q->head;
}

static void
mt76_dma_kick_queue(struct mt76_dev *dev, struct mt76_queue *q)
{
	writel(q->head, &q->regs->cpu_idx);
}

@@ -141,7 +146,7 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, enum mt76_txq_id qid, bool flush)
	struct mt76_sw_queue *sq = &dev->q_tx[qid];
	struct mt76_queue *q = sq->q;
	struct mt76_queue_entry entry;
	unsigned int n_swq_queued[4] = {};
	unsigned int n_swq_queued[8] = {};
	unsigned int n_queued = 0;
	bool wake = false;
	int i, last;
@@ -178,15 +183,25 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, enum mt76_txq_id qid, bool flush)
	spin_lock_bh(&q->lock);

	q->queued -= n_queued;
	for (i = 0; i < ARRAY_SIZE(n_swq_queued); i++) {
	for (i = 0; i < 4; i++) {
		if (!n_swq_queued[i])
			continue;

		dev->q_tx[i].swq_queued -= n_swq_queued[i];
	}

	if (flush)
	/* ext PHY */
	for (i = 0; i < 4; i++) {
		if (!n_swq_queued[i])
			continue;

		dev->q_tx[__MT_TXQ_MAX + i].swq_queued -= n_swq_queued[4 + i];
	}

	if (flush) {
		mt76_dma_sync_idx(dev, q);
		mt76_dma_kick_queue(dev, q);
	}

	wake = wake && q->stopped &&
	       qid < IEEE80211_NUM_ACS && q->queued < q->ndesc - 8;
@@ -238,7 +253,9 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
	if (!q->queued)
		return NULL;

	if (!flush && !(q->desc[idx].ctrl & cpu_to_le32(MT_DMA_CTL_DMA_DONE)))
	if (flush)
		q->desc[idx].ctrl |= cpu_to_le32(MT_DMA_CTL_DMA_DONE);
	else if (!(q->desc[idx].ctrl & cpu_to_le32(MT_DMA_CTL_DMA_DONE)))
		return NULL;

	q->tail = (q->tail + 1) % q->ndesc;
@@ -247,12 +264,6 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
	return mt76_dma_get_buf(dev, q, idx, len, info, more);
}

static void
mt76_dma_kick_queue(struct mt76_dev *dev, struct mt76_queue *q)
{
	writel(q->head, &q->regs->cpu_idx);
}

static int
mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, enum mt76_txq_id qid,
			  struct sk_buff *skb, u32 tx_info)
@@ -261,10 +272,13 @@ mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, enum mt76_txq_id qid,
	struct mt76_queue_buf buf;
	dma_addr_t addr;

	if (q->queued + 1 >= q->ndesc - 1)
		goto error;

	addr = dma_map_single(dev->dev, skb->data, skb->len,
			      DMA_TO_DEVICE);
	if (unlikely(dma_mapping_error(dev->dev, addr)))
		return -ENOMEM;
		goto error;

	buf.addr = addr;
	buf.len = skb->len;
@@ -275,6 +289,10 @@ mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, enum mt76_txq_id qid,
	spin_unlock_bh(&q->lock);

	return 0;

error:
	dev_kfree_skb(skb);
	return -ENOMEM;
}

static int
@@ -286,6 +304,7 @@ mt76_dma_tx_queue_skb(struct mt76_dev *dev, enum mt76_txq_id qid,
	struct mt76_tx_info tx_info = {
		.skb = skb,
	};
	struct ieee80211_hw *hw;
	int len, n = 0, ret = -ENOMEM;
	struct mt76_queue_entry e;
	struct mt76_txwi_cache *t;
@@ -295,7 +314,8 @@ mt76_dma_tx_queue_skb(struct mt76_dev *dev, enum mt76_txq_id qid,

	t = mt76_get_txwi(dev);
	if (!t) {
		ieee80211_free_txskb(dev->hw, skb);
		hw = mt76_tx_status_get_hw(dev, skb);
		ieee80211_free_txskb(hw, skb);
		return -ENOMEM;
	}
	txwi = mt76_get_txwi_ptr(dev, t);
@@ -427,7 +447,7 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
	int i;

	for (i = 0; i < q->ndesc; i++)
		q->desc[i].ctrl &= ~cpu_to_le32(MT_DMA_CTL_DMA_DONE);
		q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE);

	mt76_dma_rx_cleanup(dev, q);
	mt76_dma_sync_idx(dev, q);
@@ -528,6 +548,7 @@ mt76_dma_rx_poll(struct napi_struct *napi, int budget)
	dev = container_of(napi->dev, struct mt76_dev, napi_dev);
	qid = napi - dev->napi;

	local_bh_disable();
	rcu_read_lock();

	do {
@@ -537,6 +558,7 @@ mt76_dma_rx_poll(struct napi_struct *napi, int budget)
	} while (cur && done < budget);

	rcu_read_unlock();
	local_bh_enable();

	if (done < budget && napi_complete(napi))
		dev->drv->rx_poll_complete(dev, qid);
@@ -555,7 +577,6 @@ mt76_dma_init(struct mt76_dev *dev)
		netif_napi_add(&dev->napi_dev, &dev->napi[i], mt76_dma_rx_poll,
			       64);
		mt76_dma_rx_fill(dev, &dev->q_rx[i]);
		skb_queue_head_init(&dev->rx_skb[i]);
		napi_enable(&dev->napi[i]);
	}

Loading