170025d76Sjohnny /* 270025d76Sjohnny * CDDL HEADER START 370025d76Sjohnny * 470025d76Sjohnny * The contents of this file are subject to the terms of the 5102cb92eSjohnny * Common Development and Distribution License (the "License"). 6102cb92eSjohnny * You may not use this file except in compliance with the License. 770025d76Sjohnny * 870025d76Sjohnny * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 970025d76Sjohnny * or http://www.opensolaris.org/os/licensing. 1070025d76Sjohnny * See the License for the specific language governing permissions 1170025d76Sjohnny * and limitations under the License. 1270025d76Sjohnny * 1370025d76Sjohnny * When distributing Covered Code, include this CDDL HEADER in each 1470025d76Sjohnny * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1570025d76Sjohnny * If applicable, add the following below this CDDL HEADER, with the 1670025d76Sjohnny * fields enclosed by brackets "[]" replaced with your own identifying 1770025d76Sjohnny * information: Portions Copyright [yyyy] [name of copyright owner] 1870025d76Sjohnny * 1970025d76Sjohnny * CDDL HEADER END 2070025d76Sjohnny */ 2170025d76Sjohnny 2270025d76Sjohnny /* 23102cb92eSjohnny * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 2470025d76Sjohnny * Use is subject to license terms. 2570025d76Sjohnny */ 2670025d76Sjohnny 2770025d76Sjohnny #pragma ident "%Z%%M% %I% %E% SMI" 2870025d76Sjohnny 2970025d76Sjohnny /* 3070025d76Sjohnny * File that has code which is common between pci(7d) and npe(7d) 3170025d76Sjohnny * It shares the following: 3270025d76Sjohnny * - interrupt code 3370025d76Sjohnny * - pci_tools ioctl code 3470025d76Sjohnny * - name_child code 3570025d76Sjohnny * - set_parent_private_data code 3670025d76Sjohnny */ 3770025d76Sjohnny 3870025d76Sjohnny #include <sys/conf.h> 3970025d76Sjohnny #include <sys/pci.h> 4070025d76Sjohnny #include <sys/sunndi.h> 417a364d25Sschwartz #include <sys/mach_intr.h> 4270025d76Sjohnny #include <sys/hotplug/pci/pcihp.h> 4370025d76Sjohnny #include <sys/pci_intr_lib.h> 4470025d76Sjohnny #include <sys/psm.h> 4570025d76Sjohnny #include <sys/policy.h> 4670025d76Sjohnny #include <sys/sysmacros.h> 477a364d25Sschwartz #include <sys/clock.h> 487a364d25Sschwartz #include <io/pcplusmp/apic.h> 4970025d76Sjohnny #include <sys/pci_tools.h> 5070025d76Sjohnny #include <io/pci/pci_var.h> 5170025d76Sjohnny #include <io/pci/pci_tools_ext.h> 5270025d76Sjohnny #include <io/pci/pci_common.h> 53649d4cceSanish #include <sys/pci_cfgspace.h> 54649d4cceSanish #include <sys/pci_impl.h> 5570025d76Sjohnny 5670025d76Sjohnny /* 5770025d76Sjohnny * Function prototypes 5870025d76Sjohnny */ 5970025d76Sjohnny static int pci_get_priority(dev_info_t *, ddi_intr_handle_impl_t *, int *); 6070025d76Sjohnny static int pci_get_nintrs(dev_info_t *, int, int *); 6170025d76Sjohnny static int pci_enable_intr(dev_info_t *, dev_info_t *, 6270025d76Sjohnny ddi_intr_handle_impl_t *, uint32_t); 6370025d76Sjohnny static void pci_disable_intr(dev_info_t *, dev_info_t *, 6470025d76Sjohnny ddi_intr_handle_impl_t *, uint32_t); 6570025d76Sjohnny 6670025d76Sjohnny /* Extern decalration for pcplusmp module */ 6770025d76Sjohnny extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *, 6870025d76Sjohnny psm_intr_op_t, int *); 6970025d76Sjohnny 7070025d76Sjohnny 7170025d76Sjohnny /* 7270025d76Sjohnny * pci_name_child: 7370025d76Sjohnny * 7470025d76Sjohnny * Assign the address portion of the node name 7570025d76Sjohnny */ 7670025d76Sjohnny int 7770025d76Sjohnny pci_common_name_child(dev_info_t *child, char *name, int namelen) 7870025d76Sjohnny { 7970025d76Sjohnny int dev, func, length; 8070025d76Sjohnny char **unit_addr; 8170025d76Sjohnny uint_t n; 8270025d76Sjohnny pci_regspec_t *pci_rp; 8370025d76Sjohnny 8470025d76Sjohnny if (ndi_dev_is_persistent_node(child) == 0) { 8570025d76Sjohnny /* 8670025d76Sjohnny * For .conf node, use "unit-address" property 8770025d76Sjohnny */ 8870025d76Sjohnny if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 8970025d76Sjohnny DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) != 9070025d76Sjohnny DDI_PROP_SUCCESS) { 9170025d76Sjohnny cmn_err(CE_WARN, "cannot find unit-address in %s.conf", 9270025d76Sjohnny ddi_get_name(child)); 9370025d76Sjohnny return (DDI_FAILURE); 9470025d76Sjohnny } 9570025d76Sjohnny if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 9670025d76Sjohnny cmn_err(CE_WARN, "unit-address property in %s.conf" 9770025d76Sjohnny " not well-formed", ddi_get_name(child)); 9870025d76Sjohnny ddi_prop_free(unit_addr); 9970025d76Sjohnny return (DDI_FAILURE); 10070025d76Sjohnny } 10170025d76Sjohnny (void) snprintf(name, namelen, "%s", *unit_addr); 10270025d76Sjohnny ddi_prop_free(unit_addr); 10370025d76Sjohnny return (DDI_SUCCESS); 10470025d76Sjohnny } 10570025d76Sjohnny 10670025d76Sjohnny if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 10770025d76Sjohnny "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) { 10870025d76Sjohnny cmn_err(CE_WARN, "cannot find reg property in %s", 10970025d76Sjohnny ddi_get_name(child)); 11070025d76Sjohnny return (DDI_FAILURE); 11170025d76Sjohnny } 11270025d76Sjohnny 11370025d76Sjohnny /* copy the device identifications */ 11470025d76Sjohnny dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 11570025d76Sjohnny func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); 11670025d76Sjohnny 11770025d76Sjohnny /* 11870025d76Sjohnny * free the memory allocated by ddi_prop_lookup_int_array 11970025d76Sjohnny */ 12070025d76Sjohnny ddi_prop_free(pci_rp); 12170025d76Sjohnny 12270025d76Sjohnny if (func != 0) { 12370025d76Sjohnny (void) snprintf(name, namelen, "%x,%x", dev, func); 12470025d76Sjohnny } else { 12570025d76Sjohnny (void) snprintf(name, namelen, "%x", dev); 12670025d76Sjohnny } 12770025d76Sjohnny 12870025d76Sjohnny return (DDI_SUCCESS); 12970025d76Sjohnny } 13070025d76Sjohnny 13170025d76Sjohnny /* 13270025d76Sjohnny * Interrupt related code: 13370025d76Sjohnny * 13470025d76Sjohnny * The following busop is common to npe and pci drivers 13570025d76Sjohnny * bus_introp 13670025d76Sjohnny */ 13770025d76Sjohnny 13870025d76Sjohnny /* 13970025d76Sjohnny * Create the ddi_parent_private_data for a pseudo child. 14070025d76Sjohnny */ 14170025d76Sjohnny void 14270025d76Sjohnny pci_common_set_parent_private_data(dev_info_t *dip) 14370025d76Sjohnny { 14470025d76Sjohnny struct ddi_parent_private_data *pdptr; 14570025d76Sjohnny 14670025d76Sjohnny pdptr = (struct ddi_parent_private_data *)kmem_zalloc( 14770025d76Sjohnny (sizeof (struct ddi_parent_private_data) + 14870025d76Sjohnny sizeof (struct intrspec)), KM_SLEEP); 14970025d76Sjohnny pdptr->par_intr = (struct intrspec *)(pdptr + 1); 15070025d76Sjohnny pdptr->par_nintr = 1; 15170025d76Sjohnny ddi_set_parent_data(dip, pdptr); 15270025d76Sjohnny } 15370025d76Sjohnny 15470025d76Sjohnny /* 15570025d76Sjohnny * pci_get_priority: 15670025d76Sjohnny * Figure out the priority of the device 15770025d76Sjohnny */ 15870025d76Sjohnny static int 15970025d76Sjohnny pci_get_priority(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, int *pri) 16070025d76Sjohnny { 16170025d76Sjohnny struct intrspec *ispec; 16270025d76Sjohnny 16370025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_get_priority: dip = 0x%p, hdlp = %p\n", 16470025d76Sjohnny (void *)dip, (void *)hdlp)); 16570025d76Sjohnny 16670025d76Sjohnny if ((ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip, 16770025d76Sjohnny hdlp->ih_inum)) == NULL) { 16870025d76Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) { 16970025d76Sjohnny int class = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 17070025d76Sjohnny DDI_PROP_DONTPASS, "class-code", -1); 17170025d76Sjohnny 17270025d76Sjohnny *pri = (class == -1) ? 1 : pci_devclass_to_ipl(class); 17370025d76Sjohnny pci_common_set_parent_private_data(hdlp->ih_dip); 17470025d76Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip, 17570025d76Sjohnny hdlp->ih_inum); 17670025d76Sjohnny return (DDI_SUCCESS); 17770025d76Sjohnny } 17870025d76Sjohnny return (DDI_FAILURE); 17970025d76Sjohnny } 18070025d76Sjohnny 18170025d76Sjohnny *pri = ispec->intrspec_pri; 18270025d76Sjohnny return (DDI_SUCCESS); 18370025d76Sjohnny } 18470025d76Sjohnny 18570025d76Sjohnny 18670025d76Sjohnny /* 18770025d76Sjohnny * pci_get_nintrs: 18870025d76Sjohnny * Figure out how many interrupts the device supports 18970025d76Sjohnny */ 19070025d76Sjohnny static int 19170025d76Sjohnny pci_get_nintrs(dev_info_t *dip, int type, int *nintrs) 19270025d76Sjohnny { 19370025d76Sjohnny int ret; 19470025d76Sjohnny 19570025d76Sjohnny *nintrs = 0; 19670025d76Sjohnny 19770025d76Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(type)) 19870025d76Sjohnny ret = pci_msi_get_nintrs(dip, type, nintrs); 19970025d76Sjohnny else { 20070025d76Sjohnny ret = DDI_FAILURE; 20170025d76Sjohnny if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 20270025d76Sjohnny "interrupts", -1) != -1) { 20370025d76Sjohnny *nintrs = 1; 20470025d76Sjohnny ret = DDI_SUCCESS; 20570025d76Sjohnny } 20670025d76Sjohnny } 20770025d76Sjohnny 20870025d76Sjohnny return (ret); 20970025d76Sjohnny } 21070025d76Sjohnny 21170025d76Sjohnny static int pcie_pci_intr_pri_counter = 0; 21270025d76Sjohnny 21370025d76Sjohnny /* 21470025d76Sjohnny * pci_common_intr_ops: bus_intr_op() function for interrupt support 21570025d76Sjohnny */ 21670025d76Sjohnny int 21770025d76Sjohnny pci_common_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, 21870025d76Sjohnny ddi_intr_handle_impl_t *hdlp, void *result) 21970025d76Sjohnny { 22070025d76Sjohnny int priority = 0; 22170025d76Sjohnny int psm_status = 0; 22270025d76Sjohnny int pci_status = 0; 22370025d76Sjohnny int pci_rval, psm_rval = PSM_FAILURE; 22470025d76Sjohnny int types = 0; 22570025d76Sjohnny int pciepci = 0; 226102cb92eSjohnny int i, j, count; 22770025d76Sjohnny int behavior; 228d12abe7cSanish int cap_ptr; 22970025d76Sjohnny ddi_intrspec_t isp; 23070025d76Sjohnny struct intrspec *ispec; 23170025d76Sjohnny ddi_intr_handle_impl_t tmp_hdl; 23270025d76Sjohnny ddi_intr_msix_t *msix_p; 233e1d9f4e6Sschwartz ihdl_plat_t *ihdl_plat_datap; 234102cb92eSjohnny ddi_intr_handle_t *h_array; 235d12abe7cSanish ddi_acc_handle_t handle; 23670025d76Sjohnny 23770025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, 23870025d76Sjohnny "pci_common_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n", 23970025d76Sjohnny (void *)pdip, (void *)rdip, intr_op, (void *)hdlp)); 24070025d76Sjohnny 24170025d76Sjohnny /* Process the request */ 24270025d76Sjohnny switch (intr_op) { 24370025d76Sjohnny case DDI_INTROP_SUPPORTED_TYPES: 24470025d76Sjohnny /* Fixed supported by default */ 24570025d76Sjohnny *(int *)result = DDI_INTR_TYPE_FIXED; 24670025d76Sjohnny 24770025d76Sjohnny /* Figure out if MSI or MSI-X is supported? */ 24870025d76Sjohnny if (pci_msi_get_supported_type(rdip, &types) != DDI_SUCCESS) 24970025d76Sjohnny return (DDI_SUCCESS); 25070025d76Sjohnny 25170025d76Sjohnny if (psm_intr_ops != NULL) { 25220036fe5Segillett /* 25320036fe5Segillett * Only support MSI for now, OR it in 25420036fe5Segillett */ 25520036fe5Segillett *(int *)result |= (types & DDI_INTR_TYPE_MSI); 25670025d76Sjohnny 25770025d76Sjohnny tmp_hdl.ih_type = *(int *)result; 25870025d76Sjohnny (void) (*psm_intr_ops)(rdip, &tmp_hdl, 25970025d76Sjohnny PSM_INTR_OP_CHECK_MSI, result); 26070025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 26170025d76Sjohnny "rdip: 0x%p supported types: 0x%x\n", (void *)rdip, 26270025d76Sjohnny *(int *)result)); 26370025d76Sjohnny } 26470025d76Sjohnny break; 26570025d76Sjohnny case DDI_INTROP_NINTRS: 26670025d76Sjohnny if (pci_get_nintrs(rdip, hdlp->ih_type, result) != DDI_SUCCESS) 26770025d76Sjohnny return (DDI_FAILURE); 26870025d76Sjohnny break; 26970025d76Sjohnny case DDI_INTROP_ALLOC: 27070025d76Sjohnny /* 27170025d76Sjohnny * MSI or MSIX (figure out number of vectors available) 27270025d76Sjohnny * FIXED interrupts: just return available interrupts 27370025d76Sjohnny */ 27470025d76Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && 27570025d76Sjohnny (psm_intr_ops != NULL) && 27670025d76Sjohnny (pci_get_priority(rdip, hdlp, &priority) == DDI_SUCCESS)) { 27770025d76Sjohnny /* 27870025d76Sjohnny * Following check is a special case for 'pcie_pci'. 27970025d76Sjohnny * This makes sure vectors with the right priority 28070025d76Sjohnny * are allocated for pcie_pci during ALLOC time. 28170025d76Sjohnny */ 28270025d76Sjohnny if (strcmp(ddi_driver_name(rdip), "pcie_pci") == 0) { 28370025d76Sjohnny hdlp->ih_pri = 28470025d76Sjohnny (pcie_pci_intr_pri_counter % 2) ? 4 : 7; 28570025d76Sjohnny pciepci = 1; 28670025d76Sjohnny } else 28770025d76Sjohnny hdlp->ih_pri = priority; 28880ab886dSwesolows behavior = (int)(uintptr_t)hdlp->ih_scratch2; 289d12abe7cSanish 290d12abe7cSanish /* 291d12abe7cSanish * Cache in the config handle and cap_ptr 292d12abe7cSanish */ 293d12abe7cSanish if (i_ddi_get_pci_config_handle(rdip) == NULL) { 294d12abe7cSanish if (pci_config_setup(rdip, &handle) != 295d12abe7cSanish DDI_SUCCESS) 296d12abe7cSanish return (DDI_FAILURE); 297d12abe7cSanish i_ddi_set_pci_config_handle(rdip, handle); 298d12abe7cSanish } 299d12abe7cSanish 300d12abe7cSanish if (i_ddi_get_msi_msix_cap_ptr(rdip) == 0) { 301d12abe7cSanish char *prop = 302d12abe7cSanish (hdlp->ih_type == DDI_INTR_TYPE_MSI) ? 303d12abe7cSanish "pci-msi-capid-pointer" : 304d12abe7cSanish "pci-msix-capid-pointer"; 305d12abe7cSanish 306d12abe7cSanish cap_ptr = ddi_prop_get_int(DDI_DEV_T_ANY, rdip, 307d12abe7cSanish DDI_PROP_DONTPASS, prop, 308d12abe7cSanish PCI_CAP_NEXT_PTR_NULL); 309d12abe7cSanish i_ddi_set_msi_msix_cap_ptr(rdip, cap_ptr); 310d12abe7cSanish } 311d12abe7cSanish 312d12abe7cSanish 31370025d76Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 31470025d76Sjohnny PSM_INTR_OP_ALLOC_VECTORS, result); 31570025d76Sjohnny 31670025d76Sjohnny /* verify behavior flag and take appropriate action */ 31770025d76Sjohnny if ((behavior == DDI_INTR_ALLOC_STRICT) && 31870025d76Sjohnny (*(int *)result < hdlp->ih_scratch1)) { 31970025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, 32070025d76Sjohnny "pci_common_intr_ops: behavior %x, " 32170025d76Sjohnny "couldn't get enough intrs\n", behavior)); 32270025d76Sjohnny hdlp->ih_scratch1 = *(int *)result; 32370025d76Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 32470025d76Sjohnny PSM_INTR_OP_FREE_VECTORS, NULL); 32570025d76Sjohnny return (DDI_EAGAIN); 32670025d76Sjohnny } 32770025d76Sjohnny 32870025d76Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) { 32970025d76Sjohnny if (!(msix_p = i_ddi_get_msix(hdlp->ih_dip))) { 33070025d76Sjohnny msix_p = pci_msix_init(hdlp->ih_dip); 33170025d76Sjohnny if (msix_p) 33270025d76Sjohnny i_ddi_set_msix(hdlp->ih_dip, 33370025d76Sjohnny msix_p); 33470025d76Sjohnny } 33570025d76Sjohnny } 33670025d76Sjohnny 33770025d76Sjohnny if (pciepci) { 33870025d76Sjohnny /* update priority in ispec */ 33970025d76Sjohnny isp = pci_intx_get_ispec(pdip, rdip, 34070025d76Sjohnny (int)hdlp->ih_inum); 34170025d76Sjohnny ispec = (struct intrspec *)isp; 34270025d76Sjohnny if (ispec) 34370025d76Sjohnny ispec->intrspec_pri = hdlp->ih_pri; 34470025d76Sjohnny ++pcie_pci_intr_pri_counter; 34570025d76Sjohnny } 34670025d76Sjohnny 34770025d76Sjohnny } else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) { 34870025d76Sjohnny /* Figure out if this device supports MASKING */ 34970025d76Sjohnny pci_rval = pci_intx_get_cap(rdip, &pci_status); 35070025d76Sjohnny if (pci_rval == DDI_SUCCESS && pci_status) 35170025d76Sjohnny hdlp->ih_cap |= pci_status; 35270025d76Sjohnny *(int *)result = 1; /* DDI_INTR_TYPE_FIXED */ 35370025d76Sjohnny } else 35470025d76Sjohnny return (DDI_FAILURE); 35570025d76Sjohnny break; 35670025d76Sjohnny case DDI_INTROP_FREE: 35770025d76Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && 35870025d76Sjohnny (psm_intr_ops != NULL)) { 359*68565200Sanish if (i_ddi_intr_get_current_nintrs(hdlp->ih_dip) - 1 == 360*68565200Sanish 0) { 361d12abe7cSanish if (handle = i_ddi_get_pci_config_handle( 362d12abe7cSanish rdip)) { 363d12abe7cSanish (void) pci_config_teardown(&handle); 364d12abe7cSanish i_ddi_set_pci_config_handle(rdip, NULL); 365d12abe7cSanish } 366d12abe7cSanish if (cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip)) 367d12abe7cSanish i_ddi_set_msi_msix_cap_ptr(rdip, 0); 368d12abe7cSanish } 369d12abe7cSanish 37070025d76Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 37170025d76Sjohnny PSM_INTR_OP_FREE_VECTORS, NULL); 37270025d76Sjohnny 37370025d76Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) { 37470025d76Sjohnny msix_p = i_ddi_get_msix(hdlp->ih_dip); 37570025d76Sjohnny if (msix_p && 376*68565200Sanish (i_ddi_intr_get_current_nintrs( 377*68565200Sanish hdlp->ih_dip) - 1) == 0) { 37870025d76Sjohnny pci_msix_fini(msix_p); 37970025d76Sjohnny i_ddi_set_msix(hdlp->ih_dip, NULL); 38070025d76Sjohnny } 38170025d76Sjohnny } 38270025d76Sjohnny } 38370025d76Sjohnny break; 38470025d76Sjohnny case DDI_INTROP_GETPRI: 38570025d76Sjohnny /* Get the priority */ 38670025d76Sjohnny if (pci_get_priority(rdip, hdlp, &priority) != DDI_SUCCESS) 38770025d76Sjohnny return (DDI_FAILURE); 38870025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 38970025d76Sjohnny "priority = 0x%x\n", priority)); 39070025d76Sjohnny *(int *)result = priority; 39170025d76Sjohnny break; 39270025d76Sjohnny case DDI_INTROP_SETPRI: 39370025d76Sjohnny /* Validate the interrupt priority passed */ 39470025d76Sjohnny if (*(int *)result > LOCK_LEVEL) 39570025d76Sjohnny return (DDI_FAILURE); 39670025d76Sjohnny 39770025d76Sjohnny /* Ensure that PSM is all initialized */ 39870025d76Sjohnny if (psm_intr_ops == NULL) 39970025d76Sjohnny return (DDI_FAILURE); 40070025d76Sjohnny 40170025d76Sjohnny /* Change the priority */ 40270025d76Sjohnny if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_PRI, result) == 40370025d76Sjohnny PSM_FAILURE) 40470025d76Sjohnny return (DDI_FAILURE); 40570025d76Sjohnny 40670025d76Sjohnny /* update ispec */ 40770025d76Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 40870025d76Sjohnny ispec = (struct intrspec *)isp; 40970025d76Sjohnny if (ispec) 41070025d76Sjohnny ispec->intrspec_pri = *(int *)result; 41170025d76Sjohnny break; 41270025d76Sjohnny case DDI_INTROP_ADDISR: 41370025d76Sjohnny /* update ispec */ 41470025d76Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 41570025d76Sjohnny ispec = (struct intrspec *)isp; 416e1d9f4e6Sschwartz if (ispec) { 41770025d76Sjohnny ispec->intrspec_func = hdlp->ih_cb_func; 418e1d9f4e6Sschwartz ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 419e1d9f4e6Sschwartz pci_kstat_create(&ihdl_plat_datap->ip_ksp, pdip, hdlp); 420e1d9f4e6Sschwartz } 42170025d76Sjohnny break; 42270025d76Sjohnny case DDI_INTROP_REMISR: 42370025d76Sjohnny /* Get the interrupt structure pointer */ 42470025d76Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 42570025d76Sjohnny ispec = (struct intrspec *)isp; 426e1d9f4e6Sschwartz if (ispec) { 42770025d76Sjohnny ispec->intrspec_func = (uint_t (*)()) 0; 428e1d9f4e6Sschwartz ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 429e1d9f4e6Sschwartz if (ihdl_plat_datap->ip_ksp != NULL) 430e1d9f4e6Sschwartz pci_kstat_delete(ihdl_plat_datap->ip_ksp); 431e1d9f4e6Sschwartz } 43270025d76Sjohnny break; 43370025d76Sjohnny case DDI_INTROP_GETCAP: 43470025d76Sjohnny /* 43570025d76Sjohnny * First check the config space and/or 43670025d76Sjohnny * MSI capability register(s) 43770025d76Sjohnny */ 43870025d76Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 43970025d76Sjohnny pci_rval = pci_msi_get_cap(rdip, hdlp->ih_type, 44070025d76Sjohnny &pci_status); 44170025d76Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 44270025d76Sjohnny pci_rval = pci_intx_get_cap(rdip, &pci_status); 44370025d76Sjohnny 44470025d76Sjohnny /* next check with pcplusmp */ 44570025d76Sjohnny if (psm_intr_ops != NULL) 44670025d76Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 44770025d76Sjohnny PSM_INTR_OP_GET_CAP, &psm_status); 44870025d76Sjohnny 44970025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned psm_rval = %x, " 45070025d76Sjohnny "psm_status = %x, pci_rval = %x, pci_status = %x\n", 45170025d76Sjohnny psm_rval, psm_status, pci_rval, pci_status)); 45270025d76Sjohnny 45370025d76Sjohnny if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) { 45470025d76Sjohnny *(int *)result = 0; 45570025d76Sjohnny return (DDI_FAILURE); 45670025d76Sjohnny } 45770025d76Sjohnny 45870025d76Sjohnny if (psm_rval == PSM_SUCCESS) 45970025d76Sjohnny *(int *)result = psm_status; 46070025d76Sjohnny 46170025d76Sjohnny if (pci_rval == DDI_SUCCESS) 46270025d76Sjohnny *(int *)result |= pci_status; 46370025d76Sjohnny 46470025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned = %x\n", 46570025d76Sjohnny *(int *)result)); 46670025d76Sjohnny break; 46770025d76Sjohnny case DDI_INTROP_SETCAP: 46870025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 46970025d76Sjohnny "SETCAP cap=0x%x\n", *(int *)result)); 47070025d76Sjohnny if (psm_intr_ops == NULL) 47170025d76Sjohnny return (DDI_FAILURE); 47270025d76Sjohnny 47370025d76Sjohnny if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) { 47470025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "GETCAP: psm_intr_ops" 47570025d76Sjohnny " returned failure\n")); 47670025d76Sjohnny return (DDI_FAILURE); 47770025d76Sjohnny } 47870025d76Sjohnny break; 47970025d76Sjohnny case DDI_INTROP_ENABLE: 48070025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE\n")); 48170025d76Sjohnny if (psm_intr_ops == NULL) 48270025d76Sjohnny return (DDI_FAILURE); 48370025d76Sjohnny 48470025d76Sjohnny if (pci_enable_intr(pdip, rdip, hdlp, hdlp->ih_inum) != 48570025d76Sjohnny DDI_SUCCESS) 48670025d76Sjohnny return (DDI_FAILURE); 48770025d76Sjohnny 48870025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE " 48970025d76Sjohnny "vector=0x%x\n", hdlp->ih_vector)); 49070025d76Sjohnny break; 49170025d76Sjohnny case DDI_INTROP_DISABLE: 49270025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE\n")); 49370025d76Sjohnny if (psm_intr_ops == NULL) 49470025d76Sjohnny return (DDI_FAILURE); 49570025d76Sjohnny 49670025d76Sjohnny pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum); 49770025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE " 49870025d76Sjohnny "vector = %x\n", hdlp->ih_vector)); 49970025d76Sjohnny break; 50070025d76Sjohnny case DDI_INTROP_BLOCKENABLE: 50170025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 50270025d76Sjohnny "BLOCKENABLE\n")); 50370025d76Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_MSI) { 50470025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: not MSI\n")); 50570025d76Sjohnny return (DDI_FAILURE); 50670025d76Sjohnny } 50770025d76Sjohnny 50870025d76Sjohnny /* Check if psm_intr_ops is NULL? */ 50970025d76Sjohnny if (psm_intr_ops == NULL) 51070025d76Sjohnny return (DDI_FAILURE); 51170025d76Sjohnny 512102cb92eSjohnny count = hdlp->ih_scratch1; 513102cb92eSjohnny h_array = (ddi_intr_handle_t *)hdlp->ih_scratch2; 514102cb92eSjohnny for (i = 0; i < count; i++) { 515102cb92eSjohnny hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 51670025d76Sjohnny if (pci_enable_intr(pdip, rdip, hdlp, 517102cb92eSjohnny hdlp->ih_inum) != DDI_SUCCESS) { 51870025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: " 51970025d76Sjohnny "pci_enable_intr failed for %d\n", i)); 520102cb92eSjohnny for (j = 0; j < i; j++) { 521102cb92eSjohnny hdlp = (ddi_intr_handle_impl_t *)h_array[j]; 52270025d76Sjohnny pci_disable_intr(pdip, rdip, hdlp, 523102cb92eSjohnny hdlp->ih_inum); 524102cb92eSjohnny } 52570025d76Sjohnny return (DDI_FAILURE); 52670025d76Sjohnny } 52770025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 528102cb92eSjohnny "BLOCKENABLE inum %x done\n", hdlp->ih_inum)); 52970025d76Sjohnny } 53070025d76Sjohnny break; 53170025d76Sjohnny case DDI_INTROP_BLOCKDISABLE: 53270025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 53370025d76Sjohnny "BLOCKDISABLE\n")); 53470025d76Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_MSI) { 53570025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKDISABLE: not MSI\n")); 53670025d76Sjohnny return (DDI_FAILURE); 53770025d76Sjohnny } 53870025d76Sjohnny 53970025d76Sjohnny /* Check if psm_intr_ops is present */ 54070025d76Sjohnny if (psm_intr_ops == NULL) 54170025d76Sjohnny return (DDI_FAILURE); 54270025d76Sjohnny 543102cb92eSjohnny count = hdlp->ih_scratch1; 544102cb92eSjohnny h_array = (ddi_intr_handle_t *)hdlp->ih_scratch2; 545102cb92eSjohnny for (i = 0; i < count; i++) { 546102cb92eSjohnny hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 547102cb92eSjohnny pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum); 54870025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 549102cb92eSjohnny "BLOCKDISABLE inum %x done\n", hdlp->ih_inum)); 55070025d76Sjohnny } 55170025d76Sjohnny break; 55270025d76Sjohnny case DDI_INTROP_SETMASK: 55370025d76Sjohnny case DDI_INTROP_CLRMASK: 55470025d76Sjohnny /* 55570025d76Sjohnny * First handle in the config space 55670025d76Sjohnny */ 55770025d76Sjohnny if (intr_op == DDI_INTROP_SETMASK) { 55870025d76Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 55970025d76Sjohnny pci_status = pci_msi_set_mask(rdip, 56070025d76Sjohnny hdlp->ih_type, hdlp->ih_inum); 56170025d76Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 56270025d76Sjohnny pci_status = pci_intx_set_mask(rdip); 56370025d76Sjohnny } else { 56470025d76Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 56570025d76Sjohnny pci_status = pci_msi_clr_mask(rdip, 56670025d76Sjohnny hdlp->ih_type, hdlp->ih_inum); 56770025d76Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 56870025d76Sjohnny pci_status = pci_intx_clr_mask(rdip); 56970025d76Sjohnny } 57070025d76Sjohnny 57170025d76Sjohnny /* For MSI/X; no need to check with pcplusmp */ 57270025d76Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 57370025d76Sjohnny return (pci_status); 57470025d76Sjohnny 57570025d76Sjohnny /* For fixed interrupts only: handle config space first */ 57670025d76Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_FIXED && 57770025d76Sjohnny pci_status == DDI_SUCCESS) 57870025d76Sjohnny break; 57970025d76Sjohnny 58070025d76Sjohnny /* For fixed interrupts only: confer with pcplusmp next */ 58170025d76Sjohnny if (psm_intr_ops != NULL) { 58270025d76Sjohnny /* If interrupt is shared; do nothing */ 58370025d76Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 58470025d76Sjohnny PSM_INTR_OP_GET_SHARED, &psm_status); 58570025d76Sjohnny 58670025d76Sjohnny if (psm_rval == PSM_FAILURE || psm_status == 1) 58770025d76Sjohnny return (pci_status); 58870025d76Sjohnny 58970025d76Sjohnny /* Now, pcplusmp should try to set/clear the mask */ 59070025d76Sjohnny if (intr_op == DDI_INTROP_SETMASK) 59170025d76Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 59270025d76Sjohnny PSM_INTR_OP_SET_MASK, NULL); 59370025d76Sjohnny else 59470025d76Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 59570025d76Sjohnny PSM_INTR_OP_CLEAR_MASK, NULL); 59670025d76Sjohnny } 59770025d76Sjohnny return ((psm_rval == PSM_FAILURE) ? DDI_FAILURE : DDI_SUCCESS); 59870025d76Sjohnny case DDI_INTROP_GETPENDING: 59970025d76Sjohnny /* 60070025d76Sjohnny * First check the config space and/or 60170025d76Sjohnny * MSI capability register(s) 60270025d76Sjohnny */ 60370025d76Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 60470025d76Sjohnny pci_rval = pci_msi_get_pending(rdip, hdlp->ih_type, 60570025d76Sjohnny hdlp->ih_inum, &pci_status); 60670025d76Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 60770025d76Sjohnny pci_rval = pci_intx_get_pending(rdip, &pci_status); 60870025d76Sjohnny 60970025d76Sjohnny /* On failure; next try with pcplusmp */ 61070025d76Sjohnny if (pci_rval != DDI_SUCCESS && psm_intr_ops != NULL) 61170025d76Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 61270025d76Sjohnny PSM_INTR_OP_GET_PENDING, &psm_status); 61370025d76Sjohnny 61470025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned " 61570025d76Sjohnny "psm_rval = %x, psm_status = %x, pci_rval = %x, " 61670025d76Sjohnny "pci_status = %x\n", psm_rval, psm_status, pci_rval, 61770025d76Sjohnny pci_status)); 61870025d76Sjohnny if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) { 61970025d76Sjohnny *(int *)result = 0; 62070025d76Sjohnny return (DDI_FAILURE); 62170025d76Sjohnny } 62270025d76Sjohnny 62370025d76Sjohnny if (psm_rval != PSM_FAILURE) 62470025d76Sjohnny *(int *)result = psm_status; 62570025d76Sjohnny else if (pci_rval != DDI_FAILURE) 62670025d76Sjohnny *(int *)result = pci_status; 62770025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned = %x\n", 62870025d76Sjohnny *(int *)result)); 62970025d76Sjohnny break; 63070025d76Sjohnny case DDI_INTROP_NAVAIL: 63170025d76Sjohnny if ((psm_intr_ops != NULL) && (pci_get_priority(rdip, 63270025d76Sjohnny hdlp, &priority) == DDI_SUCCESS)) { 63370025d76Sjohnny /* Priority in the handle not initialized yet */ 63470025d76Sjohnny hdlp->ih_pri = priority; 63570025d76Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 63670025d76Sjohnny PSM_INTR_OP_NAVAIL_VECTORS, result); 63770025d76Sjohnny } else { 63870025d76Sjohnny *(int *)result = 1; 63970025d76Sjohnny } 64070025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: NAVAIL returned = %x\n", 64170025d76Sjohnny *(int *)result)); 64270025d76Sjohnny break; 64370025d76Sjohnny default: 64470025d76Sjohnny return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result)); 64570025d76Sjohnny } 64670025d76Sjohnny 64770025d76Sjohnny return (DDI_SUCCESS); 64870025d76Sjohnny } 64970025d76Sjohnny 6507a364d25Sschwartz int 6517a364d25Sschwartz pci_get_intr_from_vecirq(apic_get_intr_t *intrinfo_p, 6527a364d25Sschwartz int vecirq, boolean_t is_irq) 6537a364d25Sschwartz { 6547a364d25Sschwartz ddi_intr_handle_impl_t get_info_ii_hdl; 6557a364d25Sschwartz 6567a364d25Sschwartz if (is_irq) 6577a364d25Sschwartz intrinfo_p->avgi_req_flags |= PSMGI_INTRBY_IRQ; 6587a364d25Sschwartz 6597a364d25Sschwartz /* 6607a364d25Sschwartz * For this locally-declared and used handle, ih_private will contain a 6617a364d25Sschwartz * pointer to apic_get_intr_t, not an ihdl_plat_t as used for 6627a364d25Sschwartz * global interrupt handling. 6637a364d25Sschwartz */ 6647a364d25Sschwartz get_info_ii_hdl.ih_private = intrinfo_p; 6657a364d25Sschwartz get_info_ii_hdl.ih_vector = (ushort_t)vecirq; 6667a364d25Sschwartz 6677a364d25Sschwartz if ((*psm_intr_ops)(NULL, &get_info_ii_hdl, 6687a364d25Sschwartz PSM_INTR_OP_GET_INTR, NULL) == PSM_FAILURE) 6697a364d25Sschwartz return (DDI_FAILURE); 6707a364d25Sschwartz 6717a364d25Sschwartz return (DDI_SUCCESS); 6727a364d25Sschwartz } 6737a364d25Sschwartz 6747a364d25Sschwartz 6757a364d25Sschwartz int 6767a364d25Sschwartz pci_get_cpu_from_vecirq(int vecirq, boolean_t is_irq) 6777a364d25Sschwartz { 6787a364d25Sschwartz int rval; 6797a364d25Sschwartz 6807a364d25Sschwartz apic_get_intr_t intrinfo; 6817a364d25Sschwartz intrinfo.avgi_req_flags = PSMGI_REQ_CPUID; 6827a364d25Sschwartz rval = pci_get_intr_from_vecirq(&intrinfo, vecirq, is_irq); 6837a364d25Sschwartz 6847a364d25Sschwartz if (rval == DDI_SUCCESS) 6857a364d25Sschwartz return (intrinfo.avgi_cpu_id); 6867a364d25Sschwartz else 6877a364d25Sschwartz return (-1); 6887a364d25Sschwartz } 6897a364d25Sschwartz 69070025d76Sjohnny 69170025d76Sjohnny static int 69270025d76Sjohnny pci_enable_intr(dev_info_t *pdip, dev_info_t *rdip, 69370025d76Sjohnny ddi_intr_handle_impl_t *hdlp, uint32_t inum) 69470025d76Sjohnny { 69570025d76Sjohnny struct intrspec *ispec; 6967a364d25Sschwartz int irq; 6977a364d25Sschwartz ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 69870025d76Sjohnny 69970025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: hdlp %p inum %x\n", 70070025d76Sjohnny (void *)hdlp, inum)); 70170025d76Sjohnny 70270025d76Sjohnny /* Translate the interrupt if needed */ 70370025d76Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum); 70470025d76Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec) 70570025d76Sjohnny ispec->intrspec_vec = inum; 7067a364d25Sschwartz ihdl_plat_datap->ip_ispecp = ispec; 70770025d76Sjohnny 70870025d76Sjohnny /* translate the interrupt if needed */ 7097a364d25Sschwartz (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq); 7107a364d25Sschwartz DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: priority=%x irq=%x\n", 7117a364d25Sschwartz hdlp->ih_pri, irq)); 71270025d76Sjohnny 71370025d76Sjohnny /* Add the interrupt handler */ 71470025d76Sjohnny if (!add_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, 7157a364d25Sschwartz DEVI(rdip)->devi_name, irq, hdlp->ih_cb_arg1, 7167a364d25Sschwartz hdlp->ih_cb_arg2, &ihdl_plat_datap->ip_ticks, rdip)) 71770025d76Sjohnny return (DDI_FAILURE); 71870025d76Sjohnny 7197a364d25Sschwartz /* Note this really is an irq. */ 7207a364d25Sschwartz hdlp->ih_vector = (ushort_t)irq; 7217a364d25Sschwartz 72270025d76Sjohnny return (DDI_SUCCESS); 72370025d76Sjohnny } 72470025d76Sjohnny 72570025d76Sjohnny 72670025d76Sjohnny static void 72770025d76Sjohnny pci_disable_intr(dev_info_t *pdip, dev_info_t *rdip, 72870025d76Sjohnny ddi_intr_handle_impl_t *hdlp, uint32_t inum) 72970025d76Sjohnny { 7307a364d25Sschwartz int irq; 73170025d76Sjohnny struct intrspec *ispec; 7327a364d25Sschwartz ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 73370025d76Sjohnny 73470025d76Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_disable_intr: \n")); 73570025d76Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum); 73670025d76Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec) 73770025d76Sjohnny ispec->intrspec_vec = inum; 7387a364d25Sschwartz ihdl_plat_datap->ip_ispecp = ispec; 73970025d76Sjohnny 74070025d76Sjohnny /* translate the interrupt if needed */ 7417a364d25Sschwartz (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq); 74270025d76Sjohnny 74370025d76Sjohnny /* Disable the interrupt handler */ 7447a364d25Sschwartz rem_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, irq); 7457a364d25Sschwartz ihdl_plat_datap->ip_ispecp = NULL; 74670025d76Sjohnny } 74770025d76Sjohnny 74870025d76Sjohnny /* 74970025d76Sjohnny * Miscellaneous library function 75070025d76Sjohnny */ 75170025d76Sjohnny int 75270025d76Sjohnny pci_common_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp) 75370025d76Sjohnny { 75470025d76Sjohnny int i; 75570025d76Sjohnny int number; 75670025d76Sjohnny int assigned_addr_len; 75770025d76Sjohnny uint_t phys_hi = pci_rp->pci_phys_hi; 75870025d76Sjohnny pci_regspec_t *assigned_addr; 75970025d76Sjohnny 76070025d76Sjohnny if (((phys_hi & PCI_REG_ADDR_M) == PCI_ADDR_CONFIG) || 76170025d76Sjohnny (phys_hi & PCI_RELOCAT_B)) 76270025d76Sjohnny return (DDI_SUCCESS); 76370025d76Sjohnny 76470025d76Sjohnny /* 76570025d76Sjohnny * the "reg" property specifies relocatable, get and interpret the 76670025d76Sjohnny * "assigned-addresses" property. 76770025d76Sjohnny */ 76870025d76Sjohnny if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 76970025d76Sjohnny "assigned-addresses", (int **)&assigned_addr, 77070025d76Sjohnny (uint_t *)&assigned_addr_len) != DDI_PROP_SUCCESS) 77170025d76Sjohnny return (DDI_FAILURE); 77270025d76Sjohnny 77370025d76Sjohnny /* 77470025d76Sjohnny * Scan the "assigned-addresses" for one that matches the specified 77570025d76Sjohnny * "reg" property entry. 77670025d76Sjohnny */ 77770025d76Sjohnny phys_hi &= PCI_CONF_ADDR_MASK; 77870025d76Sjohnny number = assigned_addr_len / (sizeof (pci_regspec_t) / sizeof (int)); 77970025d76Sjohnny for (i = 0; i < number; i++) { 78070025d76Sjohnny if ((assigned_addr[i].pci_phys_hi & PCI_CONF_ADDR_MASK) == 78170025d76Sjohnny phys_hi) { 78270025d76Sjohnny pci_rp->pci_phys_mid = assigned_addr[i].pci_phys_mid; 78370025d76Sjohnny pci_rp->pci_phys_low = assigned_addr[i].pci_phys_low; 78470025d76Sjohnny ddi_prop_free(assigned_addr); 78570025d76Sjohnny return (DDI_SUCCESS); 78670025d76Sjohnny } 78770025d76Sjohnny } 78870025d76Sjohnny 78970025d76Sjohnny ddi_prop_free(assigned_addr); 79070025d76Sjohnny return (DDI_FAILURE); 79170025d76Sjohnny } 79270025d76Sjohnny 79370025d76Sjohnny 79470025d76Sjohnny /* 79570025d76Sjohnny * For pci_tools 79670025d76Sjohnny */ 79770025d76Sjohnny 79870025d76Sjohnny int 79970025d76Sjohnny pci_common_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg, 80070025d76Sjohnny int mode, cred_t *credp, int *rvalp) 80170025d76Sjohnny { 80270025d76Sjohnny int rv = ENOTTY; 80370025d76Sjohnny 80470025d76Sjohnny minor_t minor = getminor(dev); 80570025d76Sjohnny 80670025d76Sjohnny switch (PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(minor)) { 80770025d76Sjohnny case PCI_TOOL_REG_MINOR_NUM: 80870025d76Sjohnny 80970025d76Sjohnny switch (cmd) { 81070025d76Sjohnny case PCITOOL_DEVICE_SET_REG: 81170025d76Sjohnny case PCITOOL_DEVICE_GET_REG: 81270025d76Sjohnny 81370025d76Sjohnny /* Require full privileges. */ 81470025d76Sjohnny if (secpolicy_kmdb(credp)) 81570025d76Sjohnny rv = EPERM; 81670025d76Sjohnny else 81770025d76Sjohnny rv = pcitool_dev_reg_ops(dip, (void *)arg, 81870025d76Sjohnny cmd, mode); 81970025d76Sjohnny break; 82070025d76Sjohnny 82170025d76Sjohnny case PCITOOL_NEXUS_SET_REG: 82270025d76Sjohnny case PCITOOL_NEXUS_GET_REG: 82370025d76Sjohnny 82470025d76Sjohnny /* Require full privileges. */ 82570025d76Sjohnny if (secpolicy_kmdb(credp)) 82670025d76Sjohnny rv = EPERM; 82770025d76Sjohnny else 82870025d76Sjohnny rv = pcitool_bus_reg_ops(dip, (void *)arg, 82970025d76Sjohnny cmd, mode); 83070025d76Sjohnny break; 83170025d76Sjohnny } 83270025d76Sjohnny break; 83370025d76Sjohnny 83470025d76Sjohnny case PCI_TOOL_INTR_MINOR_NUM: 83570025d76Sjohnny 83670025d76Sjohnny switch (cmd) { 83770025d76Sjohnny case PCITOOL_DEVICE_SET_INTR: 83870025d76Sjohnny 83970025d76Sjohnny /* Require PRIV_SYS_RES_CONFIG, same as psradm */ 84070025d76Sjohnny if (secpolicy_ponline(credp)) { 84170025d76Sjohnny rv = EPERM; 84270025d76Sjohnny break; 84370025d76Sjohnny } 84470025d76Sjohnny 84570025d76Sjohnny /*FALLTHRU*/ 84670025d76Sjohnny /* These require no special privileges. */ 84770025d76Sjohnny case PCITOOL_DEVICE_GET_INTR: 84870025d76Sjohnny case PCITOOL_DEVICE_NUM_INTR: 84970025d76Sjohnny rv = pcitool_intr_admn(dip, (void *)arg, cmd, mode); 85070025d76Sjohnny break; 85170025d76Sjohnny } 85270025d76Sjohnny break; 85370025d76Sjohnny 85470025d76Sjohnny /* 85570025d76Sjohnny * All non-PCItool ioctls go through here, including: 85670025d76Sjohnny * devctl ioctls with minor number PCIHP_DEVCTL_MINOR and 85770025d76Sjohnny * those for attachment points with where minor number is the 85870025d76Sjohnny * device number. 85970025d76Sjohnny */ 86070025d76Sjohnny default: 86170025d76Sjohnny rv = (pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, 86270025d76Sjohnny credp, rvalp); 86370025d76Sjohnny break; 86470025d76Sjohnny } 86570025d76Sjohnny 86670025d76Sjohnny return (rv); 86770025d76Sjohnny } 868649d4cceSanish 869649d4cceSanish 87000d0963fSdilpreet int 87100d0963fSdilpreet pci_common_ctlops_poke(peekpoke_ctlops_t *in_args) 87200d0963fSdilpreet { 87300d0963fSdilpreet size_t size = in_args->size; 87400d0963fSdilpreet uintptr_t dev_addr = in_args->dev_addr; 87500d0963fSdilpreet uintptr_t host_addr = in_args->host_addr; 87600d0963fSdilpreet ddi_acc_impl_t *hp = (ddi_acc_impl_t *)in_args->handle; 87700d0963fSdilpreet ddi_acc_hdl_t *hdlp = (ddi_acc_hdl_t *)in_args->handle; 87800d0963fSdilpreet size_t repcount = in_args->repcount; 87900d0963fSdilpreet uint_t flags = in_args->flags; 88000d0963fSdilpreet int err = DDI_SUCCESS; 88100d0963fSdilpreet 88200d0963fSdilpreet /* 88300d0963fSdilpreet * if no handle then this is a poke. We have to return failure here 88400d0963fSdilpreet * as we have no way of knowing whether this is a MEM or IO space access 88500d0963fSdilpreet */ 88600d0963fSdilpreet if (in_args->handle == NULL) 88700d0963fSdilpreet return (DDI_FAILURE); 88800d0963fSdilpreet 88900d0963fSdilpreet /* 89000d0963fSdilpreet * rest of this function is actually for cautious puts 89100d0963fSdilpreet */ 89200d0963fSdilpreet for (; repcount; repcount--) { 89300d0963fSdilpreet if (hp->ahi_acc_attr == DDI_ACCATTR_CONFIG_SPACE) { 89400d0963fSdilpreet switch (size) { 89500d0963fSdilpreet case sizeof (uint8_t): 89600d0963fSdilpreet pci_config_wr8(hp, (uint8_t *)dev_addr, 89700d0963fSdilpreet *(uint8_t *)host_addr); 89800d0963fSdilpreet break; 89900d0963fSdilpreet case sizeof (uint16_t): 90000d0963fSdilpreet pci_config_wr16(hp, (uint16_t *)dev_addr, 90100d0963fSdilpreet *(uint16_t *)host_addr); 90200d0963fSdilpreet break; 90300d0963fSdilpreet case sizeof (uint32_t): 90400d0963fSdilpreet pci_config_wr32(hp, (uint32_t *)dev_addr, 90500d0963fSdilpreet *(uint32_t *)host_addr); 90600d0963fSdilpreet break; 90700d0963fSdilpreet case sizeof (uint64_t): 90800d0963fSdilpreet pci_config_wr64(hp, (uint64_t *)dev_addr, 90900d0963fSdilpreet *(uint64_t *)host_addr); 91000d0963fSdilpreet break; 91100d0963fSdilpreet default: 91200d0963fSdilpreet err = DDI_FAILURE; 91300d0963fSdilpreet break; 91400d0963fSdilpreet } 91500d0963fSdilpreet } else if (hp->ahi_acc_attr & DDI_ACCATTR_IO_SPACE) { 91600d0963fSdilpreet if (hdlp->ah_acc.devacc_attr_endian_flags == 91700d0963fSdilpreet DDI_STRUCTURE_BE_ACC) { 91800d0963fSdilpreet switch (size) { 91900d0963fSdilpreet case sizeof (uint8_t): 92000d0963fSdilpreet i_ddi_io_put8(hp, 92100d0963fSdilpreet (uint8_t *)dev_addr, 92200d0963fSdilpreet *(uint8_t *)host_addr); 92300d0963fSdilpreet break; 92400d0963fSdilpreet case sizeof (uint16_t): 92500d0963fSdilpreet i_ddi_io_swap_put16(hp, 92600d0963fSdilpreet (uint16_t *)dev_addr, 92700d0963fSdilpreet *(uint16_t *)host_addr); 92800d0963fSdilpreet break; 92900d0963fSdilpreet case sizeof (uint32_t): 93000d0963fSdilpreet i_ddi_io_swap_put32(hp, 93100d0963fSdilpreet (uint32_t *)dev_addr, 93200d0963fSdilpreet *(uint32_t *)host_addr); 93300d0963fSdilpreet break; 93400d0963fSdilpreet /* 93500d0963fSdilpreet * note the 64-bit case is a dummy 93600d0963fSdilpreet * function - so no need to swap 93700d0963fSdilpreet */ 93800d0963fSdilpreet case sizeof (uint64_t): 93900d0963fSdilpreet i_ddi_io_put64(hp, 94000d0963fSdilpreet (uint64_t *)dev_addr, 94100d0963fSdilpreet *(uint64_t *)host_addr); 94200d0963fSdilpreet break; 94300d0963fSdilpreet default: 94400d0963fSdilpreet err = DDI_FAILURE; 94500d0963fSdilpreet break; 94600d0963fSdilpreet } 94700d0963fSdilpreet } else { 94800d0963fSdilpreet switch (size) { 94900d0963fSdilpreet case sizeof (uint8_t): 95000d0963fSdilpreet i_ddi_io_put8(hp, 95100d0963fSdilpreet (uint8_t *)dev_addr, 95200d0963fSdilpreet *(uint8_t *)host_addr); 95300d0963fSdilpreet break; 95400d0963fSdilpreet case sizeof (uint16_t): 95500d0963fSdilpreet i_ddi_io_put16(hp, 95600d0963fSdilpreet (uint16_t *)dev_addr, 95700d0963fSdilpreet *(uint16_t *)host_addr); 95800d0963fSdilpreet break; 95900d0963fSdilpreet case sizeof (uint32_t): 96000d0963fSdilpreet i_ddi_io_put32(hp, 96100d0963fSdilpreet (uint32_t *)dev_addr, 96200d0963fSdilpreet *(uint32_t *)host_addr); 96300d0963fSdilpreet break; 96400d0963fSdilpreet case sizeof (uint64_t): 96500d0963fSdilpreet i_ddi_io_put64(hp, 96600d0963fSdilpreet (uint64_t *)dev_addr, 96700d0963fSdilpreet *(uint64_t *)host_addr); 96800d0963fSdilpreet break; 96900d0963fSdilpreet default: 97000d0963fSdilpreet err = DDI_FAILURE; 97100d0963fSdilpreet break; 97200d0963fSdilpreet } 97300d0963fSdilpreet } 97400d0963fSdilpreet } else { 97500d0963fSdilpreet if (hdlp->ah_acc.devacc_attr_endian_flags == 97600d0963fSdilpreet DDI_STRUCTURE_BE_ACC) { 97700d0963fSdilpreet switch (size) { 97800d0963fSdilpreet case sizeof (uint8_t): 97900d0963fSdilpreet *(uint8_t *)dev_addr = 98000d0963fSdilpreet *(uint8_t *)host_addr; 98100d0963fSdilpreet break; 98200d0963fSdilpreet case sizeof (uint16_t): 98300d0963fSdilpreet *(uint16_t *)dev_addr = 98400d0963fSdilpreet ddi_swap16(*(uint16_t *)host_addr); 98500d0963fSdilpreet break; 98600d0963fSdilpreet case sizeof (uint32_t): 98700d0963fSdilpreet *(uint32_t *)dev_addr = 98800d0963fSdilpreet ddi_swap32(*(uint32_t *)host_addr); 98900d0963fSdilpreet break; 99000d0963fSdilpreet case sizeof (uint64_t): 99100d0963fSdilpreet *(uint64_t *)dev_addr = 99200d0963fSdilpreet ddi_swap64(*(uint64_t *)host_addr); 99300d0963fSdilpreet break; 99400d0963fSdilpreet default: 99500d0963fSdilpreet err = DDI_FAILURE; 99600d0963fSdilpreet break; 99700d0963fSdilpreet } 99800d0963fSdilpreet } else { 99900d0963fSdilpreet switch (size) { 100000d0963fSdilpreet case sizeof (uint8_t): 100100d0963fSdilpreet *(uint8_t *)dev_addr = 100200d0963fSdilpreet *(uint8_t *)host_addr; 100300d0963fSdilpreet break; 100400d0963fSdilpreet case sizeof (uint16_t): 100500d0963fSdilpreet *(uint16_t *)dev_addr = 100600d0963fSdilpreet *(uint16_t *)host_addr; 100700d0963fSdilpreet break; 100800d0963fSdilpreet case sizeof (uint32_t): 100900d0963fSdilpreet *(uint32_t *)dev_addr = 101000d0963fSdilpreet *(uint32_t *)host_addr; 101100d0963fSdilpreet break; 101200d0963fSdilpreet case sizeof (uint64_t): 101300d0963fSdilpreet *(uint64_t *)dev_addr = 101400d0963fSdilpreet *(uint64_t *)host_addr; 101500d0963fSdilpreet break; 101600d0963fSdilpreet default: 101700d0963fSdilpreet err = DDI_FAILURE; 101800d0963fSdilpreet break; 101900d0963fSdilpreet } 102000d0963fSdilpreet } 102100d0963fSdilpreet } 102200d0963fSdilpreet host_addr += size; 102300d0963fSdilpreet if (flags == DDI_DEV_AUTOINCR) 102400d0963fSdilpreet dev_addr += size; 102500d0963fSdilpreet } 102600d0963fSdilpreet return (err); 102700d0963fSdilpreet } 102800d0963fSdilpreet 102900d0963fSdilpreet 103000d0963fSdilpreet int 103100d0963fSdilpreet pci_fm_acc_setup(ddi_acc_hdl_t *hp, off_t offset, off_t len) 103200d0963fSdilpreet { 103300d0963fSdilpreet ddi_acc_impl_t *ap = (ddi_acc_impl_t *)hp->ah_platform_private; 103400d0963fSdilpreet 103500d0963fSdilpreet /* endian-ness check */ 103600d0963fSdilpreet if (hp->ah_acc.devacc_attr_endian_flags == DDI_STRUCTURE_BE_ACC) 103700d0963fSdilpreet return (DDI_FAILURE); 103800d0963fSdilpreet 103900d0963fSdilpreet /* 104000d0963fSdilpreet * range check 104100d0963fSdilpreet */ 104200d0963fSdilpreet if ((offset >= PCI_CONF_HDR_SIZE) || 104300d0963fSdilpreet (len > PCI_CONF_HDR_SIZE) || 104400d0963fSdilpreet (offset + len > PCI_CONF_HDR_SIZE)) 104500d0963fSdilpreet return (DDI_FAILURE); 104600d0963fSdilpreet 104700d0963fSdilpreet ap->ahi_acc_attr |= DDI_ACCATTR_CONFIG_SPACE; 104800d0963fSdilpreet /* 104900d0963fSdilpreet * always use cautious mechanism for config space gets 105000d0963fSdilpreet */ 105100d0963fSdilpreet ap->ahi_get8 = i_ddi_caut_get8; 105200d0963fSdilpreet ap->ahi_get16 = i_ddi_caut_get16; 105300d0963fSdilpreet ap->ahi_get32 = i_ddi_caut_get32; 105400d0963fSdilpreet ap->ahi_get64 = i_ddi_caut_get64; 105500d0963fSdilpreet ap->ahi_rep_get8 = i_ddi_caut_rep_get8; 105600d0963fSdilpreet ap->ahi_rep_get16 = i_ddi_caut_rep_get16; 105700d0963fSdilpreet ap->ahi_rep_get32 = i_ddi_caut_rep_get32; 105800d0963fSdilpreet ap->ahi_rep_get64 = i_ddi_caut_rep_get64; 105900d0963fSdilpreet if (hp->ah_acc.devacc_attr_access == DDI_CAUTIOUS_ACC) { 106000d0963fSdilpreet ap->ahi_put8 = i_ddi_caut_put8; 106100d0963fSdilpreet ap->ahi_put16 = i_ddi_caut_put16; 106200d0963fSdilpreet ap->ahi_put32 = i_ddi_caut_put32; 106300d0963fSdilpreet ap->ahi_put64 = i_ddi_caut_put64; 106400d0963fSdilpreet ap->ahi_rep_put8 = i_ddi_caut_rep_put8; 106500d0963fSdilpreet ap->ahi_rep_put16 = i_ddi_caut_rep_put16; 106600d0963fSdilpreet ap->ahi_rep_put32 = i_ddi_caut_rep_put32; 106700d0963fSdilpreet ap->ahi_rep_put64 = i_ddi_caut_rep_put64; 106800d0963fSdilpreet } else { 106900d0963fSdilpreet ap->ahi_put8 = pci_config_wr8; 107000d0963fSdilpreet ap->ahi_put16 = pci_config_wr16; 107100d0963fSdilpreet ap->ahi_put32 = pci_config_wr32; 107200d0963fSdilpreet ap->ahi_put64 = pci_config_wr64; 107300d0963fSdilpreet ap->ahi_rep_put8 = pci_config_rep_wr8; 107400d0963fSdilpreet ap->ahi_rep_put16 = pci_config_rep_wr16; 107500d0963fSdilpreet ap->ahi_rep_put32 = pci_config_rep_wr32; 107600d0963fSdilpreet ap->ahi_rep_put64 = pci_config_rep_wr64; 107700d0963fSdilpreet } 107800d0963fSdilpreet 107900d0963fSdilpreet /* Initialize to default check/notify functions */ 108000d0963fSdilpreet ap->ahi_fault_check = i_ddi_acc_fault_check; 108100d0963fSdilpreet ap->ahi_fault_notify = i_ddi_acc_fault_notify; 108200d0963fSdilpreet ap->ahi_fault = 0; 108300d0963fSdilpreet impl_acc_err_init(hp); 108400d0963fSdilpreet return (DDI_SUCCESS); 108500d0963fSdilpreet } 108600d0963fSdilpreet 108700d0963fSdilpreet 108800d0963fSdilpreet int 108900d0963fSdilpreet pci_common_ctlops_peek(peekpoke_ctlops_t *in_args) 109000d0963fSdilpreet { 109100d0963fSdilpreet size_t size = in_args->size; 109200d0963fSdilpreet uintptr_t dev_addr = in_args->dev_addr; 109300d0963fSdilpreet uintptr_t host_addr = in_args->host_addr; 109400d0963fSdilpreet ddi_acc_impl_t *hp = (ddi_acc_impl_t *)in_args->handle; 109500d0963fSdilpreet ddi_acc_hdl_t *hdlp = (ddi_acc_hdl_t *)in_args->handle; 109600d0963fSdilpreet size_t repcount = in_args->repcount; 109700d0963fSdilpreet uint_t flags = in_args->flags; 109800d0963fSdilpreet int err = DDI_SUCCESS; 109900d0963fSdilpreet 110000d0963fSdilpreet /* 110100d0963fSdilpreet * if no handle then this is a peek. We have to return failure here 110200d0963fSdilpreet * as we have no way of knowing whether this is a MEM or IO space access 110300d0963fSdilpreet */ 110400d0963fSdilpreet if (in_args->handle == NULL) 110500d0963fSdilpreet return (DDI_FAILURE); 110600d0963fSdilpreet 110700d0963fSdilpreet for (; repcount; repcount--) { 110800d0963fSdilpreet if (hp->ahi_acc_attr == DDI_ACCATTR_CONFIG_SPACE) { 110900d0963fSdilpreet switch (size) { 111000d0963fSdilpreet case sizeof (uint8_t): 111100d0963fSdilpreet *(uint8_t *)host_addr = pci_config_rd8(hp, 111200d0963fSdilpreet (uint8_t *)dev_addr); 111300d0963fSdilpreet break; 111400d0963fSdilpreet case sizeof (uint16_t): 111500d0963fSdilpreet *(uint16_t *)host_addr = pci_config_rd16(hp, 111600d0963fSdilpreet (uint16_t *)dev_addr); 111700d0963fSdilpreet break; 111800d0963fSdilpreet case sizeof (uint32_t): 111900d0963fSdilpreet *(uint32_t *)host_addr = pci_config_rd32(hp, 112000d0963fSdilpreet (uint32_t *)dev_addr); 112100d0963fSdilpreet break; 112200d0963fSdilpreet case sizeof (uint64_t): 112300d0963fSdilpreet *(uint64_t *)host_addr = pci_config_rd64(hp, 112400d0963fSdilpreet (uint64_t *)dev_addr); 112500d0963fSdilpreet break; 112600d0963fSdilpreet default: 112700d0963fSdilpreet err = DDI_FAILURE; 112800d0963fSdilpreet break; 112900d0963fSdilpreet } 113000d0963fSdilpreet } else if (hp->ahi_acc_attr & DDI_ACCATTR_IO_SPACE) { 113100d0963fSdilpreet if (hdlp->ah_acc.devacc_attr_endian_flags == 113200d0963fSdilpreet DDI_STRUCTURE_BE_ACC) { 113300d0963fSdilpreet switch (size) { 113400d0963fSdilpreet case sizeof (uint8_t): 113500d0963fSdilpreet *(uint8_t *)host_addr = 113600d0963fSdilpreet i_ddi_io_get8(hp, 113700d0963fSdilpreet (uint8_t *)dev_addr); 113800d0963fSdilpreet break; 113900d0963fSdilpreet case sizeof (uint16_t): 114000d0963fSdilpreet *(uint16_t *)host_addr = 114100d0963fSdilpreet i_ddi_io_swap_get16(hp, 114200d0963fSdilpreet (uint16_t *)dev_addr); 114300d0963fSdilpreet break; 114400d0963fSdilpreet case sizeof (uint32_t): 114500d0963fSdilpreet *(uint32_t *)host_addr = 114600d0963fSdilpreet i_ddi_io_swap_get32(hp, 114700d0963fSdilpreet (uint32_t *)dev_addr); 114800d0963fSdilpreet break; 114900d0963fSdilpreet /* 115000d0963fSdilpreet * note the 64-bit case is a dummy 115100d0963fSdilpreet * function - so no need to swap 115200d0963fSdilpreet */ 115300d0963fSdilpreet case sizeof (uint64_t): 115400d0963fSdilpreet *(uint64_t *)host_addr = 115500d0963fSdilpreet i_ddi_io_get64(hp, 115600d0963fSdilpreet (uint64_t *)dev_addr); 115700d0963fSdilpreet break; 115800d0963fSdilpreet default: 115900d0963fSdilpreet err = DDI_FAILURE; 116000d0963fSdilpreet break; 116100d0963fSdilpreet } 116200d0963fSdilpreet } else { 116300d0963fSdilpreet switch (size) { 116400d0963fSdilpreet case sizeof (uint8_t): 116500d0963fSdilpreet *(uint8_t *)host_addr = 116600d0963fSdilpreet i_ddi_io_get8(hp, 116700d0963fSdilpreet (uint8_t *)dev_addr); 116800d0963fSdilpreet break; 116900d0963fSdilpreet case sizeof (uint16_t): 117000d0963fSdilpreet *(uint16_t *)host_addr = 117100d0963fSdilpreet i_ddi_io_get16(hp, 117200d0963fSdilpreet (uint16_t *)dev_addr); 117300d0963fSdilpreet break; 117400d0963fSdilpreet case sizeof (uint32_t): 117500d0963fSdilpreet *(uint32_t *)host_addr = 117600d0963fSdilpreet i_ddi_io_get32(hp, 117700d0963fSdilpreet (uint32_t *)dev_addr); 117800d0963fSdilpreet break; 117900d0963fSdilpreet case sizeof (uint64_t): 118000d0963fSdilpreet *(uint64_t *)host_addr = 118100d0963fSdilpreet i_ddi_io_get64(hp, 118200d0963fSdilpreet (uint64_t *)dev_addr); 118300d0963fSdilpreet break; 118400d0963fSdilpreet default: 118500d0963fSdilpreet err = DDI_FAILURE; 118600d0963fSdilpreet break; 118700d0963fSdilpreet } 118800d0963fSdilpreet } 118900d0963fSdilpreet } else { 119000d0963fSdilpreet if (hdlp->ah_acc.devacc_attr_endian_flags == 119100d0963fSdilpreet DDI_STRUCTURE_BE_ACC) { 119200d0963fSdilpreet switch (in_args->size) { 119300d0963fSdilpreet case sizeof (uint8_t): 119400d0963fSdilpreet *(uint8_t *)host_addr = 119500d0963fSdilpreet *(uint8_t *)dev_addr; 119600d0963fSdilpreet break; 119700d0963fSdilpreet case sizeof (uint16_t): 119800d0963fSdilpreet *(uint16_t *)host_addr = 119900d0963fSdilpreet ddi_swap16(*(uint16_t *)dev_addr); 120000d0963fSdilpreet break; 120100d0963fSdilpreet case sizeof (uint32_t): 120200d0963fSdilpreet *(uint32_t *)host_addr = 120300d0963fSdilpreet ddi_swap32(*(uint32_t *)dev_addr); 120400d0963fSdilpreet break; 120500d0963fSdilpreet case sizeof (uint64_t): 120600d0963fSdilpreet *(uint64_t *)host_addr = 120700d0963fSdilpreet ddi_swap64(*(uint64_t *)dev_addr); 120800d0963fSdilpreet break; 120900d0963fSdilpreet default: 121000d0963fSdilpreet err = DDI_FAILURE; 121100d0963fSdilpreet break; 121200d0963fSdilpreet } 121300d0963fSdilpreet } else { 121400d0963fSdilpreet switch (in_args->size) { 121500d0963fSdilpreet case sizeof (uint8_t): 121600d0963fSdilpreet *(uint8_t *)host_addr = 121700d0963fSdilpreet *(uint8_t *)dev_addr; 121800d0963fSdilpreet break; 121900d0963fSdilpreet case sizeof (uint16_t): 122000d0963fSdilpreet *(uint16_t *)host_addr = 122100d0963fSdilpreet *(uint16_t *)dev_addr; 122200d0963fSdilpreet break; 122300d0963fSdilpreet case sizeof (uint32_t): 122400d0963fSdilpreet *(uint32_t *)host_addr = 122500d0963fSdilpreet *(uint32_t *)dev_addr; 122600d0963fSdilpreet break; 122700d0963fSdilpreet case sizeof (uint64_t): 122800d0963fSdilpreet *(uint64_t *)host_addr = 122900d0963fSdilpreet *(uint64_t *)dev_addr; 123000d0963fSdilpreet break; 123100d0963fSdilpreet default: 123200d0963fSdilpreet err = DDI_FAILURE; 123300d0963fSdilpreet break; 123400d0963fSdilpreet } 123500d0963fSdilpreet } 123600d0963fSdilpreet } 123700d0963fSdilpreet host_addr += size; 123800d0963fSdilpreet if (flags == DDI_DEV_AUTOINCR) 123900d0963fSdilpreet dev_addr += size; 124000d0963fSdilpreet } 124100d0963fSdilpreet return (err); 124200d0963fSdilpreet } 124300d0963fSdilpreet 124400d0963fSdilpreet /*ARGSUSED*/ 124500d0963fSdilpreet int 124600d0963fSdilpreet pci_common_peekpoke(dev_info_t *dip, dev_info_t *rdip, 124700d0963fSdilpreet ddi_ctl_enum_t ctlop, void *arg, void *result) 124800d0963fSdilpreet { 124900d0963fSdilpreet if (ctlop == DDI_CTLOPS_PEEK) 125000d0963fSdilpreet return (pci_common_ctlops_peek((peekpoke_ctlops_t *)arg)); 125100d0963fSdilpreet else 125200d0963fSdilpreet return (pci_common_ctlops_poke((peekpoke_ctlops_t *)arg)); 125300d0963fSdilpreet } 125400d0963fSdilpreet 1255649d4cceSanish /* 1256649d4cceSanish * These are the get and put functions to be shared with drivers. The 1257649d4cceSanish * mutex locking is done inside the functions referenced, rather than 1258649d4cceSanish * here, and is thus shared across PCI child drivers and any other 1259649d4cceSanish * consumers of PCI config space (such as the ACPI subsystem). 1260649d4cceSanish * 1261649d4cceSanish * The configuration space addresses come in as pointers. This is fine on 1262649d4cceSanish * a 32-bit system, where the VM space and configuration space are the same 1263649d4cceSanish * size. It's not such a good idea on a 64-bit system, where memory 1264649d4cceSanish * addresses are twice as large as configuration space addresses. At some 1265649d4cceSanish * point in the call tree we need to take a stand and say "you are 32-bit 1266649d4cceSanish * from this time forth", and this seems like a nice self-contained place. 1267649d4cceSanish */ 1268649d4cceSanish 1269649d4cceSanish uint8_t 1270649d4cceSanish pci_config_rd8(ddi_acc_impl_t *hdlp, uint8_t *addr) 1271649d4cceSanish { 1272649d4cceSanish pci_acc_cfblk_t *cfp; 1273649d4cceSanish uint8_t rval; 1274649d4cceSanish int reg; 1275649d4cceSanish 1276649d4cceSanish ASSERT64(((uintptr_t)addr >> 32) == 0); 1277649d4cceSanish 1278649d4cceSanish reg = (int)(uintptr_t)addr; 1279649d4cceSanish 1280649d4cceSanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 1281649d4cceSanish 1282649d4cceSanish rval = (*pci_getb_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum, 1283649d4cceSanish reg); 1284649d4cceSanish 1285649d4cceSanish return (rval); 1286649d4cceSanish } 1287649d4cceSanish 1288649d4cceSanish void 1289649d4cceSanish pci_config_rep_rd8(ddi_acc_impl_t *hdlp, uint8_t *host_addr, 1290649d4cceSanish uint8_t *dev_addr, size_t repcount, uint_t flags) 1291649d4cceSanish { 1292649d4cceSanish uint8_t *h, *d; 1293649d4cceSanish 1294649d4cceSanish h = host_addr; 1295649d4cceSanish d = dev_addr; 1296649d4cceSanish 1297649d4cceSanish if (flags == DDI_DEV_AUTOINCR) 1298649d4cceSanish for (; repcount; repcount--) 1299649d4cceSanish *h++ = pci_config_rd8(hdlp, d++); 1300649d4cceSanish else 1301649d4cceSanish for (; repcount; repcount--) 1302649d4cceSanish *h++ = pci_config_rd8(hdlp, d); 1303649d4cceSanish } 1304649d4cceSanish 1305649d4cceSanish uint16_t 1306649d4cceSanish pci_config_rd16(ddi_acc_impl_t *hdlp, uint16_t *addr) 1307649d4cceSanish { 1308649d4cceSanish pci_acc_cfblk_t *cfp; 1309649d4cceSanish uint16_t rval; 1310649d4cceSanish int reg; 1311649d4cceSanish 1312649d4cceSanish ASSERT64(((uintptr_t)addr >> 32) == 0); 1313649d4cceSanish 1314649d4cceSanish reg = (int)(uintptr_t)addr; 1315649d4cceSanish 1316649d4cceSanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 1317649d4cceSanish 1318649d4cceSanish rval = (*pci_getw_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum, 1319649d4cceSanish reg); 1320649d4cceSanish 1321649d4cceSanish return (rval); 1322649d4cceSanish } 1323649d4cceSanish 1324649d4cceSanish void 1325649d4cceSanish pci_config_rep_rd16(ddi_acc_impl_t *hdlp, uint16_t *host_addr, 1326649d4cceSanish uint16_t *dev_addr, size_t repcount, uint_t flags) 1327649d4cceSanish { 1328649d4cceSanish uint16_t *h, *d; 1329649d4cceSanish 1330649d4cceSanish h = host_addr; 1331649d4cceSanish d = dev_addr; 1332649d4cceSanish 1333649d4cceSanish if (flags == DDI_DEV_AUTOINCR) 1334649d4cceSanish for (; repcount; repcount--) 1335649d4cceSanish *h++ = pci_config_rd16(hdlp, d++); 1336649d4cceSanish else 1337649d4cceSanish for (; repcount; repcount--) 1338649d4cceSanish *h++ = pci_config_rd16(hdlp, d); 1339649d4cceSanish } 1340649d4cceSanish 1341649d4cceSanish uint32_t 1342649d4cceSanish pci_config_rd32(ddi_acc_impl_t *hdlp, uint32_t *addr) 1343649d4cceSanish { 1344649d4cceSanish pci_acc_cfblk_t *cfp; 1345649d4cceSanish uint32_t rval; 1346649d4cceSanish int reg; 1347649d4cceSanish 1348649d4cceSanish ASSERT64(((uintptr_t)addr >> 32) == 0); 1349649d4cceSanish 1350649d4cceSanish reg = (int)(uintptr_t)addr; 1351649d4cceSanish 1352649d4cceSanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 1353649d4cceSanish 1354649d4cceSanish rval = (*pci_getl_func)(cfp->c_busnum, cfp->c_devnum, 1355649d4cceSanish cfp->c_funcnum, reg); 1356649d4cceSanish 1357649d4cceSanish return (rval); 1358649d4cceSanish } 1359649d4cceSanish 1360649d4cceSanish void 1361649d4cceSanish pci_config_rep_rd32(ddi_acc_impl_t *hdlp, uint32_t *host_addr, 1362649d4cceSanish uint32_t *dev_addr, size_t repcount, uint_t flags) 1363649d4cceSanish { 1364649d4cceSanish uint32_t *h, *d; 1365649d4cceSanish 1366649d4cceSanish h = host_addr; 1367649d4cceSanish d = dev_addr; 1368649d4cceSanish 1369649d4cceSanish if (flags == DDI_DEV_AUTOINCR) 1370649d4cceSanish for (; repcount; repcount--) 1371649d4cceSanish *h++ = pci_config_rd32(hdlp, d++); 1372649d4cceSanish else 1373649d4cceSanish for (; repcount; repcount--) 1374649d4cceSanish *h++ = pci_config_rd32(hdlp, d); 1375649d4cceSanish } 1376649d4cceSanish 1377649d4cceSanish 1378649d4cceSanish void 1379649d4cceSanish pci_config_wr8(ddi_acc_impl_t *hdlp, uint8_t *addr, uint8_t value) 1380649d4cceSanish { 1381649d4cceSanish pci_acc_cfblk_t *cfp; 1382649d4cceSanish int reg; 1383649d4cceSanish 1384649d4cceSanish ASSERT64(((uintptr_t)addr >> 32) == 0); 1385649d4cceSanish 1386649d4cceSanish reg = (int)(uintptr_t)addr; 1387649d4cceSanish 1388649d4cceSanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 1389649d4cceSanish 1390649d4cceSanish (*pci_putb_func)(cfp->c_busnum, cfp->c_devnum, 1391649d4cceSanish cfp->c_funcnum, reg, value); 1392649d4cceSanish } 1393649d4cceSanish 1394649d4cceSanish void 1395649d4cceSanish pci_config_rep_wr8(ddi_acc_impl_t *hdlp, uint8_t *host_addr, 1396649d4cceSanish uint8_t *dev_addr, size_t repcount, uint_t flags) 1397649d4cceSanish { 1398649d4cceSanish uint8_t *h, *d; 1399649d4cceSanish 1400649d4cceSanish h = host_addr; 1401649d4cceSanish d = dev_addr; 1402649d4cceSanish 1403649d4cceSanish if (flags == DDI_DEV_AUTOINCR) 1404649d4cceSanish for (; repcount; repcount--) 1405649d4cceSanish pci_config_wr8(hdlp, d++, *h++); 1406649d4cceSanish else 1407649d4cceSanish for (; repcount; repcount--) 1408649d4cceSanish pci_config_wr8(hdlp, d, *h++); 1409649d4cceSanish } 1410649d4cceSanish 1411649d4cceSanish void 1412649d4cceSanish pci_config_wr16(ddi_acc_impl_t *hdlp, uint16_t *addr, uint16_t value) 1413649d4cceSanish { 1414649d4cceSanish pci_acc_cfblk_t *cfp; 1415649d4cceSanish int reg; 1416649d4cceSanish 1417649d4cceSanish ASSERT64(((uintptr_t)addr >> 32) == 0); 1418649d4cceSanish 1419649d4cceSanish reg = (int)(uintptr_t)addr; 1420649d4cceSanish 1421649d4cceSanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 1422649d4cceSanish 1423649d4cceSanish (*pci_putw_func)(cfp->c_busnum, cfp->c_devnum, 1424649d4cceSanish cfp->c_funcnum, reg, value); 1425649d4cceSanish } 1426649d4cceSanish 1427649d4cceSanish void 1428649d4cceSanish pci_config_rep_wr16(ddi_acc_impl_t *hdlp, uint16_t *host_addr, 1429649d4cceSanish uint16_t *dev_addr, size_t repcount, uint_t flags) 1430649d4cceSanish { 1431649d4cceSanish uint16_t *h, *d; 1432649d4cceSanish 1433649d4cceSanish h = host_addr; 1434649d4cceSanish d = dev_addr; 1435649d4cceSanish 1436649d4cceSanish if (flags == DDI_DEV_AUTOINCR) 1437649d4cceSanish for (; repcount; repcount--) 1438649d4cceSanish pci_config_wr16(hdlp, d++, *h++); 1439649d4cceSanish else 1440649d4cceSanish for (; repcount; repcount--) 1441649d4cceSanish pci_config_wr16(hdlp, d, *h++); 1442649d4cceSanish } 1443649d4cceSanish 1444649d4cceSanish void 1445649d4cceSanish pci_config_wr32(ddi_acc_impl_t *hdlp, uint32_t *addr, uint32_t value) 1446649d4cceSanish { 1447649d4cceSanish pci_acc_cfblk_t *cfp; 1448649d4cceSanish int reg; 1449649d4cceSanish 1450649d4cceSanish ASSERT64(((uintptr_t)addr >> 32) == 0); 1451649d4cceSanish 1452649d4cceSanish reg = (int)(uintptr_t)addr; 1453649d4cceSanish 1454649d4cceSanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 1455649d4cceSanish 1456649d4cceSanish (*pci_putl_func)(cfp->c_busnum, cfp->c_devnum, 1457649d4cceSanish cfp->c_funcnum, reg, value); 1458649d4cceSanish } 1459649d4cceSanish 1460649d4cceSanish void 1461649d4cceSanish pci_config_rep_wr32(ddi_acc_impl_t *hdlp, uint32_t *host_addr, 1462649d4cceSanish uint32_t *dev_addr, size_t repcount, uint_t flags) 1463649d4cceSanish { 1464649d4cceSanish uint32_t *h, *d; 1465649d4cceSanish 1466649d4cceSanish h = host_addr; 1467649d4cceSanish d = dev_addr; 1468649d4cceSanish 1469649d4cceSanish if (flags == DDI_DEV_AUTOINCR) 1470649d4cceSanish for (; repcount; repcount--) 1471649d4cceSanish pci_config_wr32(hdlp, d++, *h++); 1472649d4cceSanish else 1473649d4cceSanish for (; repcount; repcount--) 1474649d4cceSanish pci_config_wr32(hdlp, d, *h++); 1475649d4cceSanish } 1476649d4cceSanish 1477649d4cceSanish uint64_t 1478649d4cceSanish pci_config_rd64(ddi_acc_impl_t *hdlp, uint64_t *addr) 1479649d4cceSanish { 1480649d4cceSanish uint32_t lw_val; 1481649d4cceSanish uint32_t hi_val; 1482649d4cceSanish uint32_t *dp; 1483649d4cceSanish uint64_t val; 1484649d4cceSanish 1485649d4cceSanish dp = (uint32_t *)addr; 1486649d4cceSanish lw_val = pci_config_rd32(hdlp, dp); 1487649d4cceSanish dp++; 1488649d4cceSanish hi_val = pci_config_rd32(hdlp, dp); 1489649d4cceSanish val = ((uint64_t)hi_val << 32) | lw_val; 1490649d4cceSanish return (val); 1491649d4cceSanish } 1492649d4cceSanish 1493649d4cceSanish void 1494649d4cceSanish pci_config_wr64(ddi_acc_impl_t *hdlp, uint64_t *addr, uint64_t value) 1495649d4cceSanish { 1496649d4cceSanish uint32_t lw_val; 1497649d4cceSanish uint32_t hi_val; 1498649d4cceSanish uint32_t *dp; 1499649d4cceSanish 1500649d4cceSanish dp = (uint32_t *)addr; 1501649d4cceSanish lw_val = (uint32_t)(value & 0xffffffff); 1502649d4cceSanish hi_val = (uint32_t)(value >> 32); 1503649d4cceSanish pci_config_wr32(hdlp, dp, lw_val); 1504649d4cceSanish dp++; 1505649d4cceSanish pci_config_wr32(hdlp, dp, hi_val); 1506649d4cceSanish } 1507649d4cceSanish 1508649d4cceSanish void 1509649d4cceSanish pci_config_rep_rd64(ddi_acc_impl_t *hdlp, uint64_t *host_addr, 1510649d4cceSanish uint64_t *dev_addr, size_t repcount, uint_t flags) 1511649d4cceSanish { 1512649d4cceSanish if (flags == DDI_DEV_AUTOINCR) { 1513649d4cceSanish for (; repcount; repcount--) 1514649d4cceSanish *host_addr++ = pci_config_rd64(hdlp, dev_addr++); 1515649d4cceSanish } else { 1516649d4cceSanish for (; repcount; repcount--) 1517649d4cceSanish *host_addr++ = pci_config_rd64(hdlp, dev_addr); 1518649d4cceSanish } 1519649d4cceSanish } 1520649d4cceSanish 1521649d4cceSanish void 1522649d4cceSanish pci_config_rep_wr64(ddi_acc_impl_t *hdlp, uint64_t *host_addr, 1523649d4cceSanish uint64_t *dev_addr, size_t repcount, uint_t flags) 1524649d4cceSanish { 1525649d4cceSanish if (flags == DDI_DEV_AUTOINCR) { 1526649d4cceSanish for (; repcount; repcount--) 1527649d4cceSanish pci_config_wr64(hdlp, host_addr++, *dev_addr++); 1528649d4cceSanish } else { 1529649d4cceSanish for (; repcount; repcount--) 1530649d4cceSanish pci_config_wr64(hdlp, host_addr++, *dev_addr); 1531649d4cceSanish } 1532649d4cceSanish } 1533649d4cceSanish 1534649d4cceSanish 1535649d4cceSanish /* 1536649d4cceSanish * Enable Legacy PCI config space access for the following four north bridges 1537649d4cceSanish * Host bridge: AMD HyperTransport Technology Configuration 1538649d4cceSanish * Host bridge: AMD Address Map 1539649d4cceSanish * Host bridge: AMD DRAM Controller 1540649d4cceSanish * Host bridge: AMD Miscellaneous Control 1541649d4cceSanish */ 1542649d4cceSanish int 1543649d4cceSanish is_amd_northbridge(dev_info_t *dip) 1544649d4cceSanish { 1545649d4cceSanish int vendor_id, device_id; 1546649d4cceSanish 1547649d4cceSanish vendor_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1548649d4cceSanish "vendor-id", -1); 1549649d4cceSanish device_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1550649d4cceSanish "device-id", -1); 1551649d4cceSanish 1552649d4cceSanish if (IS_AMD_NTBRIDGE(vendor_id, device_id)) 1553649d4cceSanish return (0); 1554649d4cceSanish 1555649d4cceSanish return (1); 1556649d4cceSanish } 1557