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