xref: /openbmc/linux/drivers/net/wireless/mediatek/mt76/sdio_txrx.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1764dee47SLorenzo Bianconi // SPDX-License-Identifier: ISC
2764dee47SLorenzo Bianconi /* Copyright (C) 2020 MediaTek Inc.
3764dee47SLorenzo Bianconi  *
4764dee47SLorenzo Bianconi  * Author: Felix Fietkau <nbd@nbd.name>
5764dee47SLorenzo Bianconi  *	   Lorenzo Bianconi <lorenzo@kernel.org>
6764dee47SLorenzo Bianconi  *	   Sean Wang <sean.wang@mediatek.com>
7764dee47SLorenzo Bianconi  */
8764dee47SLorenzo Bianconi 
9764dee47SLorenzo Bianconi #include <linux/kernel.h>
10764dee47SLorenzo Bianconi #include <linux/iopoll.h>
11764dee47SLorenzo Bianconi #include <linux/module.h>
12764dee47SLorenzo Bianconi 
13764dee47SLorenzo Bianconi #include <linux/mmc/host.h>
14764dee47SLorenzo Bianconi #include <linux/mmc/sdio_ids.h>
15764dee47SLorenzo Bianconi #include <linux/mmc/sdio_func.h>
16764dee47SLorenzo Bianconi 
17764dee47SLorenzo Bianconi #include "trace.h"
18764dee47SLorenzo Bianconi #include "sdio.h"
19764dee47SLorenzo Bianconi #include "mt76.h"
20764dee47SLorenzo Bianconi 
mt76s_refill_sched_quota(struct mt76_dev * dev,u32 * data)21764dee47SLorenzo Bianconi static int mt76s_refill_sched_quota(struct mt76_dev *dev, u32 *data)
22764dee47SLorenzo Bianconi {
23764dee47SLorenzo Bianconi 	u32 ple_ac_data_quota[] = {
24764dee47SLorenzo Bianconi 		FIELD_GET(TXQ_CNT_L, data[4]), /* VO */
25764dee47SLorenzo Bianconi 		FIELD_GET(TXQ_CNT_H, data[3]), /* VI */
26764dee47SLorenzo Bianconi 		FIELD_GET(TXQ_CNT_L, data[3]), /* BE */
27764dee47SLorenzo Bianconi 		FIELD_GET(TXQ_CNT_H, data[2]), /* BK */
28764dee47SLorenzo Bianconi 	};
29764dee47SLorenzo Bianconi 	u32 pse_ac_data_quota[] = {
30764dee47SLorenzo Bianconi 		FIELD_GET(TXQ_CNT_H, data[1]), /* VO */
31764dee47SLorenzo Bianconi 		FIELD_GET(TXQ_CNT_L, data[1]), /* VI */
32764dee47SLorenzo Bianconi 		FIELD_GET(TXQ_CNT_H, data[0]), /* BE */
33764dee47SLorenzo Bianconi 		FIELD_GET(TXQ_CNT_L, data[0]), /* BK */
34764dee47SLorenzo Bianconi 	};
35764dee47SLorenzo Bianconi 	u32 pse_mcu_quota = FIELD_GET(TXQ_CNT_L, data[2]);
36764dee47SLorenzo Bianconi 	u32 pse_data_quota = 0, ple_data_quota = 0;
37764dee47SLorenzo Bianconi 	struct mt76_sdio *sdio = &dev->sdio;
38764dee47SLorenzo Bianconi 	int i;
39764dee47SLorenzo Bianconi 
40764dee47SLorenzo Bianconi 	for (i = 0; i < ARRAY_SIZE(pse_ac_data_quota); i++) {
41764dee47SLorenzo Bianconi 		pse_data_quota += pse_ac_data_quota[i];
42764dee47SLorenzo Bianconi 		ple_data_quota += ple_ac_data_quota[i];
43764dee47SLorenzo Bianconi 	}
44764dee47SLorenzo Bianconi 
45764dee47SLorenzo Bianconi 	if (!pse_data_quota && !ple_data_quota && !pse_mcu_quota)
46764dee47SLorenzo Bianconi 		return 0;
47764dee47SLorenzo Bianconi 
48764dee47SLorenzo Bianconi 	sdio->sched.pse_mcu_quota += pse_mcu_quota;
49764dee47SLorenzo Bianconi 	sdio->sched.pse_data_quota += pse_data_quota;
50764dee47SLorenzo Bianconi 	sdio->sched.ple_data_quota += ple_data_quota;
51764dee47SLorenzo Bianconi 
52764dee47SLorenzo Bianconi 	return pse_data_quota + ple_data_quota + pse_mcu_quota;
53764dee47SLorenzo Bianconi }
54764dee47SLorenzo Bianconi 
55764dee47SLorenzo Bianconi static struct sk_buff *
mt76s_build_rx_skb(void * data,int data_len,int buf_len)56764dee47SLorenzo Bianconi mt76s_build_rx_skb(void *data, int data_len, int buf_len)
57764dee47SLorenzo Bianconi {
58764dee47SLorenzo Bianconi 	int len = min_t(int, data_len, MT_SKB_HEAD_LEN);
59764dee47SLorenzo Bianconi 	struct sk_buff *skb;
60764dee47SLorenzo Bianconi 
61764dee47SLorenzo Bianconi 	skb = alloc_skb(len, GFP_KERNEL);
62764dee47SLorenzo Bianconi 	if (!skb)
63764dee47SLorenzo Bianconi 		return NULL;
64764dee47SLorenzo Bianconi 
65764dee47SLorenzo Bianconi 	skb_put_data(skb, data, len);
66764dee47SLorenzo Bianconi 	if (data_len > len) {
67764dee47SLorenzo Bianconi 		struct page *page;
68764dee47SLorenzo Bianconi 
69764dee47SLorenzo Bianconi 		data += len;
70764dee47SLorenzo Bianconi 		page = virt_to_head_page(data);
71764dee47SLorenzo Bianconi 		skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
72764dee47SLorenzo Bianconi 				page, data - page_address(page),
73764dee47SLorenzo Bianconi 				data_len - len, buf_len);
74764dee47SLorenzo Bianconi 		get_page(page);
75764dee47SLorenzo Bianconi 	}
76764dee47SLorenzo Bianconi 
77764dee47SLorenzo Bianconi 	return skb;
78764dee47SLorenzo Bianconi }
79764dee47SLorenzo Bianconi 
80764dee47SLorenzo Bianconi static int
mt76s_rx_run_queue(struct mt76_dev * dev,enum mt76_rxq_id qid,struct mt76s_intr * intr)81764dee47SLorenzo Bianconi mt76s_rx_run_queue(struct mt76_dev *dev, enum mt76_rxq_id qid,
82764dee47SLorenzo Bianconi 		   struct mt76s_intr *intr)
83764dee47SLorenzo Bianconi {
84764dee47SLorenzo Bianconi 	struct mt76_queue *q = &dev->q_rx[qid];
85764dee47SLorenzo Bianconi 	struct mt76_sdio *sdio = &dev->sdio;
86764dee47SLorenzo Bianconi 	int len = 0, err, i;
87764dee47SLorenzo Bianconi 	struct page *page;
8835e37a2bSLorenzo Bianconi 	u8 *buf, *end;
89764dee47SLorenzo Bianconi 
90764dee47SLorenzo Bianconi 	for (i = 0; i < intr->rx.num[qid]; i++)
91764dee47SLorenzo Bianconi 		len += round_up(intr->rx.len[qid][i] + 4, 4);
92764dee47SLorenzo Bianconi 
93764dee47SLorenzo Bianconi 	if (!len)
94764dee47SLorenzo Bianconi 		return 0;
95764dee47SLorenzo Bianconi 
96764dee47SLorenzo Bianconi 	if (len > sdio->func->cur_blksize)
97764dee47SLorenzo Bianconi 		len = roundup(len, sdio->func->cur_blksize);
98764dee47SLorenzo Bianconi 
99764dee47SLorenzo Bianconi 	page = __dev_alloc_pages(GFP_KERNEL, get_order(len));
100764dee47SLorenzo Bianconi 	if (!page)
101764dee47SLorenzo Bianconi 		return -ENOMEM;
102764dee47SLorenzo Bianconi 
103764dee47SLorenzo Bianconi 	buf = page_address(page);
104764dee47SLorenzo Bianconi 
105491e3731SSean Wang 	sdio_claim_host(sdio->func);
106764dee47SLorenzo Bianconi 	err = sdio_readsb(sdio->func, buf, MCR_WRDR(qid), len);
107491e3731SSean Wang 	sdio_release_host(sdio->func);
108491e3731SSean Wang 
109764dee47SLorenzo Bianconi 	if (err < 0) {
110764dee47SLorenzo Bianconi 		dev_err(dev->dev, "sdio read data failed:%d\n", err);
111764dee47SLorenzo Bianconi 		put_page(page);
112764dee47SLorenzo Bianconi 		return err;
113764dee47SLorenzo Bianconi 	}
114764dee47SLorenzo Bianconi 
11535e37a2bSLorenzo Bianconi 	end = buf + len;
11635e37a2bSLorenzo Bianconi 	i = 0;
11735e37a2bSLorenzo Bianconi 
11835e37a2bSLorenzo Bianconi 	while (i < intr->rx.num[qid] && buf < end) {
119764dee47SLorenzo Bianconi 		int index = (q->head + i) % q->ndesc;
120764dee47SLorenzo Bianconi 		struct mt76_queue_entry *e = &q->entry[index];
121dacf0acfSSean Wang 		__le32 *rxd = (__le32 *)buf;
122764dee47SLorenzo Bianconi 
123dacf0acfSSean Wang 		/* parse rxd to get the actual packet length */
124f1fe8eefSRyder Lee 		len = le32_get_bits(rxd[0], GENMASK(15, 0));
12535e37a2bSLorenzo Bianconi 
12635e37a2bSLorenzo Bianconi 		/* Optimized path for TXS */
12735e37a2bSLorenzo Bianconi 		if (!dev->drv->rx_check || dev->drv->rx_check(dev, buf, len)) {
12835e37a2bSLorenzo Bianconi 			e->skb = mt76s_build_rx_skb(buf, len,
12935e37a2bSLorenzo Bianconi 						    round_up(len + 4, 4));
130764dee47SLorenzo Bianconi 			if (!e->skb)
131764dee47SLorenzo Bianconi 				break;
132764dee47SLorenzo Bianconi 
133764dee47SLorenzo Bianconi 			if (q->queued + i + 1 == q->ndesc)
134764dee47SLorenzo Bianconi 				break;
13535e37a2bSLorenzo Bianconi 			i++;
13635e37a2bSLorenzo Bianconi 		}
13735e37a2bSLorenzo Bianconi 		buf += round_up(len + 4, 4);
138764dee47SLorenzo Bianconi 	}
139764dee47SLorenzo Bianconi 	put_page(page);
140764dee47SLorenzo Bianconi 
141764dee47SLorenzo Bianconi 	spin_lock_bh(&q->lock);
142764dee47SLorenzo Bianconi 	q->head = (q->head + i) % q->ndesc;
143764dee47SLorenzo Bianconi 	q->queued += i;
144764dee47SLorenzo Bianconi 	spin_unlock_bh(&q->lock);
145764dee47SLorenzo Bianconi 
146764dee47SLorenzo Bianconi 	return i;
147764dee47SLorenzo Bianconi }
148764dee47SLorenzo Bianconi 
mt76s_rx_handler(struct mt76_dev * dev)149764dee47SLorenzo Bianconi static int mt76s_rx_handler(struct mt76_dev *dev)
150764dee47SLorenzo Bianconi {
151764dee47SLorenzo Bianconi 	struct mt76_sdio *sdio = &dev->sdio;
1523ad08509SLorenzo Bianconi 	struct mt76s_intr intr;
153764dee47SLorenzo Bianconi 	int nframes = 0, ret;
154764dee47SLorenzo Bianconi 
1553ad08509SLorenzo Bianconi 	ret = sdio->parse_irq(dev, &intr);
1563ad08509SLorenzo Bianconi 	if (ret)
157764dee47SLorenzo Bianconi 		return ret;
158764dee47SLorenzo Bianconi 
1593ad08509SLorenzo Bianconi 	trace_dev_irq(dev, intr.isr, 0);
160764dee47SLorenzo Bianconi 
1613ad08509SLorenzo Bianconi 	if (intr.isr & WHIER_RX0_DONE_INT_EN) {
1623ad08509SLorenzo Bianconi 		ret = mt76s_rx_run_queue(dev, 0, &intr);
163764dee47SLorenzo Bianconi 		if (ret > 0) {
164764dee47SLorenzo Bianconi 			mt76_worker_schedule(&sdio->net_worker);
165764dee47SLorenzo Bianconi 			nframes += ret;
166764dee47SLorenzo Bianconi 		}
167764dee47SLorenzo Bianconi 	}
168764dee47SLorenzo Bianconi 
1693ad08509SLorenzo Bianconi 	if (intr.isr & WHIER_RX1_DONE_INT_EN) {
1703ad08509SLorenzo Bianconi 		ret = mt76s_rx_run_queue(dev, 1, &intr);
171764dee47SLorenzo Bianconi 		if (ret > 0) {
172764dee47SLorenzo Bianconi 			mt76_worker_schedule(&sdio->net_worker);
173764dee47SLorenzo Bianconi 			nframes += ret;
174764dee47SLorenzo Bianconi 		}
175764dee47SLorenzo Bianconi 	}
176764dee47SLorenzo Bianconi 
1773ad08509SLorenzo Bianconi 	nframes += !!mt76s_refill_sched_quota(dev, intr.tx.wtqcr);
178764dee47SLorenzo Bianconi 
179764dee47SLorenzo Bianconi 	return nframes;
180764dee47SLorenzo Bianconi }
181764dee47SLorenzo Bianconi 
182764dee47SLorenzo Bianconi static int
mt76s_tx_pick_quota(struct mt76_sdio * sdio,bool mcu,int buf_sz,int * pse_size,int * ple_size)183764dee47SLorenzo Bianconi mt76s_tx_pick_quota(struct mt76_sdio *sdio, bool mcu, int buf_sz,
184764dee47SLorenzo Bianconi 		    int *pse_size, int *ple_size)
185764dee47SLorenzo Bianconi {
186764dee47SLorenzo Bianconi 	int pse_sz;
187764dee47SLorenzo Bianconi 
18816d98b54SSean Wang 	pse_sz = DIV_ROUND_UP(buf_sz + sdio->sched.deficit,
18916d98b54SSean Wang 			      sdio->sched.pse_page_size);
190764dee47SLorenzo Bianconi 
191dacf0acfSSean Wang 	if (mcu && sdio->hw_ver == MT76_CONNAC2_SDIO)
192dacf0acfSSean Wang 		pse_sz = 1;
193dacf0acfSSean Wang 
194764dee47SLorenzo Bianconi 	if (mcu) {
195764dee47SLorenzo Bianconi 		if (sdio->sched.pse_mcu_quota < *pse_size + pse_sz)
196764dee47SLorenzo Bianconi 			return -EBUSY;
197764dee47SLorenzo Bianconi 	} else {
198764dee47SLorenzo Bianconi 		if (sdio->sched.pse_data_quota < *pse_size + pse_sz ||
199764dee47SLorenzo Bianconi 		    sdio->sched.ple_data_quota < *ple_size + 1)
200764dee47SLorenzo Bianconi 			return -EBUSY;
201764dee47SLorenzo Bianconi 
202764dee47SLorenzo Bianconi 		*ple_size = *ple_size + 1;
203764dee47SLorenzo Bianconi 	}
204764dee47SLorenzo Bianconi 	*pse_size = *pse_size + pse_sz;
205764dee47SLorenzo Bianconi 
206764dee47SLorenzo Bianconi 	return 0;
207764dee47SLorenzo Bianconi }
208764dee47SLorenzo Bianconi 
209764dee47SLorenzo Bianconi static void
mt76s_tx_update_quota(struct mt76_sdio * sdio,bool mcu,int pse_size,int ple_size)210764dee47SLorenzo Bianconi mt76s_tx_update_quota(struct mt76_sdio *sdio, bool mcu, int pse_size,
211764dee47SLorenzo Bianconi 		      int ple_size)
212764dee47SLorenzo Bianconi {
213764dee47SLorenzo Bianconi 	if (mcu) {
214764dee47SLorenzo Bianconi 		sdio->sched.pse_mcu_quota -= pse_size;
215764dee47SLorenzo Bianconi 	} else {
216764dee47SLorenzo Bianconi 		sdio->sched.pse_data_quota -= pse_size;
217764dee47SLorenzo Bianconi 		sdio->sched.ple_data_quota -= ple_size;
218764dee47SLorenzo Bianconi 	}
219764dee47SLorenzo Bianconi }
220764dee47SLorenzo Bianconi 
__mt76s_xmit_queue(struct mt76_dev * dev,u8 * data,int len)221764dee47SLorenzo Bianconi static int __mt76s_xmit_queue(struct mt76_dev *dev, u8 *data, int len)
222764dee47SLorenzo Bianconi {
223764dee47SLorenzo Bianconi 	struct mt76_sdio *sdio = &dev->sdio;
224764dee47SLorenzo Bianconi 	int err;
225764dee47SLorenzo Bianconi 
226764dee47SLorenzo Bianconi 	if (len > sdio->func->cur_blksize)
227764dee47SLorenzo Bianconi 		len = roundup(len, sdio->func->cur_blksize);
228764dee47SLorenzo Bianconi 
229491e3731SSean Wang 	sdio_claim_host(sdio->func);
230764dee47SLorenzo Bianconi 	err = sdio_writesb(sdio->func, MCR_WTDR1, data, len);
231491e3731SSean Wang 	sdio_release_host(sdio->func);
232491e3731SSean Wang 
233764dee47SLorenzo Bianconi 	if (err)
234764dee47SLorenzo Bianconi 		dev_err(dev->dev, "sdio write failed: %d\n", err);
235764dee47SLorenzo Bianconi 
236764dee47SLorenzo Bianconi 	return err;
237764dee47SLorenzo Bianconi }
238764dee47SLorenzo Bianconi 
mt76s_tx_run_queue(struct mt76_dev * dev,struct mt76_queue * q)239764dee47SLorenzo Bianconi static int mt76s_tx_run_queue(struct mt76_dev *dev, struct mt76_queue *q)
240764dee47SLorenzo Bianconi {
241bf08d585SSean Wang 	int err, nframes = 0, len = 0, pse_sz = 0, ple_sz = 0;
242764dee47SLorenzo Bianconi 	bool mcu = q == dev->q_mcu[MT_MCUQ_WM];
243764dee47SLorenzo Bianconi 	struct mt76_sdio *sdio = &dev->sdio;
244764dee47SLorenzo Bianconi 	u8 pad;
245764dee47SLorenzo Bianconi 
246764dee47SLorenzo Bianconi 	while (q->first != q->head) {
247764dee47SLorenzo Bianconi 		struct mt76_queue_entry *e = &q->entry[q->first];
248764dee47SLorenzo Bianconi 		struct sk_buff *iter;
249764dee47SLorenzo Bianconi 
250764dee47SLorenzo Bianconi 		smp_rmb();
251764dee47SLorenzo Bianconi 
252ca74b9b9SSean Wang 		if (test_bit(MT76_MCU_RESET, &dev->phy.state))
253ca74b9b9SSean Wang 			goto next;
254ca74b9b9SSean Wang 
255764dee47SLorenzo Bianconi 		if (!test_bit(MT76_STATE_MCU_RUNNING, &dev->phy.state)) {
256764dee47SLorenzo Bianconi 			__skb_put_zero(e->skb, 4);
257*aec4cf2eSDeren Wu 			err = __skb_grow(e->skb, roundup(e->skb->len,
258*aec4cf2eSDeren Wu 							 sdio->func->cur_blksize));
259*aec4cf2eSDeren Wu 			if (err)
260*aec4cf2eSDeren Wu 				return err;
261764dee47SLorenzo Bianconi 			err = __mt76s_xmit_queue(dev, e->skb->data,
262764dee47SLorenzo Bianconi 						 e->skb->len);
263764dee47SLorenzo Bianconi 			if (err)
264764dee47SLorenzo Bianconi 				return err;
265764dee47SLorenzo Bianconi 
266764dee47SLorenzo Bianconi 			goto next;
267764dee47SLorenzo Bianconi 		}
268764dee47SLorenzo Bianconi 
269764dee47SLorenzo Bianconi 		pad = roundup(e->skb->len, 4) - e->skb->len;
270bf08d585SSean Wang 		if (len + e->skb->len + pad + 4 > dev->sdio.xmit_buf_sz)
271764dee47SLorenzo Bianconi 			break;
272764dee47SLorenzo Bianconi 
273764dee47SLorenzo Bianconi 		if (mt76s_tx_pick_quota(sdio, mcu, e->buf_sz, &pse_sz,
274764dee47SLorenzo Bianconi 					&ple_sz))
275764dee47SLorenzo Bianconi 			break;
276764dee47SLorenzo Bianconi 
277bf08d585SSean Wang 		memcpy(sdio->xmit_buf + len, e->skb->data, skb_headlen(e->skb));
278764dee47SLorenzo Bianconi 		len += skb_headlen(e->skb);
279764dee47SLorenzo Bianconi 		nframes++;
280764dee47SLorenzo Bianconi 
281764dee47SLorenzo Bianconi 		skb_walk_frags(e->skb, iter) {
282bf08d585SSean Wang 			memcpy(sdio->xmit_buf + len, iter->data, iter->len);
283764dee47SLorenzo Bianconi 			len += iter->len;
284764dee47SLorenzo Bianconi 			nframes++;
285764dee47SLorenzo Bianconi 		}
286764dee47SLorenzo Bianconi 
287764dee47SLorenzo Bianconi 		if (unlikely(pad)) {
288bf08d585SSean Wang 			memset(sdio->xmit_buf + len, 0, pad);
289764dee47SLorenzo Bianconi 			len += pad;
290764dee47SLorenzo Bianconi 		}
291764dee47SLorenzo Bianconi next:
292764dee47SLorenzo Bianconi 		q->first = (q->first + 1) % q->ndesc;
293764dee47SLorenzo Bianconi 		e->done = true;
294764dee47SLorenzo Bianconi 	}
295764dee47SLorenzo Bianconi 
296764dee47SLorenzo Bianconi 	if (nframes) {
297bf08d585SSean Wang 		memset(sdio->xmit_buf + len, 0, 4);
298bf08d585SSean Wang 		err = __mt76s_xmit_queue(dev, sdio->xmit_buf, len + 4);
299764dee47SLorenzo Bianconi 		if (err)
300764dee47SLorenzo Bianconi 			return err;
301764dee47SLorenzo Bianconi 	}
302764dee47SLorenzo Bianconi 	mt76s_tx_update_quota(sdio, mcu, pse_sz, ple_sz);
303764dee47SLorenzo Bianconi 
304764dee47SLorenzo Bianconi 	mt76_worker_schedule(&sdio->status_worker);
305764dee47SLorenzo Bianconi 
306764dee47SLorenzo Bianconi 	return nframes;
307764dee47SLorenzo Bianconi }
308764dee47SLorenzo Bianconi 
mt76s_txrx_worker(struct mt76_sdio * sdio)309764dee47SLorenzo Bianconi void mt76s_txrx_worker(struct mt76_sdio *sdio)
310764dee47SLorenzo Bianconi {
311764dee47SLorenzo Bianconi 	struct mt76_dev *dev = container_of(sdio, struct mt76_dev, sdio);
312764dee47SLorenzo Bianconi 	int i, nframes, ret;
313764dee47SLorenzo Bianconi 
314764dee47SLorenzo Bianconi 	/* disable interrupt */
315764dee47SLorenzo Bianconi 	sdio_claim_host(sdio->func);
316764dee47SLorenzo Bianconi 	sdio_writel(sdio->func, WHLPCR_INT_EN_CLR, MCR_WHLPCR, NULL);
317491e3731SSean Wang 	sdio_release_host(sdio->func);
318764dee47SLorenzo Bianconi 
319764dee47SLorenzo Bianconi 	do {
320764dee47SLorenzo Bianconi 		nframes = 0;
321764dee47SLorenzo Bianconi 
322764dee47SLorenzo Bianconi 		/* tx */
323764dee47SLorenzo Bianconi 		for (i = 0; i <= MT_TXQ_PSD; i++) {
324764dee47SLorenzo Bianconi 			ret = mt76s_tx_run_queue(dev, dev->phy.q_tx[i]);
325764dee47SLorenzo Bianconi 			if (ret > 0)
326764dee47SLorenzo Bianconi 				nframes += ret;
327764dee47SLorenzo Bianconi 		}
328764dee47SLorenzo Bianconi 		ret = mt76s_tx_run_queue(dev, dev->q_mcu[MT_MCUQ_WM]);
329764dee47SLorenzo Bianconi 		if (ret > 0)
330764dee47SLorenzo Bianconi 			nframes += ret;
331764dee47SLorenzo Bianconi 
332764dee47SLorenzo Bianconi 		/* rx */
333764dee47SLorenzo Bianconi 		ret = mt76s_rx_handler(dev);
334764dee47SLorenzo Bianconi 		if (ret > 0)
335764dee47SLorenzo Bianconi 			nframes += ret;
336ca74b9b9SSean Wang 
3375ad4facaSSean Wang 		if (test_bit(MT76_MCU_RESET, &dev->phy.state) ||
3385ad4facaSSean Wang 		    test_bit(MT76_STATE_SUSPEND, &dev->phy.state)) {
339ca74b9b9SSean Wang 			if (!mt76s_txqs_empty(dev))
340ca74b9b9SSean Wang 				continue;
341ca74b9b9SSean Wang 			else
342ca74b9b9SSean Wang 				wake_up(&sdio->wait);
343ca74b9b9SSean Wang 		}
344764dee47SLorenzo Bianconi 	} while (nframes > 0);
345764dee47SLorenzo Bianconi 
346764dee47SLorenzo Bianconi 	/* enable interrupt */
347491e3731SSean Wang 	sdio_claim_host(sdio->func);
348764dee47SLorenzo Bianconi 	sdio_writel(sdio->func, WHLPCR_INT_EN_SET, MCR_WHLPCR, NULL);
349764dee47SLorenzo Bianconi 	sdio_release_host(sdio->func);
350764dee47SLorenzo Bianconi }
351764dee47SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76s_txrx_worker);
352764dee47SLorenzo Bianconi 
mt76s_sdio_irq(struct sdio_func * func)353764dee47SLorenzo Bianconi void mt76s_sdio_irq(struct sdio_func *func)
354764dee47SLorenzo Bianconi {
355764dee47SLorenzo Bianconi 	struct mt76_dev *dev = sdio_get_drvdata(func);
356764dee47SLorenzo Bianconi 	struct mt76_sdio *sdio = &dev->sdio;
357764dee47SLorenzo Bianconi 
358ca74b9b9SSean Wang 	if (!test_bit(MT76_STATE_INITIALIZED, &dev->phy.state) ||
359ca74b9b9SSean Wang 	    test_bit(MT76_MCU_RESET, &dev->phy.state))
360764dee47SLorenzo Bianconi 		return;
361764dee47SLorenzo Bianconi 
3624090d43aSSean Wang 	sdio_writel(sdio->func, WHLPCR_INT_EN_CLR, MCR_WHLPCR, NULL);
363764dee47SLorenzo Bianconi 	mt76_worker_schedule(&sdio->txrx_worker);
364764dee47SLorenzo Bianconi }
365764dee47SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76s_sdio_irq);
366ca74b9b9SSean Wang 
mt76s_txqs_empty(struct mt76_dev * dev)367ca74b9b9SSean Wang bool mt76s_txqs_empty(struct mt76_dev *dev)
368ca74b9b9SSean Wang {
369ca74b9b9SSean Wang 	struct mt76_queue *q;
370ca74b9b9SSean Wang 	int i;
371ca74b9b9SSean Wang 
372ca74b9b9SSean Wang 	for (i = 0; i <= MT_TXQ_PSD + 1; i++) {
373ca74b9b9SSean Wang 		if (i <= MT_TXQ_PSD)
374ca74b9b9SSean Wang 			q = dev->phy.q_tx[i];
375ca74b9b9SSean Wang 		else
376ca74b9b9SSean Wang 			q = dev->q_mcu[MT_MCUQ_WM];
377ca74b9b9SSean Wang 
378ca74b9b9SSean Wang 		if (q->first != q->head)
379ca74b9b9SSean Wang 			return false;
380ca74b9b9SSean Wang 	}
381ca74b9b9SSean Wang 
382ca74b9b9SSean Wang 	return true;
383ca74b9b9SSean Wang }
384ca74b9b9SSean Wang EXPORT_SYMBOL_GPL(mt76s_txqs_empty);
385