162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC
262306a36Sopenharmony_ci/* Copyright (C) 2020 MediaTek Inc.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Author: Felix Fietkau <nbd@nbd.name>
562306a36Sopenharmony_ci *	   Lorenzo Bianconi <lorenzo@kernel.org>
662306a36Sopenharmony_ci *	   Sean Wang <sean.wang@mediatek.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/iopoll.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/mmc/host.h>
1462306a36Sopenharmony_ci#include <linux/mmc/sdio_ids.h>
1562306a36Sopenharmony_ci#include <linux/mmc/sdio_func.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "trace.h"
1862306a36Sopenharmony_ci#include "sdio.h"
1962306a36Sopenharmony_ci#include "mt76.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic int mt76s_refill_sched_quota(struct mt76_dev *dev, u32 *data)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	u32 ple_ac_data_quota[] = {
2462306a36Sopenharmony_ci		FIELD_GET(TXQ_CNT_L, data[4]), /* VO */
2562306a36Sopenharmony_ci		FIELD_GET(TXQ_CNT_H, data[3]), /* VI */
2662306a36Sopenharmony_ci		FIELD_GET(TXQ_CNT_L, data[3]), /* BE */
2762306a36Sopenharmony_ci		FIELD_GET(TXQ_CNT_H, data[2]), /* BK */
2862306a36Sopenharmony_ci	};
2962306a36Sopenharmony_ci	u32 pse_ac_data_quota[] = {
3062306a36Sopenharmony_ci		FIELD_GET(TXQ_CNT_H, data[1]), /* VO */
3162306a36Sopenharmony_ci		FIELD_GET(TXQ_CNT_L, data[1]), /* VI */
3262306a36Sopenharmony_ci		FIELD_GET(TXQ_CNT_H, data[0]), /* BE */
3362306a36Sopenharmony_ci		FIELD_GET(TXQ_CNT_L, data[0]), /* BK */
3462306a36Sopenharmony_ci	};
3562306a36Sopenharmony_ci	u32 pse_mcu_quota = FIELD_GET(TXQ_CNT_L, data[2]);
3662306a36Sopenharmony_ci	u32 pse_data_quota = 0, ple_data_quota = 0;
3762306a36Sopenharmony_ci	struct mt76_sdio *sdio = &dev->sdio;
3862306a36Sopenharmony_ci	int i;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pse_ac_data_quota); i++) {
4162306a36Sopenharmony_ci		pse_data_quota += pse_ac_data_quota[i];
4262306a36Sopenharmony_ci		ple_data_quota += ple_ac_data_quota[i];
4362306a36Sopenharmony_ci	}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	if (!pse_data_quota && !ple_data_quota && !pse_mcu_quota)
4662306a36Sopenharmony_ci		return 0;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	sdio->sched.pse_mcu_quota += pse_mcu_quota;
4962306a36Sopenharmony_ci	sdio->sched.pse_data_quota += pse_data_quota;
5062306a36Sopenharmony_ci	sdio->sched.ple_data_quota += ple_data_quota;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	return pse_data_quota + ple_data_quota + pse_mcu_quota;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic struct sk_buff *
5662306a36Sopenharmony_cimt76s_build_rx_skb(void *data, int data_len, int buf_len)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	int len = min_t(int, data_len, MT_SKB_HEAD_LEN);
5962306a36Sopenharmony_ci	struct sk_buff *skb;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	skb = alloc_skb(len, GFP_KERNEL);
6262306a36Sopenharmony_ci	if (!skb)
6362306a36Sopenharmony_ci		return NULL;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	skb_put_data(skb, data, len);
6662306a36Sopenharmony_ci	if (data_len > len) {
6762306a36Sopenharmony_ci		struct page *page;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci		data += len;
7062306a36Sopenharmony_ci		page = virt_to_head_page(data);
7162306a36Sopenharmony_ci		skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
7262306a36Sopenharmony_ci				page, data - page_address(page),
7362306a36Sopenharmony_ci				data_len - len, buf_len);
7462306a36Sopenharmony_ci		get_page(page);
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return skb;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic int
8162306a36Sopenharmony_cimt76s_rx_run_queue(struct mt76_dev *dev, enum mt76_rxq_id qid,
8262306a36Sopenharmony_ci		   struct mt76s_intr *intr)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	struct mt76_queue *q = &dev->q_rx[qid];
8562306a36Sopenharmony_ci	struct mt76_sdio *sdio = &dev->sdio;
8662306a36Sopenharmony_ci	int len = 0, err, i;
8762306a36Sopenharmony_ci	struct page *page;
8862306a36Sopenharmony_ci	u8 *buf, *end;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	for (i = 0; i < intr->rx.num[qid]; i++)
9162306a36Sopenharmony_ci		len += round_up(intr->rx.len[qid][i] + 4, 4);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (!len)
9462306a36Sopenharmony_ci		return 0;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (len > sdio->func->cur_blksize)
9762306a36Sopenharmony_ci		len = roundup(len, sdio->func->cur_blksize);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	page = __dev_alloc_pages(GFP_KERNEL, get_order(len));
10062306a36Sopenharmony_ci	if (!page)
10162306a36Sopenharmony_ci		return -ENOMEM;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	buf = page_address(page);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	sdio_claim_host(sdio->func);
10662306a36Sopenharmony_ci	err = sdio_readsb(sdio->func, buf, MCR_WRDR(qid), len);
10762306a36Sopenharmony_ci	sdio_release_host(sdio->func);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (err < 0) {
11062306a36Sopenharmony_ci		dev_err(dev->dev, "sdio read data failed:%d\n", err);
11162306a36Sopenharmony_ci		put_page(page);
11262306a36Sopenharmony_ci		return err;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	end = buf + len;
11662306a36Sopenharmony_ci	i = 0;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	while (i < intr->rx.num[qid] && buf < end) {
11962306a36Sopenharmony_ci		int index = (q->head + i) % q->ndesc;
12062306a36Sopenharmony_ci		struct mt76_queue_entry *e = &q->entry[index];
12162306a36Sopenharmony_ci		__le32 *rxd = (__le32 *)buf;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci		/* parse rxd to get the actual packet length */
12462306a36Sopenharmony_ci		len = le32_get_bits(rxd[0], GENMASK(15, 0));
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci		/* Optimized path for TXS */
12762306a36Sopenharmony_ci		if (!dev->drv->rx_check || dev->drv->rx_check(dev, buf, len)) {
12862306a36Sopenharmony_ci			e->skb = mt76s_build_rx_skb(buf, len,
12962306a36Sopenharmony_ci						    round_up(len + 4, 4));
13062306a36Sopenharmony_ci			if (!e->skb)
13162306a36Sopenharmony_ci				break;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci			if (q->queued + i + 1 == q->ndesc)
13462306a36Sopenharmony_ci				break;
13562306a36Sopenharmony_ci			i++;
13662306a36Sopenharmony_ci		}
13762306a36Sopenharmony_ci		buf += round_up(len + 4, 4);
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci	put_page(page);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	spin_lock_bh(&q->lock);
14262306a36Sopenharmony_ci	q->head = (q->head + i) % q->ndesc;
14362306a36Sopenharmony_ci	q->queued += i;
14462306a36Sopenharmony_ci	spin_unlock_bh(&q->lock);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	return i;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic int mt76s_rx_handler(struct mt76_dev *dev)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct mt76_sdio *sdio = &dev->sdio;
15262306a36Sopenharmony_ci	struct mt76s_intr intr;
15362306a36Sopenharmony_ci	int nframes = 0, ret;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	ret = sdio->parse_irq(dev, &intr);
15662306a36Sopenharmony_ci	if (ret)
15762306a36Sopenharmony_ci		return ret;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	trace_dev_irq(dev, intr.isr, 0);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (intr.isr & WHIER_RX0_DONE_INT_EN) {
16262306a36Sopenharmony_ci		ret = mt76s_rx_run_queue(dev, 0, &intr);
16362306a36Sopenharmony_ci		if (ret > 0) {
16462306a36Sopenharmony_ci			mt76_worker_schedule(&sdio->net_worker);
16562306a36Sopenharmony_ci			nframes += ret;
16662306a36Sopenharmony_ci		}
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (intr.isr & WHIER_RX1_DONE_INT_EN) {
17062306a36Sopenharmony_ci		ret = mt76s_rx_run_queue(dev, 1, &intr);
17162306a36Sopenharmony_ci		if (ret > 0) {
17262306a36Sopenharmony_ci			mt76_worker_schedule(&sdio->net_worker);
17362306a36Sopenharmony_ci			nframes += ret;
17462306a36Sopenharmony_ci		}
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	nframes += !!mt76s_refill_sched_quota(dev, intr.tx.wtqcr);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	return nframes;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic int
18362306a36Sopenharmony_cimt76s_tx_pick_quota(struct mt76_sdio *sdio, bool mcu, int buf_sz,
18462306a36Sopenharmony_ci		    int *pse_size, int *ple_size)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	int pse_sz;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	pse_sz = DIV_ROUND_UP(buf_sz + sdio->sched.deficit,
18962306a36Sopenharmony_ci			      sdio->sched.pse_page_size);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (mcu && sdio->hw_ver == MT76_CONNAC2_SDIO)
19262306a36Sopenharmony_ci		pse_sz = 1;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (mcu) {
19562306a36Sopenharmony_ci		if (sdio->sched.pse_mcu_quota < *pse_size + pse_sz)
19662306a36Sopenharmony_ci			return -EBUSY;
19762306a36Sopenharmony_ci	} else {
19862306a36Sopenharmony_ci		if (sdio->sched.pse_data_quota < *pse_size + pse_sz ||
19962306a36Sopenharmony_ci		    sdio->sched.ple_data_quota < *ple_size + 1)
20062306a36Sopenharmony_ci			return -EBUSY;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		*ple_size = *ple_size + 1;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci	*pse_size = *pse_size + pse_sz;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	return 0;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic void
21062306a36Sopenharmony_cimt76s_tx_update_quota(struct mt76_sdio *sdio, bool mcu, int pse_size,
21162306a36Sopenharmony_ci		      int ple_size)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	if (mcu) {
21462306a36Sopenharmony_ci		sdio->sched.pse_mcu_quota -= pse_size;
21562306a36Sopenharmony_ci	} else {
21662306a36Sopenharmony_ci		sdio->sched.pse_data_quota -= pse_size;
21762306a36Sopenharmony_ci		sdio->sched.ple_data_quota -= ple_size;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic int __mt76s_xmit_queue(struct mt76_dev *dev, u8 *data, int len)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	struct mt76_sdio *sdio = &dev->sdio;
22462306a36Sopenharmony_ci	int err;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (len > sdio->func->cur_blksize)
22762306a36Sopenharmony_ci		len = roundup(len, sdio->func->cur_blksize);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	sdio_claim_host(sdio->func);
23062306a36Sopenharmony_ci	err = sdio_writesb(sdio->func, MCR_WTDR1, data, len);
23162306a36Sopenharmony_ci	sdio_release_host(sdio->func);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (err)
23462306a36Sopenharmony_ci		dev_err(dev->dev, "sdio write failed: %d\n", err);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	return err;
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic int mt76s_tx_run_queue(struct mt76_dev *dev, struct mt76_queue *q)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	int err, nframes = 0, len = 0, pse_sz = 0, ple_sz = 0;
24262306a36Sopenharmony_ci	bool mcu = q == dev->q_mcu[MT_MCUQ_WM];
24362306a36Sopenharmony_ci	struct mt76_sdio *sdio = &dev->sdio;
24462306a36Sopenharmony_ci	u8 pad;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	while (q->first != q->head) {
24762306a36Sopenharmony_ci		struct mt76_queue_entry *e = &q->entry[q->first];
24862306a36Sopenharmony_ci		struct sk_buff *iter;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci		smp_rmb();
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci		if (test_bit(MT76_MCU_RESET, &dev->phy.state))
25362306a36Sopenharmony_ci			goto next;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		if (!test_bit(MT76_STATE_MCU_RUNNING, &dev->phy.state)) {
25662306a36Sopenharmony_ci			__skb_put_zero(e->skb, 4);
25762306a36Sopenharmony_ci			err = __skb_grow(e->skb, roundup(e->skb->len,
25862306a36Sopenharmony_ci							 sdio->func->cur_blksize));
25962306a36Sopenharmony_ci			if (err)
26062306a36Sopenharmony_ci				return err;
26162306a36Sopenharmony_ci			err = __mt76s_xmit_queue(dev, e->skb->data,
26262306a36Sopenharmony_ci						 e->skb->len);
26362306a36Sopenharmony_ci			if (err)
26462306a36Sopenharmony_ci				return err;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci			goto next;
26762306a36Sopenharmony_ci		}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci		pad = roundup(e->skb->len, 4) - e->skb->len;
27062306a36Sopenharmony_ci		if (len + e->skb->len + pad + 4 > dev->sdio.xmit_buf_sz)
27162306a36Sopenharmony_ci			break;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci		if (mt76s_tx_pick_quota(sdio, mcu, e->buf_sz, &pse_sz,
27462306a36Sopenharmony_ci					&ple_sz))
27562306a36Sopenharmony_ci			break;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci		memcpy(sdio->xmit_buf + len, e->skb->data, skb_headlen(e->skb));
27862306a36Sopenharmony_ci		len += skb_headlen(e->skb);
27962306a36Sopenharmony_ci		nframes++;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		skb_walk_frags(e->skb, iter) {
28262306a36Sopenharmony_ci			memcpy(sdio->xmit_buf + len, iter->data, iter->len);
28362306a36Sopenharmony_ci			len += iter->len;
28462306a36Sopenharmony_ci			nframes++;
28562306a36Sopenharmony_ci		}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci		if (unlikely(pad)) {
28862306a36Sopenharmony_ci			memset(sdio->xmit_buf + len, 0, pad);
28962306a36Sopenharmony_ci			len += pad;
29062306a36Sopenharmony_ci		}
29162306a36Sopenharmony_cinext:
29262306a36Sopenharmony_ci		q->first = (q->first + 1) % q->ndesc;
29362306a36Sopenharmony_ci		e->done = true;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if (nframes) {
29762306a36Sopenharmony_ci		memset(sdio->xmit_buf + len, 0, 4);
29862306a36Sopenharmony_ci		err = __mt76s_xmit_queue(dev, sdio->xmit_buf, len + 4);
29962306a36Sopenharmony_ci		if (err)
30062306a36Sopenharmony_ci			return err;
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci	mt76s_tx_update_quota(sdio, mcu, pse_sz, ple_sz);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	mt76_worker_schedule(&sdio->status_worker);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	return nframes;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_civoid mt76s_txrx_worker(struct mt76_sdio *sdio)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	struct mt76_dev *dev = container_of(sdio, struct mt76_dev, sdio);
31262306a36Sopenharmony_ci	int i, nframes, ret;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	/* disable interrupt */
31562306a36Sopenharmony_ci	sdio_claim_host(sdio->func);
31662306a36Sopenharmony_ci	sdio_writel(sdio->func, WHLPCR_INT_EN_CLR, MCR_WHLPCR, NULL);
31762306a36Sopenharmony_ci	sdio_release_host(sdio->func);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	do {
32062306a36Sopenharmony_ci		nframes = 0;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci		/* tx */
32362306a36Sopenharmony_ci		for (i = 0; i <= MT_TXQ_PSD; i++) {
32462306a36Sopenharmony_ci			ret = mt76s_tx_run_queue(dev, dev->phy.q_tx[i]);
32562306a36Sopenharmony_ci			if (ret > 0)
32662306a36Sopenharmony_ci				nframes += ret;
32762306a36Sopenharmony_ci		}
32862306a36Sopenharmony_ci		ret = mt76s_tx_run_queue(dev, dev->q_mcu[MT_MCUQ_WM]);
32962306a36Sopenharmony_ci		if (ret > 0)
33062306a36Sopenharmony_ci			nframes += ret;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci		/* rx */
33362306a36Sopenharmony_ci		ret = mt76s_rx_handler(dev);
33462306a36Sopenharmony_ci		if (ret > 0)
33562306a36Sopenharmony_ci			nframes += ret;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		if (test_bit(MT76_MCU_RESET, &dev->phy.state) ||
33862306a36Sopenharmony_ci		    test_bit(MT76_STATE_SUSPEND, &dev->phy.state)) {
33962306a36Sopenharmony_ci			if (!mt76s_txqs_empty(dev))
34062306a36Sopenharmony_ci				continue;
34162306a36Sopenharmony_ci			else
34262306a36Sopenharmony_ci				wake_up(&sdio->wait);
34362306a36Sopenharmony_ci		}
34462306a36Sopenharmony_ci	} while (nframes > 0);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	/* enable interrupt */
34762306a36Sopenharmony_ci	sdio_claim_host(sdio->func);
34862306a36Sopenharmony_ci	sdio_writel(sdio->func, WHLPCR_INT_EN_SET, MCR_WHLPCR, NULL);
34962306a36Sopenharmony_ci	sdio_release_host(sdio->func);
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76s_txrx_worker);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_civoid mt76s_sdio_irq(struct sdio_func *func)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	struct mt76_dev *dev = sdio_get_drvdata(func);
35662306a36Sopenharmony_ci	struct mt76_sdio *sdio = &dev->sdio;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	if (!test_bit(MT76_STATE_INITIALIZED, &dev->phy.state) ||
35962306a36Sopenharmony_ci	    test_bit(MT76_MCU_RESET, &dev->phy.state))
36062306a36Sopenharmony_ci		return;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	sdio_writel(sdio->func, WHLPCR_INT_EN_CLR, MCR_WHLPCR, NULL);
36362306a36Sopenharmony_ci	mt76_worker_schedule(&sdio->txrx_worker);
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76s_sdio_irq);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cibool mt76s_txqs_empty(struct mt76_dev *dev)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct mt76_queue *q;
37062306a36Sopenharmony_ci	int i;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	for (i = 0; i <= MT_TXQ_PSD + 1; i++) {
37362306a36Sopenharmony_ci		if (i <= MT_TXQ_PSD)
37462306a36Sopenharmony_ci			q = dev->phy.q_tx[i];
37562306a36Sopenharmony_ci		else
37662306a36Sopenharmony_ci			q = dev->q_mcu[MT_MCUQ_WM];
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci		if (q->first != q->head)
37962306a36Sopenharmony_ci			return false;
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	return true;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76s_txqs_empty);
385