162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. 462306a36Sopenharmony_ci * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/interrupt.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "wil6210.h" 1062306a36Sopenharmony_ci#include "trace.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* 1362306a36Sopenharmony_ci * Theory of operation: 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * There is ISR pseudo-cause register, 1662306a36Sopenharmony_ci * dma_rgf->DMA_RGF.PSEUDO_CAUSE.PSEUDO_CAUSE 1762306a36Sopenharmony_ci * Its bits represents OR'ed bits from 3 real ISR registers: 1862306a36Sopenharmony_ci * TX, RX, and MISC. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Registers may be configured to either "write 1 to clear" or 2162306a36Sopenharmony_ci * "clear on read" mode 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * When handling interrupt, one have to mask/unmask interrupts for the 2462306a36Sopenharmony_ci * real ISR registers, or hardware may malfunction. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define WIL6210_IRQ_DISABLE (0xFFFFFFFFUL) 2962306a36Sopenharmony_ci#define WIL6210_IRQ_DISABLE_NO_HALP (0xF7FFFFFFUL) 3062306a36Sopenharmony_ci#define WIL6210_IMC_RX (BIT_DMA_EP_RX_ICR_RX_DONE | \ 3162306a36Sopenharmony_ci BIT_DMA_EP_RX_ICR_RX_HTRSH) 3262306a36Sopenharmony_ci#define WIL6210_IMC_RX_NO_RX_HTRSH (WIL6210_IMC_RX & \ 3362306a36Sopenharmony_ci (~(BIT_DMA_EP_RX_ICR_RX_HTRSH))) 3462306a36Sopenharmony_ci#define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \ 3562306a36Sopenharmony_ci BIT_DMA_EP_TX_ICR_TX_DONE_N(0)) 3662306a36Sopenharmony_ci#define WIL6210_IMC_TX_EDMA BIT_TX_STATUS_IRQ 3762306a36Sopenharmony_ci#define WIL6210_IMC_RX_EDMA BIT_RX_STATUS_IRQ 3862306a36Sopenharmony_ci#define WIL6210_IMC_MISC_NO_HALP (ISR_MISC_FW_READY | \ 3962306a36Sopenharmony_ci ISR_MISC_MBOX_EVT | \ 4062306a36Sopenharmony_ci ISR_MISC_FW_ERROR) 4162306a36Sopenharmony_ci#define WIL6210_IMC_MISC (WIL6210_IMC_MISC_NO_HALP | \ 4262306a36Sopenharmony_ci BIT_DMA_EP_MISC_ICR_HALP) 4362306a36Sopenharmony_ci#define WIL6210_IRQ_PSEUDO_MASK (u32)(~(BIT_DMA_PSEUDO_CAUSE_RX | \ 4462306a36Sopenharmony_ci BIT_DMA_PSEUDO_CAUSE_TX | \ 4562306a36Sopenharmony_ci BIT_DMA_PSEUDO_CAUSE_MISC)) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#if defined(CONFIG_WIL6210_ISR_COR) 4862306a36Sopenharmony_ci/* configure to Clear-On-Read mode */ 4962306a36Sopenharmony_ci#define WIL_ICR_ICC_VALUE (0xFFFFFFFFUL) 5062306a36Sopenharmony_ci#define WIL_ICR_ICC_MISC_VALUE (0xF7FFFFFFUL) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic inline void wil_icr_clear(u32 x, void __iomem *addr) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci#else /* defined(CONFIG_WIL6210_ISR_COR) */ 5662306a36Sopenharmony_ci/* configure to Write-1-to-Clear mode */ 5762306a36Sopenharmony_ci#define WIL_ICR_ICC_VALUE (0UL) 5862306a36Sopenharmony_ci#define WIL_ICR_ICC_MISC_VALUE (0UL) 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic inline void wil_icr_clear(u32 x, void __iomem *addr) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci writel(x, addr); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci#endif /* defined(CONFIG_WIL6210_ISR_COR) */ 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic inline u32 wil_ioread32_and_clear(void __iomem *addr) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci u32 x = readl(addr); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci wil_icr_clear(x, addr); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return x; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic void wil6210_mask_irq_tx(struct wil6210_priv *wil) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci wil_w(wil, RGF_DMA_EP_TX_ICR + offsetof(struct RGF_ICR, IMS), 7862306a36Sopenharmony_ci WIL6210_IRQ_DISABLE); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void wil6210_mask_irq_tx_edma(struct wil6210_priv *wil) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, IMS), 8462306a36Sopenharmony_ci WIL6210_IRQ_DISABLE); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void wil6210_mask_irq_rx(struct wil6210_priv *wil) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci wil_w(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, IMS), 9062306a36Sopenharmony_ci WIL6210_IRQ_DISABLE); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void wil6210_mask_irq_rx_edma(struct wil6210_priv *wil) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci wil_w(wil, RGF_INT_GEN_RX_ICR + offsetof(struct RGF_ICR, IMS), 9662306a36Sopenharmony_ci WIL6210_IRQ_DISABLE); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic void wil6210_mask_irq_misc(struct wil6210_priv *wil, bool mask_halp) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci wil_dbg_irq(wil, "mask_irq_misc: mask_halp(%s)\n", 10262306a36Sopenharmony_ci mask_halp ? "true" : "false"); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, IMS), 10562306a36Sopenharmony_ci mask_halp ? WIL6210_IRQ_DISABLE : WIL6210_IRQ_DISABLE_NO_HALP); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_civoid wil6210_mask_halp(struct wil6210_priv *wil) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci wil_dbg_irq(wil, "mask_halp\n"); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, IMS), 11362306a36Sopenharmony_ci BIT_DMA_EP_MISC_ICR_HALP); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void wil6210_mask_irq_pseudo(struct wil6210_priv *wil) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci wil_dbg_irq(wil, "mask_irq_pseudo\n"); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci wil_w(wil, RGF_DMA_PSEUDO_CAUSE_MASK_SW, WIL6210_IRQ_DISABLE); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci clear_bit(wil_status_irqen, wil->status); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_civoid wil6210_unmask_irq_tx(struct wil6210_priv *wil) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci wil_w(wil, RGF_DMA_EP_TX_ICR + offsetof(struct RGF_ICR, IMC), 12862306a36Sopenharmony_ci WIL6210_IMC_TX); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_civoid wil6210_unmask_irq_tx_edma(struct wil6210_priv *wil) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, IMC), 13462306a36Sopenharmony_ci WIL6210_IMC_TX_EDMA); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_civoid wil6210_unmask_irq_rx(struct wil6210_priv *wil) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci bool unmask_rx_htrsh = atomic_read(&wil->connected_vifs) > 0; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci wil_w(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, IMC), 14262306a36Sopenharmony_ci unmask_rx_htrsh ? WIL6210_IMC_RX : WIL6210_IMC_RX_NO_RX_HTRSH); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_civoid wil6210_unmask_irq_rx_edma(struct wil6210_priv *wil) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci wil_w(wil, RGF_INT_GEN_RX_ICR + offsetof(struct RGF_ICR, IMC), 14862306a36Sopenharmony_ci WIL6210_IMC_RX_EDMA); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic void wil6210_unmask_irq_misc(struct wil6210_priv *wil, bool unmask_halp) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci wil_dbg_irq(wil, "unmask_irq_misc: unmask_halp(%s)\n", 15462306a36Sopenharmony_ci unmask_halp ? "true" : "false"); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, IMC), 15762306a36Sopenharmony_ci unmask_halp ? WIL6210_IMC_MISC : WIL6210_IMC_MISC_NO_HALP); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic void wil6210_unmask_halp(struct wil6210_priv *wil) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci wil_dbg_irq(wil, "unmask_halp\n"); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, IMC), 16562306a36Sopenharmony_ci BIT_DMA_EP_MISC_ICR_HALP); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci wil_dbg_irq(wil, "unmask_irq_pseudo\n"); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci set_bit(wil_status_irqen, wil->status); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci wil_w(wil, RGF_DMA_PSEUDO_CAUSE_MASK_SW, WIL6210_IRQ_PSEUDO_MASK); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_civoid wil_mask_irq(struct wil6210_priv *wil) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci wil_dbg_irq(wil, "mask_irq\n"); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci wil6210_mask_irq_tx(wil); 18262306a36Sopenharmony_ci wil6210_mask_irq_tx_edma(wil); 18362306a36Sopenharmony_ci wil6210_mask_irq_rx(wil); 18462306a36Sopenharmony_ci wil6210_mask_irq_rx_edma(wil); 18562306a36Sopenharmony_ci wil6210_mask_irq_misc(wil, true); 18662306a36Sopenharmony_ci wil6210_mask_irq_pseudo(wil); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_civoid wil_unmask_irq(struct wil6210_priv *wil) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci wil_dbg_irq(wil, "unmask_irq\n"); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci wil_w(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, ICC), 19462306a36Sopenharmony_ci WIL_ICR_ICC_VALUE); 19562306a36Sopenharmony_ci wil_w(wil, RGF_DMA_EP_TX_ICR + offsetof(struct RGF_ICR, ICC), 19662306a36Sopenharmony_ci WIL_ICR_ICC_VALUE); 19762306a36Sopenharmony_ci wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, ICC), 19862306a36Sopenharmony_ci WIL_ICR_ICC_MISC_VALUE); 19962306a36Sopenharmony_ci wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, ICC), 20062306a36Sopenharmony_ci WIL_ICR_ICC_VALUE); 20162306a36Sopenharmony_ci wil_w(wil, RGF_INT_GEN_RX_ICR + offsetof(struct RGF_ICR, ICC), 20262306a36Sopenharmony_ci WIL_ICR_ICC_VALUE); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci wil6210_unmask_irq_pseudo(wil); 20562306a36Sopenharmony_ci if (wil->use_enhanced_dma_hw) { 20662306a36Sopenharmony_ci wil6210_unmask_irq_tx_edma(wil); 20762306a36Sopenharmony_ci wil6210_unmask_irq_rx_edma(wil); 20862306a36Sopenharmony_ci } else { 20962306a36Sopenharmony_ci wil6210_unmask_irq_tx(wil); 21062306a36Sopenharmony_ci wil6210_unmask_irq_rx(wil); 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci wil6210_unmask_irq_misc(wil, true); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_civoid wil_configure_interrupt_moderation_edma(struct wil6210_priv *wil) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci u32 moderation; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci wil_s(wil, RGF_INT_GEN_IDLE_TIME_LIMIT, WIL_EDMA_IDLE_TIME_LIMIT_USEC); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci wil_s(wil, RGF_INT_GEN_TIME_UNIT_LIMIT, WIL_EDMA_TIME_UNIT_CLK_CYCLES); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* Update RX and TX moderation */ 22462306a36Sopenharmony_ci moderation = wil->rx_max_burst_duration | 22562306a36Sopenharmony_ci (WIL_EDMA_AGG_WATERMARK << WIL_EDMA_AGG_WATERMARK_POS); 22662306a36Sopenharmony_ci wil_w(wil, RGF_INT_CTRL_INT_GEN_CFG_0, moderation); 22762306a36Sopenharmony_ci wil_w(wil, RGF_INT_CTRL_INT_GEN_CFG_1, moderation); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Treat special events as regular 23062306a36Sopenharmony_ci * (set bit 0 to 0x1 and clear bits 1-8) 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_ci wil_c(wil, RGF_INT_COUNT_ON_SPECIAL_EVT, 0x1FE); 23362306a36Sopenharmony_ci wil_s(wil, RGF_INT_COUNT_ON_SPECIAL_EVT, 0x1); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_civoid wil_configure_interrupt_moderation(struct wil6210_priv *wil) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci wil_dbg_irq(wil, "configure_interrupt_moderation\n"); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* disable interrupt moderation for monitor 24362306a36Sopenharmony_ci * to get better timestamp precision 24462306a36Sopenharmony_ci */ 24562306a36Sopenharmony_ci if (wdev->iftype == NL80211_IFTYPE_MONITOR) 24662306a36Sopenharmony_ci return; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* Disable and clear tx counter before (re)configuration */ 24962306a36Sopenharmony_ci wil_w(wil, RGF_DMA_ITR_TX_CNT_CTL, BIT_DMA_ITR_TX_CNT_CTL_CLR); 25062306a36Sopenharmony_ci wil_w(wil, RGF_DMA_ITR_TX_CNT_TRSH, wil->tx_max_burst_duration); 25162306a36Sopenharmony_ci wil_info(wil, "set ITR_TX_CNT_TRSH = %d usec\n", 25262306a36Sopenharmony_ci wil->tx_max_burst_duration); 25362306a36Sopenharmony_ci /* Configure TX max burst duration timer to use usec units */ 25462306a36Sopenharmony_ci wil_w(wil, RGF_DMA_ITR_TX_CNT_CTL, 25562306a36Sopenharmony_ci BIT_DMA_ITR_TX_CNT_CTL_EN | BIT_DMA_ITR_TX_CNT_CTL_EXT_TIC_SEL); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* Disable and clear tx idle counter before (re)configuration */ 25862306a36Sopenharmony_ci wil_w(wil, RGF_DMA_ITR_TX_IDL_CNT_CTL, BIT_DMA_ITR_TX_IDL_CNT_CTL_CLR); 25962306a36Sopenharmony_ci wil_w(wil, RGF_DMA_ITR_TX_IDL_CNT_TRSH, wil->tx_interframe_timeout); 26062306a36Sopenharmony_ci wil_info(wil, "set ITR_TX_IDL_CNT_TRSH = %d usec\n", 26162306a36Sopenharmony_ci wil->tx_interframe_timeout); 26262306a36Sopenharmony_ci /* Configure TX max burst duration timer to use usec units */ 26362306a36Sopenharmony_ci wil_w(wil, RGF_DMA_ITR_TX_IDL_CNT_CTL, BIT_DMA_ITR_TX_IDL_CNT_CTL_EN | 26462306a36Sopenharmony_ci BIT_DMA_ITR_TX_IDL_CNT_CTL_EXT_TIC_SEL); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* Disable and clear rx counter before (re)configuration */ 26762306a36Sopenharmony_ci wil_w(wil, RGF_DMA_ITR_RX_CNT_CTL, BIT_DMA_ITR_RX_CNT_CTL_CLR); 26862306a36Sopenharmony_ci wil_w(wil, RGF_DMA_ITR_RX_CNT_TRSH, wil->rx_max_burst_duration); 26962306a36Sopenharmony_ci wil_info(wil, "set ITR_RX_CNT_TRSH = %d usec\n", 27062306a36Sopenharmony_ci wil->rx_max_burst_duration); 27162306a36Sopenharmony_ci /* Configure TX max burst duration timer to use usec units */ 27262306a36Sopenharmony_ci wil_w(wil, RGF_DMA_ITR_RX_CNT_CTL, 27362306a36Sopenharmony_ci BIT_DMA_ITR_RX_CNT_CTL_EN | BIT_DMA_ITR_RX_CNT_CTL_EXT_TIC_SEL); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* Disable and clear rx idle counter before (re)configuration */ 27662306a36Sopenharmony_ci wil_w(wil, RGF_DMA_ITR_RX_IDL_CNT_CTL, BIT_DMA_ITR_RX_IDL_CNT_CTL_CLR); 27762306a36Sopenharmony_ci wil_w(wil, RGF_DMA_ITR_RX_IDL_CNT_TRSH, wil->rx_interframe_timeout); 27862306a36Sopenharmony_ci wil_info(wil, "set ITR_RX_IDL_CNT_TRSH = %d usec\n", 27962306a36Sopenharmony_ci wil->rx_interframe_timeout); 28062306a36Sopenharmony_ci /* Configure TX max burst duration timer to use usec units */ 28162306a36Sopenharmony_ci wil_w(wil, RGF_DMA_ITR_RX_IDL_CNT_CTL, BIT_DMA_ITR_RX_IDL_CNT_CTL_EN | 28262306a36Sopenharmony_ci BIT_DMA_ITR_RX_IDL_CNT_CTL_EXT_TIC_SEL); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic irqreturn_t wil6210_irq_rx(int irq, void *cookie) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct wil6210_priv *wil = cookie; 28862306a36Sopenharmony_ci u32 isr; 28962306a36Sopenharmony_ci bool need_unmask = true; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci wil6210_mask_irq_rx(wil); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci isr = wil_ioread32_and_clear(wil->csr + 29462306a36Sopenharmony_ci HOSTADDR(RGF_DMA_EP_RX_ICR) + 29562306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICR)); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci trace_wil6210_irq_rx(isr); 29862306a36Sopenharmony_ci wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (unlikely(!isr)) { 30162306a36Sopenharmony_ci wil_err_ratelimited(wil, "spurious IRQ: RX\n"); 30262306a36Sopenharmony_ci wil6210_unmask_irq_rx(wil); 30362306a36Sopenharmony_ci return IRQ_NONE; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* RX_DONE and RX_HTRSH interrupts are the same if interrupt 30762306a36Sopenharmony_ci * moderation is not used. Interrupt moderation may cause RX 30862306a36Sopenharmony_ci * buffer overflow while RX_DONE is delayed. The required 30962306a36Sopenharmony_ci * action is always the same - should empty the accumulated 31062306a36Sopenharmony_ci * packets from the RX ring. 31162306a36Sopenharmony_ci */ 31262306a36Sopenharmony_ci if (likely(isr & (BIT_DMA_EP_RX_ICR_RX_DONE | 31362306a36Sopenharmony_ci BIT_DMA_EP_RX_ICR_RX_HTRSH))) { 31462306a36Sopenharmony_ci wil_dbg_irq(wil, "RX done / RX_HTRSH received, ISR (0x%x)\n", 31562306a36Sopenharmony_ci isr); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci isr &= ~(BIT_DMA_EP_RX_ICR_RX_DONE | 31862306a36Sopenharmony_ci BIT_DMA_EP_RX_ICR_RX_HTRSH); 31962306a36Sopenharmony_ci if (likely(test_bit(wil_status_fwready, wil->status))) { 32062306a36Sopenharmony_ci if (likely(test_bit(wil_status_napi_en, wil->status))) { 32162306a36Sopenharmony_ci wil_dbg_txrx(wil, "NAPI(Rx) schedule\n"); 32262306a36Sopenharmony_ci need_unmask = false; 32362306a36Sopenharmony_ci napi_schedule(&wil->napi_rx); 32462306a36Sopenharmony_ci } else { 32562306a36Sopenharmony_ci wil_err_ratelimited( 32662306a36Sopenharmony_ci wil, 32762306a36Sopenharmony_ci "Got Rx interrupt while stopping interface\n"); 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci } else { 33062306a36Sopenharmony_ci wil_err_ratelimited(wil, "Got Rx interrupt while in reset\n"); 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (unlikely(isr)) 33562306a36Sopenharmony_ci wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Rx IRQ will be enabled when NAPI processing finished */ 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci atomic_inc(&wil->isr_count_rx); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (unlikely(need_unmask)) 34262306a36Sopenharmony_ci wil6210_unmask_irq_rx(wil); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci return IRQ_HANDLED; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic irqreturn_t wil6210_irq_rx_edma(int irq, void *cookie) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct wil6210_priv *wil = cookie; 35062306a36Sopenharmony_ci u32 isr; 35162306a36Sopenharmony_ci bool need_unmask = true; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci wil6210_mask_irq_rx_edma(wil); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci isr = wil_ioread32_and_clear(wil->csr + 35662306a36Sopenharmony_ci HOSTADDR(RGF_INT_GEN_RX_ICR) + 35762306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICR)); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci trace_wil6210_irq_rx(isr); 36062306a36Sopenharmony_ci wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (unlikely(!isr)) { 36362306a36Sopenharmony_ci wil_err(wil, "spurious IRQ: RX\n"); 36462306a36Sopenharmony_ci wil6210_unmask_irq_rx_edma(wil); 36562306a36Sopenharmony_ci return IRQ_NONE; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (likely(isr & BIT_RX_STATUS_IRQ)) { 36962306a36Sopenharmony_ci wil_dbg_irq(wil, "RX status ring\n"); 37062306a36Sopenharmony_ci isr &= ~BIT_RX_STATUS_IRQ; 37162306a36Sopenharmony_ci if (likely(test_bit(wil_status_fwready, wil->status))) { 37262306a36Sopenharmony_ci if (likely(test_bit(wil_status_napi_en, wil->status))) { 37362306a36Sopenharmony_ci wil_dbg_txrx(wil, "NAPI(Rx) schedule\n"); 37462306a36Sopenharmony_ci need_unmask = false; 37562306a36Sopenharmony_ci napi_schedule(&wil->napi_rx); 37662306a36Sopenharmony_ci } else { 37762306a36Sopenharmony_ci wil_err(wil, 37862306a36Sopenharmony_ci "Got Rx interrupt while stopping interface\n"); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci } else { 38162306a36Sopenharmony_ci wil_err(wil, "Got Rx interrupt while in reset\n"); 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (unlikely(isr)) 38662306a36Sopenharmony_ci wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* Rx IRQ will be enabled when NAPI processing finished */ 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci atomic_inc(&wil->isr_count_rx); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (unlikely(need_unmask)) 39362306a36Sopenharmony_ci wil6210_unmask_irq_rx_edma(wil); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return IRQ_HANDLED; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic irqreturn_t wil6210_irq_tx_edma(int irq, void *cookie) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci struct wil6210_priv *wil = cookie; 40162306a36Sopenharmony_ci u32 isr; 40262306a36Sopenharmony_ci bool need_unmask = true; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci wil6210_mask_irq_tx_edma(wil); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci isr = wil_ioread32_and_clear(wil->csr + 40762306a36Sopenharmony_ci HOSTADDR(RGF_INT_GEN_TX_ICR) + 40862306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICR)); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci trace_wil6210_irq_tx(isr); 41162306a36Sopenharmony_ci wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (unlikely(!isr)) { 41462306a36Sopenharmony_ci wil_err(wil, "spurious IRQ: TX\n"); 41562306a36Sopenharmony_ci wil6210_unmask_irq_tx_edma(wil); 41662306a36Sopenharmony_ci return IRQ_NONE; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (likely(isr & BIT_TX_STATUS_IRQ)) { 42062306a36Sopenharmony_ci wil_dbg_irq(wil, "TX status ring\n"); 42162306a36Sopenharmony_ci isr &= ~BIT_TX_STATUS_IRQ; 42262306a36Sopenharmony_ci if (likely(test_bit(wil_status_fwready, wil->status))) { 42362306a36Sopenharmony_ci wil_dbg_txrx(wil, "NAPI(Tx) schedule\n"); 42462306a36Sopenharmony_ci need_unmask = false; 42562306a36Sopenharmony_ci napi_schedule(&wil->napi_tx); 42662306a36Sopenharmony_ci } else { 42762306a36Sopenharmony_ci wil_err(wil, "Got Tx status ring IRQ while in reset\n"); 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (unlikely(isr)) 43262306a36Sopenharmony_ci wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* Tx IRQ will be enabled when NAPI processing finished */ 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci atomic_inc(&wil->isr_count_tx); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (unlikely(need_unmask)) 43962306a36Sopenharmony_ci wil6210_unmask_irq_tx_edma(wil); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return IRQ_HANDLED; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic irqreturn_t wil6210_irq_tx(int irq, void *cookie) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct wil6210_priv *wil = cookie; 44762306a36Sopenharmony_ci u32 isr; 44862306a36Sopenharmony_ci bool need_unmask = true; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci wil6210_mask_irq_tx(wil); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci isr = wil_ioread32_and_clear(wil->csr + 45362306a36Sopenharmony_ci HOSTADDR(RGF_DMA_EP_TX_ICR) + 45462306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICR)); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci trace_wil6210_irq_tx(isr); 45762306a36Sopenharmony_ci wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (unlikely(!isr)) { 46062306a36Sopenharmony_ci wil_err_ratelimited(wil, "spurious IRQ: TX\n"); 46162306a36Sopenharmony_ci wil6210_unmask_irq_tx(wil); 46262306a36Sopenharmony_ci return IRQ_NONE; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (likely(isr & BIT_DMA_EP_TX_ICR_TX_DONE)) { 46662306a36Sopenharmony_ci wil_dbg_irq(wil, "TX done\n"); 46762306a36Sopenharmony_ci isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; 46862306a36Sopenharmony_ci /* clear also all VRING interrupts */ 46962306a36Sopenharmony_ci isr &= ~(BIT(25) - 1UL); 47062306a36Sopenharmony_ci if (likely(test_bit(wil_status_fwready, wil->status))) { 47162306a36Sopenharmony_ci wil_dbg_txrx(wil, "NAPI(Tx) schedule\n"); 47262306a36Sopenharmony_ci need_unmask = false; 47362306a36Sopenharmony_ci napi_schedule(&wil->napi_tx); 47462306a36Sopenharmony_ci } else { 47562306a36Sopenharmony_ci wil_err_ratelimited(wil, "Got Tx interrupt while in reset\n"); 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (unlikely(isr)) 48062306a36Sopenharmony_ci wil_err_ratelimited(wil, "un-handled TX ISR bits 0x%08x\n", 48162306a36Sopenharmony_ci isr); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* Tx IRQ will be enabled when NAPI processing finished */ 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci atomic_inc(&wil->isr_count_tx); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (unlikely(need_unmask)) 48862306a36Sopenharmony_ci wil6210_unmask_irq_tx(wil); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci return IRQ_HANDLED; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic void wil_notify_fw_error(struct wil6210_priv *wil) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci struct device *dev = &wil->main_ndev->dev; 49662306a36Sopenharmony_ci char *envp[3] = { 49762306a36Sopenharmony_ci [0] = "SOURCE=wil6210", 49862306a36Sopenharmony_ci [1] = "EVENT=FW_ERROR", 49962306a36Sopenharmony_ci [2] = NULL, 50062306a36Sopenharmony_ci }; 50162306a36Sopenharmony_ci wil_err(wil, "Notify about firmware error\n"); 50262306a36Sopenharmony_ci kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic void wil_cache_mbox_regs(struct wil6210_priv *wil) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci /* make shadow copy of registers that should not change on run time */ 50862306a36Sopenharmony_ci wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, 50962306a36Sopenharmony_ci sizeof(struct wil6210_mbox_ctl)); 51062306a36Sopenharmony_ci wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx); 51162306a36Sopenharmony_ci wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic bool wil_validate_mbox_regs(struct wil6210_priv *wil) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci size_t min_size = sizeof(struct wil6210_mbox_hdr) + 51762306a36Sopenharmony_ci sizeof(struct wmi_cmd_hdr); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (wil->mbox_ctl.rx.entry_size < min_size) { 52062306a36Sopenharmony_ci wil_err(wil, "rx mbox entry too small (%d)\n", 52162306a36Sopenharmony_ci wil->mbox_ctl.rx.entry_size); 52262306a36Sopenharmony_ci return false; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci if (wil->mbox_ctl.tx.entry_size < min_size) { 52562306a36Sopenharmony_ci wil_err(wil, "tx mbox entry too small (%d)\n", 52662306a36Sopenharmony_ci wil->mbox_ctl.tx.entry_size); 52762306a36Sopenharmony_ci return false; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci return true; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic irqreturn_t wil6210_irq_misc(int irq, void *cookie) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct wil6210_priv *wil = cookie; 53662306a36Sopenharmony_ci u32 isr; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci wil6210_mask_irq_misc(wil, false); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci isr = wil_ioread32_and_clear(wil->csr + 54162306a36Sopenharmony_ci HOSTADDR(RGF_DMA_EP_MISC_ICR) + 54262306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICR)); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci trace_wil6210_irq_misc(isr); 54562306a36Sopenharmony_ci wil_dbg_irq(wil, "ISR MISC 0x%08x\n", isr); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (!isr) { 54862306a36Sopenharmony_ci wil_err(wil, "spurious IRQ: MISC\n"); 54962306a36Sopenharmony_ci wil6210_unmask_irq_misc(wil, false); 55062306a36Sopenharmony_ci return IRQ_NONE; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (isr & ISR_MISC_FW_ERROR) { 55462306a36Sopenharmony_ci u32 fw_assert_code = wil_r(wil, wil->rgf_fw_assert_code_addr); 55562306a36Sopenharmony_ci u32 ucode_assert_code = 55662306a36Sopenharmony_ci wil_r(wil, wil->rgf_ucode_assert_code_addr); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci wil_err(wil, 55962306a36Sopenharmony_ci "Firmware error detected, assert codes FW 0x%08x, UCODE 0x%08x\n", 56062306a36Sopenharmony_ci fw_assert_code, ucode_assert_code); 56162306a36Sopenharmony_ci clear_bit(wil_status_fwready, wil->status); 56262306a36Sopenharmony_ci /* 56362306a36Sopenharmony_ci * do not clear @isr here - we do 2-nd part in thread 56462306a36Sopenharmony_ci * there, user space get notified, and it should be done 56562306a36Sopenharmony_ci * in non-atomic context 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (isr & ISR_MISC_FW_READY) { 57062306a36Sopenharmony_ci wil_dbg_irq(wil, "IRQ: FW ready\n"); 57162306a36Sopenharmony_ci wil_cache_mbox_regs(wil); 57262306a36Sopenharmony_ci if (wil_validate_mbox_regs(wil)) 57362306a36Sopenharmony_ci set_bit(wil_status_mbox_ready, wil->status); 57462306a36Sopenharmony_ci /** 57562306a36Sopenharmony_ci * Actual FW ready indicated by the 57662306a36Sopenharmony_ci * WMI_FW_READY_EVENTID 57762306a36Sopenharmony_ci */ 57862306a36Sopenharmony_ci isr &= ~ISR_MISC_FW_READY; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (isr & BIT_DMA_EP_MISC_ICR_HALP) { 58262306a36Sopenharmony_ci isr &= ~BIT_DMA_EP_MISC_ICR_HALP; 58362306a36Sopenharmony_ci if (wil->halp.handle_icr) { 58462306a36Sopenharmony_ci /* no need to handle HALP ICRs until next vote */ 58562306a36Sopenharmony_ci wil->halp.handle_icr = false; 58662306a36Sopenharmony_ci wil_dbg_irq(wil, "irq_misc: HALP IRQ invoked\n"); 58762306a36Sopenharmony_ci wil6210_mask_irq_misc(wil, true); 58862306a36Sopenharmony_ci complete(&wil->halp.comp); 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci wil->isr_misc = isr; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (isr) { 59562306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 59662306a36Sopenharmony_ci } else { 59762306a36Sopenharmony_ci wil6210_unmask_irq_misc(wil, false); 59862306a36Sopenharmony_ci return IRQ_HANDLED; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci struct wil6210_priv *wil = cookie; 60562306a36Sopenharmony_ci u32 isr = wil->isr_misc; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci trace_wil6210_irq_misc_thread(isr); 60862306a36Sopenharmony_ci wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (isr & ISR_MISC_FW_ERROR) { 61162306a36Sopenharmony_ci wil->recovery_state = fw_recovery_pending; 61262306a36Sopenharmony_ci wil_fw_core_dump(wil); 61362306a36Sopenharmony_ci wil_notify_fw_error(wil); 61462306a36Sopenharmony_ci isr &= ~ISR_MISC_FW_ERROR; 61562306a36Sopenharmony_ci if (wil->platform_ops.notify) { 61662306a36Sopenharmony_ci wil_err(wil, "notify platform driver about FW crash"); 61762306a36Sopenharmony_ci wil->platform_ops.notify(wil->platform_handle, 61862306a36Sopenharmony_ci WIL_PLATFORM_EVT_FW_CRASH); 61962306a36Sopenharmony_ci } else { 62062306a36Sopenharmony_ci wil_fw_error_recovery(wil); 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci if (isr & ISR_MISC_MBOX_EVT) { 62462306a36Sopenharmony_ci wil_dbg_irq(wil, "MBOX event\n"); 62562306a36Sopenharmony_ci wmi_recv_cmd(wil); 62662306a36Sopenharmony_ci isr &= ~ISR_MISC_MBOX_EVT; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if (isr) 63062306a36Sopenharmony_ci wil_dbg_irq(wil, "un-handled MISC ISR bits 0x%08x\n", isr); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci wil->isr_misc = 0; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci wil6210_unmask_irq_misc(wil, false); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* in non-triple MSI case, this is done inside wil6210_thread_irq 63762306a36Sopenharmony_ci * because it has to be done after unmasking the pseudo. 63862306a36Sopenharmony_ci */ 63962306a36Sopenharmony_ci if (wil->n_msi == 3 && wil->suspend_resp_rcvd) { 64062306a36Sopenharmony_ci wil_dbg_irq(wil, "set suspend_resp_comp to true\n"); 64162306a36Sopenharmony_ci wil->suspend_resp_comp = true; 64262306a36Sopenharmony_ci wake_up_interruptible(&wil->wq); 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci return IRQ_HANDLED; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci/* thread IRQ handler */ 64962306a36Sopenharmony_cistatic irqreturn_t wil6210_thread_irq(int irq, void *cookie) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci struct wil6210_priv *wil = cookie; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci wil_dbg_irq(wil, "Thread IRQ\n"); 65462306a36Sopenharmony_ci /* Discover real IRQ cause */ 65562306a36Sopenharmony_ci if (wil->isr_misc) 65662306a36Sopenharmony_ci wil6210_irq_misc_thread(irq, cookie); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci wil6210_unmask_irq_pseudo(wil); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (wil->suspend_resp_rcvd) { 66162306a36Sopenharmony_ci wil_dbg_irq(wil, "set suspend_resp_comp to true\n"); 66262306a36Sopenharmony_ci wil->suspend_resp_comp = true; 66362306a36Sopenharmony_ci wake_up_interruptible(&wil->wq); 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci return IRQ_HANDLED; 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci/* DEBUG 67062306a36Sopenharmony_ci * There is subtle bug in hardware that causes IRQ to raise when it should be 67162306a36Sopenharmony_ci * masked. It is quite rare and hard to debug. 67262306a36Sopenharmony_ci * 67362306a36Sopenharmony_ci * Catch irq issue if it happens and print all I can. 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_cistatic int wil6210_debug_irq_mask(struct wil6210_priv *wil, u32 pseudo_cause) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci u32 icm_rx, icr_rx, imv_rx; 67862306a36Sopenharmony_ci u32 icm_tx, icr_tx, imv_tx; 67962306a36Sopenharmony_ci u32 icm_misc, icr_misc, imv_misc; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (!test_bit(wil_status_irqen, wil->status)) { 68262306a36Sopenharmony_ci if (wil->use_enhanced_dma_hw) { 68362306a36Sopenharmony_ci icm_rx = wil_ioread32_and_clear(wil->csr + 68462306a36Sopenharmony_ci HOSTADDR(RGF_INT_GEN_RX_ICR) + 68562306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICM)); 68662306a36Sopenharmony_ci icr_rx = wil_ioread32_and_clear(wil->csr + 68762306a36Sopenharmony_ci HOSTADDR(RGF_INT_GEN_RX_ICR) + 68862306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICR)); 68962306a36Sopenharmony_ci imv_rx = wil_r(wil, RGF_INT_GEN_RX_ICR + 69062306a36Sopenharmony_ci offsetof(struct RGF_ICR, IMV)); 69162306a36Sopenharmony_ci icm_tx = wil_ioread32_and_clear(wil->csr + 69262306a36Sopenharmony_ci HOSTADDR(RGF_INT_GEN_TX_ICR) + 69362306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICM)); 69462306a36Sopenharmony_ci icr_tx = wil_ioread32_and_clear(wil->csr + 69562306a36Sopenharmony_ci HOSTADDR(RGF_INT_GEN_TX_ICR) + 69662306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICR)); 69762306a36Sopenharmony_ci imv_tx = wil_r(wil, RGF_INT_GEN_TX_ICR + 69862306a36Sopenharmony_ci offsetof(struct RGF_ICR, IMV)); 69962306a36Sopenharmony_ci } else { 70062306a36Sopenharmony_ci icm_rx = wil_ioread32_and_clear(wil->csr + 70162306a36Sopenharmony_ci HOSTADDR(RGF_DMA_EP_RX_ICR) + 70262306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICM)); 70362306a36Sopenharmony_ci icr_rx = wil_ioread32_and_clear(wil->csr + 70462306a36Sopenharmony_ci HOSTADDR(RGF_DMA_EP_RX_ICR) + 70562306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICR)); 70662306a36Sopenharmony_ci imv_rx = wil_r(wil, RGF_DMA_EP_RX_ICR + 70762306a36Sopenharmony_ci offsetof(struct RGF_ICR, IMV)); 70862306a36Sopenharmony_ci icm_tx = wil_ioread32_and_clear(wil->csr + 70962306a36Sopenharmony_ci HOSTADDR(RGF_DMA_EP_TX_ICR) + 71062306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICM)); 71162306a36Sopenharmony_ci icr_tx = wil_ioread32_and_clear(wil->csr + 71262306a36Sopenharmony_ci HOSTADDR(RGF_DMA_EP_TX_ICR) + 71362306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICR)); 71462306a36Sopenharmony_ci imv_tx = wil_r(wil, RGF_DMA_EP_TX_ICR + 71562306a36Sopenharmony_ci offsetof(struct RGF_ICR, IMV)); 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci icm_misc = wil_ioread32_and_clear(wil->csr + 71862306a36Sopenharmony_ci HOSTADDR(RGF_DMA_EP_MISC_ICR) + 71962306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICM)); 72062306a36Sopenharmony_ci icr_misc = wil_ioread32_and_clear(wil->csr + 72162306a36Sopenharmony_ci HOSTADDR(RGF_DMA_EP_MISC_ICR) + 72262306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICR)); 72362306a36Sopenharmony_ci imv_misc = wil_r(wil, RGF_DMA_EP_MISC_ICR + 72462306a36Sopenharmony_ci offsetof(struct RGF_ICR, IMV)); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* HALP interrupt can be unmasked when misc interrupts are 72762306a36Sopenharmony_ci * masked 72862306a36Sopenharmony_ci */ 72962306a36Sopenharmony_ci if (icr_misc & BIT_DMA_EP_MISC_ICR_HALP) 73062306a36Sopenharmony_ci return 0; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci wil_err(wil, "IRQ when it should be masked: pseudo 0x%08x\n" 73362306a36Sopenharmony_ci "Rx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" 73462306a36Sopenharmony_ci "Tx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" 73562306a36Sopenharmony_ci "Misc icm:icr:imv 0x%08x 0x%08x 0x%08x\n", 73662306a36Sopenharmony_ci pseudo_cause, 73762306a36Sopenharmony_ci icm_rx, icr_rx, imv_rx, 73862306a36Sopenharmony_ci icm_tx, icr_tx, imv_tx, 73962306a36Sopenharmony_ci icm_misc, icr_misc, imv_misc); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci return -EINVAL; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci return 0; 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_cistatic irqreturn_t wil6210_hardirq(int irq, void *cookie) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci irqreturn_t rc = IRQ_HANDLED; 75062306a36Sopenharmony_ci struct wil6210_priv *wil = cookie; 75162306a36Sopenharmony_ci u32 pseudo_cause = wil_r(wil, RGF_DMA_PSEUDO_CAUSE); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /** 75462306a36Sopenharmony_ci * pseudo_cause is Clear-On-Read, no need to ACK 75562306a36Sopenharmony_ci */ 75662306a36Sopenharmony_ci if (unlikely((pseudo_cause == 0) || ((pseudo_cause & 0xff) == 0xff))) 75762306a36Sopenharmony_ci return IRQ_NONE; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci /* IRQ mask debug */ 76062306a36Sopenharmony_ci if (unlikely(wil6210_debug_irq_mask(wil, pseudo_cause))) 76162306a36Sopenharmony_ci return IRQ_NONE; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci trace_wil6210_irq_pseudo(pseudo_cause); 76462306a36Sopenharmony_ci wil_dbg_irq(wil, "Pseudo IRQ 0x%08x\n", pseudo_cause); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci wil6210_mask_irq_pseudo(wil); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci /* Discover real IRQ cause 76962306a36Sopenharmony_ci * There are 2 possible phases for every IRQ: 77062306a36Sopenharmony_ci * - hard IRQ handler called right here 77162306a36Sopenharmony_ci * - threaded handler called later 77262306a36Sopenharmony_ci * 77362306a36Sopenharmony_ci * Hard IRQ handler reads and clears ISR. 77462306a36Sopenharmony_ci * 77562306a36Sopenharmony_ci * If threaded handler requested, hard IRQ handler 77662306a36Sopenharmony_ci * returns IRQ_WAKE_THREAD and saves ISR register value 77762306a36Sopenharmony_ci * for the threaded handler use. 77862306a36Sopenharmony_ci * 77962306a36Sopenharmony_ci * voting for wake thread - need at least 1 vote 78062306a36Sopenharmony_ci */ 78162306a36Sopenharmony_ci if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_RX) && 78262306a36Sopenharmony_ci (wil->txrx_ops.irq_rx(irq, cookie) == IRQ_WAKE_THREAD)) 78362306a36Sopenharmony_ci rc = IRQ_WAKE_THREAD; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_TX) && 78662306a36Sopenharmony_ci (wil->txrx_ops.irq_tx(irq, cookie) == IRQ_WAKE_THREAD)) 78762306a36Sopenharmony_ci rc = IRQ_WAKE_THREAD; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_MISC) && 79062306a36Sopenharmony_ci (wil6210_irq_misc(irq, cookie) == IRQ_WAKE_THREAD)) 79162306a36Sopenharmony_ci rc = IRQ_WAKE_THREAD; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* if thread is requested, it will unmask IRQ */ 79462306a36Sopenharmony_ci if (rc != IRQ_WAKE_THREAD) 79562306a36Sopenharmony_ci wil6210_unmask_irq_pseudo(wil); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci return rc; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic int wil6210_request_3msi(struct wil6210_priv *wil, int irq) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci int rc; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* IRQ's are in the following order: 80562306a36Sopenharmony_ci * - Tx 80662306a36Sopenharmony_ci * - Rx 80762306a36Sopenharmony_ci * - Misc 80862306a36Sopenharmony_ci */ 80962306a36Sopenharmony_ci rc = request_irq(irq, wil->txrx_ops.irq_tx, IRQF_SHARED, 81062306a36Sopenharmony_ci WIL_NAME "_tx", wil); 81162306a36Sopenharmony_ci if (rc) 81262306a36Sopenharmony_ci return rc; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci rc = request_irq(irq + 1, wil->txrx_ops.irq_rx, IRQF_SHARED, 81562306a36Sopenharmony_ci WIL_NAME "_rx", wil); 81662306a36Sopenharmony_ci if (rc) 81762306a36Sopenharmony_ci goto free0; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci rc = request_threaded_irq(irq + 2, wil6210_irq_misc, 82062306a36Sopenharmony_ci wil6210_irq_misc_thread, 82162306a36Sopenharmony_ci IRQF_SHARED, WIL_NAME "_misc", wil); 82262306a36Sopenharmony_ci if (rc) 82362306a36Sopenharmony_ci goto free1; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci return 0; 82662306a36Sopenharmony_cifree1: 82762306a36Sopenharmony_ci free_irq(irq + 1, wil); 82862306a36Sopenharmony_cifree0: 82962306a36Sopenharmony_ci free_irq(irq, wil); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci return rc; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci/* can't use wil_ioread32_and_clear because ICC value is not set yet */ 83562306a36Sopenharmony_cistatic inline void wil_clear32(void __iomem *addr) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci u32 x = readl(addr); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci writel(x, addr); 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_civoid wil6210_clear_irq(struct wil6210_priv *wil) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) + 84562306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICR)); 84662306a36Sopenharmony_ci wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) + 84762306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICR)); 84862306a36Sopenharmony_ci wil_clear32(wil->csr + HOSTADDR(RGF_INT_GEN_RX_ICR) + 84962306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICR)); 85062306a36Sopenharmony_ci wil_clear32(wil->csr + HOSTADDR(RGF_INT_GEN_TX_ICR) + 85162306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICR)); 85262306a36Sopenharmony_ci wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) + 85362306a36Sopenharmony_ci offsetof(struct RGF_ICR, ICR)); 85462306a36Sopenharmony_ci wmb(); /* make sure write completed */ 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_civoid wil6210_set_halp(struct wil6210_priv *wil) 85862306a36Sopenharmony_ci{ 85962306a36Sopenharmony_ci wil_dbg_irq(wil, "set_halp\n"); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, ICS), 86262306a36Sopenharmony_ci BIT_DMA_EP_MISC_ICR_HALP); 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_civoid wil6210_clear_halp(struct wil6210_priv *wil) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci wil_dbg_irq(wil, "clear_halp\n"); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, ICR), 87062306a36Sopenharmony_ci BIT_DMA_EP_MISC_ICR_HALP); 87162306a36Sopenharmony_ci wil6210_unmask_halp(wil); 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ciint wil6210_init_irq(struct wil6210_priv *wil, int irq) 87562306a36Sopenharmony_ci{ 87662306a36Sopenharmony_ci int rc; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci wil_dbg_misc(wil, "init_irq: %s, n_msi=%d\n", 87962306a36Sopenharmony_ci wil->n_msi ? "MSI" : "INTx", wil->n_msi); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci if (wil->use_enhanced_dma_hw) { 88262306a36Sopenharmony_ci wil->txrx_ops.irq_tx = wil6210_irq_tx_edma; 88362306a36Sopenharmony_ci wil->txrx_ops.irq_rx = wil6210_irq_rx_edma; 88462306a36Sopenharmony_ci } else { 88562306a36Sopenharmony_ci wil->txrx_ops.irq_tx = wil6210_irq_tx; 88662306a36Sopenharmony_ci wil->txrx_ops.irq_rx = wil6210_irq_rx; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci if (wil->n_msi == 3) 89062306a36Sopenharmony_ci rc = wil6210_request_3msi(wil, irq); 89162306a36Sopenharmony_ci else 89262306a36Sopenharmony_ci rc = request_threaded_irq(irq, wil6210_hardirq, 89362306a36Sopenharmony_ci wil6210_thread_irq, 89462306a36Sopenharmony_ci wil->n_msi ? 0 : IRQF_SHARED, 89562306a36Sopenharmony_ci WIL_NAME, wil); 89662306a36Sopenharmony_ci return rc; 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_civoid wil6210_fini_irq(struct wil6210_priv *wil, int irq) 90062306a36Sopenharmony_ci{ 90162306a36Sopenharmony_ci wil_dbg_misc(wil, "fini_irq:\n"); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci wil_mask_irq(wil); 90462306a36Sopenharmony_ci free_irq(irq, wil); 90562306a36Sopenharmony_ci if (wil->n_msi == 3) { 90662306a36Sopenharmony_ci free_irq(irq + 1, wil); 90762306a36Sopenharmony_ci free_irq(irq + 2, wil); 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci} 910