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 2009 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 reg |= HOSTINTR_MASK; 161 OCE_CFG_WRITE32(dev, PCICFG_INTR_CTRL, reg); 162 } 163 164 /* 165 * function to enable interrupts 166 * 167 * dev - software handle to the device 168 * 169 * return DDI_SUCCESS => success, failure otherwise 170 */ 171 void 172 oce_ei(struct oce_dev *dev) 173 { 174 int i; 175 int ret; 176 177 if (dev->intr_cap & DDI_INTR_FLAG_BLOCK) { 178 (void) ddi_intr_block_enable(dev->htable, dev->num_vectors); 179 } else { 180 181 for (i = 0; i < dev->num_vectors; i++) { 182 ret = ddi_intr_enable(dev->htable[i]); 183 if (ret != DDI_SUCCESS) { 184 for (i--; i >= 0; i--) { 185 (void) ddi_intr_disable(dev->htable[i]); 186 } 187 } 188 } 189 } 190 oce_chip_ei(dev); 191 } /* oce_ei */ 192 193 void 194 oce_chip_di(struct oce_dev *dev) 195 { 196 uint32_t reg; 197 198 reg = OCE_CFG_READ32(dev, PCICFG_INTR_CTRL); 199 reg &= ~HOSTINTR_MASK; 200 OCE_CFG_WRITE32(dev, PCICFG_INTR_CTRL, reg); 201 } 202 203 /* 204 * function to disable interrupts 205 * 206 * dev - software handle to the device 207 * 208 * return DDI_SUCCESS => success, failure otherwise 209 */ 210 void 211 oce_di(struct oce_dev *dev) 212 { 213 int i; 214 int ret; 215 216 if (dev->intr_cap & DDI_INTR_FLAG_BLOCK) { 217 (void) ddi_intr_block_disable(dev->htable, dev->num_vectors); 218 } else { 219 for (i = 0; i < dev->num_vectors; i++) { 220 ret = ddi_intr_disable(dev->htable[i]); 221 if (ret != DDI_SUCCESS) { 222 oce_log(dev, CE_WARN, MOD_CONFIG, 223 "Failed to disable interrupts 0x%x", ret); 224 } 225 } 226 } 227 oce_chip_di(dev); 228 } /* oce_di */ 229 230 /* 231 * function to setup the MSIX vectors 232 * 233 * dev - software handle to the device 234 * 235 * return 0=>success, failure otherwise 236 */ 237 static int 238 oce_setup_msix(struct oce_dev *dev) 239 { 240 int navail = 0; 241 int ret = 0; 242 243 ret = ddi_intr_get_nintrs(dev->dip, DDI_INTR_TYPE_MSIX, &navail); 244 if (ret != DDI_SUCCESS) { 245 oce_log(dev, CE_WARN, MOD_CONFIG, 246 "Could not get nintrs:0x%x %d", 247 navail, ret); 248 return (DDI_FAILURE); 249 } 250 251 /* get the number of vectors available */ 252 ret = ddi_intr_get_navail(dev->dip, DDI_INTR_TYPE_MSIX, &navail); 253 if (ret != DDI_SUCCESS) { 254 oce_log(dev, CE_WARN, MOD_CONFIG, 255 "Could not get msix vectors:0x%x", 256 navail); 257 return (DDI_FAILURE); 258 } 259 260 if (navail < dev->num_vectors) 261 return (DDI_FAILURE); 262 263 /* allocate htable */ 264 dev->htable = kmem_zalloc(dev->num_vectors * 265 sizeof (ddi_intr_handle_t), KM_NOSLEEP); 266 267 if (dev->htable == NULL) 268 return (DDI_FAILURE); 269 270 /* allocate interrupt handlers */ 271 ret = ddi_intr_alloc(dev->dip, dev->htable, DDI_INTR_TYPE_MSIX, 272 0, dev->num_vectors, &navail, DDI_INTR_ALLOC_NORMAL); 273 274 if (ret != DDI_SUCCESS || navail < dev->num_vectors) { 275 oce_log(dev, CE_WARN, MOD_CONFIG, 276 "Alloc intr failed: %d %d", 277 navail, ret); 278 kmem_free(dev->htable, 279 dev->num_vectors * sizeof (ddi_intr_handle_t)); 280 return (DDI_FAILURE); 281 } 282 283 /* update the actual number of interrupts allocated */ 284 dev->num_vectors = navail; 285 286 /* 287 * get the interrupt priority. Assumption is that all handlers have 288 * equal priority 289 */ 290 291 ret = ddi_intr_get_pri(dev->htable[0], &dev->intr_pri); 292 293 if (ret != DDI_SUCCESS) { 294 int i; 295 oce_log(dev, CE_WARN, MOD_CONFIG, 296 "Unable to get intr priority: 0x%x", 297 dev->intr_pri); 298 for (i = 0; i < dev->num_vectors; i++) { 299 (void) ddi_intr_free(dev->htable[i]); 300 } 301 kmem_free(dev->htable, 302 dev->num_vectors * sizeof (ddi_intr_handle_t)); 303 return (DDI_FAILURE); 304 } 305 306 (void) ddi_intr_get_cap(dev->htable[0], &dev->intr_cap); 307 return (DDI_SUCCESS); 308 } /* oce_setup_msix */ 309 310 /* 311 * helper function to teardown MSIX interrupts 312 * 313 * dev - software handle to the device 314 * 315 * return 0 => success, failure otherwise 316 */ 317 static int 318 oce_teardown_msix(struct oce_dev *dev) 319 { 320 int i; 321 322 /* release handlers */ 323 for (i = 0; i < dev->num_vectors; i++) { 324 (void) ddi_intr_free(dev->htable[i]); 325 } 326 327 /* release htable */ 328 kmem_free(dev->htable, 329 dev->num_vectors * sizeof (ddi_intr_handle_t)); 330 331 return (DDI_SUCCESS); 332 } /* oce_teardown_msix */ 333 334 /* 335 * function to add MSIX handlers to vectors 336 * 337 * dev - software handle to the device 338 * 339 * return DDI_SUCCESS => success, failure otherwise 340 */ 341 static int 342 oce_add_msix_handlers(struct oce_dev *dev) 343 { 344 int ret; 345 int i; 346 347 for (i = 0; i < dev->neqs; i++) { 348 ret = ddi_intr_add_handler(dev->htable[i], oce_isr, 349 (caddr_t)dev->eq[i], NULL); 350 if (ret != DDI_SUCCESS) { 351 oce_log(dev, CE_WARN, MOD_CONFIG, "%s", 352 "Failed to add interrupt handlers"); 353 for (i--; i >= 0; i--) { 354 (void) ddi_intr_remove_handler(dev->htable[i]); 355 } 356 return (DDI_FAILURE); 357 } 358 } 359 360 return (DDI_SUCCESS); 361 } /* oce_add_msix_handlers */ 362 363 /* 364 * function to disassociate msix handlers added in oce_add_msix_handlers 365 * 366 * dev - software handle to the device 367 * 368 * return DDI_SUCCESS => success, failure otherwise 369 */ 370 static void 371 oce_del_msix_handlers(struct oce_dev *dev) 372 { 373 int nvec; 374 375 for (nvec = 0; nvec < dev->num_vectors; nvec++) { 376 (void) ddi_intr_remove_handler(dev->htable[nvec]); 377 } 378 } /* oce_del_msix_handlers */ 379 380 /* 381 * command interrupt handler routine added to all vectors 382 * 383 * arg1 = callback data 384 * arg2 - callback data 385 * 386 * return DDI_INTR_CLAIMED => interrupt was claimed by the ISR 387 */ 388 static uint_t 389 oce_isr(caddr_t arg1, caddr_t arg2) 390 { 391 struct oce_eq *eq; 392 struct oce_eqe *eqe; 393 uint16_t num_eqe = 0; 394 uint16_t cq_id; 395 struct oce_cq *cq; 396 struct oce_dev *dev; 397 398 _NOTE(ARGUNUSED(arg2)); 399 400 eq = (struct oce_eq *)(void *)(arg1); 401 402 if (eq == NULL) { 403 return (DDI_INTR_UNCLAIMED); 404 } 405 dev = eq->parent; 406 407 /* If device is getting suspended or closing, then return */ 408 if ((dev == NULL) || 409 (dev->state & STATE_MAC_STOPPING) || 410 !(dev->state & STATE_MAC_STARTED) || 411 dev->suspended) { 412 return (DDI_INTR_UNCLAIMED); 413 } 414 415 eqe = RING_GET_CONSUMER_ITEM_VA(eq->ring, struct oce_eqe); 416 417 while (eqe->u0.dw0) { 418 419 eqe->u0.dw0 = LE_32(eqe->u0.dw0); 420 421 /* if not CQ then continue else flag an error */ 422 if (EQ_MAJOR_CODE_COMPLETION != eqe->u0.s.major_code) { 423 oce_log(dev, CE_WARN, MOD_ISR, 424 "NOT a CQ event. 0x%x", 425 eqe->u0.s.major_code); 426 } 427 428 /* get the cq from the eqe */ 429 cq_id = eqe->u0.s.resource_id; 430 cq = dev->cq[cq_id]; 431 432 /* Call the completion handler */ 433 (void) cq->cq_handler(cq->cb_arg); 434 435 /* clear valid bit and progress eqe */ 436 eqe->u0.dw0 = 0; 437 RING_GET(eq->ring, 1); 438 eqe = RING_GET_CONSUMER_ITEM_VA(eq->ring, struct oce_eqe); 439 num_eqe++; 440 } /* for all EQEs */ 441 442 /* ring the eq doorbell, signify that it's done processing */ 443 if (num_eqe > 0) { 444 oce_arm_eq(dev, eq->eq_id, num_eqe, B_TRUE, B_TRUE); 445 return (DDI_INTR_CLAIMED); 446 } else { 447 return (DDI_INTR_UNCLAIMED); 448 } 449 } /* oce_msix_handler */ 450 451 static int 452 oce_setup_intx(struct oce_dev *dev) 453 { 454 int navail = 0; 455 int nintr = 0; 456 int ret = 0; 457 458 ret = ddi_intr_get_nintrs(dev->dip, DDI_INTR_TYPE_FIXED, &nintr); 459 if (ret != DDI_SUCCESS) { 460 oce_log(dev, CE_WARN, MOD_CONFIG, 461 "could not get nintrs:0x%x %d", 462 navail, ret); 463 return (DDI_FAILURE); 464 } 465 466 /* get the number of vectors available */ 467 ret = ddi_intr_get_navail(dev->dip, DDI_INTR_TYPE_FIXED, &navail); 468 if (ret != DDI_SUCCESS) { 469 oce_log(dev, CE_WARN, MOD_CONFIG, 470 "could not get intx vectors:0x%x", 471 navail); 472 return (DDI_FAILURE); 473 } 474 475 /* always 1 */ 476 if (navail != nintr) 477 return (DDI_FAILURE); 478 479 dev->num_vectors = navail; 480 481 /* allocate htable */ 482 dev->htable = kmem_zalloc(sizeof (ddi_intr_handle_t), KM_SLEEP); 483 484 /* allocate interrupt handlers */ 485 ret = ddi_intr_alloc(dev->dip, dev->htable, DDI_INTR_TYPE_FIXED, 486 0, dev->num_vectors, &navail, DDI_INTR_ALLOC_NORMAL); 487 488 if (ret != DDI_SUCCESS || navail != 1) { 489 oce_log(dev, CE_WARN, MOD_CONFIG, 490 "alloc intr failed: %d %d", 491 navail, ret); 492 kmem_free(dev->htable, sizeof (ddi_intr_handle_t)); 493 return (DDI_FAILURE); 494 } 495 496 /* update the actual number of interrupts allocated */ 497 dev->num_vectors = navail; 498 499 /* 500 * get the interrupt priority. Assumption is that all handlers have 501 * equal priority 502 */ 503 504 ret = ddi_intr_get_pri(dev->htable[0], &dev->intr_pri); 505 506 if (ret != DDI_SUCCESS) { 507 int i; 508 oce_log(dev, CE_WARN, MOD_CONFIG, 509 "Unable to get intr priority: 0x%x", 510 dev->intr_pri); 511 for (i = 0; i < dev->num_vectors; i++) { 512 (void) ddi_intr_free(dev->htable[i]); 513 } 514 kmem_free(dev->htable, sizeof (ddi_intr_handle_t)); 515 return (DDI_FAILURE); 516 } 517 518 (void) ddi_intr_get_cap(dev->htable[0], &dev->intr_cap); 519 return (DDI_SUCCESS); 520 } /* oce_setup_intx */ 521 522 static int 523 oce_teardown_intx(struct oce_dev *dev) 524 { 525 /* release handlers */ 526 (void) ddi_intr_free(dev->htable[0]); 527 528 /* release htable */ 529 kmem_free(dev->htable, sizeof (ddi_intr_handle_t)); 530 531 return (DDI_FAILURE); 532 } /* oce_teardown_intx */ 533 534 static int 535 oce_add_intx_handlers(struct oce_dev *dev) 536 { 537 int ret; 538 539 ret = ddi_intr_add_handler(dev->htable[0], oce_isr, 540 (caddr_t)dev->eq[0], NULL); 541 if (ret != DDI_SUCCESS) { 542 oce_log(dev, CE_NOTE, MOD_CONFIG, "%s", 543 "failed to add intr handlers"); 544 (void) ddi_intr_remove_handler(dev->htable[0]); 545 return (DDI_FAILURE); 546 } 547 548 return (DDI_SUCCESS); 549 } /* oce_add_intx_handlers */ 550 551 static void 552 oce_del_intx_handlers(struct oce_dev *dev) 553 { 554 (void) ddi_intr_remove_handler(dev->htable[0]); 555 } /* oce_del_intx_handlers */ 556