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 apic_irq_t *apic_find_irq(dev_info_t *, struct intrspec *, int); 510Sstevel@tonic-gate static int apic_get_pending(apic_irq_t *, int); 520Sstevel@tonic-gate static void apic_clear_mask(apic_irq_t *); 530Sstevel@tonic-gate static void apic_set_mask(apic_irq_t *); 540Sstevel@tonic-gate 550Sstevel@tonic-gate /* 560Sstevel@tonic-gate * MSI support flag: 570Sstevel@tonic-gate * reflects whether MSI is supported at APIC level 580Sstevel@tonic-gate * it can also be patched through /etc/system 590Sstevel@tonic-gate * 600Sstevel@tonic-gate * 0 = default value - don't know and need to call apic_check_msi_support() 610Sstevel@tonic-gate * to find out then set it accordingly 620Sstevel@tonic-gate * 1 = supported 630Sstevel@tonic-gate * -1 = not supported 640Sstevel@tonic-gate */ 650Sstevel@tonic-gate int apic_support_msi = 0; 660Sstevel@tonic-gate 670Sstevel@tonic-gate /* Multiple vector support for MSI */ 680Sstevel@tonic-gate int apic_multi_msi_enable = 1; 690Sstevel@tonic-gate int apic_multi_msi_max = 2; 700Sstevel@tonic-gate 71*4937Sjohnny /* Maximum no. of MSI-X vectors supported */ 72*4937Sjohnny int apic_msix_enable = 1; 73*4937Sjohnny int apic_msix_max = 2; 74*4937Sjohnny 750Sstevel@tonic-gate /* 760Sstevel@tonic-gate * apic_pci_msi_enable_vector: 770Sstevel@tonic-gate * Set the address/data fields in the MSI/X capability structure 780Sstevel@tonic-gate * XXX: MSI-X support 790Sstevel@tonic-gate */ 800Sstevel@tonic-gate /* ARGSUSED */ 81*4937Sjohnny void 820Sstevel@tonic-gate apic_pci_msi_enable_vector(dev_info_t *dip, int type, int inum, int vector, 830Sstevel@tonic-gate int count, int target_apic_id) 840Sstevel@tonic-gate { 851997Sanish uint64_t msi_addr, msi_data; 861997Sanish ushort_t msi_ctrl; 871997Sanish int cap_ptr = i_ddi_get_msi_msix_cap_ptr(dip); 881997Sanish ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(dip); 890Sstevel@tonic-gate 900Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: dip=0x%p\n" 910Sstevel@tonic-gate "\tdriver = %s, inum=0x%x vector=0x%x apicid=0x%x\n", (void *)dip, 920Sstevel@tonic-gate ddi_driver_name(dip), inum, vector, target_apic_id)); 930Sstevel@tonic-gate 94*4937Sjohnny ASSERT((handle != NULL) && (cap_ptr != 0)); 951997Sanish 960Sstevel@tonic-gate /* MSI Address */ 970Sstevel@tonic-gate msi_addr = (MSI_ADDR_HDR | (target_apic_id << MSI_ADDR_DEST_SHIFT)); 980Sstevel@tonic-gate msi_addr |= ((MSI_ADDR_RH_FIXED << MSI_ADDR_RH_SHIFT) | 994397Sschwartz (MSI_ADDR_DM_PHYSICAL << MSI_ADDR_DM_SHIFT)); 1000Sstevel@tonic-gate 1010Sstevel@tonic-gate /* MSI Data: MSI is edge triggered according to spec */ 1020Sstevel@tonic-gate msi_data = ((MSI_DATA_TM_EDGE << MSI_DATA_TM_SHIFT) | vector); 1030Sstevel@tonic-gate 1040Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: addr=0x%lx " 1050Sstevel@tonic-gate "data=0x%lx\n", (long)msi_addr, (long)msi_data)); 1060Sstevel@tonic-gate 1071997Sanish if (type == DDI_INTR_TYPE_MSI) { 1081997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 1091997Sanish 1101997Sanish /* Set the bits to inform how many MSIs are enabled */ 1111997Sanish msi_ctrl |= ((highbit(count) -1) << PCI_MSI_MME_SHIFT); 1121997Sanish pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 1131997Sanish 1141997Sanish pci_config_put32(handle, 1151997Sanish cap_ptr + PCI_MSI_ADDR_OFFSET, msi_addr); 1161997Sanish 1171997Sanish if (msi_ctrl & PCI_MSI_64BIT_MASK) { 1181997Sanish pci_config_put32(handle, 1191997Sanish cap_ptr + PCI_MSI_ADDR_OFFSET + 4, msi_addr >> 32); 1201997Sanish pci_config_put16(handle, 1211997Sanish cap_ptr + PCI_MSI_64BIT_DATA, msi_data); 1221997Sanish } else { 1231997Sanish pci_config_put16(handle, 1241997Sanish cap_ptr + PCI_MSI_32BIT_DATA, msi_data); 1251997Sanish } 1261997Sanish 1271997Sanish } else if (type == DDI_INTR_TYPE_MSIX) { 1281997Sanish uintptr_t off; 1291997Sanish ddi_intr_msix_t *msix_p = i_ddi_get_msix(dip); 1301997Sanish 1311997Sanish /* Offset into the "inum"th entry in the MSI-X table */ 1321997Sanish off = (uintptr_t)msix_p->msix_tbl_addr + 1331997Sanish (inum * PCI_MSIX_VECTOR_SIZE); 1341997Sanish 1351997Sanish ddi_put32(msix_p->msix_tbl_hdl, 1361997Sanish (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), msi_data); 1371997Sanish ddi_put64(msix_p->msix_tbl_hdl, 1381997Sanish (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), msi_addr); 1390Sstevel@tonic-gate } 1400Sstevel@tonic-gate } 1410Sstevel@tonic-gate 1420Sstevel@tonic-gate 1430Sstevel@tonic-gate /* 1440Sstevel@tonic-gate * This function returns the no. of vectors available for the pri. 1450Sstevel@tonic-gate * dip is not used at this moment. If we really don't need that, 1460Sstevel@tonic-gate * it will be removed. 1470Sstevel@tonic-gate */ 1480Sstevel@tonic-gate /*ARGSUSED*/ 1490Sstevel@tonic-gate int 1500Sstevel@tonic-gate apic_navail_vector(dev_info_t *dip, int pri) 1510Sstevel@tonic-gate { 1520Sstevel@tonic-gate int lowest, highest, i, navail, count; 1530Sstevel@tonic-gate 1540Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_navail_vector: dip: %p, pri: %x\n", 1550Sstevel@tonic-gate (void *)dip, pri)); 1560Sstevel@tonic-gate 1570Sstevel@tonic-gate highest = apic_ipltopri[pri] + APIC_VECTOR_MASK; 1580Sstevel@tonic-gate lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL; 1590Sstevel@tonic-gate navail = count = 0; 1600Sstevel@tonic-gate 1610Sstevel@tonic-gate /* It has to be contiguous */ 1620Sstevel@tonic-gate for (i = lowest; i < highest; i++) { 1630Sstevel@tonic-gate count = 0; 1640Sstevel@tonic-gate while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) && 1654397Sschwartz (i < highest)) { 1662335Sjohnny if (APIC_CHECK_RESERVE_VECTORS(i)) 1670Sstevel@tonic-gate break; 1680Sstevel@tonic-gate count++; 1690Sstevel@tonic-gate i++; 1700Sstevel@tonic-gate } 1710Sstevel@tonic-gate if (count > navail) 1720Sstevel@tonic-gate navail = count; 1730Sstevel@tonic-gate } 1740Sstevel@tonic-gate return (navail); 1750Sstevel@tonic-gate } 1760Sstevel@tonic-gate 1772335Sjohnny /* 1782335Sjohnny * Finds "count" contiguous MSI vectors starting at the proper alignment 1792335Sjohnny * at "pri". 1802335Sjohnny * Caller needs to make sure that count has to be power of 2 and should not 1812335Sjohnny * be < 1. 1822335Sjohnny */ 1833446Smrj uchar_t 1840Sstevel@tonic-gate apic_find_multi_vectors(int pri, int count) 1850Sstevel@tonic-gate { 1862335Sjohnny int lowest, highest, i, navail, start, msibits; 1870Sstevel@tonic-gate 1880Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_find_mult: pri: %x, count: %x\n", 1890Sstevel@tonic-gate pri, count)); 1900Sstevel@tonic-gate 1910Sstevel@tonic-gate highest = apic_ipltopri[pri] + APIC_VECTOR_MASK; 1920Sstevel@tonic-gate lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL; 1930Sstevel@tonic-gate navail = 0; 1940Sstevel@tonic-gate 1952335Sjohnny /* 1962335Sjohnny * msibits is the no. of lower order message data bits for the 1972335Sjohnny * allocated MSI vectors and is used to calculate the aligned 1982335Sjohnny * starting vector 1992335Sjohnny */ 2002335Sjohnny msibits = count - 1; 2012335Sjohnny 2020Sstevel@tonic-gate /* It has to be contiguous */ 2030Sstevel@tonic-gate for (i = lowest; i < highest; i++) { 2040Sstevel@tonic-gate navail = 0; 2052335Sjohnny 2062335Sjohnny /* 2072335Sjohnny * starting vector has to be aligned accordingly for 2082335Sjohnny * multiple MSIs 2092335Sjohnny */ 2102335Sjohnny if (msibits) 2112335Sjohnny i = (i + msibits) & ~msibits; 2120Sstevel@tonic-gate start = i; 2130Sstevel@tonic-gate while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) && 2144397Sschwartz (i < highest)) { 2152335Sjohnny if (APIC_CHECK_RESERVE_VECTORS(i)) 2160Sstevel@tonic-gate break; 2170Sstevel@tonic-gate navail++; 2180Sstevel@tonic-gate if (navail >= count) 2190Sstevel@tonic-gate return (start); 2200Sstevel@tonic-gate i++; 2210Sstevel@tonic-gate } 2220Sstevel@tonic-gate } 2230Sstevel@tonic-gate return (0); 2240Sstevel@tonic-gate } 2250Sstevel@tonic-gate 2263446Smrj 2270Sstevel@tonic-gate /* 2280Sstevel@tonic-gate * It finds the apic_irq_t associates with the dip, ispec and type. 2290Sstevel@tonic-gate */ 2300Sstevel@tonic-gate apic_irq_t * 2310Sstevel@tonic-gate apic_find_irq(dev_info_t *dip, struct intrspec *ispec, int type) 2320Sstevel@tonic-gate { 2330Sstevel@tonic-gate apic_irq_t *irqp; 2340Sstevel@tonic-gate int i; 2350Sstevel@tonic-gate 2360Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: dip=0x%p vec=0x%x " 2370Sstevel@tonic-gate "ipl=0x%x type=0x%x\n", (void *)dip, ispec->intrspec_vec, 2380Sstevel@tonic-gate ispec->intrspec_pri, type)); 2390Sstevel@tonic-gate 2400Sstevel@tonic-gate for (i = apic_min_device_irq; i <= apic_max_device_irq; i++) { 2410Sstevel@tonic-gate if ((irqp = apic_irq_table[i]) == NULL) 2420Sstevel@tonic-gate continue; 2430Sstevel@tonic-gate if ((irqp->airq_dip == dip) && 2440Sstevel@tonic-gate (irqp->airq_origirq == ispec->intrspec_vec) && 2450Sstevel@tonic-gate (irqp->airq_ipl == ispec->intrspec_pri)) { 246*4937Sjohnny if (type == DDI_INTR_TYPE_MSI) { 247*4937Sjohnny if (irqp->airq_mps_intr_index == MSI_INDEX) 248*4937Sjohnny return (irqp); 249*4937Sjohnny } else if (type == DDI_INTR_TYPE_MSIX) { 250*4937Sjohnny if (irqp->airq_mps_intr_index == MSIX_INDEX) 2510Sstevel@tonic-gate return (irqp); 2520Sstevel@tonic-gate } else 2530Sstevel@tonic-gate return (irqp); 2540Sstevel@tonic-gate } 2550Sstevel@tonic-gate } 2560Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: return NULL\n")); 2570Sstevel@tonic-gate return (NULL); 2580Sstevel@tonic-gate } 2590Sstevel@tonic-gate 2600Sstevel@tonic-gate 2610Sstevel@tonic-gate /* 2620Sstevel@tonic-gate * This function will return the pending bit of the irqp. 2630Sstevel@tonic-gate * It either comes from the IRR register of the APIC or the RDT 2640Sstevel@tonic-gate * entry of the I/O APIC. 2650Sstevel@tonic-gate * For the IRR to work, it needs to be to its binding CPU 2660Sstevel@tonic-gate */ 2670Sstevel@tonic-gate static int 2680Sstevel@tonic-gate apic_get_pending(apic_irq_t *irqp, int type) 2690Sstevel@tonic-gate { 2700Sstevel@tonic-gate int bit, index, irr, pending; 2710Sstevel@tonic-gate int intin_no; 2723446Smrj int apic_ix; 2730Sstevel@tonic-gate 2740Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_get_pending: irqp: %p, cpuid: %x " 2750Sstevel@tonic-gate "type: %x\n", (void *)irqp, irqp->airq_cpu & ~IRQ_USER_BOUND, 2760Sstevel@tonic-gate type)); 2770Sstevel@tonic-gate 2780Sstevel@tonic-gate /* need to get on the bound cpu */ 2790Sstevel@tonic-gate mutex_enter(&cpu_lock); 2800Sstevel@tonic-gate affinity_set(irqp->airq_cpu & ~IRQ_USER_BOUND); 2810Sstevel@tonic-gate 2820Sstevel@tonic-gate index = irqp->airq_vector / 32; 2830Sstevel@tonic-gate bit = irqp->airq_vector % 32; 2840Sstevel@tonic-gate irr = apicadr[APIC_IRR_REG + index]; 2850Sstevel@tonic-gate 2860Sstevel@tonic-gate affinity_clear(); 2870Sstevel@tonic-gate mutex_exit(&cpu_lock); 2880Sstevel@tonic-gate 2890Sstevel@tonic-gate pending = (irr & (1 << bit)) ? 1 : 0; 2900Sstevel@tonic-gate if (!pending && (type == DDI_INTR_TYPE_FIXED)) { 2910Sstevel@tonic-gate /* check I/O APIC for fixed interrupt */ 2920Sstevel@tonic-gate intin_no = irqp->airq_intin_no; 2933446Smrj apic_ix = irqp->airq_ioapicindex; 2943446Smrj pending = (READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no) & 2950Sstevel@tonic-gate AV_PENDING) ? 1 : 0; 2960Sstevel@tonic-gate } 2970Sstevel@tonic-gate return (pending); 2980Sstevel@tonic-gate } 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate 3010Sstevel@tonic-gate /* 3020Sstevel@tonic-gate * This function will clear the mask for the interrupt on the I/O APIC 3030Sstevel@tonic-gate */ 3040Sstevel@tonic-gate static void 3050Sstevel@tonic-gate apic_clear_mask(apic_irq_t *irqp) 3060Sstevel@tonic-gate { 3070Sstevel@tonic-gate int intin_no; 3083446Smrj ulong_t iflag; 3090Sstevel@tonic-gate int32_t rdt_entry; 3103446Smrj int apic_ix; 3110Sstevel@tonic-gate 3120Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_clear_mask: irqp: %p\n", 3130Sstevel@tonic-gate (void *)irqp)); 3140Sstevel@tonic-gate 3150Sstevel@tonic-gate intin_no = irqp->airq_intin_no; 3163446Smrj apic_ix = irqp->airq_ioapicindex; 3170Sstevel@tonic-gate 3180Sstevel@tonic-gate iflag = intr_clear(); 3190Sstevel@tonic-gate lock_set(&apic_ioapic_lock); 3200Sstevel@tonic-gate 3213446Smrj rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no); 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate /* clear mask */ 3243446Smrj WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no, 3250Sstevel@tonic-gate ((~AV_MASK) & rdt_entry)); 3260Sstevel@tonic-gate 3270Sstevel@tonic-gate lock_clear(&apic_ioapic_lock); 3280Sstevel@tonic-gate intr_restore(iflag); 3290Sstevel@tonic-gate } 3300Sstevel@tonic-gate 3310Sstevel@tonic-gate 3320Sstevel@tonic-gate /* 3330Sstevel@tonic-gate * This function will mask the interrupt on the I/O APIC 3340Sstevel@tonic-gate */ 3350Sstevel@tonic-gate static void 3360Sstevel@tonic-gate apic_set_mask(apic_irq_t *irqp) 3370Sstevel@tonic-gate { 3380Sstevel@tonic-gate int intin_no; 3393446Smrj int apic_ix; 3403446Smrj ulong_t iflag; 3410Sstevel@tonic-gate int32_t rdt_entry; 3420Sstevel@tonic-gate 3430Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_set_mask: irqp: %p\n", (void *)irqp)); 3440Sstevel@tonic-gate 3450Sstevel@tonic-gate intin_no = irqp->airq_intin_no; 3463446Smrj apic_ix = irqp->airq_ioapicindex; 3470Sstevel@tonic-gate 3480Sstevel@tonic-gate iflag = intr_clear(); 3490Sstevel@tonic-gate 3500Sstevel@tonic-gate lock_set(&apic_ioapic_lock); 3510Sstevel@tonic-gate 3523446Smrj rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no); 3530Sstevel@tonic-gate 3540Sstevel@tonic-gate /* mask it */ 3553446Smrj WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no, 3560Sstevel@tonic-gate (AV_MASK | rdt_entry)); 3570Sstevel@tonic-gate 3580Sstevel@tonic-gate lock_clear(&apic_ioapic_lock); 3590Sstevel@tonic-gate intr_restore(iflag); 3600Sstevel@tonic-gate } 3610Sstevel@tonic-gate 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate void 3640Sstevel@tonic-gate apic_free_vectors(dev_info_t *dip, int inum, int count, int pri, int type) 3650Sstevel@tonic-gate { 3660Sstevel@tonic-gate int i; 3670Sstevel@tonic-gate apic_irq_t *irqptr; 3680Sstevel@tonic-gate struct intrspec ispec; 3690Sstevel@tonic-gate 3700Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: dip: %p inum: %x " 3710Sstevel@tonic-gate "count: %x pri: %x type: %x\n", 3720Sstevel@tonic-gate (void *)dip, inum, count, pri, type)); 3730Sstevel@tonic-gate 3740Sstevel@tonic-gate /* for MSI/X only */ 3750Sstevel@tonic-gate if (!DDI_INTR_IS_MSI_OR_MSIX(type)) 3760Sstevel@tonic-gate return; 3770Sstevel@tonic-gate 3780Sstevel@tonic-gate for (i = 0; i < count; i++) { 3790Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: inum=0x%x " 3800Sstevel@tonic-gate "pri=0x%x count=0x%x\n", inum, pri, count)); 3810Sstevel@tonic-gate ispec.intrspec_vec = inum + i; 3820Sstevel@tonic-gate ispec.intrspec_pri = pri; 3830Sstevel@tonic-gate if ((irqptr = apic_find_irq(dip, &ispec, type)) == NULL) { 3840Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: " 3850Sstevel@tonic-gate "dip=0x%p inum=0x%x pri=0x%x apic_find_irq() " 3860Sstevel@tonic-gate "failed\n", (void *)dip, inum, pri)); 3870Sstevel@tonic-gate continue; 3880Sstevel@tonic-gate } 3890Sstevel@tonic-gate irqptr->airq_mps_intr_index = FREE_INDEX; 3900Sstevel@tonic-gate apic_vector_to_irq[irqptr->airq_vector] = APIC_RESV_IRQ; 3910Sstevel@tonic-gate } 3920Sstevel@tonic-gate } 3930Sstevel@tonic-gate 3940Sstevel@tonic-gate 3950Sstevel@tonic-gate /* 3960Sstevel@tonic-gate * check whether the system supports MSI 3970Sstevel@tonic-gate * 3980Sstevel@tonic-gate * If PCI-E capability is found, then this must be a PCI-E system. 3990Sstevel@tonic-gate * Since MSI is required for PCI-E system, it returns PSM_SUCCESS 4000Sstevel@tonic-gate * to indicate this system supports MSI. 4010Sstevel@tonic-gate */ 4020Sstevel@tonic-gate int 4031701Sjohnny apic_check_msi_support() 4040Sstevel@tonic-gate { 4051701Sjohnny dev_info_t *cdip; 406881Sjohnny char dev_type[16]; 407881Sjohnny int dev_len; 4080Sstevel@tonic-gate 4091701Sjohnny DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support:\n")); 4100Sstevel@tonic-gate 4111701Sjohnny /* 4121701Sjohnny * check whether the first level children of root_node have 4131701Sjohnny * PCI-E capability 4141701Sjohnny */ 4151701Sjohnny for (cdip = ddi_get_child(ddi_root_node()); cdip != NULL; 4161701Sjohnny cdip = ddi_get_next_sibling(cdip)) { 4171701Sjohnny 4181701Sjohnny DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: cdip: 0x%p," 4191701Sjohnny " driver: %s, binding: %s, nodename: %s\n", (void *)cdip, 4201701Sjohnny ddi_driver_name(cdip), ddi_binding_name(cdip), 4211701Sjohnny ddi_node_name(cdip))); 422881Sjohnny dev_len = sizeof (dev_type); 4231701Sjohnny if (ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS, 424881Sjohnny "device_type", (caddr_t)dev_type, &dev_len) 425881Sjohnny != DDI_PROP_SUCCESS) 426881Sjohnny continue; 427881Sjohnny if (strcmp(dev_type, "pciex") == 0) 428881Sjohnny return (PSM_SUCCESS); 4290Sstevel@tonic-gate } 4300Sstevel@tonic-gate 4310Sstevel@tonic-gate /* MSI is not supported on this system */ 432881Sjohnny DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: no 'pciex' " 433881Sjohnny "device_type found\n")); 4340Sstevel@tonic-gate return (PSM_FAILURE); 4350Sstevel@tonic-gate } 4360Sstevel@tonic-gate 4370Sstevel@tonic-gate /* 4381997Sanish * apic_pci_msi_unconfigure: 4391997Sanish * 4401997Sanish * This and next two interfaces are copied from pci_intr_lib.c 4411997Sanish * Do ensure that these two files stay in sync. 4421997Sanish * These needed to be copied over here to avoid a deadlock situation on 4431997Sanish * certain mp systems that use MSI interrupts. 4441997Sanish * 4451997Sanish * IMPORTANT regards next three interfaces: 4461997Sanish * i) are called only for MSI/X interrupts. 4471997Sanish * ii) called with interrupts disabled, and must not block 4481997Sanish */ 449*4937Sjohnny void 4501997Sanish apic_pci_msi_unconfigure(dev_info_t *rdip, int type, int inum) 4511997Sanish { 4521997Sanish ushort_t msi_ctrl; 4531997Sanish int cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip); 4541997Sanish ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(rdip); 4551997Sanish 456*4937Sjohnny ASSERT((handle != NULL) && (cap_ptr != 0)); 4571997Sanish 4581997Sanish if (type == DDI_INTR_TYPE_MSI) { 4591997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 4601997Sanish msi_ctrl &= (~PCI_MSI_MME_MASK); 4611997Sanish pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 4621997Sanish pci_config_put32(handle, cap_ptr + PCI_MSI_ADDR_OFFSET, 0); 4631997Sanish 4641997Sanish if (msi_ctrl & PCI_MSI_64BIT_MASK) { 4651997Sanish pci_config_put16(handle, 4661997Sanish cap_ptr + PCI_MSI_64BIT_DATA, 0); 4671997Sanish pci_config_put32(handle, 4681997Sanish cap_ptr + PCI_MSI_ADDR_OFFSET + 4, 0); 4691997Sanish } else { 4701997Sanish pci_config_put16(handle, 4711997Sanish cap_ptr + PCI_MSI_32BIT_DATA, 0); 4721997Sanish } 4731997Sanish 4741997Sanish } else if (type == DDI_INTR_TYPE_MSIX) { 4751997Sanish uintptr_t off; 476*4937Sjohnny uint32_t mask; 4771997Sanish ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 4781997Sanish 479*4937Sjohnny /* Offset into "inum"th entry in the MSI-X table & mask it */ 480*4937Sjohnny off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 481*4937Sjohnny PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 482*4937Sjohnny 483*4937Sjohnny mask = ddi_get32(msix_p->msix_tbl_hdl, (uint32_t *)off); 484*4937Sjohnny 485*4937Sjohnny ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, (mask | 1)); 486*4937Sjohnny 4871997Sanish /* Offset into the "inum"th entry in the MSI-X table */ 4881997Sanish off = (uintptr_t)msix_p->msix_tbl_addr + 4891997Sanish (inum * PCI_MSIX_VECTOR_SIZE); 4901997Sanish 4911997Sanish /* Reset the "data" and "addr" bits */ 4921997Sanish ddi_put32(msix_p->msix_tbl_hdl, 4931997Sanish (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), 0); 4941997Sanish ddi_put64(msix_p->msix_tbl_hdl, (uint64_t *)off, 0); 4951997Sanish } 4961997Sanish } 4971997Sanish 4981997Sanish 4991997Sanish /* 5001997Sanish * apic_pci_msi_enable_mode: 5011997Sanish */ 502*4937Sjohnny void 5031997Sanish apic_pci_msi_enable_mode(dev_info_t *rdip, int type, int inum) 5041997Sanish { 5051997Sanish ushort_t msi_ctrl; 5061997Sanish int cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip); 5071997Sanish ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(rdip); 5081997Sanish 509*4937Sjohnny ASSERT((handle != NULL) && (cap_ptr != 0)); 5101997Sanish 5111997Sanish if (type == DDI_INTR_TYPE_MSI) { 5121997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 5131997Sanish if ((msi_ctrl & PCI_MSI_ENABLE_BIT)) 514*4937Sjohnny return; 5151997Sanish 5161997Sanish msi_ctrl |= PCI_MSI_ENABLE_BIT; 5171997Sanish pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 5181997Sanish 5191997Sanish } else if (type == DDI_INTR_TYPE_MSIX) { 5201997Sanish uintptr_t off; 521*4937Sjohnny uint32_t mask; 5221997Sanish ddi_intr_msix_t *msix_p; 5231997Sanish 5241997Sanish msix_p = i_ddi_get_msix(rdip); 5251997Sanish 5261997Sanish /* Offset into "inum"th entry in the MSI-X table & clear mask */ 5271997Sanish off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 5281997Sanish PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 529*4937Sjohnny 530*4937Sjohnny mask = ddi_get32(msix_p->msix_tbl_hdl, (uint32_t *)off); 531*4937Sjohnny 532*4937Sjohnny ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, (mask & ~1)); 533*4937Sjohnny 534*4937Sjohnny msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSIX_CTRL); 535*4937Sjohnny 536*4937Sjohnny if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT)) { 537*4937Sjohnny msi_ctrl |= PCI_MSIX_ENABLE_BIT; 538*4937Sjohnny pci_config_put16(handle, cap_ptr + PCI_MSIX_CTRL, 539*4937Sjohnny msi_ctrl); 540*4937Sjohnny } 5411997Sanish } 5421997Sanish } 5431997Sanish 5441997Sanish /* 5451997Sanish * apic_pci_msi_disable_mode: 5461997Sanish */ 547*4937Sjohnny void 548*4937Sjohnny apic_pci_msi_disable_mode(dev_info_t *rdip, int type) 5491997Sanish { 5501997Sanish ushort_t msi_ctrl; 5511997Sanish int cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip); 5521997Sanish ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(rdip); 5531997Sanish 554*4937Sjohnny ASSERT((handle != NULL) && (cap_ptr != 0)); 5551997Sanish 5561997Sanish if (type == DDI_INTR_TYPE_MSI) { 5571997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 5581997Sanish if (!(msi_ctrl & PCI_MSI_ENABLE_BIT)) 559*4937Sjohnny return; 5601997Sanish 5611997Sanish msi_ctrl &= ~PCI_MSI_ENABLE_BIT; /* MSI disable */ 5621997Sanish pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 5631997Sanish 5641997Sanish } else if (type == DDI_INTR_TYPE_MSIX) { 5651997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSIX_CTRL); 566*4937Sjohnny if (msi_ctrl & PCI_MSIX_ENABLE_BIT) { 567*4937Sjohnny msi_ctrl &= ~PCI_MSIX_ENABLE_BIT; 568*4937Sjohnny pci_config_put16(handle, cap_ptr + PCI_MSIX_CTRL, 569*4937Sjohnny msi_ctrl); 570*4937Sjohnny } 5711997Sanish } 5721997Sanish } 5731997Sanish 5744397Sschwartz static int 5754397Sschwartz apic_set_cpu(uint32_t vector, int cpu, int *result) 5764397Sschwartz { 5774397Sschwartz apic_irq_t *irqp; 5784397Sschwartz int iflag; 5794397Sschwartz int ret; 5804397Sschwartz 5814397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, "APIC_SET_CPU\n")); 5824397Sschwartz 5834397Sschwartz /* Convert the vector to the irq using vector_to_irq table. */ 5844397Sschwartz mutex_enter(&airq_mutex); 5854397Sschwartz irqp = apic_irq_table[apic_vector_to_irq[vector]]; 5864397Sschwartz mutex_exit(&airq_mutex); 5874397Sschwartz 5884397Sschwartz if (irqp == NULL) { 5894397Sschwartz *result = ENXIO; 5904397Sschwartz return (PSM_FAILURE); 5914397Sschwartz } 5924397Sschwartz 5934397Sschwartz /* Fail if this is an MSI intr and is part of a group. */ 5944397Sschwartz if ((irqp->airq_mps_intr_index == MSI_INDEX) && 5954397Sschwartz (irqp->airq_intin_no > 1)) { 5964397Sschwartz *result = ENXIO; 5974397Sschwartz return (PSM_FAILURE); 5984397Sschwartz } 5994397Sschwartz 6004397Sschwartz iflag = intr_clear(); 6014397Sschwartz lock_set(&apic_ioapic_lock); 6024397Sschwartz 6034397Sschwartz ret = apic_rebind_all(irqp, cpu); 6044397Sschwartz 6054397Sschwartz lock_clear(&apic_ioapic_lock); 6064397Sschwartz intr_restore(iflag); 6074397Sschwartz 6084397Sschwartz if (ret) { 6094397Sschwartz *result = EIO; 6104397Sschwartz return (PSM_FAILURE); 6114397Sschwartz } 6124397Sschwartz *result = 0; 6134397Sschwartz return (PSM_SUCCESS); 6144397Sschwartz } 6154397Sschwartz 6164397Sschwartz static int 6174397Sschwartz apic_grp_set_cpu(uint32_t vector, int new_cpu, int *result) 6184397Sschwartz { 6194397Sschwartz dev_info_t *orig_dip; 6204397Sschwartz uchar_t orig_cpu; 6214397Sschwartz int iflag; 6224397Sschwartz apic_irq_t *irqps[PCI_MSI_MAX_INTRS]; 6234397Sschwartz int i; 6244397Sschwartz int cap_ptr; 6254397Sschwartz int msi_mask_off; 6264397Sschwartz ushort_t msi_ctrl; 6274397Sschwartz uint32_t msi_pvm; 6284397Sschwartz ddi_acc_handle_t handle; 6294397Sschwartz int num_vectors = 0; 6304397Sschwartz 6314397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, "APIC_GRP_SET_CPU\n")); 6324397Sschwartz 6334397Sschwartz /* 6344397Sschwartz * Take mutex to insure that table doesn't change out from underneath 6354397Sschwartz * us while we're playing with it. 6364397Sschwartz */ 6374397Sschwartz mutex_enter(&airq_mutex); 6384397Sschwartz irqps[0] = apic_irq_table[apic_vector_to_irq[vector]]; 6394397Sschwartz orig_cpu = irqps[0]->airq_temp_cpu; 6404397Sschwartz orig_dip = irqps[0]->airq_dip; 6414397Sschwartz num_vectors = irqps[0]->airq_intin_no; 6424397Sschwartz 6434397Sschwartz /* A "group" of 1 */ 6444397Sschwartz if (num_vectors == 1) { 6454397Sschwartz mutex_exit(&airq_mutex); 6464397Sschwartz return (apic_set_cpu(vector, new_cpu, result)); 6474397Sschwartz } 6484397Sschwartz 6494397Sschwartz *result = ENXIO; 6504397Sschwartz 6514397Sschwartz if (irqps[0]->airq_mps_intr_index != MSI_INDEX) { 6524397Sschwartz mutex_exit(&airq_mutex); 6534397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, "set_grp: intr not MSI\n")); 6544397Sschwartz goto set_grp_intr_done; 6554397Sschwartz } 6564397Sschwartz if ((num_vectors < 1) || ((num_vectors - 1) & vector)) { 6574397Sschwartz mutex_exit(&airq_mutex); 6584397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 6594397Sschwartz "set_grp: base vec not part of a grp or not aligned: " 6604397Sschwartz "vec:0x%x, num_vec:0x%x\n", vector, num_vectors)); 6614397Sschwartz goto set_grp_intr_done; 6624397Sschwartz } 6634397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, "set_grp: num intrs in grp: %d\n", 6644397Sschwartz num_vectors)); 6654397Sschwartz 6664397Sschwartz ASSERT((num_vectors + vector) < APIC_MAX_VECTOR); 6674397Sschwartz 6684397Sschwartz *result = EIO; 6694397Sschwartz 6704397Sschwartz /* 6714397Sschwartz * All IRQ entries in the table for the given device will be not 6724397Sschwartz * shared. Since they are not shared, the dip in the table will 6734397Sschwartz * be true to the device of interest. 6744397Sschwartz */ 6754397Sschwartz for (i = 1; i < num_vectors; i++) { 6764397Sschwartz irqps[i] = apic_irq_table[apic_vector_to_irq[vector + i]]; 6774397Sschwartz if (irqps[i] == NULL) { 6784397Sschwartz mutex_exit(&airq_mutex); 6794397Sschwartz goto set_grp_intr_done; 6804397Sschwartz } 6814397Sschwartz #ifdef DEBUG 6824397Sschwartz /* Sanity check: CPU and dip is the same for all entries. */ 6834397Sschwartz if ((irqps[i]->airq_dip != orig_dip) || 6844397Sschwartz (irqps[i]->airq_temp_cpu != orig_cpu)) { 6854397Sschwartz mutex_exit(&airq_mutex); 6864397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 6874397Sschwartz "set_grp: cpu or dip for vec 0x%x difft than for " 6884397Sschwartz "vec 0x%x\n", vector, vector + i)); 6894397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 6904397Sschwartz " cpu: %d vs %d, dip: 0x%p vs 0x%p\n", orig_cpu, 6914397Sschwartz irqps[i]->airq_temp_cpu, (void *)orig_dip, 6924397Sschwartz (void *)irqps[i]->airq_dip)); 6934397Sschwartz goto set_grp_intr_done; 6944397Sschwartz } 6954397Sschwartz #endif /* DEBUG */ 6964397Sschwartz } 6974397Sschwartz mutex_exit(&airq_mutex); 6984397Sschwartz 6994397Sschwartz cap_ptr = i_ddi_get_msi_msix_cap_ptr(orig_dip); 7004397Sschwartz handle = i_ddi_get_pci_config_handle(orig_dip); 7014397Sschwartz msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 7024397Sschwartz 7034397Sschwartz /* MSI Per vector masking is supported. */ 7044397Sschwartz if (msi_ctrl & PCI_MSI_PVM_MASK) { 7054397Sschwartz if (msi_ctrl & PCI_MSI_64BIT_MASK) 7064397Sschwartz msi_mask_off = cap_ptr + PCI_MSI_64BIT_MASKBITS; 7074397Sschwartz else 7084397Sschwartz msi_mask_off = cap_ptr + PCI_MSI_32BIT_MASK; 7094397Sschwartz msi_pvm = pci_config_get32(handle, msi_mask_off); 7104397Sschwartz pci_config_put32(handle, msi_mask_off, (uint32_t)-1); 7114397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 7124397Sschwartz "set_grp: pvm supported. Mask set to 0x%x\n", 7134397Sschwartz pci_config_get32(handle, msi_mask_off))); 7144397Sschwartz } 7154397Sschwartz 7164397Sschwartz iflag = intr_clear(); 7174397Sschwartz lock_set(&apic_ioapic_lock); 7184397Sschwartz 7194397Sschwartz /* 7204397Sschwartz * Do the first rebind and check for errors. Apic_rebind_all returns 7214397Sschwartz * an error if the CPU is not accepting interrupts. If the first one 7224397Sschwartz * succeeds they all will. 7234397Sschwartz */ 7244397Sschwartz if (apic_rebind_all(irqps[0], new_cpu)) 7254397Sschwartz (void) apic_rebind_all(irqps[0], orig_cpu); 7264397Sschwartz else { 7274397Sschwartz for (i = 1; i < num_vectors; i++) 7284397Sschwartz (void) apic_rebind_all(irqps[i], new_cpu); 7294397Sschwartz *result = 0; /* SUCCESS */ 7304397Sschwartz } 7314397Sschwartz 7324397Sschwartz lock_clear(&apic_ioapic_lock); 7334397Sschwartz intr_restore(iflag); 7344397Sschwartz 7354397Sschwartz /* Reenable vectors if per vector masking is supported. */ 7364397Sschwartz if (msi_ctrl & PCI_MSI_PVM_MASK) { 7374397Sschwartz pci_config_put32(handle, msi_mask_off, msi_pvm); 7384397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 7394397Sschwartz "set_grp: pvm supported. Mask restored to 0x%x\n", 7404397Sschwartz pci_config_get32(handle, msi_mask_off))); 7414397Sschwartz } 7424397Sschwartz 7434397Sschwartz set_grp_intr_done: 7444397Sschwartz if (*result != 0) 7454397Sschwartz return (PSM_FAILURE); 7464397Sschwartz 7474397Sschwartz return (PSM_SUCCESS); 7484397Sschwartz } 7494397Sschwartz 7504397Sschwartz static int 7514397Sschwartz apic_get_vector_intr_info(int vecirq, apic_get_intr_t *intr_params_p) 7524397Sschwartz { 7534397Sschwartz struct autovec *av_dev; 7544397Sschwartz uchar_t irqno; 7554397Sschwartz int i; 7564397Sschwartz apic_irq_t *irq_p; 7574397Sschwartz 7584397Sschwartz /* Sanity check the vector/irq argument. */ 7594397Sschwartz ASSERT((vecirq >= 0) || (vecirq <= APIC_MAX_VECTOR)); 7604397Sschwartz 7614397Sschwartz mutex_enter(&airq_mutex); 7624397Sschwartz 7634397Sschwartz /* 7644397Sschwartz * Convert the vecirq arg to an irq using vector_to_irq table 7654397Sschwartz * if the arg is a vector. Pass thru if already an irq. 7664397Sschwartz */ 7674397Sschwartz if ((intr_params_p->avgi_req_flags & PSMGI_INTRBY_FLAGS) == 7684397Sschwartz PSMGI_INTRBY_VEC) 7694397Sschwartz irqno = apic_vector_to_irq[vecirq]; 7704397Sschwartz else 7714397Sschwartz irqno = vecirq; 7724397Sschwartz 7734397Sschwartz irq_p = apic_irq_table[irqno]; 7744397Sschwartz 7754397Sschwartz if ((irq_p == NULL) || 7764397Sschwartz (irq_p->airq_temp_cpu == IRQ_UNBOUND) || 7774397Sschwartz (irq_p->airq_temp_cpu == IRQ_UNINIT)) { 7784397Sschwartz mutex_exit(&airq_mutex); 7794397Sschwartz return (PSM_FAILURE); 7804397Sschwartz } 7814397Sschwartz 7824397Sschwartz if (intr_params_p->avgi_req_flags & PSMGI_REQ_CPUID) { 7834397Sschwartz 7844397Sschwartz /* Get the (temp) cpu from apic_irq table, indexed by irq. */ 7854397Sschwartz intr_params_p->avgi_cpu_id = irq_p->airq_temp_cpu; 7864397Sschwartz 7874397Sschwartz /* Return user bound info for intrd. */ 7884397Sschwartz if (intr_params_p->avgi_cpu_id & IRQ_USER_BOUND) { 7894397Sschwartz intr_params_p->avgi_cpu_id &= ~IRQ_USER_BOUND; 7904397Sschwartz intr_params_p->avgi_cpu_id |= PSMGI_CPU_USER_BOUND; 7914397Sschwartz } 7924397Sschwartz } 7934397Sschwartz 7944397Sschwartz if (intr_params_p->avgi_req_flags & PSMGI_REQ_VECTOR) 7954397Sschwartz intr_params_p->avgi_vector = irq_p->airq_vector; 7964397Sschwartz 7974397Sschwartz if (intr_params_p->avgi_req_flags & 7984397Sschwartz (PSMGI_REQ_NUM_DEVS | PSMGI_REQ_GET_DEVS)) 7994397Sschwartz /* Get number of devices from apic_irq table shared field. */ 8004397Sschwartz intr_params_p->avgi_num_devs = irq_p->airq_share; 8014397Sschwartz 8024397Sschwartz if (intr_params_p->avgi_req_flags & PSMGI_REQ_GET_DEVS) { 8034397Sschwartz 8044397Sschwartz intr_params_p->avgi_req_flags |= PSMGI_REQ_NUM_DEVS; 8054397Sschwartz 8064397Sschwartz /* Some devices have NULL dip. Don't count these. */ 8074397Sschwartz if (intr_params_p->avgi_num_devs > 0) { 8084397Sschwartz for (i = 0, av_dev = autovect[irqno].avh_link; 8094397Sschwartz av_dev; av_dev = av_dev->av_link) 8104397Sschwartz if (av_dev->av_vector && av_dev->av_dip) 8114397Sschwartz i++; 8124397Sschwartz intr_params_p->avgi_num_devs = 8134397Sschwartz MIN(intr_params_p->avgi_num_devs, i); 8144397Sschwartz } 8154397Sschwartz 8164397Sschwartz /* There are no viable dips to return. */ 8174397Sschwartz if (intr_params_p->avgi_num_devs == 0) 8184397Sschwartz intr_params_p->avgi_dip_list = NULL; 8194397Sschwartz 8204397Sschwartz else { /* Return list of dips */ 8214397Sschwartz 8224397Sschwartz /* Allocate space in array for that number of devs. */ 8234397Sschwartz intr_params_p->avgi_dip_list = kmem_zalloc( 8244397Sschwartz intr_params_p->avgi_num_devs * 8254397Sschwartz sizeof (dev_info_t *), 8264397Sschwartz KM_SLEEP); 8274397Sschwartz 8284397Sschwartz /* 8294397Sschwartz * Loop through the device list of the autovec table 8304397Sschwartz * filling in the dip array. 8314397Sschwartz * 8324397Sschwartz * Note that the autovect table may have some special 8334397Sschwartz * entries which contain NULL dips. These will be 8344397Sschwartz * ignored. 8354397Sschwartz */ 8364397Sschwartz for (i = 0, av_dev = autovect[irqno].avh_link; 8374397Sschwartz av_dev; av_dev = av_dev->av_link) 8384397Sschwartz if (av_dev->av_vector && av_dev->av_dip) 8394397Sschwartz intr_params_p->avgi_dip_list[i++] = 8404397Sschwartz av_dev->av_dip; 8414397Sschwartz } 8424397Sschwartz } 8434397Sschwartz 8444397Sschwartz mutex_exit(&airq_mutex); 8454397Sschwartz 8464397Sschwartz return (PSM_SUCCESS); 8474397Sschwartz } 8484397Sschwartz 8494397Sschwartz 8501997Sanish /* 8510Sstevel@tonic-gate * This function provides external interface to the nexus for all 8520Sstevel@tonic-gate * functionalities related to the new DDI interrupt framework. 8530Sstevel@tonic-gate * 8540Sstevel@tonic-gate * Input: 8550Sstevel@tonic-gate * dip - pointer to the dev_info structure of the requested device 8560Sstevel@tonic-gate * hdlp - pointer to the internal interrupt handle structure for the 8570Sstevel@tonic-gate * requested interrupt 8580Sstevel@tonic-gate * intr_op - opcode for this call 8590Sstevel@tonic-gate * result - pointer to the integer that will hold the result to be 8600Sstevel@tonic-gate * passed back if return value is PSM_SUCCESS 8610Sstevel@tonic-gate * 8620Sstevel@tonic-gate * Output: 8630Sstevel@tonic-gate * return value is either PSM_SUCCESS or PSM_FAILURE 8640Sstevel@tonic-gate */ 8650Sstevel@tonic-gate int 8660Sstevel@tonic-gate apic_intr_ops(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, 8670Sstevel@tonic-gate psm_intr_op_t intr_op, int *result) 8680Sstevel@tonic-gate { 8694397Sschwartz int cap; 8700Sstevel@tonic-gate int count_vec; 8710Sstevel@tonic-gate int old_priority; 8720Sstevel@tonic-gate int new_priority; 8734397Sschwartz int new_cpu; 8740Sstevel@tonic-gate apic_irq_t *irqp; 8750Sstevel@tonic-gate struct intrspec *ispec, intr_spec; 8760Sstevel@tonic-gate 8770Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_intr_ops: dip: %p hdlp: %p " 8780Sstevel@tonic-gate "intr_op: %x\n", (void *)dip, (void *)hdlp, intr_op)); 8790Sstevel@tonic-gate 8800Sstevel@tonic-gate ispec = &intr_spec; 8810Sstevel@tonic-gate ispec->intrspec_pri = hdlp->ih_pri; 8820Sstevel@tonic-gate ispec->intrspec_vec = hdlp->ih_inum; 8830Sstevel@tonic-gate ispec->intrspec_func = hdlp->ih_cb_func; 8840Sstevel@tonic-gate 8850Sstevel@tonic-gate switch (intr_op) { 8860Sstevel@tonic-gate case PSM_INTR_OP_CHECK_MSI: 8870Sstevel@tonic-gate /* 8880Sstevel@tonic-gate * Check MSI/X is supported or not at APIC level and 8890Sstevel@tonic-gate * masked off the MSI/X bits in hdlp->ih_type if not 8900Sstevel@tonic-gate * supported before return. If MSI/X is supported, 8910Sstevel@tonic-gate * leave the ih_type unchanged and return. 8920Sstevel@tonic-gate * 8930Sstevel@tonic-gate * hdlp->ih_type passed in from the nexus has all the 8940Sstevel@tonic-gate * interrupt types supported by the device. 8950Sstevel@tonic-gate */ 8960Sstevel@tonic-gate if (apic_support_msi == 0) { 8970Sstevel@tonic-gate /* 8980Sstevel@tonic-gate * if apic_support_msi is not set, call 8990Sstevel@tonic-gate * apic_check_msi_support() to check whether msi 9000Sstevel@tonic-gate * is supported first 9010Sstevel@tonic-gate */ 9021701Sjohnny if (apic_check_msi_support() == PSM_SUCCESS) 9030Sstevel@tonic-gate apic_support_msi = 1; 9040Sstevel@tonic-gate else 9050Sstevel@tonic-gate apic_support_msi = -1; 9060Sstevel@tonic-gate } 907*4937Sjohnny if (apic_support_msi == 1) { 908*4937Sjohnny if (apic_msix_enable) 909*4937Sjohnny *result = hdlp->ih_type; 910*4937Sjohnny else 911*4937Sjohnny *result = hdlp->ih_type & ~DDI_INTR_TYPE_MSIX; 912*4937Sjohnny } else 9130Sstevel@tonic-gate *result = hdlp->ih_type & ~(DDI_INTR_TYPE_MSI | 9140Sstevel@tonic-gate DDI_INTR_TYPE_MSIX); 9150Sstevel@tonic-gate break; 9160Sstevel@tonic-gate case PSM_INTR_OP_ALLOC_VECTORS: 917*4937Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_MSI) 918*4937Sjohnny *result = apic_alloc_msi_vectors(dip, hdlp->ih_inum, 919*4937Sjohnny hdlp->ih_scratch1, hdlp->ih_pri, 920*4937Sjohnny (int)(uintptr_t)hdlp->ih_scratch2); 921*4937Sjohnny else 922*4937Sjohnny *result = apic_alloc_msix_vectors(dip, hdlp->ih_inum, 923*4937Sjohnny hdlp->ih_scratch1, hdlp->ih_pri, 924*4937Sjohnny (int)(uintptr_t)hdlp->ih_scratch2); 9250Sstevel@tonic-gate break; 9260Sstevel@tonic-gate case PSM_INTR_OP_FREE_VECTORS: 9270Sstevel@tonic-gate apic_free_vectors(dip, hdlp->ih_inum, hdlp->ih_scratch1, 9280Sstevel@tonic-gate hdlp->ih_pri, hdlp->ih_type); 9290Sstevel@tonic-gate break; 9300Sstevel@tonic-gate case PSM_INTR_OP_NAVAIL_VECTORS: 9310Sstevel@tonic-gate *result = apic_navail_vector(dip, hdlp->ih_pri); 9320Sstevel@tonic-gate break; 9330Sstevel@tonic-gate case PSM_INTR_OP_XLATE_VECTOR: 934916Sschwartz ispec = ((ihdl_plat_t *)hdlp->ih_private)->ip_ispecp; 9350Sstevel@tonic-gate *result = apic_introp_xlate(dip, ispec, hdlp->ih_type); 9360Sstevel@tonic-gate break; 9370Sstevel@tonic-gate case PSM_INTR_OP_GET_PENDING: 9380Sstevel@tonic-gate if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 9390Sstevel@tonic-gate return (PSM_FAILURE); 9400Sstevel@tonic-gate *result = apic_get_pending(irqp, hdlp->ih_type); 9410Sstevel@tonic-gate break; 9420Sstevel@tonic-gate case PSM_INTR_OP_CLEAR_MASK: 9430Sstevel@tonic-gate if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 9440Sstevel@tonic-gate return (PSM_FAILURE); 9450Sstevel@tonic-gate irqp = apic_find_irq(dip, ispec, hdlp->ih_type); 9460Sstevel@tonic-gate if (irqp == NULL) 9470Sstevel@tonic-gate return (PSM_FAILURE); 9480Sstevel@tonic-gate apic_clear_mask(irqp); 9490Sstevel@tonic-gate break; 9500Sstevel@tonic-gate case PSM_INTR_OP_SET_MASK: 9510Sstevel@tonic-gate if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 9520Sstevel@tonic-gate return (PSM_FAILURE); 9530Sstevel@tonic-gate if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 9540Sstevel@tonic-gate return (PSM_FAILURE); 9550Sstevel@tonic-gate apic_set_mask(irqp); 9560Sstevel@tonic-gate break; 9570Sstevel@tonic-gate case PSM_INTR_OP_GET_CAP: 9580Sstevel@tonic-gate cap = DDI_INTR_FLAG_PENDING; 9590Sstevel@tonic-gate if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 9600Sstevel@tonic-gate cap |= DDI_INTR_FLAG_MASKABLE; 9610Sstevel@tonic-gate *result = cap; 9620Sstevel@tonic-gate break; 9630Sstevel@tonic-gate case PSM_INTR_OP_GET_SHARED: 9640Sstevel@tonic-gate if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 9650Sstevel@tonic-gate return (PSM_FAILURE); 9660Sstevel@tonic-gate if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 9670Sstevel@tonic-gate return (PSM_FAILURE); 9680Sstevel@tonic-gate *result = irqp->airq_share ? 1: 0; 9690Sstevel@tonic-gate break; 9700Sstevel@tonic-gate case PSM_INTR_OP_SET_PRI: 9710Sstevel@tonic-gate old_priority = hdlp->ih_pri; /* save old value */ 9720Sstevel@tonic-gate new_priority = *(int *)result; /* try the new value */ 9730Sstevel@tonic-gate 9740Sstevel@tonic-gate /* First, check if "hdlp->ih_scratch1" vectors exist? */ 9750Sstevel@tonic-gate if (apic_navail_vector(dip, new_priority) < hdlp->ih_scratch1) 9760Sstevel@tonic-gate return (PSM_FAILURE); 9770Sstevel@tonic-gate 9780Sstevel@tonic-gate /* Now allocate the vectors */ 979*4937Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_MSI) 980*4937Sjohnny count_vec = apic_alloc_msi_vectors(dip, hdlp->ih_inum, 981*4937Sjohnny hdlp->ih_scratch1, new_priority, 982*4937Sjohnny DDI_INTR_ALLOC_STRICT); 983*4937Sjohnny else 984*4937Sjohnny count_vec = apic_alloc_msix_vectors(dip, hdlp->ih_inum, 985*4937Sjohnny hdlp->ih_scratch1, new_priority, 986*4937Sjohnny DDI_INTR_ALLOC_STRICT); 9870Sstevel@tonic-gate 9883446Smrj /* Did we get new vectors? */ 9892335Sjohnny if (!count_vec) 9900Sstevel@tonic-gate return (PSM_FAILURE); 9910Sstevel@tonic-gate 9920Sstevel@tonic-gate /* Finally, free the previously allocated vectors */ 9930Sstevel@tonic-gate apic_free_vectors(dip, hdlp->ih_inum, count_vec, 9940Sstevel@tonic-gate old_priority, hdlp->ih_type); 9950Sstevel@tonic-gate hdlp->ih_pri = new_priority; /* set the new value */ 9960Sstevel@tonic-gate break; 997916Sschwartz case PSM_INTR_OP_SET_CPU: 9984397Sschwartz case PSM_INTR_OP_GRP_SET_CPU: 999916Sschwartz /* 1000916Sschwartz * The interrupt handle given here has been allocated 1001916Sschwartz * specifically for this command, and ih_private carries 1002916Sschwartz * a CPU value. 1003916Sschwartz */ 10044397Sschwartz new_cpu = (int)(intptr_t)hdlp->ih_private; 10054397Sschwartz if (!apic_cpu_in_range(new_cpu)) { 10064397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 10074397Sschwartz "[grp_]set_cpu: cpu out of range: %d\n", new_cpu)); 1008916Sschwartz *result = EINVAL; 1009916Sschwartz return (PSM_FAILURE); 1010916Sschwartz } 10114397Sschwartz if (intr_op == PSM_INTR_OP_SET_CPU) { 10124397Sschwartz if (apic_set_cpu(hdlp->ih_vector, new_cpu, result) != 10134397Sschwartz PSM_SUCCESS) 10144397Sschwartz return (PSM_FAILURE); 10154397Sschwartz } else { 10164397Sschwartz if (apic_grp_set_cpu(hdlp->ih_vector, new_cpu, 10174397Sschwartz result) != PSM_SUCCESS) 10184397Sschwartz return (PSM_FAILURE); 1019916Sschwartz } 1020916Sschwartz break; 1021916Sschwartz case PSM_INTR_OP_GET_INTR: 1022916Sschwartz /* 1023916Sschwartz * The interrupt handle given here has been allocated 1024916Sschwartz * specifically for this command, and ih_private carries 1025916Sschwartz * a pointer to a apic_get_intr_t. 1026916Sschwartz */ 1027916Sschwartz if (apic_get_vector_intr_info( 1028916Sschwartz hdlp->ih_vector, hdlp->ih_private) != PSM_SUCCESS) 1029916Sschwartz return (PSM_FAILURE); 1030916Sschwartz break; 10314397Sschwartz case PSM_INTR_OP_APIC_TYPE: 10324397Sschwartz hdlp->ih_private = apic_get_apic_type(); 10334397Sschwartz hdlp->ih_ver = apic_get_apic_version(); 10344397Sschwartz break; 10350Sstevel@tonic-gate case PSM_INTR_OP_SET_CAP: 10360Sstevel@tonic-gate default: 10370Sstevel@tonic-gate return (PSM_FAILURE); 10380Sstevel@tonic-gate } 10390Sstevel@tonic-gate return (PSM_SUCCESS); 10400Sstevel@tonic-gate } 1041