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