Files
mt76/dma.c
T
Lorenzo BianconiandFelix Fietkau fcc2f3d82b wifi: mt76: fix lock dependency problem for wed_lock
Fix the following kernel depency lock holding wed_lock with BH disabled.

[   40.579696] mt798x-wmac 18000000.wifi: attaching wed device 0 version 2
[   40.604648] platform 15010000.wed: MTK WED WO Firmware Version: DEV_000000, Build Time: 20221208202138
[   40.613972] platform 15010000.wed: MTK WED WO Chip ID 00 Region 3
[   40.943617]
[   40.945118] ========================================================
[   40.951457] WARNING: possible irq lock inversion dependency detected
[   40.957797] 5.15.127 #0 Not tainted
[   40.961276] --------------------------------------------------------
[   40.967614] insmod/2329 just changed the state of lock:
[   40.972827] ffffff8004003b08 (&dev->wed_lock){+.+.}-{2:2}, at: mt76_get_rxwi+0x1c/0xac [mt76]
[   40.981387] but this lock was taken by another, SOFTIRQ-safe lock in the past:
[   40.988592]  (&q->lock){+.-.}-{2:2}
[   40.988602]
[   40.988602]
[   40.988602] and interrupts could create inverse lock ordering between them.
[   40.988602]
[   41.003445]
[   41.003445] other info that might help us debug this:
[   41.009957]  Possible interrupt unsafe locking scenario:
[   41.009957]
[   41.016729]        CPU0                    CPU1
[   41.021245]        ----                    ----
[   41.025761]   lock(&dev->wed_lock);
[   41.029241]                                local_irq_disable();
[   41.035145]                                lock(&q->lock);
[   41.040620]                                lock(&dev->wed_lock);
[   41.046616]   <Interrupt>
[   41.049223]     lock(&q->lock);
[   41.052356]
[   41.052356]  *** DEADLOCK ***
[   41.052356]
[   41.058260] 1 lock held by insmod/2329:
[   41.062085]  #0: ffffff80003b9988 (&dev->mutex){....}-{3:3}, at: __driver_attach+0x88/0x190
[   41.070442]
[   41.070442] the shortest dependencies between 2nd lock and 1st lock:
[   41.078257]  -> (&q->lock){+.-.}-{2:2} {
[   41.082177]     HARDIRQ-ON-W at:
[   41.085396]                       lock_acquire+0xfc/0x2c0
[   41.090787]                       _raw_spin_lock_bh+0x84/0xa0
[   41.096525]                       mt76_dma_cleanup+0x24c/0x650 [mt76]
[   41.102977]                       mt76_dma_cleanup+0x614/0x650 [mt76]
[   41.109428]                       mt7915_eeprom_get_power_delta+0x1168/0x2464 [mt7915e]
[   41.117435]                       mt7915_eeprom_init+0x40/0x340 [mt7915e]
[   41.124222]                       cleanup_module+0x94/0xb28 [mt7915e]
[   41.130662]                       platform_probe+0x64/0xbc
[   41.136139]                       really_probe.part.0+0x98/0x2f4
[   41.142134]                       __driver_probe_device+0x94/0x16c
[   41.148303]                       driver_probe_device+0x40/0x120
[   41.154299]                       __driver_attach+0x94/0x190
[   41.159947]                       bus_for_each_dev+0x5c/0x94
[   41.165594]                       driver_attach+0x20/0x30
[   41.170983]                       bus_add_driver+0x104/0x1f4
[   41.176631]                       driver_register+0x74/0x120
[   41.182280]                       __platform_driver_register+0x24/0x30
[   41.188797]                       0xffffffc000cb1074
[   41.193754]                       do_one_initcall+0x70/0x2cc
[   41.199403]                       do_init_module+0x44/0x240
[   41.204968]                       load_module+0x1f5c/0x2874
[   41.210532]                       __do_sys_init_module+0x1d8/0x2ac
[   41.216702]                       __arm64_sys_init_module+0x18/0x20
[   41.222958]                       invoke_syscall.constprop.0+0x4c/0xe0
[   41.229474]                       do_el0_svc+0x50/0xf0
[   41.234602]                       el0_svc+0x4c/0xcc
[   41.239471]                       el0t_64_sync_handler+0xe0/0x110
[   41.245556]                       el0t_64_sync+0x15c/0x160
[   41.251029]     IN-SOFTIRQ-W at:
[   41.254249]                       lock_acquire+0xfc/0x2c0
[   41.259638]                       _raw_spin_lock_bh+0x84/0xa0
[   41.265372]                       mt76_queue_tx_complete+0x34/0x70 [mt76]
[   41.272170]                       mt76_free_pending_rxwi+0x36c/0x5d0 [mt76]
[   41.279140]                       mt76_free_pending_rxwi+0x5c0/0x5d0 [mt76]
[   41.286111]                       mt7915_eeprom_get_power_delta+0x620/0x2464 [mt7915e]
[   41.294026]                       __napi_poll.constprop.0+0x5c/0x230
[   41.300372]                       net_rx_action+0xe4/0x294
[   41.305847]                       _stext+0x154/0x4cc
[   41.310801]                       do_softirq+0xa4/0xbc
[   41.315930]                       __local_bh_enable_ip+0x168/0x174
[   41.322097]                       napi_threaded_poll+0xbc/0x140
[   41.328007]                       kthread+0x13c/0x150
[   41.333049]                       ret_from_fork+0x10/0x20
[   41.338437]     INITIAL USE at:
[   41.341568]                      lock_acquire+0xfc/0x2c0
[   41.346869]                      _raw_spin_lock_bh+0x84/0xa0
[   41.352519]                      mt76_dma_cleanup+0x24c/0x650 [mt76]
[   41.358882]                      mt76_dma_cleanup+0x614/0x650 [mt76]
[   41.365245]                      mt7915_eeprom_get_power_delta+0x1168/0x2464 [mt7915e]
[   41.373160]                      mt7915_eeprom_init+0x40/0x340 [mt7915e]
[   41.379860]                      cleanup_module+0x94/0xb28 [mt7915e]
[   41.386213]                      platform_probe+0x64/0xbc
[   41.391602]                      really_probe.part.0+0x98/0x2f4
[   41.397511]                      __driver_probe_device+0x94/0x16c
[   41.403594]                      driver_probe_device+0x40/0x120
[   41.409502]                      __driver_attach+0x94/0x190
[   41.415063]                      bus_for_each_dev+0x5c/0x94
[   41.420625]                      driver_attach+0x20/0x30
[   41.425926]                      bus_add_driver+0x104/0x1f4
[   41.431487]                      driver_register+0x74/0x120
[   41.437049]                      __platform_driver_register+0x24/0x30
[   41.443479]                      0xffffffc000cb1074
[   41.448346]                      do_one_initcall+0x70/0x2cc
[   41.453907]                      do_init_module+0x44/0x240
[   41.459383]                      load_module+0x1f5c/0x2874
[   41.464860]                      __do_sys_init_module+0x1d8/0x2ac
[   41.470944]                      __arm64_sys_init_module+0x18/0x20
[   41.477113]                      invoke_syscall.constprop.0+0x4c/0xe0
[   41.483542]                      do_el0_svc+0x50/0xf0
[   41.488582]                      el0_svc+0x4c/0xcc
[   41.493364]                      el0t_64_sync_handler+0xe0/0x110
[   41.499361]                      el0t_64_sync+0x15c/0x160
[   41.504748]   }
[   41.506489]   ... key      at: [<ffffffc000c65ba0>] __this_module+0x3e0/0xffffffffffffa840 [mt76]
[   41.515371]   ... acquired at:
[   41.518413]    _raw_spin_lock+0x60/0x74
[   41.522240]    mt76_get_rxwi+0x1c/0xac [mt76]
[   41.526608]    mt76_dma_cleanup+0x3e0/0x650 [mt76]
[   41.531410]    mt76_dma_cleanup+0x614/0x650 [mt76]
[   41.536211]    mt7915_dma_init+0x408/0x7b0 [mt7915e]
[   41.541177]    mt7915_register_device+0x310/0x620 [mt7915e]
[   41.546749]    mt7915_mmio_probe+0xcec/0x1d44 [mt7915e]
[   41.551973]    platform_probe+0x64/0xbc
[   41.555802]    really_probe.part.0+0x98/0x2f4
[   41.560149]    __driver_probe_device+0x94/0x16c
[   41.564670]    driver_probe_device+0x40/0x120
[   41.569017]    __driver_attach+0x94/0x190
[   41.573019]    bus_for_each_dev+0x5c/0x94
[   41.577018]    driver_attach+0x20/0x30
[   41.580758]    bus_add_driver+0x104/0x1f4
[   41.584758]    driver_register+0x74/0x120
[   41.588759]    __platform_driver_register+0x24/0x30
[   41.593628]    init_module+0x74/0x1000 [mt7915e]
[   41.598248]    do_one_initcall+0x70/0x2cc
[   41.602248]    do_init_module+0x44/0x240
[   41.606162]    load_module+0x1f5c/0x2874
[   41.610078]    __do_sys_init_module+0x1d8/0x2ac
[   41.614600]    __arm64_sys_init_module+0x18/0x20
[   41.619209]    invoke_syscall.constprop.0+0x4c/0xe0
[   41.624076]    do_el0_svc+0x50/0xf0
[   41.627555]    el0_svc+0x4c/0xcc
[   41.630776]    el0t_64_sync_handler+0xe0/0x110
[   41.635211]    el0t_64_sync+0x15c/0x160
[   41.639037]
[   41.640517] -> (&dev->wed_lock){+.+.}-{2:2} {
[   41.644872]    HARDIRQ-ON-W at:
[   41.648003]                     lock_acquire+0xfc/0x2c0
[   41.653219]                     _raw_spin_lock+0x60/0x74
[   41.658520]                     mt76_free_pending_rxwi+0xc0/0x5d0 [mt76]
[   41.665232]                     mt76_dma_cleanup+0x1dc/0x650 [mt76]
[   41.671508]                     mt7915_eeprom_get_power_delta+0x1830/0x2464 [mt7915e]
[   41.679336]                     mt7915_unregister_device+0x5b4/0x910 [mt7915e]
[   41.686555]                     mt7915_eeprom_get_target_power+0xb8/0x230 [mt7915e]
[   41.694209]                     mt7986_wmac_enable+0xc30/0xcd0 [mt7915e]
[   41.700909]                     platform_remove+0x4c/0x64
[   41.706298]                     __device_release_driver+0x194/0x240
[   41.712554]                     driver_detach+0xc0/0x100
[   41.717857]                     bus_remove_driver+0x54/0xac
[   41.723418]                     driver_unregister+0x2c/0x54
[   41.728980]                     platform_driver_unregister+0x10/0x20
[   41.735323]                     mt7915_ops+0x244/0xffffffffffffed58 [mt7915e]
[   41.742457]                     __arm64_sys_delete_module+0x170/0x23c
[   41.748887]                     invoke_syscall.constprop.0+0x4c/0xe0
[   41.755229]                     do_el0_svc+0x50/0xf0
[   41.760183]                     el0_svc+0x4c/0xcc
[   41.764878]                     el0t_64_sync_handler+0xe0/0x110
[   41.770788]                     el0t_64_sync+0x15c/0x160
[   41.776088]    SOFTIRQ-ON-W at:
[   41.779220]                     lock_acquire+0xfc/0x2c0
[   41.784435]                     _raw_spin_lock+0x60/0x74
[   41.789737]                     mt76_get_rxwi+0x1c/0xac [mt76]
[   41.795580]                     mt7915_debugfs_rx_log+0x804/0xb74 [mt7915e]
[   41.802540]                     mtk_wed_start+0x970/0xaa0
[   41.807929]                     mt7915_dma_start+0x26c/0x630 [mt7915e]
[   41.814455]                     mt7915_dma_start+0x5a4/0x630 [mt7915e]
[   41.820981]                     mt7915_dma_init+0x45c/0x7b0 [mt7915e]
[   41.827420]                     mt7915_register_device+0x310/0x620 [mt7915e]
[   41.834467]                     mt7915_mmio_probe+0xcec/0x1d44 [mt7915e]
[   41.841167]                     platform_probe+0x64/0xbc
[   41.846469]                     really_probe.part.0+0x98/0x2f4
[   41.852291]                     __driver_probe_device+0x94/0x16c
[   41.858286]                     driver_probe_device+0x40/0x120
[   41.864107]                     __driver_attach+0x94/0x190
[   41.869582]                     bus_for_each_dev+0x5c/0x94
[   41.875056]                     driver_attach+0x20/0x30
[   41.880270]                     bus_add_driver+0x104/0x1f4
[   41.885745]                     driver_register+0x74/0x120
[   41.891221]                     __platform_driver_register+0x24/0x30
[   41.897564]                     init_module+0x74/0x1000 [mt7915e]
[   41.903657]                     do_one_initcall+0x70/0x2cc
[   41.909130]                     do_init_module+0x44/0x240
[   41.914520]                     load_module+0x1f5c/0x2874
[   41.919909]                     __do_sys_init_module+0x1d8/0x2ac
[   41.925905]                     __arm64_sys_init_module+0x18/0x20
[   41.931989]                     invoke_syscall.constprop.0+0x4c/0xe0
[   41.938331]                     do_el0_svc+0x50/0xf0
[   41.943285]                     el0_svc+0x4c/0xcc
[   41.947981]                     el0t_64_sync_handler+0xe0/0x110
[   41.953892]                     el0t_64_sync+0x15c/0x160
[   41.959192]    INITIAL USE at:
[   41.962238]                    lock_acquire+0xfc/0x2c0
[   41.967365]                    _raw_spin_lock+0x60/0x74
[   41.972580]                    mt76_free_pending_rxwi+0xc0/0x5d0 [mt76]
[   41.979206]                    mt76_dma_cleanup+0x1dc/0x650 [mt76]
[   41.985395]                    mt7915_eeprom_get_power_delta+0x1830/0x2464 [mt7915e]
[   41.993137]                    mt7915_unregister_device+0x5b4/0x910 [mt7915e]
[   42.000270]                    mt7915_eeprom_get_target_power+0xb8/0x230 [mt7915e]
[   42.007837]                    mt7986_wmac_enable+0xc30/0xcd0 [mt7915e]
[   42.014450]                    platform_remove+0x4c/0x64
[   42.019753]                    __device_release_driver+0x194/0x240
[   42.025922]                    driver_detach+0xc0/0x100
[   42.031137]                    bus_remove_driver+0x54/0xac
[   42.036612]                    driver_unregister+0x2c/0x54
[   42.042087]                    platform_driver_unregister+0x10/0x20
[   42.048344]                    mt7915_ops+0x244/0xffffffffffffed58 [mt7915e]
[   42.055391]                    __arm64_sys_delete_module+0x170/0x23c
[   42.061735]                    invoke_syscall.constprop.0+0x4c/0xe0
[   42.067990]                    do_el0_svc+0x50/0xf0
[   42.072857]                    el0_svc+0x4c/0xcc
[   42.077466]                    el0t_64_sync_handler+0xe0/0x110
[   42.083289]                    el0t_64_sync+0x15c/0x160
[   42.088503]  }
[   42.090157]  ... key      at: [<ffffffc000c65c10>] __this_module+0x450/0xffffffffffffa840 [mt76]
[   42.098951]  ... acquired at:
[   42.101907]    __lock_acquire+0x718/0x1df0
[   42.105994]    lock_acquire+0xfc/0x2c0
[   42.109734]    _raw_spin_lock+0x60/0x74
[   42.113561]    mt76_get_rxwi+0x1c/0xac [mt76]
[   42.117929]    mt7915_debugfs_rx_log+0x804/0xb74 [mt7915e]
[   42.123415]    mtk_wed_start+0x970/0xaa0
[   42.127328]    mt7915_dma_start+0x26c/0x630 [mt7915e]
[   42.132379]    mt7915_dma_start+0x5a4/0x630 [mt7915e]
[   42.137430]    mt7915_dma_init+0x45c/0x7b0 [mt7915e]
[   42.142395]    mt7915_register_device+0x310/0x620 [mt7915e]
[   42.147967]    mt7915_mmio_probe+0xcec/0x1d44 [mt7915e]
[   42.153192]    platform_probe+0x64/0xbc
[   42.157019]    really_probe.part.0+0x98/0x2f4
[   42.161367]    __driver_probe_device+0x94/0x16c
[   42.165887]    driver_probe_device+0x40/0x120
[   42.170234]    __driver_attach+0x94/0x190
[   42.174235]    bus_for_each_dev+0x5c/0x94
[   42.178235]    driver_attach+0x20/0x30
[   42.181974]    bus_add_driver+0x104/0x1f4
[   42.185974]    driver_register+0x74/0x120
[   42.189974]    __platform_driver_register+0x24/0x30
[   42.194842]    init_module+0x74/0x1000 [mt7915e]
[   42.199460]    do_one_initcall+0x70/0x2cc
[   42.203460]    do_init_module+0x44/0x240
[   42.207376]    load_module+0x1f5c/0x2874
[   42.211290]    __do_sys_init_module+0x1d8/0x2ac
[   42.215813]    __arm64_sys_init_module+0x18/0x20
[   42.220421]    invoke_syscall.constprop.0+0x4c/0xe0
[   42.225288]    do_el0_svc+0x50/0xf0
[   42.228768]    el0_svc+0x4c/0xcc
[   42.231989]    el0t_64_sync_handler+0xe0/0x110
[   42.236424]    el0t_64_sync+0x15c/0x160
[   42.240249]
[   42.241730]
[   42.241730] stack backtrace:
[   42.246074] CPU: 1 PID: 2329 Comm: insmod Not tainted 5.15.127 #0
[   42.252157] Hardware name: GainStrong Oolite-MT7981B V1 Dev Board (NAND boot) (DT)
[   42.259712] Call trace:
[   42.262147]  dump_backtrace+0x0/0x174
[   42.265802]  show_stack+0x14/0x20
[   42.269108]  dump_stack_lvl+0x84/0xac
[   42.272761]  dump_stack+0x14/0x2c
[   42.276066]  print_irq_inversion_bug.part.0+0x1b0/0x1c4
[   42.281285]  mark_lock+0x8b8/0x8bc
[   42.284678]  __lock_acquire+0x718/0x1df0
[   42.288592]  lock_acquire+0xfc/0x2c0
[   42.292158]  _raw_spin_lock+0x60/0x74
[   42.295811]  mt76_get_rxwi+0x1c/0xac [mt76]
[   42.300008]  mt7915_debugfs_rx_log+0x804/0xb74 [mt7915e]
[   42.305320]  mtk_wed_start+0x970/0xaa0
[   42.309059]  mt7915_dma_start+0x26c/0x630 [mt7915e]
[   42.313937]  mt7915_dma_start+0x5a4/0x630 [mt7915e]
[   42.318815]  mt7915_dma_init+0x45c/0x7b0 [mt7915e]
[   42.323606]  mt7915_register_device+0x310/0x620 [mt7915e]
[   42.329005]  mt7915_mmio_probe+0xcec/0x1d44 [mt7915e]
[   42.334056]  platform_probe+0x64/0xbc
[   42.337711]  really_probe.part.0+0x98/0x2f4
[   42.341885]  __driver_probe_device+0x94/0x16c
[   42.346232]  driver_probe_device+0x40/0x120
[   42.350407]  __driver_attach+0x94/0x190
[   42.354234]  bus_for_each_dev+0x5c/0x94
[   42.358061]  driver_attach+0x20/0x30
[   42.361627]  bus_add_driver+0x104/0x1f4
[   42.365454]  driver_register+0x74/0x120
[   42.369282]  __platform_driver_register+0x24/0x30
[   42.373977]  init_module+0x74/0x1000 [mt7915e]
[   42.378423]  do_one_initcall+0x70/0x2cc
[   42.382249]  do_init_module+0x44/0x240
[   42.385990]  load_module+0x1f5c/0x2874
[   42.389733]  __do_sys_init_module+0x1d8/0x2ac
[   42.394082]  __arm64_sys_init_module+0x18/0x20
[   42.398518]  invoke_syscall.constprop.0+0x4c/0xe0
[   42.403211]  do_el0_svc+0x50/0xf0
[   42.406517]  el0_svc+0x4c/0xcc
[   42.409565]  el0t_64_sync_handler+0xe0/0x110
[   42.413827]  el0t_64_sync+0x15c/0x160
[   42.674858] mt798x-wmac 18000000.wifi: HW/SW Version: 0x8a108a10, Build Time: 20221208201745a
[   42.674858]
[   42.692078] mt798x-wmac 18000000.wifi: WM Firmware Version: ____000000, Build Time: 20221208201806
[   42.735606] mt798x-wmac 18000000.wifi: WA Firmware Version: DEV_000000, Build Time: 20221208202048

Tested-by: Daniel Golle <[email protected]>
Fixes: 2666bece0905 ("wifi: mt76: introduce rxwi and rx token utility routines")
Signed-off-by: Lorenzo Bianconi <[email protected]>
Acked-by: Felix Fietkau <[email protected]>
Signed-off-by: Kalle Valo <[email protected]>
Link: https://lore.kernel.org/r/ee80be41c2a8d8749d83c6950a272a5e77aadd45.1693228333.git.lorenzo@kernel.org
2023-09-19 21:48:30 +02:00

995 lines
21 KiB
C

// SPDX-License-Identifier: ISC
/*
* Copyright (C) 2016 Felix Fietkau <[email protected]>
*/
#include <linux/dma-mapping.h>
#include "mt76.h"
#include "dma.h"
#if IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED)
#define Q_READ(_dev, _q, _field) ({ \
u32 _offset = offsetof(struct mt76_queue_regs, _field); \
u32 _val; \
if ((_q)->flags & MT_QFLAG_WED) \
_val = mtk_wed_device_reg_read(&(_dev)->mmio.wed, \
((_q)->wed_regs + \
_offset)); \
else \
_val = readl(&(_q)->regs->_field); \
_val; \
})
#define Q_WRITE(_dev, _q, _field, _val) do { \
u32 _offset = offsetof(struct mt76_queue_regs, _field); \
if ((_q)->flags & MT_QFLAG_WED) \
mtk_wed_device_reg_write(&(_dev)->mmio.wed, \
((_q)->wed_regs + _offset), \
_val); \
else \
writel(_val, &(_q)->regs->_field); \
} while (0)
#else
#define Q_READ(_dev, _q, _field) readl(&(_q)->regs->_field)
#define Q_WRITE(_dev, _q, _field, _val) writel(_val, &(_q)->regs->_field)
#endif
static struct mt76_txwi_cache *
mt76_alloc_txwi(struct mt76_dev *dev)
{
struct mt76_txwi_cache *t;
dma_addr_t addr;
u8 *txwi;
int size;
size = L1_CACHE_ALIGN(dev->drv->txwi_size + sizeof(*t));
txwi = kzalloc(size, GFP_ATOMIC);
if (!txwi)
return NULL;
addr = dma_map_single(dev->dma_dev, txwi, dev->drv->txwi_size,
DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(dev->dma_dev, addr))) {
kfree(txwi);
return NULL;
}
t = (struct mt76_txwi_cache *)(txwi + dev->drv->txwi_size);
t->dma_addr = addr;
return t;
}
static struct mt76_txwi_cache *
mt76_alloc_rxwi(struct mt76_dev *dev)
{
struct mt76_txwi_cache *t;
t = kzalloc(L1_CACHE_ALIGN(sizeof(*t)), GFP_ATOMIC);
if (!t)
return NULL;
t->ptr = NULL;
return t;
}
static struct mt76_txwi_cache *
__mt76_get_txwi(struct mt76_dev *dev)
{
struct mt76_txwi_cache *t = NULL;
spin_lock(&dev->lock);
if (!list_empty(&dev->txwi_cache)) {
t = list_first_entry(&dev->txwi_cache, struct mt76_txwi_cache,
list);
list_del(&t->list);
}
spin_unlock(&dev->lock);
return t;
}
static struct mt76_txwi_cache *
__mt76_get_rxwi(struct mt76_dev *dev)
{
struct mt76_txwi_cache *t = NULL;
spin_lock_bh(&dev->wed_lock);
if (!list_empty(&dev->rxwi_cache)) {
t = list_first_entry(&dev->rxwi_cache, struct mt76_txwi_cache,
list);
list_del(&t->list);
}
spin_unlock_bh(&dev->wed_lock);
return t;
}
static struct mt76_txwi_cache *
mt76_get_txwi(struct mt76_dev *dev)
{
struct mt76_txwi_cache *t = __mt76_get_txwi(dev);
if (t)
return t;
return mt76_alloc_txwi(dev);
}
struct mt76_txwi_cache *
mt76_get_rxwi(struct mt76_dev *dev)
{
struct mt76_txwi_cache *t = __mt76_get_rxwi(dev);
if (t)
return t;
return mt76_alloc_rxwi(dev);
}
EXPORT_SYMBOL_GPL(mt76_get_rxwi);
void
mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t)
{
if (!t)
return;
spin_lock(&dev->lock);
list_add(&t->list, &dev->txwi_cache);
spin_unlock(&dev->lock);
}
EXPORT_SYMBOL_GPL(mt76_put_txwi);
void
mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t)
{
if (!t)
return;
spin_lock_bh(&dev->wed_lock);
list_add(&t->list, &dev->rxwi_cache);
spin_unlock_bh(&dev->wed_lock);
}
EXPORT_SYMBOL_GPL(mt76_put_rxwi);
static void
mt76_free_pending_txwi(struct mt76_dev *dev)
{
struct mt76_txwi_cache *t;
local_bh_disable();
while ((t = __mt76_get_txwi(dev)) != NULL) {
dma_unmap_single(dev->dma_dev, t->dma_addr, dev->drv->txwi_size,
DMA_TO_DEVICE);
kfree(mt76_get_txwi_ptr(dev, t));
}
local_bh_enable();
}
void
mt76_free_pending_rxwi(struct mt76_dev *dev)
{
struct mt76_txwi_cache *t;
local_bh_disable();
while ((t = __mt76_get_rxwi(dev)) != NULL) {
if (t->ptr)
mt76_put_page_pool_buf(t->ptr, false);
kfree(t);
}
local_bh_enable();
}
EXPORT_SYMBOL_GPL(mt76_free_pending_rxwi);
static void
mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q)
{
Q_WRITE(dev, q, desc_base, q->desc_dma);
Q_WRITE(dev, q, ring_size, q->ndesc);
q->head = Q_READ(dev, q, dma_idx);
q->tail = q->head;
}
static void
mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q)
{
int i;
if (!q || !q->ndesc)
return;
/* clear descriptors */
for (i = 0; i < q->ndesc; i++)
q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE);
Q_WRITE(dev, q, cpu_idx, 0);
Q_WRITE(dev, q, dma_idx, 0);
mt76_dma_sync_idx(dev, q);
}
static int
mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
struct mt76_queue_buf *buf, void *data)
{
struct mt76_desc *desc = &q->desc[q->head];
struct mt76_queue_entry *entry = &q->entry[q->head];
struct mt76_txwi_cache *txwi = NULL;
u32 buf1 = 0, ctrl;
int idx = q->head;
int rx_token;
ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len);
if (mt76_queue_is_wed_rx(q)) {
txwi = mt76_get_rxwi(dev);
if (!txwi)
return -ENOMEM;
rx_token = mt76_rx_token_consume(dev, data, txwi, buf->addr);
if (rx_token < 0) {
mt76_put_rxwi(dev, txwi);
return -ENOMEM;
}
buf1 |= FIELD_PREP(MT_DMA_CTL_TOKEN, rx_token);
ctrl |= MT_DMA_CTL_TO_HOST;
}
WRITE_ONCE(desc->buf0, cpu_to_le32(buf->addr));
WRITE_ONCE(desc->buf1, cpu_to_le32(buf1));
WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
WRITE_ONCE(desc->info, 0);
entry->dma_addr[0] = buf->addr;
entry->dma_len[0] = buf->len;
entry->txwi = txwi;
entry->buf = data;
entry->wcid = 0xffff;
entry->skip_buf1 = true;
q->head = (q->head + 1) % q->ndesc;
q->queued++;
return idx;
}
static int
mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q,
struct mt76_queue_buf *buf, int nbufs, u32 info,
struct sk_buff *skb, void *txwi)
{
struct mt76_queue_entry *entry;
struct mt76_desc *desc;
int i, idx = -1;
u32 ctrl, next;
if (txwi) {
q->entry[q->head].txwi = DMA_DUMMY_DATA;
q->entry[q->head].skip_buf0 = true;
}
for (i = 0; i < nbufs; i += 2, buf += 2) {
u32 buf0 = buf[0].addr, buf1 = 0;
idx = q->head;
next = (q->head + 1) % q->ndesc;
desc = &q->desc[idx];
entry = &q->entry[idx];
if (buf[0].skip_unmap)
entry->skip_buf0 = true;
entry->skip_buf1 = i == nbufs - 1;
entry->dma_addr[0] = buf[0].addr;
entry->dma_len[0] = buf[0].len;
ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len);
if (i < nbufs - 1) {
entry->dma_addr[1] = buf[1].addr;
entry->dma_len[1] = buf[1].len;
buf1 = buf[1].addr;
ctrl |= FIELD_PREP(MT_DMA_CTL_SD_LEN1, buf[1].len);
if (buf[1].skip_unmap)
entry->skip_buf1 = true;
}
if (i == nbufs - 1)
ctrl |= MT_DMA_CTL_LAST_SEC0;
else if (i == nbufs - 2)
ctrl |= MT_DMA_CTL_LAST_SEC1;
WRITE_ONCE(desc->buf0, cpu_to_le32(buf0));
WRITE_ONCE(desc->buf1, cpu_to_le32(buf1));
WRITE_ONCE(desc->info, cpu_to_le32(info));
WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
q->head = next;
q->queued++;
}
q->entry[idx].txwi = txwi;
q->entry[idx].skb = skb;
q->entry[idx].wcid = 0xffff;
return idx;
}
static void
mt76_dma_tx_cleanup_idx(struct mt76_dev *dev, struct mt76_queue *q, int idx,
struct mt76_queue_entry *prev_e)
{
struct mt76_queue_entry *e = &q->entry[idx];
if (!e->skip_buf0)
dma_unmap_single(dev->dma_dev, e->dma_addr[0], e->dma_len[0],
DMA_TO_DEVICE);
if (!e->skip_buf1)
dma_unmap_single(dev->dma_dev, e->dma_addr[1], e->dma_len[1],
DMA_TO_DEVICE);
if (e->txwi == DMA_DUMMY_DATA)
e->txwi = NULL;
*prev_e = *e;
memset(e, 0, sizeof(*e));
}
static void
mt76_dma_kick_queue(struct mt76_dev *dev, struct mt76_queue *q)
{
wmb();
Q_WRITE(dev, q, cpu_idx, q->head);
}
static void
mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush)
{
struct mt76_queue_entry entry;
int last;
if (!q || !q->ndesc)
return;
spin_lock_bh(&q->cleanup_lock);
if (flush)
last = -1;
else
last = Q_READ(dev, q, dma_idx);
while (q->queued > 0 && q->tail != last) {
mt76_dma_tx_cleanup_idx(dev, q, q->tail, &entry);
mt76_queue_tx_complete(dev, q, &entry);
if (entry.txwi) {
if (!(dev->drv->drv_flags & MT_DRV_TXWI_NO_FREE))
mt76_put_txwi(dev, entry.txwi);
}
if (!flush && q->tail == last)
last = Q_READ(dev, q, dma_idx);
}
spin_unlock_bh(&q->cleanup_lock);
if (flush) {
spin_lock_bh(&q->lock);
mt76_dma_sync_idx(dev, q);
mt76_dma_kick_queue(dev, q);
spin_unlock_bh(&q->lock);
}
if (!q->queued)
wake_up(&dev->tx_wait);
}
static void *
mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
int *len, u32 *info, bool *more, bool *drop)
{
struct mt76_queue_entry *e = &q->entry[idx];
struct mt76_desc *desc = &q->desc[idx];
void *buf;
if (len) {
u32 ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
*len = FIELD_GET(MT_DMA_CTL_SD_LEN0, ctrl);
*more = !(ctrl & MT_DMA_CTL_LAST_SEC0);
}
if (info)
*info = le32_to_cpu(desc->info);
if (mt76_queue_is_wed_rx(q)) {
u32 buf1 = le32_to_cpu(desc->buf1);
u32 token = FIELD_GET(MT_DMA_CTL_TOKEN, buf1);
struct mt76_txwi_cache *t = mt76_rx_token_release(dev, token);
if (!t)
return NULL;
dma_sync_single_for_cpu(dev->dma_dev, t->dma_addr,
SKB_WITH_OVERHEAD(q->buf_size),
page_pool_get_dma_dir(q->page_pool));
buf = t->ptr;
t->dma_addr = 0;
t->ptr = NULL;
mt76_put_rxwi(dev, t);
if (drop) {
u32 ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
*drop = !!(ctrl & (MT_DMA_CTL_TO_HOST_A |
MT_DMA_CTL_DROP));
*drop |= !!(buf1 & MT_DMA_CTL_WO_DROP);
}
} else {
buf = e->buf;
e->buf = NULL;
dma_sync_single_for_cpu(dev->dma_dev, e->dma_addr[0],
SKB_WITH_OVERHEAD(q->buf_size),
page_pool_get_dma_dir(q->page_pool));
}
return buf;
}
static void *
mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
int *len, u32 *info, bool *more, bool *drop)
{
int idx = q->tail;
*more = false;
if (!q->queued)
return NULL;
if (flush)
q->desc[idx].ctrl |= cpu_to_le32(MT_DMA_CTL_DMA_DONE);
else if (!(q->desc[idx].ctrl & cpu_to_le32(MT_DMA_CTL_DMA_DONE)))
return NULL;
q->tail = (q->tail + 1) % q->ndesc;
q->queued--;
return mt76_dma_get_buf(dev, q, idx, len, info, more, drop);
}
static int
mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, struct mt76_queue *q,
struct sk_buff *skb, u32 tx_info)
{
struct mt76_queue_buf buf = {};
dma_addr_t addr;
if (test_bit(MT76_MCU_RESET, &dev->phy.state))
goto error;
if (q->queued + 1 >= q->ndesc - 1)
goto error;
addr = dma_map_single(dev->dma_dev, skb->data, skb->len,
DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(dev->dma_dev, addr)))
goto error;
buf.addr = addr;
buf.len = skb->len;
spin_lock_bh(&q->lock);
mt76_dma_add_buf(dev, q, &buf, 1, tx_info, skb, NULL);
mt76_dma_kick_queue(dev, q);
spin_unlock_bh(&q->lock);
return 0;
error:
dev_kfree_skb(skb);
return -ENOMEM;
}
static int
mt76_dma_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
enum mt76_txq_id qid, struct sk_buff *skb,
struct mt76_wcid *wcid, struct ieee80211_sta *sta)
{
struct ieee80211_tx_status status = {
.sta = sta,
};
struct mt76_tx_info tx_info = {
.skb = skb,
};
struct ieee80211_hw *hw;
int len, n = 0, ret = -ENOMEM;
struct mt76_txwi_cache *t;
struct sk_buff *iter;
dma_addr_t addr;
u8 *txwi;
if (test_bit(MT76_RESET, &dev->phy.state))
goto free_skb;
t = mt76_get_txwi(dev);
if (!t)
goto free_skb;
txwi = mt76_get_txwi_ptr(dev, t);
skb->prev = skb->next = NULL;
if (dev->drv->drv_flags & MT_DRV_TX_ALIGNED4_SKBS)
mt76_insert_hdr_pad(skb);
len = skb_headlen(skb);
addr = dma_map_single(dev->dma_dev, skb->data, len, DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(dev->dma_dev, addr)))
goto free;
tx_info.buf[n].addr = t->dma_addr;
tx_info.buf[n++].len = dev->drv->txwi_size;
tx_info.buf[n].addr = addr;
tx_info.buf[n++].len = len;
skb_walk_frags(skb, iter) {
if (n == ARRAY_SIZE(tx_info.buf))
goto unmap;
addr = dma_map_single(dev->dma_dev, iter->data, iter->len,
DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(dev->dma_dev, addr)))
goto unmap;
tx_info.buf[n].addr = addr;
tx_info.buf[n++].len = iter->len;
}
tx_info.nbuf = n;
if (q->queued + (tx_info.nbuf + 1) / 2 >= q->ndesc - 1) {
ret = -ENOMEM;
goto unmap;
}
dma_sync_single_for_cpu(dev->dma_dev, t->dma_addr, dev->drv->txwi_size,
DMA_TO_DEVICE);
ret = dev->drv->tx_prepare_skb(dev, txwi, qid, wcid, sta, &tx_info);
dma_sync_single_for_device(dev->dma_dev, t->dma_addr, dev->drv->txwi_size,
DMA_TO_DEVICE);
if (ret < 0)
goto unmap;
return mt76_dma_add_buf(dev, q, tx_info.buf, tx_info.nbuf,
tx_info.info, tx_info.skb, t);
unmap:
for (n--; n > 0; n--)
dma_unmap_single(dev->dma_dev, tx_info.buf[n].addr,
tx_info.buf[n].len, DMA_TO_DEVICE);
free:
#ifdef CONFIG_NL80211_TESTMODE
/* fix tx_done accounting on queue overflow */
if (mt76_is_testmode_skb(dev, skb, &hw)) {
struct mt76_phy *phy = hw->priv;
if (tx_info.skb == phy->test.tx_skb)
phy->test.tx_done--;
}
#endif
mt76_put_txwi(dev, t);
free_skb:
status.skb = tx_info.skb;
hw = mt76_tx_status_get_hw(dev, tx_info.skb);
spin_lock_bh(&dev->rx_lock);
ieee80211_tx_status_ext(hw, &status);
spin_unlock_bh(&dev->rx_lock);
return ret;
}
static int
mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
bool allow_direct)
{
int len = SKB_WITH_OVERHEAD(q->buf_size);
int frames = 0;
if (!q->ndesc)
return 0;
spin_lock_bh(&q->lock);
while (q->queued < q->ndesc - 1) {
enum dma_data_direction dir;
struct mt76_queue_buf qbuf;
dma_addr_t addr;
int offset;
void *buf;
buf = mt76_get_page_pool_buf(q, &offset, q->buf_size);
if (!buf)
break;
addr = page_pool_get_dma_addr(virt_to_head_page(buf)) + offset;
dir = page_pool_get_dma_dir(q->page_pool);
dma_sync_single_for_device(dev->dma_dev, addr, len, dir);
qbuf.addr = addr + q->buf_offset;
qbuf.len = len - q->buf_offset;
qbuf.skip_unmap = false;
if (mt76_dma_add_rx_buf(dev, q, &qbuf, buf) < 0) {
mt76_put_page_pool_buf(buf, allow_direct);
break;
}
frames++;
}
if (frames)
mt76_dma_kick_queue(dev, q);
spin_unlock_bh(&q->lock);
return frames;
}
int mt76_dma_wed_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
{
#ifdef CONFIG_NET_MEDIATEK_SOC_WED
struct mtk_wed_device *wed = &dev->mmio.wed;
int ret, type, ring;
u8 flags;
if (!q || !q->ndesc)
return -EINVAL;
flags = q->flags;
if (!mtk_wed_device_active(wed))
q->flags &= ~MT_QFLAG_WED;
if (!(q->flags & MT_QFLAG_WED))
return 0;
type = FIELD_GET(MT_QFLAG_WED_TYPE, q->flags);
ring = FIELD_GET(MT_QFLAG_WED_RING, q->flags);
switch (type) {
case MT76_WED_Q_TX:
ret = mtk_wed_device_tx_ring_setup(wed, ring, q->regs, reset);
if (!ret)
q->wed_regs = wed->tx_ring[ring].reg_base;
break;
case MT76_WED_Q_TXFREE:
/* WED txfree queue needs ring to be initialized before setup */
q->flags = 0;
mt76_dma_queue_reset(dev, q);
mt76_dma_rx_fill(dev, q, false);
q->flags = flags;
ret = mtk_wed_device_txfree_ring_setup(wed, q->regs);
if (!ret)
q->wed_regs = wed->txfree_ring.reg_base;
break;
case MT76_WED_Q_RX:
ret = mtk_wed_device_rx_ring_setup(wed, ring, q->regs, reset);
if (!ret)
q->wed_regs = wed->rx_ring[ring].reg_base;
break;
default:
ret = -EINVAL;
}
return ret;
#else
return 0;
#endif
}
EXPORT_SYMBOL_GPL(mt76_dma_wed_setup);
static int
mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
int idx, int n_desc, int bufsize,
u32 ring_base)
{
int ret, size;
spin_lock_init(&q->lock);
spin_lock_init(&q->cleanup_lock);
q->regs = dev->mmio.regs + ring_base + idx * MT_RING_SIZE;
q->ndesc = n_desc;
q->buf_size = bufsize;
q->hw_idx = idx;
size = q->ndesc * sizeof(struct mt76_desc);
q->desc = dmam_alloc_coherent(dev->dma_dev, size, &q->desc_dma, GFP_KERNEL);
if (!q->desc)
return -ENOMEM;
size = q->ndesc * sizeof(*q->entry);
q->entry = devm_kzalloc(dev->dev, size, GFP_KERNEL);
if (!q->entry)
return -ENOMEM;
ret = mt76_create_page_pool(dev, q);
if (ret)
return ret;
ret = mt76_dma_wed_setup(dev, q, false);
if (ret)
return ret;
if (q->flags != MT_WED_Q_TXFREE)
mt76_dma_queue_reset(dev, q);
return 0;
}
static void
mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
{
void *buf;
bool more;
if (!q->ndesc)
return;
spin_lock_bh(&q->lock);
do {
buf = mt76_dma_dequeue(dev, q, true, NULL, NULL, &more, NULL);
if (!buf)
break;
mt76_put_page_pool_buf(buf, false);
} while (1);
if (q->rx_head) {
dev_kfree_skb(q->rx_head);
q->rx_head = NULL;
}
spin_unlock_bh(&q->lock);
}
static void
mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
{
struct mt76_queue *q = &dev->q_rx[qid];
int i;
if (!q->ndesc)
return;
for (i = 0; i < q->ndesc; i++)
q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE);
mt76_dma_rx_cleanup(dev, q);
/* reset WED rx queues */
mt76_dma_wed_setup(dev, q, true);
if (q->flags != MT_WED_Q_TXFREE) {
mt76_dma_sync_idx(dev, q);
mt76_dma_rx_fill(dev, q, false);
}
}
static void
mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
int len, bool more, u32 info)
{
struct sk_buff *skb = q->rx_head;
struct skb_shared_info *shinfo = skb_shinfo(skb);
int nr_frags = shinfo->nr_frags;
if (nr_frags < ARRAY_SIZE(shinfo->frags)) {
struct page *page = virt_to_head_page(data);
int offset = data - page_address(page) + q->buf_offset;
skb_add_rx_frag(skb, nr_frags, page, offset, len, q->buf_size);
} else {
mt76_put_page_pool_buf(data, true);
}
if (more)
return;
q->rx_head = NULL;
if (nr_frags < ARRAY_SIZE(shinfo->frags))
dev->drv->rx_skb(dev, q - dev->q_rx, skb, &info);
else
dev_kfree_skb(skb);
}
static int
mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
{
int len, data_len, done = 0, dma_idx;
struct sk_buff *skb;
unsigned char *data;
bool check_ddone = false;
bool more;
if (IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED) &&
q->flags == MT_WED_Q_TXFREE) {
dma_idx = Q_READ(dev, q, dma_idx);
check_ddone = true;
}
while (done < budget) {
bool drop = false;
u32 info;
if (check_ddone) {
if (q->tail == dma_idx)
dma_idx = Q_READ(dev, q, dma_idx);
if (q->tail == dma_idx)
break;
}
data = mt76_dma_dequeue(dev, q, false, &len, &info, &more,
&drop);
if (!data)
break;
if (drop)
goto free_frag;
if (q->rx_head)
data_len = q->buf_size;
else
data_len = SKB_WITH_OVERHEAD(q->buf_size);
if (data_len < len + q->buf_offset) {
dev_kfree_skb(q->rx_head);
q->rx_head = NULL;
goto free_frag;
}
if (q->rx_head) {
mt76_add_fragment(dev, q, data, len, more, info);
continue;
}
if (!more && dev->drv->rx_check &&
!(dev->drv->rx_check(dev, data, len)))
goto free_frag;
skb = napi_build_skb(data, q->buf_size);
if (!skb)
goto free_frag;
skb_reserve(skb, q->buf_offset);
skb_mark_for_recycle(skb);
*(u32 *)skb->cb = info;
__skb_put(skb, len);
done++;
if (more) {
q->rx_head = skb;
continue;
}
dev->drv->rx_skb(dev, q - dev->q_rx, skb, &info);
continue;
free_frag:
mt76_put_page_pool_buf(data, true);
}
mt76_dma_rx_fill(dev, q, true);
return done;
}
int mt76_dma_rx_poll(struct napi_struct *napi, int budget)
{
struct mt76_dev *dev;
int qid, done = 0, cur;
dev = container_of(napi->dev, struct mt76_dev, napi_dev);
qid = napi - dev->napi;
rcu_read_lock();
do {
cur = mt76_dma_rx_process(dev, &dev->q_rx[qid], budget - done);
mt76_rx_poll_complete(dev, qid, napi);
done += cur;
} while (cur && done < budget);
rcu_read_unlock();
if (done < budget && napi_complete(napi))
dev->drv->rx_poll_complete(dev, qid);
return done;
}
EXPORT_SYMBOL_GPL(mt76_dma_rx_poll);
static int
mt76_dma_init(struct mt76_dev *dev,
int (*poll)(struct napi_struct *napi, int budget))
{
int i;
init_dummy_netdev(&dev->napi_dev);
init_dummy_netdev(&dev->tx_napi_dev);
snprintf(dev->napi_dev.name, sizeof(dev->napi_dev.name), "%s",
wiphy_name(dev->hw->wiphy));
dev->napi_dev.threaded = 1;
init_completion(&dev->mmio.wed_reset);
init_completion(&dev->mmio.wed_reset_complete);
mt76_for_each_q_rx(dev, i) {
netif_napi_add(&dev->napi_dev, &dev->napi[i], poll);
mt76_dma_rx_fill(dev, &dev->q_rx[i], false);
napi_enable(&dev->napi[i]);
}
return 0;
}
static const struct mt76_queue_ops mt76_dma_ops = {
.init = mt76_dma_init,
.alloc = mt76_dma_alloc_queue,
.reset_q = mt76_dma_queue_reset,
.tx_queue_skb_raw = mt76_dma_tx_queue_skb_raw,
.tx_queue_skb = mt76_dma_tx_queue_skb,
.tx_cleanup = mt76_dma_tx_cleanup,
.rx_cleanup = mt76_dma_rx_cleanup,
.rx_reset = mt76_dma_rx_reset,
.kick = mt76_dma_kick_queue,
};
void mt76_dma_attach(struct mt76_dev *dev)
{
dev->queue_ops = &mt76_dma_ops;
}
EXPORT_SYMBOL_GPL(mt76_dma_attach);
void mt76_dma_cleanup(struct mt76_dev *dev)
{
int i;
mt76_worker_disable(&dev->tx_worker);
netif_napi_del(&dev->tx_napi);
for (i = 0; i < ARRAY_SIZE(dev->phys); i++) {
struct mt76_phy *phy = dev->phys[i];
int j;
if (!phy)
continue;
for (j = 0; j < ARRAY_SIZE(phy->q_tx); j++)
mt76_dma_tx_cleanup(dev, phy->q_tx[j], true);
}
for (i = 0; i < ARRAY_SIZE(dev->q_mcu); i++)
mt76_dma_tx_cleanup(dev, dev->q_mcu[i], true);
mt76_for_each_q_rx(dev, i) {
struct mt76_queue *q = &dev->q_rx[i];
netif_napi_del(&dev->napi[i]);
mt76_dma_rx_cleanup(dev, q);
page_pool_destroy(q->page_pool);
}
mt76_free_pending_txwi(dev);
mt76_free_pending_rxwi(dev);
if (mtk_wed_device_active(&dev->mmio.wed))
mtk_wed_device_detach(&dev->mmio.wed);
}
EXPORT_SYMBOL_GPL(mt76_dma_cleanup);