10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51701Sjohnny * Common Development and Distribution License (the "License"). 61701Sjohnny * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 223446Smrj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate /* 290Sstevel@tonic-gate * apic_introp.c: 300Sstevel@tonic-gate * Has code for Advanced DDI interrupt framework support. 310Sstevel@tonic-gate */ 320Sstevel@tonic-gate 330Sstevel@tonic-gate #include <sys/cpuvar.h> 340Sstevel@tonic-gate #include <sys/psm.h> 353446Smrj #include <sys/archsystm.h> 363446Smrj #include <sys/apic.h> 370Sstevel@tonic-gate #include <sys/sunddi.h> 380Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 39916Sschwartz #include <sys/mach_intr.h> 40916Sschwartz #include <sys/sysmacros.h> 410Sstevel@tonic-gate #include <sys/trap.h> 420Sstevel@tonic-gate #include <sys/pci.h> 430Sstevel@tonic-gate #include <sys/pci_intr_lib.h> 440Sstevel@tonic-gate 45916Sschwartz extern struct av_head autovect[]; 46916Sschwartz 470Sstevel@tonic-gate /* 480Sstevel@tonic-gate * Local Function Prototypes 490Sstevel@tonic-gate */ 500Sstevel@tonic-gate int apic_pci_msi_enable_vector(dev_info_t *, int, int, 510Sstevel@tonic-gate int, int, int); 520Sstevel@tonic-gate apic_irq_t *apic_find_irq(dev_info_t *, struct intrspec *, int); 530Sstevel@tonic-gate static int apic_get_pending(apic_irq_t *, int); 540Sstevel@tonic-gate static void apic_clear_mask(apic_irq_t *); 550Sstevel@tonic-gate static void apic_set_mask(apic_irq_t *); 560Sstevel@tonic-gate 570Sstevel@tonic-gate /* 580Sstevel@tonic-gate * MSI support flag: 590Sstevel@tonic-gate * reflects whether MSI is supported at APIC level 600Sstevel@tonic-gate * it can also be patched through /etc/system 610Sstevel@tonic-gate * 620Sstevel@tonic-gate * 0 = default value - don't know and need to call apic_check_msi_support() 630Sstevel@tonic-gate * to find out then set it accordingly 640Sstevel@tonic-gate * 1 = supported 650Sstevel@tonic-gate * -1 = not supported 660Sstevel@tonic-gate */ 670Sstevel@tonic-gate int apic_support_msi = 0; 680Sstevel@tonic-gate 690Sstevel@tonic-gate /* Multiple vector support for MSI */ 700Sstevel@tonic-gate int apic_multi_msi_enable = 1; 710Sstevel@tonic-gate int apic_multi_msi_max = 2; 720Sstevel@tonic-gate 730Sstevel@tonic-gate /* 740Sstevel@tonic-gate * apic_pci_msi_enable_vector: 750Sstevel@tonic-gate * Set the address/data fields in the MSI/X capability structure 760Sstevel@tonic-gate * XXX: MSI-X support 770Sstevel@tonic-gate */ 780Sstevel@tonic-gate /* ARGSUSED */ 790Sstevel@tonic-gate int 800Sstevel@tonic-gate apic_pci_msi_enable_vector(dev_info_t *dip, int type, int inum, int vector, 810Sstevel@tonic-gate int count, int target_apic_id) 820Sstevel@tonic-gate { 831997Sanish uint64_t msi_addr, msi_data; 841997Sanish ushort_t msi_ctrl; 851997Sanish int cap_ptr = i_ddi_get_msi_msix_cap_ptr(dip); 861997Sanish ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(dip); 870Sstevel@tonic-gate 880Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: dip=0x%p\n" 890Sstevel@tonic-gate "\tdriver = %s, inum=0x%x vector=0x%x apicid=0x%x\n", (void *)dip, 900Sstevel@tonic-gate ddi_driver_name(dip), inum, vector, target_apic_id)); 910Sstevel@tonic-gate 92*4160Sjveta if (handle == NULL || cap_ptr == 0) 931997Sanish return (PSM_FAILURE); 941997Sanish 950Sstevel@tonic-gate /* MSI Address */ 960Sstevel@tonic-gate msi_addr = (MSI_ADDR_HDR | (target_apic_id << MSI_ADDR_DEST_SHIFT)); 970Sstevel@tonic-gate msi_addr |= ((MSI_ADDR_RH_FIXED << MSI_ADDR_RH_SHIFT) | 980Sstevel@tonic-gate (MSI_ADDR_DM_PHYSICAL << MSI_ADDR_DM_SHIFT)); 990Sstevel@tonic-gate 1000Sstevel@tonic-gate /* MSI Data: MSI is edge triggered according to spec */ 1010Sstevel@tonic-gate msi_data = ((MSI_DATA_TM_EDGE << MSI_DATA_TM_SHIFT) | vector); 1020Sstevel@tonic-gate 1030Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: addr=0x%lx " 1040Sstevel@tonic-gate "data=0x%lx\n", (long)msi_addr, (long)msi_data)); 1050Sstevel@tonic-gate 1061997Sanish if (type == DDI_INTR_TYPE_MSI) { 1071997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 1081997Sanish 1091997Sanish /* Set the bits to inform how many MSIs are enabled */ 1101997Sanish msi_ctrl |= ((highbit(count) -1) << PCI_MSI_MME_SHIFT); 1111997Sanish pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 1121997Sanish 1131997Sanish pci_config_put32(handle, 1141997Sanish cap_ptr + PCI_MSI_ADDR_OFFSET, msi_addr); 1151997Sanish 1161997Sanish if (msi_ctrl & PCI_MSI_64BIT_MASK) { 1171997Sanish pci_config_put32(handle, 1181997Sanish cap_ptr + PCI_MSI_ADDR_OFFSET + 4, msi_addr >> 32); 1191997Sanish pci_config_put16(handle, 1201997Sanish cap_ptr + PCI_MSI_64BIT_DATA, msi_data); 1211997Sanish } else { 1221997Sanish pci_config_put16(handle, 1231997Sanish cap_ptr + PCI_MSI_32BIT_DATA, msi_data); 1241997Sanish } 1251997Sanish 1261997Sanish } else if (type == DDI_INTR_TYPE_MSIX) { 1271997Sanish uintptr_t off; 1281997Sanish ddi_intr_msix_t *msix_p = i_ddi_get_msix(dip); 1291997Sanish 1301997Sanish /* Offset into the "inum"th entry in the MSI-X table */ 1311997Sanish off = (uintptr_t)msix_p->msix_tbl_addr + 1321997Sanish (inum * PCI_MSIX_VECTOR_SIZE); 1331997Sanish 1341997Sanish ddi_put32(msix_p->msix_tbl_hdl, 1351997Sanish (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), msi_data); 1361997Sanish ddi_put64(msix_p->msix_tbl_hdl, 1371997Sanish (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), msi_addr); 1380Sstevel@tonic-gate } 1390Sstevel@tonic-gate 1400Sstevel@tonic-gate return (PSM_SUCCESS); 1410Sstevel@tonic-gate } 1420Sstevel@tonic-gate 1430Sstevel@tonic-gate 1440Sstevel@tonic-gate /* 1450Sstevel@tonic-gate * This function returns the no. of vectors available for the pri. 1460Sstevel@tonic-gate * dip is not used at this moment. If we really don't need that, 1470Sstevel@tonic-gate * it will be removed. 1480Sstevel@tonic-gate */ 1490Sstevel@tonic-gate /*ARGSUSED*/ 1500Sstevel@tonic-gate int 1510Sstevel@tonic-gate apic_navail_vector(dev_info_t *dip, int pri) 1520Sstevel@tonic-gate { 1530Sstevel@tonic-gate int lowest, highest, i, navail, count; 1540Sstevel@tonic-gate 1550Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_navail_vector: dip: %p, pri: %x\n", 1560Sstevel@tonic-gate (void *)dip, pri)); 1570Sstevel@tonic-gate 1580Sstevel@tonic-gate highest = apic_ipltopri[pri] + APIC_VECTOR_MASK; 1590Sstevel@tonic-gate lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL; 1600Sstevel@tonic-gate navail = count = 0; 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate /* It has to be contiguous */ 1630Sstevel@tonic-gate for (i = lowest; i < highest; i++) { 1640Sstevel@tonic-gate count = 0; 1650Sstevel@tonic-gate while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) && 1660Sstevel@tonic-gate (i < highest)) { 1672335Sjohnny if (APIC_CHECK_RESERVE_VECTORS(i)) 1680Sstevel@tonic-gate break; 1690Sstevel@tonic-gate count++; 1700Sstevel@tonic-gate i++; 1710Sstevel@tonic-gate } 1720Sstevel@tonic-gate if (count > navail) 1730Sstevel@tonic-gate navail = count; 1740Sstevel@tonic-gate } 1750Sstevel@tonic-gate return (navail); 1760Sstevel@tonic-gate } 1770Sstevel@tonic-gate 1782335Sjohnny /* 1792335Sjohnny * Finds "count" contiguous MSI vectors starting at the proper alignment 1802335Sjohnny * at "pri". 1812335Sjohnny * Caller needs to make sure that count has to be power of 2 and should not 1822335Sjohnny * be < 1. 1832335Sjohnny */ 1843446Smrj uchar_t 1850Sstevel@tonic-gate apic_find_multi_vectors(int pri, int count) 1860Sstevel@tonic-gate { 1872335Sjohnny int lowest, highest, i, navail, start, msibits; 1880Sstevel@tonic-gate 1890Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_find_mult: pri: %x, count: %x\n", 1900Sstevel@tonic-gate pri, count)); 1910Sstevel@tonic-gate 1920Sstevel@tonic-gate highest = apic_ipltopri[pri] + APIC_VECTOR_MASK; 1930Sstevel@tonic-gate lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL; 1940Sstevel@tonic-gate navail = 0; 1950Sstevel@tonic-gate 1962335Sjohnny /* 1972335Sjohnny * msibits is the no. of lower order message data bits for the 1982335Sjohnny * allocated MSI vectors and is used to calculate the aligned 1992335Sjohnny * starting vector 2002335Sjohnny */ 2012335Sjohnny msibits = count - 1; 2022335Sjohnny 2030Sstevel@tonic-gate /* It has to be contiguous */ 2040Sstevel@tonic-gate for (i = lowest; i < highest; i++) { 2050Sstevel@tonic-gate navail = 0; 2062335Sjohnny 2072335Sjohnny /* 2082335Sjohnny * starting vector has to be aligned accordingly for 2092335Sjohnny * multiple MSIs 2102335Sjohnny */ 2112335Sjohnny if (msibits) 2122335Sjohnny i = (i + msibits) & ~msibits; 2130Sstevel@tonic-gate start = i; 2140Sstevel@tonic-gate while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) && 2150Sstevel@tonic-gate (i < highest)) { 2162335Sjohnny if (APIC_CHECK_RESERVE_VECTORS(i)) 2170Sstevel@tonic-gate break; 2180Sstevel@tonic-gate navail++; 2190Sstevel@tonic-gate if (navail >= count) 2200Sstevel@tonic-gate return (start); 2210Sstevel@tonic-gate i++; 2220Sstevel@tonic-gate } 2230Sstevel@tonic-gate } 2240Sstevel@tonic-gate return (0); 2250Sstevel@tonic-gate } 2260Sstevel@tonic-gate 2273446Smrj 2280Sstevel@tonic-gate /* 2290Sstevel@tonic-gate * It finds the apic_irq_t associates with the dip, ispec and type. 2300Sstevel@tonic-gate */ 2310Sstevel@tonic-gate apic_irq_t * 2320Sstevel@tonic-gate apic_find_irq(dev_info_t *dip, struct intrspec *ispec, int type) 2330Sstevel@tonic-gate { 2340Sstevel@tonic-gate apic_irq_t *irqp; 2350Sstevel@tonic-gate int i; 2360Sstevel@tonic-gate 2370Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: dip=0x%p vec=0x%x " 2380Sstevel@tonic-gate "ipl=0x%x type=0x%x\n", (void *)dip, ispec->intrspec_vec, 2390Sstevel@tonic-gate ispec->intrspec_pri, type)); 2400Sstevel@tonic-gate 2410Sstevel@tonic-gate for (i = apic_min_device_irq; i <= apic_max_device_irq; i++) { 2420Sstevel@tonic-gate if ((irqp = apic_irq_table[i]) == NULL) 2430Sstevel@tonic-gate continue; 2440Sstevel@tonic-gate if ((irqp->airq_dip == dip) && 2450Sstevel@tonic-gate (irqp->airq_origirq == ispec->intrspec_vec) && 2460Sstevel@tonic-gate (irqp->airq_ipl == ispec->intrspec_pri)) { 2470Sstevel@tonic-gate if (DDI_INTR_IS_MSI_OR_MSIX(type)) { 2480Sstevel@tonic-gate if (APIC_IS_MSI_OR_MSIX_INDEX(irqp-> 2490Sstevel@tonic-gate airq_mps_intr_index)) 2500Sstevel@tonic-gate return (irqp); 2510Sstevel@tonic-gate } else 2520Sstevel@tonic-gate return (irqp); 2530Sstevel@tonic-gate } 2540Sstevel@tonic-gate } 2550Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: return NULL\n")); 2560Sstevel@tonic-gate return (NULL); 2570Sstevel@tonic-gate } 2580Sstevel@tonic-gate 2590Sstevel@tonic-gate 2600Sstevel@tonic-gate /* 2610Sstevel@tonic-gate * This function will return the pending bit of the irqp. 2620Sstevel@tonic-gate * It either comes from the IRR register of the APIC or the RDT 2630Sstevel@tonic-gate * entry of the I/O APIC. 2640Sstevel@tonic-gate * For the IRR to work, it needs to be to its binding CPU 2650Sstevel@tonic-gate */ 2660Sstevel@tonic-gate static int 2670Sstevel@tonic-gate apic_get_pending(apic_irq_t *irqp, int type) 2680Sstevel@tonic-gate { 2690Sstevel@tonic-gate int bit, index, irr, pending; 2700Sstevel@tonic-gate int intin_no; 2713446Smrj int apic_ix; 2720Sstevel@tonic-gate 2730Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_get_pending: irqp: %p, cpuid: %x " 2740Sstevel@tonic-gate "type: %x\n", (void *)irqp, irqp->airq_cpu & ~IRQ_USER_BOUND, 2750Sstevel@tonic-gate type)); 2760Sstevel@tonic-gate 2770Sstevel@tonic-gate /* need to get on the bound cpu */ 2780Sstevel@tonic-gate mutex_enter(&cpu_lock); 2790Sstevel@tonic-gate affinity_set(irqp->airq_cpu & ~IRQ_USER_BOUND); 2800Sstevel@tonic-gate 2810Sstevel@tonic-gate index = irqp->airq_vector / 32; 2820Sstevel@tonic-gate bit = irqp->airq_vector % 32; 2830Sstevel@tonic-gate irr = apicadr[APIC_IRR_REG + index]; 2840Sstevel@tonic-gate 2850Sstevel@tonic-gate affinity_clear(); 2860Sstevel@tonic-gate mutex_exit(&cpu_lock); 2870Sstevel@tonic-gate 2880Sstevel@tonic-gate pending = (irr & (1 << bit)) ? 1 : 0; 2890Sstevel@tonic-gate if (!pending && (type == DDI_INTR_TYPE_FIXED)) { 2900Sstevel@tonic-gate /* check I/O APIC for fixed interrupt */ 2910Sstevel@tonic-gate intin_no = irqp->airq_intin_no; 2923446Smrj apic_ix = irqp->airq_ioapicindex; 2933446Smrj pending = (READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no) & 2940Sstevel@tonic-gate AV_PENDING) ? 1 : 0; 2950Sstevel@tonic-gate } 2960Sstevel@tonic-gate return (pending); 2970Sstevel@tonic-gate } 2980Sstevel@tonic-gate 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate /* 3010Sstevel@tonic-gate * This function will clear the mask for the interrupt on the I/O APIC 3020Sstevel@tonic-gate */ 3030Sstevel@tonic-gate static void 3040Sstevel@tonic-gate apic_clear_mask(apic_irq_t *irqp) 3050Sstevel@tonic-gate { 3060Sstevel@tonic-gate int intin_no; 3073446Smrj ulong_t iflag; 3080Sstevel@tonic-gate int32_t rdt_entry; 3093446Smrj int apic_ix; 3100Sstevel@tonic-gate 3110Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_clear_mask: irqp: %p\n", 3120Sstevel@tonic-gate (void *)irqp)); 3130Sstevel@tonic-gate 3140Sstevel@tonic-gate intin_no = irqp->airq_intin_no; 3153446Smrj apic_ix = irqp->airq_ioapicindex; 3160Sstevel@tonic-gate 3170Sstevel@tonic-gate iflag = intr_clear(); 3180Sstevel@tonic-gate lock_set(&apic_ioapic_lock); 3190Sstevel@tonic-gate 3203446Smrj rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no); 3210Sstevel@tonic-gate 3220Sstevel@tonic-gate /* clear mask */ 3233446Smrj WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no, 3240Sstevel@tonic-gate ((~AV_MASK) & rdt_entry)); 3250Sstevel@tonic-gate 3260Sstevel@tonic-gate lock_clear(&apic_ioapic_lock); 3270Sstevel@tonic-gate intr_restore(iflag); 3280Sstevel@tonic-gate } 3290Sstevel@tonic-gate 3300Sstevel@tonic-gate 3310Sstevel@tonic-gate /* 3320Sstevel@tonic-gate * This function will mask the interrupt on the I/O APIC 3330Sstevel@tonic-gate */ 3340Sstevel@tonic-gate static void 3350Sstevel@tonic-gate apic_set_mask(apic_irq_t *irqp) 3360Sstevel@tonic-gate { 3370Sstevel@tonic-gate int intin_no; 3383446Smrj int apic_ix; 3393446Smrj ulong_t iflag; 3400Sstevel@tonic-gate int32_t rdt_entry; 3410Sstevel@tonic-gate 3420Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_set_mask: irqp: %p\n", (void *)irqp)); 3430Sstevel@tonic-gate 3440Sstevel@tonic-gate intin_no = irqp->airq_intin_no; 3453446Smrj apic_ix = irqp->airq_ioapicindex; 3460Sstevel@tonic-gate 3470Sstevel@tonic-gate iflag = intr_clear(); 3480Sstevel@tonic-gate 3490Sstevel@tonic-gate lock_set(&apic_ioapic_lock); 3500Sstevel@tonic-gate 3513446Smrj rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no); 3520Sstevel@tonic-gate 3530Sstevel@tonic-gate /* mask it */ 3543446Smrj WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no, 3550Sstevel@tonic-gate (AV_MASK | rdt_entry)); 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate lock_clear(&apic_ioapic_lock); 3580Sstevel@tonic-gate intr_restore(iflag); 3590Sstevel@tonic-gate } 3600Sstevel@tonic-gate 3610Sstevel@tonic-gate 3620Sstevel@tonic-gate void 3630Sstevel@tonic-gate apic_free_vectors(dev_info_t *dip, int inum, int count, int pri, int type) 3640Sstevel@tonic-gate { 3650Sstevel@tonic-gate int i; 3660Sstevel@tonic-gate apic_irq_t *irqptr; 3670Sstevel@tonic-gate struct intrspec ispec; 3680Sstevel@tonic-gate 3690Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: dip: %p inum: %x " 3700Sstevel@tonic-gate "count: %x pri: %x type: %x\n", 3710Sstevel@tonic-gate (void *)dip, inum, count, pri, type)); 3720Sstevel@tonic-gate 3730Sstevel@tonic-gate /* for MSI/X only */ 3740Sstevel@tonic-gate if (!DDI_INTR_IS_MSI_OR_MSIX(type)) 3750Sstevel@tonic-gate return; 3760Sstevel@tonic-gate 3770Sstevel@tonic-gate for (i = 0; i < count; i++) { 3780Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: inum=0x%x " 3790Sstevel@tonic-gate "pri=0x%x count=0x%x\n", inum, pri, count)); 3800Sstevel@tonic-gate ispec.intrspec_vec = inum + i; 3810Sstevel@tonic-gate ispec.intrspec_pri = pri; 3820Sstevel@tonic-gate if ((irqptr = apic_find_irq(dip, &ispec, type)) == NULL) { 3830Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: " 3840Sstevel@tonic-gate "dip=0x%p inum=0x%x pri=0x%x apic_find_irq() " 3850Sstevel@tonic-gate "failed\n", (void *)dip, inum, pri)); 3860Sstevel@tonic-gate continue; 3870Sstevel@tonic-gate } 3880Sstevel@tonic-gate irqptr->airq_mps_intr_index = FREE_INDEX; 3890Sstevel@tonic-gate apic_vector_to_irq[irqptr->airq_vector] = APIC_RESV_IRQ; 3900Sstevel@tonic-gate } 3910Sstevel@tonic-gate } 3920Sstevel@tonic-gate 3930Sstevel@tonic-gate 3940Sstevel@tonic-gate /* 3950Sstevel@tonic-gate * check whether the system supports MSI 3960Sstevel@tonic-gate * 3970Sstevel@tonic-gate * If PCI-E capability is found, then this must be a PCI-E system. 3980Sstevel@tonic-gate * Since MSI is required for PCI-E system, it returns PSM_SUCCESS 3990Sstevel@tonic-gate * to indicate this system supports MSI. 4000Sstevel@tonic-gate */ 4010Sstevel@tonic-gate int 4021701Sjohnny apic_check_msi_support() 4030Sstevel@tonic-gate { 4041701Sjohnny dev_info_t *cdip; 405881Sjohnny char dev_type[16]; 406881Sjohnny int dev_len; 4070Sstevel@tonic-gate 4081701Sjohnny DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support:\n")); 4090Sstevel@tonic-gate 4101701Sjohnny /* 4111701Sjohnny * check whether the first level children of root_node have 4121701Sjohnny * PCI-E capability 4131701Sjohnny */ 4141701Sjohnny for (cdip = ddi_get_child(ddi_root_node()); cdip != NULL; 4151701Sjohnny cdip = ddi_get_next_sibling(cdip)) { 4161701Sjohnny 4171701Sjohnny DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: cdip: 0x%p," 4181701Sjohnny " driver: %s, binding: %s, nodename: %s\n", (void *)cdip, 4191701Sjohnny ddi_driver_name(cdip), ddi_binding_name(cdip), 4201701Sjohnny ddi_node_name(cdip))); 421881Sjohnny dev_len = sizeof (dev_type); 4221701Sjohnny if (ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS, 423881Sjohnny "device_type", (caddr_t)dev_type, &dev_len) 424881Sjohnny != DDI_PROP_SUCCESS) 425881Sjohnny continue; 426881Sjohnny if (strcmp(dev_type, "pciex") == 0) 427881Sjohnny return (PSM_SUCCESS); 4280Sstevel@tonic-gate } 4290Sstevel@tonic-gate 4300Sstevel@tonic-gate /* MSI is not supported on this system */ 431881Sjohnny DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: no 'pciex' " 432881Sjohnny "device_type found\n")); 4330Sstevel@tonic-gate return (PSM_FAILURE); 4340Sstevel@tonic-gate } 4350Sstevel@tonic-gate 436916Sschwartz int 437916Sschwartz apic_get_vector_intr_info(int vecirq, apic_get_intr_t *intr_params_p) 438916Sschwartz { 439916Sschwartz struct autovec *av_dev; 4401087Sschwartz uchar_t irqno; 441916Sschwartz int i; 4421087Sschwartz apic_irq_t *irq_p; 443916Sschwartz 444916Sschwartz /* Sanity check the vector/irq argument. */ 445916Sschwartz ASSERT((vecirq >= 0) || (vecirq <= APIC_MAX_VECTOR)); 446916Sschwartz 447916Sschwartz mutex_enter(&airq_mutex); 448916Sschwartz 449916Sschwartz /* 450916Sschwartz * Convert the vecirq arg to an irq using vector_to_irq table 451916Sschwartz * if the arg is a vector. Pass thru if already an irq. 452916Sschwartz */ 453916Sschwartz if ((intr_params_p->avgi_req_flags & PSMGI_INTRBY_FLAGS) == 454916Sschwartz PSMGI_INTRBY_VEC) 4551087Sschwartz irqno = apic_vector_to_irq[vecirq]; 456916Sschwartz else 4571087Sschwartz irqno = vecirq; 4581087Sschwartz 4591087Sschwartz irq_p = apic_irq_table[irqno]; 4601087Sschwartz 4611087Sschwartz if ((irq_p == NULL) || 4621087Sschwartz (irq_p->airq_temp_cpu == IRQ_UNBOUND) || 4631087Sschwartz (irq_p->airq_temp_cpu == IRQ_UNINIT)) { 4641087Sschwartz mutex_exit(&airq_mutex); 4651087Sschwartz return (PSM_FAILURE); 4661087Sschwartz } 467916Sschwartz 468916Sschwartz if (intr_params_p->avgi_req_flags & PSMGI_REQ_CPUID) { 469916Sschwartz 470916Sschwartz /* Get the (temp) cpu from apic_irq table, indexed by irq. */ 4711087Sschwartz intr_params_p->avgi_cpu_id = irq_p->airq_temp_cpu; 472916Sschwartz 473916Sschwartz /* Return user bound info for intrd. */ 474916Sschwartz if (intr_params_p->avgi_cpu_id & IRQ_USER_BOUND) { 475916Sschwartz intr_params_p->avgi_cpu_id &= ~IRQ_USER_BOUND; 476916Sschwartz intr_params_p->avgi_cpu_id |= PSMGI_CPU_USER_BOUND; 477916Sschwartz } 478916Sschwartz } 479916Sschwartz 480916Sschwartz if (intr_params_p->avgi_req_flags & PSMGI_REQ_VECTOR) { 4811087Sschwartz intr_params_p->avgi_vector = irq_p->airq_vector; 482916Sschwartz } 483916Sschwartz 484916Sschwartz if (intr_params_p->avgi_req_flags & 485916Sschwartz (PSMGI_REQ_NUM_DEVS | PSMGI_REQ_GET_DEVS)) { 486916Sschwartz /* Get number of devices from apic_irq table shared field. */ 4871087Sschwartz intr_params_p->avgi_num_devs = irq_p->airq_share; 488916Sschwartz } 489916Sschwartz 490916Sschwartz if (intr_params_p->avgi_req_flags & PSMGI_REQ_GET_DEVS) { 491916Sschwartz 492916Sschwartz intr_params_p->avgi_req_flags |= PSMGI_REQ_NUM_DEVS; 493916Sschwartz 494916Sschwartz /* Some devices have NULL dip. Don't count these. */ 495916Sschwartz if (intr_params_p->avgi_num_devs > 0) { 4961087Sschwartz for (i = 0, av_dev = autovect[irqno].avh_link; 497916Sschwartz av_dev; av_dev = av_dev->av_link) 498916Sschwartz if (av_dev->av_vector && av_dev->av_dip) 499916Sschwartz i++; 500916Sschwartz intr_params_p->avgi_num_devs = 501916Sschwartz MIN(intr_params_p->avgi_num_devs, i); 502916Sschwartz } 503916Sschwartz 504916Sschwartz /* There are no viable dips to return. */ 505916Sschwartz if (intr_params_p->avgi_num_devs == 0) 506916Sschwartz intr_params_p->avgi_dip_list = NULL; 507916Sschwartz 508916Sschwartz else { /* Return list of dips */ 509916Sschwartz 510916Sschwartz /* Allocate space in array for that number of devs. */ 511916Sschwartz intr_params_p->avgi_dip_list = kmem_zalloc( 512916Sschwartz intr_params_p->avgi_num_devs * 513916Sschwartz sizeof (dev_info_t *), 514916Sschwartz KM_SLEEP); 515916Sschwartz 516916Sschwartz /* 517916Sschwartz * Loop through the device list of the autovec table 518916Sschwartz * filling in the dip array. 519916Sschwartz * 520916Sschwartz * Note that the autovect table may have some special 521916Sschwartz * entries which contain NULL dips. These will be 522916Sschwartz * ignored. 523916Sschwartz */ 5241087Sschwartz for (i = 0, av_dev = autovect[irqno].avh_link; 525916Sschwartz av_dev; av_dev = av_dev->av_link) 526916Sschwartz if (av_dev->av_vector && av_dev->av_dip) 527916Sschwartz intr_params_p->avgi_dip_list[i++] = 528916Sschwartz av_dev->av_dip; 529916Sschwartz } 530916Sschwartz } 531916Sschwartz 532916Sschwartz mutex_exit(&airq_mutex); 533916Sschwartz 534916Sschwartz return (PSM_SUCCESS); 535916Sschwartz } 536916Sschwartz 5370Sstevel@tonic-gate /* 5381997Sanish * apic_pci_msi_unconfigure: 5391997Sanish * 5401997Sanish * This and next two interfaces are copied from pci_intr_lib.c 5411997Sanish * Do ensure that these two files stay in sync. 5421997Sanish * These needed to be copied over here to avoid a deadlock situation on 5431997Sanish * certain mp systems that use MSI interrupts. 5441997Sanish * 5451997Sanish * IMPORTANT regards next three interfaces: 5461997Sanish * i) are called only for MSI/X interrupts. 5471997Sanish * ii) called with interrupts disabled, and must not block 5481997Sanish */ 5491997Sanish int 5501997Sanish apic_pci_msi_unconfigure(dev_info_t *rdip, int type, int inum) 5511997Sanish { 5521997Sanish ushort_t msi_ctrl; 5531997Sanish int cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip); 5541997Sanish ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(rdip); 5551997Sanish 556*4160Sjveta if (handle == NULL || cap_ptr == 0) 5571997Sanish return (PSM_FAILURE); 5581997Sanish 5591997Sanish if (type == DDI_INTR_TYPE_MSI) { 5601997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 5611997Sanish msi_ctrl &= (~PCI_MSI_MME_MASK); 5621997Sanish pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 5631997Sanish pci_config_put32(handle, cap_ptr + PCI_MSI_ADDR_OFFSET, 0); 5641997Sanish 5651997Sanish if (msi_ctrl & PCI_MSI_64BIT_MASK) { 5661997Sanish pci_config_put16(handle, 5671997Sanish cap_ptr + PCI_MSI_64BIT_DATA, 0); 5681997Sanish pci_config_put32(handle, 5691997Sanish cap_ptr + PCI_MSI_ADDR_OFFSET + 4, 0); 5701997Sanish } else { 5711997Sanish pci_config_put16(handle, 5721997Sanish cap_ptr + PCI_MSI_32BIT_DATA, 0); 5731997Sanish } 5741997Sanish 5751997Sanish } else if (type == DDI_INTR_TYPE_MSIX) { 5761997Sanish uintptr_t off; 5771997Sanish ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 5781997Sanish 5791997Sanish /* Offset into the "inum"th entry in the MSI-X table */ 5801997Sanish off = (uintptr_t)msix_p->msix_tbl_addr + 5811997Sanish (inum * PCI_MSIX_VECTOR_SIZE); 5821997Sanish 5831997Sanish /* Reset the "data" and "addr" bits */ 5841997Sanish ddi_put32(msix_p->msix_tbl_hdl, 5851997Sanish (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), 0); 5861997Sanish ddi_put64(msix_p->msix_tbl_hdl, (uint64_t *)off, 0); 5871997Sanish } 5881997Sanish 5891997Sanish return (PSM_SUCCESS); 5901997Sanish } 5911997Sanish 5921997Sanish 5931997Sanish /* 5941997Sanish * apic_pci_msi_enable_mode: 5951997Sanish */ 5961997Sanish int 5971997Sanish apic_pci_msi_enable_mode(dev_info_t *rdip, int type, int inum) 5981997Sanish { 5991997Sanish ushort_t msi_ctrl; 6001997Sanish int cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip); 6011997Sanish ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(rdip); 6021997Sanish 603*4160Sjveta if (handle == NULL || cap_ptr == 0) 6041997Sanish return (PSM_FAILURE); 6051997Sanish 6061997Sanish if (type == DDI_INTR_TYPE_MSI) { 6071997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 6081997Sanish if ((msi_ctrl & PCI_MSI_ENABLE_BIT)) 6091997Sanish return (PSM_SUCCESS); 6101997Sanish 6111997Sanish msi_ctrl |= PCI_MSI_ENABLE_BIT; 6121997Sanish pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 6131997Sanish 6141997Sanish } else if (type == DDI_INTR_TYPE_MSIX) { 6151997Sanish uintptr_t off; 6161997Sanish ddi_intr_msix_t *msix_p; 6171997Sanish 6181997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSIX_CTRL); 6191997Sanish 6201997Sanish if (msi_ctrl & PCI_MSIX_ENABLE_BIT) 6211997Sanish return (PSM_SUCCESS); 6221997Sanish 6231997Sanish msi_ctrl |= PCI_MSIX_ENABLE_BIT; 6241997Sanish pci_config_put16(handle, cap_ptr + PCI_MSIX_CTRL, msi_ctrl); 6251997Sanish 6261997Sanish msix_p = i_ddi_get_msix(rdip); 6271997Sanish 6281997Sanish /* Offset into "inum"th entry in the MSI-X table & clear mask */ 6291997Sanish off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 6301997Sanish PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 6311997Sanish ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0); 6321997Sanish } 6331997Sanish 6341997Sanish return (PSM_SUCCESS); 6351997Sanish } 6361997Sanish 6371997Sanish /* 6381997Sanish * apic_pci_msi_disable_mode: 6391997Sanish */ 6401997Sanish int 6411997Sanish apic_pci_msi_disable_mode(dev_info_t *rdip, int type, int inum) 6421997Sanish { 6431997Sanish ushort_t msi_ctrl; 6441997Sanish int cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip); 6451997Sanish ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(rdip); 6461997Sanish 647*4160Sjveta if (handle == NULL || cap_ptr == 0) 6481997Sanish return (PSM_FAILURE); 6491997Sanish 6501997Sanish if (type == DDI_INTR_TYPE_MSI) { 6511997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 6521997Sanish if (!(msi_ctrl & PCI_MSI_ENABLE_BIT)) 6531997Sanish return (PSM_SUCCESS); 6541997Sanish 6551997Sanish msi_ctrl &= ~PCI_MSI_ENABLE_BIT; /* MSI disable */ 6561997Sanish pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 6571997Sanish 6581997Sanish } else if (type == DDI_INTR_TYPE_MSIX) { 6591997Sanish uintptr_t off; 6601997Sanish ddi_intr_msix_t *msix_p; 6611997Sanish 6621997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSIX_CTRL); 6631997Sanish 6641997Sanish if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT)) 6651997Sanish return (PSM_SUCCESS); 6661997Sanish 6671997Sanish msix_p = i_ddi_get_msix(rdip); 6681997Sanish 6691997Sanish /* Offset into "inum"th entry in the MSI-X table & mask it */ 6701997Sanish off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 6711997Sanish PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 6721997Sanish ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1); 6731997Sanish } 6741997Sanish 6751997Sanish return (PSM_SUCCESS); 6761997Sanish } 6771997Sanish 6783446Smrj 6791997Sanish /* 6800Sstevel@tonic-gate * This function provides external interface to the nexus for all 6810Sstevel@tonic-gate * functionalities related to the new DDI interrupt framework. 6820Sstevel@tonic-gate * 6830Sstevel@tonic-gate * Input: 6840Sstevel@tonic-gate * dip - pointer to the dev_info structure of the requested device 6850Sstevel@tonic-gate * hdlp - pointer to the internal interrupt handle structure for the 6860Sstevel@tonic-gate * requested interrupt 6870Sstevel@tonic-gate * intr_op - opcode for this call 6880Sstevel@tonic-gate * result - pointer to the integer that will hold the result to be 6890Sstevel@tonic-gate * passed back if return value is PSM_SUCCESS 6900Sstevel@tonic-gate * 6910Sstevel@tonic-gate * Output: 6920Sstevel@tonic-gate * return value is either PSM_SUCCESS or PSM_FAILURE 6930Sstevel@tonic-gate */ 6940Sstevel@tonic-gate int 6950Sstevel@tonic-gate apic_intr_ops(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, 6960Sstevel@tonic-gate psm_intr_op_t intr_op, int *result) 6970Sstevel@tonic-gate { 698916Sschwartz int cap, ret; 6990Sstevel@tonic-gate int count_vec; 700916Sschwartz int cpu; 7010Sstevel@tonic-gate int old_priority; 7020Sstevel@tonic-gate int new_priority; 7033139Ssethg int iflag; 7040Sstevel@tonic-gate apic_irq_t *irqp; 7050Sstevel@tonic-gate struct intrspec *ispec, intr_spec; 7060Sstevel@tonic-gate 7070Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_intr_ops: dip: %p hdlp: %p " 7080Sstevel@tonic-gate "intr_op: %x\n", (void *)dip, (void *)hdlp, intr_op)); 7090Sstevel@tonic-gate 7100Sstevel@tonic-gate ispec = &intr_spec; 7110Sstevel@tonic-gate ispec->intrspec_pri = hdlp->ih_pri; 7120Sstevel@tonic-gate ispec->intrspec_vec = hdlp->ih_inum; 7130Sstevel@tonic-gate ispec->intrspec_func = hdlp->ih_cb_func; 7140Sstevel@tonic-gate 7150Sstevel@tonic-gate switch (intr_op) { 7160Sstevel@tonic-gate case PSM_INTR_OP_CHECK_MSI: 7170Sstevel@tonic-gate /* 7180Sstevel@tonic-gate * Check MSI/X is supported or not at APIC level and 7190Sstevel@tonic-gate * masked off the MSI/X bits in hdlp->ih_type if not 7200Sstevel@tonic-gate * supported before return. If MSI/X is supported, 7210Sstevel@tonic-gate * leave the ih_type unchanged and return. 7220Sstevel@tonic-gate * 7230Sstevel@tonic-gate * hdlp->ih_type passed in from the nexus has all the 7240Sstevel@tonic-gate * interrupt types supported by the device. 7250Sstevel@tonic-gate */ 7260Sstevel@tonic-gate if (apic_support_msi == 0) { 7270Sstevel@tonic-gate /* 7280Sstevel@tonic-gate * if apic_support_msi is not set, call 7290Sstevel@tonic-gate * apic_check_msi_support() to check whether msi 7300Sstevel@tonic-gate * is supported first 7310Sstevel@tonic-gate */ 7321701Sjohnny if (apic_check_msi_support() == PSM_SUCCESS) 7330Sstevel@tonic-gate apic_support_msi = 1; 7340Sstevel@tonic-gate else 7350Sstevel@tonic-gate apic_support_msi = -1; 7360Sstevel@tonic-gate } 7370Sstevel@tonic-gate if (apic_support_msi == 1) 7380Sstevel@tonic-gate *result = hdlp->ih_type; 7390Sstevel@tonic-gate else 7400Sstevel@tonic-gate *result = hdlp->ih_type & ~(DDI_INTR_TYPE_MSI | 7410Sstevel@tonic-gate DDI_INTR_TYPE_MSIX); 7420Sstevel@tonic-gate break; 7430Sstevel@tonic-gate case PSM_INTR_OP_ALLOC_VECTORS: 7440Sstevel@tonic-gate *result = apic_alloc_vectors(dip, hdlp->ih_inum, 7452335Sjohnny hdlp->ih_scratch1, hdlp->ih_pri, hdlp->ih_type, 7462335Sjohnny (int)(uintptr_t)hdlp->ih_scratch2); 7470Sstevel@tonic-gate break; 7480Sstevel@tonic-gate case PSM_INTR_OP_FREE_VECTORS: 7490Sstevel@tonic-gate apic_free_vectors(dip, hdlp->ih_inum, hdlp->ih_scratch1, 7500Sstevel@tonic-gate hdlp->ih_pri, hdlp->ih_type); 7510Sstevel@tonic-gate break; 7520Sstevel@tonic-gate case PSM_INTR_OP_NAVAIL_VECTORS: 7530Sstevel@tonic-gate *result = apic_navail_vector(dip, hdlp->ih_pri); 7540Sstevel@tonic-gate break; 7550Sstevel@tonic-gate case PSM_INTR_OP_XLATE_VECTOR: 756916Sschwartz ispec = ((ihdl_plat_t *)hdlp->ih_private)->ip_ispecp; 7570Sstevel@tonic-gate *result = apic_introp_xlate(dip, ispec, hdlp->ih_type); 7580Sstevel@tonic-gate break; 7590Sstevel@tonic-gate case PSM_INTR_OP_GET_PENDING: 7600Sstevel@tonic-gate if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 7610Sstevel@tonic-gate return (PSM_FAILURE); 7620Sstevel@tonic-gate *result = apic_get_pending(irqp, hdlp->ih_type); 7630Sstevel@tonic-gate break; 7640Sstevel@tonic-gate case PSM_INTR_OP_CLEAR_MASK: 7650Sstevel@tonic-gate if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 7660Sstevel@tonic-gate return (PSM_FAILURE); 7670Sstevel@tonic-gate irqp = apic_find_irq(dip, ispec, hdlp->ih_type); 7680Sstevel@tonic-gate if (irqp == NULL) 7690Sstevel@tonic-gate return (PSM_FAILURE); 7700Sstevel@tonic-gate apic_clear_mask(irqp); 7710Sstevel@tonic-gate break; 7720Sstevel@tonic-gate case PSM_INTR_OP_SET_MASK: 7730Sstevel@tonic-gate if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 7740Sstevel@tonic-gate return (PSM_FAILURE); 7750Sstevel@tonic-gate if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 7760Sstevel@tonic-gate return (PSM_FAILURE); 7770Sstevel@tonic-gate apic_set_mask(irqp); 7780Sstevel@tonic-gate break; 7790Sstevel@tonic-gate case PSM_INTR_OP_GET_CAP: 7800Sstevel@tonic-gate cap = DDI_INTR_FLAG_PENDING; 7810Sstevel@tonic-gate if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 7820Sstevel@tonic-gate cap |= DDI_INTR_FLAG_MASKABLE; 7830Sstevel@tonic-gate *result = cap; 7840Sstevel@tonic-gate break; 7850Sstevel@tonic-gate case PSM_INTR_OP_GET_SHARED: 7860Sstevel@tonic-gate if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 7870Sstevel@tonic-gate return (PSM_FAILURE); 7880Sstevel@tonic-gate if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 7890Sstevel@tonic-gate return (PSM_FAILURE); 7900Sstevel@tonic-gate *result = irqp->airq_share ? 1: 0; 7910Sstevel@tonic-gate break; 7920Sstevel@tonic-gate case PSM_INTR_OP_SET_PRI: 7930Sstevel@tonic-gate old_priority = hdlp->ih_pri; /* save old value */ 7940Sstevel@tonic-gate new_priority = *(int *)result; /* try the new value */ 7950Sstevel@tonic-gate 7960Sstevel@tonic-gate /* First, check if "hdlp->ih_scratch1" vectors exist? */ 7970Sstevel@tonic-gate if (apic_navail_vector(dip, new_priority) < hdlp->ih_scratch1) 7980Sstevel@tonic-gate return (PSM_FAILURE); 7990Sstevel@tonic-gate 8000Sstevel@tonic-gate /* Now allocate the vectors */ 8010Sstevel@tonic-gate count_vec = apic_alloc_vectors(dip, hdlp->ih_inum, 8022335Sjohnny hdlp->ih_scratch1, new_priority, hdlp->ih_type, 8032335Sjohnny DDI_INTR_ALLOC_STRICT); 8040Sstevel@tonic-gate 8053446Smrj /* Did we get new vectors? */ 8062335Sjohnny if (!count_vec) 8070Sstevel@tonic-gate return (PSM_FAILURE); 8080Sstevel@tonic-gate 8090Sstevel@tonic-gate /* Finally, free the previously allocated vectors */ 8100Sstevel@tonic-gate apic_free_vectors(dip, hdlp->ih_inum, count_vec, 8110Sstevel@tonic-gate old_priority, hdlp->ih_type); 8120Sstevel@tonic-gate hdlp->ih_pri = new_priority; /* set the new value */ 8130Sstevel@tonic-gate break; 814916Sschwartz case PSM_INTR_OP_SET_CPU: 815916Sschwartz /* 816916Sschwartz * The interrupt handle given here has been allocated 817916Sschwartz * specifically for this command, and ih_private carries 818916Sschwartz * a CPU value. 819916Sschwartz */ 820916Sschwartz cpu = (int)(intptr_t)hdlp->ih_private; 821916Sschwartz 822916Sschwartz if (!apic_cpu_in_range(cpu)) { 823916Sschwartz *result = EINVAL; 824916Sschwartz return (PSM_FAILURE); 825916Sschwartz } 826916Sschwartz 827916Sschwartz 828916Sschwartz /* Convert the vector to the irq using vector_to_irq table. */ 8293139Ssethg mutex_enter(&airq_mutex); 830916Sschwartz irqp = apic_irq_table[apic_vector_to_irq[hdlp->ih_vector]]; 8313139Ssethg mutex_exit(&airq_mutex); 8323139Ssethg 833916Sschwartz if (irqp == NULL) { 834916Sschwartz *result = ENXIO; 835916Sschwartz return (PSM_FAILURE); 836916Sschwartz } 8373139Ssethg 8383139Ssethg iflag = intr_clear(); 8393139Ssethg lock_set(&apic_ioapic_lock); 8403139Ssethg 8413139Ssethg ret = apic_rebind_all(irqp, cpu); 8423139Ssethg 8433139Ssethg lock_clear(&apic_ioapic_lock); 8443139Ssethg intr_restore(iflag); 8453139Ssethg 846916Sschwartz if (ret) { 847916Sschwartz *result = EIO; 848916Sschwartz return (PSM_FAILURE); 849916Sschwartz } 850916Sschwartz *result = 0; 851916Sschwartz break; 852916Sschwartz case PSM_INTR_OP_GET_INTR: 853916Sschwartz /* 854916Sschwartz * The interrupt handle given here has been allocated 855916Sschwartz * specifically for this command, and ih_private carries 856916Sschwartz * a pointer to a apic_get_intr_t. 857916Sschwartz */ 858916Sschwartz if (apic_get_vector_intr_info( 859916Sschwartz hdlp->ih_vector, hdlp->ih_private) != PSM_SUCCESS) 860916Sschwartz return (PSM_FAILURE); 861916Sschwartz break; 8620Sstevel@tonic-gate case PSM_INTR_OP_SET_CAP: 8630Sstevel@tonic-gate default: 8640Sstevel@tonic-gate return (PSM_FAILURE); 8650Sstevel@tonic-gate } 8660Sstevel@tonic-gate return (PSM_SUCCESS); 8670Sstevel@tonic-gate } 868