18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC 28c2ecf20Sopenharmony_ci/* Copyright (C) 2020 MediaTek Inc. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This file is written based on mt76/usb.c. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Author: Felix Fietkau <nbd@nbd.name> 78c2ecf20Sopenharmony_ci * Lorenzo Bianconi <lorenzo@kernel.org> 88c2ecf20Sopenharmony_ci * Sean Wang <sean.wang@mediatek.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/mmc/sdio_func.h> 158c2ecf20Sopenharmony_ci#include <linux/sched.h> 168c2ecf20Sopenharmony_ci#include <linux/kthread.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "mt76.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic int 218c2ecf20Sopenharmony_cimt76s_alloc_rx_queue(struct mt76_dev *dev, enum mt76_rxq_id qid) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci struct mt76_queue *q = &dev->q_rx[qid]; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci spin_lock_init(&q->lock); 268c2ecf20Sopenharmony_ci q->entry = devm_kcalloc(dev->dev, 278c2ecf20Sopenharmony_ci MT_NUM_RX_ENTRIES, sizeof(*q->entry), 288c2ecf20Sopenharmony_ci GFP_KERNEL); 298c2ecf20Sopenharmony_ci if (!q->entry) 308c2ecf20Sopenharmony_ci return -ENOMEM; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci q->ndesc = MT_NUM_RX_ENTRIES; 338c2ecf20Sopenharmony_ci q->head = q->tail = 0; 348c2ecf20Sopenharmony_ci q->queued = 0; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci return 0; 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int mt76s_alloc_tx(struct mt76_dev *dev) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct mt76_queue *q; 428c2ecf20Sopenharmony_ci int i; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci for (i = 0; i < MT_TXQ_MCU_WA; i++) { 458c2ecf20Sopenharmony_ci q = devm_kzalloc(dev->dev, sizeof(*q), GFP_KERNEL); 468c2ecf20Sopenharmony_ci if (!q) 478c2ecf20Sopenharmony_ci return -ENOMEM; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci spin_lock_init(&q->lock); 508c2ecf20Sopenharmony_ci q->hw_idx = i; 518c2ecf20Sopenharmony_ci dev->q_tx[i] = q; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci q->entry = devm_kcalloc(dev->dev, 548c2ecf20Sopenharmony_ci MT_NUM_TX_ENTRIES, sizeof(*q->entry), 558c2ecf20Sopenharmony_ci GFP_KERNEL); 568c2ecf20Sopenharmony_ci if (!q->entry) 578c2ecf20Sopenharmony_ci return -ENOMEM; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci q->ndesc = MT_NUM_TX_ENTRIES; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_civoid mt76s_stop_txrx(struct mt76_dev *dev) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct mt76_sdio *sdio = &dev->sdio; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci cancel_work_sync(&sdio->tx.xmit_work); 708c2ecf20Sopenharmony_ci cancel_work_sync(&sdio->tx.status_work); 718c2ecf20Sopenharmony_ci cancel_work_sync(&sdio->rx.recv_work); 728c2ecf20Sopenharmony_ci cancel_work_sync(&sdio->rx.net_work); 738c2ecf20Sopenharmony_ci cancel_work_sync(&sdio->stat_work); 748c2ecf20Sopenharmony_ci clear_bit(MT76_READING_STATS, &dev->phy.state); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci mt76_tx_status_check(dev, NULL, true); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76s_stop_txrx); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ciint mt76s_alloc_queues(struct mt76_dev *dev) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci int err; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci err = mt76s_alloc_rx_queue(dev, MT_RXQ_MAIN); 858c2ecf20Sopenharmony_ci if (err < 0) 868c2ecf20Sopenharmony_ci return err; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return mt76s_alloc_tx(dev); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76s_alloc_queues); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic struct mt76_queue_entry * 938c2ecf20Sopenharmony_cimt76s_get_next_rx_entry(struct mt76_queue *q) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct mt76_queue_entry *e = NULL; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci spin_lock_bh(&q->lock); 988c2ecf20Sopenharmony_ci if (q->queued > 0) { 998c2ecf20Sopenharmony_ci e = &q->entry[q->tail]; 1008c2ecf20Sopenharmony_ci q->tail = (q->tail + 1) % q->ndesc; 1018c2ecf20Sopenharmony_ci q->queued--; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci spin_unlock_bh(&q->lock); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return e; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic int 1098c2ecf20Sopenharmony_cimt76s_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci int qid = q - &dev->q_rx[MT_RXQ_MAIN]; 1128c2ecf20Sopenharmony_ci int nframes = 0; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci while (true) { 1158c2ecf20Sopenharmony_ci struct mt76_queue_entry *e; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (!test_bit(MT76_STATE_INITIALIZED, &dev->phy.state)) 1188c2ecf20Sopenharmony_ci break; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci e = mt76s_get_next_rx_entry(q); 1218c2ecf20Sopenharmony_ci if (!e || !e->skb) 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci dev->drv->rx_skb(dev, MT_RXQ_MAIN, e->skb); 1258c2ecf20Sopenharmony_ci e->skb = NULL; 1268c2ecf20Sopenharmony_ci nframes++; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci if (qid == MT_RXQ_MAIN) 1298c2ecf20Sopenharmony_ci mt76_rx_poll_complete(dev, MT_RXQ_MAIN, NULL); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return nframes; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic void mt76s_process_tx_queue(struct mt76_dev *dev, enum mt76_txq_id qid) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct mt76_queue *q = dev->q_tx[qid]; 1378c2ecf20Sopenharmony_ci struct mt76_queue_entry entry; 1388c2ecf20Sopenharmony_ci bool wake; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci while (q->queued > 0) { 1418c2ecf20Sopenharmony_ci if (!q->entry[q->tail].done) 1428c2ecf20Sopenharmony_ci break; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci entry = q->entry[q->tail]; 1458c2ecf20Sopenharmony_ci q->entry[q->tail].done = false; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (qid == MT_TXQ_MCU) { 1488c2ecf20Sopenharmony_ci dev_kfree_skb(entry.skb); 1498c2ecf20Sopenharmony_ci entry.skb = NULL; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci mt76_queue_tx_complete(dev, q, &entry); 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci wake = q->stopped && q->queued < q->ndesc - 8; 1568c2ecf20Sopenharmony_ci if (wake) 1578c2ecf20Sopenharmony_ci q->stopped = false; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (!q->queued) 1608c2ecf20Sopenharmony_ci wake_up(&dev->tx_wait); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (qid == MT_TXQ_MCU) 1638c2ecf20Sopenharmony_ci return; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci mt76_txq_schedule(&dev->phy, qid); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (wake) 1688c2ecf20Sopenharmony_ci ieee80211_wake_queue(dev->hw, qid); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic void mt76s_tx_status_data(struct work_struct *work) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct mt76_sdio *sdio; 1748c2ecf20Sopenharmony_ci struct mt76_dev *dev; 1758c2ecf20Sopenharmony_ci u8 update = 1; 1768c2ecf20Sopenharmony_ci u16 count = 0; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci sdio = container_of(work, struct mt76_sdio, stat_work); 1798c2ecf20Sopenharmony_ci dev = container_of(sdio, struct mt76_dev, sdio); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci while (true) { 1828c2ecf20Sopenharmony_ci if (test_bit(MT76_REMOVED, &dev->phy.state)) 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (!dev->drv->tx_status_data(dev, &update)) 1868c2ecf20Sopenharmony_ci break; 1878c2ecf20Sopenharmony_ci count++; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (count && test_bit(MT76_STATE_RUNNING, &dev->phy.state)) 1918c2ecf20Sopenharmony_ci queue_work(dev->wq, &sdio->stat_work); 1928c2ecf20Sopenharmony_ci else 1938c2ecf20Sopenharmony_ci clear_bit(MT76_READING_STATS, &dev->phy.state); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int 1978c2ecf20Sopenharmony_cimt76s_tx_queue_skb(struct mt76_dev *dev, enum mt76_txq_id qid, 1988c2ecf20Sopenharmony_ci struct sk_buff *skb, struct mt76_wcid *wcid, 1998c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct mt76_queue *q = dev->q_tx[qid]; 2028c2ecf20Sopenharmony_ci struct mt76_tx_info tx_info = { 2038c2ecf20Sopenharmony_ci .skb = skb, 2048c2ecf20Sopenharmony_ci }; 2058c2ecf20Sopenharmony_ci int err, len = skb->len; 2068c2ecf20Sopenharmony_ci u16 idx = q->head; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (q->queued == q->ndesc) 2098c2ecf20Sopenharmony_ci return -ENOSPC; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci skb->prev = skb->next = NULL; 2128c2ecf20Sopenharmony_ci err = dev->drv->tx_prepare_skb(dev, NULL, qid, wcid, sta, &tx_info); 2138c2ecf20Sopenharmony_ci if (err < 0) 2148c2ecf20Sopenharmony_ci return err; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci q->entry[q->head].skb = tx_info.skb; 2178c2ecf20Sopenharmony_ci q->entry[q->head].buf_sz = len; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci smp_wmb(); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci q->head = (q->head + 1) % q->ndesc; 2228c2ecf20Sopenharmony_ci q->queued++; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return idx; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic int 2288c2ecf20Sopenharmony_cimt76s_tx_queue_skb_raw(struct mt76_dev *dev, enum mt76_txq_id qid, 2298c2ecf20Sopenharmony_ci struct sk_buff *skb, u32 tx_info) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct mt76_queue *q = dev->q_tx[qid]; 2328c2ecf20Sopenharmony_ci int ret = -ENOSPC, len = skb->len, pad; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (q->queued == q->ndesc) 2358c2ecf20Sopenharmony_ci goto error; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci pad = round_up(skb->len, 4) - skb->len; 2388c2ecf20Sopenharmony_ci ret = mt76_skb_adjust_pad(skb, pad); 2398c2ecf20Sopenharmony_ci if (ret) 2408c2ecf20Sopenharmony_ci goto error; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci spin_lock_bh(&q->lock); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci q->entry[q->head].buf_sz = len; 2458c2ecf20Sopenharmony_ci q->entry[q->head].skb = skb; 2468c2ecf20Sopenharmony_ci q->head = (q->head + 1) % q->ndesc; 2478c2ecf20Sopenharmony_ci q->queued++; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci spin_unlock_bh(&q->lock); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return 0; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cierror: 2548c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return ret; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic void mt76s_tx_kick(struct mt76_dev *dev, struct mt76_queue *q) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct mt76_sdio *sdio = &dev->sdio; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci queue_work(sdio->txrx_wq, &sdio->tx.xmit_work); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic const struct mt76_queue_ops sdio_queue_ops = { 2678c2ecf20Sopenharmony_ci .tx_queue_skb = mt76s_tx_queue_skb, 2688c2ecf20Sopenharmony_ci .kick = mt76s_tx_kick, 2698c2ecf20Sopenharmony_ci .tx_queue_skb_raw = mt76s_tx_queue_skb_raw, 2708c2ecf20Sopenharmony_ci}; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic void mt76s_tx_work(struct work_struct *work) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct mt76_sdio *sdio = container_of(work, struct mt76_sdio, 2758c2ecf20Sopenharmony_ci tx.status_work); 2768c2ecf20Sopenharmony_ci struct mt76_dev *dev = container_of(sdio, struct mt76_dev, sdio); 2778c2ecf20Sopenharmony_ci int i; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci for (i = 0; i < MT_TXQ_MCU_WA; i++) 2808c2ecf20Sopenharmony_ci mt76s_process_tx_queue(dev, i); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (dev->drv->tx_status_data && 2838c2ecf20Sopenharmony_ci !test_and_set_bit(MT76_READING_STATS, &dev->phy.state)) 2848c2ecf20Sopenharmony_ci queue_work(dev->wq, &dev->sdio.stat_work); 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic void mt76s_rx_work(struct work_struct *work) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct mt76_sdio *sdio = container_of(work, struct mt76_sdio, 2908c2ecf20Sopenharmony_ci rx.net_work); 2918c2ecf20Sopenharmony_ci struct mt76_dev *dev = container_of(sdio, struct mt76_dev, sdio); 2928c2ecf20Sopenharmony_ci int i; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* rx processing */ 2958c2ecf20Sopenharmony_ci local_bh_disable(); 2968c2ecf20Sopenharmony_ci rcu_read_lock(); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci mt76_for_each_q_rx(dev, i) 2998c2ecf20Sopenharmony_ci mt76s_process_rx_queue(dev, &dev->q_rx[i]); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci rcu_read_unlock(); 3028c2ecf20Sopenharmony_ci local_bh_enable(); 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_civoid mt76s_deinit(struct mt76_dev *dev) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct mt76_sdio *sdio = &dev->sdio; 3088c2ecf20Sopenharmony_ci int i; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci mt76s_stop_txrx(dev); 3118c2ecf20Sopenharmony_ci if (sdio->txrx_wq) { 3128c2ecf20Sopenharmony_ci destroy_workqueue(sdio->txrx_wq); 3138c2ecf20Sopenharmony_ci sdio->txrx_wq = NULL; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci sdio_claim_host(sdio->func); 3178c2ecf20Sopenharmony_ci sdio_release_irq(sdio->func); 3188c2ecf20Sopenharmony_ci sdio_release_host(sdio->func); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci mt76_for_each_q_rx(dev, i) { 3218c2ecf20Sopenharmony_ci struct mt76_queue *q = &dev->q_rx[i]; 3228c2ecf20Sopenharmony_ci int j; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci for (j = 0; j < q->ndesc; j++) { 3258c2ecf20Sopenharmony_ci struct mt76_queue_entry *e = &q->entry[j]; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (!e->skb) 3288c2ecf20Sopenharmony_ci continue; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci dev_kfree_skb(e->skb); 3318c2ecf20Sopenharmony_ci e->skb = NULL; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76s_deinit); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ciint mt76s_init(struct mt76_dev *dev, struct sdio_func *func, 3388c2ecf20Sopenharmony_ci const struct mt76_bus_ops *bus_ops) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct mt76_sdio *sdio = &dev->sdio; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci sdio->txrx_wq = alloc_workqueue("mt76s_txrx_wq", 3438c2ecf20Sopenharmony_ci WQ_UNBOUND | WQ_HIGHPRI, 3448c2ecf20Sopenharmony_ci WQ_UNBOUND_MAX_ACTIVE); 3458c2ecf20Sopenharmony_ci if (!sdio->txrx_wq) 3468c2ecf20Sopenharmony_ci return -ENOMEM; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci INIT_WORK(&sdio->stat_work, mt76s_tx_status_data); 3498c2ecf20Sopenharmony_ci INIT_WORK(&sdio->tx.status_work, mt76s_tx_work); 3508c2ecf20Sopenharmony_ci INIT_WORK(&sdio->rx.net_work, mt76s_rx_work); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci mutex_init(&sdio->sched.lock); 3538c2ecf20Sopenharmony_ci dev->queue_ops = &sdio_queue_ops; 3548c2ecf20Sopenharmony_ci dev->bus = bus_ops; 3558c2ecf20Sopenharmony_ci dev->sdio.func = func; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76s_init); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); 3628c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>"); 3638c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 364