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 /* 2211465SKerry.Shu@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate /* 270Sstevel@tonic-gate * apic_introp.c: 280Sstevel@tonic-gate * Has code for Advanced DDI interrupt framework support. 290Sstevel@tonic-gate */ 300Sstevel@tonic-gate 310Sstevel@tonic-gate #include <sys/cpuvar.h> 320Sstevel@tonic-gate #include <sys/psm.h> 333446Smrj #include <sys/archsystm.h> 343446Smrj #include <sys/apic.h> 350Sstevel@tonic-gate #include <sys/sunddi.h> 360Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 37916Sschwartz #include <sys/mach_intr.h> 38916Sschwartz #include <sys/sysmacros.h> 390Sstevel@tonic-gate #include <sys/trap.h> 400Sstevel@tonic-gate #include <sys/pci.h> 410Sstevel@tonic-gate #include <sys/pci_intr_lib.h> 420Sstevel@tonic-gate 43916Sschwartz extern struct av_head autovect[]; 44916Sschwartz 450Sstevel@tonic-gate /* 460Sstevel@tonic-gate * Local Function Prototypes 470Sstevel@tonic-gate */ 480Sstevel@tonic-gate apic_irq_t *apic_find_irq(dev_info_t *, struct intrspec *, int); 490Sstevel@tonic-gate 500Sstevel@tonic-gate /* 510Sstevel@tonic-gate * MSI support flag: 520Sstevel@tonic-gate * reflects whether MSI is supported at APIC level 530Sstevel@tonic-gate * it can also be patched through /etc/system 540Sstevel@tonic-gate * 550Sstevel@tonic-gate * 0 = default value - don't know and need to call apic_check_msi_support() 560Sstevel@tonic-gate * to find out then set it accordingly 570Sstevel@tonic-gate * 1 = supported 580Sstevel@tonic-gate * -1 = not supported 590Sstevel@tonic-gate */ 600Sstevel@tonic-gate int apic_support_msi = 0; 610Sstevel@tonic-gate 620Sstevel@tonic-gate /* Multiple vector support for MSI */ 630Sstevel@tonic-gate int apic_multi_msi_enable = 1; 640Sstevel@tonic-gate 658925SEvan.Yan@Sun.COM /* Multiple vector support for MSI-X */ 664937Sjohnny int apic_msix_enable = 1; 674937Sjohnny 680Sstevel@tonic-gate /* 690Sstevel@tonic-gate * apic_pci_msi_enable_vector: 700Sstevel@tonic-gate * Set the address/data fields in the MSI/X capability structure 710Sstevel@tonic-gate * XXX: MSI-X support 720Sstevel@tonic-gate */ 730Sstevel@tonic-gate /* ARGSUSED */ 744937Sjohnny void 758675SVikram.Hegde@Sun.COM apic_pci_msi_enable_vector(apic_irq_t *irq_ptr, int type, int inum, int vector, 760Sstevel@tonic-gate int count, int target_apic_id) 770Sstevel@tonic-gate { 781997Sanish uint64_t msi_addr, msi_data; 791997Sanish ushort_t msi_ctrl; 808675SVikram.Hegde@Sun.COM dev_info_t *dip = irq_ptr->airq_dip; 811997Sanish int cap_ptr = i_ddi_get_msi_msix_cap_ptr(dip); 821997Sanish ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(dip); 838678SVikram.Hegde@Sun.COM #if !defined(__xpv) 848675SVikram.Hegde@Sun.COM msi_regs_t msi_regs; 858678SVikram.Hegde@Sun.COM #endif /* ! __xpv */ 860Sstevel@tonic-gate 870Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: dip=0x%p\n" 880Sstevel@tonic-gate "\tdriver = %s, inum=0x%x vector=0x%x apicid=0x%x\n", (void *)dip, 890Sstevel@tonic-gate ddi_driver_name(dip), inum, vector, target_apic_id)); 900Sstevel@tonic-gate 914937Sjohnny ASSERT((handle != NULL) && (cap_ptr != 0)); 921997Sanish 938678SVikram.Hegde@Sun.COM #if !defined(__xpv) 948675SVikram.Hegde@Sun.COM msi_regs.mr_data = vector; 958675SVikram.Hegde@Sun.COM msi_regs.mr_addr = target_apic_id; 968675SVikram.Hegde@Sun.COM 9711600SVikram.Hegde@Sun.COM apic_vt_ops->apic_intrmap_alloc_entry(irq_ptr); 9811600SVikram.Hegde@Sun.COM apic_vt_ops->apic_intrmap_map_entry(irq_ptr, (void *)&msi_regs); 9911600SVikram.Hegde@Sun.COM apic_vt_ops->apic_intrmap_record_msi(irq_ptr, &msi_regs); 1008678SVikram.Hegde@Sun.COM 1010Sstevel@tonic-gate /* MSI Address */ 1028675SVikram.Hegde@Sun.COM msi_addr = msi_regs.mr_addr; 1030Sstevel@tonic-gate 1040Sstevel@tonic-gate /* MSI Data: MSI is edge triggered according to spec */ 1058675SVikram.Hegde@Sun.COM msi_data = msi_regs.mr_data; 1068678SVikram.Hegde@Sun.COM #else 1078678SVikram.Hegde@Sun.COM /* MSI Address */ 1088678SVikram.Hegde@Sun.COM msi_addr = (MSI_ADDR_HDR | 1098678SVikram.Hegde@Sun.COM (target_apic_id << MSI_ADDR_DEST_SHIFT)); 1108678SVikram.Hegde@Sun.COM msi_addr |= ((MSI_ADDR_RH_FIXED << MSI_ADDR_RH_SHIFT) | 1118678SVikram.Hegde@Sun.COM (MSI_ADDR_DM_PHYSICAL << MSI_ADDR_DM_SHIFT)); 1128678SVikram.Hegde@Sun.COM 1138678SVikram.Hegde@Sun.COM /* MSI Data: MSI is edge triggered according to spec */ 1148678SVikram.Hegde@Sun.COM msi_data = ((MSI_DATA_TM_EDGE << MSI_DATA_TM_SHIFT) | vector); 1158678SVikram.Hegde@Sun.COM #endif /* ! __xpv */ 1160Sstevel@tonic-gate 1170Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: addr=0x%lx " 1180Sstevel@tonic-gate "data=0x%lx\n", (long)msi_addr, (long)msi_data)); 1190Sstevel@tonic-gate 1201997Sanish if (type == DDI_INTR_TYPE_MSI) { 1211997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 1221997Sanish 1231997Sanish /* Set the bits to inform how many MSIs are enabled */ 1241997Sanish msi_ctrl |= ((highbit(count) -1) << PCI_MSI_MME_SHIFT); 1251997Sanish pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 1261997Sanish 12710175SStuart.Maybee@Sun.COM #if !defined(__xpv) 12810175SStuart.Maybee@Sun.COM /* 12910175SStuart.Maybee@Sun.COM * Only set vector if not on hypervisor 13010175SStuart.Maybee@Sun.COM */ 1311997Sanish pci_config_put32(handle, 1321997Sanish cap_ptr + PCI_MSI_ADDR_OFFSET, msi_addr); 1331997Sanish 1341997Sanish if (msi_ctrl & PCI_MSI_64BIT_MASK) { 1351997Sanish pci_config_put32(handle, 1361997Sanish cap_ptr + PCI_MSI_ADDR_OFFSET + 4, msi_addr >> 32); 1371997Sanish pci_config_put16(handle, 1381997Sanish cap_ptr + PCI_MSI_64BIT_DATA, msi_data); 1391997Sanish } else { 1401997Sanish pci_config_put16(handle, 1411997Sanish cap_ptr + PCI_MSI_32BIT_DATA, msi_data); 1421997Sanish } 1431997Sanish 1441997Sanish } else if (type == DDI_INTR_TYPE_MSIX) { 1451997Sanish uintptr_t off; 1461997Sanish ddi_intr_msix_t *msix_p = i_ddi_get_msix(dip); 1471997Sanish 14810647SLipeng.Sang@Sun.COM ASSERT(msix_p != NULL); 14910647SLipeng.Sang@Sun.COM 1501997Sanish /* Offset into the "inum"th entry in the MSI-X table */ 1511997Sanish off = (uintptr_t)msix_p->msix_tbl_addr + 1521997Sanish (inum * PCI_MSIX_VECTOR_SIZE); 1531997Sanish 1541997Sanish ddi_put32(msix_p->msix_tbl_hdl, 1551997Sanish (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), msi_data); 1561997Sanish ddi_put64(msix_p->msix_tbl_hdl, 1571997Sanish (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), msi_addr); 15810175SStuart.Maybee@Sun.COM #endif /* ! __xpv */ 1590Sstevel@tonic-gate } 1600Sstevel@tonic-gate } 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate 16310175SStuart.Maybee@Sun.COM #if !defined(__xpv) 16410175SStuart.Maybee@Sun.COM 1650Sstevel@tonic-gate /* 1660Sstevel@tonic-gate * This function returns the no. of vectors available for the pri. 1670Sstevel@tonic-gate * dip is not used at this moment. If we really don't need that, 1680Sstevel@tonic-gate * it will be removed. 1690Sstevel@tonic-gate */ 1700Sstevel@tonic-gate /*ARGSUSED*/ 1710Sstevel@tonic-gate int 1720Sstevel@tonic-gate apic_navail_vector(dev_info_t *dip, int pri) 1730Sstevel@tonic-gate { 1740Sstevel@tonic-gate int lowest, highest, i, navail, count; 1750Sstevel@tonic-gate 1760Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_navail_vector: dip: %p, pri: %x\n", 1770Sstevel@tonic-gate (void *)dip, pri)); 1780Sstevel@tonic-gate 1790Sstevel@tonic-gate highest = apic_ipltopri[pri] + APIC_VECTOR_MASK; 1800Sstevel@tonic-gate lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL; 1810Sstevel@tonic-gate navail = count = 0; 1820Sstevel@tonic-gate 1837282Smishra if (highest < lowest) /* Both ipl and ipl - 1 map to same pri */ 1847282Smishra lowest -= APIC_VECTOR_PER_IPL; 1857282Smishra 1860Sstevel@tonic-gate /* It has to be contiguous */ 187*11641SKerry.Shu@Sun.COM for (i = lowest; i <= highest; i++) { 1880Sstevel@tonic-gate count = 0; 1890Sstevel@tonic-gate while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) && 190*11641SKerry.Shu@Sun.COM (i <= highest)) { 1912335Sjohnny if (APIC_CHECK_RESERVE_VECTORS(i)) 1920Sstevel@tonic-gate break; 1930Sstevel@tonic-gate count++; 1940Sstevel@tonic-gate i++; 1950Sstevel@tonic-gate } 1960Sstevel@tonic-gate if (count > navail) 1970Sstevel@tonic-gate navail = count; 1980Sstevel@tonic-gate } 1990Sstevel@tonic-gate return (navail); 2000Sstevel@tonic-gate } 2010Sstevel@tonic-gate 20210175SStuart.Maybee@Sun.COM #endif /* ! __xpv */ 20310175SStuart.Maybee@Sun.COM 2042335Sjohnny /* 2052335Sjohnny * Finds "count" contiguous MSI vectors starting at the proper alignment 2062335Sjohnny * at "pri". 2072335Sjohnny * Caller needs to make sure that count has to be power of 2 and should not 2082335Sjohnny * be < 1. 2092335Sjohnny */ 2103446Smrj uchar_t 2110Sstevel@tonic-gate apic_find_multi_vectors(int pri, int count) 2120Sstevel@tonic-gate { 2132335Sjohnny int lowest, highest, i, navail, start, msibits; 2140Sstevel@tonic-gate 2150Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_find_mult: pri: %x, count: %x\n", 2160Sstevel@tonic-gate pri, count)); 2170Sstevel@tonic-gate 2180Sstevel@tonic-gate highest = apic_ipltopri[pri] + APIC_VECTOR_MASK; 2190Sstevel@tonic-gate lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL; 2200Sstevel@tonic-gate navail = 0; 2210Sstevel@tonic-gate 2227282Smishra if (highest < lowest) /* Both ipl and ipl - 1 map to same pri */ 2237282Smishra lowest -= APIC_VECTOR_PER_IPL; 2247282Smishra 2252335Sjohnny /* 2262335Sjohnny * msibits is the no. of lower order message data bits for the 2272335Sjohnny * allocated MSI vectors and is used to calculate the aligned 2282335Sjohnny * starting vector 2292335Sjohnny */ 2302335Sjohnny msibits = count - 1; 2312335Sjohnny 2320Sstevel@tonic-gate /* It has to be contiguous */ 233*11641SKerry.Shu@Sun.COM for (i = lowest; i <= highest; i++) { 2340Sstevel@tonic-gate navail = 0; 2352335Sjohnny 2362335Sjohnny /* 2372335Sjohnny * starting vector has to be aligned accordingly for 2382335Sjohnny * multiple MSIs 2392335Sjohnny */ 2402335Sjohnny if (msibits) 2412335Sjohnny i = (i + msibits) & ~msibits; 2420Sstevel@tonic-gate start = i; 2430Sstevel@tonic-gate while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) && 244*11641SKerry.Shu@Sun.COM (i <= highest)) { 2452335Sjohnny if (APIC_CHECK_RESERVE_VECTORS(i)) 2460Sstevel@tonic-gate break; 2470Sstevel@tonic-gate navail++; 2480Sstevel@tonic-gate if (navail >= count) 2490Sstevel@tonic-gate return (start); 2500Sstevel@tonic-gate i++; 2510Sstevel@tonic-gate } 2520Sstevel@tonic-gate } 2530Sstevel@tonic-gate return (0); 2540Sstevel@tonic-gate } 2550Sstevel@tonic-gate 2563446Smrj 2570Sstevel@tonic-gate /* 2580Sstevel@tonic-gate * It finds the apic_irq_t associates with the dip, ispec and type. 2590Sstevel@tonic-gate */ 2600Sstevel@tonic-gate apic_irq_t * 2610Sstevel@tonic-gate apic_find_irq(dev_info_t *dip, struct intrspec *ispec, int type) 2620Sstevel@tonic-gate { 2630Sstevel@tonic-gate apic_irq_t *irqp; 2640Sstevel@tonic-gate int i; 2650Sstevel@tonic-gate 2660Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: dip=0x%p vec=0x%x " 2670Sstevel@tonic-gate "ipl=0x%x type=0x%x\n", (void *)dip, ispec->intrspec_vec, 2680Sstevel@tonic-gate ispec->intrspec_pri, type)); 2690Sstevel@tonic-gate 2700Sstevel@tonic-gate for (i = apic_min_device_irq; i <= apic_max_device_irq; i++) { 27111465SKerry.Shu@Sun.COM for (irqp = apic_irq_table[i]; irqp; irqp = irqp->airq_next) { 27211465SKerry.Shu@Sun.COM if ((irqp->airq_dip == dip) && 27311465SKerry.Shu@Sun.COM (irqp->airq_origirq == ispec->intrspec_vec) && 27411465SKerry.Shu@Sun.COM (irqp->airq_ipl == ispec->intrspec_pri)) { 27511465SKerry.Shu@Sun.COM if (type == DDI_INTR_TYPE_MSI) { 27611465SKerry.Shu@Sun.COM if (irqp->airq_mps_intr_index == 27711465SKerry.Shu@Sun.COM MSI_INDEX) 27811465SKerry.Shu@Sun.COM return (irqp); 27911465SKerry.Shu@Sun.COM } else if (type == DDI_INTR_TYPE_MSIX) { 28011465SKerry.Shu@Sun.COM if (irqp->airq_mps_intr_index == 28111465SKerry.Shu@Sun.COM MSIX_INDEX) 28211465SKerry.Shu@Sun.COM return (irqp); 28311465SKerry.Shu@Sun.COM } else 2844937Sjohnny return (irqp); 28511465SKerry.Shu@Sun.COM } 2860Sstevel@tonic-gate } 2870Sstevel@tonic-gate } 2880Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: return NULL\n")); 2890Sstevel@tonic-gate return (NULL); 2900Sstevel@tonic-gate } 2910Sstevel@tonic-gate 2920Sstevel@tonic-gate 2935084Sjohnlev #if !defined(__xpv) 2945084Sjohnlev 2950Sstevel@tonic-gate /* 2960Sstevel@tonic-gate * This function will return the pending bit of the irqp. 2970Sstevel@tonic-gate * It either comes from the IRR register of the APIC or the RDT 2980Sstevel@tonic-gate * entry of the I/O APIC. 2990Sstevel@tonic-gate * For the IRR to work, it needs to be to its binding CPU 3000Sstevel@tonic-gate */ 3010Sstevel@tonic-gate static int 3020Sstevel@tonic-gate apic_get_pending(apic_irq_t *irqp, int type) 3030Sstevel@tonic-gate { 3040Sstevel@tonic-gate int bit, index, irr, pending; 3050Sstevel@tonic-gate int intin_no; 3063446Smrj int apic_ix; 3070Sstevel@tonic-gate 3080Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_get_pending: irqp: %p, cpuid: %x " 3090Sstevel@tonic-gate "type: %x\n", (void *)irqp, irqp->airq_cpu & ~IRQ_USER_BOUND, 3100Sstevel@tonic-gate type)); 3110Sstevel@tonic-gate 3120Sstevel@tonic-gate /* need to get on the bound cpu */ 3130Sstevel@tonic-gate mutex_enter(&cpu_lock); 3140Sstevel@tonic-gate affinity_set(irqp->airq_cpu & ~IRQ_USER_BOUND); 3150Sstevel@tonic-gate 3160Sstevel@tonic-gate index = irqp->airq_vector / 32; 3170Sstevel@tonic-gate bit = irqp->airq_vector % 32; 3187282Smishra irr = apic_reg_ops->apic_read(APIC_IRR_REG + index); 3190Sstevel@tonic-gate 3200Sstevel@tonic-gate affinity_clear(); 3210Sstevel@tonic-gate mutex_exit(&cpu_lock); 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate pending = (irr & (1 << bit)) ? 1 : 0; 3240Sstevel@tonic-gate if (!pending && (type == DDI_INTR_TYPE_FIXED)) { 3250Sstevel@tonic-gate /* check I/O APIC for fixed interrupt */ 3260Sstevel@tonic-gate intin_no = irqp->airq_intin_no; 3273446Smrj apic_ix = irqp->airq_ioapicindex; 3283446Smrj pending = (READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no) & 3290Sstevel@tonic-gate AV_PENDING) ? 1 : 0; 3300Sstevel@tonic-gate } 3310Sstevel@tonic-gate return (pending); 3320Sstevel@tonic-gate } 3330Sstevel@tonic-gate 3340Sstevel@tonic-gate 3350Sstevel@tonic-gate /* 3360Sstevel@tonic-gate * This function will clear the mask for the interrupt on the I/O APIC 3370Sstevel@tonic-gate */ 3380Sstevel@tonic-gate static void 3390Sstevel@tonic-gate apic_clear_mask(apic_irq_t *irqp) 3400Sstevel@tonic-gate { 3410Sstevel@tonic-gate int intin_no; 3423446Smrj ulong_t iflag; 3430Sstevel@tonic-gate int32_t rdt_entry; 3443446Smrj int apic_ix; 3450Sstevel@tonic-gate 3460Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_clear_mask: irqp: %p\n", 3470Sstevel@tonic-gate (void *)irqp)); 3480Sstevel@tonic-gate 3490Sstevel@tonic-gate intin_no = irqp->airq_intin_no; 3503446Smrj apic_ix = irqp->airq_ioapicindex; 3510Sstevel@tonic-gate 3520Sstevel@tonic-gate iflag = intr_clear(); 3530Sstevel@tonic-gate lock_set(&apic_ioapic_lock); 3540Sstevel@tonic-gate 3553446Smrj rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no); 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate /* clear mask */ 3583446Smrj WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no, 3590Sstevel@tonic-gate ((~AV_MASK) & rdt_entry)); 3600Sstevel@tonic-gate 3610Sstevel@tonic-gate lock_clear(&apic_ioapic_lock); 3620Sstevel@tonic-gate intr_restore(iflag); 3630Sstevel@tonic-gate } 3640Sstevel@tonic-gate 3650Sstevel@tonic-gate 3660Sstevel@tonic-gate /* 3670Sstevel@tonic-gate * This function will mask the interrupt on the I/O APIC 3680Sstevel@tonic-gate */ 3690Sstevel@tonic-gate static void 3700Sstevel@tonic-gate apic_set_mask(apic_irq_t *irqp) 3710Sstevel@tonic-gate { 3720Sstevel@tonic-gate int intin_no; 3733446Smrj int apic_ix; 3743446Smrj ulong_t iflag; 3750Sstevel@tonic-gate int32_t rdt_entry; 3760Sstevel@tonic-gate 3770Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_set_mask: irqp: %p\n", (void *)irqp)); 3780Sstevel@tonic-gate 3790Sstevel@tonic-gate intin_no = irqp->airq_intin_no; 3803446Smrj apic_ix = irqp->airq_ioapicindex; 3810Sstevel@tonic-gate 3820Sstevel@tonic-gate iflag = intr_clear(); 3830Sstevel@tonic-gate 3840Sstevel@tonic-gate lock_set(&apic_ioapic_lock); 3850Sstevel@tonic-gate 3863446Smrj rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no); 3870Sstevel@tonic-gate 3880Sstevel@tonic-gate /* mask it */ 3893446Smrj WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no, 3900Sstevel@tonic-gate (AV_MASK | rdt_entry)); 3910Sstevel@tonic-gate 3920Sstevel@tonic-gate lock_clear(&apic_ioapic_lock); 3930Sstevel@tonic-gate intr_restore(iflag); 3940Sstevel@tonic-gate } 3950Sstevel@tonic-gate 3960Sstevel@tonic-gate 3970Sstevel@tonic-gate void 3980Sstevel@tonic-gate apic_free_vectors(dev_info_t *dip, int inum, int count, int pri, int type) 3990Sstevel@tonic-gate { 4000Sstevel@tonic-gate int i; 4010Sstevel@tonic-gate apic_irq_t *irqptr; 4020Sstevel@tonic-gate struct intrspec ispec; 4030Sstevel@tonic-gate 4040Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: dip: %p inum: %x " 4050Sstevel@tonic-gate "count: %x pri: %x type: %x\n", 4060Sstevel@tonic-gate (void *)dip, inum, count, pri, type)); 4070Sstevel@tonic-gate 4080Sstevel@tonic-gate /* for MSI/X only */ 4090Sstevel@tonic-gate if (!DDI_INTR_IS_MSI_OR_MSIX(type)) 4100Sstevel@tonic-gate return; 4110Sstevel@tonic-gate 4120Sstevel@tonic-gate for (i = 0; i < count; i++) { 4130Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: inum=0x%x " 4140Sstevel@tonic-gate "pri=0x%x count=0x%x\n", inum, pri, count)); 4150Sstevel@tonic-gate ispec.intrspec_vec = inum + i; 4160Sstevel@tonic-gate ispec.intrspec_pri = pri; 4170Sstevel@tonic-gate if ((irqptr = apic_find_irq(dip, &ispec, type)) == NULL) { 4180Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: " 4190Sstevel@tonic-gate "dip=0x%p inum=0x%x pri=0x%x apic_find_irq() " 4200Sstevel@tonic-gate "failed\n", (void *)dip, inum, pri)); 4210Sstevel@tonic-gate continue; 4220Sstevel@tonic-gate } 4230Sstevel@tonic-gate irqptr->airq_mps_intr_index = FREE_INDEX; 4240Sstevel@tonic-gate apic_vector_to_irq[irqptr->airq_vector] = APIC_RESV_IRQ; 4250Sstevel@tonic-gate } 4260Sstevel@tonic-gate } 4270Sstevel@tonic-gate 42810175SStuart.Maybee@Sun.COM #endif /* ! __xpv */ 4290Sstevel@tonic-gate 4300Sstevel@tonic-gate /* 4310Sstevel@tonic-gate * check whether the system supports MSI 4320Sstevel@tonic-gate * 4330Sstevel@tonic-gate * If PCI-E capability is found, then this must be a PCI-E system. 4340Sstevel@tonic-gate * Since MSI is required for PCI-E system, it returns PSM_SUCCESS 4350Sstevel@tonic-gate * to indicate this system supports MSI. 4360Sstevel@tonic-gate */ 4370Sstevel@tonic-gate int 4381701Sjohnny apic_check_msi_support() 4390Sstevel@tonic-gate { 4401701Sjohnny dev_info_t *cdip; 441881Sjohnny char dev_type[16]; 442881Sjohnny int dev_len; 4430Sstevel@tonic-gate 4441701Sjohnny DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support:\n")); 4450Sstevel@tonic-gate 4461701Sjohnny /* 4471701Sjohnny * check whether the first level children of root_node have 4481701Sjohnny * PCI-E capability 4491701Sjohnny */ 4501701Sjohnny for (cdip = ddi_get_child(ddi_root_node()); cdip != NULL; 4511701Sjohnny cdip = ddi_get_next_sibling(cdip)) { 4521701Sjohnny 4531701Sjohnny DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: cdip: 0x%p," 4541701Sjohnny " driver: %s, binding: %s, nodename: %s\n", (void *)cdip, 4551701Sjohnny ddi_driver_name(cdip), ddi_binding_name(cdip), 4561701Sjohnny ddi_node_name(cdip))); 457881Sjohnny dev_len = sizeof (dev_type); 4581701Sjohnny if (ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS, 459881Sjohnny "device_type", (caddr_t)dev_type, &dev_len) 460881Sjohnny != DDI_PROP_SUCCESS) 461881Sjohnny continue; 462881Sjohnny if (strcmp(dev_type, "pciex") == 0) 463881Sjohnny return (PSM_SUCCESS); 4640Sstevel@tonic-gate } 4650Sstevel@tonic-gate 4660Sstevel@tonic-gate /* MSI is not supported on this system */ 467881Sjohnny DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: no 'pciex' " 468881Sjohnny "device_type found\n")); 4690Sstevel@tonic-gate return (PSM_FAILURE); 4700Sstevel@tonic-gate } 4710Sstevel@tonic-gate 47210175SStuart.Maybee@Sun.COM #if !defined(__xpv) 47310175SStuart.Maybee@Sun.COM 4740Sstevel@tonic-gate /* 4751997Sanish * apic_pci_msi_unconfigure: 4761997Sanish * 4771997Sanish * This and next two interfaces are copied from pci_intr_lib.c 4781997Sanish * Do ensure that these two files stay in sync. 4791997Sanish * These needed to be copied over here to avoid a deadlock situation on 4801997Sanish * certain mp systems that use MSI interrupts. 4811997Sanish * 4821997Sanish * IMPORTANT regards next three interfaces: 4831997Sanish * i) are called only for MSI/X interrupts. 4841997Sanish * ii) called with interrupts disabled, and must not block 4851997Sanish */ 4864937Sjohnny void 4871997Sanish apic_pci_msi_unconfigure(dev_info_t *rdip, int type, int inum) 4881997Sanish { 4891997Sanish ushort_t msi_ctrl; 4901997Sanish int cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip); 4911997Sanish ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(rdip); 4921997Sanish 4934937Sjohnny ASSERT((handle != NULL) && (cap_ptr != 0)); 4941997Sanish 4951997Sanish if (type == DDI_INTR_TYPE_MSI) { 4961997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 4971997Sanish msi_ctrl &= (~PCI_MSI_MME_MASK); 4981997Sanish pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 4991997Sanish pci_config_put32(handle, cap_ptr + PCI_MSI_ADDR_OFFSET, 0); 5001997Sanish 5011997Sanish if (msi_ctrl & PCI_MSI_64BIT_MASK) { 5021997Sanish pci_config_put16(handle, 5031997Sanish cap_ptr + PCI_MSI_64BIT_DATA, 0); 5041997Sanish pci_config_put32(handle, 5051997Sanish cap_ptr + PCI_MSI_ADDR_OFFSET + 4, 0); 5061997Sanish } else { 5071997Sanish pci_config_put16(handle, 5081997Sanish cap_ptr + PCI_MSI_32BIT_DATA, 0); 5091997Sanish } 5101997Sanish 5111997Sanish } else if (type == DDI_INTR_TYPE_MSIX) { 5121997Sanish uintptr_t off; 5134937Sjohnny uint32_t mask; 5141997Sanish ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 5151997Sanish 51610647SLipeng.Sang@Sun.COM ASSERT(msix_p != NULL); 51710647SLipeng.Sang@Sun.COM 5184937Sjohnny /* Offset into "inum"th entry in the MSI-X table & mask it */ 5194937Sjohnny off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 5204937Sjohnny PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 5214937Sjohnny 5224937Sjohnny mask = ddi_get32(msix_p->msix_tbl_hdl, (uint32_t *)off); 5234937Sjohnny 5244937Sjohnny ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, (mask | 1)); 5254937Sjohnny 5261997Sanish /* Offset into the "inum"th entry in the MSI-X table */ 5271997Sanish off = (uintptr_t)msix_p->msix_tbl_addr + 5281997Sanish (inum * PCI_MSIX_VECTOR_SIZE); 5291997Sanish 5301997Sanish /* Reset the "data" and "addr" bits */ 5311997Sanish ddi_put32(msix_p->msix_tbl_hdl, 5321997Sanish (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), 0); 5331997Sanish ddi_put64(msix_p->msix_tbl_hdl, (uint64_t *)off, 0); 5341997Sanish } 5351997Sanish } 5361997Sanish 53710323SStuart.Maybee@Sun.COM #endif /* __xpv */ 5381997Sanish 5391997Sanish /* 5401997Sanish * apic_pci_msi_enable_mode: 5411997Sanish */ 5424937Sjohnny void 5431997Sanish apic_pci_msi_enable_mode(dev_info_t *rdip, int type, int inum) 5441997Sanish { 5451997Sanish ushort_t msi_ctrl; 5461997Sanish int cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip); 5471997Sanish ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(rdip); 5481997Sanish 5494937Sjohnny ASSERT((handle != NULL) && (cap_ptr != 0)); 5501997Sanish 5511997Sanish if (type == DDI_INTR_TYPE_MSI) { 5521997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 5531997Sanish if ((msi_ctrl & PCI_MSI_ENABLE_BIT)) 5544937Sjohnny return; 5551997Sanish 5561997Sanish msi_ctrl |= PCI_MSI_ENABLE_BIT; 5571997Sanish pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 5581997Sanish 5591997Sanish } else if (type == DDI_INTR_TYPE_MSIX) { 5601997Sanish uintptr_t off; 5614937Sjohnny uint32_t mask; 5621997Sanish ddi_intr_msix_t *msix_p; 5631997Sanish 5641997Sanish msix_p = i_ddi_get_msix(rdip); 5651997Sanish 56610647SLipeng.Sang@Sun.COM ASSERT(msix_p != NULL); 56710647SLipeng.Sang@Sun.COM 5681997Sanish /* Offset into "inum"th entry in the MSI-X table & clear mask */ 5691997Sanish off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 5701997Sanish PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 5714937Sjohnny 5724937Sjohnny mask = ddi_get32(msix_p->msix_tbl_hdl, (uint32_t *)off); 5734937Sjohnny 5744937Sjohnny ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, (mask & ~1)); 5754937Sjohnny 5764937Sjohnny msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSIX_CTRL); 5774937Sjohnny 5784937Sjohnny if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT)) { 5794937Sjohnny msi_ctrl |= PCI_MSIX_ENABLE_BIT; 5804937Sjohnny pci_config_put16(handle, cap_ptr + PCI_MSIX_CTRL, 5814937Sjohnny msi_ctrl); 5824937Sjohnny } 5831997Sanish } 5841997Sanish } 5851997Sanish 5861997Sanish /* 5871997Sanish * apic_pci_msi_disable_mode: 5881997Sanish */ 5894937Sjohnny void 5904937Sjohnny apic_pci_msi_disable_mode(dev_info_t *rdip, int type) 5911997Sanish { 5921997Sanish ushort_t msi_ctrl; 5931997Sanish int cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip); 5941997Sanish ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(rdip); 5951997Sanish 5964937Sjohnny ASSERT((handle != NULL) && (cap_ptr != 0)); 5971997Sanish 5981997Sanish if (type == DDI_INTR_TYPE_MSI) { 5991997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 6001997Sanish if (!(msi_ctrl & PCI_MSI_ENABLE_BIT)) 6014937Sjohnny return; 6021997Sanish 6031997Sanish msi_ctrl &= ~PCI_MSI_ENABLE_BIT; /* MSI disable */ 6041997Sanish pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 6051997Sanish 6061997Sanish } else if (type == DDI_INTR_TYPE_MSIX) { 6071997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSIX_CTRL); 6084937Sjohnny if (msi_ctrl & PCI_MSIX_ENABLE_BIT) { 6094937Sjohnny msi_ctrl &= ~PCI_MSIX_ENABLE_BIT; 6104937Sjohnny pci_config_put16(handle, cap_ptr + PCI_MSIX_CTRL, 6114937Sjohnny msi_ctrl); 6124937Sjohnny } 6131997Sanish } 6141997Sanish } 6151997Sanish 61610323SStuart.Maybee@Sun.COM #if !defined(__xpv) 6175084Sjohnlev 6184397Sschwartz static int 61910053SEvan.Yan@Sun.COM apic_set_cpu(int irqno, int cpu, int *result) 6204397Sschwartz { 6214397Sschwartz apic_irq_t *irqp; 6226336Sbholler ulong_t iflag; 6234397Sschwartz int ret; 6244397Sschwartz 6254397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, "APIC_SET_CPU\n")); 6264397Sschwartz 6274397Sschwartz mutex_enter(&airq_mutex); 62810053SEvan.Yan@Sun.COM irqp = apic_irq_table[irqno]; 6294397Sschwartz mutex_exit(&airq_mutex); 6304397Sschwartz 6314397Sschwartz if (irqp == NULL) { 6324397Sschwartz *result = ENXIO; 6334397Sschwartz return (PSM_FAILURE); 6344397Sschwartz } 6354397Sschwartz 6364397Sschwartz /* Fail if this is an MSI intr and is part of a group. */ 6374397Sschwartz if ((irqp->airq_mps_intr_index == MSI_INDEX) && 6384397Sschwartz (irqp->airq_intin_no > 1)) { 6394397Sschwartz *result = ENXIO; 6404397Sschwartz return (PSM_FAILURE); 6414397Sschwartz } 6424397Sschwartz 6434397Sschwartz iflag = intr_clear(); 6444397Sschwartz lock_set(&apic_ioapic_lock); 6454397Sschwartz 6464397Sschwartz ret = apic_rebind_all(irqp, cpu); 6474397Sschwartz 6484397Sschwartz lock_clear(&apic_ioapic_lock); 6494397Sschwartz intr_restore(iflag); 6504397Sschwartz 6514397Sschwartz if (ret) { 6524397Sschwartz *result = EIO; 6534397Sschwartz return (PSM_FAILURE); 6544397Sschwartz } 65510053SEvan.Yan@Sun.COM /* 65610053SEvan.Yan@Sun.COM * keep tracking the default interrupt cpu binding 65710053SEvan.Yan@Sun.COM */ 65810053SEvan.Yan@Sun.COM irqp->airq_cpu = cpu; 65910053SEvan.Yan@Sun.COM 6604397Sschwartz *result = 0; 6614397Sschwartz return (PSM_SUCCESS); 6624397Sschwartz } 6634397Sschwartz 6644397Sschwartz static int 66510053SEvan.Yan@Sun.COM apic_grp_set_cpu(int irqno, int new_cpu, int *result) 6664397Sschwartz { 6674397Sschwartz dev_info_t *orig_dip; 6687282Smishra uint32_t orig_cpu; 6696336Sbholler ulong_t iflag; 6704397Sschwartz apic_irq_t *irqps[PCI_MSI_MAX_INTRS]; 6714397Sschwartz int i; 6724397Sschwartz int cap_ptr; 6734397Sschwartz int msi_mask_off; 6744397Sschwartz ushort_t msi_ctrl; 6754397Sschwartz uint32_t msi_pvm; 6764397Sschwartz ddi_acc_handle_t handle; 6774397Sschwartz int num_vectors = 0; 67810053SEvan.Yan@Sun.COM uint32_t vector; 6794397Sschwartz 6804397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, "APIC_GRP_SET_CPU\n")); 6814397Sschwartz 6824397Sschwartz /* 6834397Sschwartz * Take mutex to insure that table doesn't change out from underneath 6844397Sschwartz * us while we're playing with it. 6854397Sschwartz */ 6864397Sschwartz mutex_enter(&airq_mutex); 68710053SEvan.Yan@Sun.COM irqps[0] = apic_irq_table[irqno]; 6884397Sschwartz orig_cpu = irqps[0]->airq_temp_cpu; 6894397Sschwartz orig_dip = irqps[0]->airq_dip; 6904397Sschwartz num_vectors = irqps[0]->airq_intin_no; 69110053SEvan.Yan@Sun.COM vector = irqps[0]->airq_vector; 6924397Sschwartz 6934397Sschwartz /* A "group" of 1 */ 6944397Sschwartz if (num_vectors == 1) { 6954397Sschwartz mutex_exit(&airq_mutex); 69610053SEvan.Yan@Sun.COM return (apic_set_cpu(irqno, new_cpu, result)); 6974397Sschwartz } 6984397Sschwartz 6994397Sschwartz *result = ENXIO; 7004397Sschwartz 7014397Sschwartz if (irqps[0]->airq_mps_intr_index != MSI_INDEX) { 7024397Sschwartz mutex_exit(&airq_mutex); 7034397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, "set_grp: intr not MSI\n")); 7044397Sschwartz goto set_grp_intr_done; 7054397Sschwartz } 7064397Sschwartz if ((num_vectors < 1) || ((num_vectors - 1) & vector)) { 7074397Sschwartz mutex_exit(&airq_mutex); 7084397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 7094397Sschwartz "set_grp: base vec not part of a grp or not aligned: " 7104397Sschwartz "vec:0x%x, num_vec:0x%x\n", vector, num_vectors)); 7114397Sschwartz goto set_grp_intr_done; 7124397Sschwartz } 7134397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, "set_grp: num intrs in grp: %d\n", 7144397Sschwartz num_vectors)); 7154397Sschwartz 7164397Sschwartz ASSERT((num_vectors + vector) < APIC_MAX_VECTOR); 7174397Sschwartz 7184397Sschwartz *result = EIO; 7194397Sschwartz 7204397Sschwartz /* 7214397Sschwartz * All IRQ entries in the table for the given device will be not 7224397Sschwartz * shared. Since they are not shared, the dip in the table will 7234397Sschwartz * be true to the device of interest. 7244397Sschwartz */ 7254397Sschwartz for (i = 1; i < num_vectors; i++) { 7264397Sschwartz irqps[i] = apic_irq_table[apic_vector_to_irq[vector + i]]; 7274397Sschwartz if (irqps[i] == NULL) { 7284397Sschwartz mutex_exit(&airq_mutex); 7294397Sschwartz goto set_grp_intr_done; 7304397Sschwartz } 7314397Sschwartz #ifdef DEBUG 7324397Sschwartz /* Sanity check: CPU and dip is the same for all entries. */ 7334397Sschwartz if ((irqps[i]->airq_dip != orig_dip) || 7344397Sschwartz (irqps[i]->airq_temp_cpu != orig_cpu)) { 7354397Sschwartz mutex_exit(&airq_mutex); 7364397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 7374397Sschwartz "set_grp: cpu or dip for vec 0x%x difft than for " 7384397Sschwartz "vec 0x%x\n", vector, vector + i)); 7394397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 7404397Sschwartz " cpu: %d vs %d, dip: 0x%p vs 0x%p\n", orig_cpu, 7414397Sschwartz irqps[i]->airq_temp_cpu, (void *)orig_dip, 7424397Sschwartz (void *)irqps[i]->airq_dip)); 7434397Sschwartz goto set_grp_intr_done; 7444397Sschwartz } 7454397Sschwartz #endif /* DEBUG */ 7464397Sschwartz } 7474397Sschwartz mutex_exit(&airq_mutex); 7484397Sschwartz 7494397Sschwartz cap_ptr = i_ddi_get_msi_msix_cap_ptr(orig_dip); 7504397Sschwartz handle = i_ddi_get_pci_config_handle(orig_dip); 7514397Sschwartz msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 7524397Sschwartz 7534397Sschwartz /* MSI Per vector masking is supported. */ 7544397Sschwartz if (msi_ctrl & PCI_MSI_PVM_MASK) { 7554397Sschwartz if (msi_ctrl & PCI_MSI_64BIT_MASK) 7564397Sschwartz msi_mask_off = cap_ptr + PCI_MSI_64BIT_MASKBITS; 7574397Sschwartz else 7584397Sschwartz msi_mask_off = cap_ptr + PCI_MSI_32BIT_MASK; 7594397Sschwartz msi_pvm = pci_config_get32(handle, msi_mask_off); 7604397Sschwartz pci_config_put32(handle, msi_mask_off, (uint32_t)-1); 7614397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 7624397Sschwartz "set_grp: pvm supported. Mask set to 0x%x\n", 7634397Sschwartz pci_config_get32(handle, msi_mask_off))); 7644397Sschwartz } 7654397Sschwartz 7664397Sschwartz iflag = intr_clear(); 7674397Sschwartz lock_set(&apic_ioapic_lock); 7684397Sschwartz 7694397Sschwartz /* 7704397Sschwartz * Do the first rebind and check for errors. Apic_rebind_all returns 7714397Sschwartz * an error if the CPU is not accepting interrupts. If the first one 7724397Sschwartz * succeeds they all will. 7734397Sschwartz */ 7744397Sschwartz if (apic_rebind_all(irqps[0], new_cpu)) 7754397Sschwartz (void) apic_rebind_all(irqps[0], orig_cpu); 7764397Sschwartz else { 77710053SEvan.Yan@Sun.COM irqps[0]->airq_cpu = new_cpu; 77810053SEvan.Yan@Sun.COM 77910053SEvan.Yan@Sun.COM for (i = 1; i < num_vectors; i++) { 7804397Sschwartz (void) apic_rebind_all(irqps[i], new_cpu); 78110053SEvan.Yan@Sun.COM irqps[i]->airq_cpu = new_cpu; 78210053SEvan.Yan@Sun.COM } 7834397Sschwartz *result = 0; /* SUCCESS */ 7844397Sschwartz } 7854397Sschwartz 7864397Sschwartz lock_clear(&apic_ioapic_lock); 7874397Sschwartz intr_restore(iflag); 7884397Sschwartz 7894397Sschwartz /* Reenable vectors if per vector masking is supported. */ 7904397Sschwartz if (msi_ctrl & PCI_MSI_PVM_MASK) { 7914397Sschwartz pci_config_put32(handle, msi_mask_off, msi_pvm); 7924397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 7934397Sschwartz "set_grp: pvm supported. Mask restored to 0x%x\n", 7944397Sschwartz pci_config_get32(handle, msi_mask_off))); 7954397Sschwartz } 7964397Sschwartz 7974397Sschwartz set_grp_intr_done: 7984397Sschwartz if (*result != 0) 7994397Sschwartz return (PSM_FAILURE); 8004397Sschwartz 8014397Sschwartz return (PSM_SUCCESS); 8024397Sschwartz } 8034397Sschwartz 80410323SStuart.Maybee@Sun.COM #else /* __xpv */ 80510175SStuart.Maybee@Sun.COM 80610175SStuart.Maybee@Sun.COM /* 80710175SStuart.Maybee@Sun.COM * We let the hypervisor deal with msi configutation 80810323SStuart.Maybee@Sun.COM * so just stub this out. 80910175SStuart.Maybee@Sun.COM */ 81010175SStuart.Maybee@Sun.COM 81110175SStuart.Maybee@Sun.COM /* ARGSUSED */ 81210175SStuart.Maybee@Sun.COM void 81310175SStuart.Maybee@Sun.COM apic_pci_msi_unconfigure(dev_info_t *rdip, int type, int inum) 81410175SStuart.Maybee@Sun.COM { 81510175SStuart.Maybee@Sun.COM } 81610175SStuart.Maybee@Sun.COM 81710175SStuart.Maybee@Sun.COM #endif /* __xpv */ 8185084Sjohnlev 8195084Sjohnlev int 8204397Sschwartz apic_get_vector_intr_info(int vecirq, apic_get_intr_t *intr_params_p) 8214397Sschwartz { 8224397Sschwartz struct autovec *av_dev; 8234397Sschwartz uchar_t irqno; 8244397Sschwartz int i; 8254397Sschwartz apic_irq_t *irq_p; 8264397Sschwartz 8274397Sschwartz /* Sanity check the vector/irq argument. */ 8284397Sschwartz ASSERT((vecirq >= 0) || (vecirq <= APIC_MAX_VECTOR)); 8294397Sschwartz 8304397Sschwartz mutex_enter(&airq_mutex); 8314397Sschwartz 8324397Sschwartz /* 8334397Sschwartz * Convert the vecirq arg to an irq using vector_to_irq table 8344397Sschwartz * if the arg is a vector. Pass thru if already an irq. 8354397Sschwartz */ 8364397Sschwartz if ((intr_params_p->avgi_req_flags & PSMGI_INTRBY_FLAGS) == 8374397Sschwartz PSMGI_INTRBY_VEC) 8384397Sschwartz irqno = apic_vector_to_irq[vecirq]; 8394397Sschwartz else 8404397Sschwartz irqno = vecirq; 8414397Sschwartz 8424397Sschwartz irq_p = apic_irq_table[irqno]; 8434397Sschwartz 8444397Sschwartz if ((irq_p == NULL) || 8454397Sschwartz (irq_p->airq_temp_cpu == IRQ_UNBOUND) || 8464397Sschwartz (irq_p->airq_temp_cpu == IRQ_UNINIT)) { 8474397Sschwartz mutex_exit(&airq_mutex); 8484397Sschwartz return (PSM_FAILURE); 8494397Sschwartz } 8504397Sschwartz 8514397Sschwartz if (intr_params_p->avgi_req_flags & PSMGI_REQ_CPUID) { 8524397Sschwartz 8534397Sschwartz /* Get the (temp) cpu from apic_irq table, indexed by irq. */ 8544397Sschwartz intr_params_p->avgi_cpu_id = irq_p->airq_temp_cpu; 8554397Sschwartz 8564397Sschwartz /* Return user bound info for intrd. */ 8574397Sschwartz if (intr_params_p->avgi_cpu_id & IRQ_USER_BOUND) { 8584397Sschwartz intr_params_p->avgi_cpu_id &= ~IRQ_USER_BOUND; 8594397Sschwartz intr_params_p->avgi_cpu_id |= PSMGI_CPU_USER_BOUND; 8604397Sschwartz } 8614397Sschwartz } 8624397Sschwartz 8634397Sschwartz if (intr_params_p->avgi_req_flags & PSMGI_REQ_VECTOR) 8644397Sschwartz intr_params_p->avgi_vector = irq_p->airq_vector; 8654397Sschwartz 8664397Sschwartz if (intr_params_p->avgi_req_flags & 8674397Sschwartz (PSMGI_REQ_NUM_DEVS | PSMGI_REQ_GET_DEVS)) 8684397Sschwartz /* Get number of devices from apic_irq table shared field. */ 8694397Sschwartz intr_params_p->avgi_num_devs = irq_p->airq_share; 8704397Sschwartz 8714397Sschwartz if (intr_params_p->avgi_req_flags & PSMGI_REQ_GET_DEVS) { 8724397Sschwartz 8734397Sschwartz intr_params_p->avgi_req_flags |= PSMGI_REQ_NUM_DEVS; 8744397Sschwartz 8754397Sschwartz /* Some devices have NULL dip. Don't count these. */ 8764397Sschwartz if (intr_params_p->avgi_num_devs > 0) { 8774397Sschwartz for (i = 0, av_dev = autovect[irqno].avh_link; 8784397Sschwartz av_dev; av_dev = av_dev->av_link) 8794397Sschwartz if (av_dev->av_vector && av_dev->av_dip) 8804397Sschwartz i++; 8814397Sschwartz intr_params_p->avgi_num_devs = 8824397Sschwartz MIN(intr_params_p->avgi_num_devs, i); 8834397Sschwartz } 8844397Sschwartz 8854397Sschwartz /* There are no viable dips to return. */ 8864397Sschwartz if (intr_params_p->avgi_num_devs == 0) 8874397Sschwartz intr_params_p->avgi_dip_list = NULL; 8884397Sschwartz 8894397Sschwartz else { /* Return list of dips */ 8904397Sschwartz 8914397Sschwartz /* Allocate space in array for that number of devs. */ 8924397Sschwartz intr_params_p->avgi_dip_list = kmem_zalloc( 8934397Sschwartz intr_params_p->avgi_num_devs * 8944397Sschwartz sizeof (dev_info_t *), 8954397Sschwartz KM_SLEEP); 8964397Sschwartz 8974397Sschwartz /* 8984397Sschwartz * Loop through the device list of the autovec table 8994397Sschwartz * filling in the dip array. 9004397Sschwartz * 9014397Sschwartz * Note that the autovect table may have some special 9024397Sschwartz * entries which contain NULL dips. These will be 9034397Sschwartz * ignored. 9044397Sschwartz */ 9054397Sschwartz for (i = 0, av_dev = autovect[irqno].avh_link; 9064397Sschwartz av_dev; av_dev = av_dev->av_link) 9074397Sschwartz if (av_dev->av_vector && av_dev->av_dip) 9084397Sschwartz intr_params_p->avgi_dip_list[i++] = 9094397Sschwartz av_dev->av_dip; 9104397Sschwartz } 9114397Sschwartz } 9124397Sschwartz 9134397Sschwartz mutex_exit(&airq_mutex); 9144397Sschwartz 9154397Sschwartz return (PSM_SUCCESS); 9164397Sschwartz } 9174397Sschwartz 9184397Sschwartz 9195084Sjohnlev #if !defined(__xpv) 9205084Sjohnlev 9211997Sanish /* 9220Sstevel@tonic-gate * This function provides external interface to the nexus for all 9230Sstevel@tonic-gate * functionalities related to the new DDI interrupt framework. 9240Sstevel@tonic-gate * 9250Sstevel@tonic-gate * Input: 9260Sstevel@tonic-gate * dip - pointer to the dev_info structure of the requested device 9270Sstevel@tonic-gate * hdlp - pointer to the internal interrupt handle structure for the 9280Sstevel@tonic-gate * requested interrupt 9290Sstevel@tonic-gate * intr_op - opcode for this call 9300Sstevel@tonic-gate * result - pointer to the integer that will hold the result to be 9310Sstevel@tonic-gate * passed back if return value is PSM_SUCCESS 9320Sstevel@tonic-gate * 9330Sstevel@tonic-gate * Output: 9340Sstevel@tonic-gate * return value is either PSM_SUCCESS or PSM_FAILURE 9350Sstevel@tonic-gate */ 9360Sstevel@tonic-gate int 9370Sstevel@tonic-gate apic_intr_ops(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, 9380Sstevel@tonic-gate psm_intr_op_t intr_op, int *result) 9390Sstevel@tonic-gate { 9404397Sschwartz int cap; 9410Sstevel@tonic-gate int count_vec; 9420Sstevel@tonic-gate int old_priority; 9430Sstevel@tonic-gate int new_priority; 9444397Sschwartz int new_cpu; 9450Sstevel@tonic-gate apic_irq_t *irqp; 9460Sstevel@tonic-gate struct intrspec *ispec, intr_spec; 9470Sstevel@tonic-gate 9480Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_intr_ops: dip: %p hdlp: %p " 9490Sstevel@tonic-gate "intr_op: %x\n", (void *)dip, (void *)hdlp, intr_op)); 9500Sstevel@tonic-gate 9510Sstevel@tonic-gate ispec = &intr_spec; 9520Sstevel@tonic-gate ispec->intrspec_pri = hdlp->ih_pri; 9530Sstevel@tonic-gate ispec->intrspec_vec = hdlp->ih_inum; 9540Sstevel@tonic-gate ispec->intrspec_func = hdlp->ih_cb_func; 9550Sstevel@tonic-gate 9560Sstevel@tonic-gate switch (intr_op) { 9570Sstevel@tonic-gate case PSM_INTR_OP_CHECK_MSI: 9580Sstevel@tonic-gate /* 9590Sstevel@tonic-gate * Check MSI/X is supported or not at APIC level and 9600Sstevel@tonic-gate * masked off the MSI/X bits in hdlp->ih_type if not 9610Sstevel@tonic-gate * supported before return. If MSI/X is supported, 9620Sstevel@tonic-gate * leave the ih_type unchanged and return. 9630Sstevel@tonic-gate * 9640Sstevel@tonic-gate * hdlp->ih_type passed in from the nexus has all the 9650Sstevel@tonic-gate * interrupt types supported by the device. 9660Sstevel@tonic-gate */ 9670Sstevel@tonic-gate if (apic_support_msi == 0) { 9680Sstevel@tonic-gate /* 9690Sstevel@tonic-gate * if apic_support_msi is not set, call 9700Sstevel@tonic-gate * apic_check_msi_support() to check whether msi 9710Sstevel@tonic-gate * is supported first 9720Sstevel@tonic-gate */ 9731701Sjohnny if (apic_check_msi_support() == PSM_SUCCESS) 9740Sstevel@tonic-gate apic_support_msi = 1; 9750Sstevel@tonic-gate else 9760Sstevel@tonic-gate apic_support_msi = -1; 9770Sstevel@tonic-gate } 9784937Sjohnny if (apic_support_msi == 1) { 9794937Sjohnny if (apic_msix_enable) 9804937Sjohnny *result = hdlp->ih_type; 9814937Sjohnny else 9824937Sjohnny *result = hdlp->ih_type & ~DDI_INTR_TYPE_MSIX; 9834937Sjohnny } else 9840Sstevel@tonic-gate *result = hdlp->ih_type & ~(DDI_INTR_TYPE_MSI | 9850Sstevel@tonic-gate DDI_INTR_TYPE_MSIX); 9860Sstevel@tonic-gate break; 9870Sstevel@tonic-gate case PSM_INTR_OP_ALLOC_VECTORS: 9884937Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_MSI) 9894937Sjohnny *result = apic_alloc_msi_vectors(dip, hdlp->ih_inum, 9904937Sjohnny hdlp->ih_scratch1, hdlp->ih_pri, 9914937Sjohnny (int)(uintptr_t)hdlp->ih_scratch2); 9924937Sjohnny else 9934937Sjohnny *result = apic_alloc_msix_vectors(dip, hdlp->ih_inum, 9944937Sjohnny hdlp->ih_scratch1, hdlp->ih_pri, 9954937Sjohnny (int)(uintptr_t)hdlp->ih_scratch2); 9960Sstevel@tonic-gate break; 9970Sstevel@tonic-gate case PSM_INTR_OP_FREE_VECTORS: 9980Sstevel@tonic-gate apic_free_vectors(dip, hdlp->ih_inum, hdlp->ih_scratch1, 9990Sstevel@tonic-gate hdlp->ih_pri, hdlp->ih_type); 10000Sstevel@tonic-gate break; 10010Sstevel@tonic-gate case PSM_INTR_OP_NAVAIL_VECTORS: 10020Sstevel@tonic-gate *result = apic_navail_vector(dip, hdlp->ih_pri); 10030Sstevel@tonic-gate break; 10040Sstevel@tonic-gate case PSM_INTR_OP_XLATE_VECTOR: 1005916Sschwartz ispec = ((ihdl_plat_t *)hdlp->ih_private)->ip_ispecp; 10060Sstevel@tonic-gate *result = apic_introp_xlate(dip, ispec, hdlp->ih_type); 100711465SKerry.Shu@Sun.COM if (*result == -1) 100811465SKerry.Shu@Sun.COM return (PSM_FAILURE); 10090Sstevel@tonic-gate break; 10100Sstevel@tonic-gate case PSM_INTR_OP_GET_PENDING: 10110Sstevel@tonic-gate if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 10120Sstevel@tonic-gate return (PSM_FAILURE); 10130Sstevel@tonic-gate *result = apic_get_pending(irqp, hdlp->ih_type); 10140Sstevel@tonic-gate break; 10150Sstevel@tonic-gate case PSM_INTR_OP_CLEAR_MASK: 10160Sstevel@tonic-gate if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 10170Sstevel@tonic-gate return (PSM_FAILURE); 10180Sstevel@tonic-gate irqp = apic_find_irq(dip, ispec, hdlp->ih_type); 10190Sstevel@tonic-gate if (irqp == NULL) 10200Sstevel@tonic-gate return (PSM_FAILURE); 10210Sstevel@tonic-gate apic_clear_mask(irqp); 10220Sstevel@tonic-gate break; 10230Sstevel@tonic-gate case PSM_INTR_OP_SET_MASK: 10240Sstevel@tonic-gate if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 10250Sstevel@tonic-gate return (PSM_FAILURE); 10260Sstevel@tonic-gate if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 10270Sstevel@tonic-gate return (PSM_FAILURE); 10280Sstevel@tonic-gate apic_set_mask(irqp); 10290Sstevel@tonic-gate break; 10300Sstevel@tonic-gate case PSM_INTR_OP_GET_CAP: 10310Sstevel@tonic-gate cap = DDI_INTR_FLAG_PENDING; 10320Sstevel@tonic-gate if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 10330Sstevel@tonic-gate cap |= DDI_INTR_FLAG_MASKABLE; 103410053SEvan.Yan@Sun.COM else if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) 103510053SEvan.Yan@Sun.COM cap |= DDI_INTR_FLAG_RETARGETABLE; 10360Sstevel@tonic-gate *result = cap; 10370Sstevel@tonic-gate break; 10380Sstevel@tonic-gate case PSM_INTR_OP_GET_SHARED: 10390Sstevel@tonic-gate if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 10400Sstevel@tonic-gate return (PSM_FAILURE); 104110190SSophia.Li@Sun.COM ispec = ((ihdl_plat_t *)hdlp->ih_private)->ip_ispecp; 10420Sstevel@tonic-gate if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 10430Sstevel@tonic-gate return (PSM_FAILURE); 104410190SSophia.Li@Sun.COM *result = (irqp->airq_share > 1) ? 1: 0; 10450Sstevel@tonic-gate break; 10460Sstevel@tonic-gate case PSM_INTR_OP_SET_PRI: 10470Sstevel@tonic-gate old_priority = hdlp->ih_pri; /* save old value */ 10480Sstevel@tonic-gate new_priority = *(int *)result; /* try the new value */ 10490Sstevel@tonic-gate 105010190SSophia.Li@Sun.COM if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) { 105110190SSophia.Li@Sun.COM return (PSM_SUCCESS); 105210190SSophia.Li@Sun.COM } 10530Sstevel@tonic-gate 10540Sstevel@tonic-gate /* Now allocate the vectors */ 105510190SSophia.Li@Sun.COM if (hdlp->ih_type == DDI_INTR_TYPE_MSI) { 105610190SSophia.Li@Sun.COM /* SET_PRI does not support the case of multiple MSI */ 105710190SSophia.Li@Sun.COM if (i_ddi_intr_get_current_nintrs(hdlp->ih_dip) > 1) 105810190SSophia.Li@Sun.COM return (PSM_FAILURE); 105910190SSophia.Li@Sun.COM 10604937Sjohnny count_vec = apic_alloc_msi_vectors(dip, hdlp->ih_inum, 106110190SSophia.Li@Sun.COM 1, new_priority, 10624937Sjohnny DDI_INTR_ALLOC_STRICT); 106310190SSophia.Li@Sun.COM } else { 10644937Sjohnny count_vec = apic_alloc_msix_vectors(dip, hdlp->ih_inum, 106510190SSophia.Li@Sun.COM 1, new_priority, 10664937Sjohnny DDI_INTR_ALLOC_STRICT); 106710190SSophia.Li@Sun.COM } 10680Sstevel@tonic-gate 10693446Smrj /* Did we get new vectors? */ 10702335Sjohnny if (!count_vec) 10710Sstevel@tonic-gate return (PSM_FAILURE); 10720Sstevel@tonic-gate 10730Sstevel@tonic-gate /* Finally, free the previously allocated vectors */ 10740Sstevel@tonic-gate apic_free_vectors(dip, hdlp->ih_inum, count_vec, 10750Sstevel@tonic-gate old_priority, hdlp->ih_type); 10760Sstevel@tonic-gate break; 1077916Sschwartz case PSM_INTR_OP_SET_CPU: 10784397Sschwartz case PSM_INTR_OP_GRP_SET_CPU: 1079916Sschwartz /* 1080916Sschwartz * The interrupt handle given here has been allocated 1081916Sschwartz * specifically for this command, and ih_private carries 1082916Sschwartz * a CPU value. 1083916Sschwartz */ 10844397Sschwartz new_cpu = (int)(intptr_t)hdlp->ih_private; 10854397Sschwartz if (!apic_cpu_in_range(new_cpu)) { 10864397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 10874397Sschwartz "[grp_]set_cpu: cpu out of range: %d\n", new_cpu)); 1088916Sschwartz *result = EINVAL; 1089916Sschwartz return (PSM_FAILURE); 1090916Sschwartz } 109110053SEvan.Yan@Sun.COM if (hdlp->ih_vector > APIC_MAX_VECTOR) { 109210053SEvan.Yan@Sun.COM DDI_INTR_IMPLDBG((CE_CONT, 109310053SEvan.Yan@Sun.COM "[grp_]set_cpu: vector out of range: %d\n", 109410053SEvan.Yan@Sun.COM hdlp->ih_vector)); 109510053SEvan.Yan@Sun.COM *result = EINVAL; 109610053SEvan.Yan@Sun.COM return (PSM_FAILURE); 109710053SEvan.Yan@Sun.COM } 109810053SEvan.Yan@Sun.COM if (!(hdlp->ih_flags & PSMGI_INTRBY_IRQ)) 109910053SEvan.Yan@Sun.COM hdlp->ih_vector = apic_vector_to_irq[hdlp->ih_vector]; 11004397Sschwartz if (intr_op == PSM_INTR_OP_SET_CPU) { 11014397Sschwartz if (apic_set_cpu(hdlp->ih_vector, new_cpu, result) != 11024397Sschwartz PSM_SUCCESS) 11034397Sschwartz return (PSM_FAILURE); 11044397Sschwartz } else { 11054397Sschwartz if (apic_grp_set_cpu(hdlp->ih_vector, new_cpu, 11064397Sschwartz result) != PSM_SUCCESS) 11074397Sschwartz return (PSM_FAILURE); 1108916Sschwartz } 1109916Sschwartz break; 1110916Sschwartz case PSM_INTR_OP_GET_INTR: 1111916Sschwartz /* 1112916Sschwartz * The interrupt handle given here has been allocated 1113916Sschwartz * specifically for this command, and ih_private carries 1114916Sschwartz * a pointer to a apic_get_intr_t. 1115916Sschwartz */ 1116916Sschwartz if (apic_get_vector_intr_info( 1117916Sschwartz hdlp->ih_vector, hdlp->ih_private) != PSM_SUCCESS) 1118916Sschwartz return (PSM_FAILURE); 1119916Sschwartz break; 11204397Sschwartz case PSM_INTR_OP_APIC_TYPE: 11214397Sschwartz hdlp->ih_private = apic_get_apic_type(); 11224397Sschwartz hdlp->ih_ver = apic_get_apic_version(); 11234397Sschwartz break; 11240Sstevel@tonic-gate case PSM_INTR_OP_SET_CAP: 11250Sstevel@tonic-gate default: 11260Sstevel@tonic-gate return (PSM_FAILURE); 11270Sstevel@tonic-gate } 11280Sstevel@tonic-gate return (PSM_SUCCESS); 11290Sstevel@tonic-gate } 11305084Sjohnlev #endif /* !__xpv */ 1131