1 /* 2 * Copyright 2014-2017 Cavium, Inc. 3 * The contents of this file are subject to the terms of the Common Development 4 * and Distribution License, v.1, (the "License"). 5 * 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the License at available 9 * at http://opensource.org/licenses/CDDL-1.0 10 * 11 * See the License for the specific language governing permissions and 12 * limitations under the License. 13 */ 14 15 /* 16 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 17 * Copyright (c) 2019, Joyent, Inc. 18 */ 19 20 #include "bnxint.h" 21 #include "bnxsnd.h" 22 #include "bnxrcv.h" 23 24 25 #define BNX_INTR_NUMBER 0 26 27 /* 28 * Name: bnx_intr_priv 29 * 30 * Input: ptr to um_device_t 31 * 32 * Return: Interrupt status 33 * 34 * Description: 35 * This routine is called from ISR and POLL API routines to consume 36 * any pending events. This function determines if there is any 37 * pending status and calls corresponding LM functions to consume 38 * the event. L2 driver consumes three events - L2 Tx compete, 39 * L2 Rx indication and link status change. 40 */ 41 static lm_interrupt_status_t 42 bnx_intr_priv(um_device_t *const umdevice) 43 { 44 u32_t idx; 45 lm_device_t *lmdevice; 46 lm_interrupt_status_t intrstat; 47 48 lmdevice = &(umdevice->lm_dev); 49 50 /* 51 * Following LM routine checks for pending interrupts and 52 * returns corresponding bits set in a 32bit integer value. 53 */ 54 intrstat = lm_get_interrupt_status(lmdevice); 55 56 if (intrstat & LM_KNOCK_KNOCK_EVENT) { 57 um_send_driver_pulse(umdevice); 58 } 59 60 if (intrstat & LM_RX_EVENT_MASK) { 61 for (idx = RX_CHAIN_IDX0; idx < NUM_RX_CHAIN; idx++) { 62 if (intrstat & (LM_RX0_EVENT_ACTIVE << idx)) { 63 s_list_t *waitq; 64 65 waitq = &(_RX_QINFO(umdevice, idx).waitq); 66 67 mutex_enter(&umdevice->os_param.rcv_mutex); 68 (void) lm_get_packets_rcvd(lmdevice, idx, 0, 69 waitq); 70 mutex_exit(&umdevice->os_param.rcv_mutex); 71 } 72 } 73 } 74 75 if (intrstat & LM_TX_EVENT_MASK) { 76 for (idx = TX_CHAIN_IDX0; idx < NUM_TX_CHAIN; idx++) { 77 if (intrstat & (LM_TX0_EVENT_ACTIVE << idx)) { 78 /* This call is mutex protected internally. */ 79 bnx_xmit_ring_intr(umdevice, idx); 80 } 81 } 82 } 83 84 if (intrstat & LM_PHY_EVENT_ACTIVE) { 85 mutex_enter(&umdevice->os_param.phy_mutex); 86 lm_service_phy_int(lmdevice, FALSE); 87 mutex_exit(&umdevice->os_param.phy_mutex); 88 } 89 90 return (intrstat); 91 } 92 93 /* 94 * Description: 95 * 96 * This function sends rx traffic up the stack and replenishes the hardware 97 * rx buffers. Although we share the responsibility of replenishing the 98 * rx buffers with the timer, we still need to wait here indefinitely. This 99 * is the only place where we send rx traffic back up the stack. 100 * 101 * We go through a lot of mental gymnastics to make sure we are not holding a 102 * lock while calling gld_recv(). We can deadlock in the following scenario 103 * if we aren't careful : 104 * 105 * Thread 1: 106 * bnx_intr_disable() 107 * bnx_intr_wait() 108 * mutex_enter(intr_*_mutex) 109 * 110 * Thread 2: 111 * bnx_intr_[soft|1lvl]() 112 * bnx_intr_recv() 113 * mutex_enter(rcv_mutex) 114 * 115 * Thread 3: 116 * bnx_intr_[soft|1lvl]() 117 * mutex_enter(intr_*_mutex) 118 * mutex_enter(rcv_mutex) 119 * 120 * Return: 121 */ 122 static void 123 bnx_intr_recv(um_device_t * const umdevice) 124 { 125 mutex_enter(&umdevice->os_param.rcv_mutex); 126 127 if (umdevice->intr_enabled == B_TRUE) { 128 /* 129 * Send the rx packets up. This function will release and 130 * acquire the receive mutex across calls to gld_recv(). 131 */ 132 bnx_rxpkts_intr(umdevice); 133 } 134 135 /* 136 * Since gld_recv() can hang while decommisioning the driver, we 137 * need to double check that interrupts are still enabled before 138 * attempting to replenish the rx buffers. 139 */ 140 if (umdevice->intr_enabled == B_TRUE) { 141 /* This function does an implicit *_fill(). */ 142 bnx_rxpkts_post(umdevice); 143 } 144 145 mutex_exit(&umdevice->os_param.rcv_mutex); 146 } 147 148 static void 149 bnx_intr_xmit(um_device_t *const umdevice) 150 { 151 mutex_enter(&umdevice->os_param.xmit_mutex); 152 153 if (umdevice->intr_enabled == B_TRUE) { 154 /* 155 * Send the tx packets in waitq & notify the GLD. 156 */ 157 bnx_txpkts_intr(umdevice); 158 } 159 160 mutex_exit(&umdevice->os_param.xmit_mutex); 161 } 162 163 static unsigned int 164 bnx_intr_1lvl(caddr_t arg1, caddr_t arg2) 165 { 166 lm_device_t *lmdevice; 167 um_device_t *umdevice; 168 lm_interrupt_status_t intrstat = 0; 169 u32_t value32; 170 umdevice = (um_device_t *)arg1; 171 172 lmdevice = &(umdevice->lm_dev); 173 174 mutex_enter(&umdevice->intr_mutex); 175 176 if (umdevice->intr_enabled != B_TRUE) { 177 /* 178 * The interrupt cannot be ours. Interrupts 179 * from our device have been disabled. 180 */ 181 mutex_exit(&umdevice->intr_mutex); 182 umdevice->intr_in_disabled++; 183 return (DDI_INTR_UNCLAIMED); 184 } 185 186 /* Make sure we are working with current data. */ 187 (void) ddi_dma_sync(*(umdevice->os_param.status_block_dma_hdl), 0, 188 STATUS_BLOCK_BUFFER_SIZE, DDI_DMA_SYNC_FORKERNEL); 189 190 /* Make sure it is our device that is interrupting. */ 191 if (lmdevice->vars.status_virt->deflt.status_idx == 192 umdevice->dev_var.processed_status_idx) { 193 /* 194 * It is possible that we could have arrived at the ISR 195 * before the status block had a chance to be DMA'd into 196 * host memory. Reading the status of the INTA line will 197 * implicitly force the DMA, and inform us of whether we 198 * are truly interrupting. INTA is active low. 199 */ 200 REG_RD(lmdevice, pci_config.pcicfg_misc_status, &value32); 201 if (value32 & PCICFG_MISC_STATUS_INTA_VALUE) { 202 /* This isn't our interrupt. */ 203 umdevice->intr_no_change++; 204 mutex_exit(&umdevice->intr_mutex); 205 return (DDI_INTR_UNCLAIMED); 206 } 207 } 208 209 umdevice->intrFired++; 210 211 /* Disable interrupt and enqueue soft intr processing. */ 212 REG_WR(lmdevice, pci_config.pcicfg_int_ack_cmd, 213 (PCICFG_INT_ACK_CMD_USE_INT_HC_PARAM | 214 PCICFG_INT_ACK_CMD_MASK_INT)); 215 216 FLUSHPOSTEDWRITES(lmdevice); 217 218 umdevice->dev_var.processed_status_idx = 219 lmdevice->vars.status_virt->deflt.status_idx; 220 221 /* Service the interrupts. */ 222 intrstat = bnx_intr_priv(umdevice); 223 224 value32 = umdevice->dev_var.processed_status_idx; 225 value32 |= PCICFG_INT_ACK_CMD_INDEX_VALID; 226 227 /* 228 * Inform the hardware of the last interrupt event we processed 229 * and reinstate the hardware's ability to assert interrupts. 230 */ 231 REG_WR(lmdevice, pci_config.pcicfg_int_ack_cmd, value32); 232 233 FLUSHPOSTEDWRITES(lmdevice); 234 235 umdevice->intr_count++; 236 237 if (intrstat & LM_RX_EVENT_MASK) { 238 bnx_intr_recv(umdevice); 239 } 240 241 if (intrstat & LM_TX_EVENT_MASK) { 242 bnx_intr_xmit(umdevice); 243 } 244 245 mutex_exit(&umdevice->intr_mutex); 246 247 return (DDI_INTR_CLAIMED); 248 } 249 250 void 251 bnx_intr_enable(um_device_t * const umdevice) 252 { 253 int rc; 254 255 umdevice->intr_count = 0; 256 257 /* 258 * Allow interrupts to touch the hardware. 259 */ 260 umdevice->intr_enabled = B_TRUE; 261 262 if ((rc = ddi_intr_enable(umdevice->pIntrBlock[0])) != DDI_SUCCESS) { 263 cmn_err(CE_WARN, "%s: Failed to enable default isr block (%d)", 264 umdevice->dev_name, rc); 265 return; /* XXX return error */ 266 } 267 268 /* Allow the hardware to generate interrupts. */ 269 lm_enable_int(&(umdevice->lm_dev)); 270 271 FLUSHPOSTEDWRITES(&(umdevice->lm_dev)); 272 273 /* 274 * XXX This delay is here because of a discovered problem regarding a 275 * call to ddi_intr_disable immediately after enabling interrupts. This 276 * can occur with the "ifconfig -a plumb up" command which brings an 277 * interface up/down/up/down/up. There seems to be a race condition 278 * between the ddi_intr_enable/lm_enable_int and ddi_intr_disable 279 * routines that results in interrupts to no longer fire on the 280 * interface and a REBOOT IS REQUIRED to fix! 281 */ 282 drv_usecwait(2000000); 283 } 284 285 /* 286 * Description: 287 * 288 * This function makes sure the ISR no longer touches the hardware. It 289 * accomplishes this by making sure the ISR either completes, or that it 290 * acknowledges the intr_enabled status change. 291 * 292 * Return: 293 */ 294 static void 295 bnx_intr_wait(um_device_t * const umdevice) 296 { 297 if (mutex_tryenter(&umdevice->intr_mutex)) { 298 /* 299 * If we were able to get the hardware interrupt mutex, then it 300 * means that either the ISR wasn't processing at this time, or 301 * that it was at the end, processing the receive packets. If it 302 * the latter case, then all we need to do is acquire the 303 * rcv_mutex. If we can acquire it, it means the receive 304 * processing is stalled, waiting for a GLD mutex, or that the 305 * ISR is not processing RX packets. 306 */ 307 mutex_enter(&umdevice->os_param.rcv_mutex); 308 mutex_exit(&umdevice->os_param.rcv_mutex); 309 } else { 310 /* 311 * We couldn't acquire the hardware interrupt mutex. This means 312 * the ISR is running. Wait for it to complete by 313 * (re)attempting to acquire the interrupt mutex. Whether we 314 * acquire it immediately or not, we will know the ISR has 315 * acknowledged the intr_enabled status change. 316 */ 317 mutex_enter(&umdevice->intr_mutex); 318 } 319 mutex_exit(&umdevice->intr_mutex); 320 } 321 322 323 void 324 bnx_intr_disable(um_device_t * const umdevice) 325 { 326 int rc; 327 328 /* 329 * Prevent any future interrupts to no longer touch the hardware. 330 */ 331 umdevice->intr_enabled = B_FALSE; 332 333 /* 334 * Wait for any currently running interrupt to complete. 335 */ 336 bnx_intr_wait(umdevice); 337 338 /* Stop the device from generating any interrupts. */ 339 lm_disable_int(&(umdevice->lm_dev)); 340 341 FLUSHPOSTEDWRITES(&(umdevice->lm_dev)); 342 343 if ((rc = ddi_intr_disable(umdevice->pIntrBlock[0])) != DDI_SUCCESS) { 344 cmn_err(CE_WARN, "%s: Failed to disable default isr (%d)", 345 umdevice->dev_name, rc); 346 } 347 } 348 349 int 350 bnxIntrInit(um_device_t *umdevice) 351 { 352 dev_info_t *pDev = umdevice->os_param.dip; 353 int intrActual, rc; 354 355 if ((umdevice->pIntrBlock = kmem_zalloc(sizeof (ddi_intr_handle_t), 356 KM_SLEEP)) == NULL) { 357 cmn_err(CE_WARN, "%s: Failed to allocate interrupt handle " 358 "block!", umdevice->dev_name); 359 return (-1); 360 } 361 362 umdevice->intrType = (umdevice->dev_var.disableMsix) ? 363 DDI_INTR_TYPE_FIXED : DDI_INTR_TYPE_MSIX; 364 365 while (1) { 366 if ((rc = ddi_intr_alloc(pDev, umdevice->pIntrBlock, 367 umdevice->intrType, 0, 1, &intrActual, 368 DDI_INTR_ALLOC_NORMAL)) != DDI_SUCCESS) { 369 cmn_err(CE_WARN, "!%s: Failed to initialize default " 370 "%s isr handle block (%d)", umdevice->dev_name, 371 (umdevice->intrType == DDI_INTR_TYPE_MSIX) ? 372 "MSIX" : "Fixed", rc); 373 374 if (umdevice->intrType == DDI_INTR_TYPE_MSIX) { 375 cmn_err(CE_WARN, "!%s: Reverting to Fixed " 376 "level interrupts", umdevice->dev_name); 377 378 umdevice->intrType = DDI_INTR_TYPE_FIXED; 379 continue; 380 } else { 381 kmem_free(umdevice->pIntrBlock, 382 sizeof (ddi_intr_handle_t)); 383 return (-1); 384 } 385 } 386 break; 387 } 388 389 if (intrActual != 1) { 390 cmn_err(CE_WARN, "%s: Failed to alloc minimum default " 391 "isr handler!", umdevice->dev_name); 392 (void) ddi_intr_free(umdevice->pIntrBlock[0]); 393 kmem_free(umdevice->pIntrBlock, sizeof (ddi_intr_handle_t)); 394 return (-1); 395 } 396 397 if ((rc = ddi_intr_get_pri(umdevice->pIntrBlock[0], 398 &umdevice->intrPriority)) != DDI_SUCCESS) { 399 cmn_err(CE_WARN, "%s: Failed to get isr priority (%d)", 400 umdevice->dev_name, rc); 401 (void) ddi_intr_free(umdevice->pIntrBlock[0]); 402 kmem_free(umdevice->pIntrBlock, sizeof (ddi_intr_handle_t)); 403 return (-1); 404 } 405 406 if (umdevice->intrPriority >= ddi_intr_get_hilevel_pri()) { 407 cmn_err(CE_WARN, "%s: Interrupt priority is too high", 408 umdevice->dev_name); 409 (void) ddi_intr_free(umdevice->pIntrBlock[0]); 410 kmem_free(umdevice->pIntrBlock, sizeof (ddi_intr_handle_t)); 411 return (-1); 412 } 413 414 if ((rc = ddi_intr_add_handler(umdevice->pIntrBlock[0], bnx_intr_1lvl, 415 (caddr_t)umdevice, NULL)) != DDI_SUCCESS) { 416 cmn_err(CE_WARN, "%s: Failed to add the default isr " 417 "handler (%d)", umdevice->dev_name, rc); 418 (void) ddi_intr_free(umdevice->pIntrBlock[0]); 419 kmem_free(umdevice->pIntrBlock, sizeof (ddi_intr_handle_t)); 420 return (-1); 421 } 422 423 /* Intialize the mutex used by the hardware interrupt handler. */ 424 mutex_init(&umdevice->intr_mutex, NULL, MUTEX_DRIVER, 425 DDI_INTR_PRI(umdevice->intrPriority)); 426 427 umdevice->lm_dev.vars.interrupt_mode = 428 (umdevice->intrType == DDI_INTR_TYPE_FIXED) ? 429 IRQ_MODE_LINE_BASED : IRQ_MODE_MSIX_BASED; 430 return (0); 431 } 432 433 void 434 bnxIntrFini(um_device_t *umdevice) 435 { 436 int ret; 437 438 if ((ret = ddi_intr_disable(umdevice->pIntrBlock[0])) != 0) { 439 dev_err(umdevice->os_param.dip, CE_WARN, 440 "failed to disable interrupt: %d", ret); 441 } 442 if ((ret = ddi_intr_remove_handler(umdevice->pIntrBlock[0])) != 0) { 443 dev_err(umdevice->os_param.dip, CE_WARN, 444 "failed to remove interrupt: %d", ret); 445 } 446 if ((ret = ddi_intr_free(umdevice->pIntrBlock[0])) != 0) { 447 dev_err(umdevice->os_param.dip, CE_WARN, 448 "failed to free interrupt: %d", ret); 449 } 450 kmem_free(umdevice->pIntrBlock, sizeof (ddi_intr_handle_t)); 451 452 umdevice->pIntrBlock = NULL; 453 454 mutex_destroy(&umdevice->intr_mutex); 455 } 456