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