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