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