1 | // SPDX-License-Identifier: ISC |
2 | /* |
3 | * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. |
4 | * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. |
5 | */ |
6 | |
7 | #include <linux/interrupt.h> |
8 | |
9 | #include "wil6210.h" |
10 | #include "trace.h" |
11 | |
12 | /* |
13 | * Theory of operation: |
14 | * |
15 | * There is ISR pseudo-cause register, |
16 | * dma_rgf->DMA_RGF.PSEUDO_CAUSE.PSEUDO_CAUSE |
17 | * Its bits represents OR'ed bits from 3 real ISR registers: |
18 | * TX, RX, and MISC. |
19 | * |
20 | * Registers may be configured to either "write 1 to clear" or |
21 | * "clear on read" mode |
22 | * |
23 | * When handling interrupt, one have to mask/unmask interrupts for the |
24 | * real ISR registers, or hardware may malfunction. |
25 | * |
26 | */ |
27 | |
28 | #define WIL6210_IRQ_DISABLE (0xFFFFFFFFUL) |
29 | #define WIL6210_IRQ_DISABLE_NO_HALP (0xF7FFFFFFUL) |
30 | #define WIL6210_IMC_RX (BIT_DMA_EP_RX_ICR_RX_DONE | \ |
31 | BIT_DMA_EP_RX_ICR_RX_HTRSH) |
32 | #define WIL6210_IMC_RX_NO_RX_HTRSH (WIL6210_IMC_RX & \ |
33 | (~(BIT_DMA_EP_RX_ICR_RX_HTRSH))) |
34 | #define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \ |
35 | BIT_DMA_EP_TX_ICR_TX_DONE_N(0)) |
36 | #define WIL6210_IMC_TX_EDMA BIT_TX_STATUS_IRQ |
37 | #define WIL6210_IMC_RX_EDMA BIT_RX_STATUS_IRQ |
38 | #define WIL6210_IMC_MISC_NO_HALP (ISR_MISC_FW_READY | \ |
39 | ISR_MISC_MBOX_EVT | \ |
40 | ISR_MISC_FW_ERROR) |
41 | #define WIL6210_IMC_MISC (WIL6210_IMC_MISC_NO_HALP | \ |
42 | BIT_DMA_EP_MISC_ICR_HALP) |
43 | #define WIL6210_IRQ_PSEUDO_MASK (u32)(~(BIT_DMA_PSEUDO_CAUSE_RX | \ |
44 | BIT_DMA_PSEUDO_CAUSE_TX | \ |
45 | BIT_DMA_PSEUDO_CAUSE_MISC)) |
46 | |
47 | #if defined(CONFIG_WIL6210_ISR_COR) |
48 | /* configure to Clear-On-Read mode */ |
49 | #define WIL_ICR_ICC_VALUE (0xFFFFFFFFUL) |
50 | #define WIL_ICR_ICC_MISC_VALUE (0xF7FFFFFFUL) |
51 | |
52 | static inline void wil_icr_clear(u32 x, void __iomem *addr) |
53 | { |
54 | } |
55 | #else /* defined(CONFIG_WIL6210_ISR_COR) */ |
56 | /* configure to Write-1-to-Clear mode */ |
57 | #define WIL_ICR_ICC_VALUE (0UL) |
58 | #define WIL_ICR_ICC_MISC_VALUE (0UL) |
59 | |
60 | static inline void wil_icr_clear(u32 x, void __iomem *addr) |
61 | { |
62 | writel(x, addr); |
63 | } |
64 | #endif /* defined(CONFIG_WIL6210_ISR_COR) */ |
65 | |
66 | static inline u32 wil_ioread32_and_clear(void __iomem *addr) |
67 | { |
68 | u32 x = readl(addr); |
69 | |
70 | wil_icr_clear(x, addr); |
71 | |
72 | return x; |
73 | } |
74 | |
75 | static void wil6210_mask_irq_tx(struct wil6210_priv *wil) |
76 | { |
77 | wil_w(wil, RGF_DMA_EP_TX_ICR + offsetof(struct RGF_ICR, IMS), |
78 | WIL6210_IRQ_DISABLE); |
79 | } |
80 | |
81 | static void wil6210_mask_irq_tx_edma(struct wil6210_priv *wil) |
82 | { |
83 | wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, IMS), |
84 | WIL6210_IRQ_DISABLE); |
85 | } |
86 | |
87 | static void wil6210_mask_irq_rx(struct wil6210_priv *wil) |
88 | { |
89 | wil_w(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, IMS), |
90 | WIL6210_IRQ_DISABLE); |
91 | } |
92 | |
93 | static void wil6210_mask_irq_rx_edma(struct wil6210_priv *wil) |
94 | { |
95 | wil_w(wil, RGF_INT_GEN_RX_ICR + offsetof(struct RGF_ICR, IMS), |
96 | WIL6210_IRQ_DISABLE); |
97 | } |
98 | |
99 | static void wil6210_mask_irq_misc(struct wil6210_priv *wil, bool mask_halp) |
100 | { |
101 | wil_dbg_irq(wil, "mask_irq_misc: mask_halp(%s)\n" , |
102 | mask_halp ? "true" : "false" ); |
103 | |
104 | wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, IMS), |
105 | val: mask_halp ? WIL6210_IRQ_DISABLE : WIL6210_IRQ_DISABLE_NO_HALP); |
106 | } |
107 | |
108 | void wil6210_mask_halp(struct wil6210_priv *wil) |
109 | { |
110 | wil_dbg_irq(wil, "mask_halp\n" ); |
111 | |
112 | wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, IMS), |
113 | BIT_DMA_EP_MISC_ICR_HALP); |
114 | } |
115 | |
116 | static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil) |
117 | { |
118 | wil_dbg_irq(wil, "mask_irq_pseudo\n" ); |
119 | |
120 | wil_w(wil, RGF_DMA_PSEUDO_CAUSE_MASK_SW, WIL6210_IRQ_DISABLE); |
121 | |
122 | clear_bit(nr: wil_status_irqen, addr: wil->status); |
123 | } |
124 | |
125 | void wil6210_unmask_irq_tx(struct wil6210_priv *wil) |
126 | { |
127 | wil_w(wil, RGF_DMA_EP_TX_ICR + offsetof(struct RGF_ICR, IMC), |
128 | WIL6210_IMC_TX); |
129 | } |
130 | |
131 | void wil6210_unmask_irq_tx_edma(struct wil6210_priv *wil) |
132 | { |
133 | wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, IMC), |
134 | WIL6210_IMC_TX_EDMA); |
135 | } |
136 | |
137 | void wil6210_unmask_irq_rx(struct wil6210_priv *wil) |
138 | { |
139 | bool unmask_rx_htrsh = atomic_read(v: &wil->connected_vifs) > 0; |
140 | |
141 | wil_w(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, IMC), |
142 | val: unmask_rx_htrsh ? WIL6210_IMC_RX : WIL6210_IMC_RX_NO_RX_HTRSH); |
143 | } |
144 | |
145 | void wil6210_unmask_irq_rx_edma(struct wil6210_priv *wil) |
146 | { |
147 | wil_w(wil, RGF_INT_GEN_RX_ICR + offsetof(struct RGF_ICR, IMC), |
148 | WIL6210_IMC_RX_EDMA); |
149 | } |
150 | |
151 | static void wil6210_unmask_irq_misc(struct wil6210_priv *wil, bool unmask_halp) |
152 | { |
153 | wil_dbg_irq(wil, "unmask_irq_misc: unmask_halp(%s)\n" , |
154 | unmask_halp ? "true" : "false" ); |
155 | |
156 | wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, IMC), |
157 | val: unmask_halp ? WIL6210_IMC_MISC : WIL6210_IMC_MISC_NO_HALP); |
158 | } |
159 | |
160 | static void wil6210_unmask_halp(struct wil6210_priv *wil) |
161 | { |
162 | wil_dbg_irq(wil, "unmask_halp\n" ); |
163 | |
164 | wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, IMC), |
165 | BIT_DMA_EP_MISC_ICR_HALP); |
166 | } |
167 | |
168 | static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil) |
169 | { |
170 | wil_dbg_irq(wil, "unmask_irq_pseudo\n" ); |
171 | |
172 | set_bit(nr: wil_status_irqen, addr: wil->status); |
173 | |
174 | wil_w(wil, RGF_DMA_PSEUDO_CAUSE_MASK_SW, WIL6210_IRQ_PSEUDO_MASK); |
175 | } |
176 | |
177 | void wil_mask_irq(struct wil6210_priv *wil) |
178 | { |
179 | wil_dbg_irq(wil, "mask_irq\n" ); |
180 | |
181 | wil6210_mask_irq_tx(wil); |
182 | wil6210_mask_irq_tx_edma(wil); |
183 | wil6210_mask_irq_rx(wil); |
184 | wil6210_mask_irq_rx_edma(wil); |
185 | wil6210_mask_irq_misc(wil, mask_halp: true); |
186 | wil6210_mask_irq_pseudo(wil); |
187 | } |
188 | |
189 | void wil_unmask_irq(struct wil6210_priv *wil) |
190 | { |
191 | wil_dbg_irq(wil, "unmask_irq\n" ); |
192 | |
193 | wil_w(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, ICC), |
194 | WIL_ICR_ICC_VALUE); |
195 | wil_w(wil, RGF_DMA_EP_TX_ICR + offsetof(struct RGF_ICR, ICC), |
196 | WIL_ICR_ICC_VALUE); |
197 | wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, ICC), |
198 | WIL_ICR_ICC_MISC_VALUE); |
199 | wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, ICC), |
200 | WIL_ICR_ICC_VALUE); |
201 | wil_w(wil, RGF_INT_GEN_RX_ICR + offsetof(struct RGF_ICR, ICC), |
202 | WIL_ICR_ICC_VALUE); |
203 | |
204 | wil6210_unmask_irq_pseudo(wil); |
205 | if (wil->use_enhanced_dma_hw) { |
206 | wil6210_unmask_irq_tx_edma(wil); |
207 | wil6210_unmask_irq_rx_edma(wil); |
208 | } else { |
209 | wil6210_unmask_irq_tx(wil); |
210 | wil6210_unmask_irq_rx(wil); |
211 | } |
212 | wil6210_unmask_irq_misc(wil, unmask_halp: true); |
213 | } |
214 | |
215 | void wil_configure_interrupt_moderation_edma(struct wil6210_priv *wil) |
216 | { |
217 | u32 moderation; |
218 | |
219 | wil_s(wil, RGF_INT_GEN_IDLE_TIME_LIMIT, WIL_EDMA_IDLE_TIME_LIMIT_USEC); |
220 | |
221 | wil_s(wil, RGF_INT_GEN_TIME_UNIT_LIMIT, WIL_EDMA_TIME_UNIT_CLK_CYCLES); |
222 | |
223 | /* Update RX and TX moderation */ |
224 | moderation = wil->rx_max_burst_duration | |
225 | (WIL_EDMA_AGG_WATERMARK << WIL_EDMA_AGG_WATERMARK_POS); |
226 | wil_w(wil, RGF_INT_CTRL_INT_GEN_CFG_0, val: moderation); |
227 | wil_w(wil, RGF_INT_CTRL_INT_GEN_CFG_1, val: moderation); |
228 | |
229 | /* Treat special events as regular |
230 | * (set bit 0 to 0x1 and clear bits 1-8) |
231 | */ |
232 | wil_c(wil, RGF_INT_COUNT_ON_SPECIAL_EVT, val: 0x1FE); |
233 | wil_s(wil, RGF_INT_COUNT_ON_SPECIAL_EVT, val: 0x1); |
234 | } |
235 | |
236 | void wil_configure_interrupt_moderation(struct wil6210_priv *wil) |
237 | { |
238 | struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr; |
239 | |
240 | wil_dbg_irq(wil, "configure_interrupt_moderation\n" ); |
241 | |
242 | /* disable interrupt moderation for monitor |
243 | * to get better timestamp precision |
244 | */ |
245 | if (wdev->iftype == NL80211_IFTYPE_MONITOR) |
246 | return; |
247 | |
248 | /* Disable and clear tx counter before (re)configuration */ |
249 | wil_w(wil, RGF_DMA_ITR_TX_CNT_CTL, BIT_DMA_ITR_TX_CNT_CTL_CLR); |
250 | wil_w(wil, RGF_DMA_ITR_TX_CNT_TRSH, val: wil->tx_max_burst_duration); |
251 | wil_info(wil, "set ITR_TX_CNT_TRSH = %d usec\n" , |
252 | wil->tx_max_burst_duration); |
253 | /* Configure TX max burst duration timer to use usec units */ |
254 | wil_w(wil, RGF_DMA_ITR_TX_CNT_CTL, |
255 | BIT_DMA_ITR_TX_CNT_CTL_EN | BIT_DMA_ITR_TX_CNT_CTL_EXT_TIC_SEL); |
256 | |
257 | /* Disable and clear tx idle counter before (re)configuration */ |
258 | wil_w(wil, RGF_DMA_ITR_TX_IDL_CNT_CTL, BIT_DMA_ITR_TX_IDL_CNT_CTL_CLR); |
259 | wil_w(wil, RGF_DMA_ITR_TX_IDL_CNT_TRSH, val: wil->tx_interframe_timeout); |
260 | wil_info(wil, "set ITR_TX_IDL_CNT_TRSH = %d usec\n" , |
261 | wil->tx_interframe_timeout); |
262 | /* Configure TX max burst duration timer to use usec units */ |
263 | wil_w(wil, RGF_DMA_ITR_TX_IDL_CNT_CTL, BIT_DMA_ITR_TX_IDL_CNT_CTL_EN | |
264 | BIT_DMA_ITR_TX_IDL_CNT_CTL_EXT_TIC_SEL); |
265 | |
266 | /* Disable and clear rx counter before (re)configuration */ |
267 | wil_w(wil, RGF_DMA_ITR_RX_CNT_CTL, BIT_DMA_ITR_RX_CNT_CTL_CLR); |
268 | wil_w(wil, RGF_DMA_ITR_RX_CNT_TRSH, val: wil->rx_max_burst_duration); |
269 | wil_info(wil, "set ITR_RX_CNT_TRSH = %d usec\n" , |
270 | wil->rx_max_burst_duration); |
271 | /* Configure TX max burst duration timer to use usec units */ |
272 | wil_w(wil, RGF_DMA_ITR_RX_CNT_CTL, |
273 | BIT_DMA_ITR_RX_CNT_CTL_EN | BIT_DMA_ITR_RX_CNT_CTL_EXT_TIC_SEL); |
274 | |
275 | /* Disable and clear rx idle counter before (re)configuration */ |
276 | wil_w(wil, RGF_DMA_ITR_RX_IDL_CNT_CTL, BIT_DMA_ITR_RX_IDL_CNT_CTL_CLR); |
277 | wil_w(wil, RGF_DMA_ITR_RX_IDL_CNT_TRSH, val: wil->rx_interframe_timeout); |
278 | wil_info(wil, "set ITR_RX_IDL_CNT_TRSH = %d usec\n" , |
279 | wil->rx_interframe_timeout); |
280 | /* Configure TX max burst duration timer to use usec units */ |
281 | wil_w(wil, RGF_DMA_ITR_RX_IDL_CNT_CTL, BIT_DMA_ITR_RX_IDL_CNT_CTL_EN | |
282 | BIT_DMA_ITR_RX_IDL_CNT_CTL_EXT_TIC_SEL); |
283 | } |
284 | |
285 | static irqreturn_t wil6210_irq_rx(int irq, void *cookie) |
286 | { |
287 | struct wil6210_priv *wil = cookie; |
288 | u32 isr; |
289 | bool need_unmask = true; |
290 | |
291 | wil6210_mask_irq_rx(wil); |
292 | |
293 | isr = wil_ioread32_and_clear(addr: wil->csr + |
294 | HOSTADDR(RGF_DMA_EP_RX_ICR) + |
295 | offsetof(struct RGF_ICR, ICR)); |
296 | |
297 | trace_wil6210_irq_rx(x: isr); |
298 | wil_dbg_irq(wil, "ISR RX 0x%08x\n" , isr); |
299 | |
300 | if (unlikely(!isr)) { |
301 | wil_err_ratelimited(wil, "spurious IRQ: RX\n" ); |
302 | wil6210_unmask_irq_rx(wil); |
303 | return IRQ_NONE; |
304 | } |
305 | |
306 | /* RX_DONE and RX_HTRSH interrupts are the same if interrupt |
307 | * moderation is not used. Interrupt moderation may cause RX |
308 | * buffer overflow while RX_DONE is delayed. The required |
309 | * action is always the same - should empty the accumulated |
310 | * packets from the RX ring. |
311 | */ |
312 | if (likely(isr & (BIT_DMA_EP_RX_ICR_RX_DONE | |
313 | BIT_DMA_EP_RX_ICR_RX_HTRSH))) { |
314 | wil_dbg_irq(wil, "RX done / RX_HTRSH received, ISR (0x%x)\n" , |
315 | isr); |
316 | |
317 | isr &= ~(BIT_DMA_EP_RX_ICR_RX_DONE | |
318 | BIT_DMA_EP_RX_ICR_RX_HTRSH); |
319 | if (likely(test_bit(wil_status_fwready, wil->status))) { |
320 | if (likely(test_bit(wil_status_napi_en, wil->status))) { |
321 | wil_dbg_txrx(wil, "NAPI(Rx) schedule\n" ); |
322 | need_unmask = false; |
323 | napi_schedule(n: &wil->napi_rx); |
324 | } else { |
325 | wil_err_ratelimited( |
326 | wil, |
327 | "Got Rx interrupt while stopping interface\n" ); |
328 | } |
329 | } else { |
330 | wil_err_ratelimited(wil, "Got Rx interrupt while in reset\n" ); |
331 | } |
332 | } |
333 | |
334 | if (unlikely(isr)) |
335 | wil_err(wil, "un-handled RX ISR bits 0x%08x\n" , isr); |
336 | |
337 | /* Rx IRQ will be enabled when NAPI processing finished */ |
338 | |
339 | atomic_inc(v: &wil->isr_count_rx); |
340 | |
341 | if (unlikely(need_unmask)) |
342 | wil6210_unmask_irq_rx(wil); |
343 | |
344 | return IRQ_HANDLED; |
345 | } |
346 | |
347 | static irqreturn_t wil6210_irq_rx_edma(int irq, void *cookie) |
348 | { |
349 | struct wil6210_priv *wil = cookie; |
350 | u32 isr; |
351 | bool need_unmask = true; |
352 | |
353 | wil6210_mask_irq_rx_edma(wil); |
354 | |
355 | isr = wil_ioread32_and_clear(addr: wil->csr + |
356 | HOSTADDR(RGF_INT_GEN_RX_ICR) + |
357 | offsetof(struct RGF_ICR, ICR)); |
358 | |
359 | trace_wil6210_irq_rx(x: isr); |
360 | wil_dbg_irq(wil, "ISR RX 0x%08x\n" , isr); |
361 | |
362 | if (unlikely(!isr)) { |
363 | wil_err(wil, "spurious IRQ: RX\n" ); |
364 | wil6210_unmask_irq_rx_edma(wil); |
365 | return IRQ_NONE; |
366 | } |
367 | |
368 | if (likely(isr & BIT_RX_STATUS_IRQ)) { |
369 | wil_dbg_irq(wil, "RX status ring\n" ); |
370 | isr &= ~BIT_RX_STATUS_IRQ; |
371 | if (likely(test_bit(wil_status_fwready, wil->status))) { |
372 | if (likely(test_bit(wil_status_napi_en, wil->status))) { |
373 | wil_dbg_txrx(wil, "NAPI(Rx) schedule\n" ); |
374 | need_unmask = false; |
375 | napi_schedule(n: &wil->napi_rx); |
376 | } else { |
377 | wil_err(wil, |
378 | "Got Rx interrupt while stopping interface\n" ); |
379 | } |
380 | } else { |
381 | wil_err(wil, "Got Rx interrupt while in reset\n" ); |
382 | } |
383 | } |
384 | |
385 | if (unlikely(isr)) |
386 | wil_err(wil, "un-handled RX ISR bits 0x%08x\n" , isr); |
387 | |
388 | /* Rx IRQ will be enabled when NAPI processing finished */ |
389 | |
390 | atomic_inc(v: &wil->isr_count_rx); |
391 | |
392 | if (unlikely(need_unmask)) |
393 | wil6210_unmask_irq_rx_edma(wil); |
394 | |
395 | return IRQ_HANDLED; |
396 | } |
397 | |
398 | static irqreturn_t wil6210_irq_tx_edma(int irq, void *cookie) |
399 | { |
400 | struct wil6210_priv *wil = cookie; |
401 | u32 isr; |
402 | bool need_unmask = true; |
403 | |
404 | wil6210_mask_irq_tx_edma(wil); |
405 | |
406 | isr = wil_ioread32_and_clear(addr: wil->csr + |
407 | HOSTADDR(RGF_INT_GEN_TX_ICR) + |
408 | offsetof(struct RGF_ICR, ICR)); |
409 | |
410 | trace_wil6210_irq_tx(x: isr); |
411 | wil_dbg_irq(wil, "ISR TX 0x%08x\n" , isr); |
412 | |
413 | if (unlikely(!isr)) { |
414 | wil_err(wil, "spurious IRQ: TX\n" ); |
415 | wil6210_unmask_irq_tx_edma(wil); |
416 | return IRQ_NONE; |
417 | } |
418 | |
419 | if (likely(isr & BIT_TX_STATUS_IRQ)) { |
420 | wil_dbg_irq(wil, "TX status ring\n" ); |
421 | isr &= ~BIT_TX_STATUS_IRQ; |
422 | if (likely(test_bit(wil_status_fwready, wil->status))) { |
423 | wil_dbg_txrx(wil, "NAPI(Tx) schedule\n" ); |
424 | need_unmask = false; |
425 | napi_schedule(n: &wil->napi_tx); |
426 | } else { |
427 | wil_err(wil, "Got Tx status ring IRQ while in reset\n" ); |
428 | } |
429 | } |
430 | |
431 | if (unlikely(isr)) |
432 | wil_err(wil, "un-handled TX ISR bits 0x%08x\n" , isr); |
433 | |
434 | /* Tx IRQ will be enabled when NAPI processing finished */ |
435 | |
436 | atomic_inc(v: &wil->isr_count_tx); |
437 | |
438 | if (unlikely(need_unmask)) |
439 | wil6210_unmask_irq_tx_edma(wil); |
440 | |
441 | return IRQ_HANDLED; |
442 | } |
443 | |
444 | static irqreturn_t wil6210_irq_tx(int irq, void *cookie) |
445 | { |
446 | struct wil6210_priv *wil = cookie; |
447 | u32 isr; |
448 | bool need_unmask = true; |
449 | |
450 | wil6210_mask_irq_tx(wil); |
451 | |
452 | isr = wil_ioread32_and_clear(addr: wil->csr + |
453 | HOSTADDR(RGF_DMA_EP_TX_ICR) + |
454 | offsetof(struct RGF_ICR, ICR)); |
455 | |
456 | trace_wil6210_irq_tx(x: isr); |
457 | wil_dbg_irq(wil, "ISR TX 0x%08x\n" , isr); |
458 | |
459 | if (unlikely(!isr)) { |
460 | wil_err_ratelimited(wil, "spurious IRQ: TX\n" ); |
461 | wil6210_unmask_irq_tx(wil); |
462 | return IRQ_NONE; |
463 | } |
464 | |
465 | if (likely(isr & BIT_DMA_EP_TX_ICR_TX_DONE)) { |
466 | wil_dbg_irq(wil, "TX done\n" ); |
467 | isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; |
468 | /* clear also all VRING interrupts */ |
469 | isr &= ~(BIT(25) - 1UL); |
470 | if (likely(test_bit(wil_status_fwready, wil->status))) { |
471 | wil_dbg_txrx(wil, "NAPI(Tx) schedule\n" ); |
472 | need_unmask = false; |
473 | napi_schedule(n: &wil->napi_tx); |
474 | } else { |
475 | wil_err_ratelimited(wil, "Got Tx interrupt while in reset\n" ); |
476 | } |
477 | } |
478 | |
479 | if (unlikely(isr)) |
480 | wil_err_ratelimited(wil, "un-handled TX ISR bits 0x%08x\n" , |
481 | isr); |
482 | |
483 | /* Tx IRQ will be enabled when NAPI processing finished */ |
484 | |
485 | atomic_inc(v: &wil->isr_count_tx); |
486 | |
487 | if (unlikely(need_unmask)) |
488 | wil6210_unmask_irq_tx(wil); |
489 | |
490 | return IRQ_HANDLED; |
491 | } |
492 | |
493 | static void wil_notify_fw_error(struct wil6210_priv *wil) |
494 | { |
495 | struct device *dev = &wil->main_ndev->dev; |
496 | char *envp[3] = { |
497 | [0] = "SOURCE=wil6210" , |
498 | [1] = "EVENT=FW_ERROR" , |
499 | [2] = NULL, |
500 | }; |
501 | wil_err(wil, "Notify about firmware error\n" ); |
502 | kobject_uevent_env(kobj: &dev->kobj, action: KOBJ_CHANGE, envp); |
503 | } |
504 | |
505 | static void wil_cache_mbox_regs(struct wil6210_priv *wil) |
506 | { |
507 | /* make shadow copy of registers that should not change on run time */ |
508 | wil_memcpy_fromio_32(dst: &wil->mbox_ctl, src: wil->csr + HOST_MBOX, |
509 | count: sizeof(struct wil6210_mbox_ctl)); |
510 | wil_mbox_ring_le2cpus(r: &wil->mbox_ctl.rx); |
511 | wil_mbox_ring_le2cpus(r: &wil->mbox_ctl.tx); |
512 | } |
513 | |
514 | static bool wil_validate_mbox_regs(struct wil6210_priv *wil) |
515 | { |
516 | size_t min_size = sizeof(struct wil6210_mbox_hdr) + |
517 | sizeof(struct wmi_cmd_hdr); |
518 | |
519 | if (wil->mbox_ctl.rx.entry_size < min_size) { |
520 | wil_err(wil, "rx mbox entry too small (%d)\n" , |
521 | wil->mbox_ctl.rx.entry_size); |
522 | return false; |
523 | } |
524 | if (wil->mbox_ctl.tx.entry_size < min_size) { |
525 | wil_err(wil, "tx mbox entry too small (%d)\n" , |
526 | wil->mbox_ctl.tx.entry_size); |
527 | return false; |
528 | } |
529 | |
530 | return true; |
531 | } |
532 | |
533 | static irqreturn_t wil6210_irq_misc(int irq, void *cookie) |
534 | { |
535 | struct wil6210_priv *wil = cookie; |
536 | u32 isr; |
537 | |
538 | wil6210_mask_irq_misc(wil, mask_halp: false); |
539 | |
540 | isr = wil_ioread32_and_clear(addr: wil->csr + |
541 | HOSTADDR(RGF_DMA_EP_MISC_ICR) + |
542 | offsetof(struct RGF_ICR, ICR)); |
543 | |
544 | trace_wil6210_irq_misc(x: isr); |
545 | wil_dbg_irq(wil, "ISR MISC 0x%08x\n" , isr); |
546 | |
547 | if (!isr) { |
548 | wil_err(wil, "spurious IRQ: MISC\n" ); |
549 | wil6210_unmask_irq_misc(wil, unmask_halp: false); |
550 | return IRQ_NONE; |
551 | } |
552 | |
553 | if (isr & ISR_MISC_FW_ERROR) { |
554 | u32 fw_assert_code = wil_r(wil, reg: wil->rgf_fw_assert_code_addr); |
555 | u32 ucode_assert_code = |
556 | wil_r(wil, reg: wil->rgf_ucode_assert_code_addr); |
557 | |
558 | wil_err(wil, |
559 | "Firmware error detected, assert codes FW 0x%08x, UCODE 0x%08x\n" , |
560 | fw_assert_code, ucode_assert_code); |
561 | clear_bit(nr: wil_status_fwready, addr: wil->status); |
562 | /* |
563 | * do not clear @isr here - we do 2-nd part in thread |
564 | * there, user space get notified, and it should be done |
565 | * in non-atomic context |
566 | */ |
567 | } |
568 | |
569 | if (isr & ISR_MISC_FW_READY) { |
570 | wil_dbg_irq(wil, "IRQ: FW ready\n" ); |
571 | wil_cache_mbox_regs(wil); |
572 | if (wil_validate_mbox_regs(wil)) |
573 | set_bit(nr: wil_status_mbox_ready, addr: wil->status); |
574 | /** |
575 | * Actual FW ready indicated by the |
576 | * WMI_FW_READY_EVENTID |
577 | */ |
578 | isr &= ~ISR_MISC_FW_READY; |
579 | } |
580 | |
581 | if (isr & BIT_DMA_EP_MISC_ICR_HALP) { |
582 | isr &= ~BIT_DMA_EP_MISC_ICR_HALP; |
583 | if (wil->halp.handle_icr) { |
584 | /* no need to handle HALP ICRs until next vote */ |
585 | wil->halp.handle_icr = false; |
586 | wil_dbg_irq(wil, "irq_misc: HALP IRQ invoked\n" ); |
587 | wil6210_mask_irq_misc(wil, mask_halp: true); |
588 | complete(&wil->halp.comp); |
589 | } |
590 | } |
591 | |
592 | wil->isr_misc = isr; |
593 | |
594 | if (isr) { |
595 | return IRQ_WAKE_THREAD; |
596 | } else { |
597 | wil6210_unmask_irq_misc(wil, unmask_halp: false); |
598 | return IRQ_HANDLED; |
599 | } |
600 | } |
601 | |
602 | static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) |
603 | { |
604 | struct wil6210_priv *wil = cookie; |
605 | u32 isr = wil->isr_misc; |
606 | |
607 | trace_wil6210_irq_misc_thread(x: isr); |
608 | wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n" , isr); |
609 | |
610 | if (isr & ISR_MISC_FW_ERROR) { |
611 | wil->recovery_state = fw_recovery_pending; |
612 | wil_fw_core_dump(wil); |
613 | wil_notify_fw_error(wil); |
614 | isr &= ~ISR_MISC_FW_ERROR; |
615 | if (wil->platform_ops.notify) { |
616 | wil_err(wil, "notify platform driver about FW crash" ); |
617 | wil->platform_ops.notify(wil->platform_handle, |
618 | WIL_PLATFORM_EVT_FW_CRASH); |
619 | } else { |
620 | wil_fw_error_recovery(wil); |
621 | } |
622 | } |
623 | if (isr & ISR_MISC_MBOX_EVT) { |
624 | wil_dbg_irq(wil, "MBOX event\n" ); |
625 | wmi_recv_cmd(wil); |
626 | isr &= ~ISR_MISC_MBOX_EVT; |
627 | } |
628 | |
629 | if (isr) |
630 | wil_dbg_irq(wil, "un-handled MISC ISR bits 0x%08x\n" , isr); |
631 | |
632 | wil->isr_misc = 0; |
633 | |
634 | wil6210_unmask_irq_misc(wil, unmask_halp: false); |
635 | |
636 | /* in non-triple MSI case, this is done inside wil6210_thread_irq |
637 | * because it has to be done after unmasking the pseudo. |
638 | */ |
639 | if (wil->n_msi == 3 && wil->suspend_resp_rcvd) { |
640 | wil_dbg_irq(wil, "set suspend_resp_comp to true\n" ); |
641 | wil->suspend_resp_comp = true; |
642 | wake_up_interruptible(&wil->wq); |
643 | } |
644 | |
645 | return IRQ_HANDLED; |
646 | } |
647 | |
648 | /* thread IRQ handler */ |
649 | static irqreturn_t wil6210_thread_irq(int irq, void *cookie) |
650 | { |
651 | struct wil6210_priv *wil = cookie; |
652 | |
653 | wil_dbg_irq(wil, "Thread IRQ\n" ); |
654 | /* Discover real IRQ cause */ |
655 | if (wil->isr_misc) |
656 | wil6210_irq_misc_thread(irq, cookie); |
657 | |
658 | wil6210_unmask_irq_pseudo(wil); |
659 | |
660 | if (wil->suspend_resp_rcvd) { |
661 | wil_dbg_irq(wil, "set suspend_resp_comp to true\n" ); |
662 | wil->suspend_resp_comp = true; |
663 | wake_up_interruptible(&wil->wq); |
664 | } |
665 | |
666 | return IRQ_HANDLED; |
667 | } |
668 | |
669 | /* DEBUG |
670 | * There is subtle bug in hardware that causes IRQ to raise when it should be |
671 | * masked. It is quite rare and hard to debug. |
672 | * |
673 | * Catch irq issue if it happens and print all I can. |
674 | */ |
675 | static int wil6210_debug_irq_mask(struct wil6210_priv *wil, u32 pseudo_cause) |
676 | { |
677 | u32 icm_rx, icr_rx, imv_rx; |
678 | u32 icm_tx, icr_tx, imv_tx; |
679 | u32 icm_misc, icr_misc, imv_misc; |
680 | |
681 | if (!test_bit(wil_status_irqen, wil->status)) { |
682 | if (wil->use_enhanced_dma_hw) { |
683 | icm_rx = wil_ioread32_and_clear(addr: wil->csr + |
684 | HOSTADDR(RGF_INT_GEN_RX_ICR) + |
685 | offsetof(struct RGF_ICR, ICM)); |
686 | icr_rx = wil_ioread32_and_clear(addr: wil->csr + |
687 | HOSTADDR(RGF_INT_GEN_RX_ICR) + |
688 | offsetof(struct RGF_ICR, ICR)); |
689 | imv_rx = wil_r(wil, RGF_INT_GEN_RX_ICR + |
690 | offsetof(struct RGF_ICR, IMV)); |
691 | icm_tx = wil_ioread32_and_clear(addr: wil->csr + |
692 | HOSTADDR(RGF_INT_GEN_TX_ICR) + |
693 | offsetof(struct RGF_ICR, ICM)); |
694 | icr_tx = wil_ioread32_and_clear(addr: wil->csr + |
695 | HOSTADDR(RGF_INT_GEN_TX_ICR) + |
696 | offsetof(struct RGF_ICR, ICR)); |
697 | imv_tx = wil_r(wil, RGF_INT_GEN_TX_ICR + |
698 | offsetof(struct RGF_ICR, IMV)); |
699 | } else { |
700 | icm_rx = wil_ioread32_and_clear(addr: wil->csr + |
701 | HOSTADDR(RGF_DMA_EP_RX_ICR) + |
702 | offsetof(struct RGF_ICR, ICM)); |
703 | icr_rx = wil_ioread32_and_clear(addr: wil->csr + |
704 | HOSTADDR(RGF_DMA_EP_RX_ICR) + |
705 | offsetof(struct RGF_ICR, ICR)); |
706 | imv_rx = wil_r(wil, RGF_DMA_EP_RX_ICR + |
707 | offsetof(struct RGF_ICR, IMV)); |
708 | icm_tx = wil_ioread32_and_clear(addr: wil->csr + |
709 | HOSTADDR(RGF_DMA_EP_TX_ICR) + |
710 | offsetof(struct RGF_ICR, ICM)); |
711 | icr_tx = wil_ioread32_and_clear(addr: wil->csr + |
712 | HOSTADDR(RGF_DMA_EP_TX_ICR) + |
713 | offsetof(struct RGF_ICR, ICR)); |
714 | imv_tx = wil_r(wil, RGF_DMA_EP_TX_ICR + |
715 | offsetof(struct RGF_ICR, IMV)); |
716 | } |
717 | icm_misc = wil_ioread32_and_clear(addr: wil->csr + |
718 | HOSTADDR(RGF_DMA_EP_MISC_ICR) + |
719 | offsetof(struct RGF_ICR, ICM)); |
720 | icr_misc = wil_ioread32_and_clear(addr: wil->csr + |
721 | HOSTADDR(RGF_DMA_EP_MISC_ICR) + |
722 | offsetof(struct RGF_ICR, ICR)); |
723 | imv_misc = wil_r(wil, RGF_DMA_EP_MISC_ICR + |
724 | offsetof(struct RGF_ICR, IMV)); |
725 | |
726 | /* HALP interrupt can be unmasked when misc interrupts are |
727 | * masked |
728 | */ |
729 | if (icr_misc & BIT_DMA_EP_MISC_ICR_HALP) |
730 | return 0; |
731 | |
732 | wil_err(wil, "IRQ when it should be masked: pseudo 0x%08x\n" |
733 | "Rx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" |
734 | "Tx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" |
735 | "Misc icm:icr:imv 0x%08x 0x%08x 0x%08x\n" , |
736 | pseudo_cause, |
737 | icm_rx, icr_rx, imv_rx, |
738 | icm_tx, icr_tx, imv_tx, |
739 | icm_misc, icr_misc, imv_misc); |
740 | |
741 | return -EINVAL; |
742 | } |
743 | |
744 | return 0; |
745 | } |
746 | |
747 | static irqreturn_t wil6210_hardirq(int irq, void *cookie) |
748 | { |
749 | irqreturn_t rc = IRQ_HANDLED; |
750 | struct wil6210_priv *wil = cookie; |
751 | u32 pseudo_cause = wil_r(wil, RGF_DMA_PSEUDO_CAUSE); |
752 | |
753 | /** |
754 | * pseudo_cause is Clear-On-Read, no need to ACK |
755 | */ |
756 | if (unlikely((pseudo_cause == 0) || ((pseudo_cause & 0xff) == 0xff))) |
757 | return IRQ_NONE; |
758 | |
759 | /* IRQ mask debug */ |
760 | if (unlikely(wil6210_debug_irq_mask(wil, pseudo_cause))) |
761 | return IRQ_NONE; |
762 | |
763 | trace_wil6210_irq_pseudo(x: pseudo_cause); |
764 | wil_dbg_irq(wil, "Pseudo IRQ 0x%08x\n" , pseudo_cause); |
765 | |
766 | wil6210_mask_irq_pseudo(wil); |
767 | |
768 | /* Discover real IRQ cause |
769 | * There are 2 possible phases for every IRQ: |
770 | * - hard IRQ handler called right here |
771 | * - threaded handler called later |
772 | * |
773 | * Hard IRQ handler reads and clears ISR. |
774 | * |
775 | * If threaded handler requested, hard IRQ handler |
776 | * returns IRQ_WAKE_THREAD and saves ISR register value |
777 | * for the threaded handler use. |
778 | * |
779 | * voting for wake thread - need at least 1 vote |
780 | */ |
781 | if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_RX) && |
782 | (wil->txrx_ops.irq_rx(irq, cookie) == IRQ_WAKE_THREAD)) |
783 | rc = IRQ_WAKE_THREAD; |
784 | |
785 | if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_TX) && |
786 | (wil->txrx_ops.irq_tx(irq, cookie) == IRQ_WAKE_THREAD)) |
787 | rc = IRQ_WAKE_THREAD; |
788 | |
789 | if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_MISC) && |
790 | (wil6210_irq_misc(irq, cookie) == IRQ_WAKE_THREAD)) |
791 | rc = IRQ_WAKE_THREAD; |
792 | |
793 | /* if thread is requested, it will unmask IRQ */ |
794 | if (rc != IRQ_WAKE_THREAD) |
795 | wil6210_unmask_irq_pseudo(wil); |
796 | |
797 | return rc; |
798 | } |
799 | |
800 | static int wil6210_request_3msi(struct wil6210_priv *wil, int irq) |
801 | { |
802 | int rc; |
803 | |
804 | /* IRQ's are in the following order: |
805 | * - Tx |
806 | * - Rx |
807 | * - Misc |
808 | */ |
809 | rc = request_irq(irq, handler: wil->txrx_ops.irq_tx, IRQF_SHARED, |
810 | WIL_NAME "_tx" , dev: wil); |
811 | if (rc) |
812 | return rc; |
813 | |
814 | rc = request_irq(irq: irq + 1, handler: wil->txrx_ops.irq_rx, IRQF_SHARED, |
815 | WIL_NAME "_rx" , dev: wil); |
816 | if (rc) |
817 | goto free0; |
818 | |
819 | rc = request_threaded_irq(irq: irq + 2, handler: wil6210_irq_misc, |
820 | thread_fn: wil6210_irq_misc_thread, |
821 | IRQF_SHARED, WIL_NAME "_misc" , dev: wil); |
822 | if (rc) |
823 | goto free1; |
824 | |
825 | return 0; |
826 | free1: |
827 | free_irq(irq + 1, wil); |
828 | free0: |
829 | free_irq(irq, wil); |
830 | |
831 | return rc; |
832 | } |
833 | |
834 | /* can't use wil_ioread32_and_clear because ICC value is not set yet */ |
835 | static inline void wil_clear32(void __iomem *addr) |
836 | { |
837 | u32 x = readl(addr); |
838 | |
839 | writel(val: x, addr); |
840 | } |
841 | |
842 | void wil6210_clear_irq(struct wil6210_priv *wil) |
843 | { |
844 | wil_clear32(addr: wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) + |
845 | offsetof(struct RGF_ICR, ICR)); |
846 | wil_clear32(addr: wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) + |
847 | offsetof(struct RGF_ICR, ICR)); |
848 | wil_clear32(addr: wil->csr + HOSTADDR(RGF_INT_GEN_RX_ICR) + |
849 | offsetof(struct RGF_ICR, ICR)); |
850 | wil_clear32(addr: wil->csr + HOSTADDR(RGF_INT_GEN_TX_ICR) + |
851 | offsetof(struct RGF_ICR, ICR)); |
852 | wil_clear32(addr: wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) + |
853 | offsetof(struct RGF_ICR, ICR)); |
854 | wmb(); /* make sure write completed */ |
855 | } |
856 | |
857 | void wil6210_set_halp(struct wil6210_priv *wil) |
858 | { |
859 | wil_dbg_irq(wil, "set_halp\n" ); |
860 | |
861 | wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, ICS), |
862 | BIT_DMA_EP_MISC_ICR_HALP); |
863 | } |
864 | |
865 | void wil6210_clear_halp(struct wil6210_priv *wil) |
866 | { |
867 | wil_dbg_irq(wil, "clear_halp\n" ); |
868 | |
869 | wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, ICR), |
870 | BIT_DMA_EP_MISC_ICR_HALP); |
871 | wil6210_unmask_halp(wil); |
872 | } |
873 | |
874 | int wil6210_init_irq(struct wil6210_priv *wil, int irq) |
875 | { |
876 | int rc; |
877 | |
878 | wil_dbg_misc(wil, "init_irq: %s, n_msi=%d\n" , |
879 | wil->n_msi ? "MSI" : "INTx" , wil->n_msi); |
880 | |
881 | if (wil->use_enhanced_dma_hw) { |
882 | wil->txrx_ops.irq_tx = wil6210_irq_tx_edma; |
883 | wil->txrx_ops.irq_rx = wil6210_irq_rx_edma; |
884 | } else { |
885 | wil->txrx_ops.irq_tx = wil6210_irq_tx; |
886 | wil->txrx_ops.irq_rx = wil6210_irq_rx; |
887 | } |
888 | |
889 | if (wil->n_msi == 3) |
890 | rc = wil6210_request_3msi(wil, irq); |
891 | else |
892 | rc = request_threaded_irq(irq, handler: wil6210_hardirq, |
893 | thread_fn: wil6210_thread_irq, |
894 | flags: wil->n_msi ? 0 : IRQF_SHARED, |
895 | WIL_NAME, dev: wil); |
896 | return rc; |
897 | } |
898 | |
899 | void wil6210_fini_irq(struct wil6210_priv *wil, int irq) |
900 | { |
901 | wil_dbg_misc(wil, "fini_irq:\n" ); |
902 | |
903 | wil_mask_irq(wil); |
904 | free_irq(irq, wil); |
905 | if (wil->n_msi == 3) { |
906 | free_irq(irq + 1, wil); |
907 | free_irq(irq + 2, wil); |
908 | } |
909 | } |
910 | |