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