1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * apic_introp.c: 30 * Has code for Advanced DDI interrupt framework support. 31 */ 32 33 #include <sys/cpuvar.h> 34 #include <sys/psm.h> 35 #include <sys/archsystm.h> 36 #include <sys/apic.h> 37 #include <sys/sunddi.h> 38 #include <sys/ddi_impldefs.h> 39 #include <sys/mach_intr.h> 40 #include <sys/sysmacros.h> 41 #include <sys/trap.h> 42 #include <sys/pci.h> 43 #include <sys/pci_intr_lib.h> 44 45 extern struct av_head autovect[]; 46 47 /* 48 * Local Function Prototypes 49 */ 50 int apic_pci_msi_enable_vector(dev_info_t *, int, int, 51 int, int, int); 52 apic_irq_t *apic_find_irq(dev_info_t *, struct intrspec *, int); 53 static int apic_get_pending(apic_irq_t *, int); 54 static void apic_clear_mask(apic_irq_t *); 55 static void apic_set_mask(apic_irq_t *); 56 57 /* 58 * MSI support flag: 59 * reflects whether MSI is supported at APIC level 60 * it can also be patched through /etc/system 61 * 62 * 0 = default value - don't know and need to call apic_check_msi_support() 63 * to find out then set it accordingly 64 * 1 = supported 65 * -1 = not supported 66 */ 67 int apic_support_msi = 0; 68 69 /* Multiple vector support for MSI */ 70 int apic_multi_msi_enable = 1; 71 int apic_multi_msi_max = 2; 72 73 /* 74 * apic_pci_msi_enable_vector: 75 * Set the address/data fields in the MSI/X capability structure 76 * XXX: MSI-X support 77 */ 78 /* ARGSUSED */ 79 int 80 apic_pci_msi_enable_vector(dev_info_t *dip, int type, int inum, int vector, 81 int count, int target_apic_id) 82 { 83 uint64_t msi_addr, msi_data; 84 ushort_t msi_ctrl; 85 int cap_ptr = i_ddi_get_msi_msix_cap_ptr(dip); 86 ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(dip); 87 88 DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: dip=0x%p\n" 89 "\tdriver = %s, inum=0x%x vector=0x%x apicid=0x%x\n", (void *)dip, 90 ddi_driver_name(dip), inum, vector, target_apic_id)); 91 92 if (handle == NULL || cap_ptr == 0) 93 return (PSM_FAILURE); 94 95 /* MSI Address */ 96 msi_addr = (MSI_ADDR_HDR | (target_apic_id << MSI_ADDR_DEST_SHIFT)); 97 msi_addr |= ((MSI_ADDR_RH_FIXED << MSI_ADDR_RH_SHIFT) | 98 (MSI_ADDR_DM_PHYSICAL << MSI_ADDR_DM_SHIFT)); 99 100 /* MSI Data: MSI is edge triggered according to spec */ 101 msi_data = ((MSI_DATA_TM_EDGE << MSI_DATA_TM_SHIFT) | vector); 102 103 DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: addr=0x%lx " 104 "data=0x%lx\n", (long)msi_addr, (long)msi_data)); 105 106 if (type == DDI_INTR_TYPE_MSI) { 107 msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 108 109 /* Set the bits to inform how many MSIs are enabled */ 110 msi_ctrl |= ((highbit(count) -1) << PCI_MSI_MME_SHIFT); 111 pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 112 113 pci_config_put32(handle, 114 cap_ptr + PCI_MSI_ADDR_OFFSET, msi_addr); 115 116 if (msi_ctrl & PCI_MSI_64BIT_MASK) { 117 pci_config_put32(handle, 118 cap_ptr + PCI_MSI_ADDR_OFFSET + 4, msi_addr >> 32); 119 pci_config_put16(handle, 120 cap_ptr + PCI_MSI_64BIT_DATA, msi_data); 121 } else { 122 pci_config_put16(handle, 123 cap_ptr + PCI_MSI_32BIT_DATA, msi_data); 124 } 125 126 } else if (type == DDI_INTR_TYPE_MSIX) { 127 uintptr_t off; 128 ddi_intr_msix_t *msix_p = i_ddi_get_msix(dip); 129 130 /* Offset into the "inum"th entry in the MSI-X table */ 131 off = (uintptr_t)msix_p->msix_tbl_addr + 132 (inum * PCI_MSIX_VECTOR_SIZE); 133 134 ddi_put32(msix_p->msix_tbl_hdl, 135 (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), msi_data); 136 ddi_put64(msix_p->msix_tbl_hdl, 137 (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), msi_addr); 138 } 139 140 return (PSM_SUCCESS); 141 } 142 143 144 /* 145 * This function returns the no. of vectors available for the pri. 146 * dip is not used at this moment. If we really don't need that, 147 * it will be removed. 148 */ 149 /*ARGSUSED*/ 150 int 151 apic_navail_vector(dev_info_t *dip, int pri) 152 { 153 int lowest, highest, i, navail, count; 154 155 DDI_INTR_IMPLDBG((CE_CONT, "apic_navail_vector: dip: %p, pri: %x\n", 156 (void *)dip, pri)); 157 158 highest = apic_ipltopri[pri] + APIC_VECTOR_MASK; 159 lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL; 160 navail = count = 0; 161 162 /* It has to be contiguous */ 163 for (i = lowest; i < highest; i++) { 164 count = 0; 165 while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) && 166 (i < highest)) { 167 if (APIC_CHECK_RESERVE_VECTORS(i)) 168 break; 169 count++; 170 i++; 171 } 172 if (count > navail) 173 navail = count; 174 } 175 return (navail); 176 } 177 178 /* 179 * Finds "count" contiguous MSI vectors starting at the proper alignment 180 * at "pri". 181 * Caller needs to make sure that count has to be power of 2 and should not 182 * be < 1. 183 */ 184 uchar_t 185 apic_find_multi_vectors(int pri, int count) 186 { 187 int lowest, highest, i, navail, start, msibits; 188 189 DDI_INTR_IMPLDBG((CE_CONT, "apic_find_mult: pri: %x, count: %x\n", 190 pri, count)); 191 192 highest = apic_ipltopri[pri] + APIC_VECTOR_MASK; 193 lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL; 194 navail = 0; 195 196 /* 197 * msibits is the no. of lower order message data bits for the 198 * allocated MSI vectors and is used to calculate the aligned 199 * starting vector 200 */ 201 msibits = count - 1; 202 203 /* It has to be contiguous */ 204 for (i = lowest; i < highest; i++) { 205 navail = 0; 206 207 /* 208 * starting vector has to be aligned accordingly for 209 * multiple MSIs 210 */ 211 if (msibits) 212 i = (i + msibits) & ~msibits; 213 start = i; 214 while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) && 215 (i < highest)) { 216 if (APIC_CHECK_RESERVE_VECTORS(i)) 217 break; 218 navail++; 219 if (navail >= count) 220 return (start); 221 i++; 222 } 223 } 224 return (0); 225 } 226 227 228 /* 229 * It finds the apic_irq_t associates with the dip, ispec and type. 230 */ 231 apic_irq_t * 232 apic_find_irq(dev_info_t *dip, struct intrspec *ispec, int type) 233 { 234 apic_irq_t *irqp; 235 int i; 236 237 DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: dip=0x%p vec=0x%x " 238 "ipl=0x%x type=0x%x\n", (void *)dip, ispec->intrspec_vec, 239 ispec->intrspec_pri, type)); 240 241 for (i = apic_min_device_irq; i <= apic_max_device_irq; i++) { 242 if ((irqp = apic_irq_table[i]) == NULL) 243 continue; 244 if ((irqp->airq_dip == dip) && 245 (irqp->airq_origirq == ispec->intrspec_vec) && 246 (irqp->airq_ipl == ispec->intrspec_pri)) { 247 if (DDI_INTR_IS_MSI_OR_MSIX(type)) { 248 if (APIC_IS_MSI_OR_MSIX_INDEX(irqp-> 249 airq_mps_intr_index)) 250 return (irqp); 251 } else 252 return (irqp); 253 } 254 } 255 DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: return NULL\n")); 256 return (NULL); 257 } 258 259 260 /* 261 * This function will return the pending bit of the irqp. 262 * It either comes from the IRR register of the APIC or the RDT 263 * entry of the I/O APIC. 264 * For the IRR to work, it needs to be to its binding CPU 265 */ 266 static int 267 apic_get_pending(apic_irq_t *irqp, int type) 268 { 269 int bit, index, irr, pending; 270 int intin_no; 271 int apic_ix; 272 273 DDI_INTR_IMPLDBG((CE_CONT, "apic_get_pending: irqp: %p, cpuid: %x " 274 "type: %x\n", (void *)irqp, irqp->airq_cpu & ~IRQ_USER_BOUND, 275 type)); 276 277 /* need to get on the bound cpu */ 278 mutex_enter(&cpu_lock); 279 affinity_set(irqp->airq_cpu & ~IRQ_USER_BOUND); 280 281 index = irqp->airq_vector / 32; 282 bit = irqp->airq_vector % 32; 283 irr = apicadr[APIC_IRR_REG + index]; 284 285 affinity_clear(); 286 mutex_exit(&cpu_lock); 287 288 pending = (irr & (1 << bit)) ? 1 : 0; 289 if (!pending && (type == DDI_INTR_TYPE_FIXED)) { 290 /* check I/O APIC for fixed interrupt */ 291 intin_no = irqp->airq_intin_no; 292 apic_ix = irqp->airq_ioapicindex; 293 pending = (READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no) & 294 AV_PENDING) ? 1 : 0; 295 } 296 return (pending); 297 } 298 299 300 /* 301 * This function will clear the mask for the interrupt on the I/O APIC 302 */ 303 static void 304 apic_clear_mask(apic_irq_t *irqp) 305 { 306 int intin_no; 307 ulong_t iflag; 308 int32_t rdt_entry; 309 int apic_ix; 310 311 DDI_INTR_IMPLDBG((CE_CONT, "apic_clear_mask: irqp: %p\n", 312 (void *)irqp)); 313 314 intin_no = irqp->airq_intin_no; 315 apic_ix = irqp->airq_ioapicindex; 316 317 iflag = intr_clear(); 318 lock_set(&apic_ioapic_lock); 319 320 rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no); 321 322 /* clear mask */ 323 WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no, 324 ((~AV_MASK) & rdt_entry)); 325 326 lock_clear(&apic_ioapic_lock); 327 intr_restore(iflag); 328 } 329 330 331 /* 332 * This function will mask the interrupt on the I/O APIC 333 */ 334 static void 335 apic_set_mask(apic_irq_t *irqp) 336 { 337 int intin_no; 338 int apic_ix; 339 ulong_t iflag; 340 int32_t rdt_entry; 341 342 DDI_INTR_IMPLDBG((CE_CONT, "apic_set_mask: irqp: %p\n", (void *)irqp)); 343 344 intin_no = irqp->airq_intin_no; 345 apic_ix = irqp->airq_ioapicindex; 346 347 iflag = intr_clear(); 348 349 lock_set(&apic_ioapic_lock); 350 351 rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no); 352 353 /* mask it */ 354 WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no, 355 (AV_MASK | rdt_entry)); 356 357 lock_clear(&apic_ioapic_lock); 358 intr_restore(iflag); 359 } 360 361 362 void 363 apic_free_vectors(dev_info_t *dip, int inum, int count, int pri, int type) 364 { 365 int i; 366 apic_irq_t *irqptr; 367 struct intrspec ispec; 368 369 DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: dip: %p inum: %x " 370 "count: %x pri: %x type: %x\n", 371 (void *)dip, inum, count, pri, type)); 372 373 /* for MSI/X only */ 374 if (!DDI_INTR_IS_MSI_OR_MSIX(type)) 375 return; 376 377 for (i = 0; i < count; i++) { 378 DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: inum=0x%x " 379 "pri=0x%x count=0x%x\n", inum, pri, count)); 380 ispec.intrspec_vec = inum + i; 381 ispec.intrspec_pri = pri; 382 if ((irqptr = apic_find_irq(dip, &ispec, type)) == NULL) { 383 DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: " 384 "dip=0x%p inum=0x%x pri=0x%x apic_find_irq() " 385 "failed\n", (void *)dip, inum, pri)); 386 continue; 387 } 388 irqptr->airq_mps_intr_index = FREE_INDEX; 389 apic_vector_to_irq[irqptr->airq_vector] = APIC_RESV_IRQ; 390 } 391 } 392 393 394 /* 395 * check whether the system supports MSI 396 * 397 * If PCI-E capability is found, then this must be a PCI-E system. 398 * Since MSI is required for PCI-E system, it returns PSM_SUCCESS 399 * to indicate this system supports MSI. 400 */ 401 int 402 apic_check_msi_support() 403 { 404 dev_info_t *cdip; 405 char dev_type[16]; 406 int dev_len; 407 408 DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support:\n")); 409 410 /* 411 * check whether the first level children of root_node have 412 * PCI-E capability 413 */ 414 for (cdip = ddi_get_child(ddi_root_node()); cdip != NULL; 415 cdip = ddi_get_next_sibling(cdip)) { 416 417 DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: cdip: 0x%p," 418 " driver: %s, binding: %s, nodename: %s\n", (void *)cdip, 419 ddi_driver_name(cdip), ddi_binding_name(cdip), 420 ddi_node_name(cdip))); 421 dev_len = sizeof (dev_type); 422 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS, 423 "device_type", (caddr_t)dev_type, &dev_len) 424 != DDI_PROP_SUCCESS) 425 continue; 426 if (strcmp(dev_type, "pciex") == 0) 427 return (PSM_SUCCESS); 428 } 429 430 /* MSI is not supported on this system */ 431 DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: no 'pciex' " 432 "device_type found\n")); 433 return (PSM_FAILURE); 434 } 435 436 int 437 apic_get_vector_intr_info(int vecirq, apic_get_intr_t *intr_params_p) 438 { 439 struct autovec *av_dev; 440 uchar_t irqno; 441 int i; 442 apic_irq_t *irq_p; 443 444 /* Sanity check the vector/irq argument. */ 445 ASSERT((vecirq >= 0) || (vecirq <= APIC_MAX_VECTOR)); 446 447 mutex_enter(&airq_mutex); 448 449 /* 450 * Convert the vecirq arg to an irq using vector_to_irq table 451 * if the arg is a vector. Pass thru if already an irq. 452 */ 453 if ((intr_params_p->avgi_req_flags & PSMGI_INTRBY_FLAGS) == 454 PSMGI_INTRBY_VEC) 455 irqno = apic_vector_to_irq[vecirq]; 456 else 457 irqno = vecirq; 458 459 irq_p = apic_irq_table[irqno]; 460 461 if ((irq_p == NULL) || 462 (irq_p->airq_temp_cpu == IRQ_UNBOUND) || 463 (irq_p->airq_temp_cpu == IRQ_UNINIT)) { 464 mutex_exit(&airq_mutex); 465 return (PSM_FAILURE); 466 } 467 468 if (intr_params_p->avgi_req_flags & PSMGI_REQ_CPUID) { 469 470 /* Get the (temp) cpu from apic_irq table, indexed by irq. */ 471 intr_params_p->avgi_cpu_id = irq_p->airq_temp_cpu; 472 473 /* Return user bound info for intrd. */ 474 if (intr_params_p->avgi_cpu_id & IRQ_USER_BOUND) { 475 intr_params_p->avgi_cpu_id &= ~IRQ_USER_BOUND; 476 intr_params_p->avgi_cpu_id |= PSMGI_CPU_USER_BOUND; 477 } 478 } 479 480 if (intr_params_p->avgi_req_flags & PSMGI_REQ_VECTOR) { 481 intr_params_p->avgi_vector = irq_p->airq_vector; 482 } 483 484 if (intr_params_p->avgi_req_flags & 485 (PSMGI_REQ_NUM_DEVS | PSMGI_REQ_GET_DEVS)) { 486 /* Get number of devices from apic_irq table shared field. */ 487 intr_params_p->avgi_num_devs = irq_p->airq_share; 488 } 489 490 if (intr_params_p->avgi_req_flags & PSMGI_REQ_GET_DEVS) { 491 492 intr_params_p->avgi_req_flags |= PSMGI_REQ_NUM_DEVS; 493 494 /* Some devices have NULL dip. Don't count these. */ 495 if (intr_params_p->avgi_num_devs > 0) { 496 for (i = 0, av_dev = autovect[irqno].avh_link; 497 av_dev; av_dev = av_dev->av_link) 498 if (av_dev->av_vector && av_dev->av_dip) 499 i++; 500 intr_params_p->avgi_num_devs = 501 MIN(intr_params_p->avgi_num_devs, i); 502 } 503 504 /* There are no viable dips to return. */ 505 if (intr_params_p->avgi_num_devs == 0) 506 intr_params_p->avgi_dip_list = NULL; 507 508 else { /* Return list of dips */ 509 510 /* Allocate space in array for that number of devs. */ 511 intr_params_p->avgi_dip_list = kmem_zalloc( 512 intr_params_p->avgi_num_devs * 513 sizeof (dev_info_t *), 514 KM_SLEEP); 515 516 /* 517 * Loop through the device list of the autovec table 518 * filling in the dip array. 519 * 520 * Note that the autovect table may have some special 521 * entries which contain NULL dips. These will be 522 * ignored. 523 */ 524 for (i = 0, av_dev = autovect[irqno].avh_link; 525 av_dev; av_dev = av_dev->av_link) 526 if (av_dev->av_vector && av_dev->av_dip) 527 intr_params_p->avgi_dip_list[i++] = 528 av_dev->av_dip; 529 } 530 } 531 532 mutex_exit(&airq_mutex); 533 534 return (PSM_SUCCESS); 535 } 536 537 /* 538 * apic_pci_msi_unconfigure: 539 * 540 * This and next two interfaces are copied from pci_intr_lib.c 541 * Do ensure that these two files stay in sync. 542 * These needed to be copied over here to avoid a deadlock situation on 543 * certain mp systems that use MSI interrupts. 544 * 545 * IMPORTANT regards next three interfaces: 546 * i) are called only for MSI/X interrupts. 547 * ii) called with interrupts disabled, and must not block 548 */ 549 int 550 apic_pci_msi_unconfigure(dev_info_t *rdip, int type, int inum) 551 { 552 ushort_t msi_ctrl; 553 int cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip); 554 ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(rdip); 555 556 if (handle == NULL || cap_ptr == 0) 557 return (PSM_FAILURE); 558 559 if (type == DDI_INTR_TYPE_MSI) { 560 msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 561 msi_ctrl &= (~PCI_MSI_MME_MASK); 562 pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 563 pci_config_put32(handle, cap_ptr + PCI_MSI_ADDR_OFFSET, 0); 564 565 if (msi_ctrl & PCI_MSI_64BIT_MASK) { 566 pci_config_put16(handle, 567 cap_ptr + PCI_MSI_64BIT_DATA, 0); 568 pci_config_put32(handle, 569 cap_ptr + PCI_MSI_ADDR_OFFSET + 4, 0); 570 } else { 571 pci_config_put16(handle, 572 cap_ptr + PCI_MSI_32BIT_DATA, 0); 573 } 574 575 } else if (type == DDI_INTR_TYPE_MSIX) { 576 uintptr_t off; 577 ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 578 579 /* Offset into the "inum"th entry in the MSI-X table */ 580 off = (uintptr_t)msix_p->msix_tbl_addr + 581 (inum * PCI_MSIX_VECTOR_SIZE); 582 583 /* Reset the "data" and "addr" bits */ 584 ddi_put32(msix_p->msix_tbl_hdl, 585 (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), 0); 586 ddi_put64(msix_p->msix_tbl_hdl, (uint64_t *)off, 0); 587 } 588 589 return (PSM_SUCCESS); 590 } 591 592 593 /* 594 * apic_pci_msi_enable_mode: 595 */ 596 int 597 apic_pci_msi_enable_mode(dev_info_t *rdip, int type, int inum) 598 { 599 ushort_t msi_ctrl; 600 int cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip); 601 ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(rdip); 602 603 if (handle == NULL || cap_ptr == 0) 604 return (PSM_FAILURE); 605 606 if (type == DDI_INTR_TYPE_MSI) { 607 msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 608 if ((msi_ctrl & PCI_MSI_ENABLE_BIT)) 609 return (PSM_SUCCESS); 610 611 msi_ctrl |= PCI_MSI_ENABLE_BIT; 612 pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 613 614 } else if (type == DDI_INTR_TYPE_MSIX) { 615 uintptr_t off; 616 ddi_intr_msix_t *msix_p; 617 618 msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSIX_CTRL); 619 620 if (msi_ctrl & PCI_MSIX_ENABLE_BIT) 621 return (PSM_SUCCESS); 622 623 msi_ctrl |= PCI_MSIX_ENABLE_BIT; 624 pci_config_put16(handle, cap_ptr + PCI_MSIX_CTRL, msi_ctrl); 625 626 msix_p = i_ddi_get_msix(rdip); 627 628 /* Offset into "inum"th entry in the MSI-X table & clear mask */ 629 off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 630 PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 631 ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0); 632 } 633 634 return (PSM_SUCCESS); 635 } 636 637 /* 638 * apic_pci_msi_disable_mode: 639 */ 640 int 641 apic_pci_msi_disable_mode(dev_info_t *rdip, int type, int inum) 642 { 643 ushort_t msi_ctrl; 644 int cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip); 645 ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(rdip); 646 647 if (handle == NULL || cap_ptr == 0) 648 return (PSM_FAILURE); 649 650 if (type == DDI_INTR_TYPE_MSI) { 651 msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 652 if (!(msi_ctrl & PCI_MSI_ENABLE_BIT)) 653 return (PSM_SUCCESS); 654 655 msi_ctrl &= ~PCI_MSI_ENABLE_BIT; /* MSI disable */ 656 pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 657 658 } else if (type == DDI_INTR_TYPE_MSIX) { 659 uintptr_t off; 660 ddi_intr_msix_t *msix_p; 661 662 msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSIX_CTRL); 663 664 if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT)) 665 return (PSM_SUCCESS); 666 667 msix_p = i_ddi_get_msix(rdip); 668 669 /* Offset into "inum"th entry in the MSI-X table & mask it */ 670 off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 671 PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 672 ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1); 673 } 674 675 return (PSM_SUCCESS); 676 } 677 678 679 /* 680 * This function provides external interface to the nexus for all 681 * functionalities related to the new DDI interrupt framework. 682 * 683 * Input: 684 * dip - pointer to the dev_info structure of the requested device 685 * hdlp - pointer to the internal interrupt handle structure for the 686 * requested interrupt 687 * intr_op - opcode for this call 688 * result - pointer to the integer that will hold the result to be 689 * passed back if return value is PSM_SUCCESS 690 * 691 * Output: 692 * return value is either PSM_SUCCESS or PSM_FAILURE 693 */ 694 int 695 apic_intr_ops(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, 696 psm_intr_op_t intr_op, int *result) 697 { 698 int cap, ret; 699 int count_vec; 700 int cpu; 701 int old_priority; 702 int new_priority; 703 int iflag; 704 apic_irq_t *irqp; 705 struct intrspec *ispec, intr_spec; 706 707 DDI_INTR_IMPLDBG((CE_CONT, "apic_intr_ops: dip: %p hdlp: %p " 708 "intr_op: %x\n", (void *)dip, (void *)hdlp, intr_op)); 709 710 ispec = &intr_spec; 711 ispec->intrspec_pri = hdlp->ih_pri; 712 ispec->intrspec_vec = hdlp->ih_inum; 713 ispec->intrspec_func = hdlp->ih_cb_func; 714 715 switch (intr_op) { 716 case PSM_INTR_OP_CHECK_MSI: 717 /* 718 * Check MSI/X is supported or not at APIC level and 719 * masked off the MSI/X bits in hdlp->ih_type if not 720 * supported before return. If MSI/X is supported, 721 * leave the ih_type unchanged and return. 722 * 723 * hdlp->ih_type passed in from the nexus has all the 724 * interrupt types supported by the device. 725 */ 726 if (apic_support_msi == 0) { 727 /* 728 * if apic_support_msi is not set, call 729 * apic_check_msi_support() to check whether msi 730 * is supported first 731 */ 732 if (apic_check_msi_support() == PSM_SUCCESS) 733 apic_support_msi = 1; 734 else 735 apic_support_msi = -1; 736 } 737 if (apic_support_msi == 1) 738 *result = hdlp->ih_type; 739 else 740 *result = hdlp->ih_type & ~(DDI_INTR_TYPE_MSI | 741 DDI_INTR_TYPE_MSIX); 742 break; 743 case PSM_INTR_OP_ALLOC_VECTORS: 744 *result = apic_alloc_vectors(dip, hdlp->ih_inum, 745 hdlp->ih_scratch1, hdlp->ih_pri, hdlp->ih_type, 746 (int)(uintptr_t)hdlp->ih_scratch2); 747 break; 748 case PSM_INTR_OP_FREE_VECTORS: 749 apic_free_vectors(dip, hdlp->ih_inum, hdlp->ih_scratch1, 750 hdlp->ih_pri, hdlp->ih_type); 751 break; 752 case PSM_INTR_OP_NAVAIL_VECTORS: 753 *result = apic_navail_vector(dip, hdlp->ih_pri); 754 break; 755 case PSM_INTR_OP_XLATE_VECTOR: 756 ispec = ((ihdl_plat_t *)hdlp->ih_private)->ip_ispecp; 757 *result = apic_introp_xlate(dip, ispec, hdlp->ih_type); 758 break; 759 case PSM_INTR_OP_GET_PENDING: 760 if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 761 return (PSM_FAILURE); 762 *result = apic_get_pending(irqp, hdlp->ih_type); 763 break; 764 case PSM_INTR_OP_CLEAR_MASK: 765 if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 766 return (PSM_FAILURE); 767 irqp = apic_find_irq(dip, ispec, hdlp->ih_type); 768 if (irqp == NULL) 769 return (PSM_FAILURE); 770 apic_clear_mask(irqp); 771 break; 772 case PSM_INTR_OP_SET_MASK: 773 if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 774 return (PSM_FAILURE); 775 if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 776 return (PSM_FAILURE); 777 apic_set_mask(irqp); 778 break; 779 case PSM_INTR_OP_GET_CAP: 780 cap = DDI_INTR_FLAG_PENDING; 781 if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 782 cap |= DDI_INTR_FLAG_MASKABLE; 783 *result = cap; 784 break; 785 case PSM_INTR_OP_GET_SHARED: 786 if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 787 return (PSM_FAILURE); 788 if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 789 return (PSM_FAILURE); 790 *result = irqp->airq_share ? 1: 0; 791 break; 792 case PSM_INTR_OP_SET_PRI: 793 old_priority = hdlp->ih_pri; /* save old value */ 794 new_priority = *(int *)result; /* try the new value */ 795 796 /* First, check if "hdlp->ih_scratch1" vectors exist? */ 797 if (apic_navail_vector(dip, new_priority) < hdlp->ih_scratch1) 798 return (PSM_FAILURE); 799 800 /* Now allocate the vectors */ 801 count_vec = apic_alloc_vectors(dip, hdlp->ih_inum, 802 hdlp->ih_scratch1, new_priority, hdlp->ih_type, 803 DDI_INTR_ALLOC_STRICT); 804 805 /* Did we get new vectors? */ 806 if (!count_vec) 807 return (PSM_FAILURE); 808 809 /* Finally, free the previously allocated vectors */ 810 apic_free_vectors(dip, hdlp->ih_inum, count_vec, 811 old_priority, hdlp->ih_type); 812 hdlp->ih_pri = new_priority; /* set the new value */ 813 break; 814 case PSM_INTR_OP_SET_CPU: 815 /* 816 * The interrupt handle given here has been allocated 817 * specifically for this command, and ih_private carries 818 * a CPU value. 819 */ 820 cpu = (int)(intptr_t)hdlp->ih_private; 821 822 if (!apic_cpu_in_range(cpu)) { 823 *result = EINVAL; 824 return (PSM_FAILURE); 825 } 826 827 828 /* Convert the vector to the irq using vector_to_irq table. */ 829 mutex_enter(&airq_mutex); 830 irqp = apic_irq_table[apic_vector_to_irq[hdlp->ih_vector]]; 831 mutex_exit(&airq_mutex); 832 833 if (irqp == NULL) { 834 *result = ENXIO; 835 return (PSM_FAILURE); 836 } 837 838 iflag = intr_clear(); 839 lock_set(&apic_ioapic_lock); 840 841 ret = apic_rebind_all(irqp, cpu); 842 843 lock_clear(&apic_ioapic_lock); 844 intr_restore(iflag); 845 846 if (ret) { 847 *result = EIO; 848 return (PSM_FAILURE); 849 } 850 *result = 0; 851 break; 852 case PSM_INTR_OP_GET_INTR: 853 /* 854 * The interrupt handle given here has been allocated 855 * specifically for this command, and ih_private carries 856 * a pointer to a apic_get_intr_t. 857 */ 858 if (apic_get_vector_intr_info( 859 hdlp->ih_vector, hdlp->ih_private) != PSM_SUCCESS) 860 return (PSM_FAILURE); 861 break; 862 case PSM_INTR_OP_SET_CAP: 863 default: 864 return (PSM_FAILURE); 865 } 866 return (PSM_SUCCESS); 867 } 868