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 2006 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\n", 244 (void *)rdip)); 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, (uint64_t *)off, 0); 282 } 283 284 pci_config_teardown(&h); 285 return (DDI_SUCCESS); 286 } 287 288 289 /* 290 * pci_is_msi_enabled: 291 * 292 * This function returns DDI_SUCCESS if MSI/X is already enabled, otherwise 293 * it returns DDI_FAILURE. 294 */ 295 int 296 pci_is_msi_enabled(dev_info_t *rdip, int type) 297 { 298 ushort_t caps_ptr, msi_ctrl; 299 ddi_acc_handle_t cfg_hdle; 300 int ret = DDI_FAILURE; 301 302 DDI_INTR_NEXDBG((CE_CONT, "pci_is_msi_enabled: rdip = 0x%p, " 303 "type = 0x%x\n", (void *)rdip, type)); 304 305 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 306 &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 307 return (DDI_FAILURE); 308 309 if ((type == DDI_INTR_TYPE_MSI) && (msi_ctrl & PCI_MSI_ENABLE_BIT)) 310 ret = DDI_SUCCESS; 311 312 if ((type == DDI_INTR_TYPE_MSIX) && (msi_ctrl & PCI_MSIX_ENABLE_BIT)) 313 ret = DDI_SUCCESS; 314 315 pci_config_teardown(&cfg_hdle); 316 return (ret); 317 } 318 319 320 /* 321 * pci_msi_enable_mode: 322 * 323 * This function sets the MSI_ENABLE bit in the capability structure 324 * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure. 325 */ 326 int 327 pci_msi_enable_mode(dev_info_t *rdip, int type, int inum) 328 { 329 ushort_t caps_ptr, msi_ctrl; 330 ddi_acc_handle_t cfg_hdle; 331 332 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: rdip = 0x%p, " 333 "inum = 0x%x\n", (void *)rdip, inum)); 334 335 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 336 &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 337 return (DDI_FAILURE); 338 339 if (type == DDI_INTR_TYPE_MSI) { 340 if (msi_ctrl & PCI_MSI_ENABLE_BIT) 341 goto finished; 342 343 msi_ctrl |= PCI_MSI_ENABLE_BIT; 344 PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl); 345 346 } else if (type == DDI_INTR_TYPE_MSIX) { 347 uintptr_t off; 348 ddi_intr_msix_t *msix_p; 349 350 if (msi_ctrl & PCI_MSIX_ENABLE_BIT) 351 goto finished; 352 353 msi_ctrl |= PCI_MSIX_ENABLE_BIT; 354 PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSIX_CTRL, 355 msi_ctrl); 356 357 msix_p = i_ddi_get_msix(rdip); 358 359 /* Offset into the "inum"th entry in the MSI-X table */ 360 off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 361 PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 362 363 /* Clear the Mask bit */ 364 ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x0); 365 366 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable: " 367 "msix_vector_mask 0x%x\n", 368 ddi_get32(msix_p->msix_tbl_hdl, (uint32_t *)off))); 369 } 370 371 finished: 372 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: msi_ctrl = %x\n", 373 msi_ctrl)); 374 375 pci_config_teardown(&cfg_hdle); 376 return (DDI_SUCCESS); 377 } 378 379 380 /* 381 * pci_msi_disable_mode: 382 * 383 * This function resets the MSI_ENABLE bit in the capability structure 384 * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure. 385 */ 386 int 387 pci_msi_disable_mode(dev_info_t *rdip, int type, int inum) 388 { 389 ushort_t caps_ptr, msi_ctrl; 390 ddi_acc_handle_t cfg_hdle; 391 392 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: rdip = 0x%p " 393 "inum = 0x%x\n", (void *)rdip, inum)); 394 395 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 396 &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 397 return (DDI_FAILURE); 398 399 /* Reset the "enable" bit */ 400 if (type == DDI_INTR_TYPE_MSI) { 401 if (!(msi_ctrl & PCI_MSI_ENABLE_BIT)) 402 goto finished; 403 msi_ctrl &= ~PCI_MSI_ENABLE_BIT; 404 PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl); 405 } else if (type == DDI_INTR_TYPE_MSIX) { 406 uintptr_t off; 407 ddi_intr_msix_t *msix_p; 408 409 if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT)) 410 goto finished; 411 412 msix_p = i_ddi_get_msix(rdip); 413 414 /* Offset into the "inum"th entry in the MSI-X table */ 415 off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 416 PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 417 418 /* Set the Mask bit */ 419 ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1); 420 } 421 422 finished: 423 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: msi_ctrl = %x\n", 424 msi_ctrl)); 425 426 pci_config_teardown(&cfg_hdle); 427 return (DDI_SUCCESS); 428 } 429 430 431 /* 432 * pci_msi_set_mask: 433 * 434 * Set the mask bit in the MSI/X capability structure 435 */ 436 /* ARGSUSED */ 437 int 438 pci_msi_set_mask(dev_info_t *rdip, int type, int inum) 439 { 440 int offset; 441 int ret = DDI_FAILURE; 442 ushort_t caps_ptr, msi_ctrl; 443 ddi_acc_handle_t cfg_hdle; 444 uint32_t mask_bits; 445 446 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_mask: rdip = 0x%p, " 447 "type = 0x%x\n", (void *)rdip, type)); 448 449 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 450 &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 451 return (DDI_FAILURE); 452 453 if (type == DDI_INTR_TYPE_MSI) { 454 if (!(msi_ctrl & PCI_MSI_PVM_MASK)) 455 goto done; 456 457 offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ? 458 PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK; 459 460 if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 461 offset)) == PCI_CAP_EINVAL32) 462 goto done; 463 464 mask_bits |= (1 << inum); 465 466 PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits); 467 468 } else if (type == DDI_INTR_TYPE_MSIX) { 469 uintptr_t off; 470 ddi_intr_msix_t *msix_p; 471 472 /* Set function mask */ 473 if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) { 474 ret = DDI_SUCCESS; 475 goto done; 476 } 477 478 msix_p = i_ddi_get_msix(rdip); 479 480 /* Offset into the "inum"th entry in the MSI-X table */ 481 off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 482 PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 483 484 /* Set the Mask bit */ 485 ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1); 486 } 487 488 ret = DDI_SUCCESS; 489 done: 490 pci_config_teardown(&cfg_hdle); 491 return (ret); 492 } 493 494 495 /* 496 * pci_msi_clr_mask: 497 * 498 * Clear the mask bit in the MSI/X capability structure 499 */ 500 /* ARGSUSED */ 501 int 502 pci_msi_clr_mask(dev_info_t *rdip, int type, int inum) 503 { 504 ushort_t caps_ptr, msi_ctrl; 505 ddi_acc_handle_t cfg_hdle; 506 int offset; 507 int ret = DDI_FAILURE; 508 uint32_t mask_bits; 509 510 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_clr_mask: rdip = 0x%p, " 511 "type = 0x%x\n", (void *)rdip, type)); 512 513 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 514 &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 515 return (DDI_FAILURE); 516 517 if (type == DDI_INTR_TYPE_MSI) { 518 if (!(msi_ctrl & PCI_MSI_PVM_MASK)) 519 goto done; 520 521 offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ? 522 PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK; 523 if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 524 offset)) == PCI_CAP_EINVAL32) 525 goto done; 526 527 mask_bits &= ~(1 << inum); 528 529 PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits); 530 531 } else if (type == DDI_INTR_TYPE_MSIX) { 532 uintptr_t off; 533 ddi_intr_msix_t *msix_p; 534 535 if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) { 536 ret = DDI_SUCCESS; 537 goto done; 538 } 539 540 msix_p = i_ddi_get_msix(rdip); 541 542 /* Offset into the "inum"th entry in the MSI-X table */ 543 off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 544 PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 545 546 /* Clear the Mask bit */ 547 ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x0); 548 } 549 550 ret = DDI_SUCCESS; 551 done: 552 pci_config_teardown(&cfg_hdle); 553 return (ret); 554 } 555 556 557 /* 558 * pci_msi_get_pending: 559 * 560 * Get the pending bit from the MSI/X capability structure 561 */ 562 /* ARGSUSED */ 563 int 564 pci_msi_get_pending(dev_info_t *rdip, int type, int inum, int *pendingp) 565 { 566 ushort_t caps_ptr, msi_ctrl; 567 ddi_acc_handle_t cfg_hdle; 568 int offset; 569 int ret = DDI_FAILURE; 570 571 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: rdip = 0x%p\n", 572 (void *)rdip)); 573 574 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 575 &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 576 return (DDI_FAILURE); 577 578 if (type == DDI_INTR_TYPE_MSI) { 579 uint32_t pending_bits; 580 581 if (!(msi_ctrl & PCI_MSI_PVM_MASK)) { 582 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: " 583 "PVM is not supported\n")); 584 goto done; 585 } 586 587 offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ? 588 PCI_MSI_64BIT_PENDING : PCI_MSI_32BIT_PENDING; 589 590 if ((pending_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 591 offset)) == PCI_CAP_EINVAL32) 592 goto done; 593 594 *pendingp = pending_bits & ~(1 >> inum); 595 596 } else if (type == DDI_INTR_TYPE_MSIX) { 597 uintptr_t off; 598 uint64_t pending_bits; 599 ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 600 601 /* Offset into the PBA array which has entry for "inum" */ 602 off = (uintptr_t)msix_p->msix_pba_addr + (inum / 64); 603 604 /* Read the PBA array */ 605 pending_bits = ddi_get64(msix_p->msix_pba_hdl, (uint64_t *)off); 606 607 *pendingp = pending_bits & ~(1 >> inum); 608 } 609 610 ret = DDI_SUCCESS; 611 done: 612 pci_config_teardown(&cfg_hdle); 613 return (ret); 614 } 615 616 617 /* 618 * pci_msi_get_nintrs: 619 * 620 * For a given type (MSI/X) returns the number of interrupts supported 621 */ 622 int 623 pci_msi_get_nintrs(dev_info_t *rdip, int type, int *nintrs) 624 { 625 ushort_t caps_ptr, msi_ctrl; 626 ddi_acc_handle_t cfg_hdle; 627 628 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: rdip = 0x%p\n", 629 (void *)rdip)); 630 631 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 632 &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 633 return (DDI_FAILURE); 634 635 if (type == DDI_INTR_TYPE_MSI) { 636 *nintrs = 1 << ((msi_ctrl & PCI_MSI_MMC_MASK) >> 637 PCI_MSI_MMC_SHIFT); 638 } else if (type == DDI_INTR_TYPE_MSIX) { 639 if (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) 640 *nintrs = (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1; 641 } 642 643 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: " 644 "nintr = 0x%x\n", *nintrs)); 645 646 pci_config_teardown(&cfg_hdle); 647 return (DDI_SUCCESS); 648 } 649 650 651 /* 652 * pci_msi_set_nintrs: 653 * 654 * For a given type (MSI/X) sets the number of interrupts supported 655 * by the system. 656 * For MSI: Return an error if this func is called for navail > 32 657 * For MSI-X: Return an error if this func is called for navail > 2048 658 */ 659 int 660 pci_msi_set_nintrs(dev_info_t *rdip, int type, int navail) 661 { 662 ushort_t caps_ptr, msi_ctrl; 663 ddi_acc_handle_t cfg_hdle; 664 665 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: rdip = 0x%p, " 666 "navail = 0x%x\n", (void *)rdip, navail)); 667 668 /* Check for valid input argument */ 669 if (((type == DDI_INTR_TYPE_MSI) && (navail > PCI_MSI_MAX_INTRS)) || 670 ((type == DDI_INTR_TYPE_MSIX) && (navail > PCI_MSIX_MAX_INTRS))) 671 return (DDI_EINVAL); 672 673 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 674 &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 675 return (DDI_FAILURE); 676 677 if (type == DDI_INTR_TYPE_MSI) { 678 msi_ctrl |= ((highbit(navail) -1) << PCI_MSI_MME_SHIFT); 679 680 PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl); 681 } else if (type == DDI_INTR_TYPE_MSIX) { 682 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: unsupported\n")); 683 } 684 685 pci_config_teardown(&cfg_hdle); 686 return (DDI_SUCCESS); 687 } 688 689 690 /* 691 * pci_msi_get_supported_type: 692 * 693 * Returns DDI_INTR_TYPE_MSI and/or DDI_INTR_TYPE_MSIX as supported 694 * types if device supports them. A DDI_FAILURE is returned otherwise. 695 */ 696 int 697 pci_msi_get_supported_type(dev_info_t *rdip, int *typesp) 698 { 699 ushort_t caps_ptr, msi_ctrl; 700 ddi_acc_handle_t cfg_hdle; 701 702 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: " 703 "rdip = 0x%p\n", (void *)rdip)); 704 705 *typesp = 0; 706 707 if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSI, &msi_ctrl, 708 &caps_ptr, &cfg_hdle) == DDI_SUCCESS) { 709 *typesp |= DDI_INTR_TYPE_MSI; 710 pci_config_teardown(&cfg_hdle); 711 } 712 713 if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msi_ctrl, 714 &caps_ptr, &cfg_hdle) == DDI_SUCCESS) { 715 *typesp |= DDI_INTR_TYPE_MSIX; 716 pci_config_teardown(&cfg_hdle); 717 } 718 719 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: " 720 "rdip = 0x%p types 0x%x\n", (void *)rdip, *typesp)); 721 722 return (*typesp == 0 ? DDI_FAILURE : DDI_SUCCESS); 723 } 724 725 726 /* 727 * pci_msix_init: 728 * This function initializes the various handles/addrs etc. 729 * needed for MSI-X support. It also allocates a private 730 * structure to keep track of these. 731 */ 732 ddi_intr_msix_t * 733 pci_msix_init(dev_info_t *rdip) 734 { 735 uint_t rnumber, breg, nregs; 736 size_t msix_tbl_size; 737 size_t pba_tbl_size; 738 ushort_t caps_ptr, msix_ctrl; 739 ddi_intr_msix_t *msix_p; 740 ddi_acc_handle_t cfg_hdle; 741 pci_regspec_t *rp; 742 int reg_size, addr_space, offset, *regs_list; 743 int i, ret; 744 745 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: rdip = %p\n", (void *)rdip)); 746 747 if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msix_ctrl, 748 &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 749 return (NULL); 750 751 msix_p = kmem_zalloc(sizeof (ddi_intr_msix_t), KM_SLEEP); 752 753 /* 754 * Initialize the devacc structure 755 */ 756 msix_p->msix_dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 757 msix_p->msix_dev_attr.devacc_attr_endian_flags = 758 DDI_STRUCTURE_LE_ACC; 759 msix_p->msix_dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 760 761 /* Map the entire MSI-X vector table */ 762 msix_p->msix_tbl_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 763 PCI_MSIX_TBL_OFFSET); 764 765 if ((breg = pci_msix_bir_index[msix_p->msix_tbl_offset & 766 PCI_MSIX_TBL_BIR_MASK]) == 0xff) 767 goto fail1; 768 769 msix_p->msix_tbl_offset = msix_p->msix_tbl_offset & 770 ~PCI_MSIX_TBL_BIR_MASK; 771 msix_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1) * 772 PCI_MSIX_VECTOR_SIZE; 773 774 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X table offset 0x%x " 775 "breg 0x%x size 0x%lx\n", msix_p->msix_tbl_offset, breg, 776 msix_tbl_size)); 777 778 if ((ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip, 779 DDI_PROP_DONTPASS, "reg", (int **)®s_list, &nregs)) 780 != DDI_PROP_SUCCESS) { 781 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: " 782 "ddi_prop_lookup_int_array failed %d\n", ret)); 783 784 goto fail1; 785 } 786 787 reg_size = sizeof (pci_regspec_t) / sizeof (int); 788 789 for (i = 1, rnumber = 0; i < nregs/reg_size; i++) { 790 rp = (pci_regspec_t *)®s_list[i * reg_size]; 791 addr_space = rp->pci_phys_hi & PCI_ADDR_MASK; 792 offset = PCI_REG_REG_G(rp->pci_phys_hi); 793 794 if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) || 795 (addr_space == PCI_ADDR_MEM64))) { 796 rnumber = i; 797 break; 798 } 799 } 800 801 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X rnum = %d\n", rnumber)); 802 803 if (rnumber == 0) { 804 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: " 805 "no mtaching reg number for offset 0x%x\n", breg)); 806 807 goto fail2; 808 } 809 810 if ((ret = ddi_regs_map_setup(rdip, rnumber, 811 (caddr_t *)&msix_p->msix_tbl_addr, msix_p->msix_tbl_offset, 812 msix_tbl_size, &msix_p->msix_dev_attr, 813 &msix_p->msix_tbl_hdl)) != DDI_SUCCESS) { 814 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X Table " 815 "ddi_regs_map_setup failed %d\n", ret)); 816 817 goto fail2; 818 } 819 820 /* 821 * Map in the MSI-X Pending Bit Array 822 */ 823 msix_p->msix_pba_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 824 PCI_MSIX_PBA_OFFSET); 825 826 if ((breg = pci_msix_bir_index[msix_p->msix_pba_offset & 827 PCI_MSIX_PBA_BIR_MASK]) == 0xff) 828 goto fail3; 829 830 msix_p->msix_pba_offset = msix_p->msix_pba_offset & 831 ~PCI_MSIX_PBA_BIR_MASK; 832 pba_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1)/8; 833 834 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA table offset 0x%x " 835 "breg 0x%x size 0x%lx\n", msix_p->msix_pba_offset, breg, 836 pba_tbl_size)); 837 838 for (i = 1, rnumber = 0; i < nregs/reg_size; i++) { 839 rp = (pci_regspec_t *)®s_list[i * reg_size]; 840 addr_space = rp->pci_phys_hi & PCI_ADDR_MASK; 841 offset = PCI_REG_REG_G(rp->pci_phys_hi); 842 843 if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) || 844 (addr_space == PCI_ADDR_MEM64))) { 845 ddi_prop_free(regs_list); 846 rnumber = i; 847 break; 848 } 849 } 850 851 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA rnum = %d\n", rnumber)); 852 853 if (rnumber == 0) { 854 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: " 855 "no matching reg number for offset 0x%x\n", breg)); 856 857 goto fail3; 858 } 859 860 if ((ret = ddi_regs_map_setup(rdip, rnumber, 861 (caddr_t *)&msix_p->msix_pba_addr, msix_p->msix_pba_offset, 862 pba_tbl_size, &msix_p->msix_dev_attr, 863 &msix_p->msix_pba_hdl)) != DDI_SUCCESS) { 864 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA " 865 "ddi_regs_map_setup failed %d\n", ret)); 866 867 goto fail3; 868 } 869 870 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: msix_p = 0x%p DONE!!\n", 871 (void *)msix_p)); 872 873 goto done; 874 875 fail3: 876 ddi_regs_map_free(&msix_p->msix_tbl_hdl); 877 fail2: 878 ddi_prop_free(regs_list); 879 fail1: 880 kmem_free(msix_p, sizeof (ddi_intr_msix_t)); 881 msix_p = NULL; 882 done: 883 pci_config_teardown(&cfg_hdle); 884 return (msix_p); 885 } 886 887 888 /* 889 * pci_msix_fini: 890 * This function cleans up previously allocated handles/addrs etc. 891 * It is only called if no more MSI-X interrupts are being used. 892 */ 893 void 894 pci_msix_fini(ddi_intr_msix_t *msix_p) 895 { 896 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_fini: msix_p = 0x%p\n", 897 (void *)msix_p)); 898 899 ddi_regs_map_free(&msix_p->msix_pba_hdl); 900 ddi_regs_map_free(&msix_p->msix_tbl_hdl); 901 kmem_free(msix_p, sizeof (ddi_intr_msix_t)); 902 } 903 904 905 /* 906 * pci_msix_dup: 907 * This function duplicates the address and data pair of one msi-x 908 * vector to another msi-x vector. 909 */ 910 int 911 pci_msix_dup(dev_info_t *rdip, int org_inum, int dup_inum) 912 { 913 ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 914 uint64_t addr; 915 uint64_t data; 916 uintptr_t off; 917 918 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_dup: dip = %p, inum = 0x%x, " 919 "to_vector = 0x%x\n", (void *)rdip, org_inum, dup_inum)); 920 921 /* Offset into the original inum's entry in the MSI-X table */ 922 off = (uintptr_t)msix_p->msix_tbl_addr + 923 (org_inum * PCI_MSIX_VECTOR_SIZE); 924 925 /* For the MSI-X number passed in, get the "data" and "addr" fields */ 926 addr = ddi_get64(msix_p->msix_tbl_hdl, 927 (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET)); 928 929 data = ddi_get32(msix_p->msix_tbl_hdl, 930 (uint32_t *)(off + PCI_MSIX_DATA_OFFSET)); 931 932 /* Program new vector with these existing values */ 933 return (pci_msi_configure(rdip, DDI_INTR_TYPE_MSIX, 1, dup_inum, addr, 934 data)); 935 } 936 937 938 /* 939 * Next set of routines are for INTx (legacy) PCI interrupt 940 * support only. 941 */ 942 943 /* 944 * pci_intx_get_cap: 945 * For non-MSI devices that comply to PCI v2.3 or greater; 946 * read the command register. Bit 10 implies interrupt disable. 947 * Set this bit and then read the status register bit 3. 948 * Bit 3 of status register is Interrupt state. 949 * If it is set; then the device supports 'Masking' 950 * 951 * Reset the device back to the original state. 952 */ 953 int 954 pci_intx_get_cap(dev_info_t *dip, int *flagsp) 955 { 956 uint16_t cmdreg, savereg; 957 ddi_acc_handle_t cfg_hdl; 958 #ifdef DEBUG 959 uint16_t statreg; 960 #endif /* DEBUG */ 961 962 *flagsp = 0; 963 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: %s%d: called\n", 964 ddi_driver_name(dip), ddi_get_instance(dip))); 965 966 if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 967 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: can't get " 968 "config handle\n")); 969 return (DDI_FAILURE); 970 } 971 972 savereg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 973 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 974 "command register was 0x%x\n", savereg)); 975 976 /* Disable the interrupts */ 977 cmdreg = savereg | PCI_COMM_INTX_DISABLE; 978 pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg); 979 980 #ifdef DEBUG 981 statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT); 982 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 983 "status register is 0x%x\n", statreg)); 984 #endif /* DEBUG */ 985 986 /* Read the bit back */ 987 cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 988 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 989 "command register is now 0x%x\n", cmdreg)); 990 991 *flagsp = DDI_INTR_FLAG_LEVEL; 992 993 if (cmdreg & PCI_COMM_INTX_DISABLE) { 994 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 995 "masking supported\n")); 996 *flagsp |= (DDI_INTR_FLAG_MASKABLE | 997 DDI_INTR_FLAG_PENDING); 998 } 999 1000 /* Restore the device back to the original state and return */ 1001 pci_config_put16(cfg_hdl, PCI_CONF_COMM, savereg); 1002 1003 pci_config_teardown(&cfg_hdl); 1004 return (DDI_SUCCESS); 1005 } 1006 1007 1008 /* 1009 * pci_intx_clr_mask: 1010 * For non-MSI devices that comply to PCI v2.3 or greater; 1011 * clear the bit10 in the command register. 1012 */ 1013 int 1014 pci_intx_clr_mask(dev_info_t *dip) 1015 { 1016 uint16_t cmdreg; 1017 ddi_acc_handle_t cfg_hdl; 1018 1019 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: %s%d: called\n", 1020 ddi_driver_name(dip), ddi_get_instance(dip))); 1021 1022 if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 1023 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: can't get " 1024 "config handle\n")); 1025 return (DDI_FAILURE); 1026 } 1027 1028 cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 1029 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: " 1030 "command register was 0x%x\n", cmdreg)); 1031 1032 /* Enable the interrupts */ 1033 cmdreg &= ~PCI_COMM_INTX_DISABLE; 1034 pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg); 1035 pci_config_teardown(&cfg_hdl); 1036 return (DDI_SUCCESS); 1037 } 1038 1039 1040 /* 1041 * pci_intx_set_mask: 1042 * For non-MSI devices that comply to PCI v2.3 or greater; 1043 * set the bit10 in the command register. 1044 */ 1045 int 1046 pci_intx_set_mask(dev_info_t *dip) 1047 { 1048 uint16_t cmdreg; 1049 ddi_acc_handle_t cfg_hdl; 1050 1051 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: %s%d: called\n", 1052 ddi_driver_name(dip), ddi_get_instance(dip))); 1053 1054 if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 1055 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: can't get " 1056 "config handle\n")); 1057 return (DDI_FAILURE); 1058 } 1059 1060 cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 1061 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: " 1062 "command register was 0x%x\n", cmdreg)); 1063 1064 /* Disable the interrupts */ 1065 cmdreg |= PCI_COMM_INTX_DISABLE; 1066 pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg); 1067 pci_config_teardown(&cfg_hdl); 1068 return (DDI_SUCCESS); 1069 } 1070 1071 /* 1072 * pci_intx_get_pending: 1073 * For non-MSI devices that comply to PCI v2.3 or greater; 1074 * read the status register. Bit 3 of status register is 1075 * Interrupt state. If it is set; then the interrupt is 1076 * 'Pending'. 1077 */ 1078 int 1079 pci_intx_get_pending(dev_info_t *dip, int *pendingp) 1080 { 1081 uint16_t statreg; 1082 ddi_acc_handle_t cfg_hdl; 1083 1084 *pendingp = 0; 1085 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: %s%d: called\n", 1086 ddi_driver_name(dip), ddi_get_instance(dip))); 1087 1088 if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 1089 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: can't get " 1090 "config handle\n")); 1091 return (DDI_FAILURE); 1092 } 1093 1094 statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT); 1095 1096 if (statreg & PCI_STAT_INTR) { 1097 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: " 1098 "interrupt is pending\n")); 1099 *pendingp = 1; 1100 } 1101 1102 pci_config_teardown(&cfg_hdl); 1103 return (DDI_SUCCESS); 1104 } 1105 1106 1107 /* 1108 * pci_devclass_to_ipl: 1109 * translate from device class to ipl 1110 * NOTE: This function is added here as pci_intx_get_ispec() 1111 * calls this to figure out the priority. 1112 * It is moved over from x86 pci.c 1113 */ 1114 int 1115 pci_devclass_to_ipl(int class) 1116 { 1117 int base_cl; 1118 int ipl; 1119 1120 base_cl = (class & 0xff0000) >> 16; 1121 1122 /* 1123 * Use the class code values to construct an ipl for the device. 1124 */ 1125 switch (base_cl) { 1126 default: 1127 case PCI_CLASS_NONE: 1128 ipl = 1; 1129 break; 1130 case PCI_CLASS_MASS: 1131 ipl = 0x5; 1132 break; 1133 case PCI_CLASS_NET: 1134 ipl = 0x6; 1135 break; 1136 case PCI_CLASS_DISPLAY: 1137 ipl = 0x9; 1138 break; 1139 /* 1140 * for high priority interrupt handlers, use level 12 1141 * as the highest for device drivers 1142 */ 1143 case PCI_CLASS_MM: 1144 ipl = 0xc; 1145 break; 1146 case PCI_CLASS_MEM: 1147 ipl = 0xc; 1148 break; 1149 case PCI_CLASS_BRIDGE: 1150 ipl = 0xc; 1151 break; 1152 } 1153 return (ipl); 1154 } 1155 1156 1157 /* 1158 * pci_intx_get_ispec: 1159 * Get intrspec for PCI devices (legacy support) 1160 * NOTE: This is moved here from x86 pci.c and is 1161 * needed here as pci-ide.c uses it as well 1162 */ 1163 /*ARGSUSED*/ 1164 ddi_intrspec_t 1165 pci_intx_get_ispec(dev_info_t *dip, dev_info_t *rdip, int inum) 1166 { 1167 int class, *intpriorities; 1168 uint_t num_intpriorities; 1169 struct intrspec *ispec; 1170 ddi_acc_handle_t cfg_hdl; 1171 struct ddi_parent_private_data *pdptr; 1172 1173 if ((pdptr = ddi_get_parent_data(rdip)) == NULL) 1174 return (NULL); 1175 1176 ispec = pdptr->par_intr; 1177 ASSERT(ispec); 1178 1179 /* check if the intrspec_pri has been initialized */ 1180 if (!ispec->intrspec_pri) { 1181 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip, 1182 DDI_PROP_DONTPASS, "interrupt-priorities", 1183 &intpriorities, &num_intpriorities) == DDI_PROP_SUCCESS) { 1184 if (inum < num_intpriorities) 1185 ispec->intrspec_pri = intpriorities[inum]; 1186 ddi_prop_free(intpriorities); 1187 } 1188 1189 /* If still no priority, guess based on the class code */ 1190 if (ispec->intrspec_pri == 0) { 1191 /* get 'class' property to derive the intr priority */ 1192 class = ddi_prop_get_int(DDI_DEV_T_ANY, rdip, 1193 DDI_PROP_DONTPASS, "class-code", -1); 1194 ispec->intrspec_pri = (class == -1) ? 1 : 1195 pci_devclass_to_ipl(class); 1196 } 1197 } 1198 1199 /* Get interrupt line value */ 1200 if (!ispec->intrspec_vec) { 1201 if (pci_config_setup(rdip, &cfg_hdl) != DDI_SUCCESS) { 1202 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_iline: " 1203 "can't get config handle\n")); 1204 return ((ddi_intrspec_t)ispec); 1205 } 1206 1207 ispec->intrspec_vec = pci_config_get8(cfg_hdl, PCI_CONF_ILINE); 1208 pci_config_teardown(&cfg_hdl); 1209 } 1210 1211 return ((ddi_intrspec_t)ispec); 1212 } 1213