1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2010 Emulex. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Source file interrupt registration 29 * and related helper functions 30 */ 31 32 #include <oce_impl.h> 33 34 static int oce_setup_msix(struct oce_dev *dev); 35 static int oce_teardown_msix(struct oce_dev *dev); 36 static int oce_add_msix_handlers(struct oce_dev *dev); 37 static void oce_del_msix_handlers(struct oce_dev *dev); 38 static uint_t oce_isr(caddr_t arg1, caddr_t arg2); 39 40 static int oce_setup_intx(struct oce_dev *dev); 41 static int oce_teardown_intx(struct oce_dev *dev); 42 static int oce_add_intx_handlers(struct oce_dev *dev); 43 static void oce_del_intx_handlers(struct oce_dev *dev); 44 45 /* 46 * top level function to setup interrupts 47 * 48 * dev - software handle to the device 49 * 50 * return DDI_SUCCESS => success, failure otherwise 51 */ 52 int 53 oce_setup_intr(struct oce_dev *dev) 54 { 55 int ret; 56 int intr_types = 0; 57 58 /* get supported intr types */ 59 ret = ddi_intr_get_supported_types(dev->dip, &intr_types); 60 if (ret != DDI_SUCCESS) { 61 oce_log(dev, CE_WARN, MOD_CONFIG, "%s", 62 "Failed to retrieve intr types "); 63 return (DDI_FAILURE); 64 } 65 66 if (intr_types & DDI_INTR_TYPE_MSIX) { 67 dev->intr_types = DDI_INTR_TYPE_MSIX; 68 dev->num_vectors = 2; 69 return (DDI_SUCCESS); 70 } 71 72 if (intr_types & DDI_INTR_TYPE_FIXED) { 73 dev->intr_types = DDI_INTR_TYPE_FIXED; 74 dev->num_vectors = 1; 75 return (DDI_SUCCESS); 76 } 77 return (DDI_FAILURE); 78 } 79 80 int 81 oce_alloc_intr(struct oce_dev *dev) 82 { 83 if (dev->intr_types == DDI_INTR_TYPE_MSIX) { 84 return (oce_setup_msix(dev)); 85 } 86 if (dev->intr_types == DDI_INTR_TYPE_FIXED) { 87 return (oce_setup_intx(dev)); 88 } 89 90 return (DDI_FAILURE); 91 } 92 93 /* 94 * top level function to undo initialization in oce_setup_intr 95 * 96 * dev - software handle to the device 97 * 98 * return DDI_SUCCESS => success, failure otherwise 99 */ 100 int 101 oce_teardown_intr(struct oce_dev *dev) 102 { 103 if (dev->intr_types == DDI_INTR_TYPE_MSIX) { 104 return (oce_teardown_msix(dev)); 105 } 106 107 if (dev->intr_types == DDI_INTR_TYPE_FIXED) { 108 return (oce_teardown_intx(dev)); 109 } 110 111 return (DDI_FAILURE); 112 } 113 114 /* 115 * helper function to add ISR based on interrupt type 116 * 117 * dev - software handle to the device 118 * 119 * return DDI_SUCCESS => success, failure otherwise 120 */ 121 int 122 oce_setup_handlers(struct oce_dev *dev) 123 { 124 if (dev->intr_types == DDI_INTR_TYPE_MSIX) { 125 return (oce_add_msix_handlers(dev)); 126 } 127 128 if (dev->intr_types == DDI_INTR_TYPE_FIXED) { 129 return (oce_add_intx_handlers(dev)); 130 } 131 132 return (DDI_FAILURE); 133 } 134 135 /* 136 * helper function to remove ISRs added in oce_setup_handlers 137 * 138 * dev - software handle to the device 139 * 140 * return DDI_SUCCESS => success, failure otherwise 141 */ 142 void 143 oce_remove_handler(struct oce_dev *dev) 144 { 145 if (dev->intr_types == DDI_INTR_TYPE_MSIX) { 146 oce_del_msix_handlers(dev); 147 } 148 149 if (dev->intr_types == DDI_INTR_TYPE_FIXED) { 150 oce_del_intx_handlers(dev); 151 } 152 } 153 154 void 155 oce_chip_ei(struct oce_dev *dev) 156 { 157 uint32_t reg; 158 159 reg = OCE_CFG_READ32(dev, PCICFG_INTR_CTRL); 160 if (oce_fm_check_acc_handle(dev, dev->dev_cfg_handle) != DDI_FM_OK) { 161 ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); 162 } 163 reg |= HOSTINTR_MASK; 164 OCE_CFG_WRITE32(dev, PCICFG_INTR_CTRL, reg); 165 if (oce_fm_check_acc_handle(dev, dev->dev_cfg_handle) != DDI_FM_OK) { 166 ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); 167 } 168 } 169 170 /* 171 * function to enable interrupts 172 * 173 * dev - software handle to the device 174 * 175 * return DDI_SUCCESS => success, failure otherwise 176 */ 177 void 178 oce_ei(struct oce_dev *dev) 179 { 180 int i; 181 int ret; 182 183 if (dev->intr_cap & DDI_INTR_FLAG_BLOCK) { 184 (void) ddi_intr_block_enable(dev->htable, dev->num_vectors); 185 } else { 186 187 for (i = 0; i < dev->num_vectors; i++) { 188 ret = ddi_intr_enable(dev->htable[i]); 189 if (ret != DDI_SUCCESS) { 190 for (i--; i >= 0; i--) { 191 (void) ddi_intr_disable(dev->htable[i]); 192 } 193 } 194 } 195 } 196 oce_chip_ei(dev); 197 } /* oce_ei */ 198 199 void 200 oce_chip_di(struct oce_dev *dev) 201 { 202 uint32_t reg; 203 204 reg = OCE_CFG_READ32(dev, PCICFG_INTR_CTRL); 205 if (oce_fm_check_acc_handle(dev, dev->dev_cfg_handle) != DDI_FM_OK) { 206 ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); 207 } 208 reg &= ~HOSTINTR_MASK; 209 OCE_CFG_WRITE32(dev, PCICFG_INTR_CTRL, reg); 210 if (oce_fm_check_acc_handle(dev, dev->dev_cfg_handle) != DDI_FM_OK) { 211 ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); 212 } 213 } 214 215 /* 216 * function to disable interrupts 217 * 218 * dev - software handle to the device 219 * 220 * return DDI_SUCCESS => success, failure otherwise 221 */ 222 void 223 oce_di(struct oce_dev *dev) 224 { 225 int i; 226 int ret; 227 228 if (dev->intr_cap & DDI_INTR_FLAG_BLOCK) { 229 (void) ddi_intr_block_disable(dev->htable, dev->num_vectors); 230 } else { 231 for (i = 0; i < dev->num_vectors; i++) { 232 ret = ddi_intr_disable(dev->htable[i]); 233 if (ret != DDI_SUCCESS) { 234 oce_log(dev, CE_WARN, MOD_CONFIG, 235 "Failed to disable interrupts 0x%x", ret); 236 } 237 } 238 } 239 oce_chip_di(dev); 240 } /* oce_di */ 241 242 /* 243 * function to setup the MSIX vectors 244 * 245 * dev - software handle to the device 246 * 247 * return 0=>success, failure otherwise 248 */ 249 static int 250 oce_setup_msix(struct oce_dev *dev) 251 { 252 int navail = 0; 253 int ret = 0; 254 255 ret = ddi_intr_get_nintrs(dev->dip, DDI_INTR_TYPE_MSIX, &navail); 256 if (ret != DDI_SUCCESS) { 257 oce_log(dev, CE_WARN, MOD_CONFIG, 258 "Could not get nintrs:0x%x %d", 259 navail, ret); 260 return (DDI_FAILURE); 261 } 262 263 /* get the number of vectors available */ 264 ret = ddi_intr_get_navail(dev->dip, DDI_INTR_TYPE_MSIX, &navail); 265 if (ret != DDI_SUCCESS) { 266 oce_log(dev, CE_WARN, MOD_CONFIG, 267 "Could not get msix vectors:0x%x", 268 navail); 269 return (DDI_FAILURE); 270 } 271 272 if (navail < dev->num_vectors) 273 return (DDI_FAILURE); 274 275 /* allocate htable */ 276 dev->htable = kmem_zalloc(dev->num_vectors * 277 sizeof (ddi_intr_handle_t), KM_NOSLEEP); 278 279 if (dev->htable == NULL) 280 return (DDI_FAILURE); 281 282 /* allocate interrupt handlers */ 283 ret = ddi_intr_alloc(dev->dip, dev->htable, DDI_INTR_TYPE_MSIX, 284 0, dev->num_vectors, &navail, DDI_INTR_ALLOC_NORMAL); 285 286 if (ret != DDI_SUCCESS || navail < dev->num_vectors) { 287 oce_log(dev, CE_WARN, MOD_CONFIG, 288 "Alloc intr failed: %d %d", 289 navail, ret); 290 kmem_free(dev->htable, 291 dev->num_vectors * sizeof (ddi_intr_handle_t)); 292 return (DDI_FAILURE); 293 } 294 295 /* update the actual number of interrupts allocated */ 296 dev->num_vectors = navail; 297 298 /* 299 * get the interrupt priority. Assumption is that all handlers have 300 * equal priority 301 */ 302 303 ret = ddi_intr_get_pri(dev->htable[0], &dev->intr_pri); 304 305 if (ret != DDI_SUCCESS) { 306 int i; 307 oce_log(dev, CE_WARN, MOD_CONFIG, 308 "Unable to get intr priority: 0x%x", 309 dev->intr_pri); 310 for (i = 0; i < dev->num_vectors; i++) { 311 (void) ddi_intr_free(dev->htable[i]); 312 } 313 kmem_free(dev->htable, 314 dev->num_vectors * sizeof (ddi_intr_handle_t)); 315 return (DDI_FAILURE); 316 } 317 318 (void) ddi_intr_get_cap(dev->htable[0], &dev->intr_cap); 319 return (DDI_SUCCESS); 320 } /* oce_setup_msix */ 321 322 /* 323 * helper function to teardown MSIX interrupts 324 * 325 * dev - software handle to the device 326 * 327 * return 0 => success, failure otherwise 328 */ 329 static int 330 oce_teardown_msix(struct oce_dev *dev) 331 { 332 int i; 333 334 /* release handlers */ 335 for (i = 0; i < dev->num_vectors; i++) { 336 (void) ddi_intr_free(dev->htable[i]); 337 } 338 339 /* release htable */ 340 kmem_free(dev->htable, 341 dev->num_vectors * sizeof (ddi_intr_handle_t)); 342 343 return (DDI_SUCCESS); 344 } /* oce_teardown_msix */ 345 346 /* 347 * function to add MSIX handlers to vectors 348 * 349 * dev - software handle to the device 350 * 351 * return DDI_SUCCESS => success, failure otherwise 352 */ 353 static int 354 oce_add_msix_handlers(struct oce_dev *dev) 355 { 356 int ret; 357 int i; 358 359 for (i = 0; i < dev->neqs; i++) { 360 ret = ddi_intr_add_handler(dev->htable[i], oce_isr, 361 (caddr_t)dev->eq[i], NULL); 362 if (ret != DDI_SUCCESS) { 363 oce_log(dev, CE_WARN, MOD_CONFIG, "%s", 364 "Failed to add interrupt handlers"); 365 for (i--; i >= 0; i--) { 366 (void) ddi_intr_remove_handler(dev->htable[i]); 367 } 368 return (DDI_FAILURE); 369 } 370 } 371 372 return (DDI_SUCCESS); 373 } /* oce_add_msix_handlers */ 374 375 /* 376 * function to disassociate msix handlers added in oce_add_msix_handlers 377 * 378 * dev - software handle to the device 379 * 380 * return DDI_SUCCESS => success, failure otherwise 381 */ 382 static void 383 oce_del_msix_handlers(struct oce_dev *dev) 384 { 385 int nvec; 386 387 for (nvec = 0; nvec < dev->num_vectors; nvec++) { 388 (void) ddi_intr_remove_handler(dev->htable[nvec]); 389 } 390 } /* oce_del_msix_handlers */ 391 392 /* 393 * command interrupt handler routine added to all vectors 394 * 395 * arg1 = callback data 396 * arg2 - callback data 397 * 398 * return DDI_INTR_CLAIMED => interrupt was claimed by the ISR 399 */ 400 static uint_t 401 oce_isr(caddr_t arg1, caddr_t arg2) 402 { 403 struct oce_eq *eq; 404 struct oce_eqe *eqe; 405 uint16_t num_eqe = 0; 406 uint16_t cq_id; 407 struct oce_cq *cq; 408 struct oce_dev *dev; 409 410 _NOTE(ARGUNUSED(arg2)); 411 412 eq = (struct oce_eq *)(void *)(arg1); 413 414 if (eq == NULL) { 415 return (DDI_INTR_UNCLAIMED); 416 } 417 dev = eq->parent; 418 419 /* If device is getting suspended or closing, then return */ 420 if ((dev == NULL) || 421 (dev->state & STATE_MAC_STOPPING) || 422 !(dev->state & STATE_MAC_STARTED) || 423 dev->suspended) { 424 return (DDI_INTR_UNCLAIMED); 425 } 426 427 (void) ddi_dma_sync(eq->ring->dbuf->dma_handle, 0, 0, 428 DDI_DMA_SYNC_FORKERNEL); 429 430 eqe = RING_GET_CONSUMER_ITEM_VA(eq->ring, struct oce_eqe); 431 432 while (eqe->u0.dw0) { 433 434 eqe->u0.dw0 = LE_32(eqe->u0.dw0); 435 436 /* if not CQ then continue else flag an error */ 437 if (EQ_MAJOR_CODE_COMPLETION != eqe->u0.s.major_code) { 438 oce_log(dev, CE_WARN, MOD_ISR, 439 "NOT a CQ event. 0x%x", 440 eqe->u0.s.major_code); 441 } 442 443 /* get the cq from the eqe */ 444 cq_id = eqe->u0.s.resource_id; 445 cq = dev->cq[cq_id]; 446 447 /* Call the completion handler */ 448 (void) cq->cq_handler(cq->cb_arg); 449 450 /* clear valid bit and progress eqe */ 451 eqe->u0.dw0 = 0; 452 RING_GET(eq->ring, 1); 453 eqe = RING_GET_CONSUMER_ITEM_VA(eq->ring, struct oce_eqe); 454 num_eqe++; 455 } /* for all EQEs */ 456 457 /* ring the eq doorbell, signify that it's done processing */ 458 if (num_eqe > 0) { 459 oce_arm_eq(dev, eq->eq_id, num_eqe, B_TRUE, B_TRUE); 460 return (DDI_INTR_CLAIMED); 461 } else { 462 return (DDI_INTR_UNCLAIMED); 463 } 464 } /* oce_msix_handler */ 465 466 static int 467 oce_setup_intx(struct oce_dev *dev) 468 { 469 int navail = 0; 470 int nintr = 0; 471 int ret = 0; 472 473 ret = ddi_intr_get_nintrs(dev->dip, DDI_INTR_TYPE_FIXED, &nintr); 474 if (ret != DDI_SUCCESS) { 475 oce_log(dev, CE_WARN, MOD_CONFIG, 476 "could not get nintrs:0x%x %d", 477 navail, ret); 478 return (DDI_FAILURE); 479 } 480 481 /* get the number of vectors available */ 482 ret = ddi_intr_get_navail(dev->dip, DDI_INTR_TYPE_FIXED, &navail); 483 if (ret != DDI_SUCCESS) { 484 oce_log(dev, CE_WARN, MOD_CONFIG, 485 "could not get intx vectors:0x%x", 486 navail); 487 return (DDI_FAILURE); 488 } 489 490 /* always 1 */ 491 if (navail != nintr) 492 return (DDI_FAILURE); 493 494 dev->num_vectors = navail; 495 496 /* allocate htable */ 497 dev->htable = kmem_zalloc(sizeof (ddi_intr_handle_t), KM_NOSLEEP); 498 if (dev->htable == NULL) { 499 return (DDI_FAILURE); 500 } 501 502 /* allocate interrupt handlers */ 503 ret = ddi_intr_alloc(dev->dip, dev->htable, DDI_INTR_TYPE_FIXED, 504 0, dev->num_vectors, &navail, DDI_INTR_ALLOC_NORMAL); 505 506 if (ret != DDI_SUCCESS || navail != 1) { 507 oce_log(dev, CE_WARN, MOD_CONFIG, 508 "alloc intr failed: %d %d", 509 navail, ret); 510 kmem_free(dev->htable, sizeof (ddi_intr_handle_t)); 511 return (DDI_FAILURE); 512 } 513 514 /* update the actual number of interrupts allocated */ 515 dev->num_vectors = navail; 516 517 /* 518 * get the interrupt priority. Assumption is that all handlers have 519 * equal priority 520 */ 521 522 ret = ddi_intr_get_pri(dev->htable[0], &dev->intr_pri); 523 524 if (ret != DDI_SUCCESS) { 525 int i; 526 oce_log(dev, CE_WARN, MOD_CONFIG, 527 "Unable to get intr priority: 0x%x", 528 dev->intr_pri); 529 for (i = 0; i < dev->num_vectors; i++) { 530 (void) ddi_intr_free(dev->htable[i]); 531 } 532 kmem_free(dev->htable, sizeof (ddi_intr_handle_t)); 533 return (DDI_FAILURE); 534 } 535 536 (void) ddi_intr_get_cap(dev->htable[0], &dev->intr_cap); 537 return (DDI_SUCCESS); 538 } /* oce_setup_intx */ 539 540 static int 541 oce_teardown_intx(struct oce_dev *dev) 542 { 543 /* release handlers */ 544 (void) ddi_intr_free(dev->htable[0]); 545 546 /* release htable */ 547 kmem_free(dev->htable, sizeof (ddi_intr_handle_t)); 548 549 return (DDI_FAILURE); 550 } /* oce_teardown_intx */ 551 552 static int 553 oce_add_intx_handlers(struct oce_dev *dev) 554 { 555 int ret; 556 557 ret = ddi_intr_add_handler(dev->htable[0], oce_isr, 558 (caddr_t)dev->eq[0], NULL); 559 if (ret != DDI_SUCCESS) { 560 oce_log(dev, CE_NOTE, MOD_CONFIG, "%s", 561 "failed to add intr handlers"); 562 (void) ddi_intr_remove_handler(dev->htable[0]); 563 return (DDI_FAILURE); 564 } 565 566 return (DDI_SUCCESS); 567 } /* oce_add_intx_handlers */ 568 569 static void 570 oce_del_intx_handlers(struct oce_dev *dev) 571 { 572 (void) ddi_intr_remove_handler(dev->htable[0]); 573 } /* oce_del_intx_handlers */ 574