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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * Support for MSI, MSIX and INTx 30 */ 31 32 #include <sys/conf.h> 33 #include <sys/debug.h> 34 #include <sys/pci.h> 35 #include <sys/pci_cap.h> 36 #include <sys/sunddi.h> 37 #include <sys/bitmap.h> 38 39 /* 40 * MSI-X BIR Index Table: 41 * 42 * BAR indicator register (BIR) to Base Address register. 43 */ 44 static uchar_t pci_msix_bir_index[8] = {0x10, 0x14, 0x18, 0x1c, 45 0x20, 0x24, 0xff, 0xff}; 46 47 /* 48 * Library utility functions 49 */ 50 51 /* 52 * pci_get_msi_ctrl: 53 * 54 * Helper function that returns with 'cfg_hdl', MSI/X ctrl pointer, 55 * and caps_ptr for MSI/X if these are found. 56 */ 57 static int 58 pci_get_msi_ctrl(dev_info_t *dip, int type, ushort_t *msi_ctrl, 59 ushort_t *caps_ptr, ddi_acc_handle_t *h) 60 { 61 *msi_ctrl = *caps_ptr = 0; 62 63 if (pci_config_setup(dip, h) != DDI_SUCCESS) { 64 DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: " 65 "%s%d can't get config handle", 66 ddi_driver_name(dip), ddi_get_instance(dip))); 67 68 return (DDI_FAILURE); 69 } 70 71 if ((PCI_CAP_LOCATE(*h, PCI_CAP_ID_MSI, caps_ptr) == DDI_SUCCESS) && 72 (type == DDI_INTR_TYPE_MSI)) { 73 if ((*msi_ctrl = PCI_CAP_GET16(*h, NULL, *caps_ptr, 74 PCI_MSI_CTRL)) == PCI_CAP_EINVAL16) 75 goto done; 76 77 DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI " 78 "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl)); 79 80 return (DDI_SUCCESS); 81 } 82 83 if ((PCI_CAP_LOCATE(*h, PCI_CAP_ID_MSI_X, caps_ptr) == DDI_SUCCESS) && 84 (type == DDI_INTR_TYPE_MSIX)) { 85 if ((*msi_ctrl = PCI_CAP_GET16(*h, NULL, *caps_ptr, 86 PCI_MSIX_CTRL)) == PCI_CAP_EINVAL16) 87 goto done; 88 89 DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI-X " 90 "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl)); 91 92 return (DDI_SUCCESS); 93 } 94 95 done: 96 pci_config_teardown(h); 97 return (DDI_FAILURE); 98 } 99 100 101 /* 102 * pci_msi_get_cap: 103 * 104 * Get the capabilities of the MSI/X interrupt 105 */ 106 int 107 pci_msi_get_cap(dev_info_t *rdip, int type, int *flagsp) 108 { 109 ushort_t caps_ptr, msi_ctrl; 110 ddi_acc_handle_t cfg_hdle; 111 112 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: rdip = 0x%p\n", 113 (void *)rdip)); 114 115 *flagsp = 0; 116 117 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 118 &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 119 return (DDI_FAILURE); 120 121 if (type == DDI_INTR_TYPE_MSI) { 122 if (msi_ctrl & PCI_MSI_64BIT_MASK) 123 *flagsp |= DDI_INTR_FLAG_MSI64; 124 if (msi_ctrl & PCI_MSI_PVM_MASK) 125 *flagsp |= (DDI_INTR_FLAG_MASKABLE | 126 DDI_INTR_FLAG_PENDING); 127 else 128 *flagsp |= DDI_INTR_FLAG_BLOCK; 129 } else if (type == DDI_INTR_TYPE_MSIX) { 130 /* MSI-X supports PVM, 64bit by default */ 131 *flagsp |= (DDI_INTR_FLAG_MASKABLE | DDI_INTR_FLAG_MSI64 | 132 DDI_INTR_FLAG_PENDING); 133 } 134 135 *flagsp |= DDI_INTR_FLAG_EDGE; 136 137 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: flags = 0x%x\n", *flagsp)); 138 139 pci_config_teardown(&cfg_hdle); 140 return (DDI_SUCCESS); 141 } 142 143 144 /* 145 * pci_msi_configure: 146 * 147 * Configure address/data and number MSI/Xs fields in the MSI/X 148 * capability structure. 149 */ 150 /* ARGSUSED */ 151 int 152 pci_msi_configure(dev_info_t *rdip, int type, int count, int inum, 153 uint64_t addr, uint64_t data) 154 { 155 ushort_t caps_ptr, msi_ctrl; 156 ddi_acc_handle_t h; 157 158 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: rdip = 0x%p type 0x%x " 159 "count 0x%x inum 0x%x addr 0x%" PRIx64 " data 0x%" PRIx64 "\n", 160 (void *)rdip, type, count, inum, addr, data)); 161 162 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 163 &caps_ptr, &h) != DDI_SUCCESS) 164 return (DDI_FAILURE); 165 166 if (type == DDI_INTR_TYPE_MSI) { 167 /* Set the bits to inform how many MSIs are enabled */ 168 msi_ctrl |= ((highbit(count) -1) << PCI_MSI_MME_SHIFT); 169 PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl); 170 171 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_ctrl = %x\n", 172 PCI_CAP_GET16(h, NULL, caps_ptr, PCI_MSI_CTRL))); 173 174 /* Set the "data" and "addr" bits */ 175 PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET, addr); 176 177 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_addr = %x\n", 178 PCI_CAP_GET32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET))); 179 180 if (msi_ctrl & PCI_MSI_64BIT_MASK) { 181 PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET 182 + 4, addr >> 32); 183 184 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: upper " 185 "32bit msi_addr = %x\n", PCI_CAP_GET32(h, NULL, 186 caps_ptr, PCI_MSI_ADDR_OFFSET + 4))); 187 188 PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_64BIT_DATA, 189 data); 190 191 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data " 192 "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr, 193 PCI_MSI_64BIT_DATA))); 194 } else { 195 PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_32BIT_DATA, 196 data); 197 198 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data " 199 "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr, 200 PCI_MSI_32BIT_DATA))); 201 } 202 } else if (type == DDI_INTR_TYPE_MSIX) { 203 uintptr_t off; 204 ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 205 206 /* Offset into the "inum"th entry in the MSI-X table */ 207 off = (uintptr_t)msix_p->msix_tbl_addr + 208 (inum * PCI_MSIX_VECTOR_SIZE); 209 210 /* Set the "data" and "addr" bits */ 211 ddi_put32(msix_p->msix_tbl_hdl, 212 (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), data); 213 214 ddi_put64(msix_p->msix_tbl_hdl, 215 (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), addr); 216 217 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: " 218 "msix_addr 0x%" PRIx64 " msix_data 0x%x\n", 219 ddi_get64(msix_p->msix_tbl_hdl, 220 (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET)), 221 ddi_get32(msix_p->msix_tbl_hdl, 222 (uint32_t *)(off + PCI_MSIX_DATA_OFFSET)))); 223 } 224 225 pci_config_teardown(&h); 226 return (DDI_SUCCESS); 227 } 228 229 230 /* 231 * pci_msi_unconfigure: 232 * 233 * Unconfigure address/data and number MSI/Xs fields in the MSI/X 234 * capability structure. 235 */ 236 /* ARGSUSED */ 237 int 238 pci_msi_unconfigure(dev_info_t *rdip, int type, int inum) 239 { 240 ushort_t msi_ctrl, caps_ptr; 241 ddi_acc_handle_t h; 242 243 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: rdip = 0x%p type 0x%x " 244 "inum 0x%x\n", (void *)rdip, type, inum)); 245 246 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, &caps_ptr, &h) != 247 DDI_SUCCESS) 248 return (DDI_FAILURE); 249 250 if (type == DDI_INTR_TYPE_MSI) { 251 msi_ctrl &= (~PCI_MSI_MME_MASK); 252 PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl); 253 254 PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET, 0); 255 256 if (msi_ctrl & PCI_MSI_64BIT_MASK) { 257 PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_64BIT_DATA, 258 0); 259 PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET 260 + 4, 0); 261 } else { 262 PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_32BIT_DATA, 263 0); 264 } 265 266 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: msi_ctrl " 267 "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr, PCI_MSI_CTRL))); 268 269 } else if (type == DDI_INTR_TYPE_MSIX) { 270 uintptr_t off; 271 ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 272 273 /* Offset into the "inum"th entry in the MSI-X table */ 274 off = (uintptr_t)msix_p->msix_tbl_addr + 275 (inum * PCI_MSIX_VECTOR_SIZE); 276 277 /* Reset the "data" and "addr" bits */ 278 ddi_put32(msix_p->msix_tbl_hdl, 279 (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), 0); 280 281 ddi_put64(msix_p->msix_tbl_hdl, 282 (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), 0); 283 } 284 285 pci_config_teardown(&h); 286 return (DDI_SUCCESS); 287 } 288 289 290 /* 291 * pci_is_msi_enabled: 292 * 293 * This function returns DDI_SUCCESS if MSI/X is already enabled, otherwise 294 * it returns DDI_FAILURE. 295 */ 296 int 297 pci_is_msi_enabled(dev_info_t *rdip, int type) 298 { 299 ushort_t caps_ptr, msi_ctrl; 300 ddi_acc_handle_t cfg_hdle; 301 int ret = DDI_FAILURE; 302 303 DDI_INTR_NEXDBG((CE_CONT, "pci_is_msi_enabled: rdip = 0x%p, " 304 "type = 0x%x\n", (void *)rdip, type)); 305 306 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 307 &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 308 return (DDI_FAILURE); 309 310 if ((type == DDI_INTR_TYPE_MSI) && (msi_ctrl & PCI_MSI_ENABLE_BIT)) 311 ret = DDI_SUCCESS; 312 313 if ((type == DDI_INTR_TYPE_MSIX) && (msi_ctrl & PCI_MSIX_ENABLE_BIT)) 314 ret = DDI_SUCCESS; 315 316 pci_config_teardown(&cfg_hdle); 317 return (ret); 318 } 319 320 321 /* 322 * pci_msi_enable_mode: 323 * 324 * This function sets the MSI_ENABLE bit in the capability structure 325 * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure. 326 * 327 * NOTE: It is the nexus driver's responsibility to clear the MSI/X 328 * interrupt's mask bit in the MSI/X capability structure before the 329 * interrupt can be used. 330 */ 331 int 332 pci_msi_enable_mode(dev_info_t *rdip, int type) 333 { 334 ushort_t caps_ptr, msi_ctrl; 335 ddi_acc_handle_t cfg_hdle; 336 337 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: rdip = 0x%p\n", 338 (void *)rdip)); 339 340 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 341 &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 342 return (DDI_FAILURE); 343 344 if (type == DDI_INTR_TYPE_MSI) { 345 if (msi_ctrl & PCI_MSI_ENABLE_BIT) 346 goto finished; 347 348 msi_ctrl |= PCI_MSI_ENABLE_BIT; 349 PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl); 350 351 } else if (type == DDI_INTR_TYPE_MSIX) { 352 if (msi_ctrl & PCI_MSIX_ENABLE_BIT) 353 goto finished; 354 355 msi_ctrl |= PCI_MSIX_ENABLE_BIT; 356 PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSIX_CTRL, 357 msi_ctrl); 358 } 359 360 finished: 361 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: msi_ctrl = %x\n", 362 msi_ctrl)); 363 364 pci_config_teardown(&cfg_hdle); 365 return (DDI_SUCCESS); 366 } 367 368 369 /* 370 * pci_msi_disable_mode: 371 * 372 * This function resets the MSI_ENABLE bit in the capability structure 373 * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure. 374 * 375 * NOTE: It is the nexus driver's responsibility to set the MSI/X 376 * interrupt's mask bit in the MSI/X capability structure before the 377 * interrupt can be disabled. 378 */ 379 int 380 pci_msi_disable_mode(dev_info_t *rdip, int type, uint_t flags) 381 { 382 ushort_t caps_ptr, msi_ctrl; 383 ddi_acc_handle_t cfg_hdle; 384 385 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: rdip = 0x%p " 386 "flags = 0x%x\n", (void *)rdip, flags)); 387 388 /* 389 * Do not turn off the master enable bit if other interrupts are 390 * still active. 391 */ 392 if ((flags != DDI_INTR_FLAG_BLOCK) && 393 ((i_ddi_intr_get_current_nintrs(rdip) - 1) > 0)) 394 return (DDI_SUCCESS); 395 396 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 397 &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 398 return (DDI_FAILURE); 399 400 /* Reset the "enable" bit */ 401 if (type == DDI_INTR_TYPE_MSI) { 402 if (!(msi_ctrl & PCI_MSI_ENABLE_BIT)) 403 goto finished; 404 msi_ctrl &= ~PCI_MSI_ENABLE_BIT; 405 PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl); 406 } else if (type == DDI_INTR_TYPE_MSIX) { 407 if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT)) 408 goto finished; 409 410 msi_ctrl &= ~PCI_MSIX_ENABLE_BIT; 411 PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSIX_CTRL, 412 msi_ctrl); 413 } 414 415 finished: 416 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: msi_ctrl = %x\n", 417 msi_ctrl)); 418 419 pci_config_teardown(&cfg_hdle); 420 return (DDI_SUCCESS); 421 } 422 423 424 /* 425 * pci_msi_set_mask: 426 * 427 * Set the mask bit in the MSI/X capability structure 428 */ 429 /* ARGSUSED */ 430 int 431 pci_msi_set_mask(dev_info_t *rdip, int type, int inum) 432 { 433 int offset; 434 int ret = DDI_FAILURE; 435 ushort_t caps_ptr, msi_ctrl; 436 ddi_acc_handle_t cfg_hdle; 437 uint32_t mask_bits; 438 439 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_mask: rdip = 0x%p, " 440 "type = 0x%x\n", (void *)rdip, type)); 441 442 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 443 &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 444 return (DDI_FAILURE); 445 446 if (type == DDI_INTR_TYPE_MSI) { 447 if (!(msi_ctrl & PCI_MSI_PVM_MASK)) 448 goto done; 449 450 offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ? 451 PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK; 452 453 if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 454 offset)) == PCI_CAP_EINVAL32) 455 goto done; 456 457 mask_bits |= (1 << inum); 458 459 PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits); 460 461 } else if (type == DDI_INTR_TYPE_MSIX) { 462 uintptr_t off; 463 ddi_intr_msix_t *msix_p; 464 465 /* Set function mask */ 466 if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) { 467 ret = DDI_SUCCESS; 468 goto done; 469 } 470 471 msix_p = i_ddi_get_msix(rdip); 472 473 /* Offset into the "inum"th entry in the MSI-X table */ 474 off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 475 PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 476 477 /* Set the Mask bit */ 478 ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1); 479 } 480 481 ret = DDI_SUCCESS; 482 done: 483 pci_config_teardown(&cfg_hdle); 484 return (ret); 485 } 486 487 488 /* 489 * pci_msi_clr_mask: 490 * 491 * Clear the mask bit in the MSI/X capability structure 492 */ 493 /* ARGSUSED */ 494 int 495 pci_msi_clr_mask(dev_info_t *rdip, int type, int inum) 496 { 497 ushort_t caps_ptr, msi_ctrl; 498 ddi_acc_handle_t cfg_hdle; 499 int offset; 500 int ret = DDI_FAILURE; 501 uint32_t mask_bits; 502 503 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_clr_mask: rdip = 0x%p, " 504 "type = 0x%x\n", (void *)rdip, type)); 505 506 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 507 &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 508 return (DDI_FAILURE); 509 510 if (type == DDI_INTR_TYPE_MSI) { 511 if (!(msi_ctrl & PCI_MSI_PVM_MASK)) 512 goto done; 513 514 offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ? 515 PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK; 516 if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 517 offset)) == PCI_CAP_EINVAL32) 518 goto done; 519 520 mask_bits &= ~(1 << inum); 521 522 PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits); 523 524 } else if (type == DDI_INTR_TYPE_MSIX) { 525 uintptr_t off; 526 ddi_intr_msix_t *msix_p; 527 528 if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) { 529 ret = DDI_SUCCESS; 530 goto done; 531 } 532 533 msix_p = i_ddi_get_msix(rdip); 534 535 /* Offset into the "inum"th entry in the MSI-X table */ 536 off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 537 PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 538 539 /* Clear the Mask bit */ 540 ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x0); 541 } 542 543 ret = DDI_SUCCESS; 544 done: 545 pci_config_teardown(&cfg_hdle); 546 return (ret); 547 } 548 549 550 /* 551 * pci_msi_get_pending: 552 * 553 * Get the pending bit from the MSI/X capability structure 554 */ 555 /* ARGSUSED */ 556 int 557 pci_msi_get_pending(dev_info_t *rdip, int type, int inum, int *pendingp) 558 { 559 ushort_t caps_ptr, msi_ctrl; 560 ddi_acc_handle_t cfg_hdle; 561 int offset; 562 int ret = DDI_FAILURE; 563 564 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: rdip = 0x%p\n", 565 (void *)rdip)); 566 567 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 568 &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 569 return (DDI_FAILURE); 570 571 if (type == DDI_INTR_TYPE_MSI) { 572 uint32_t pending_bits; 573 574 if (!(msi_ctrl & PCI_MSI_PVM_MASK)) { 575 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: " 576 "PVM is not supported\n")); 577 goto done; 578 } 579 580 offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ? 581 PCI_MSI_64BIT_PENDING : PCI_MSI_32BIT_PENDING; 582 583 if ((pending_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 584 offset)) == PCI_CAP_EINVAL32) 585 goto done; 586 587 *pendingp = pending_bits & ~(1 >> inum); 588 589 } else if (type == DDI_INTR_TYPE_MSIX) { 590 uintptr_t off; 591 uint64_t pending_bits; 592 ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 593 594 /* Offset into the PBA array which has entry for "inum" */ 595 off = (uintptr_t)msix_p->msix_pba_addr + (inum / 64); 596 597 /* Read the PBA array */ 598 pending_bits = ddi_get64(msix_p->msix_pba_hdl, (uint64_t *)off); 599 600 *pendingp = pending_bits & ~(1 >> inum); 601 } 602 603 ret = DDI_SUCCESS; 604 done: 605 pci_config_teardown(&cfg_hdle); 606 return (ret); 607 } 608 609 610 /* 611 * pci_msi_get_nintrs: 612 * 613 * For a given type (MSI/X) returns the number of interrupts supported 614 */ 615 int 616 pci_msi_get_nintrs(dev_info_t *rdip, int type, int *nintrs) 617 { 618 ushort_t caps_ptr, msi_ctrl; 619 ddi_acc_handle_t cfg_hdle; 620 621 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: rdip = 0x%p\n", 622 (void *)rdip)); 623 624 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 625 &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 626 return (DDI_FAILURE); 627 628 if (type == DDI_INTR_TYPE_MSI) { 629 *nintrs = 1 << ((msi_ctrl & PCI_MSI_MMC_MASK) >> 630 PCI_MSI_MMC_SHIFT); 631 } else if (type == DDI_INTR_TYPE_MSIX) { 632 if (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) 633 *nintrs = (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1; 634 } 635 636 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: " 637 "nintr = 0x%x\n", *nintrs)); 638 639 pci_config_teardown(&cfg_hdle); 640 return (DDI_SUCCESS); 641 } 642 643 644 /* 645 * pci_msi_set_nintrs: 646 * 647 * For a given type (MSI/X) sets the number of interrupts supported 648 * by the system. 649 * For MSI: Return an error if this func is called for navail > 32 650 * For MSI-X: Return an error if this func is called for navail > 2048 651 */ 652 int 653 pci_msi_set_nintrs(dev_info_t *rdip, int type, int navail) 654 { 655 ushort_t caps_ptr, msi_ctrl; 656 ddi_acc_handle_t cfg_hdle; 657 658 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: rdip = 0x%p, " 659 "navail = 0x%x\n", (void *)rdip, navail)); 660 661 /* Check for valid input argument */ 662 if (((type == DDI_INTR_TYPE_MSI) && (navail > PCI_MSI_MAX_INTRS)) || 663 ((type == DDI_INTR_TYPE_MSIX) && (navail > PCI_MSIX_MAX_INTRS))) 664 return (DDI_EINVAL); 665 666 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 667 &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 668 return (DDI_FAILURE); 669 670 if (type == DDI_INTR_TYPE_MSI) { 671 msi_ctrl |= ((highbit(navail) -1) << PCI_MSI_MME_SHIFT); 672 673 PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl); 674 } else if (type == DDI_INTR_TYPE_MSIX) { 675 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: unsupported\n")); 676 } 677 678 pci_config_teardown(&cfg_hdle); 679 return (DDI_SUCCESS); 680 } 681 682 683 /* 684 * pci_msi_get_supported_type: 685 * 686 * Returns DDI_INTR_TYPE_MSI and/or DDI_INTR_TYPE_MSIX as supported 687 * types if device supports them. A DDI_FAILURE is returned otherwise. 688 */ 689 int 690 pci_msi_get_supported_type(dev_info_t *rdip, int *typesp) 691 { 692 ushort_t caps_ptr, msi_ctrl; 693 ddi_acc_handle_t cfg_hdle; 694 695 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: " 696 "rdip = 0x%p\n", (void *)rdip)); 697 698 *typesp = 0; 699 700 if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSI, &msi_ctrl, 701 &caps_ptr, &cfg_hdle) == DDI_SUCCESS) { 702 *typesp |= DDI_INTR_TYPE_MSI; 703 pci_config_teardown(&cfg_hdle); 704 } 705 706 if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msi_ctrl, 707 &caps_ptr, &cfg_hdle) == DDI_SUCCESS) { 708 *typesp |= DDI_INTR_TYPE_MSIX; 709 pci_config_teardown(&cfg_hdle); 710 } 711 712 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: " 713 "rdip = 0x%p types 0x%x\n", (void *)rdip, *typesp)); 714 715 return (*typesp == 0 ? DDI_FAILURE : DDI_SUCCESS); 716 } 717 718 719 /* 720 * pci_msix_init: 721 * This function initializes the various handles/addrs etc. 722 * needed for MSI-X support. It also allocates a private 723 * structure to keep track of these. 724 */ 725 ddi_intr_msix_t * 726 pci_msix_init(dev_info_t *rdip) 727 { 728 uint_t rnumber, breg, nregs; 729 size_t msix_tbl_size; 730 size_t pba_tbl_size; 731 ushort_t caps_ptr, msix_ctrl; 732 ddi_intr_msix_t *msix_p; 733 ddi_acc_handle_t cfg_hdle; 734 pci_regspec_t *rp; 735 int reg_size, addr_space, offset, *regs_list; 736 int i, ret; 737 738 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: rdip = %p\n", (void *)rdip)); 739 740 if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msix_ctrl, 741 &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 742 return (NULL); 743 744 msix_p = kmem_zalloc(sizeof (ddi_intr_msix_t), KM_SLEEP); 745 746 /* 747 * Initialize the devacc structure 748 */ 749 msix_p->msix_dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 750 msix_p->msix_dev_attr.devacc_attr_endian_flags = 751 DDI_STRUCTURE_LE_ACC; 752 msix_p->msix_dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 753 754 /* Map the entire MSI-X vector table */ 755 msix_p->msix_tbl_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 756 PCI_MSIX_TBL_OFFSET); 757 758 if ((breg = pci_msix_bir_index[msix_p->msix_tbl_offset & 759 PCI_MSIX_TBL_BIR_MASK]) == 0xff) 760 goto fail1; 761 762 msix_p->msix_tbl_offset = msix_p->msix_tbl_offset & 763 ~PCI_MSIX_TBL_BIR_MASK; 764 msix_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1) * 765 PCI_MSIX_VECTOR_SIZE; 766 767 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X table offset 0x%x " 768 "breg 0x%x size 0x%lx\n", msix_p->msix_tbl_offset, breg, 769 msix_tbl_size)); 770 771 if ((ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip, 772 DDI_PROP_DONTPASS, "reg", (int **)®s_list, &nregs)) 773 != DDI_PROP_SUCCESS) { 774 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: " 775 "ddi_prop_lookup_int_array failed %d\n", ret)); 776 777 goto fail1; 778 } 779 780 reg_size = sizeof (pci_regspec_t) / sizeof (int); 781 782 for (i = 1, rnumber = 0; i < nregs/reg_size; i++) { 783 rp = (pci_regspec_t *)®s_list[i * reg_size]; 784 addr_space = rp->pci_phys_hi & PCI_ADDR_MASK; 785 offset = PCI_REG_REG_G(rp->pci_phys_hi); 786 787 if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) || 788 (addr_space == PCI_ADDR_MEM64))) { 789 rnumber = i; 790 break; 791 } 792 } 793 794 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X rnum = %d\n", rnumber)); 795 796 if (rnumber == 0) { 797 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: " 798 "no mtaching reg number for offset 0x%x\n", breg)); 799 800 goto fail2; 801 } 802 803 if ((ret = ddi_regs_map_setup(rdip, rnumber, 804 (caddr_t *)&msix_p->msix_tbl_addr, msix_p->msix_tbl_offset, 805 msix_tbl_size, &msix_p->msix_dev_attr, 806 &msix_p->msix_tbl_hdl)) != DDI_SUCCESS) { 807 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X Table " 808 "ddi_regs_map_setup failed %d\n", ret)); 809 810 goto fail2; 811 } 812 813 /* 814 * Map in the MSI-X Pending Bit Array 815 */ 816 msix_p->msix_pba_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 817 PCI_MSIX_PBA_OFFSET); 818 819 if ((breg = pci_msix_bir_index[msix_p->msix_pba_offset & 820 PCI_MSIX_PBA_BIR_MASK]) == 0xff) 821 goto fail3; 822 823 msix_p->msix_pba_offset = msix_p->msix_pba_offset & 824 ~PCI_MSIX_PBA_BIR_MASK; 825 pba_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1)/8; 826 827 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA table offset 0x%x " 828 "breg 0x%x size 0x%lx\n", msix_p->msix_pba_offset, breg, 829 pba_tbl_size)); 830 831 for (i = 1, rnumber = 0; i < nregs/reg_size; i++) { 832 rp = (pci_regspec_t *)®s_list[i * reg_size]; 833 addr_space = rp->pci_phys_hi & PCI_ADDR_MASK; 834 offset = PCI_REG_REG_G(rp->pci_phys_hi); 835 836 if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) || 837 (addr_space == PCI_ADDR_MEM64))) { 838 ddi_prop_free(regs_list); 839 rnumber = i; 840 break; 841 } 842 } 843 844 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA rnum = %d\n", rnumber)); 845 846 if (rnumber == 0) { 847 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: " 848 "no matching reg number for offset 0x%x\n", breg)); 849 850 goto fail3; 851 } 852 853 if ((ret = ddi_regs_map_setup(rdip, rnumber, 854 (caddr_t *)&msix_p->msix_pba_addr, msix_p->msix_pba_offset, 855 pba_tbl_size, &msix_p->msix_dev_attr, 856 &msix_p->msix_pba_hdl)) != DDI_SUCCESS) { 857 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA " 858 "ddi_regs_map_setup failed %d\n", ret)); 859 860 goto fail3; 861 } 862 863 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: msix_p = 0x%p DONE!!\n", 864 (void *)msix_p)); 865 866 goto done; 867 868 fail3: 869 ddi_regs_map_free(&msix_p->msix_tbl_hdl); 870 fail2: 871 ddi_prop_free(regs_list); 872 fail1: 873 kmem_free(msix_p, sizeof (ddi_intr_msix_t)); 874 msix_p = NULL; 875 done: 876 pci_config_teardown(&cfg_hdle); 877 return (msix_p); 878 } 879 880 881 /* 882 * pci_msix_fini: 883 * This function cleans up previously allocated handles/addrs etc. 884 * It is only called if no more MSI-X interrupts are being used. 885 */ 886 void 887 pci_msix_fini(ddi_intr_msix_t *msix_p) 888 { 889 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_fini: msix_p = 0x%p\n", 890 (void *)msix_p)); 891 892 ddi_regs_map_free(&msix_p->msix_pba_hdl); 893 ddi_regs_map_free(&msix_p->msix_tbl_hdl); 894 kmem_free(msix_p, sizeof (ddi_intr_msix_t)); 895 } 896 897 898 /* 899 * pci_msix_dup: 900 * This function duplicates the address and data pair of one msi-x 901 * vector to another msi-x vector. 902 */ 903 int 904 pci_msix_dup(dev_info_t *rdip, int org_inum, int dup_inum) 905 { 906 ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 907 uint64_t addr; 908 uint64_t data; 909 uintptr_t off; 910 911 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_dup: dip = %p, inum = 0x%x, " 912 "to_vector = 0x%x\n", (void *)rdip, org_inum, dup_inum)); 913 914 /* Offset into the original inum's entry in the MSI-X table */ 915 off = (uintptr_t)msix_p->msix_tbl_addr + 916 (org_inum * PCI_MSIX_VECTOR_SIZE); 917 918 /* For the MSI-X number passed in, get the "data" and "addr" fields */ 919 addr = ddi_get64(msix_p->msix_tbl_hdl, 920 (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET)); 921 922 data = ddi_get32(msix_p->msix_tbl_hdl, 923 (uint32_t *)(off + PCI_MSIX_DATA_OFFSET)); 924 925 /* Program new vector with these existing values */ 926 return (pci_msi_configure(rdip, DDI_INTR_TYPE_MSIX, 1, dup_inum, addr, 927 data)); 928 } 929 930 931 /* 932 * Next set of routines are for INTx (legacy) PCI interrupt 933 * support only. 934 */ 935 936 /* 937 * pci_intx_get_cap: 938 * For non-MSI devices that comply to PCI v2.3 or greater; 939 * read the command register. Bit 10 implies interrupt disable. 940 * Set this bit and then read the status register bit 3. 941 * Bit 3 of status register is Interrupt state. 942 * If it is set; then the device supports 'Masking' 943 * 944 * Reset the device back to the original state. 945 */ 946 int 947 pci_intx_get_cap(dev_info_t *dip, int *flagsp) 948 { 949 uint16_t cmdreg, savereg; 950 ddi_acc_handle_t cfg_hdl; 951 #ifdef DEBUG 952 uint16_t statreg; 953 #endif /* DEBUG */ 954 955 *flagsp = 0; 956 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: %s%d: called\n", 957 ddi_driver_name(dip), ddi_get_instance(dip))); 958 959 if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 960 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: can't get " 961 "config handle\n")); 962 return (DDI_FAILURE); 963 } 964 965 savereg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 966 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 967 "command register was 0x%x\n", savereg)); 968 969 /* Disable the interrupts */ 970 cmdreg = savereg | PCI_COMM_INTX_DISABLE; 971 pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg); 972 973 #ifdef DEBUG 974 statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT); 975 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 976 "status register is 0x%x\n", statreg)); 977 #endif /* DEBUG */ 978 979 /* Read the bit back */ 980 cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 981 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 982 "command register is now 0x%x\n", cmdreg)); 983 984 *flagsp = DDI_INTR_FLAG_LEVEL; 985 986 if (cmdreg & PCI_COMM_INTX_DISABLE) { 987 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 988 "masking supported\n")); 989 *flagsp |= (DDI_INTR_FLAG_MASKABLE | 990 DDI_INTR_FLAG_PENDING); 991 } 992 993 /* Restore the device back to the original state and return */ 994 pci_config_put16(cfg_hdl, PCI_CONF_COMM, savereg); 995 996 pci_config_teardown(&cfg_hdl); 997 return (DDI_SUCCESS); 998 } 999 1000 1001 /* 1002 * pci_intx_clr_mask: 1003 * For non-MSI devices that comply to PCI v2.3 or greater; 1004 * clear the bit10 in the command register. 1005 */ 1006 int 1007 pci_intx_clr_mask(dev_info_t *dip) 1008 { 1009 uint16_t cmdreg; 1010 ddi_acc_handle_t cfg_hdl; 1011 1012 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: %s%d: called\n", 1013 ddi_driver_name(dip), ddi_get_instance(dip))); 1014 1015 if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 1016 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: can't get " 1017 "config handle\n")); 1018 return (DDI_FAILURE); 1019 } 1020 1021 cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 1022 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: " 1023 "command register was 0x%x\n", cmdreg)); 1024 1025 /* Enable the interrupts */ 1026 cmdreg &= ~PCI_COMM_INTX_DISABLE; 1027 pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg); 1028 pci_config_teardown(&cfg_hdl); 1029 return (DDI_SUCCESS); 1030 } 1031 1032 1033 /* 1034 * pci_intx_set_mask: 1035 * For non-MSI devices that comply to PCI v2.3 or greater; 1036 * set the bit10 in the command register. 1037 */ 1038 int 1039 pci_intx_set_mask(dev_info_t *dip) 1040 { 1041 uint16_t cmdreg; 1042 ddi_acc_handle_t cfg_hdl; 1043 1044 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: %s%d: called\n", 1045 ddi_driver_name(dip), ddi_get_instance(dip))); 1046 1047 if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 1048 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: can't get " 1049 "config handle\n")); 1050 return (DDI_FAILURE); 1051 } 1052 1053 cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 1054 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: " 1055 "command register was 0x%x\n", cmdreg)); 1056 1057 /* Disable the interrupts */ 1058 cmdreg |= PCI_COMM_INTX_DISABLE; 1059 pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg); 1060 pci_config_teardown(&cfg_hdl); 1061 return (DDI_SUCCESS); 1062 } 1063 1064 /* 1065 * pci_intx_get_pending: 1066 * For non-MSI devices that comply to PCI v2.3 or greater; 1067 * read the status register. Bit 3 of status register is 1068 * Interrupt state. If it is set; then the interrupt is 1069 * 'Pending'. 1070 */ 1071 int 1072 pci_intx_get_pending(dev_info_t *dip, int *pendingp) 1073 { 1074 uint16_t statreg; 1075 ddi_acc_handle_t cfg_hdl; 1076 1077 *pendingp = 0; 1078 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: %s%d: called\n", 1079 ddi_driver_name(dip), ddi_get_instance(dip))); 1080 1081 if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 1082 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: can't get " 1083 "config handle\n")); 1084 return (DDI_FAILURE); 1085 } 1086 1087 statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT); 1088 1089 if (statreg & PCI_STAT_INTR) { 1090 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: " 1091 "interrupt is pending\n")); 1092 *pendingp = 1; 1093 } 1094 1095 pci_config_teardown(&cfg_hdl); 1096 return (DDI_SUCCESS); 1097 } 1098 1099 1100 /* 1101 * pci_devclass_to_ipl: 1102 * translate from device class to ipl 1103 * NOTE: This function is added here as pci_intx_get_ispec() 1104 * calls this to figure out the priority. 1105 * It is moved over from x86 pci.c 1106 */ 1107 int 1108 pci_devclass_to_ipl(int class) 1109 { 1110 int ipl; 1111 int base_class = (class & 0xff0000) >> 16; 1112 int sub_class = (class & 0xff00) >> 8; 1113 1114 /* 1115 * Use the class code values to construct an ipl for the device. 1116 */ 1117 1118 switch (base_class) { 1119 default: 1120 case PCI_CLASS_NONE: 1121 ipl = 1; 1122 break; 1123 case PCI_CLASS_MASS: 1124 ipl = 0x5; 1125 break; 1126 case PCI_CLASS_NET: 1127 ipl = 0x6; 1128 break; 1129 case PCI_CLASS_DISPLAY: 1130 ipl = 0x9; 1131 break; 1132 case PCI_CLASS_SERIALBUS: 1133 ipl = (sub_class == PCI_SERIAL_IB) ? 6 : 1; 1134 break; 1135 /* 1136 * for high priority interrupt handlers, use level 12 1137 * as the highest for device drivers 1138 */ 1139 case PCI_CLASS_MM: 1140 ipl = 0xc; 1141 break; 1142 case PCI_CLASS_MEM: 1143 ipl = 0xc; 1144 break; 1145 case PCI_CLASS_BRIDGE: 1146 ipl = 0xc; 1147 break; 1148 } 1149 1150 return (ipl); 1151 } 1152 1153 1154 /* 1155 * pci_intx_get_ispec: 1156 * Get intrspec for PCI devices (legacy support) 1157 * NOTE: This is moved here from x86 pci.c and is 1158 * needed here as pci-ide.c uses it as well 1159 */ 1160 /*ARGSUSED*/ 1161 ddi_intrspec_t 1162 pci_intx_get_ispec(dev_info_t *dip, dev_info_t *rdip, int inum) 1163 { 1164 int class, *intpriorities; 1165 uint_t num_intpriorities; 1166 struct intrspec *ispec; 1167 ddi_acc_handle_t cfg_hdl; 1168 struct ddi_parent_private_data *pdptr; 1169 1170 if ((pdptr = ddi_get_parent_data(rdip)) == NULL) 1171 return (NULL); 1172 1173 ispec = pdptr->par_intr; 1174 ASSERT(ispec); 1175 1176 /* check if the intrspec_pri has been initialized */ 1177 if (!ispec->intrspec_pri) { 1178 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip, 1179 DDI_PROP_DONTPASS, "interrupt-priorities", 1180 &intpriorities, &num_intpriorities) == DDI_PROP_SUCCESS) { 1181 if (inum < num_intpriorities) 1182 ispec->intrspec_pri = intpriorities[inum]; 1183 ddi_prop_free(intpriorities); 1184 } 1185 1186 /* If still no priority, guess based on the class code */ 1187 if (ispec->intrspec_pri == 0) { 1188 /* get 'class' property to derive the intr priority */ 1189 class = ddi_prop_get_int(DDI_DEV_T_ANY, rdip, 1190 DDI_PROP_DONTPASS, "class-code", -1); 1191 ispec->intrspec_pri = (class == -1) ? 1 : 1192 pci_devclass_to_ipl(class); 1193 } 1194 } 1195 1196 /* Get interrupt line value */ 1197 if (!ispec->intrspec_vec) { 1198 if (pci_config_setup(rdip, &cfg_hdl) != DDI_SUCCESS) { 1199 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_iline: " 1200 "can't get config handle\n")); 1201 return ((ddi_intrspec_t)ispec); 1202 } 1203 1204 ispec->intrspec_vec = pci_config_get8(cfg_hdl, PCI_CONF_ILINE); 1205 pci_config_teardown(&cfg_hdl); 1206 } 1207 1208 return ((ddi_intrspec_t)ispec); 1209 } 1210