17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5685482d6Sjohnny * Common Development and Distribution License (the "License"). 6685482d6Sjohnny * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 224e30c628SEvan Yan * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 23*8181b438SGarrett D'Amore * Copyright 2013 Pluribus Networks, Inc. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate /* 277c478bd9Sstevel@tonic-gate * apic_introp.c: 287c478bd9Sstevel@tonic-gate * Has code for Advanced DDI interrupt framework support. 297c478bd9Sstevel@tonic-gate */ 307c478bd9Sstevel@tonic-gate 317c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h> 327c478bd9Sstevel@tonic-gate #include <sys/psm.h> 33ae115bc7Smrj #include <sys/archsystm.h> 34ae115bc7Smrj #include <sys/apic.h> 357c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 367c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 377a364d25Sschwartz #include <sys/mach_intr.h> 387a364d25Sschwartz #include <sys/sysmacros.h> 397c478bd9Sstevel@tonic-gate #include <sys/trap.h> 407c478bd9Sstevel@tonic-gate #include <sys/pci.h> 417c478bd9Sstevel@tonic-gate #include <sys/pci_intr_lib.h> 427ff178cdSJimmy Vetayases #include <sys/apic_common.h> 437c478bd9Sstevel@tonic-gate 447a364d25Sschwartz extern struct av_head autovect[]; 457a364d25Sschwartz 467c478bd9Sstevel@tonic-gate /* 477c478bd9Sstevel@tonic-gate * Local Function Prototypes 487c478bd9Sstevel@tonic-gate */ 497c478bd9Sstevel@tonic-gate apic_irq_t *apic_find_irq(dev_info_t *, struct intrspec *, int); 507c478bd9Sstevel@tonic-gate 517c478bd9Sstevel@tonic-gate /* 527c478bd9Sstevel@tonic-gate * apic_pci_msi_enable_vector: 537c478bd9Sstevel@tonic-gate * Set the address/data fields in the MSI/X capability structure 547c478bd9Sstevel@tonic-gate * XXX: MSI-X support 557c478bd9Sstevel@tonic-gate */ 567c478bd9Sstevel@tonic-gate /* ARGSUSED */ 57a7639048Sjohnny void 58bb8220baSVikram Hegde apic_pci_msi_enable_vector(apic_irq_t *irq_ptr, int type, int inum, int vector, 597c478bd9Sstevel@tonic-gate int count, int target_apic_id) 607c478bd9Sstevel@tonic-gate { 617c478bd9Sstevel@tonic-gate uint64_t msi_addr, msi_data; 62d12abe7cSanish ushort_t msi_ctrl; 63bb8220baSVikram Hegde dev_info_t *dip = irq_ptr->airq_dip; 64d12abe7cSanish int cap_ptr = i_ddi_get_msi_msix_cap_ptr(dip); 65d12abe7cSanish ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(dip); 66bb8220baSVikram Hegde msi_regs_t msi_regs; 677ff178cdSJimmy Vetayases int irqno, i; 687ff178cdSJimmy Vetayases void *intrmap_tbl[PCI_MSI_MAX_INTRS]; 697c478bd9Sstevel@tonic-gate 707c478bd9Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: dip=0x%p\n" 717c478bd9Sstevel@tonic-gate "\tdriver = %s, inum=0x%x vector=0x%x apicid=0x%x\n", (void *)dip, 727c478bd9Sstevel@tonic-gate ddi_driver_name(dip), inum, vector, target_apic_id)); 737c478bd9Sstevel@tonic-gate 74a7639048Sjohnny ASSERT((handle != NULL) && (cap_ptr != 0)); 75d12abe7cSanish 76bb8220baSVikram Hegde msi_regs.mr_data = vector; 77bb8220baSVikram Hegde msi_regs.mr_addr = target_apic_id; 78bb8220baSVikram Hegde 792edb3dccSJudy Chen for (i = 0; i < count; i++) { 802edb3dccSJudy Chen irqno = apic_vector_to_irq[vector + i]; 812edb3dccSJudy Chen intrmap_tbl[i] = apic_irq_table[irqno]->airq_intrmap_private; 822edb3dccSJudy Chen } 837ff178cdSJimmy Vetayases apic_vt_ops->apic_intrmap_alloc_entry(intrmap_tbl, dip, type, 847ff178cdSJimmy Vetayases count, 0xff); 857ff178cdSJimmy Vetayases for (i = 0; i < count; i++) { 867ff178cdSJimmy Vetayases irqno = apic_vector_to_irq[vector + i]; 877ff178cdSJimmy Vetayases apic_irq_table[irqno]->airq_intrmap_private = 887ff178cdSJimmy Vetayases intrmap_tbl[i]; 897ff178cdSJimmy Vetayases } 907ff178cdSJimmy Vetayases 917ff178cdSJimmy Vetayases apic_vt_ops->apic_intrmap_map_entry(irq_ptr->airq_intrmap_private, 927ff178cdSJimmy Vetayases (void *)&msi_regs, type, count); 937ff178cdSJimmy Vetayases apic_vt_ops->apic_intrmap_record_msi(irq_ptr->airq_intrmap_private, 947ff178cdSJimmy Vetayases &msi_regs); 956bdf0ab5SVikram Hegde 967c478bd9Sstevel@tonic-gate /* MSI Address */ 97bb8220baSVikram Hegde msi_addr = msi_regs.mr_addr; 987c478bd9Sstevel@tonic-gate 997c478bd9Sstevel@tonic-gate /* MSI Data: MSI is edge triggered according to spec */ 100bb8220baSVikram Hegde msi_data = msi_regs.mr_data; 1017c478bd9Sstevel@tonic-gate 1027c478bd9Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: addr=0x%lx " 1037c478bd9Sstevel@tonic-gate "data=0x%lx\n", (long)msi_addr, (long)msi_data)); 1047c478bd9Sstevel@tonic-gate 105d12abe7cSanish if (type == DDI_INTR_TYPE_MSI) { 106d12abe7cSanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 107d12abe7cSanish 108d12abe7cSanish /* Set the bits to inform how many MSIs are enabled */ 109d12abe7cSanish msi_ctrl |= ((highbit(count) -1) << PCI_MSI_MME_SHIFT); 110d12abe7cSanish pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 111d12abe7cSanish 112349b53ddSStuart Maybee /* 113349b53ddSStuart Maybee * Only set vector if not on hypervisor 114349b53ddSStuart Maybee */ 115d12abe7cSanish pci_config_put32(handle, 116d12abe7cSanish cap_ptr + PCI_MSI_ADDR_OFFSET, msi_addr); 117d12abe7cSanish 118d12abe7cSanish if (msi_ctrl & PCI_MSI_64BIT_MASK) { 119d12abe7cSanish pci_config_put32(handle, 120d12abe7cSanish cap_ptr + PCI_MSI_ADDR_OFFSET + 4, msi_addr >> 32); 121d12abe7cSanish pci_config_put16(handle, 122d12abe7cSanish cap_ptr + PCI_MSI_64BIT_DATA, msi_data); 123d12abe7cSanish } else { 124d12abe7cSanish pci_config_put16(handle, 125d12abe7cSanish cap_ptr + PCI_MSI_32BIT_DATA, msi_data); 126d12abe7cSanish } 127d12abe7cSanish 128d12abe7cSanish } else if (type == DDI_INTR_TYPE_MSIX) { 129d12abe7cSanish uintptr_t off; 130d12abe7cSanish ddi_intr_msix_t *msix_p = i_ddi_get_msix(dip); 131d12abe7cSanish 132fbb8dc2cSlipeng sang - Sun Microsystems - Beijing China ASSERT(msix_p != NULL); 133fbb8dc2cSlipeng sang - Sun Microsystems - Beijing China 134d12abe7cSanish /* Offset into the "inum"th entry in the MSI-X table */ 135d12abe7cSanish off = (uintptr_t)msix_p->msix_tbl_addr + 136d12abe7cSanish (inum * PCI_MSIX_VECTOR_SIZE); 137d12abe7cSanish 138d12abe7cSanish ddi_put32(msix_p->msix_tbl_hdl, 139d12abe7cSanish (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), msi_data); 140*8181b438SGarrett D'Amore ddi_put32(msix_p->msix_tbl_hdl, 141*8181b438SGarrett D'Amore (uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), msi_addr); 142*8181b438SGarrett D'Amore ddi_put32(msix_p->msix_tbl_hdl, 143*8181b438SGarrett D'Amore (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET), 144*8181b438SGarrett D'Amore msi_addr >> 32); 1457c478bd9Sstevel@tonic-gate } 1467c478bd9Sstevel@tonic-gate } 1477c478bd9Sstevel@tonic-gate 1487c478bd9Sstevel@tonic-gate /* 1497c478bd9Sstevel@tonic-gate * This function returns the no. of vectors available for the pri. 1507c478bd9Sstevel@tonic-gate * dip is not used at this moment. If we really don't need that, 1517c478bd9Sstevel@tonic-gate * it will be removed. 1527c478bd9Sstevel@tonic-gate */ 1537c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 1547c478bd9Sstevel@tonic-gate int 1557c478bd9Sstevel@tonic-gate apic_navail_vector(dev_info_t *dip, int pri) 1567c478bd9Sstevel@tonic-gate { 1577c478bd9Sstevel@tonic-gate int lowest, highest, i, navail, count; 1587c478bd9Sstevel@tonic-gate 1597c478bd9Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_navail_vector: dip: %p, pri: %x\n", 1607c478bd9Sstevel@tonic-gate (void *)dip, pri)); 1617c478bd9Sstevel@tonic-gate 1627c478bd9Sstevel@tonic-gate highest = apic_ipltopri[pri] + APIC_VECTOR_MASK; 1637c478bd9Sstevel@tonic-gate lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL; 1647c478bd9Sstevel@tonic-gate navail = count = 0; 1657c478bd9Sstevel@tonic-gate 166b6917abeSmishra if (highest < lowest) /* Both ipl and ipl - 1 map to same pri */ 167b6917abeSmishra lowest -= APIC_VECTOR_PER_IPL; 168b6917abeSmishra 1697c478bd9Sstevel@tonic-gate /* It has to be contiguous */ 17026896e4cSGuoli Shu for (i = lowest; i <= highest; i++) { 1717c478bd9Sstevel@tonic-gate count = 0; 1727c478bd9Sstevel@tonic-gate while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) && 17326896e4cSGuoli Shu (i <= highest)) { 1740ccf9e79Sjohnny if (APIC_CHECK_RESERVE_VECTORS(i)) 1757c478bd9Sstevel@tonic-gate break; 1767c478bd9Sstevel@tonic-gate count++; 1777c478bd9Sstevel@tonic-gate i++; 1787c478bd9Sstevel@tonic-gate } 1797c478bd9Sstevel@tonic-gate if (count > navail) 1807c478bd9Sstevel@tonic-gate navail = count; 1817c478bd9Sstevel@tonic-gate } 1827c478bd9Sstevel@tonic-gate return (navail); 1837c478bd9Sstevel@tonic-gate } 1847c478bd9Sstevel@tonic-gate 1850ccf9e79Sjohnny /* 1860ccf9e79Sjohnny * Finds "count" contiguous MSI vectors starting at the proper alignment 1870ccf9e79Sjohnny * at "pri". 1880ccf9e79Sjohnny * Caller needs to make sure that count has to be power of 2 and should not 1890ccf9e79Sjohnny * be < 1. 1900ccf9e79Sjohnny */ 191ae115bc7Smrj uchar_t 1927c478bd9Sstevel@tonic-gate apic_find_multi_vectors(int pri, int count) 1937c478bd9Sstevel@tonic-gate { 1940ccf9e79Sjohnny int lowest, highest, i, navail, start, msibits; 1957c478bd9Sstevel@tonic-gate 1967c478bd9Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_find_mult: pri: %x, count: %x\n", 1977c478bd9Sstevel@tonic-gate pri, count)); 1987c478bd9Sstevel@tonic-gate 1997c478bd9Sstevel@tonic-gate highest = apic_ipltopri[pri] + APIC_VECTOR_MASK; 2007c478bd9Sstevel@tonic-gate lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL; 2017c478bd9Sstevel@tonic-gate navail = 0; 2027c478bd9Sstevel@tonic-gate 203b6917abeSmishra if (highest < lowest) /* Both ipl and ipl - 1 map to same pri */ 204b6917abeSmishra lowest -= APIC_VECTOR_PER_IPL; 205b6917abeSmishra 2060ccf9e79Sjohnny /* 2070ccf9e79Sjohnny * msibits is the no. of lower order message data bits for the 2080ccf9e79Sjohnny * allocated MSI vectors and is used to calculate the aligned 2090ccf9e79Sjohnny * starting vector 2100ccf9e79Sjohnny */ 2110ccf9e79Sjohnny msibits = count - 1; 2120ccf9e79Sjohnny 2137c478bd9Sstevel@tonic-gate /* It has to be contiguous */ 21426896e4cSGuoli Shu for (i = lowest; i <= highest; i++) { 2157c478bd9Sstevel@tonic-gate navail = 0; 2160ccf9e79Sjohnny 2170ccf9e79Sjohnny /* 2180ccf9e79Sjohnny * starting vector has to be aligned accordingly for 2190ccf9e79Sjohnny * multiple MSIs 2200ccf9e79Sjohnny */ 2210ccf9e79Sjohnny if (msibits) 2220ccf9e79Sjohnny i = (i + msibits) & ~msibits; 2237c478bd9Sstevel@tonic-gate start = i; 2247c478bd9Sstevel@tonic-gate while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) && 22526896e4cSGuoli Shu (i <= highest)) { 2260ccf9e79Sjohnny if (APIC_CHECK_RESERVE_VECTORS(i)) 2277c478bd9Sstevel@tonic-gate break; 2287c478bd9Sstevel@tonic-gate navail++; 2297c478bd9Sstevel@tonic-gate if (navail >= count) 2307c478bd9Sstevel@tonic-gate return (start); 2317c478bd9Sstevel@tonic-gate i++; 2327c478bd9Sstevel@tonic-gate } 2337c478bd9Sstevel@tonic-gate } 2347c478bd9Sstevel@tonic-gate return (0); 2357c478bd9Sstevel@tonic-gate } 2367c478bd9Sstevel@tonic-gate 237ae115bc7Smrj 2387c478bd9Sstevel@tonic-gate /* 2397c478bd9Sstevel@tonic-gate * It finds the apic_irq_t associates with the dip, ispec and type. 2407c478bd9Sstevel@tonic-gate */ 2417c478bd9Sstevel@tonic-gate apic_irq_t * 2427c478bd9Sstevel@tonic-gate apic_find_irq(dev_info_t *dip, struct intrspec *ispec, int type) 2437c478bd9Sstevel@tonic-gate { 2447c478bd9Sstevel@tonic-gate apic_irq_t *irqp; 2457c478bd9Sstevel@tonic-gate int i; 2467c478bd9Sstevel@tonic-gate 2477c478bd9Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: dip=0x%p vec=0x%x " 2487c478bd9Sstevel@tonic-gate "ipl=0x%x type=0x%x\n", (void *)dip, ispec->intrspec_vec, 2497c478bd9Sstevel@tonic-gate ispec->intrspec_pri, type)); 2507c478bd9Sstevel@tonic-gate 2517c478bd9Sstevel@tonic-gate for (i = apic_min_device_irq; i <= apic_max_device_irq; i++) { 25286a9c507SGuoli Shu for (irqp = apic_irq_table[i]; irqp; irqp = irqp->airq_next) { 2537c478bd9Sstevel@tonic-gate if ((irqp->airq_dip == dip) && 2547c478bd9Sstevel@tonic-gate (irqp->airq_origirq == ispec->intrspec_vec) && 2557c478bd9Sstevel@tonic-gate (irqp->airq_ipl == ispec->intrspec_pri)) { 256a7639048Sjohnny if (type == DDI_INTR_TYPE_MSI) { 25786a9c507SGuoli Shu if (irqp->airq_mps_intr_index == 25886a9c507SGuoli Shu MSI_INDEX) 259a7639048Sjohnny return (irqp); 260a7639048Sjohnny } else if (type == DDI_INTR_TYPE_MSIX) { 26186a9c507SGuoli Shu if (irqp->airq_mps_intr_index == 26286a9c507SGuoli Shu MSIX_INDEX) 2637c478bd9Sstevel@tonic-gate return (irqp); 2647c478bd9Sstevel@tonic-gate } else 2657c478bd9Sstevel@tonic-gate return (irqp); 2667c478bd9Sstevel@tonic-gate } 2677c478bd9Sstevel@tonic-gate } 26886a9c507SGuoli Shu } 2697c478bd9Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: return NULL\n")); 2707c478bd9Sstevel@tonic-gate return (NULL); 2717c478bd9Sstevel@tonic-gate } 2727c478bd9Sstevel@tonic-gate 2737c478bd9Sstevel@tonic-gate /* 2747c478bd9Sstevel@tonic-gate * This function will return the pending bit of the irqp. 2757c478bd9Sstevel@tonic-gate * It either comes from the IRR register of the APIC or the RDT 2767c478bd9Sstevel@tonic-gate * entry of the I/O APIC. 2777c478bd9Sstevel@tonic-gate * For the IRR to work, it needs to be to its binding CPU 2787c478bd9Sstevel@tonic-gate */ 2797c478bd9Sstevel@tonic-gate static int 2807c478bd9Sstevel@tonic-gate apic_get_pending(apic_irq_t *irqp, int type) 2817c478bd9Sstevel@tonic-gate { 2827c478bd9Sstevel@tonic-gate int bit, index, irr, pending; 2837c478bd9Sstevel@tonic-gate int intin_no; 284ae115bc7Smrj int apic_ix; 2857c478bd9Sstevel@tonic-gate 2867c478bd9Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_get_pending: irqp: %p, cpuid: %x " 2877c478bd9Sstevel@tonic-gate "type: %x\n", (void *)irqp, irqp->airq_cpu & ~IRQ_USER_BOUND, 2887c478bd9Sstevel@tonic-gate type)); 2897c478bd9Sstevel@tonic-gate 2907c478bd9Sstevel@tonic-gate /* need to get on the bound cpu */ 2917c478bd9Sstevel@tonic-gate mutex_enter(&cpu_lock); 2927c478bd9Sstevel@tonic-gate affinity_set(irqp->airq_cpu & ~IRQ_USER_BOUND); 2937c478bd9Sstevel@tonic-gate 2947c478bd9Sstevel@tonic-gate index = irqp->airq_vector / 32; 2957c478bd9Sstevel@tonic-gate bit = irqp->airq_vector % 32; 296b6917abeSmishra irr = apic_reg_ops->apic_read(APIC_IRR_REG + index); 2977c478bd9Sstevel@tonic-gate 2987c478bd9Sstevel@tonic-gate affinity_clear(); 2997c478bd9Sstevel@tonic-gate mutex_exit(&cpu_lock); 3007c478bd9Sstevel@tonic-gate 3017c478bd9Sstevel@tonic-gate pending = (irr & (1 << bit)) ? 1 : 0; 3027c478bd9Sstevel@tonic-gate if (!pending && (type == DDI_INTR_TYPE_FIXED)) { 3037c478bd9Sstevel@tonic-gate /* check I/O APIC for fixed interrupt */ 3047c478bd9Sstevel@tonic-gate intin_no = irqp->airq_intin_no; 305ae115bc7Smrj apic_ix = irqp->airq_ioapicindex; 306ae115bc7Smrj pending = (READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no) & 3077c478bd9Sstevel@tonic-gate AV_PENDING) ? 1 : 0; 3087c478bd9Sstevel@tonic-gate } 3097c478bd9Sstevel@tonic-gate return (pending); 3107c478bd9Sstevel@tonic-gate } 3117c478bd9Sstevel@tonic-gate 3127c478bd9Sstevel@tonic-gate 3137c478bd9Sstevel@tonic-gate /* 3147c478bd9Sstevel@tonic-gate * This function will clear the mask for the interrupt on the I/O APIC 3157c478bd9Sstevel@tonic-gate */ 3167c478bd9Sstevel@tonic-gate static void 3177c478bd9Sstevel@tonic-gate apic_clear_mask(apic_irq_t *irqp) 3187c478bd9Sstevel@tonic-gate { 3197c478bd9Sstevel@tonic-gate int intin_no; 320ae115bc7Smrj ulong_t iflag; 3217c478bd9Sstevel@tonic-gate int32_t rdt_entry; 322ae115bc7Smrj int apic_ix; 3237c478bd9Sstevel@tonic-gate 3247c478bd9Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_clear_mask: irqp: %p\n", 3257c478bd9Sstevel@tonic-gate (void *)irqp)); 3267c478bd9Sstevel@tonic-gate 3277c478bd9Sstevel@tonic-gate intin_no = irqp->airq_intin_no; 328ae115bc7Smrj apic_ix = irqp->airq_ioapicindex; 3297c478bd9Sstevel@tonic-gate 3307c478bd9Sstevel@tonic-gate iflag = intr_clear(); 3317c478bd9Sstevel@tonic-gate lock_set(&apic_ioapic_lock); 3327c478bd9Sstevel@tonic-gate 333ae115bc7Smrj rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no); 3347c478bd9Sstevel@tonic-gate 3357c478bd9Sstevel@tonic-gate /* clear mask */ 336ae115bc7Smrj WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no, 3377c478bd9Sstevel@tonic-gate ((~AV_MASK) & rdt_entry)); 3387c478bd9Sstevel@tonic-gate 3397c478bd9Sstevel@tonic-gate lock_clear(&apic_ioapic_lock); 3407c478bd9Sstevel@tonic-gate intr_restore(iflag); 3417c478bd9Sstevel@tonic-gate } 3427c478bd9Sstevel@tonic-gate 3437c478bd9Sstevel@tonic-gate 3447c478bd9Sstevel@tonic-gate /* 3457c478bd9Sstevel@tonic-gate * This function will mask the interrupt on the I/O APIC 3467c478bd9Sstevel@tonic-gate */ 3477c478bd9Sstevel@tonic-gate static void 3487c478bd9Sstevel@tonic-gate apic_set_mask(apic_irq_t *irqp) 3497c478bd9Sstevel@tonic-gate { 3507c478bd9Sstevel@tonic-gate int intin_no; 351ae115bc7Smrj int apic_ix; 352ae115bc7Smrj ulong_t iflag; 3537c478bd9Sstevel@tonic-gate int32_t rdt_entry; 3547c478bd9Sstevel@tonic-gate 3557c478bd9Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_set_mask: irqp: %p\n", (void *)irqp)); 3567c478bd9Sstevel@tonic-gate 3577c478bd9Sstevel@tonic-gate intin_no = irqp->airq_intin_no; 358ae115bc7Smrj apic_ix = irqp->airq_ioapicindex; 3597c478bd9Sstevel@tonic-gate 3607c478bd9Sstevel@tonic-gate iflag = intr_clear(); 3617c478bd9Sstevel@tonic-gate 3627c478bd9Sstevel@tonic-gate lock_set(&apic_ioapic_lock); 3637c478bd9Sstevel@tonic-gate 364ae115bc7Smrj rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no); 3657c478bd9Sstevel@tonic-gate 3667c478bd9Sstevel@tonic-gate /* mask it */ 367ae115bc7Smrj WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no, 3687c478bd9Sstevel@tonic-gate (AV_MASK | rdt_entry)); 3697c478bd9Sstevel@tonic-gate 3707c478bd9Sstevel@tonic-gate lock_clear(&apic_ioapic_lock); 3717c478bd9Sstevel@tonic-gate intr_restore(iflag); 3727c478bd9Sstevel@tonic-gate } 3737c478bd9Sstevel@tonic-gate 3747c478bd9Sstevel@tonic-gate 3757c478bd9Sstevel@tonic-gate void 3767c478bd9Sstevel@tonic-gate apic_free_vectors(dev_info_t *dip, int inum, int count, int pri, int type) 3777c478bd9Sstevel@tonic-gate { 3787c478bd9Sstevel@tonic-gate int i; 3797c478bd9Sstevel@tonic-gate apic_irq_t *irqptr; 3807c478bd9Sstevel@tonic-gate struct intrspec ispec; 3817c478bd9Sstevel@tonic-gate 3827c478bd9Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: dip: %p inum: %x " 3837c478bd9Sstevel@tonic-gate "count: %x pri: %x type: %x\n", 3847c478bd9Sstevel@tonic-gate (void *)dip, inum, count, pri, type)); 3857c478bd9Sstevel@tonic-gate 3867c478bd9Sstevel@tonic-gate /* for MSI/X only */ 3877c478bd9Sstevel@tonic-gate if (!DDI_INTR_IS_MSI_OR_MSIX(type)) 3887c478bd9Sstevel@tonic-gate return; 3897c478bd9Sstevel@tonic-gate 3907c478bd9Sstevel@tonic-gate for (i = 0; i < count; i++) { 3917c478bd9Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: inum=0x%x " 3927c478bd9Sstevel@tonic-gate "pri=0x%x count=0x%x\n", inum, pri, count)); 3937c478bd9Sstevel@tonic-gate ispec.intrspec_vec = inum + i; 3947c478bd9Sstevel@tonic-gate ispec.intrspec_pri = pri; 3957c478bd9Sstevel@tonic-gate if ((irqptr = apic_find_irq(dip, &ispec, type)) == NULL) { 3967c478bd9Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: " 3977c478bd9Sstevel@tonic-gate "dip=0x%p inum=0x%x pri=0x%x apic_find_irq() " 3987c478bd9Sstevel@tonic-gate "failed\n", (void *)dip, inum, pri)); 3997c478bd9Sstevel@tonic-gate continue; 4007c478bd9Sstevel@tonic-gate } 4017c478bd9Sstevel@tonic-gate irqptr->airq_mps_intr_index = FREE_INDEX; 4027c478bd9Sstevel@tonic-gate apic_vector_to_irq[irqptr->airq_vector] = APIC_RESV_IRQ; 4037c478bd9Sstevel@tonic-gate } 4047c478bd9Sstevel@tonic-gate } 4057c478bd9Sstevel@tonic-gate 406d12abe7cSanish /* 407d12abe7cSanish * apic_pci_msi_enable_mode: 408d12abe7cSanish */ 409a7639048Sjohnny void 410d12abe7cSanish apic_pci_msi_enable_mode(dev_info_t *rdip, int type, int inum) 411d12abe7cSanish { 412d12abe7cSanish ushort_t msi_ctrl; 413d12abe7cSanish int cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip); 414d12abe7cSanish ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(rdip); 415d12abe7cSanish 416a7639048Sjohnny ASSERT((handle != NULL) && (cap_ptr != 0)); 417d12abe7cSanish 418d12abe7cSanish if (type == DDI_INTR_TYPE_MSI) { 419d12abe7cSanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 420d12abe7cSanish if ((msi_ctrl & PCI_MSI_ENABLE_BIT)) 421a7639048Sjohnny return; 422d12abe7cSanish 423d12abe7cSanish msi_ctrl |= PCI_MSI_ENABLE_BIT; 424d12abe7cSanish pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 425d12abe7cSanish 426d12abe7cSanish } else if (type == DDI_INTR_TYPE_MSIX) { 427d12abe7cSanish uintptr_t off; 428a7639048Sjohnny uint32_t mask; 429d12abe7cSanish ddi_intr_msix_t *msix_p; 430d12abe7cSanish 431d12abe7cSanish msix_p = i_ddi_get_msix(rdip); 432d12abe7cSanish 433fbb8dc2cSlipeng sang - Sun Microsystems - Beijing China ASSERT(msix_p != NULL); 434fbb8dc2cSlipeng sang - Sun Microsystems - Beijing China 435d12abe7cSanish /* Offset into "inum"th entry in the MSI-X table & clear mask */ 436d12abe7cSanish off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 437d12abe7cSanish PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 438d12abe7cSanish 439a7639048Sjohnny mask = ddi_get32(msix_p->msix_tbl_hdl, (uint32_t *)off); 440a7639048Sjohnny 441a7639048Sjohnny ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, (mask & ~1)); 442a7639048Sjohnny 443a7639048Sjohnny msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSIX_CTRL); 444a7639048Sjohnny 445a7639048Sjohnny if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT)) { 446a7639048Sjohnny msi_ctrl |= PCI_MSIX_ENABLE_BIT; 447a7639048Sjohnny pci_config_put16(handle, cap_ptr + PCI_MSIX_CTRL, 448a7639048Sjohnny msi_ctrl); 449a7639048Sjohnny } 450a7639048Sjohnny } 451d12abe7cSanish } 452d12abe7cSanish 4532917a9c9Sschwartz static int 45409b1eac2SEvan Yan apic_set_cpu(int irqno, int cpu, int *result) 4552917a9c9Sschwartz { 4562917a9c9Sschwartz apic_irq_t *irqp; 457a563a037Sbholler ulong_t iflag; 4582917a9c9Sschwartz int ret; 4592917a9c9Sschwartz 4602917a9c9Sschwartz DDI_INTR_IMPLDBG((CE_CONT, "APIC_SET_CPU\n")); 4612917a9c9Sschwartz 4622917a9c9Sschwartz mutex_enter(&airq_mutex); 46309b1eac2SEvan Yan irqp = apic_irq_table[irqno]; 4642917a9c9Sschwartz mutex_exit(&airq_mutex); 4652917a9c9Sschwartz 4662917a9c9Sschwartz if (irqp == NULL) { 4672917a9c9Sschwartz *result = ENXIO; 4682917a9c9Sschwartz return (PSM_FAILURE); 4692917a9c9Sschwartz } 4702917a9c9Sschwartz 4712917a9c9Sschwartz /* Fail if this is an MSI intr and is part of a group. */ 4722917a9c9Sschwartz if ((irqp->airq_mps_intr_index == MSI_INDEX) && 4732917a9c9Sschwartz (irqp->airq_intin_no > 1)) { 4742917a9c9Sschwartz *result = ENXIO; 4752917a9c9Sschwartz return (PSM_FAILURE); 4762917a9c9Sschwartz } 4772917a9c9Sschwartz 4782917a9c9Sschwartz iflag = intr_clear(); 4792917a9c9Sschwartz lock_set(&apic_ioapic_lock); 4802917a9c9Sschwartz 4812917a9c9Sschwartz ret = apic_rebind_all(irqp, cpu); 4822917a9c9Sschwartz 4832917a9c9Sschwartz lock_clear(&apic_ioapic_lock); 4842917a9c9Sschwartz intr_restore(iflag); 4852917a9c9Sschwartz 4862917a9c9Sschwartz if (ret) { 4872917a9c9Sschwartz *result = EIO; 4882917a9c9Sschwartz return (PSM_FAILURE); 4892917a9c9Sschwartz } 49009b1eac2SEvan Yan /* 49109b1eac2SEvan Yan * keep tracking the default interrupt cpu binding 49209b1eac2SEvan Yan */ 49309b1eac2SEvan Yan irqp->airq_cpu = cpu; 49409b1eac2SEvan Yan 4952917a9c9Sschwartz *result = 0; 4962917a9c9Sschwartz return (PSM_SUCCESS); 4972917a9c9Sschwartz } 4982917a9c9Sschwartz 4992917a9c9Sschwartz static int 50009b1eac2SEvan Yan apic_grp_set_cpu(int irqno, int new_cpu, int *result) 5012917a9c9Sschwartz { 5022917a9c9Sschwartz dev_info_t *orig_dip; 503b6917abeSmishra uint32_t orig_cpu; 504a563a037Sbholler ulong_t iflag; 5052917a9c9Sschwartz apic_irq_t *irqps[PCI_MSI_MAX_INTRS]; 5062917a9c9Sschwartz int i; 5072917a9c9Sschwartz int cap_ptr; 5082917a9c9Sschwartz int msi_mask_off; 5092917a9c9Sschwartz ushort_t msi_ctrl; 5102917a9c9Sschwartz uint32_t msi_pvm; 5112917a9c9Sschwartz ddi_acc_handle_t handle; 5122917a9c9Sschwartz int num_vectors = 0; 51309b1eac2SEvan Yan uint32_t vector; 5142917a9c9Sschwartz 5152917a9c9Sschwartz DDI_INTR_IMPLDBG((CE_CONT, "APIC_GRP_SET_CPU\n")); 5162917a9c9Sschwartz 5172917a9c9Sschwartz /* 5182917a9c9Sschwartz * Take mutex to insure that table doesn't change out from underneath 5192917a9c9Sschwartz * us while we're playing with it. 5202917a9c9Sschwartz */ 5212917a9c9Sschwartz mutex_enter(&airq_mutex); 52209b1eac2SEvan Yan irqps[0] = apic_irq_table[irqno]; 5232917a9c9Sschwartz orig_cpu = irqps[0]->airq_temp_cpu; 5242917a9c9Sschwartz orig_dip = irqps[0]->airq_dip; 5252917a9c9Sschwartz num_vectors = irqps[0]->airq_intin_no; 52609b1eac2SEvan Yan vector = irqps[0]->airq_vector; 5272917a9c9Sschwartz 5282917a9c9Sschwartz /* A "group" of 1 */ 5292917a9c9Sschwartz if (num_vectors == 1) { 5302917a9c9Sschwartz mutex_exit(&airq_mutex); 53109b1eac2SEvan Yan return (apic_set_cpu(irqno, new_cpu, result)); 5322917a9c9Sschwartz } 5332917a9c9Sschwartz 5342917a9c9Sschwartz *result = ENXIO; 5352917a9c9Sschwartz 5362917a9c9Sschwartz if (irqps[0]->airq_mps_intr_index != MSI_INDEX) { 5372917a9c9Sschwartz mutex_exit(&airq_mutex); 5382917a9c9Sschwartz DDI_INTR_IMPLDBG((CE_CONT, "set_grp: intr not MSI\n")); 5392917a9c9Sschwartz goto set_grp_intr_done; 5402917a9c9Sschwartz } 5412917a9c9Sschwartz if ((num_vectors < 1) || ((num_vectors - 1) & vector)) { 5422917a9c9Sschwartz mutex_exit(&airq_mutex); 5432917a9c9Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 5442917a9c9Sschwartz "set_grp: base vec not part of a grp or not aligned: " 5452917a9c9Sschwartz "vec:0x%x, num_vec:0x%x\n", vector, num_vectors)); 5462917a9c9Sschwartz goto set_grp_intr_done; 5472917a9c9Sschwartz } 5482917a9c9Sschwartz DDI_INTR_IMPLDBG((CE_CONT, "set_grp: num intrs in grp: %d\n", 5492917a9c9Sschwartz num_vectors)); 5502917a9c9Sschwartz 5512917a9c9Sschwartz ASSERT((num_vectors + vector) < APIC_MAX_VECTOR); 5522917a9c9Sschwartz 5532917a9c9Sschwartz *result = EIO; 5542917a9c9Sschwartz 5552917a9c9Sschwartz /* 5562917a9c9Sschwartz * All IRQ entries in the table for the given device will be not 5572917a9c9Sschwartz * shared. Since they are not shared, the dip in the table will 5582917a9c9Sschwartz * be true to the device of interest. 5592917a9c9Sschwartz */ 5602917a9c9Sschwartz for (i = 1; i < num_vectors; i++) { 5612917a9c9Sschwartz irqps[i] = apic_irq_table[apic_vector_to_irq[vector + i]]; 5622917a9c9Sschwartz if (irqps[i] == NULL) { 5632917a9c9Sschwartz mutex_exit(&airq_mutex); 5642917a9c9Sschwartz goto set_grp_intr_done; 5652917a9c9Sschwartz } 5662917a9c9Sschwartz #ifdef DEBUG 5672917a9c9Sschwartz /* Sanity check: CPU and dip is the same for all entries. */ 5682917a9c9Sschwartz if ((irqps[i]->airq_dip != orig_dip) || 5692917a9c9Sschwartz (irqps[i]->airq_temp_cpu != orig_cpu)) { 5702917a9c9Sschwartz mutex_exit(&airq_mutex); 5712917a9c9Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 5722917a9c9Sschwartz "set_grp: cpu or dip for vec 0x%x difft than for " 5732917a9c9Sschwartz "vec 0x%x\n", vector, vector + i)); 5742917a9c9Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 5752917a9c9Sschwartz " cpu: %d vs %d, dip: 0x%p vs 0x%p\n", orig_cpu, 5762917a9c9Sschwartz irqps[i]->airq_temp_cpu, (void *)orig_dip, 5772917a9c9Sschwartz (void *)irqps[i]->airq_dip)); 5782917a9c9Sschwartz goto set_grp_intr_done; 5792917a9c9Sschwartz } 5802917a9c9Sschwartz #endif /* DEBUG */ 5812917a9c9Sschwartz } 5822917a9c9Sschwartz mutex_exit(&airq_mutex); 5832917a9c9Sschwartz 5842917a9c9Sschwartz cap_ptr = i_ddi_get_msi_msix_cap_ptr(orig_dip); 5852917a9c9Sschwartz handle = i_ddi_get_pci_config_handle(orig_dip); 5862917a9c9Sschwartz msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 5872917a9c9Sschwartz 5882917a9c9Sschwartz /* MSI Per vector masking is supported. */ 5892917a9c9Sschwartz if (msi_ctrl & PCI_MSI_PVM_MASK) { 5902917a9c9Sschwartz if (msi_ctrl & PCI_MSI_64BIT_MASK) 5912917a9c9Sschwartz msi_mask_off = cap_ptr + PCI_MSI_64BIT_MASKBITS; 5922917a9c9Sschwartz else 5932917a9c9Sschwartz msi_mask_off = cap_ptr + PCI_MSI_32BIT_MASK; 5942917a9c9Sschwartz msi_pvm = pci_config_get32(handle, msi_mask_off); 5952917a9c9Sschwartz pci_config_put32(handle, msi_mask_off, (uint32_t)-1); 5962917a9c9Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 5972917a9c9Sschwartz "set_grp: pvm supported. Mask set to 0x%x\n", 5982917a9c9Sschwartz pci_config_get32(handle, msi_mask_off))); 5992917a9c9Sschwartz } 6002917a9c9Sschwartz 6012917a9c9Sschwartz iflag = intr_clear(); 6022917a9c9Sschwartz lock_set(&apic_ioapic_lock); 6032917a9c9Sschwartz 6042917a9c9Sschwartz /* 6052917a9c9Sschwartz * Do the first rebind and check for errors. Apic_rebind_all returns 6062917a9c9Sschwartz * an error if the CPU is not accepting interrupts. If the first one 6072917a9c9Sschwartz * succeeds they all will. 6082917a9c9Sschwartz */ 6092917a9c9Sschwartz if (apic_rebind_all(irqps[0], new_cpu)) 6102917a9c9Sschwartz (void) apic_rebind_all(irqps[0], orig_cpu); 6112917a9c9Sschwartz else { 61209b1eac2SEvan Yan irqps[0]->airq_cpu = new_cpu; 61309b1eac2SEvan Yan 61409b1eac2SEvan Yan for (i = 1; i < num_vectors; i++) { 6152917a9c9Sschwartz (void) apic_rebind_all(irqps[i], new_cpu); 61609b1eac2SEvan Yan irqps[i]->airq_cpu = new_cpu; 61709b1eac2SEvan Yan } 6182917a9c9Sschwartz *result = 0; /* SUCCESS */ 6192917a9c9Sschwartz } 6202917a9c9Sschwartz 6212917a9c9Sschwartz lock_clear(&apic_ioapic_lock); 6222917a9c9Sschwartz intr_restore(iflag); 6232917a9c9Sschwartz 6242917a9c9Sschwartz /* Reenable vectors if per vector masking is supported. */ 6252917a9c9Sschwartz if (msi_ctrl & PCI_MSI_PVM_MASK) { 6262917a9c9Sschwartz pci_config_put32(handle, msi_mask_off, msi_pvm); 6272917a9c9Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 6282917a9c9Sschwartz "set_grp: pvm supported. Mask restored to 0x%x\n", 6292917a9c9Sschwartz pci_config_get32(handle, msi_mask_off))); 6302917a9c9Sschwartz } 6312917a9c9Sschwartz 6322917a9c9Sschwartz set_grp_intr_done: 6332917a9c9Sschwartz if (*result != 0) 6342917a9c9Sschwartz return (PSM_FAILURE); 6352917a9c9Sschwartz 6362917a9c9Sschwartz return (PSM_SUCCESS); 6372917a9c9Sschwartz } 6382917a9c9Sschwartz 639843e1988Sjohnlev int 6402917a9c9Sschwartz apic_get_vector_intr_info(int vecirq, apic_get_intr_t *intr_params_p) 6412917a9c9Sschwartz { 6422917a9c9Sschwartz struct autovec *av_dev; 6432917a9c9Sschwartz uchar_t irqno; 6442917a9c9Sschwartz int i; 6452917a9c9Sschwartz apic_irq_t *irq_p; 6462917a9c9Sschwartz 6472917a9c9Sschwartz /* Sanity check the vector/irq argument. */ 6482917a9c9Sschwartz ASSERT((vecirq >= 0) || (vecirq <= APIC_MAX_VECTOR)); 6492917a9c9Sschwartz 6502917a9c9Sschwartz mutex_enter(&airq_mutex); 6512917a9c9Sschwartz 6522917a9c9Sschwartz /* 6532917a9c9Sschwartz * Convert the vecirq arg to an irq using vector_to_irq table 6542917a9c9Sschwartz * if the arg is a vector. Pass thru if already an irq. 6552917a9c9Sschwartz */ 6562917a9c9Sschwartz if ((intr_params_p->avgi_req_flags & PSMGI_INTRBY_FLAGS) == 6572917a9c9Sschwartz PSMGI_INTRBY_VEC) 6582917a9c9Sschwartz irqno = apic_vector_to_irq[vecirq]; 6592917a9c9Sschwartz else 6602917a9c9Sschwartz irqno = vecirq; 6612917a9c9Sschwartz 6622917a9c9Sschwartz irq_p = apic_irq_table[irqno]; 6632917a9c9Sschwartz 6642917a9c9Sschwartz if ((irq_p == NULL) || 6654e30c628SEvan Yan ((irq_p->airq_mps_intr_index != RESERVE_INDEX) && 6664e30c628SEvan Yan ((irq_p->airq_temp_cpu == IRQ_UNBOUND) || 6674e30c628SEvan Yan (irq_p->airq_temp_cpu == IRQ_UNINIT)))) { 6682917a9c9Sschwartz mutex_exit(&airq_mutex); 6692917a9c9Sschwartz return (PSM_FAILURE); 6702917a9c9Sschwartz } 6712917a9c9Sschwartz 6722917a9c9Sschwartz if (intr_params_p->avgi_req_flags & PSMGI_REQ_CPUID) { 6732917a9c9Sschwartz 6742917a9c9Sschwartz /* Get the (temp) cpu from apic_irq table, indexed by irq. */ 6752917a9c9Sschwartz intr_params_p->avgi_cpu_id = irq_p->airq_temp_cpu; 6762917a9c9Sschwartz 6772917a9c9Sschwartz /* Return user bound info for intrd. */ 6782917a9c9Sschwartz if (intr_params_p->avgi_cpu_id & IRQ_USER_BOUND) { 6792917a9c9Sschwartz intr_params_p->avgi_cpu_id &= ~IRQ_USER_BOUND; 6802917a9c9Sschwartz intr_params_p->avgi_cpu_id |= PSMGI_CPU_USER_BOUND; 6812917a9c9Sschwartz } 6822917a9c9Sschwartz } 6832917a9c9Sschwartz 6842917a9c9Sschwartz if (intr_params_p->avgi_req_flags & PSMGI_REQ_VECTOR) 6852917a9c9Sschwartz intr_params_p->avgi_vector = irq_p->airq_vector; 6862917a9c9Sschwartz 6872917a9c9Sschwartz if (intr_params_p->avgi_req_flags & 6882917a9c9Sschwartz (PSMGI_REQ_NUM_DEVS | PSMGI_REQ_GET_DEVS)) 6892917a9c9Sschwartz /* Get number of devices from apic_irq table shared field. */ 6902917a9c9Sschwartz intr_params_p->avgi_num_devs = irq_p->airq_share; 6912917a9c9Sschwartz 6922917a9c9Sschwartz if (intr_params_p->avgi_req_flags & PSMGI_REQ_GET_DEVS) { 6932917a9c9Sschwartz 6942917a9c9Sschwartz intr_params_p->avgi_req_flags |= PSMGI_REQ_NUM_DEVS; 6952917a9c9Sschwartz 6962917a9c9Sschwartz /* Some devices have NULL dip. Don't count these. */ 6972917a9c9Sschwartz if (intr_params_p->avgi_num_devs > 0) { 6982917a9c9Sschwartz for (i = 0, av_dev = autovect[irqno].avh_link; 6992917a9c9Sschwartz av_dev; av_dev = av_dev->av_link) 7002917a9c9Sschwartz if (av_dev->av_vector && av_dev->av_dip) 7012917a9c9Sschwartz i++; 7022917a9c9Sschwartz intr_params_p->avgi_num_devs = 7032917a9c9Sschwartz MIN(intr_params_p->avgi_num_devs, i); 7042917a9c9Sschwartz } 7052917a9c9Sschwartz 7062917a9c9Sschwartz /* There are no viable dips to return. */ 7072917a9c9Sschwartz if (intr_params_p->avgi_num_devs == 0) 7082917a9c9Sschwartz intr_params_p->avgi_dip_list = NULL; 7092917a9c9Sschwartz 7102917a9c9Sschwartz else { /* Return list of dips */ 7112917a9c9Sschwartz 7122917a9c9Sschwartz /* Allocate space in array for that number of devs. */ 7132917a9c9Sschwartz intr_params_p->avgi_dip_list = kmem_zalloc( 7142917a9c9Sschwartz intr_params_p->avgi_num_devs * 7152917a9c9Sschwartz sizeof (dev_info_t *), 7162917a9c9Sschwartz KM_SLEEP); 7172917a9c9Sschwartz 7182917a9c9Sschwartz /* 7192917a9c9Sschwartz * Loop through the device list of the autovec table 7202917a9c9Sschwartz * filling in the dip array. 7212917a9c9Sschwartz * 7222917a9c9Sschwartz * Note that the autovect table may have some special 7232917a9c9Sschwartz * entries which contain NULL dips. These will be 7242917a9c9Sschwartz * ignored. 7252917a9c9Sschwartz */ 7262917a9c9Sschwartz for (i = 0, av_dev = autovect[irqno].avh_link; 7272917a9c9Sschwartz av_dev; av_dev = av_dev->av_link) 7282917a9c9Sschwartz if (av_dev->av_vector && av_dev->av_dip) 7292917a9c9Sschwartz intr_params_p->avgi_dip_list[i++] = 7302917a9c9Sschwartz av_dev->av_dip; 7312917a9c9Sschwartz } 7322917a9c9Sschwartz } 7332917a9c9Sschwartz 7342917a9c9Sschwartz mutex_exit(&airq_mutex); 7352917a9c9Sschwartz 7362917a9c9Sschwartz return (PSM_SUCCESS); 7372917a9c9Sschwartz } 7382917a9c9Sschwartz 739d12abe7cSanish /* 7407c478bd9Sstevel@tonic-gate * This function provides external interface to the nexus for all 7417c478bd9Sstevel@tonic-gate * functionalities related to the new DDI interrupt framework. 7427c478bd9Sstevel@tonic-gate * 7437c478bd9Sstevel@tonic-gate * Input: 7447c478bd9Sstevel@tonic-gate * dip - pointer to the dev_info structure of the requested device 7457c478bd9Sstevel@tonic-gate * hdlp - pointer to the internal interrupt handle structure for the 7467c478bd9Sstevel@tonic-gate * requested interrupt 7477c478bd9Sstevel@tonic-gate * intr_op - opcode for this call 7487c478bd9Sstevel@tonic-gate * result - pointer to the integer that will hold the result to be 7497c478bd9Sstevel@tonic-gate * passed back if return value is PSM_SUCCESS 7507c478bd9Sstevel@tonic-gate * 7517c478bd9Sstevel@tonic-gate * Output: 7527c478bd9Sstevel@tonic-gate * return value is either PSM_SUCCESS or PSM_FAILURE 7537c478bd9Sstevel@tonic-gate */ 7547c478bd9Sstevel@tonic-gate int 7557c478bd9Sstevel@tonic-gate apic_intr_ops(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, 7567c478bd9Sstevel@tonic-gate psm_intr_op_t intr_op, int *result) 7577c478bd9Sstevel@tonic-gate { 7582917a9c9Sschwartz int cap; 7597c478bd9Sstevel@tonic-gate int count_vec; 7607c478bd9Sstevel@tonic-gate int old_priority; 7617c478bd9Sstevel@tonic-gate int new_priority; 7622917a9c9Sschwartz int new_cpu; 7637c478bd9Sstevel@tonic-gate apic_irq_t *irqp; 7647c478bd9Sstevel@tonic-gate struct intrspec *ispec, intr_spec; 7657c478bd9Sstevel@tonic-gate 7667c478bd9Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_intr_ops: dip: %p hdlp: %p " 7677c478bd9Sstevel@tonic-gate "intr_op: %x\n", (void *)dip, (void *)hdlp, intr_op)); 7687c478bd9Sstevel@tonic-gate 7697c478bd9Sstevel@tonic-gate ispec = &intr_spec; 7707c478bd9Sstevel@tonic-gate ispec->intrspec_pri = hdlp->ih_pri; 7717c478bd9Sstevel@tonic-gate ispec->intrspec_vec = hdlp->ih_inum; 7727c478bd9Sstevel@tonic-gate ispec->intrspec_func = hdlp->ih_cb_func; 7737c478bd9Sstevel@tonic-gate 7747c478bd9Sstevel@tonic-gate switch (intr_op) { 7757c478bd9Sstevel@tonic-gate case PSM_INTR_OP_CHECK_MSI: 7767c478bd9Sstevel@tonic-gate /* 7777c478bd9Sstevel@tonic-gate * Check MSI/X is supported or not at APIC level and 7787c478bd9Sstevel@tonic-gate * masked off the MSI/X bits in hdlp->ih_type if not 7797c478bd9Sstevel@tonic-gate * supported before return. If MSI/X is supported, 7807c478bd9Sstevel@tonic-gate * leave the ih_type unchanged and return. 7817c478bd9Sstevel@tonic-gate * 7827c478bd9Sstevel@tonic-gate * hdlp->ih_type passed in from the nexus has all the 7837c478bd9Sstevel@tonic-gate * interrupt types supported by the device. 7847c478bd9Sstevel@tonic-gate */ 7857c478bd9Sstevel@tonic-gate if (apic_support_msi == 0) { 7867c478bd9Sstevel@tonic-gate /* 7877c478bd9Sstevel@tonic-gate * if apic_support_msi is not set, call 7887c478bd9Sstevel@tonic-gate * apic_check_msi_support() to check whether msi 7897c478bd9Sstevel@tonic-gate * is supported first 7907c478bd9Sstevel@tonic-gate */ 791685482d6Sjohnny if (apic_check_msi_support() == PSM_SUCCESS) 7927c478bd9Sstevel@tonic-gate apic_support_msi = 1; 7937c478bd9Sstevel@tonic-gate else 7947c478bd9Sstevel@tonic-gate apic_support_msi = -1; 7957c478bd9Sstevel@tonic-gate } 796a7639048Sjohnny if (apic_support_msi == 1) { 797a7639048Sjohnny if (apic_msix_enable) 7987c478bd9Sstevel@tonic-gate *result = hdlp->ih_type; 7997c478bd9Sstevel@tonic-gate else 800a7639048Sjohnny *result = hdlp->ih_type & ~DDI_INTR_TYPE_MSIX; 801a7639048Sjohnny } else 8027c478bd9Sstevel@tonic-gate *result = hdlp->ih_type & ~(DDI_INTR_TYPE_MSI | 8037c478bd9Sstevel@tonic-gate DDI_INTR_TYPE_MSIX); 8047c478bd9Sstevel@tonic-gate break; 8057c478bd9Sstevel@tonic-gate case PSM_INTR_OP_ALLOC_VECTORS: 806a7639048Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_MSI) 807a7639048Sjohnny *result = apic_alloc_msi_vectors(dip, hdlp->ih_inum, 808a7639048Sjohnny hdlp->ih_scratch1, hdlp->ih_pri, 809a7639048Sjohnny (int)(uintptr_t)hdlp->ih_scratch2); 810a7639048Sjohnny else 811a7639048Sjohnny *result = apic_alloc_msix_vectors(dip, hdlp->ih_inum, 812a7639048Sjohnny hdlp->ih_scratch1, hdlp->ih_pri, 8130ccf9e79Sjohnny (int)(uintptr_t)hdlp->ih_scratch2); 8147c478bd9Sstevel@tonic-gate break; 8157c478bd9Sstevel@tonic-gate case PSM_INTR_OP_FREE_VECTORS: 8167c478bd9Sstevel@tonic-gate apic_free_vectors(dip, hdlp->ih_inum, hdlp->ih_scratch1, 8177c478bd9Sstevel@tonic-gate hdlp->ih_pri, hdlp->ih_type); 8187c478bd9Sstevel@tonic-gate break; 8197c478bd9Sstevel@tonic-gate case PSM_INTR_OP_NAVAIL_VECTORS: 8207c478bd9Sstevel@tonic-gate *result = apic_navail_vector(dip, hdlp->ih_pri); 8217c478bd9Sstevel@tonic-gate break; 8227c478bd9Sstevel@tonic-gate case PSM_INTR_OP_XLATE_VECTOR: 8237a364d25Sschwartz ispec = ((ihdl_plat_t *)hdlp->ih_private)->ip_ispecp; 8247c478bd9Sstevel@tonic-gate *result = apic_introp_xlate(dip, ispec, hdlp->ih_type); 82586a9c507SGuoli Shu if (*result == -1) 82686a9c507SGuoli Shu return (PSM_FAILURE); 8277c478bd9Sstevel@tonic-gate break; 8287c478bd9Sstevel@tonic-gate case PSM_INTR_OP_GET_PENDING: 8297c478bd9Sstevel@tonic-gate if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 8307c478bd9Sstevel@tonic-gate return (PSM_FAILURE); 8317c478bd9Sstevel@tonic-gate *result = apic_get_pending(irqp, hdlp->ih_type); 8327c478bd9Sstevel@tonic-gate break; 8337c478bd9Sstevel@tonic-gate case PSM_INTR_OP_CLEAR_MASK: 8347c478bd9Sstevel@tonic-gate if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 8357c478bd9Sstevel@tonic-gate return (PSM_FAILURE); 8367c478bd9Sstevel@tonic-gate irqp = apic_find_irq(dip, ispec, hdlp->ih_type); 8377c478bd9Sstevel@tonic-gate if (irqp == NULL) 8387c478bd9Sstevel@tonic-gate return (PSM_FAILURE); 8397c478bd9Sstevel@tonic-gate apic_clear_mask(irqp); 8407c478bd9Sstevel@tonic-gate break; 8417c478bd9Sstevel@tonic-gate case PSM_INTR_OP_SET_MASK: 8427c478bd9Sstevel@tonic-gate if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 8437c478bd9Sstevel@tonic-gate return (PSM_FAILURE); 8447c478bd9Sstevel@tonic-gate if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 8457c478bd9Sstevel@tonic-gate return (PSM_FAILURE); 8467c478bd9Sstevel@tonic-gate apic_set_mask(irqp); 8477c478bd9Sstevel@tonic-gate break; 8487c478bd9Sstevel@tonic-gate case PSM_INTR_OP_GET_CAP: 8497c478bd9Sstevel@tonic-gate cap = DDI_INTR_FLAG_PENDING; 8507c478bd9Sstevel@tonic-gate if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 8517c478bd9Sstevel@tonic-gate cap |= DDI_INTR_FLAG_MASKABLE; 8527c478bd9Sstevel@tonic-gate *result = cap; 8537c478bd9Sstevel@tonic-gate break; 8547c478bd9Sstevel@tonic-gate case PSM_INTR_OP_GET_SHARED: 8557c478bd9Sstevel@tonic-gate if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 8567c478bd9Sstevel@tonic-gate return (PSM_FAILURE); 85796f82fefSSophia Li ispec = ((ihdl_plat_t *)hdlp->ih_private)->ip_ispecp; 8587c478bd9Sstevel@tonic-gate if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 8597c478bd9Sstevel@tonic-gate return (PSM_FAILURE); 86096f82fefSSophia Li *result = (irqp->airq_share > 1) ? 1: 0; 8617c478bd9Sstevel@tonic-gate break; 8627c478bd9Sstevel@tonic-gate case PSM_INTR_OP_SET_PRI: 8637c478bd9Sstevel@tonic-gate old_priority = hdlp->ih_pri; /* save old value */ 8647c478bd9Sstevel@tonic-gate new_priority = *(int *)result; /* try the new value */ 8657c478bd9Sstevel@tonic-gate 86696f82fefSSophia Li if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) { 86796f82fefSSophia Li return (PSM_SUCCESS); 86896f82fefSSophia Li } 8697c478bd9Sstevel@tonic-gate 8707c478bd9Sstevel@tonic-gate /* Now allocate the vectors */ 87196f82fefSSophia Li if (hdlp->ih_type == DDI_INTR_TYPE_MSI) { 87296f82fefSSophia Li /* SET_PRI does not support the case of multiple MSI */ 87396f82fefSSophia Li if (i_ddi_intr_get_current_nintrs(hdlp->ih_dip) > 1) 87496f82fefSSophia Li return (PSM_FAILURE); 87596f82fefSSophia Li 876a7639048Sjohnny count_vec = apic_alloc_msi_vectors(dip, hdlp->ih_inum, 87796f82fefSSophia Li 1, new_priority, 878a7639048Sjohnny DDI_INTR_ALLOC_STRICT); 87996f82fefSSophia Li } else { 880a7639048Sjohnny count_vec = apic_alloc_msix_vectors(dip, hdlp->ih_inum, 88196f82fefSSophia Li 1, new_priority, 8820ccf9e79Sjohnny DDI_INTR_ALLOC_STRICT); 88396f82fefSSophia Li } 8847c478bd9Sstevel@tonic-gate 885ae115bc7Smrj /* Did we get new vectors? */ 8860ccf9e79Sjohnny if (!count_vec) 8877c478bd9Sstevel@tonic-gate return (PSM_FAILURE); 8887c478bd9Sstevel@tonic-gate 8897c478bd9Sstevel@tonic-gate /* Finally, free the previously allocated vectors */ 8907c478bd9Sstevel@tonic-gate apic_free_vectors(dip, hdlp->ih_inum, count_vec, 8917c478bd9Sstevel@tonic-gate old_priority, hdlp->ih_type); 8927c478bd9Sstevel@tonic-gate break; 8937a364d25Sschwartz case PSM_INTR_OP_SET_CPU: 8942917a9c9Sschwartz case PSM_INTR_OP_GRP_SET_CPU: 8957a364d25Sschwartz /* 8967a364d25Sschwartz * The interrupt handle given here has been allocated 8977a364d25Sschwartz * specifically for this command, and ih_private carries 8987a364d25Sschwartz * a CPU value. 8997a364d25Sschwartz */ 9002917a9c9Sschwartz new_cpu = (int)(intptr_t)hdlp->ih_private; 9012917a9c9Sschwartz if (!apic_cpu_in_range(new_cpu)) { 9022917a9c9Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 9032917a9c9Sschwartz "[grp_]set_cpu: cpu out of range: %d\n", new_cpu)); 9047a364d25Sschwartz *result = EINVAL; 9057a364d25Sschwartz return (PSM_FAILURE); 9067a364d25Sschwartz } 90709b1eac2SEvan Yan if (hdlp->ih_vector > APIC_MAX_VECTOR) { 90809b1eac2SEvan Yan DDI_INTR_IMPLDBG((CE_CONT, 90909b1eac2SEvan Yan "[grp_]set_cpu: vector out of range: %d\n", 91009b1eac2SEvan Yan hdlp->ih_vector)); 91109b1eac2SEvan Yan *result = EINVAL; 91209b1eac2SEvan Yan return (PSM_FAILURE); 91309b1eac2SEvan Yan } 9147ff178cdSJimmy Vetayases if ((hdlp->ih_flags & PSMGI_INTRBY_FLAGS) == PSMGI_INTRBY_VEC) 91509b1eac2SEvan Yan hdlp->ih_vector = apic_vector_to_irq[hdlp->ih_vector]; 9162917a9c9Sschwartz if (intr_op == PSM_INTR_OP_SET_CPU) { 9172917a9c9Sschwartz if (apic_set_cpu(hdlp->ih_vector, new_cpu, result) != 9182917a9c9Sschwartz PSM_SUCCESS) 9192917a9c9Sschwartz return (PSM_FAILURE); 9202917a9c9Sschwartz } else { 9212917a9c9Sschwartz if (apic_grp_set_cpu(hdlp->ih_vector, new_cpu, 9222917a9c9Sschwartz result) != PSM_SUCCESS) 9237a364d25Sschwartz return (PSM_FAILURE); 9247a364d25Sschwartz } 9257a364d25Sschwartz break; 9267a364d25Sschwartz case PSM_INTR_OP_GET_INTR: 9277a364d25Sschwartz /* 9287a364d25Sschwartz * The interrupt handle given here has been allocated 9297a364d25Sschwartz * specifically for this command, and ih_private carries 9307a364d25Sschwartz * a pointer to a apic_get_intr_t. 9317a364d25Sschwartz */ 9327a364d25Sschwartz if (apic_get_vector_intr_info( 9337a364d25Sschwartz hdlp->ih_vector, hdlp->ih_private) != PSM_SUCCESS) 9347a364d25Sschwartz return (PSM_FAILURE); 9357a364d25Sschwartz break; 9362917a9c9Sschwartz case PSM_INTR_OP_APIC_TYPE: 9377ff178cdSJimmy Vetayases ((apic_get_type_t *)(hdlp->ih_private))->avgi_type = 9387ff178cdSJimmy Vetayases apic_get_apic_type(); 9397ff178cdSJimmy Vetayases ((apic_get_type_t *)(hdlp->ih_private))->avgi_num_intr = 9407ff178cdSJimmy Vetayases APIC_MAX_VECTOR; 9417ff178cdSJimmy Vetayases ((apic_get_type_t *)(hdlp->ih_private))->avgi_num_cpu = 9427ff178cdSJimmy Vetayases boot_ncpus; 9432917a9c9Sschwartz hdlp->ih_ver = apic_get_apic_version(); 9442917a9c9Sschwartz break; 9457c478bd9Sstevel@tonic-gate case PSM_INTR_OP_SET_CAP: 9467c478bd9Sstevel@tonic-gate default: 9477c478bd9Sstevel@tonic-gate return (PSM_FAILURE); 9487c478bd9Sstevel@tonic-gate } 9497c478bd9Sstevel@tonic-gate return (PSM_SUCCESS); 9507c478bd9Sstevel@tonic-gate } 951