1881Sjohnny /* 2881Sjohnny * CDDL HEADER START 3881Sjohnny * 4881Sjohnny * The contents of this file are subject to the terms of the 51542Sjohnny * Common Development and Distribution License (the "License"). 61542Sjohnny * You may not use this file except in compliance with the License. 7881Sjohnny * 8881Sjohnny * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9881Sjohnny * or http://www.opensolaris.org/os/licensing. 10881Sjohnny * See the License for the specific language governing permissions 11881Sjohnny * and limitations under the License. 12881Sjohnny * 13881Sjohnny * When distributing Covered Code, include this CDDL HEADER in each 14881Sjohnny * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15881Sjohnny * If applicable, add the following below this CDDL HEADER, with the 16881Sjohnny * fields enclosed by brackets "[]" replaced with your own identifying 17881Sjohnny * information: Portions Copyright [yyyy] [name of copyright owner] 18881Sjohnny * 19881Sjohnny * CDDL HEADER END 20881Sjohnny */ 21881Sjohnny 22881Sjohnny /* 233446Smrj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24881Sjohnny * Use is subject to license terms. 25881Sjohnny */ 26881Sjohnny 27881Sjohnny #pragma ident "%Z%%M% %I% %E% SMI" 28881Sjohnny 29881Sjohnny /* 30881Sjohnny * File that has code which is common between pci(7d) and npe(7d) 31881Sjohnny * It shares the following: 32881Sjohnny * - interrupt code 33881Sjohnny * - pci_tools ioctl code 34881Sjohnny * - name_child code 35881Sjohnny * - set_parent_private_data code 36881Sjohnny */ 37881Sjohnny 38881Sjohnny #include <sys/conf.h> 39881Sjohnny #include <sys/pci.h> 40881Sjohnny #include <sys/sunndi.h> 41916Sschwartz #include <sys/mach_intr.h> 42881Sjohnny #include <sys/hotplug/pci/pcihp.h> 43881Sjohnny #include <sys/pci_intr_lib.h> 44881Sjohnny #include <sys/psm.h> 45881Sjohnny #include <sys/policy.h> 46881Sjohnny #include <sys/sysmacros.h> 47916Sschwartz #include <sys/clock.h> 483446Smrj #include <sys/apic.h> 49881Sjohnny #include <sys/pci_tools.h> 50881Sjohnny #include <io/pci/pci_var.h> 51881Sjohnny #include <io/pci/pci_tools_ext.h> 52881Sjohnny #include <io/pci/pci_common.h> 531083Sanish #include <sys/pci_cfgspace.h> 541083Sanish #include <sys/pci_impl.h> 554160Sjveta #include <sys/pci_cap.h> 56881Sjohnny 57881Sjohnny /* 58881Sjohnny * Function prototypes 59881Sjohnny */ 60881Sjohnny static int pci_get_priority(dev_info_t *, ddi_intr_handle_impl_t *, int *); 61881Sjohnny static int pci_enable_intr(dev_info_t *, dev_info_t *, 62881Sjohnny ddi_intr_handle_impl_t *, uint32_t); 63881Sjohnny static void pci_disable_intr(dev_info_t *, dev_info_t *, 64881Sjohnny ddi_intr_handle_impl_t *, uint32_t); 65881Sjohnny 66881Sjohnny /* Extern decalration for pcplusmp module */ 67881Sjohnny extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *, 68881Sjohnny psm_intr_op_t, int *); 69881Sjohnny 70881Sjohnny 71881Sjohnny /* 72881Sjohnny * pci_name_child: 73881Sjohnny * 74881Sjohnny * Assign the address portion of the node name 75881Sjohnny */ 76881Sjohnny int 77881Sjohnny pci_common_name_child(dev_info_t *child, char *name, int namelen) 78881Sjohnny { 79881Sjohnny int dev, func, length; 80881Sjohnny char **unit_addr; 81881Sjohnny uint_t n; 82881Sjohnny pci_regspec_t *pci_rp; 83881Sjohnny 84881Sjohnny if (ndi_dev_is_persistent_node(child) == 0) { 85881Sjohnny /* 86881Sjohnny * For .conf node, use "unit-address" property 87881Sjohnny */ 88881Sjohnny if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 89881Sjohnny DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) != 90881Sjohnny DDI_PROP_SUCCESS) { 91881Sjohnny cmn_err(CE_WARN, "cannot find unit-address in %s.conf", 92881Sjohnny ddi_get_name(child)); 93881Sjohnny return (DDI_FAILURE); 94881Sjohnny } 95881Sjohnny if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 96881Sjohnny cmn_err(CE_WARN, "unit-address property in %s.conf" 97881Sjohnny " not well-formed", ddi_get_name(child)); 98881Sjohnny ddi_prop_free(unit_addr); 99881Sjohnny return (DDI_FAILURE); 100881Sjohnny } 101881Sjohnny (void) snprintf(name, namelen, "%s", *unit_addr); 102881Sjohnny ddi_prop_free(unit_addr); 103881Sjohnny return (DDI_SUCCESS); 104881Sjohnny } 105881Sjohnny 106881Sjohnny if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 107881Sjohnny "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) { 108881Sjohnny cmn_err(CE_WARN, "cannot find reg property in %s", 109881Sjohnny ddi_get_name(child)); 110881Sjohnny return (DDI_FAILURE); 111881Sjohnny } 112881Sjohnny 113881Sjohnny /* copy the device identifications */ 114881Sjohnny dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 115881Sjohnny func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); 116881Sjohnny 117881Sjohnny /* 118881Sjohnny * free the memory allocated by ddi_prop_lookup_int_array 119881Sjohnny */ 120881Sjohnny ddi_prop_free(pci_rp); 121881Sjohnny 122881Sjohnny if (func != 0) { 123881Sjohnny (void) snprintf(name, namelen, "%x,%x", dev, func); 124881Sjohnny } else { 125881Sjohnny (void) snprintf(name, namelen, "%x", dev); 126881Sjohnny } 127881Sjohnny 128881Sjohnny return (DDI_SUCCESS); 129881Sjohnny } 130881Sjohnny 131881Sjohnny /* 132881Sjohnny * Interrupt related code: 133881Sjohnny * 134881Sjohnny * The following busop is common to npe and pci drivers 135881Sjohnny * bus_introp 136881Sjohnny */ 137881Sjohnny 138881Sjohnny /* 139881Sjohnny * Create the ddi_parent_private_data for a pseudo child. 140881Sjohnny */ 141881Sjohnny void 142881Sjohnny pci_common_set_parent_private_data(dev_info_t *dip) 143881Sjohnny { 144881Sjohnny struct ddi_parent_private_data *pdptr; 145881Sjohnny 146881Sjohnny pdptr = (struct ddi_parent_private_data *)kmem_zalloc( 147881Sjohnny (sizeof (struct ddi_parent_private_data) + 148*4397Sschwartz sizeof (struct intrspec)), KM_SLEEP); 149881Sjohnny pdptr->par_intr = (struct intrspec *)(pdptr + 1); 150881Sjohnny pdptr->par_nintr = 1; 151881Sjohnny ddi_set_parent_data(dip, pdptr); 152881Sjohnny } 153881Sjohnny 154881Sjohnny /* 155881Sjohnny * pci_get_priority: 156881Sjohnny * Figure out the priority of the device 157881Sjohnny */ 158881Sjohnny static int 159881Sjohnny pci_get_priority(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, int *pri) 160881Sjohnny { 161881Sjohnny struct intrspec *ispec; 162881Sjohnny 163881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_get_priority: dip = 0x%p, hdlp = %p\n", 164881Sjohnny (void *)dip, (void *)hdlp)); 165881Sjohnny 166881Sjohnny if ((ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip, 167881Sjohnny hdlp->ih_inum)) == NULL) { 168881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) { 169881Sjohnny int class = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 170881Sjohnny DDI_PROP_DONTPASS, "class-code", -1); 171881Sjohnny 172881Sjohnny *pri = (class == -1) ? 1 : pci_devclass_to_ipl(class); 173881Sjohnny pci_common_set_parent_private_data(hdlp->ih_dip); 174881Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip, 175881Sjohnny hdlp->ih_inum); 176881Sjohnny return (DDI_SUCCESS); 177881Sjohnny } 178881Sjohnny return (DDI_FAILURE); 179881Sjohnny } 180881Sjohnny 181881Sjohnny *pri = ispec->intrspec_pri; 182881Sjohnny return (DDI_SUCCESS); 183881Sjohnny } 184881Sjohnny 185881Sjohnny 186881Sjohnny 187881Sjohnny static int pcie_pci_intr_pri_counter = 0; 188881Sjohnny 189881Sjohnny /* 190881Sjohnny * pci_common_intr_ops: bus_intr_op() function for interrupt support 191881Sjohnny */ 192881Sjohnny int 193881Sjohnny pci_common_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, 194881Sjohnny ddi_intr_handle_impl_t *hdlp, void *result) 195881Sjohnny { 196881Sjohnny int priority = 0; 197881Sjohnny int psm_status = 0; 198881Sjohnny int pci_status = 0; 199881Sjohnny int pci_rval, psm_rval = PSM_FAILURE; 200881Sjohnny int types = 0; 201881Sjohnny int pciepci = 0; 2021542Sjohnny int i, j, count; 2034160Sjveta int rv; 204881Sjohnny int behavior; 2051997Sanish int cap_ptr; 2064160Sjveta uint16_t msi_cap_base, msix_cap_base, cap_ctrl; 2074160Sjveta char *prop; 208881Sjohnny ddi_intrspec_t isp; 209881Sjohnny struct intrspec *ispec; 210881Sjohnny ddi_intr_handle_impl_t tmp_hdl; 211881Sjohnny ddi_intr_msix_t *msix_p; 2121087Sschwartz ihdl_plat_t *ihdl_plat_datap; 2131542Sjohnny ddi_intr_handle_t *h_array; 2141997Sanish ddi_acc_handle_t handle; 215881Sjohnny 216881Sjohnny DDI_INTR_NEXDBG((CE_CONT, 217881Sjohnny "pci_common_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n", 218881Sjohnny (void *)pdip, (void *)rdip, intr_op, (void *)hdlp)); 219881Sjohnny 220881Sjohnny /* Process the request */ 221881Sjohnny switch (intr_op) { 222881Sjohnny case DDI_INTROP_SUPPORTED_TYPES: 2234160Sjveta /* 2244160Sjveta * First we determine the interrupt types supported by the 2254160Sjveta * device itself, then we filter them through what the OS 2264160Sjveta * and system supports. We determine system-level 2274160Sjveta * interrupt type support for anything other than fixed intrs 2284160Sjveta * through the psm_intr_ops vector 2294160Sjveta */ 2304160Sjveta rv = DDI_FAILURE; 2314160Sjveta 232881Sjohnny /* Fixed supported by default */ 2334160Sjveta types = DDI_INTR_TYPE_FIXED; 2344160Sjveta 2354160Sjveta if (psm_intr_ops == NULL) { 2364160Sjveta *(int *)result = types; 2374160Sjveta return (DDI_SUCCESS); 2384160Sjveta } 2394160Sjveta if (pci_config_setup(rdip, &handle) != DDI_SUCCESS) 2404160Sjveta return (DDI_FAILURE); 241881Sjohnny 2424160Sjveta /* Sanity test cap control values if found */ 2434160Sjveta 2444160Sjveta if (PCI_CAP_LOCATE(handle, PCI_CAP_ID_MSI, &msi_cap_base) == 2454160Sjveta DDI_SUCCESS) { 2464160Sjveta cap_ctrl = PCI_CAP_GET16(handle, 0, msi_cap_base, 2474160Sjveta PCI_MSI_CTRL); 2484160Sjveta if (cap_ctrl == PCI_CAP_EINVAL16) 2494160Sjveta goto SUPPORTED_TYPES_OUT; 2504160Sjveta 2514160Sjveta types |= DDI_INTR_TYPE_MSI; 2524160Sjveta } 2534160Sjveta 2544160Sjveta if (PCI_CAP_LOCATE(handle, PCI_CAP_ID_MSI_X, &msix_cap_base) == 2554160Sjveta DDI_SUCCESS) { 2564160Sjveta cap_ctrl = PCI_CAP_GET16(handle, 0, msix_cap_base, 2574160Sjveta PCI_MSIX_CTRL); 2584160Sjveta if (cap_ctrl == PCI_CAP_EINVAL16) 2594160Sjveta goto SUPPORTED_TYPES_OUT; 260881Sjohnny 2614160Sjveta types |= DDI_INTR_TYPE_MSIX; 2624160Sjveta } 2634160Sjveta 2644160Sjveta /* 2654160Sjveta * Filter device-level types through system-level support 2664160Sjveta */ 2674160Sjveta 2684160Sjveta /* No official MSI-X support for now */ 2694160Sjveta types &= ~DDI_INTR_TYPE_MSIX; 2704160Sjveta 2714160Sjveta tmp_hdl.ih_type = types; 2724160Sjveta if ((*psm_intr_ops)(rdip, &tmp_hdl, PSM_INTR_OP_CHECK_MSI, 2734160Sjveta &types) != PSM_SUCCESS) 2744160Sjveta goto SUPPORTED_TYPES_OUT; 2754160Sjveta 2764160Sjveta DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 2774160Sjveta "rdip: 0x%p supported types: 0x%x\n", (void *)rdip, 2784160Sjveta *(int *)result)); 279881Sjohnny 2804160Sjveta /* 2814160Sjveta * Export any MSI/MSI-X cap locations via properties 2824160Sjveta */ 2834160Sjveta if (types & DDI_INTR_TYPE_MSI) { 2844160Sjveta if (ndi_prop_update_int(DDI_DEV_T_NONE, rdip, 2854160Sjveta "pci-msi-capid-pointer", (int)msi_cap_base) != 2864160Sjveta DDI_PROP_SUCCESS) 2874160Sjveta goto SUPPORTED_TYPES_OUT; 288881Sjohnny } 2894160Sjveta if (types & DDI_INTR_TYPE_MSIX) { 2904160Sjveta if (ndi_prop_update_int(DDI_DEV_T_NONE, rdip, 2914160Sjveta "pci-msix-capid-pointer", (int)msix_cap_base) != 2924160Sjveta DDI_PROP_SUCCESS) 2934160Sjveta goto SUPPORTED_TYPES_OUT; 2944160Sjveta } 2954160Sjveta 2964160Sjveta rv = DDI_SUCCESS; 2974160Sjveta 2984160Sjveta SUPPORTED_TYPES_OUT: 2994160Sjveta *(int *)result = types; 3004160Sjveta pci_config_teardown(&handle); 3014160Sjveta return (rv); 3024160Sjveta 3032580Sanish case DDI_INTROP_NAVAIL: 304881Sjohnny case DDI_INTROP_NINTRS: 3052580Sanish if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) { 3062580Sanish if (pci_msi_get_nintrs(hdlp->ih_dip, hdlp->ih_type, 3072580Sanish result) != DDI_SUCCESS) 3082580Sanish return (DDI_FAILURE); 3092580Sanish } else { 3102580Sanish *(int *)result = i_ddi_get_intx_nintrs(hdlp->ih_dip); 3112580Sanish if (*(int *)result == 0) 3122580Sanish return (DDI_FAILURE); 3132580Sanish } 314881Sjohnny break; 315881Sjohnny case DDI_INTROP_ALLOC: 316881Sjohnny /* 317881Sjohnny * MSI or MSIX (figure out number of vectors available) 318881Sjohnny * FIXED interrupts: just return available interrupts 319881Sjohnny */ 320881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && 321881Sjohnny (psm_intr_ops != NULL) && 322881Sjohnny (pci_get_priority(rdip, hdlp, &priority) == DDI_SUCCESS)) { 323881Sjohnny /* 324881Sjohnny * Following check is a special case for 'pcie_pci'. 325881Sjohnny * This makes sure vectors with the right priority 326881Sjohnny * are allocated for pcie_pci during ALLOC time. 327881Sjohnny */ 328881Sjohnny if (strcmp(ddi_driver_name(rdip), "pcie_pci") == 0) { 329881Sjohnny hdlp->ih_pri = 330881Sjohnny (pcie_pci_intr_pri_counter % 2) ? 4 : 7; 331881Sjohnny pciepci = 1; 332881Sjohnny } else 333881Sjohnny hdlp->ih_pri = priority; 3341717Swesolows behavior = (int)(uintptr_t)hdlp->ih_scratch2; 3351997Sanish 3361997Sanish /* 3371997Sanish * Cache in the config handle and cap_ptr 3381997Sanish */ 3391997Sanish if (i_ddi_get_pci_config_handle(rdip) == NULL) { 3401997Sanish if (pci_config_setup(rdip, &handle) != 3411997Sanish DDI_SUCCESS) 3421997Sanish return (DDI_FAILURE); 3431997Sanish i_ddi_set_pci_config_handle(rdip, handle); 3441997Sanish } 3451997Sanish 3464160Sjveta prop = NULL; 3474160Sjveta cap_ptr = 0; 3484160Sjveta if (hdlp->ih_type == DDI_INTR_TYPE_MSI) 3494160Sjveta prop = "pci-msi-capid-pointer"; 3504160Sjveta else if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) 3514160Sjveta prop = "pci-msix-capid-pointer"; 3521997Sanish 3534160Sjveta /* 3544160Sjveta * Enforce the calling of DDI_INTROP_SUPPORTED_TYPES 3554160Sjveta * for MSI(X) before allocation 3564160Sjveta */ 3574160Sjveta if (prop != NULL) { 3581997Sanish cap_ptr = ddi_prop_get_int(DDI_DEV_T_ANY, rdip, 3594160Sjveta DDI_PROP_DONTPASS, prop, 0); 3604160Sjveta if (cap_ptr == 0) { 3614160Sjveta DDI_INTR_NEXDBG((CE_CONT, 3624160Sjveta "pci_common_intr_ops: rdip: 0x%p " 3634160Sjveta "attempted MSI(X) alloc without " 3644160Sjveta "cap property\n", (void *)rdip)); 3654160Sjveta return (DDI_FAILURE); 3664160Sjveta } 3671997Sanish } 3684160Sjveta i_ddi_set_msi_msix_cap_ptr(rdip, cap_ptr); 3691997Sanish 3704160Sjveta /* 3714160Sjveta * Allocate interrupt vectors 3724160Sjveta */ 373881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 374881Sjohnny PSM_INTR_OP_ALLOC_VECTORS, result); 375881Sjohnny 3763112Sanish if (*(int *)result == 0) 3773112Sanish return (DDI_INTR_NOTFOUND); 3783112Sanish 379881Sjohnny /* verify behavior flag and take appropriate action */ 380881Sjohnny if ((behavior == DDI_INTR_ALLOC_STRICT) && 381881Sjohnny (*(int *)result < hdlp->ih_scratch1)) { 382881Sjohnny DDI_INTR_NEXDBG((CE_CONT, 383881Sjohnny "pci_common_intr_ops: behavior %x, " 384881Sjohnny "couldn't get enough intrs\n", behavior)); 385881Sjohnny hdlp->ih_scratch1 = *(int *)result; 386881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 387881Sjohnny PSM_INTR_OP_FREE_VECTORS, NULL); 388881Sjohnny return (DDI_EAGAIN); 389881Sjohnny } 390881Sjohnny 391881Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) { 392881Sjohnny if (!(msix_p = i_ddi_get_msix(hdlp->ih_dip))) { 393881Sjohnny msix_p = pci_msix_init(hdlp->ih_dip); 394881Sjohnny if (msix_p) 395881Sjohnny i_ddi_set_msix(hdlp->ih_dip, 396881Sjohnny msix_p); 397881Sjohnny } 398881Sjohnny } 399881Sjohnny 400881Sjohnny if (pciepci) { 401881Sjohnny /* update priority in ispec */ 402881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, 403*4397Sschwartz (int)hdlp->ih_inum); 404881Sjohnny ispec = (struct intrspec *)isp; 405881Sjohnny if (ispec) 406881Sjohnny ispec->intrspec_pri = hdlp->ih_pri; 407881Sjohnny ++pcie_pci_intr_pri_counter; 408881Sjohnny } 409881Sjohnny 410881Sjohnny } else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) { 411881Sjohnny /* Figure out if this device supports MASKING */ 412881Sjohnny pci_rval = pci_intx_get_cap(rdip, &pci_status); 413881Sjohnny if (pci_rval == DDI_SUCCESS && pci_status) 414881Sjohnny hdlp->ih_cap |= pci_status; 415881Sjohnny *(int *)result = 1; /* DDI_INTR_TYPE_FIXED */ 416881Sjohnny } else 417881Sjohnny return (DDI_FAILURE); 418881Sjohnny break; 419881Sjohnny case DDI_INTROP_FREE: 420881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && 421881Sjohnny (psm_intr_ops != NULL)) { 4222018Sanish if (i_ddi_intr_get_current_nintrs(hdlp->ih_dip) - 1 == 4232018Sanish 0) { 4241997Sanish if (handle = i_ddi_get_pci_config_handle( 4251997Sanish rdip)) { 4261997Sanish (void) pci_config_teardown(&handle); 4271997Sanish i_ddi_set_pci_config_handle(rdip, NULL); 4281997Sanish } 4291997Sanish if (cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip)) 4301997Sanish i_ddi_set_msi_msix_cap_ptr(rdip, 0); 4311997Sanish } 4321997Sanish 433881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 434881Sjohnny PSM_INTR_OP_FREE_VECTORS, NULL); 435881Sjohnny 436881Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) { 437881Sjohnny msix_p = i_ddi_get_msix(hdlp->ih_dip); 438881Sjohnny if (msix_p && 4392018Sanish (i_ddi_intr_get_current_nintrs( 440*4397Sschwartz hdlp->ih_dip) - 1) == 0) { 441881Sjohnny pci_msix_fini(msix_p); 442881Sjohnny i_ddi_set_msix(hdlp->ih_dip, NULL); 443881Sjohnny } 444881Sjohnny } 445881Sjohnny } 446881Sjohnny break; 447881Sjohnny case DDI_INTROP_GETPRI: 448881Sjohnny /* Get the priority */ 449881Sjohnny if (pci_get_priority(rdip, hdlp, &priority) != DDI_SUCCESS) 450881Sjohnny return (DDI_FAILURE); 451881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 452881Sjohnny "priority = 0x%x\n", priority)); 453881Sjohnny *(int *)result = priority; 454881Sjohnny break; 455881Sjohnny case DDI_INTROP_SETPRI: 456881Sjohnny /* Validate the interrupt priority passed */ 457881Sjohnny if (*(int *)result > LOCK_LEVEL) 458881Sjohnny return (DDI_FAILURE); 459881Sjohnny 460881Sjohnny /* Ensure that PSM is all initialized */ 461881Sjohnny if (psm_intr_ops == NULL) 462881Sjohnny return (DDI_FAILURE); 463881Sjohnny 464881Sjohnny /* Change the priority */ 465881Sjohnny if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_PRI, result) == 466881Sjohnny PSM_FAILURE) 467881Sjohnny return (DDI_FAILURE); 468881Sjohnny 469881Sjohnny /* update ispec */ 470881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 471881Sjohnny ispec = (struct intrspec *)isp; 472881Sjohnny if (ispec) 473881Sjohnny ispec->intrspec_pri = *(int *)result; 474881Sjohnny break; 475881Sjohnny case DDI_INTROP_ADDISR: 476881Sjohnny /* update ispec */ 477881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 478881Sjohnny ispec = (struct intrspec *)isp; 4791087Sschwartz if (ispec) { 480881Sjohnny ispec->intrspec_func = hdlp->ih_cb_func; 4811087Sschwartz ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 4821087Sschwartz pci_kstat_create(&ihdl_plat_datap->ip_ksp, pdip, hdlp); 4831087Sschwartz } 484881Sjohnny break; 485881Sjohnny case DDI_INTROP_REMISR: 486881Sjohnny /* Get the interrupt structure pointer */ 487881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 488881Sjohnny ispec = (struct intrspec *)isp; 4891087Sschwartz if (ispec) { 490881Sjohnny ispec->intrspec_func = (uint_t (*)()) 0; 4911087Sschwartz ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 4921087Sschwartz if (ihdl_plat_datap->ip_ksp != NULL) 4931087Sschwartz pci_kstat_delete(ihdl_plat_datap->ip_ksp); 4941087Sschwartz } 495881Sjohnny break; 496881Sjohnny case DDI_INTROP_GETCAP: 497881Sjohnny /* 498881Sjohnny * First check the config space and/or 499881Sjohnny * MSI capability register(s) 500881Sjohnny */ 501881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 502881Sjohnny pci_rval = pci_msi_get_cap(rdip, hdlp->ih_type, 503881Sjohnny &pci_status); 504881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 505881Sjohnny pci_rval = pci_intx_get_cap(rdip, &pci_status); 506881Sjohnny 507881Sjohnny /* next check with pcplusmp */ 508881Sjohnny if (psm_intr_ops != NULL) 509881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 510881Sjohnny PSM_INTR_OP_GET_CAP, &psm_status); 511881Sjohnny 512881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned psm_rval = %x, " 513881Sjohnny "psm_status = %x, pci_rval = %x, pci_status = %x\n", 514881Sjohnny psm_rval, psm_status, pci_rval, pci_status)); 515881Sjohnny 516881Sjohnny if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) { 517881Sjohnny *(int *)result = 0; 518881Sjohnny return (DDI_FAILURE); 519881Sjohnny } 520881Sjohnny 521881Sjohnny if (psm_rval == PSM_SUCCESS) 522881Sjohnny *(int *)result = psm_status; 523881Sjohnny 524881Sjohnny if (pci_rval == DDI_SUCCESS) 525881Sjohnny *(int *)result |= pci_status; 526881Sjohnny 527881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned = %x\n", 528881Sjohnny *(int *)result)); 529881Sjohnny break; 530881Sjohnny case DDI_INTROP_SETCAP: 531881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 532881Sjohnny "SETCAP cap=0x%x\n", *(int *)result)); 533881Sjohnny if (psm_intr_ops == NULL) 534881Sjohnny return (DDI_FAILURE); 535881Sjohnny 536881Sjohnny if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) { 537881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "GETCAP: psm_intr_ops" 538881Sjohnny " returned failure\n")); 539881Sjohnny return (DDI_FAILURE); 540881Sjohnny } 541881Sjohnny break; 542881Sjohnny case DDI_INTROP_ENABLE: 543881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE\n")); 544881Sjohnny if (psm_intr_ops == NULL) 545881Sjohnny return (DDI_FAILURE); 546881Sjohnny 547881Sjohnny if (pci_enable_intr(pdip, rdip, hdlp, hdlp->ih_inum) != 548881Sjohnny DDI_SUCCESS) 549881Sjohnny return (DDI_FAILURE); 550881Sjohnny 551881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE " 552881Sjohnny "vector=0x%x\n", hdlp->ih_vector)); 553881Sjohnny break; 554881Sjohnny case DDI_INTROP_DISABLE: 555881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE\n")); 556881Sjohnny if (psm_intr_ops == NULL) 557881Sjohnny return (DDI_FAILURE); 558881Sjohnny 559881Sjohnny pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum); 560881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE " 561881Sjohnny "vector = %x\n", hdlp->ih_vector)); 562881Sjohnny break; 563881Sjohnny case DDI_INTROP_BLOCKENABLE: 564881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 565881Sjohnny "BLOCKENABLE\n")); 566881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_MSI) { 567881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: not MSI\n")); 568881Sjohnny return (DDI_FAILURE); 569881Sjohnny } 570881Sjohnny 571881Sjohnny /* Check if psm_intr_ops is NULL? */ 572881Sjohnny if (psm_intr_ops == NULL) 573881Sjohnny return (DDI_FAILURE); 574881Sjohnny 5751542Sjohnny count = hdlp->ih_scratch1; 5761542Sjohnny h_array = (ddi_intr_handle_t *)hdlp->ih_scratch2; 5771542Sjohnny for (i = 0; i < count; i++) { 5781542Sjohnny hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 579881Sjohnny if (pci_enable_intr(pdip, rdip, hdlp, 5801542Sjohnny hdlp->ih_inum) != DDI_SUCCESS) { 581881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: " 582881Sjohnny "pci_enable_intr failed for %d\n", i)); 5831542Sjohnny for (j = 0; j < i; j++) { 584*4397Sschwartz hdlp = (ddi_intr_handle_impl_t *) 585*4397Sschwartz h_array[j]; 586*4397Sschwartz pci_disable_intr(pdip, rdip, hdlp, 5871542Sjohnny hdlp->ih_inum); 5881542Sjohnny } 589881Sjohnny return (DDI_FAILURE); 590881Sjohnny } 591881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 5921542Sjohnny "BLOCKENABLE inum %x done\n", hdlp->ih_inum)); 593881Sjohnny } 594881Sjohnny break; 595881Sjohnny case DDI_INTROP_BLOCKDISABLE: 596881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 597881Sjohnny "BLOCKDISABLE\n")); 598881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_MSI) { 599881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKDISABLE: not MSI\n")); 600881Sjohnny return (DDI_FAILURE); 601881Sjohnny } 602881Sjohnny 603881Sjohnny /* Check if psm_intr_ops is present */ 604881Sjohnny if (psm_intr_ops == NULL) 605881Sjohnny return (DDI_FAILURE); 606881Sjohnny 6071542Sjohnny count = hdlp->ih_scratch1; 6081542Sjohnny h_array = (ddi_intr_handle_t *)hdlp->ih_scratch2; 6091542Sjohnny for (i = 0; i < count; i++) { 6101542Sjohnny hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 6111542Sjohnny pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum); 612881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 6131542Sjohnny "BLOCKDISABLE inum %x done\n", hdlp->ih_inum)); 614881Sjohnny } 615881Sjohnny break; 616881Sjohnny case DDI_INTROP_SETMASK: 617881Sjohnny case DDI_INTROP_CLRMASK: 618881Sjohnny /* 619881Sjohnny * First handle in the config space 620881Sjohnny */ 621881Sjohnny if (intr_op == DDI_INTROP_SETMASK) { 622881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 623881Sjohnny pci_status = pci_msi_set_mask(rdip, 624881Sjohnny hdlp->ih_type, hdlp->ih_inum); 625881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 626881Sjohnny pci_status = pci_intx_set_mask(rdip); 627881Sjohnny } else { 628881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 629881Sjohnny pci_status = pci_msi_clr_mask(rdip, 630881Sjohnny hdlp->ih_type, hdlp->ih_inum); 631881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 632881Sjohnny pci_status = pci_intx_clr_mask(rdip); 633881Sjohnny } 634881Sjohnny 635881Sjohnny /* For MSI/X; no need to check with pcplusmp */ 636881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 637881Sjohnny return (pci_status); 638881Sjohnny 639881Sjohnny /* For fixed interrupts only: handle config space first */ 640881Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_FIXED && 641881Sjohnny pci_status == DDI_SUCCESS) 642881Sjohnny break; 643881Sjohnny 644881Sjohnny /* For fixed interrupts only: confer with pcplusmp next */ 645881Sjohnny if (psm_intr_ops != NULL) { 646881Sjohnny /* If interrupt is shared; do nothing */ 647881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 648881Sjohnny PSM_INTR_OP_GET_SHARED, &psm_status); 649881Sjohnny 650881Sjohnny if (psm_rval == PSM_FAILURE || psm_status == 1) 651881Sjohnny return (pci_status); 652881Sjohnny 653881Sjohnny /* Now, pcplusmp should try to set/clear the mask */ 654881Sjohnny if (intr_op == DDI_INTROP_SETMASK) 655881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 656881Sjohnny PSM_INTR_OP_SET_MASK, NULL); 657881Sjohnny else 658881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 659881Sjohnny PSM_INTR_OP_CLEAR_MASK, NULL); 660881Sjohnny } 661881Sjohnny return ((psm_rval == PSM_FAILURE) ? DDI_FAILURE : DDI_SUCCESS); 662881Sjohnny case DDI_INTROP_GETPENDING: 663881Sjohnny /* 664881Sjohnny * First check the config space and/or 665881Sjohnny * MSI capability register(s) 666881Sjohnny */ 667881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 668881Sjohnny pci_rval = pci_msi_get_pending(rdip, hdlp->ih_type, 669881Sjohnny hdlp->ih_inum, &pci_status); 670881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 671881Sjohnny pci_rval = pci_intx_get_pending(rdip, &pci_status); 672881Sjohnny 673881Sjohnny /* On failure; next try with pcplusmp */ 674881Sjohnny if (pci_rval != DDI_SUCCESS && psm_intr_ops != NULL) 675881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 676881Sjohnny PSM_INTR_OP_GET_PENDING, &psm_status); 677881Sjohnny 678881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned " 679881Sjohnny "psm_rval = %x, psm_status = %x, pci_rval = %x, " 680881Sjohnny "pci_status = %x\n", psm_rval, psm_status, pci_rval, 681881Sjohnny pci_status)); 682881Sjohnny if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) { 683881Sjohnny *(int *)result = 0; 684881Sjohnny return (DDI_FAILURE); 685881Sjohnny } 686881Sjohnny 687881Sjohnny if (psm_rval != PSM_FAILURE) 688881Sjohnny *(int *)result = psm_status; 689881Sjohnny else if (pci_rval != DDI_FAILURE) 690881Sjohnny *(int *)result = pci_status; 691881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned = %x\n", 692881Sjohnny *(int *)result)); 693881Sjohnny break; 694881Sjohnny default: 695881Sjohnny return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result)); 696881Sjohnny } 697881Sjohnny 698881Sjohnny return (DDI_SUCCESS); 699881Sjohnny } 700881Sjohnny 701916Sschwartz int 702916Sschwartz pci_get_intr_from_vecirq(apic_get_intr_t *intrinfo_p, 703916Sschwartz int vecirq, boolean_t is_irq) 704916Sschwartz { 705916Sschwartz ddi_intr_handle_impl_t get_info_ii_hdl; 706916Sschwartz 707916Sschwartz if (is_irq) 708916Sschwartz intrinfo_p->avgi_req_flags |= PSMGI_INTRBY_IRQ; 709916Sschwartz 710916Sschwartz /* 711916Sschwartz * For this locally-declared and used handle, ih_private will contain a 712916Sschwartz * pointer to apic_get_intr_t, not an ihdl_plat_t as used for 713916Sschwartz * global interrupt handling. 714916Sschwartz */ 715916Sschwartz get_info_ii_hdl.ih_private = intrinfo_p; 716916Sschwartz get_info_ii_hdl.ih_vector = (ushort_t)vecirq; 717916Sschwartz 718916Sschwartz if ((*psm_intr_ops)(NULL, &get_info_ii_hdl, 719916Sschwartz PSM_INTR_OP_GET_INTR, NULL) == PSM_FAILURE) 720916Sschwartz return (DDI_FAILURE); 721916Sschwartz 722916Sschwartz return (DDI_SUCCESS); 723916Sschwartz } 724916Sschwartz 725916Sschwartz 726916Sschwartz int 727916Sschwartz pci_get_cpu_from_vecirq(int vecirq, boolean_t is_irq) 728916Sschwartz { 729916Sschwartz int rval; 730916Sschwartz 731916Sschwartz apic_get_intr_t intrinfo; 732916Sschwartz intrinfo.avgi_req_flags = PSMGI_REQ_CPUID; 733916Sschwartz rval = pci_get_intr_from_vecirq(&intrinfo, vecirq, is_irq); 734916Sschwartz 735916Sschwartz if (rval == DDI_SUCCESS) 736916Sschwartz return (intrinfo.avgi_cpu_id); 737916Sschwartz else 738916Sschwartz return (-1); 739916Sschwartz } 740916Sschwartz 741881Sjohnny 742881Sjohnny static int 743881Sjohnny pci_enable_intr(dev_info_t *pdip, dev_info_t *rdip, 744881Sjohnny ddi_intr_handle_impl_t *hdlp, uint32_t inum) 745881Sjohnny { 746881Sjohnny struct intrspec *ispec; 747916Sschwartz int irq; 748916Sschwartz ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 749881Sjohnny 750881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: hdlp %p inum %x\n", 751881Sjohnny (void *)hdlp, inum)); 752881Sjohnny 753881Sjohnny /* Translate the interrupt if needed */ 754881Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum); 7552288Sanish if (ispec == NULL) 7562288Sanish return (DDI_FAILURE); 7572288Sanish if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 758881Sjohnny ispec->intrspec_vec = inum; 759916Sschwartz ihdl_plat_datap->ip_ispecp = ispec; 760881Sjohnny 761881Sjohnny /* translate the interrupt if needed */ 762916Sschwartz (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq); 763916Sschwartz DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: priority=%x irq=%x\n", 764916Sschwartz hdlp->ih_pri, irq)); 765881Sjohnny 766881Sjohnny /* Add the interrupt handler */ 767881Sjohnny if (!add_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, 768916Sschwartz DEVI(rdip)->devi_name, irq, hdlp->ih_cb_arg1, 769916Sschwartz hdlp->ih_cb_arg2, &ihdl_plat_datap->ip_ticks, rdip)) 770881Sjohnny return (DDI_FAILURE); 771881Sjohnny 772916Sschwartz /* Note this really is an irq. */ 773916Sschwartz hdlp->ih_vector = (ushort_t)irq; 774916Sschwartz 775881Sjohnny return (DDI_SUCCESS); 776881Sjohnny } 777881Sjohnny 778881Sjohnny 779881Sjohnny static void 780881Sjohnny pci_disable_intr(dev_info_t *pdip, dev_info_t *rdip, 781881Sjohnny ddi_intr_handle_impl_t *hdlp, uint32_t inum) 782881Sjohnny { 783916Sschwartz int irq; 784881Sjohnny struct intrspec *ispec; 785916Sschwartz ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 786881Sjohnny 787881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_disable_intr: \n")); 788881Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum); 7892288Sanish if (ispec == NULL) 7902288Sanish return; 7912288Sanish if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 792881Sjohnny ispec->intrspec_vec = inum; 793916Sschwartz ihdl_plat_datap->ip_ispecp = ispec; 794881Sjohnny 795881Sjohnny /* translate the interrupt if needed */ 796916Sschwartz (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq); 797881Sjohnny 798881Sjohnny /* Disable the interrupt handler */ 799916Sschwartz rem_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, irq); 800916Sschwartz ihdl_plat_datap->ip_ispecp = NULL; 801881Sjohnny } 802881Sjohnny 803881Sjohnny /* 804881Sjohnny * Miscellaneous library function 805881Sjohnny */ 806881Sjohnny int 807881Sjohnny pci_common_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp) 808881Sjohnny { 809881Sjohnny int i; 810881Sjohnny int number; 811881Sjohnny int assigned_addr_len; 812881Sjohnny uint_t phys_hi = pci_rp->pci_phys_hi; 813881Sjohnny pci_regspec_t *assigned_addr; 814881Sjohnny 815881Sjohnny if (((phys_hi & PCI_REG_ADDR_M) == PCI_ADDR_CONFIG) || 816881Sjohnny (phys_hi & PCI_RELOCAT_B)) 817881Sjohnny return (DDI_SUCCESS); 818881Sjohnny 819881Sjohnny /* 820881Sjohnny * the "reg" property specifies relocatable, get and interpret the 821881Sjohnny * "assigned-addresses" property. 822881Sjohnny */ 823881Sjohnny if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 824881Sjohnny "assigned-addresses", (int **)&assigned_addr, 825881Sjohnny (uint_t *)&assigned_addr_len) != DDI_PROP_SUCCESS) 826881Sjohnny return (DDI_FAILURE); 827881Sjohnny 828881Sjohnny /* 829881Sjohnny * Scan the "assigned-addresses" for one that matches the specified 830881Sjohnny * "reg" property entry. 831881Sjohnny */ 832881Sjohnny phys_hi &= PCI_CONF_ADDR_MASK; 833881Sjohnny number = assigned_addr_len / (sizeof (pci_regspec_t) / sizeof (int)); 834881Sjohnny for (i = 0; i < number; i++) { 835881Sjohnny if ((assigned_addr[i].pci_phys_hi & PCI_CONF_ADDR_MASK) == 836881Sjohnny phys_hi) { 837881Sjohnny pci_rp->pci_phys_mid = assigned_addr[i].pci_phys_mid; 838881Sjohnny pci_rp->pci_phys_low = assigned_addr[i].pci_phys_low; 839881Sjohnny ddi_prop_free(assigned_addr); 840881Sjohnny return (DDI_SUCCESS); 841881Sjohnny } 842881Sjohnny } 843881Sjohnny 844881Sjohnny ddi_prop_free(assigned_addr); 845881Sjohnny return (DDI_FAILURE); 846881Sjohnny } 847881Sjohnny 848881Sjohnny 849881Sjohnny /* 850881Sjohnny * For pci_tools 851881Sjohnny */ 852881Sjohnny 853881Sjohnny int 854881Sjohnny pci_common_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg, 855881Sjohnny int mode, cred_t *credp, int *rvalp) 856881Sjohnny { 857881Sjohnny int rv = ENOTTY; 858881Sjohnny 859881Sjohnny minor_t minor = getminor(dev); 860881Sjohnny 861881Sjohnny switch (PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(minor)) { 862881Sjohnny case PCI_TOOL_REG_MINOR_NUM: 863881Sjohnny 864881Sjohnny switch (cmd) { 865881Sjohnny case PCITOOL_DEVICE_SET_REG: 866881Sjohnny case PCITOOL_DEVICE_GET_REG: 867881Sjohnny 868881Sjohnny /* Require full privileges. */ 869881Sjohnny if (secpolicy_kmdb(credp)) 870881Sjohnny rv = EPERM; 871881Sjohnny else 872881Sjohnny rv = pcitool_dev_reg_ops(dip, (void *)arg, 873881Sjohnny cmd, mode); 874881Sjohnny break; 875881Sjohnny 876881Sjohnny case PCITOOL_NEXUS_SET_REG: 877881Sjohnny case PCITOOL_NEXUS_GET_REG: 878881Sjohnny 879881Sjohnny /* Require full privileges. */ 880881Sjohnny if (secpolicy_kmdb(credp)) 881881Sjohnny rv = EPERM; 882881Sjohnny else 883881Sjohnny rv = pcitool_bus_reg_ops(dip, (void *)arg, 884881Sjohnny cmd, mode); 885881Sjohnny break; 886881Sjohnny } 887881Sjohnny break; 888881Sjohnny 889881Sjohnny case PCI_TOOL_INTR_MINOR_NUM: 890881Sjohnny 891881Sjohnny switch (cmd) { 892881Sjohnny case PCITOOL_DEVICE_SET_INTR: 893881Sjohnny 894881Sjohnny /* Require PRIV_SYS_RES_CONFIG, same as psradm */ 895881Sjohnny if (secpolicy_ponline(credp)) { 896881Sjohnny rv = EPERM; 897881Sjohnny break; 898881Sjohnny } 899881Sjohnny 900881Sjohnny /*FALLTHRU*/ 901881Sjohnny /* These require no special privileges. */ 902881Sjohnny case PCITOOL_DEVICE_GET_INTR: 903*4397Sschwartz case PCITOOL_SYSTEM_INTR_INFO: 904881Sjohnny rv = pcitool_intr_admn(dip, (void *)arg, cmd, mode); 905881Sjohnny break; 906881Sjohnny } 907881Sjohnny break; 908881Sjohnny 909881Sjohnny /* 910881Sjohnny * All non-PCItool ioctls go through here, including: 911881Sjohnny * devctl ioctls with minor number PCIHP_DEVCTL_MINOR and 912881Sjohnny * those for attachment points with where minor number is the 913881Sjohnny * device number. 914881Sjohnny */ 915881Sjohnny default: 916881Sjohnny rv = (pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, 917881Sjohnny credp, rvalp); 918881Sjohnny break; 919881Sjohnny } 920881Sjohnny 921881Sjohnny return (rv); 922881Sjohnny } 9231083Sanish 9241083Sanish 9251865Sdilpreet int 9261865Sdilpreet pci_common_ctlops_poke(peekpoke_ctlops_t *in_args) 9271865Sdilpreet { 9281865Sdilpreet size_t size = in_args->size; 9291865Sdilpreet uintptr_t dev_addr = in_args->dev_addr; 9301865Sdilpreet uintptr_t host_addr = in_args->host_addr; 9311865Sdilpreet ddi_acc_impl_t *hp = (ddi_acc_impl_t *)in_args->handle; 9321865Sdilpreet ddi_acc_hdl_t *hdlp = (ddi_acc_hdl_t *)in_args->handle; 9331865Sdilpreet size_t repcount = in_args->repcount; 9341865Sdilpreet uint_t flags = in_args->flags; 9351865Sdilpreet int err = DDI_SUCCESS; 9361865Sdilpreet 9371865Sdilpreet /* 9381865Sdilpreet * if no handle then this is a poke. We have to return failure here 9391865Sdilpreet * as we have no way of knowing whether this is a MEM or IO space access 9401865Sdilpreet */ 9411865Sdilpreet if (in_args->handle == NULL) 9421865Sdilpreet return (DDI_FAILURE); 9431865Sdilpreet 9441865Sdilpreet /* 9451865Sdilpreet * rest of this function is actually for cautious puts 9461865Sdilpreet */ 9471865Sdilpreet for (; repcount; repcount--) { 9481865Sdilpreet if (hp->ahi_acc_attr == DDI_ACCATTR_CONFIG_SPACE) { 9491865Sdilpreet switch (size) { 9501865Sdilpreet case sizeof (uint8_t): 9511865Sdilpreet pci_config_wr8(hp, (uint8_t *)dev_addr, 9521865Sdilpreet *(uint8_t *)host_addr); 9531865Sdilpreet break; 9541865Sdilpreet case sizeof (uint16_t): 9551865Sdilpreet pci_config_wr16(hp, (uint16_t *)dev_addr, 9561865Sdilpreet *(uint16_t *)host_addr); 9571865Sdilpreet break; 9581865Sdilpreet case sizeof (uint32_t): 9591865Sdilpreet pci_config_wr32(hp, (uint32_t *)dev_addr, 9601865Sdilpreet *(uint32_t *)host_addr); 9611865Sdilpreet break; 9621865Sdilpreet case sizeof (uint64_t): 9631865Sdilpreet pci_config_wr64(hp, (uint64_t *)dev_addr, 9641865Sdilpreet *(uint64_t *)host_addr); 9651865Sdilpreet break; 9661865Sdilpreet default: 9671865Sdilpreet err = DDI_FAILURE; 9681865Sdilpreet break; 9691865Sdilpreet } 9701865Sdilpreet } else if (hp->ahi_acc_attr & DDI_ACCATTR_IO_SPACE) { 9711865Sdilpreet if (hdlp->ah_acc.devacc_attr_endian_flags == 9721865Sdilpreet DDI_STRUCTURE_BE_ACC) { 9731865Sdilpreet switch (size) { 9741865Sdilpreet case sizeof (uint8_t): 9751865Sdilpreet i_ddi_io_put8(hp, 9761865Sdilpreet (uint8_t *)dev_addr, 9771865Sdilpreet *(uint8_t *)host_addr); 9781865Sdilpreet break; 9791865Sdilpreet case sizeof (uint16_t): 9801865Sdilpreet i_ddi_io_swap_put16(hp, 9811865Sdilpreet (uint16_t *)dev_addr, 9821865Sdilpreet *(uint16_t *)host_addr); 9831865Sdilpreet break; 9841865Sdilpreet case sizeof (uint32_t): 9851865Sdilpreet i_ddi_io_swap_put32(hp, 9861865Sdilpreet (uint32_t *)dev_addr, 9871865Sdilpreet *(uint32_t *)host_addr); 9881865Sdilpreet break; 9891865Sdilpreet /* 9901865Sdilpreet * note the 64-bit case is a dummy 9911865Sdilpreet * function - so no need to swap 9921865Sdilpreet */ 9931865Sdilpreet case sizeof (uint64_t): 9941865Sdilpreet i_ddi_io_put64(hp, 9951865Sdilpreet (uint64_t *)dev_addr, 9961865Sdilpreet *(uint64_t *)host_addr); 9971865Sdilpreet break; 9981865Sdilpreet default: 9991865Sdilpreet err = DDI_FAILURE; 10001865Sdilpreet break; 10011865Sdilpreet } 10021865Sdilpreet } else { 10031865Sdilpreet switch (size) { 10041865Sdilpreet case sizeof (uint8_t): 10051865Sdilpreet i_ddi_io_put8(hp, 10061865Sdilpreet (uint8_t *)dev_addr, 10071865Sdilpreet *(uint8_t *)host_addr); 10081865Sdilpreet break; 10091865Sdilpreet case sizeof (uint16_t): 10101865Sdilpreet i_ddi_io_put16(hp, 10111865Sdilpreet (uint16_t *)dev_addr, 10121865Sdilpreet *(uint16_t *)host_addr); 10131865Sdilpreet break; 10141865Sdilpreet case sizeof (uint32_t): 10151865Sdilpreet i_ddi_io_put32(hp, 10161865Sdilpreet (uint32_t *)dev_addr, 10171865Sdilpreet *(uint32_t *)host_addr); 10181865Sdilpreet break; 10191865Sdilpreet case sizeof (uint64_t): 10201865Sdilpreet i_ddi_io_put64(hp, 10211865Sdilpreet (uint64_t *)dev_addr, 10221865Sdilpreet *(uint64_t *)host_addr); 10231865Sdilpreet break; 10241865Sdilpreet default: 10251865Sdilpreet err = DDI_FAILURE; 10261865Sdilpreet break; 10271865Sdilpreet } 10281865Sdilpreet } 10291865Sdilpreet } else { 10301865Sdilpreet if (hdlp->ah_acc.devacc_attr_endian_flags == 10311865Sdilpreet DDI_STRUCTURE_BE_ACC) { 10321865Sdilpreet switch (size) { 10331865Sdilpreet case sizeof (uint8_t): 10341865Sdilpreet *(uint8_t *)dev_addr = 10351865Sdilpreet *(uint8_t *)host_addr; 10361865Sdilpreet break; 10371865Sdilpreet case sizeof (uint16_t): 10381865Sdilpreet *(uint16_t *)dev_addr = 10391865Sdilpreet ddi_swap16(*(uint16_t *)host_addr); 10401865Sdilpreet break; 10411865Sdilpreet case sizeof (uint32_t): 10421865Sdilpreet *(uint32_t *)dev_addr = 10431865Sdilpreet ddi_swap32(*(uint32_t *)host_addr); 10441865Sdilpreet break; 10451865Sdilpreet case sizeof (uint64_t): 10461865Sdilpreet *(uint64_t *)dev_addr = 10471865Sdilpreet ddi_swap64(*(uint64_t *)host_addr); 10481865Sdilpreet break; 10491865Sdilpreet default: 10501865Sdilpreet err = DDI_FAILURE; 10511865Sdilpreet break; 10521865Sdilpreet } 10531865Sdilpreet } else { 10541865Sdilpreet switch (size) { 10551865Sdilpreet case sizeof (uint8_t): 10561865Sdilpreet *(uint8_t *)dev_addr = 10571865Sdilpreet *(uint8_t *)host_addr; 10581865Sdilpreet break; 10591865Sdilpreet case sizeof (uint16_t): 10601865Sdilpreet *(uint16_t *)dev_addr = 10611865Sdilpreet *(uint16_t *)host_addr; 10621865Sdilpreet break; 10631865Sdilpreet case sizeof (uint32_t): 10641865Sdilpreet *(uint32_t *)dev_addr = 10651865Sdilpreet *(uint32_t *)host_addr; 10661865Sdilpreet break; 10671865Sdilpreet case sizeof (uint64_t): 10681865Sdilpreet *(uint64_t *)dev_addr = 10691865Sdilpreet *(uint64_t *)host_addr; 10701865Sdilpreet break; 10711865Sdilpreet default: 10721865Sdilpreet err = DDI_FAILURE; 10731865Sdilpreet break; 10741865Sdilpreet } 10751865Sdilpreet } 10761865Sdilpreet } 10771865Sdilpreet host_addr += size; 10781865Sdilpreet if (flags == DDI_DEV_AUTOINCR) 10791865Sdilpreet dev_addr += size; 10801865Sdilpreet } 10811865Sdilpreet return (err); 10821865Sdilpreet } 10831865Sdilpreet 10841865Sdilpreet 10851865Sdilpreet int 10861865Sdilpreet pci_fm_acc_setup(ddi_acc_hdl_t *hp, off_t offset, off_t len) 10871865Sdilpreet { 10881865Sdilpreet ddi_acc_impl_t *ap = (ddi_acc_impl_t *)hp->ah_platform_private; 10891865Sdilpreet 10901865Sdilpreet /* endian-ness check */ 10911865Sdilpreet if (hp->ah_acc.devacc_attr_endian_flags == DDI_STRUCTURE_BE_ACC) 10921865Sdilpreet return (DDI_FAILURE); 10931865Sdilpreet 10941865Sdilpreet /* 10951865Sdilpreet * range check 10961865Sdilpreet */ 10971865Sdilpreet if ((offset >= PCI_CONF_HDR_SIZE) || 10981865Sdilpreet (len > PCI_CONF_HDR_SIZE) || 10991865Sdilpreet (offset + len > PCI_CONF_HDR_SIZE)) 11001865Sdilpreet return (DDI_FAILURE); 11011865Sdilpreet 11021865Sdilpreet ap->ahi_acc_attr |= DDI_ACCATTR_CONFIG_SPACE; 11031865Sdilpreet /* 11041865Sdilpreet * always use cautious mechanism for config space gets 11051865Sdilpreet */ 11061865Sdilpreet ap->ahi_get8 = i_ddi_caut_get8; 11071865Sdilpreet ap->ahi_get16 = i_ddi_caut_get16; 11081865Sdilpreet ap->ahi_get32 = i_ddi_caut_get32; 11091865Sdilpreet ap->ahi_get64 = i_ddi_caut_get64; 11101865Sdilpreet ap->ahi_rep_get8 = i_ddi_caut_rep_get8; 11111865Sdilpreet ap->ahi_rep_get16 = i_ddi_caut_rep_get16; 11121865Sdilpreet ap->ahi_rep_get32 = i_ddi_caut_rep_get32; 11131865Sdilpreet ap->ahi_rep_get64 = i_ddi_caut_rep_get64; 11141865Sdilpreet if (hp->ah_acc.devacc_attr_access == DDI_CAUTIOUS_ACC) { 11151865Sdilpreet ap->ahi_put8 = i_ddi_caut_put8; 11161865Sdilpreet ap->ahi_put16 = i_ddi_caut_put16; 11171865Sdilpreet ap->ahi_put32 = i_ddi_caut_put32; 11181865Sdilpreet ap->ahi_put64 = i_ddi_caut_put64; 11191865Sdilpreet ap->ahi_rep_put8 = i_ddi_caut_rep_put8; 11201865Sdilpreet ap->ahi_rep_put16 = i_ddi_caut_rep_put16; 11211865Sdilpreet ap->ahi_rep_put32 = i_ddi_caut_rep_put32; 11221865Sdilpreet ap->ahi_rep_put64 = i_ddi_caut_rep_put64; 11231865Sdilpreet } else { 11241865Sdilpreet ap->ahi_put8 = pci_config_wr8; 11251865Sdilpreet ap->ahi_put16 = pci_config_wr16; 11261865Sdilpreet ap->ahi_put32 = pci_config_wr32; 11271865Sdilpreet ap->ahi_put64 = pci_config_wr64; 11281865Sdilpreet ap->ahi_rep_put8 = pci_config_rep_wr8; 11291865Sdilpreet ap->ahi_rep_put16 = pci_config_rep_wr16; 11301865Sdilpreet ap->ahi_rep_put32 = pci_config_rep_wr32; 11311865Sdilpreet ap->ahi_rep_put64 = pci_config_rep_wr64; 11321865Sdilpreet } 11331865Sdilpreet 11341865Sdilpreet /* Initialize to default check/notify functions */ 11351865Sdilpreet ap->ahi_fault_check = i_ddi_acc_fault_check; 11361865Sdilpreet ap->ahi_fault_notify = i_ddi_acc_fault_notify; 11371865Sdilpreet ap->ahi_fault = 0; 11381865Sdilpreet impl_acc_err_init(hp); 11391865Sdilpreet return (DDI_SUCCESS); 11401865Sdilpreet } 11411865Sdilpreet 11421865Sdilpreet 11431865Sdilpreet int 11441865Sdilpreet pci_common_ctlops_peek(peekpoke_ctlops_t *in_args) 11451865Sdilpreet { 11461865Sdilpreet size_t size = in_args->size; 11471865Sdilpreet uintptr_t dev_addr = in_args->dev_addr; 11481865Sdilpreet uintptr_t host_addr = in_args->host_addr; 11491865Sdilpreet ddi_acc_impl_t *hp = (ddi_acc_impl_t *)in_args->handle; 11501865Sdilpreet ddi_acc_hdl_t *hdlp = (ddi_acc_hdl_t *)in_args->handle; 11511865Sdilpreet size_t repcount = in_args->repcount; 11521865Sdilpreet uint_t flags = in_args->flags; 11531865Sdilpreet int err = DDI_SUCCESS; 11541865Sdilpreet 11551865Sdilpreet /* 11561865Sdilpreet * if no handle then this is a peek. We have to return failure here 11571865Sdilpreet * as we have no way of knowing whether this is a MEM or IO space access 11581865Sdilpreet */ 11591865Sdilpreet if (in_args->handle == NULL) 11601865Sdilpreet return (DDI_FAILURE); 11611865Sdilpreet 11621865Sdilpreet for (; repcount; repcount--) { 11631865Sdilpreet if (hp->ahi_acc_attr == DDI_ACCATTR_CONFIG_SPACE) { 11641865Sdilpreet switch (size) { 11651865Sdilpreet case sizeof (uint8_t): 11661865Sdilpreet *(uint8_t *)host_addr = pci_config_rd8(hp, 11671865Sdilpreet (uint8_t *)dev_addr); 11681865Sdilpreet break; 11691865Sdilpreet case sizeof (uint16_t): 11701865Sdilpreet *(uint16_t *)host_addr = pci_config_rd16(hp, 11711865Sdilpreet (uint16_t *)dev_addr); 11721865Sdilpreet break; 11731865Sdilpreet case sizeof (uint32_t): 11741865Sdilpreet *(uint32_t *)host_addr = pci_config_rd32(hp, 11751865Sdilpreet (uint32_t *)dev_addr); 11761865Sdilpreet break; 11771865Sdilpreet case sizeof (uint64_t): 11781865Sdilpreet *(uint64_t *)host_addr = pci_config_rd64(hp, 11791865Sdilpreet (uint64_t *)dev_addr); 11801865Sdilpreet break; 11811865Sdilpreet default: 11821865Sdilpreet err = DDI_FAILURE; 11831865Sdilpreet break; 11841865Sdilpreet } 11851865Sdilpreet } else if (hp->ahi_acc_attr & DDI_ACCATTR_IO_SPACE) { 11861865Sdilpreet if (hdlp->ah_acc.devacc_attr_endian_flags == 11871865Sdilpreet DDI_STRUCTURE_BE_ACC) { 11881865Sdilpreet switch (size) { 11891865Sdilpreet case sizeof (uint8_t): 11901865Sdilpreet *(uint8_t *)host_addr = 11911865Sdilpreet i_ddi_io_get8(hp, 11921865Sdilpreet (uint8_t *)dev_addr); 11931865Sdilpreet break; 11941865Sdilpreet case sizeof (uint16_t): 11951865Sdilpreet *(uint16_t *)host_addr = 11961865Sdilpreet i_ddi_io_swap_get16(hp, 11971865Sdilpreet (uint16_t *)dev_addr); 11981865Sdilpreet break; 11991865Sdilpreet case sizeof (uint32_t): 12001865Sdilpreet *(uint32_t *)host_addr = 12011865Sdilpreet i_ddi_io_swap_get32(hp, 12021865Sdilpreet (uint32_t *)dev_addr); 12031865Sdilpreet break; 12041865Sdilpreet /* 12051865Sdilpreet * note the 64-bit case is a dummy 12061865Sdilpreet * function - so no need to swap 12071865Sdilpreet */ 12081865Sdilpreet case sizeof (uint64_t): 12091865Sdilpreet *(uint64_t *)host_addr = 12101865Sdilpreet i_ddi_io_get64(hp, 12111865Sdilpreet (uint64_t *)dev_addr); 12121865Sdilpreet break; 12131865Sdilpreet default: 12141865Sdilpreet err = DDI_FAILURE; 12151865Sdilpreet break; 12161865Sdilpreet } 12171865Sdilpreet } else { 12181865Sdilpreet switch (size) { 12191865Sdilpreet case sizeof (uint8_t): 12201865Sdilpreet *(uint8_t *)host_addr = 12211865Sdilpreet i_ddi_io_get8(hp, 12221865Sdilpreet (uint8_t *)dev_addr); 12231865Sdilpreet break; 12241865Sdilpreet case sizeof (uint16_t): 12251865Sdilpreet *(uint16_t *)host_addr = 12261865Sdilpreet i_ddi_io_get16(hp, 12271865Sdilpreet (uint16_t *)dev_addr); 12281865Sdilpreet break; 12291865Sdilpreet case sizeof (uint32_t): 12301865Sdilpreet *(uint32_t *)host_addr = 12311865Sdilpreet i_ddi_io_get32(hp, 12321865Sdilpreet (uint32_t *)dev_addr); 12331865Sdilpreet break; 12341865Sdilpreet case sizeof (uint64_t): 12351865Sdilpreet *(uint64_t *)host_addr = 12361865Sdilpreet i_ddi_io_get64(hp, 12371865Sdilpreet (uint64_t *)dev_addr); 12381865Sdilpreet break; 12391865Sdilpreet default: 12401865Sdilpreet err = DDI_FAILURE; 12411865Sdilpreet break; 12421865Sdilpreet } 12431865Sdilpreet } 12441865Sdilpreet } else { 12451865Sdilpreet if (hdlp->ah_acc.devacc_attr_endian_flags == 12461865Sdilpreet DDI_STRUCTURE_BE_ACC) { 12471865Sdilpreet switch (in_args->size) { 12481865Sdilpreet case sizeof (uint8_t): 12491865Sdilpreet *(uint8_t *)host_addr = 12501865Sdilpreet *(uint8_t *)dev_addr; 12511865Sdilpreet break; 12521865Sdilpreet case sizeof (uint16_t): 12531865Sdilpreet *(uint16_t *)host_addr = 12541865Sdilpreet ddi_swap16(*(uint16_t *)dev_addr); 12551865Sdilpreet break; 12561865Sdilpreet case sizeof (uint32_t): 12571865Sdilpreet *(uint32_t *)host_addr = 12581865Sdilpreet ddi_swap32(*(uint32_t *)dev_addr); 12591865Sdilpreet break; 12601865Sdilpreet case sizeof (uint64_t): 12611865Sdilpreet *(uint64_t *)host_addr = 12621865Sdilpreet ddi_swap64(*(uint64_t *)dev_addr); 12631865Sdilpreet break; 12641865Sdilpreet default: 12651865Sdilpreet err = DDI_FAILURE; 12661865Sdilpreet break; 12671865Sdilpreet } 12681865Sdilpreet } else { 12691865Sdilpreet switch (in_args->size) { 12701865Sdilpreet case sizeof (uint8_t): 12711865Sdilpreet *(uint8_t *)host_addr = 12721865Sdilpreet *(uint8_t *)dev_addr; 12731865Sdilpreet break; 12741865Sdilpreet case sizeof (uint16_t): 12751865Sdilpreet *(uint16_t *)host_addr = 12761865Sdilpreet *(uint16_t *)dev_addr; 12771865Sdilpreet break; 12781865Sdilpreet case sizeof (uint32_t): 12791865Sdilpreet *(uint32_t *)host_addr = 12801865Sdilpreet *(uint32_t *)dev_addr; 12811865Sdilpreet break; 12821865Sdilpreet case sizeof (uint64_t): 12831865Sdilpreet *(uint64_t *)host_addr = 12841865Sdilpreet *(uint64_t *)dev_addr; 12851865Sdilpreet break; 12861865Sdilpreet default: 12871865Sdilpreet err = DDI_FAILURE; 12881865Sdilpreet break; 12891865Sdilpreet } 12901865Sdilpreet } 12911865Sdilpreet } 12921865Sdilpreet host_addr += size; 12931865Sdilpreet if (flags == DDI_DEV_AUTOINCR) 12941865Sdilpreet dev_addr += size; 12951865Sdilpreet } 12961865Sdilpreet return (err); 12971865Sdilpreet } 12981865Sdilpreet 12991865Sdilpreet /*ARGSUSED*/ 13001865Sdilpreet int 13011865Sdilpreet pci_common_peekpoke(dev_info_t *dip, dev_info_t *rdip, 13021865Sdilpreet ddi_ctl_enum_t ctlop, void *arg, void *result) 13031865Sdilpreet { 13041865Sdilpreet if (ctlop == DDI_CTLOPS_PEEK) 13051865Sdilpreet return (pci_common_ctlops_peek((peekpoke_ctlops_t *)arg)); 13061865Sdilpreet else 13071865Sdilpreet return (pci_common_ctlops_poke((peekpoke_ctlops_t *)arg)); 13081865Sdilpreet } 13091865Sdilpreet 13101083Sanish /* 13111083Sanish * These are the get and put functions to be shared with drivers. The 13121083Sanish * mutex locking is done inside the functions referenced, rather than 13131083Sanish * here, and is thus shared across PCI child drivers and any other 13141083Sanish * consumers of PCI config space (such as the ACPI subsystem). 13151083Sanish * 13161083Sanish * The configuration space addresses come in as pointers. This is fine on 13171083Sanish * a 32-bit system, where the VM space and configuration space are the same 13181083Sanish * size. It's not such a good idea on a 64-bit system, where memory 13191083Sanish * addresses are twice as large as configuration space addresses. At some 13201083Sanish * point in the call tree we need to take a stand and say "you are 32-bit 13211083Sanish * from this time forth", and this seems like a nice self-contained place. 13221083Sanish */ 13231083Sanish 13241083Sanish uint8_t 13251083Sanish pci_config_rd8(ddi_acc_impl_t *hdlp, uint8_t *addr) 13261083Sanish { 13271083Sanish pci_acc_cfblk_t *cfp; 13281083Sanish uint8_t rval; 13291083Sanish int reg; 13301083Sanish 13311083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 13321083Sanish 13331083Sanish reg = (int)(uintptr_t)addr; 13341083Sanish 13351083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 13361083Sanish 13371083Sanish rval = (*pci_getb_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum, 13381083Sanish reg); 13391083Sanish 13401083Sanish return (rval); 13411083Sanish } 13421083Sanish 13431083Sanish void 13441083Sanish pci_config_rep_rd8(ddi_acc_impl_t *hdlp, uint8_t *host_addr, 13451083Sanish uint8_t *dev_addr, size_t repcount, uint_t flags) 13461083Sanish { 13471083Sanish uint8_t *h, *d; 13481083Sanish 13491083Sanish h = host_addr; 13501083Sanish d = dev_addr; 13511083Sanish 13521083Sanish if (flags == DDI_DEV_AUTOINCR) 13531083Sanish for (; repcount; repcount--) 13541083Sanish *h++ = pci_config_rd8(hdlp, d++); 13551083Sanish else 13561083Sanish for (; repcount; repcount--) 13571083Sanish *h++ = pci_config_rd8(hdlp, d); 13581083Sanish } 13591083Sanish 13601083Sanish uint16_t 13611083Sanish pci_config_rd16(ddi_acc_impl_t *hdlp, uint16_t *addr) 13621083Sanish { 13631083Sanish pci_acc_cfblk_t *cfp; 13641083Sanish uint16_t rval; 13651083Sanish int reg; 13661083Sanish 13671083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 13681083Sanish 13691083Sanish reg = (int)(uintptr_t)addr; 13701083Sanish 13711083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 13721083Sanish 13731083Sanish rval = (*pci_getw_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum, 13741083Sanish reg); 13751083Sanish 13761083Sanish return (rval); 13771083Sanish } 13781083Sanish 13791083Sanish void 13801083Sanish pci_config_rep_rd16(ddi_acc_impl_t *hdlp, uint16_t *host_addr, 13811083Sanish uint16_t *dev_addr, size_t repcount, uint_t flags) 13821083Sanish { 13831083Sanish uint16_t *h, *d; 13841083Sanish 13851083Sanish h = host_addr; 13861083Sanish d = dev_addr; 13871083Sanish 13881083Sanish if (flags == DDI_DEV_AUTOINCR) 13891083Sanish for (; repcount; repcount--) 13901083Sanish *h++ = pci_config_rd16(hdlp, d++); 13911083Sanish else 13921083Sanish for (; repcount; repcount--) 13931083Sanish *h++ = pci_config_rd16(hdlp, d); 13941083Sanish } 13951083Sanish 13961083Sanish uint32_t 13971083Sanish pci_config_rd32(ddi_acc_impl_t *hdlp, uint32_t *addr) 13981083Sanish { 13991083Sanish pci_acc_cfblk_t *cfp; 14001083Sanish uint32_t rval; 14011083Sanish int reg; 14021083Sanish 14031083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 14041083Sanish 14051083Sanish reg = (int)(uintptr_t)addr; 14061083Sanish 14071083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 14081083Sanish 14091083Sanish rval = (*pci_getl_func)(cfp->c_busnum, cfp->c_devnum, 14101083Sanish cfp->c_funcnum, reg); 14111083Sanish 14121083Sanish return (rval); 14131083Sanish } 14141083Sanish 14151083Sanish void 14161083Sanish pci_config_rep_rd32(ddi_acc_impl_t *hdlp, uint32_t *host_addr, 14171083Sanish uint32_t *dev_addr, size_t repcount, uint_t flags) 14181083Sanish { 14191083Sanish uint32_t *h, *d; 14201083Sanish 14211083Sanish h = host_addr; 14221083Sanish d = dev_addr; 14231083Sanish 14241083Sanish if (flags == DDI_DEV_AUTOINCR) 14251083Sanish for (; repcount; repcount--) 14261083Sanish *h++ = pci_config_rd32(hdlp, d++); 14271083Sanish else 14281083Sanish for (; repcount; repcount--) 14291083Sanish *h++ = pci_config_rd32(hdlp, d); 14301083Sanish } 14311083Sanish 14321083Sanish 14331083Sanish void 14341083Sanish pci_config_wr8(ddi_acc_impl_t *hdlp, uint8_t *addr, uint8_t value) 14351083Sanish { 14361083Sanish pci_acc_cfblk_t *cfp; 14371083Sanish int reg; 14381083Sanish 14391083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 14401083Sanish 14411083Sanish reg = (int)(uintptr_t)addr; 14421083Sanish 14431083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 14441083Sanish 14451083Sanish (*pci_putb_func)(cfp->c_busnum, cfp->c_devnum, 14461083Sanish cfp->c_funcnum, reg, value); 14471083Sanish } 14481083Sanish 14491083Sanish void 14501083Sanish pci_config_rep_wr8(ddi_acc_impl_t *hdlp, uint8_t *host_addr, 14511083Sanish uint8_t *dev_addr, size_t repcount, uint_t flags) 14521083Sanish { 14531083Sanish uint8_t *h, *d; 14541083Sanish 14551083Sanish h = host_addr; 14561083Sanish d = dev_addr; 14571083Sanish 14581083Sanish if (flags == DDI_DEV_AUTOINCR) 14591083Sanish for (; repcount; repcount--) 14601083Sanish pci_config_wr8(hdlp, d++, *h++); 14611083Sanish else 14621083Sanish for (; repcount; repcount--) 14631083Sanish pci_config_wr8(hdlp, d, *h++); 14641083Sanish } 14651083Sanish 14661083Sanish void 14671083Sanish pci_config_wr16(ddi_acc_impl_t *hdlp, uint16_t *addr, uint16_t value) 14681083Sanish { 14691083Sanish pci_acc_cfblk_t *cfp; 14701083Sanish int reg; 14711083Sanish 14721083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 14731083Sanish 14741083Sanish reg = (int)(uintptr_t)addr; 14751083Sanish 14761083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 14771083Sanish 14781083Sanish (*pci_putw_func)(cfp->c_busnum, cfp->c_devnum, 14791083Sanish cfp->c_funcnum, reg, value); 14801083Sanish } 14811083Sanish 14821083Sanish void 14831083Sanish pci_config_rep_wr16(ddi_acc_impl_t *hdlp, uint16_t *host_addr, 14841083Sanish uint16_t *dev_addr, size_t repcount, uint_t flags) 14851083Sanish { 14861083Sanish uint16_t *h, *d; 14871083Sanish 14881083Sanish h = host_addr; 14891083Sanish d = dev_addr; 14901083Sanish 14911083Sanish if (flags == DDI_DEV_AUTOINCR) 14921083Sanish for (; repcount; repcount--) 14931083Sanish pci_config_wr16(hdlp, d++, *h++); 14941083Sanish else 14951083Sanish for (; repcount; repcount--) 14961083Sanish pci_config_wr16(hdlp, d, *h++); 14971083Sanish } 14981083Sanish 14991083Sanish void 15001083Sanish pci_config_wr32(ddi_acc_impl_t *hdlp, uint32_t *addr, uint32_t value) 15011083Sanish { 15021083Sanish pci_acc_cfblk_t *cfp; 15031083Sanish int reg; 15041083Sanish 15051083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 15061083Sanish 15071083Sanish reg = (int)(uintptr_t)addr; 15081083Sanish 15091083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 15101083Sanish 15111083Sanish (*pci_putl_func)(cfp->c_busnum, cfp->c_devnum, 15121083Sanish cfp->c_funcnum, reg, value); 15131083Sanish } 15141083Sanish 15151083Sanish void 15161083Sanish pci_config_rep_wr32(ddi_acc_impl_t *hdlp, uint32_t *host_addr, 15171083Sanish uint32_t *dev_addr, size_t repcount, uint_t flags) 15181083Sanish { 15191083Sanish uint32_t *h, *d; 15201083Sanish 15211083Sanish h = host_addr; 15221083Sanish d = dev_addr; 15231083Sanish 15241083Sanish if (flags == DDI_DEV_AUTOINCR) 15251083Sanish for (; repcount; repcount--) 15261083Sanish pci_config_wr32(hdlp, d++, *h++); 15271083Sanish else 15281083Sanish for (; repcount; repcount--) 15291083Sanish pci_config_wr32(hdlp, d, *h++); 15301083Sanish } 15311083Sanish 15321083Sanish uint64_t 15331083Sanish pci_config_rd64(ddi_acc_impl_t *hdlp, uint64_t *addr) 15341083Sanish { 15351083Sanish uint32_t lw_val; 15361083Sanish uint32_t hi_val; 15371083Sanish uint32_t *dp; 15381083Sanish uint64_t val; 15391083Sanish 15401083Sanish dp = (uint32_t *)addr; 15411083Sanish lw_val = pci_config_rd32(hdlp, dp); 15421083Sanish dp++; 15431083Sanish hi_val = pci_config_rd32(hdlp, dp); 15441083Sanish val = ((uint64_t)hi_val << 32) | lw_val; 15451083Sanish return (val); 15461083Sanish } 15471083Sanish 15481083Sanish void 15491083Sanish pci_config_wr64(ddi_acc_impl_t *hdlp, uint64_t *addr, uint64_t value) 15501083Sanish { 15511083Sanish uint32_t lw_val; 15521083Sanish uint32_t hi_val; 15531083Sanish uint32_t *dp; 15541083Sanish 15551083Sanish dp = (uint32_t *)addr; 15561083Sanish lw_val = (uint32_t)(value & 0xffffffff); 15571083Sanish hi_val = (uint32_t)(value >> 32); 15581083Sanish pci_config_wr32(hdlp, dp, lw_val); 15591083Sanish dp++; 15601083Sanish pci_config_wr32(hdlp, dp, hi_val); 15611083Sanish } 15621083Sanish 15631083Sanish void 15641083Sanish pci_config_rep_rd64(ddi_acc_impl_t *hdlp, uint64_t *host_addr, 15651083Sanish uint64_t *dev_addr, size_t repcount, uint_t flags) 15661083Sanish { 15671083Sanish if (flags == DDI_DEV_AUTOINCR) { 15681083Sanish for (; repcount; repcount--) 15691083Sanish *host_addr++ = pci_config_rd64(hdlp, dev_addr++); 15701083Sanish } else { 15711083Sanish for (; repcount; repcount--) 15721083Sanish *host_addr++ = pci_config_rd64(hdlp, dev_addr); 15731083Sanish } 15741083Sanish } 15751083Sanish 15761083Sanish void 15771083Sanish pci_config_rep_wr64(ddi_acc_impl_t *hdlp, uint64_t *host_addr, 15781083Sanish uint64_t *dev_addr, size_t repcount, uint_t flags) 15791083Sanish { 15801083Sanish if (flags == DDI_DEV_AUTOINCR) { 15811083Sanish for (; repcount; repcount--) 15821083Sanish pci_config_wr64(hdlp, host_addr++, *dev_addr++); 15831083Sanish } else { 15841083Sanish for (; repcount; repcount--) 15851083Sanish pci_config_wr64(hdlp, host_addr++, *dev_addr); 15861083Sanish } 15871083Sanish } 15881083Sanish 15891083Sanish 15901083Sanish /* 15911083Sanish * Enable Legacy PCI config space access for the following four north bridges 15921083Sanish * Host bridge: AMD HyperTransport Technology Configuration 15931083Sanish * Host bridge: AMD Address Map 15941083Sanish * Host bridge: AMD DRAM Controller 15951083Sanish * Host bridge: AMD Miscellaneous Control 15961083Sanish */ 15971083Sanish int 15981083Sanish is_amd_northbridge(dev_info_t *dip) 15991083Sanish { 16001083Sanish int vendor_id, device_id; 16011083Sanish 16021083Sanish vendor_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1603*4397Sschwartz "vendor-id", -1); 16041083Sanish device_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1605*4397Sschwartz "device-id", -1); 16061083Sanish 16071083Sanish if (IS_AMD_NTBRIDGE(vendor_id, device_id)) 16081083Sanish return (0); 16091083Sanish 16101083Sanish return (1); 16111083Sanish } 1612