1 /* 2 * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include <linux/interrupt.h> 18 19 #include "wil6210.h" 20 #include "trace.h" 21 22 /** 23 * Theory of operation: 24 * 25 * There is ISR pseudo-cause register, 26 * dma_rgf->DMA_RGF.PSEUDO_CAUSE.PSEUDO_CAUSE 27 * Its bits represents OR'ed bits from 3 real ISR registers: 28 * TX, RX, and MISC. 29 * 30 * Registers may be configured to either "write 1 to clear" or 31 * "clear on read" mode 32 * 33 * When handling interrupt, one have to mask/unmask interrupts for the 34 * real ISR registers, or hardware may malfunction. 35 * 36 */ 37 38 #define WIL6210_IRQ_DISABLE (0xFFFFFFFFUL) 39 #define WIL6210_IMC_RX (BIT_DMA_EP_RX_ICR_RX_DONE | \ 40 BIT_DMA_EP_RX_ICR_RX_HTRSH) 41 #define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \ 42 BIT_DMA_EP_TX_ICR_TX_DONE_N(0)) 43 #define WIL6210_IMC_MISC (ISR_MISC_FW_READY | \ 44 ISR_MISC_MBOX_EVT | \ 45 ISR_MISC_FW_ERROR) 46 47 #define WIL6210_IRQ_PSEUDO_MASK (u32)(~(BIT_DMA_PSEUDO_CAUSE_RX | \ 48 BIT_DMA_PSEUDO_CAUSE_TX | \ 49 BIT_DMA_PSEUDO_CAUSE_MISC)) 50 51 #if defined(CONFIG_WIL6210_ISR_COR) 52 /* configure to Clear-On-Read mode */ 53 #define WIL_ICR_ICC_VALUE (0xFFFFFFFFUL) 54 55 static inline void wil_icr_clear(u32 x, void __iomem *addr) 56 { 57 } 58 #else /* defined(CONFIG_WIL6210_ISR_COR) */ 59 /* configure to Write-1-to-Clear mode */ 60 #define WIL_ICR_ICC_VALUE (0UL) 61 62 static inline void wil_icr_clear(u32 x, void __iomem *addr) 63 { 64 iowrite32(x, addr); 65 } 66 #endif /* defined(CONFIG_WIL6210_ISR_COR) */ 67 68 static inline u32 wil_ioread32_and_clear(void __iomem *addr) 69 { 70 u32 x = ioread32(addr); 71 72 wil_icr_clear(x, addr); 73 74 return x; 75 } 76 77 static void wil6210_mask_irq_tx(struct wil6210_priv *wil) 78 { 79 iowrite32(WIL6210_IRQ_DISABLE, wil->csr + 80 HOSTADDR(RGF_DMA_EP_TX_ICR) + 81 offsetof(struct RGF_ICR, IMS)); 82 } 83 84 static void wil6210_mask_irq_rx(struct wil6210_priv *wil) 85 { 86 iowrite32(WIL6210_IRQ_DISABLE, wil->csr + 87 HOSTADDR(RGF_DMA_EP_RX_ICR) + 88 offsetof(struct RGF_ICR, IMS)); 89 } 90 91 static void wil6210_mask_irq_misc(struct wil6210_priv *wil) 92 { 93 iowrite32(WIL6210_IRQ_DISABLE, wil->csr + 94 HOSTADDR(RGF_DMA_EP_MISC_ICR) + 95 offsetof(struct RGF_ICR, IMS)); 96 } 97 98 static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil) 99 { 100 wil_dbg_irq(wil, "%s()\n", __func__); 101 102 iowrite32(WIL6210_IRQ_DISABLE, wil->csr + 103 HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); 104 105 clear_bit(wil_status_irqen, wil->status); 106 } 107 108 void wil6210_unmask_irq_tx(struct wil6210_priv *wil) 109 { 110 iowrite32(WIL6210_IMC_TX, wil->csr + 111 HOSTADDR(RGF_DMA_EP_TX_ICR) + 112 offsetof(struct RGF_ICR, IMC)); 113 } 114 115 void wil6210_unmask_irq_rx(struct wil6210_priv *wil) 116 { 117 iowrite32(WIL6210_IMC_RX, wil->csr + 118 HOSTADDR(RGF_DMA_EP_RX_ICR) + 119 offsetof(struct RGF_ICR, IMC)); 120 } 121 122 static void wil6210_unmask_irq_misc(struct wil6210_priv *wil) 123 { 124 iowrite32(WIL6210_IMC_MISC, wil->csr + 125 HOSTADDR(RGF_DMA_EP_MISC_ICR) + 126 offsetof(struct RGF_ICR, IMC)); 127 } 128 129 static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil) 130 { 131 wil_dbg_irq(wil, "%s()\n", __func__); 132 133 set_bit(wil_status_irqen, wil->status); 134 135 iowrite32(WIL6210_IRQ_PSEUDO_MASK, wil->csr + 136 HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); 137 } 138 139 void wil_mask_irq(struct wil6210_priv *wil) 140 { 141 wil_dbg_irq(wil, "%s()\n", __func__); 142 143 wil6210_mask_irq_tx(wil); 144 wil6210_mask_irq_rx(wil); 145 wil6210_mask_irq_misc(wil); 146 wil6210_mask_irq_pseudo(wil); 147 } 148 149 void wil_unmask_irq(struct wil6210_priv *wil) 150 { 151 wil_dbg_irq(wil, "%s()\n", __func__); 152 153 iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) + 154 offsetof(struct RGF_ICR, ICC)); 155 iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) + 156 offsetof(struct RGF_ICR, ICC)); 157 iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) + 158 offsetof(struct RGF_ICR, ICC)); 159 160 wil6210_unmask_irq_pseudo(wil); 161 wil6210_unmask_irq_tx(wil); 162 wil6210_unmask_irq_rx(wil); 163 wil6210_unmask_irq_misc(wil); 164 } 165 166 /* target write operation */ 167 #define W(a, v) do { iowrite32(v, wil->csr + HOSTADDR(a)); wmb(); } while (0) 168 169 void wil_configure_interrupt_moderation(struct wil6210_priv *wil) 170 { 171 wil_dbg_irq(wil, "%s()\n", __func__); 172 173 /* disable interrupt moderation for monitor 174 * to get better timestamp precision 175 */ 176 if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) 177 return; 178 179 /* Disable and clear tx counter before (re)configuration */ 180 W(RGF_DMA_ITR_TX_CNT_CTL, BIT_DMA_ITR_TX_CNT_CTL_CLR); 181 W(RGF_DMA_ITR_TX_CNT_TRSH, wil->tx_max_burst_duration); 182 wil_info(wil, "set ITR_TX_CNT_TRSH = %d usec\n", 183 wil->tx_max_burst_duration); 184 /* Configure TX max burst duration timer to use usec units */ 185 W(RGF_DMA_ITR_TX_CNT_CTL, 186 BIT_DMA_ITR_TX_CNT_CTL_EN | BIT_DMA_ITR_TX_CNT_CTL_EXT_TIC_SEL); 187 188 /* Disable and clear tx idle counter before (re)configuration */ 189 W(RGF_DMA_ITR_TX_IDL_CNT_CTL, BIT_DMA_ITR_TX_IDL_CNT_CTL_CLR); 190 W(RGF_DMA_ITR_TX_IDL_CNT_TRSH, wil->tx_interframe_timeout); 191 wil_info(wil, "set ITR_TX_IDL_CNT_TRSH = %d usec\n", 192 wil->tx_interframe_timeout); 193 /* Configure TX max burst duration timer to use usec units */ 194 W(RGF_DMA_ITR_TX_IDL_CNT_CTL, BIT_DMA_ITR_TX_IDL_CNT_CTL_EN | 195 BIT_DMA_ITR_TX_IDL_CNT_CTL_EXT_TIC_SEL); 196 197 /* Disable and clear rx counter before (re)configuration */ 198 W(RGF_DMA_ITR_RX_CNT_CTL, BIT_DMA_ITR_RX_CNT_CTL_CLR); 199 W(RGF_DMA_ITR_RX_CNT_TRSH, wil->rx_max_burst_duration); 200 wil_info(wil, "set ITR_RX_CNT_TRSH = %d usec\n", 201 wil->rx_max_burst_duration); 202 /* Configure TX max burst duration timer to use usec units */ 203 W(RGF_DMA_ITR_RX_CNT_CTL, 204 BIT_DMA_ITR_RX_CNT_CTL_EN | BIT_DMA_ITR_RX_CNT_CTL_EXT_TIC_SEL); 205 206 /* Disable and clear rx idle counter before (re)configuration */ 207 W(RGF_DMA_ITR_RX_IDL_CNT_CTL, BIT_DMA_ITR_RX_IDL_CNT_CTL_CLR); 208 W(RGF_DMA_ITR_RX_IDL_CNT_TRSH, wil->rx_interframe_timeout); 209 wil_info(wil, "set ITR_RX_IDL_CNT_TRSH = %d usec\n", 210 wil->rx_interframe_timeout); 211 /* Configure TX max burst duration timer to use usec units */ 212 W(RGF_DMA_ITR_RX_IDL_CNT_CTL, BIT_DMA_ITR_RX_IDL_CNT_CTL_EN | 213 BIT_DMA_ITR_RX_IDL_CNT_CTL_EXT_TIC_SEL); 214 } 215 216 #undef W 217 218 static irqreturn_t wil6210_irq_rx(int irq, void *cookie) 219 { 220 struct wil6210_priv *wil = cookie; 221 u32 isr = wil_ioread32_and_clear(wil->csr + 222 HOSTADDR(RGF_DMA_EP_RX_ICR) + 223 offsetof(struct RGF_ICR, ICR)); 224 bool need_unmask = true; 225 226 trace_wil6210_irq_rx(isr); 227 wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr); 228 229 if (unlikely(!isr)) { 230 wil_err(wil, "spurious IRQ: RX\n"); 231 return IRQ_NONE; 232 } 233 234 wil6210_mask_irq_rx(wil); 235 236 /* RX_DONE and RX_HTRSH interrupts are the same if interrupt 237 * moderation is not used. Interrupt moderation may cause RX 238 * buffer overflow while RX_DONE is delayed. The required 239 * action is always the same - should empty the accumulated 240 * packets from the RX ring. 241 */ 242 if (likely(isr & (BIT_DMA_EP_RX_ICR_RX_DONE | 243 BIT_DMA_EP_RX_ICR_RX_HTRSH))) { 244 wil_dbg_irq(wil, "RX done\n"); 245 246 if (unlikely(isr & BIT_DMA_EP_RX_ICR_RX_HTRSH)) 247 wil_err_ratelimited(wil, 248 "Received \"Rx buffer is in risk of overflow\" interrupt\n"); 249 250 isr &= ~(BIT_DMA_EP_RX_ICR_RX_DONE | 251 BIT_DMA_EP_RX_ICR_RX_HTRSH); 252 if (likely(test_bit(wil_status_reset_done, wil->status))) { 253 if (likely(test_bit(wil_status_napi_en, wil->status))) { 254 wil_dbg_txrx(wil, "NAPI(Rx) schedule\n"); 255 need_unmask = false; 256 napi_schedule(&wil->napi_rx); 257 } else { 258 wil_err(wil, 259 "Got Rx interrupt while stopping interface\n"); 260 } 261 } else { 262 wil_err(wil, "Got Rx interrupt while in reset\n"); 263 } 264 } 265 266 if (unlikely(isr)) 267 wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr); 268 269 /* Rx IRQ will be enabled when NAPI processing finished */ 270 271 atomic_inc(&wil->isr_count_rx); 272 273 if (unlikely(need_unmask)) 274 wil6210_unmask_irq_rx(wil); 275 276 return IRQ_HANDLED; 277 } 278 279 static irqreturn_t wil6210_irq_tx(int irq, void *cookie) 280 { 281 struct wil6210_priv *wil = cookie; 282 u32 isr = wil_ioread32_and_clear(wil->csr + 283 HOSTADDR(RGF_DMA_EP_TX_ICR) + 284 offsetof(struct RGF_ICR, ICR)); 285 bool need_unmask = true; 286 287 trace_wil6210_irq_tx(isr); 288 wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr); 289 290 if (unlikely(!isr)) { 291 wil_err(wil, "spurious IRQ: TX\n"); 292 return IRQ_NONE; 293 } 294 295 wil6210_mask_irq_tx(wil); 296 297 if (likely(isr & BIT_DMA_EP_TX_ICR_TX_DONE)) { 298 wil_dbg_irq(wil, "TX done\n"); 299 isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; 300 /* clear also all VRING interrupts */ 301 isr &= ~(BIT(25) - 1UL); 302 if (likely(test_bit(wil_status_reset_done, wil->status))) { 303 wil_dbg_txrx(wil, "NAPI(Tx) schedule\n"); 304 need_unmask = false; 305 napi_schedule(&wil->napi_tx); 306 } else { 307 wil_err(wil, "Got Tx interrupt while in reset\n"); 308 } 309 } 310 311 if (unlikely(isr)) 312 wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr); 313 314 /* Tx IRQ will be enabled when NAPI processing finished */ 315 316 atomic_inc(&wil->isr_count_tx); 317 318 if (unlikely(need_unmask)) 319 wil6210_unmask_irq_tx(wil); 320 321 return IRQ_HANDLED; 322 } 323 324 static void wil_notify_fw_error(struct wil6210_priv *wil) 325 { 326 struct device *dev = &wil_to_ndev(wil)->dev; 327 char *envp[3] = { 328 [0] = "SOURCE=wil6210", 329 [1] = "EVENT=FW_ERROR", 330 [2] = NULL, 331 }; 332 wil_err(wil, "Notify about firmware error\n"); 333 kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); 334 } 335 336 static void wil_cache_mbox_regs(struct wil6210_priv *wil) 337 { 338 /* make shadow copy of registers that should not change on run time */ 339 wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, 340 sizeof(struct wil6210_mbox_ctl)); 341 wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx); 342 wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); 343 } 344 345 static irqreturn_t wil6210_irq_misc(int irq, void *cookie) 346 { 347 struct wil6210_priv *wil = cookie; 348 u32 isr = wil_ioread32_and_clear(wil->csr + 349 HOSTADDR(RGF_DMA_EP_MISC_ICR) + 350 offsetof(struct RGF_ICR, ICR)); 351 352 trace_wil6210_irq_misc(isr); 353 wil_dbg_irq(wil, "ISR MISC 0x%08x\n", isr); 354 355 if (!isr) { 356 wil_err(wil, "spurious IRQ: MISC\n"); 357 return IRQ_NONE; 358 } 359 360 wil6210_mask_irq_misc(wil); 361 362 if (isr & ISR_MISC_FW_ERROR) { 363 wil_err(wil, "Firmware error detected\n"); 364 clear_bit(wil_status_fwready, wil->status); 365 /* 366 * do not clear @isr here - we do 2-nd part in thread 367 * there, user space get notified, and it should be done 368 * in non-atomic context 369 */ 370 } 371 372 if (isr & ISR_MISC_FW_READY) { 373 wil_dbg_irq(wil, "IRQ: FW ready\n"); 374 wil_cache_mbox_regs(wil); 375 set_bit(wil_status_reset_done, wil->status); 376 /** 377 * Actual FW ready indicated by the 378 * WMI_FW_READY_EVENTID 379 */ 380 isr &= ~ISR_MISC_FW_READY; 381 } 382 383 wil->isr_misc = isr; 384 385 if (isr) { 386 return IRQ_WAKE_THREAD; 387 } else { 388 wil6210_unmask_irq_misc(wil); 389 return IRQ_HANDLED; 390 } 391 } 392 393 static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) 394 { 395 struct wil6210_priv *wil = cookie; 396 u32 isr = wil->isr_misc; 397 398 trace_wil6210_irq_misc_thread(isr); 399 wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr); 400 401 if (isr & ISR_MISC_FW_ERROR) { 402 wil_notify_fw_error(wil); 403 isr &= ~ISR_MISC_FW_ERROR; 404 wil_fw_error_recovery(wil); 405 } 406 407 if (isr & ISR_MISC_MBOX_EVT) { 408 wil_dbg_irq(wil, "MBOX event\n"); 409 wmi_recv_cmd(wil); 410 isr &= ~ISR_MISC_MBOX_EVT; 411 } 412 413 if (isr) 414 wil_dbg_irq(wil, "un-handled MISC ISR bits 0x%08x\n", isr); 415 416 wil->isr_misc = 0; 417 418 wil6210_unmask_irq_misc(wil); 419 420 return IRQ_HANDLED; 421 } 422 423 /** 424 * thread IRQ handler 425 */ 426 static irqreturn_t wil6210_thread_irq(int irq, void *cookie) 427 { 428 struct wil6210_priv *wil = cookie; 429 430 wil_dbg_irq(wil, "Thread IRQ\n"); 431 /* Discover real IRQ cause */ 432 if (wil->isr_misc) 433 wil6210_irq_misc_thread(irq, cookie); 434 435 wil6210_unmask_irq_pseudo(wil); 436 437 return IRQ_HANDLED; 438 } 439 440 /* DEBUG 441 * There is subtle bug in hardware that causes IRQ to raise when it should be 442 * masked. It is quite rare and hard to debug. 443 * 444 * Catch irq issue if it happens and print all I can. 445 */ 446 static int wil6210_debug_irq_mask(struct wil6210_priv *wil, u32 pseudo_cause) 447 { 448 if (!test_bit(wil_status_irqen, wil->status)) { 449 u32 icm_rx = wil_ioread32_and_clear(wil->csr + 450 HOSTADDR(RGF_DMA_EP_RX_ICR) + 451 offsetof(struct RGF_ICR, ICM)); 452 u32 icr_rx = wil_ioread32_and_clear(wil->csr + 453 HOSTADDR(RGF_DMA_EP_RX_ICR) + 454 offsetof(struct RGF_ICR, ICR)); 455 u32 imv_rx = ioread32(wil->csr + 456 HOSTADDR(RGF_DMA_EP_RX_ICR) + 457 offsetof(struct RGF_ICR, IMV)); 458 u32 icm_tx = wil_ioread32_and_clear(wil->csr + 459 HOSTADDR(RGF_DMA_EP_TX_ICR) + 460 offsetof(struct RGF_ICR, ICM)); 461 u32 icr_tx = wil_ioread32_and_clear(wil->csr + 462 HOSTADDR(RGF_DMA_EP_TX_ICR) + 463 offsetof(struct RGF_ICR, ICR)); 464 u32 imv_tx = ioread32(wil->csr + 465 HOSTADDR(RGF_DMA_EP_TX_ICR) + 466 offsetof(struct RGF_ICR, IMV)); 467 u32 icm_misc = wil_ioread32_and_clear(wil->csr + 468 HOSTADDR(RGF_DMA_EP_MISC_ICR) + 469 offsetof(struct RGF_ICR, ICM)); 470 u32 icr_misc = wil_ioread32_and_clear(wil->csr + 471 HOSTADDR(RGF_DMA_EP_MISC_ICR) + 472 offsetof(struct RGF_ICR, ICR)); 473 u32 imv_misc = ioread32(wil->csr + 474 HOSTADDR(RGF_DMA_EP_MISC_ICR) + 475 offsetof(struct RGF_ICR, IMV)); 476 wil_err(wil, "IRQ when it should be masked: pseudo 0x%08x\n" 477 "Rx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" 478 "Tx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" 479 "Misc icm:icr:imv 0x%08x 0x%08x 0x%08x\n", 480 pseudo_cause, 481 icm_rx, icr_rx, imv_rx, 482 icm_tx, icr_tx, imv_tx, 483 icm_misc, icr_misc, imv_misc); 484 485 return -EINVAL; 486 } 487 488 return 0; 489 } 490 491 static irqreturn_t wil6210_hardirq(int irq, void *cookie) 492 { 493 irqreturn_t rc = IRQ_HANDLED; 494 struct wil6210_priv *wil = cookie; 495 u32 pseudo_cause = ioread32(wil->csr + HOSTADDR(RGF_DMA_PSEUDO_CAUSE)); 496 497 /** 498 * pseudo_cause is Clear-On-Read, no need to ACK 499 */ 500 if (unlikely((pseudo_cause == 0) || ((pseudo_cause & 0xff) == 0xff))) 501 return IRQ_NONE; 502 503 /* FIXME: IRQ mask debug */ 504 if (unlikely(wil6210_debug_irq_mask(wil, pseudo_cause))) 505 return IRQ_NONE; 506 507 trace_wil6210_irq_pseudo(pseudo_cause); 508 wil_dbg_irq(wil, "Pseudo IRQ 0x%08x\n", pseudo_cause); 509 510 wil6210_mask_irq_pseudo(wil); 511 512 /* Discover real IRQ cause 513 * There are 2 possible phases for every IRQ: 514 * - hard IRQ handler called right here 515 * - threaded handler called later 516 * 517 * Hard IRQ handler reads and clears ISR. 518 * 519 * If threaded handler requested, hard IRQ handler 520 * returns IRQ_WAKE_THREAD and saves ISR register value 521 * for the threaded handler use. 522 * 523 * voting for wake thread - need at least 1 vote 524 */ 525 if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_RX) && 526 (wil6210_irq_rx(irq, cookie) == IRQ_WAKE_THREAD)) 527 rc = IRQ_WAKE_THREAD; 528 529 if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_TX) && 530 (wil6210_irq_tx(irq, cookie) == IRQ_WAKE_THREAD)) 531 rc = IRQ_WAKE_THREAD; 532 533 if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_MISC) && 534 (wil6210_irq_misc(irq, cookie) == IRQ_WAKE_THREAD)) 535 rc = IRQ_WAKE_THREAD; 536 537 /* if thread is requested, it will unmask IRQ */ 538 if (rc != IRQ_WAKE_THREAD) 539 wil6210_unmask_irq_pseudo(wil); 540 541 return rc; 542 } 543 544 static int wil6210_request_3msi(struct wil6210_priv *wil, int irq) 545 { 546 int rc; 547 /* 548 * IRQ's are in the following order: 549 * - Tx 550 * - Rx 551 * - Misc 552 */ 553 554 rc = request_irq(irq, wil6210_irq_tx, IRQF_SHARED, 555 WIL_NAME"_tx", wil); 556 if (rc) 557 return rc; 558 559 rc = request_irq(irq + 1, wil6210_irq_rx, IRQF_SHARED, 560 WIL_NAME"_rx", wil); 561 if (rc) 562 goto free0; 563 564 rc = request_threaded_irq(irq + 2, wil6210_irq_misc, 565 wil6210_irq_misc_thread, 566 IRQF_SHARED, WIL_NAME"_misc", wil); 567 if (rc) 568 goto free1; 569 570 return 0; 571 /* error branch */ 572 free1: 573 free_irq(irq + 1, wil); 574 free0: 575 free_irq(irq, wil); 576 577 return rc; 578 } 579 580 /* can't use wil_ioread32_and_clear because ICC value is not set yet */ 581 static inline void wil_clear32(void __iomem *addr) 582 { 583 u32 x = ioread32(addr); 584 585 iowrite32(x, addr); 586 } 587 588 void wil6210_clear_irq(struct wil6210_priv *wil) 589 { 590 wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) + 591 offsetof(struct RGF_ICR, ICR)); 592 wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) + 593 offsetof(struct RGF_ICR, ICR)); 594 wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) + 595 offsetof(struct RGF_ICR, ICR)); 596 wmb(); /* make sure write completed */ 597 } 598 599 int wil6210_init_irq(struct wil6210_priv *wil, int irq) 600 { 601 int rc; 602 603 wil_dbg_misc(wil, "%s() n_msi=%d\n", __func__, wil->n_msi); 604 605 if (wil->n_msi == 3) 606 rc = wil6210_request_3msi(wil, irq); 607 else 608 rc = request_threaded_irq(irq, wil6210_hardirq, 609 wil6210_thread_irq, 610 wil->n_msi ? 0 : IRQF_SHARED, 611 WIL_NAME, wil); 612 return rc; 613 } 614 615 void wil6210_fini_irq(struct wil6210_priv *wil, int irq) 616 { 617 wil_dbg_misc(wil, "%s()\n", __func__); 618 619 wil_mask_irq(wil); 620 free_irq(irq, wil); 621 if (wil->n_msi == 3) { 622 free_irq(irq + 1, wil); 623 free_irq(irq + 2, wil); 624 } 625 } 626