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 /* 231542Sjohnny * Copyright 2006 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> 48916Sschwartz #include <io/pcplusmp/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> 55881Sjohnny 56881Sjohnny /* 57881Sjohnny * Function prototypes 58881Sjohnny */ 59881Sjohnny static int pci_get_priority(dev_info_t *, ddi_intr_handle_impl_t *, int *); 60881Sjohnny static int pci_enable_intr(dev_info_t *, dev_info_t *, 61881Sjohnny ddi_intr_handle_impl_t *, uint32_t); 62881Sjohnny static void pci_disable_intr(dev_info_t *, dev_info_t *, 63881Sjohnny ddi_intr_handle_impl_t *, uint32_t); 64881Sjohnny 65881Sjohnny /* Extern decalration for pcplusmp module */ 66881Sjohnny extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *, 67881Sjohnny psm_intr_op_t, int *); 68881Sjohnny 69881Sjohnny 70881Sjohnny /* 71881Sjohnny * pci_name_child: 72881Sjohnny * 73881Sjohnny * Assign the address portion of the node name 74881Sjohnny */ 75881Sjohnny int 76881Sjohnny pci_common_name_child(dev_info_t *child, char *name, int namelen) 77881Sjohnny { 78881Sjohnny int dev, func, length; 79881Sjohnny char **unit_addr; 80881Sjohnny uint_t n; 81881Sjohnny pci_regspec_t *pci_rp; 82881Sjohnny 83881Sjohnny if (ndi_dev_is_persistent_node(child) == 0) { 84881Sjohnny /* 85881Sjohnny * For .conf node, use "unit-address" property 86881Sjohnny */ 87881Sjohnny if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 88881Sjohnny DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) != 89881Sjohnny DDI_PROP_SUCCESS) { 90881Sjohnny cmn_err(CE_WARN, "cannot find unit-address in %s.conf", 91881Sjohnny ddi_get_name(child)); 92881Sjohnny return (DDI_FAILURE); 93881Sjohnny } 94881Sjohnny if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 95881Sjohnny cmn_err(CE_WARN, "unit-address property in %s.conf" 96881Sjohnny " not well-formed", ddi_get_name(child)); 97881Sjohnny ddi_prop_free(unit_addr); 98881Sjohnny return (DDI_FAILURE); 99881Sjohnny } 100881Sjohnny (void) snprintf(name, namelen, "%s", *unit_addr); 101881Sjohnny ddi_prop_free(unit_addr); 102881Sjohnny return (DDI_SUCCESS); 103881Sjohnny } 104881Sjohnny 105881Sjohnny if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 106881Sjohnny "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) { 107881Sjohnny cmn_err(CE_WARN, "cannot find reg property in %s", 108881Sjohnny ddi_get_name(child)); 109881Sjohnny return (DDI_FAILURE); 110881Sjohnny } 111881Sjohnny 112881Sjohnny /* copy the device identifications */ 113881Sjohnny dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 114881Sjohnny func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); 115881Sjohnny 116881Sjohnny /* 117881Sjohnny * free the memory allocated by ddi_prop_lookup_int_array 118881Sjohnny */ 119881Sjohnny ddi_prop_free(pci_rp); 120881Sjohnny 121881Sjohnny if (func != 0) { 122881Sjohnny (void) snprintf(name, namelen, "%x,%x", dev, func); 123881Sjohnny } else { 124881Sjohnny (void) snprintf(name, namelen, "%x", dev); 125881Sjohnny } 126881Sjohnny 127881Sjohnny return (DDI_SUCCESS); 128881Sjohnny } 129881Sjohnny 130881Sjohnny /* 131881Sjohnny * Interrupt related code: 132881Sjohnny * 133881Sjohnny * The following busop is common to npe and pci drivers 134881Sjohnny * bus_introp 135881Sjohnny */ 136881Sjohnny 137881Sjohnny /* 138881Sjohnny * Create the ddi_parent_private_data for a pseudo child. 139881Sjohnny */ 140881Sjohnny void 141881Sjohnny pci_common_set_parent_private_data(dev_info_t *dip) 142881Sjohnny { 143881Sjohnny struct ddi_parent_private_data *pdptr; 144881Sjohnny 145881Sjohnny pdptr = (struct ddi_parent_private_data *)kmem_zalloc( 146881Sjohnny (sizeof (struct ddi_parent_private_data) + 147881Sjohnny sizeof (struct intrspec)), KM_SLEEP); 148881Sjohnny pdptr->par_intr = (struct intrspec *)(pdptr + 1); 149881Sjohnny pdptr->par_nintr = 1; 150881Sjohnny ddi_set_parent_data(dip, pdptr); 151881Sjohnny } 152881Sjohnny 153881Sjohnny /* 154881Sjohnny * pci_get_priority: 155881Sjohnny * Figure out the priority of the device 156881Sjohnny */ 157881Sjohnny static int 158881Sjohnny pci_get_priority(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, int *pri) 159881Sjohnny { 160881Sjohnny struct intrspec *ispec; 161881Sjohnny 162881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_get_priority: dip = 0x%p, hdlp = %p\n", 163881Sjohnny (void *)dip, (void *)hdlp)); 164881Sjohnny 165881Sjohnny if ((ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip, 166881Sjohnny hdlp->ih_inum)) == NULL) { 167881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) { 168881Sjohnny int class = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 169881Sjohnny DDI_PROP_DONTPASS, "class-code", -1); 170881Sjohnny 171881Sjohnny *pri = (class == -1) ? 1 : pci_devclass_to_ipl(class); 172881Sjohnny pci_common_set_parent_private_data(hdlp->ih_dip); 173881Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip, 174881Sjohnny hdlp->ih_inum); 175881Sjohnny return (DDI_SUCCESS); 176881Sjohnny } 177881Sjohnny return (DDI_FAILURE); 178881Sjohnny } 179881Sjohnny 180881Sjohnny *pri = ispec->intrspec_pri; 181881Sjohnny return (DDI_SUCCESS); 182881Sjohnny } 183881Sjohnny 184881Sjohnny 185881Sjohnny 186881Sjohnny static int pcie_pci_intr_pri_counter = 0; 187881Sjohnny 188881Sjohnny /* 189881Sjohnny * pci_common_intr_ops: bus_intr_op() function for interrupt support 190881Sjohnny */ 191881Sjohnny int 192881Sjohnny pci_common_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, 193881Sjohnny ddi_intr_handle_impl_t *hdlp, void *result) 194881Sjohnny { 195881Sjohnny int priority = 0; 196881Sjohnny int psm_status = 0; 197881Sjohnny int pci_status = 0; 198881Sjohnny int pci_rval, psm_rval = PSM_FAILURE; 199881Sjohnny int types = 0; 200881Sjohnny int pciepci = 0; 2011542Sjohnny int i, j, count; 202881Sjohnny int behavior; 2031997Sanish int cap_ptr; 204881Sjohnny ddi_intrspec_t isp; 205881Sjohnny struct intrspec *ispec; 206881Sjohnny ddi_intr_handle_impl_t tmp_hdl; 207881Sjohnny ddi_intr_msix_t *msix_p; 2081087Sschwartz ihdl_plat_t *ihdl_plat_datap; 2091542Sjohnny ddi_intr_handle_t *h_array; 2101997Sanish ddi_acc_handle_t handle; 211881Sjohnny 212881Sjohnny DDI_INTR_NEXDBG((CE_CONT, 213881Sjohnny "pci_common_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n", 214881Sjohnny (void *)pdip, (void *)rdip, intr_op, (void *)hdlp)); 215881Sjohnny 216881Sjohnny /* Process the request */ 217881Sjohnny switch (intr_op) { 218881Sjohnny case DDI_INTROP_SUPPORTED_TYPES: 219881Sjohnny /* Fixed supported by default */ 220881Sjohnny *(int *)result = DDI_INTR_TYPE_FIXED; 221881Sjohnny 222881Sjohnny /* Figure out if MSI or MSI-X is supported? */ 223881Sjohnny if (pci_msi_get_supported_type(rdip, &types) != DDI_SUCCESS) 224881Sjohnny return (DDI_SUCCESS); 225881Sjohnny 226881Sjohnny if (psm_intr_ops != NULL) { 2271725Segillett /* 2281725Segillett * Only support MSI for now, OR it in 2291725Segillett */ 2301725Segillett *(int *)result |= (types & DDI_INTR_TYPE_MSI); 231881Sjohnny 232881Sjohnny tmp_hdl.ih_type = *(int *)result; 233881Sjohnny (void) (*psm_intr_ops)(rdip, &tmp_hdl, 234881Sjohnny PSM_INTR_OP_CHECK_MSI, result); 235881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 236881Sjohnny "rdip: 0x%p supported types: 0x%x\n", (void *)rdip, 237881Sjohnny *(int *)result)); 238881Sjohnny } 239881Sjohnny break; 240*2580Sanish case DDI_INTROP_NAVAIL: 241881Sjohnny case DDI_INTROP_NINTRS: 242*2580Sanish if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) { 243*2580Sanish if (pci_msi_get_nintrs(hdlp->ih_dip, hdlp->ih_type, 244*2580Sanish result) != DDI_SUCCESS) 245*2580Sanish return (DDI_FAILURE); 246*2580Sanish } else { 247*2580Sanish *(int *)result = i_ddi_get_intx_nintrs(hdlp->ih_dip); 248*2580Sanish if (*(int *)result == 0) 249*2580Sanish return (DDI_FAILURE); 250*2580Sanish } 251881Sjohnny break; 252881Sjohnny case DDI_INTROP_ALLOC: 253881Sjohnny /* 254881Sjohnny * MSI or MSIX (figure out number of vectors available) 255881Sjohnny * FIXED interrupts: just return available interrupts 256881Sjohnny */ 257881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && 258881Sjohnny (psm_intr_ops != NULL) && 259881Sjohnny (pci_get_priority(rdip, hdlp, &priority) == DDI_SUCCESS)) { 260881Sjohnny /* 261881Sjohnny * Following check is a special case for 'pcie_pci'. 262881Sjohnny * This makes sure vectors with the right priority 263881Sjohnny * are allocated for pcie_pci during ALLOC time. 264881Sjohnny */ 265881Sjohnny if (strcmp(ddi_driver_name(rdip), "pcie_pci") == 0) { 266881Sjohnny hdlp->ih_pri = 267881Sjohnny (pcie_pci_intr_pri_counter % 2) ? 4 : 7; 268881Sjohnny pciepci = 1; 269881Sjohnny } else 270881Sjohnny hdlp->ih_pri = priority; 2711717Swesolows behavior = (int)(uintptr_t)hdlp->ih_scratch2; 2721997Sanish 2731997Sanish /* 2741997Sanish * Cache in the config handle and cap_ptr 2751997Sanish */ 2761997Sanish if (i_ddi_get_pci_config_handle(rdip) == NULL) { 2771997Sanish if (pci_config_setup(rdip, &handle) != 2781997Sanish DDI_SUCCESS) 2791997Sanish return (DDI_FAILURE); 2801997Sanish i_ddi_set_pci_config_handle(rdip, handle); 2811997Sanish } 2821997Sanish 2831997Sanish if (i_ddi_get_msi_msix_cap_ptr(rdip) == 0) { 2841997Sanish char *prop = 2851997Sanish (hdlp->ih_type == DDI_INTR_TYPE_MSI) ? 2861997Sanish "pci-msi-capid-pointer" : 2871997Sanish "pci-msix-capid-pointer"; 2881997Sanish 2891997Sanish cap_ptr = ddi_prop_get_int(DDI_DEV_T_ANY, rdip, 2901997Sanish DDI_PROP_DONTPASS, prop, 2911997Sanish PCI_CAP_NEXT_PTR_NULL); 2921997Sanish i_ddi_set_msi_msix_cap_ptr(rdip, cap_ptr); 2931997Sanish } 2941997Sanish 2951997Sanish 296881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 297881Sjohnny PSM_INTR_OP_ALLOC_VECTORS, result); 298881Sjohnny 299881Sjohnny /* verify behavior flag and take appropriate action */ 300881Sjohnny if ((behavior == DDI_INTR_ALLOC_STRICT) && 301881Sjohnny (*(int *)result < hdlp->ih_scratch1)) { 302881Sjohnny DDI_INTR_NEXDBG((CE_CONT, 303881Sjohnny "pci_common_intr_ops: behavior %x, " 304881Sjohnny "couldn't get enough intrs\n", behavior)); 305881Sjohnny hdlp->ih_scratch1 = *(int *)result; 306881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 307881Sjohnny PSM_INTR_OP_FREE_VECTORS, NULL); 308881Sjohnny return (DDI_EAGAIN); 309881Sjohnny } 310881Sjohnny 311881Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) { 312881Sjohnny if (!(msix_p = i_ddi_get_msix(hdlp->ih_dip))) { 313881Sjohnny msix_p = pci_msix_init(hdlp->ih_dip); 314881Sjohnny if (msix_p) 315881Sjohnny i_ddi_set_msix(hdlp->ih_dip, 316881Sjohnny msix_p); 317881Sjohnny } 318881Sjohnny } 319881Sjohnny 320881Sjohnny if (pciepci) { 321881Sjohnny /* update priority in ispec */ 322881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, 323881Sjohnny (int)hdlp->ih_inum); 324881Sjohnny ispec = (struct intrspec *)isp; 325881Sjohnny if (ispec) 326881Sjohnny ispec->intrspec_pri = hdlp->ih_pri; 327881Sjohnny ++pcie_pci_intr_pri_counter; 328881Sjohnny } 329881Sjohnny 330881Sjohnny } else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) { 331881Sjohnny /* Figure out if this device supports MASKING */ 332881Sjohnny pci_rval = pci_intx_get_cap(rdip, &pci_status); 333881Sjohnny if (pci_rval == DDI_SUCCESS && pci_status) 334881Sjohnny hdlp->ih_cap |= pci_status; 335881Sjohnny *(int *)result = 1; /* DDI_INTR_TYPE_FIXED */ 336881Sjohnny } else 337881Sjohnny return (DDI_FAILURE); 338881Sjohnny break; 339881Sjohnny case DDI_INTROP_FREE: 340881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && 341881Sjohnny (psm_intr_ops != NULL)) { 3422018Sanish if (i_ddi_intr_get_current_nintrs(hdlp->ih_dip) - 1 == 3432018Sanish 0) { 3441997Sanish if (handle = i_ddi_get_pci_config_handle( 3451997Sanish rdip)) { 3461997Sanish (void) pci_config_teardown(&handle); 3471997Sanish i_ddi_set_pci_config_handle(rdip, NULL); 3481997Sanish } 3491997Sanish if (cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip)) 3501997Sanish i_ddi_set_msi_msix_cap_ptr(rdip, 0); 3511997Sanish } 3521997Sanish 353881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 354881Sjohnny PSM_INTR_OP_FREE_VECTORS, NULL); 355881Sjohnny 356881Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) { 357881Sjohnny msix_p = i_ddi_get_msix(hdlp->ih_dip); 358881Sjohnny if (msix_p && 3592018Sanish (i_ddi_intr_get_current_nintrs( 3602018Sanish hdlp->ih_dip) - 1) == 0) { 361881Sjohnny pci_msix_fini(msix_p); 362881Sjohnny i_ddi_set_msix(hdlp->ih_dip, NULL); 363881Sjohnny } 364881Sjohnny } 365881Sjohnny } 366881Sjohnny break; 367881Sjohnny case DDI_INTROP_GETPRI: 368881Sjohnny /* Get the priority */ 369881Sjohnny if (pci_get_priority(rdip, hdlp, &priority) != DDI_SUCCESS) 370881Sjohnny return (DDI_FAILURE); 371881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 372881Sjohnny "priority = 0x%x\n", priority)); 373881Sjohnny *(int *)result = priority; 374881Sjohnny break; 375881Sjohnny case DDI_INTROP_SETPRI: 376881Sjohnny /* Validate the interrupt priority passed */ 377881Sjohnny if (*(int *)result > LOCK_LEVEL) 378881Sjohnny return (DDI_FAILURE); 379881Sjohnny 380881Sjohnny /* Ensure that PSM is all initialized */ 381881Sjohnny if (psm_intr_ops == NULL) 382881Sjohnny return (DDI_FAILURE); 383881Sjohnny 384881Sjohnny /* Change the priority */ 385881Sjohnny if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_PRI, result) == 386881Sjohnny PSM_FAILURE) 387881Sjohnny return (DDI_FAILURE); 388881Sjohnny 389881Sjohnny /* update ispec */ 390881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 391881Sjohnny ispec = (struct intrspec *)isp; 392881Sjohnny if (ispec) 393881Sjohnny ispec->intrspec_pri = *(int *)result; 394881Sjohnny break; 395881Sjohnny case DDI_INTROP_ADDISR: 396881Sjohnny /* update ispec */ 397881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 398881Sjohnny ispec = (struct intrspec *)isp; 3991087Sschwartz if (ispec) { 400881Sjohnny ispec->intrspec_func = hdlp->ih_cb_func; 4011087Sschwartz ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 4021087Sschwartz pci_kstat_create(&ihdl_plat_datap->ip_ksp, pdip, hdlp); 4031087Sschwartz } 404881Sjohnny break; 405881Sjohnny case DDI_INTROP_REMISR: 406881Sjohnny /* Get the interrupt structure pointer */ 407881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 408881Sjohnny ispec = (struct intrspec *)isp; 4091087Sschwartz if (ispec) { 410881Sjohnny ispec->intrspec_func = (uint_t (*)()) 0; 4111087Sschwartz ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 4121087Sschwartz if (ihdl_plat_datap->ip_ksp != NULL) 4131087Sschwartz pci_kstat_delete(ihdl_plat_datap->ip_ksp); 4141087Sschwartz } 415881Sjohnny break; 416881Sjohnny case DDI_INTROP_GETCAP: 417881Sjohnny /* 418881Sjohnny * First check the config space and/or 419881Sjohnny * MSI capability register(s) 420881Sjohnny */ 421881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 422881Sjohnny pci_rval = pci_msi_get_cap(rdip, hdlp->ih_type, 423881Sjohnny &pci_status); 424881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 425881Sjohnny pci_rval = pci_intx_get_cap(rdip, &pci_status); 426881Sjohnny 427881Sjohnny /* next check with pcplusmp */ 428881Sjohnny if (psm_intr_ops != NULL) 429881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 430881Sjohnny PSM_INTR_OP_GET_CAP, &psm_status); 431881Sjohnny 432881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned psm_rval = %x, " 433881Sjohnny "psm_status = %x, pci_rval = %x, pci_status = %x\n", 434881Sjohnny psm_rval, psm_status, pci_rval, pci_status)); 435881Sjohnny 436881Sjohnny if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) { 437881Sjohnny *(int *)result = 0; 438881Sjohnny return (DDI_FAILURE); 439881Sjohnny } 440881Sjohnny 441881Sjohnny if (psm_rval == PSM_SUCCESS) 442881Sjohnny *(int *)result = psm_status; 443881Sjohnny 444881Sjohnny if (pci_rval == DDI_SUCCESS) 445881Sjohnny *(int *)result |= pci_status; 446881Sjohnny 447881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned = %x\n", 448881Sjohnny *(int *)result)); 449881Sjohnny break; 450881Sjohnny case DDI_INTROP_SETCAP: 451881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 452881Sjohnny "SETCAP cap=0x%x\n", *(int *)result)); 453881Sjohnny if (psm_intr_ops == NULL) 454881Sjohnny return (DDI_FAILURE); 455881Sjohnny 456881Sjohnny if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) { 457881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "GETCAP: psm_intr_ops" 458881Sjohnny " returned failure\n")); 459881Sjohnny return (DDI_FAILURE); 460881Sjohnny } 461881Sjohnny break; 462881Sjohnny case DDI_INTROP_ENABLE: 463881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE\n")); 464881Sjohnny if (psm_intr_ops == NULL) 465881Sjohnny return (DDI_FAILURE); 466881Sjohnny 467881Sjohnny if (pci_enable_intr(pdip, rdip, hdlp, hdlp->ih_inum) != 468881Sjohnny DDI_SUCCESS) 469881Sjohnny return (DDI_FAILURE); 470881Sjohnny 471881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE " 472881Sjohnny "vector=0x%x\n", hdlp->ih_vector)); 473881Sjohnny break; 474881Sjohnny case DDI_INTROP_DISABLE: 475881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE\n")); 476881Sjohnny if (psm_intr_ops == NULL) 477881Sjohnny return (DDI_FAILURE); 478881Sjohnny 479881Sjohnny pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum); 480881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE " 481881Sjohnny "vector = %x\n", hdlp->ih_vector)); 482881Sjohnny break; 483881Sjohnny case DDI_INTROP_BLOCKENABLE: 484881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 485881Sjohnny "BLOCKENABLE\n")); 486881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_MSI) { 487881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: not MSI\n")); 488881Sjohnny return (DDI_FAILURE); 489881Sjohnny } 490881Sjohnny 491881Sjohnny /* Check if psm_intr_ops is NULL? */ 492881Sjohnny if (psm_intr_ops == NULL) 493881Sjohnny return (DDI_FAILURE); 494881Sjohnny 4951542Sjohnny count = hdlp->ih_scratch1; 4961542Sjohnny h_array = (ddi_intr_handle_t *)hdlp->ih_scratch2; 4971542Sjohnny for (i = 0; i < count; i++) { 4981542Sjohnny hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 499881Sjohnny if (pci_enable_intr(pdip, rdip, hdlp, 5001542Sjohnny hdlp->ih_inum) != DDI_SUCCESS) { 501881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: " 502881Sjohnny "pci_enable_intr failed for %d\n", i)); 5031542Sjohnny for (j = 0; j < i; j++) { 5041542Sjohnny hdlp = (ddi_intr_handle_impl_t *)h_array[j]; 5051542Sjohnny pci_disable_intr(pdip, rdip, hdlp, 5061542Sjohnny hdlp->ih_inum); 5071542Sjohnny } 508881Sjohnny return (DDI_FAILURE); 509881Sjohnny } 510881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 5111542Sjohnny "BLOCKENABLE inum %x done\n", hdlp->ih_inum)); 512881Sjohnny } 513881Sjohnny break; 514881Sjohnny case DDI_INTROP_BLOCKDISABLE: 515881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 516881Sjohnny "BLOCKDISABLE\n")); 517881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_MSI) { 518881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKDISABLE: not MSI\n")); 519881Sjohnny return (DDI_FAILURE); 520881Sjohnny } 521881Sjohnny 522881Sjohnny /* Check if psm_intr_ops is present */ 523881Sjohnny if (psm_intr_ops == NULL) 524881Sjohnny return (DDI_FAILURE); 525881Sjohnny 5261542Sjohnny count = hdlp->ih_scratch1; 5271542Sjohnny h_array = (ddi_intr_handle_t *)hdlp->ih_scratch2; 5281542Sjohnny for (i = 0; i < count; i++) { 5291542Sjohnny hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 5301542Sjohnny pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum); 531881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 5321542Sjohnny "BLOCKDISABLE inum %x done\n", hdlp->ih_inum)); 533881Sjohnny } 534881Sjohnny break; 535881Sjohnny case DDI_INTROP_SETMASK: 536881Sjohnny case DDI_INTROP_CLRMASK: 537881Sjohnny /* 538881Sjohnny * First handle in the config space 539881Sjohnny */ 540881Sjohnny if (intr_op == DDI_INTROP_SETMASK) { 541881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 542881Sjohnny pci_status = pci_msi_set_mask(rdip, 543881Sjohnny hdlp->ih_type, hdlp->ih_inum); 544881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 545881Sjohnny pci_status = pci_intx_set_mask(rdip); 546881Sjohnny } else { 547881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 548881Sjohnny pci_status = pci_msi_clr_mask(rdip, 549881Sjohnny hdlp->ih_type, hdlp->ih_inum); 550881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 551881Sjohnny pci_status = pci_intx_clr_mask(rdip); 552881Sjohnny } 553881Sjohnny 554881Sjohnny /* For MSI/X; no need to check with pcplusmp */ 555881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 556881Sjohnny return (pci_status); 557881Sjohnny 558881Sjohnny /* For fixed interrupts only: handle config space first */ 559881Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_FIXED && 560881Sjohnny pci_status == DDI_SUCCESS) 561881Sjohnny break; 562881Sjohnny 563881Sjohnny /* For fixed interrupts only: confer with pcplusmp next */ 564881Sjohnny if (psm_intr_ops != NULL) { 565881Sjohnny /* If interrupt is shared; do nothing */ 566881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 567881Sjohnny PSM_INTR_OP_GET_SHARED, &psm_status); 568881Sjohnny 569881Sjohnny if (psm_rval == PSM_FAILURE || psm_status == 1) 570881Sjohnny return (pci_status); 571881Sjohnny 572881Sjohnny /* Now, pcplusmp should try to set/clear the mask */ 573881Sjohnny if (intr_op == DDI_INTROP_SETMASK) 574881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 575881Sjohnny PSM_INTR_OP_SET_MASK, NULL); 576881Sjohnny else 577881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 578881Sjohnny PSM_INTR_OP_CLEAR_MASK, NULL); 579881Sjohnny } 580881Sjohnny return ((psm_rval == PSM_FAILURE) ? DDI_FAILURE : DDI_SUCCESS); 581881Sjohnny case DDI_INTROP_GETPENDING: 582881Sjohnny /* 583881Sjohnny * First check the config space and/or 584881Sjohnny * MSI capability register(s) 585881Sjohnny */ 586881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 587881Sjohnny pci_rval = pci_msi_get_pending(rdip, hdlp->ih_type, 588881Sjohnny hdlp->ih_inum, &pci_status); 589881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 590881Sjohnny pci_rval = pci_intx_get_pending(rdip, &pci_status); 591881Sjohnny 592881Sjohnny /* On failure; next try with pcplusmp */ 593881Sjohnny if (pci_rval != DDI_SUCCESS && psm_intr_ops != NULL) 594881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 595881Sjohnny PSM_INTR_OP_GET_PENDING, &psm_status); 596881Sjohnny 597881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned " 598881Sjohnny "psm_rval = %x, psm_status = %x, pci_rval = %x, " 599881Sjohnny "pci_status = %x\n", psm_rval, psm_status, pci_rval, 600881Sjohnny pci_status)); 601881Sjohnny if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) { 602881Sjohnny *(int *)result = 0; 603881Sjohnny return (DDI_FAILURE); 604881Sjohnny } 605881Sjohnny 606881Sjohnny if (psm_rval != PSM_FAILURE) 607881Sjohnny *(int *)result = psm_status; 608881Sjohnny else if (pci_rval != DDI_FAILURE) 609881Sjohnny *(int *)result = pci_status; 610881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned = %x\n", 611881Sjohnny *(int *)result)); 612881Sjohnny break; 613881Sjohnny default: 614881Sjohnny return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result)); 615881Sjohnny } 616881Sjohnny 617881Sjohnny return (DDI_SUCCESS); 618881Sjohnny } 619881Sjohnny 620916Sschwartz int 621916Sschwartz pci_get_intr_from_vecirq(apic_get_intr_t *intrinfo_p, 622916Sschwartz int vecirq, boolean_t is_irq) 623916Sschwartz { 624916Sschwartz ddi_intr_handle_impl_t get_info_ii_hdl; 625916Sschwartz 626916Sschwartz if (is_irq) 627916Sschwartz intrinfo_p->avgi_req_flags |= PSMGI_INTRBY_IRQ; 628916Sschwartz 629916Sschwartz /* 630916Sschwartz * For this locally-declared and used handle, ih_private will contain a 631916Sschwartz * pointer to apic_get_intr_t, not an ihdl_plat_t as used for 632916Sschwartz * global interrupt handling. 633916Sschwartz */ 634916Sschwartz get_info_ii_hdl.ih_private = intrinfo_p; 635916Sschwartz get_info_ii_hdl.ih_vector = (ushort_t)vecirq; 636916Sschwartz 637916Sschwartz if ((*psm_intr_ops)(NULL, &get_info_ii_hdl, 638916Sschwartz PSM_INTR_OP_GET_INTR, NULL) == PSM_FAILURE) 639916Sschwartz return (DDI_FAILURE); 640916Sschwartz 641916Sschwartz return (DDI_SUCCESS); 642916Sschwartz } 643916Sschwartz 644916Sschwartz 645916Sschwartz int 646916Sschwartz pci_get_cpu_from_vecirq(int vecirq, boolean_t is_irq) 647916Sschwartz { 648916Sschwartz int rval; 649916Sschwartz 650916Sschwartz apic_get_intr_t intrinfo; 651916Sschwartz intrinfo.avgi_req_flags = PSMGI_REQ_CPUID; 652916Sschwartz rval = pci_get_intr_from_vecirq(&intrinfo, vecirq, is_irq); 653916Sschwartz 654916Sschwartz if (rval == DDI_SUCCESS) 655916Sschwartz return (intrinfo.avgi_cpu_id); 656916Sschwartz else 657916Sschwartz return (-1); 658916Sschwartz } 659916Sschwartz 660881Sjohnny 661881Sjohnny static int 662881Sjohnny pci_enable_intr(dev_info_t *pdip, dev_info_t *rdip, 663881Sjohnny ddi_intr_handle_impl_t *hdlp, uint32_t inum) 664881Sjohnny { 665881Sjohnny struct intrspec *ispec; 666916Sschwartz int irq; 667916Sschwartz ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 668881Sjohnny 669881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: hdlp %p inum %x\n", 670881Sjohnny (void *)hdlp, inum)); 671881Sjohnny 672881Sjohnny /* Translate the interrupt if needed */ 673881Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum); 6742288Sanish if (ispec == NULL) 6752288Sanish return (DDI_FAILURE); 6762288Sanish if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 677881Sjohnny ispec->intrspec_vec = inum; 678916Sschwartz ihdl_plat_datap->ip_ispecp = ispec; 679881Sjohnny 680881Sjohnny /* translate the interrupt if needed */ 681916Sschwartz (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq); 682916Sschwartz DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: priority=%x irq=%x\n", 683916Sschwartz hdlp->ih_pri, irq)); 684881Sjohnny 685881Sjohnny /* Add the interrupt handler */ 686881Sjohnny if (!add_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, 687916Sschwartz DEVI(rdip)->devi_name, irq, hdlp->ih_cb_arg1, 688916Sschwartz hdlp->ih_cb_arg2, &ihdl_plat_datap->ip_ticks, rdip)) 689881Sjohnny return (DDI_FAILURE); 690881Sjohnny 691916Sschwartz /* Note this really is an irq. */ 692916Sschwartz hdlp->ih_vector = (ushort_t)irq; 693916Sschwartz 694881Sjohnny return (DDI_SUCCESS); 695881Sjohnny } 696881Sjohnny 697881Sjohnny 698881Sjohnny static void 699881Sjohnny pci_disable_intr(dev_info_t *pdip, dev_info_t *rdip, 700881Sjohnny ddi_intr_handle_impl_t *hdlp, uint32_t inum) 701881Sjohnny { 702916Sschwartz int irq; 703881Sjohnny struct intrspec *ispec; 704916Sschwartz ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 705881Sjohnny 706881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_disable_intr: \n")); 707881Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum); 7082288Sanish if (ispec == NULL) 7092288Sanish return; 7102288Sanish if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 711881Sjohnny ispec->intrspec_vec = inum; 712916Sschwartz ihdl_plat_datap->ip_ispecp = ispec; 713881Sjohnny 714881Sjohnny /* translate the interrupt if needed */ 715916Sschwartz (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq); 716881Sjohnny 717881Sjohnny /* Disable the interrupt handler */ 718916Sschwartz rem_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, irq); 719916Sschwartz ihdl_plat_datap->ip_ispecp = NULL; 720881Sjohnny } 721881Sjohnny 722881Sjohnny /* 723881Sjohnny * Miscellaneous library function 724881Sjohnny */ 725881Sjohnny int 726881Sjohnny pci_common_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp) 727881Sjohnny { 728881Sjohnny int i; 729881Sjohnny int number; 730881Sjohnny int assigned_addr_len; 731881Sjohnny uint_t phys_hi = pci_rp->pci_phys_hi; 732881Sjohnny pci_regspec_t *assigned_addr; 733881Sjohnny 734881Sjohnny if (((phys_hi & PCI_REG_ADDR_M) == PCI_ADDR_CONFIG) || 735881Sjohnny (phys_hi & PCI_RELOCAT_B)) 736881Sjohnny return (DDI_SUCCESS); 737881Sjohnny 738881Sjohnny /* 739881Sjohnny * the "reg" property specifies relocatable, get and interpret the 740881Sjohnny * "assigned-addresses" property. 741881Sjohnny */ 742881Sjohnny if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 743881Sjohnny "assigned-addresses", (int **)&assigned_addr, 744881Sjohnny (uint_t *)&assigned_addr_len) != DDI_PROP_SUCCESS) 745881Sjohnny return (DDI_FAILURE); 746881Sjohnny 747881Sjohnny /* 748881Sjohnny * Scan the "assigned-addresses" for one that matches the specified 749881Sjohnny * "reg" property entry. 750881Sjohnny */ 751881Sjohnny phys_hi &= PCI_CONF_ADDR_MASK; 752881Sjohnny number = assigned_addr_len / (sizeof (pci_regspec_t) / sizeof (int)); 753881Sjohnny for (i = 0; i < number; i++) { 754881Sjohnny if ((assigned_addr[i].pci_phys_hi & PCI_CONF_ADDR_MASK) == 755881Sjohnny phys_hi) { 756881Sjohnny pci_rp->pci_phys_mid = assigned_addr[i].pci_phys_mid; 757881Sjohnny pci_rp->pci_phys_low = assigned_addr[i].pci_phys_low; 758881Sjohnny ddi_prop_free(assigned_addr); 759881Sjohnny return (DDI_SUCCESS); 760881Sjohnny } 761881Sjohnny } 762881Sjohnny 763881Sjohnny ddi_prop_free(assigned_addr); 764881Sjohnny return (DDI_FAILURE); 765881Sjohnny } 766881Sjohnny 767881Sjohnny 768881Sjohnny /* 769881Sjohnny * For pci_tools 770881Sjohnny */ 771881Sjohnny 772881Sjohnny int 773881Sjohnny pci_common_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg, 774881Sjohnny int mode, cred_t *credp, int *rvalp) 775881Sjohnny { 776881Sjohnny int rv = ENOTTY; 777881Sjohnny 778881Sjohnny minor_t minor = getminor(dev); 779881Sjohnny 780881Sjohnny switch (PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(minor)) { 781881Sjohnny case PCI_TOOL_REG_MINOR_NUM: 782881Sjohnny 783881Sjohnny switch (cmd) { 784881Sjohnny case PCITOOL_DEVICE_SET_REG: 785881Sjohnny case PCITOOL_DEVICE_GET_REG: 786881Sjohnny 787881Sjohnny /* Require full privileges. */ 788881Sjohnny if (secpolicy_kmdb(credp)) 789881Sjohnny rv = EPERM; 790881Sjohnny else 791881Sjohnny rv = pcitool_dev_reg_ops(dip, (void *)arg, 792881Sjohnny cmd, mode); 793881Sjohnny break; 794881Sjohnny 795881Sjohnny case PCITOOL_NEXUS_SET_REG: 796881Sjohnny case PCITOOL_NEXUS_GET_REG: 797881Sjohnny 798881Sjohnny /* Require full privileges. */ 799881Sjohnny if (secpolicy_kmdb(credp)) 800881Sjohnny rv = EPERM; 801881Sjohnny else 802881Sjohnny rv = pcitool_bus_reg_ops(dip, (void *)arg, 803881Sjohnny cmd, mode); 804881Sjohnny break; 805881Sjohnny } 806881Sjohnny break; 807881Sjohnny 808881Sjohnny case PCI_TOOL_INTR_MINOR_NUM: 809881Sjohnny 810881Sjohnny switch (cmd) { 811881Sjohnny case PCITOOL_DEVICE_SET_INTR: 812881Sjohnny 813881Sjohnny /* Require PRIV_SYS_RES_CONFIG, same as psradm */ 814881Sjohnny if (secpolicy_ponline(credp)) { 815881Sjohnny rv = EPERM; 816881Sjohnny break; 817881Sjohnny } 818881Sjohnny 819881Sjohnny /*FALLTHRU*/ 820881Sjohnny /* These require no special privileges. */ 821881Sjohnny case PCITOOL_DEVICE_GET_INTR: 822881Sjohnny case PCITOOL_DEVICE_NUM_INTR: 823881Sjohnny rv = pcitool_intr_admn(dip, (void *)arg, cmd, mode); 824881Sjohnny break; 825881Sjohnny } 826881Sjohnny break; 827881Sjohnny 828881Sjohnny /* 829881Sjohnny * All non-PCItool ioctls go through here, including: 830881Sjohnny * devctl ioctls with minor number PCIHP_DEVCTL_MINOR and 831881Sjohnny * those for attachment points with where minor number is the 832881Sjohnny * device number. 833881Sjohnny */ 834881Sjohnny default: 835881Sjohnny rv = (pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, 836881Sjohnny credp, rvalp); 837881Sjohnny break; 838881Sjohnny } 839881Sjohnny 840881Sjohnny return (rv); 841881Sjohnny } 8421083Sanish 8431083Sanish 8441865Sdilpreet int 8451865Sdilpreet pci_common_ctlops_poke(peekpoke_ctlops_t *in_args) 8461865Sdilpreet { 8471865Sdilpreet size_t size = in_args->size; 8481865Sdilpreet uintptr_t dev_addr = in_args->dev_addr; 8491865Sdilpreet uintptr_t host_addr = in_args->host_addr; 8501865Sdilpreet ddi_acc_impl_t *hp = (ddi_acc_impl_t *)in_args->handle; 8511865Sdilpreet ddi_acc_hdl_t *hdlp = (ddi_acc_hdl_t *)in_args->handle; 8521865Sdilpreet size_t repcount = in_args->repcount; 8531865Sdilpreet uint_t flags = in_args->flags; 8541865Sdilpreet int err = DDI_SUCCESS; 8551865Sdilpreet 8561865Sdilpreet /* 8571865Sdilpreet * if no handle then this is a poke. We have to return failure here 8581865Sdilpreet * as we have no way of knowing whether this is a MEM or IO space access 8591865Sdilpreet */ 8601865Sdilpreet if (in_args->handle == NULL) 8611865Sdilpreet return (DDI_FAILURE); 8621865Sdilpreet 8631865Sdilpreet /* 8641865Sdilpreet * rest of this function is actually for cautious puts 8651865Sdilpreet */ 8661865Sdilpreet for (; repcount; repcount--) { 8671865Sdilpreet if (hp->ahi_acc_attr == DDI_ACCATTR_CONFIG_SPACE) { 8681865Sdilpreet switch (size) { 8691865Sdilpreet case sizeof (uint8_t): 8701865Sdilpreet pci_config_wr8(hp, (uint8_t *)dev_addr, 8711865Sdilpreet *(uint8_t *)host_addr); 8721865Sdilpreet break; 8731865Sdilpreet case sizeof (uint16_t): 8741865Sdilpreet pci_config_wr16(hp, (uint16_t *)dev_addr, 8751865Sdilpreet *(uint16_t *)host_addr); 8761865Sdilpreet break; 8771865Sdilpreet case sizeof (uint32_t): 8781865Sdilpreet pci_config_wr32(hp, (uint32_t *)dev_addr, 8791865Sdilpreet *(uint32_t *)host_addr); 8801865Sdilpreet break; 8811865Sdilpreet case sizeof (uint64_t): 8821865Sdilpreet pci_config_wr64(hp, (uint64_t *)dev_addr, 8831865Sdilpreet *(uint64_t *)host_addr); 8841865Sdilpreet break; 8851865Sdilpreet default: 8861865Sdilpreet err = DDI_FAILURE; 8871865Sdilpreet break; 8881865Sdilpreet } 8891865Sdilpreet } else if (hp->ahi_acc_attr & DDI_ACCATTR_IO_SPACE) { 8901865Sdilpreet if (hdlp->ah_acc.devacc_attr_endian_flags == 8911865Sdilpreet DDI_STRUCTURE_BE_ACC) { 8921865Sdilpreet switch (size) { 8931865Sdilpreet case sizeof (uint8_t): 8941865Sdilpreet i_ddi_io_put8(hp, 8951865Sdilpreet (uint8_t *)dev_addr, 8961865Sdilpreet *(uint8_t *)host_addr); 8971865Sdilpreet break; 8981865Sdilpreet case sizeof (uint16_t): 8991865Sdilpreet i_ddi_io_swap_put16(hp, 9001865Sdilpreet (uint16_t *)dev_addr, 9011865Sdilpreet *(uint16_t *)host_addr); 9021865Sdilpreet break; 9031865Sdilpreet case sizeof (uint32_t): 9041865Sdilpreet i_ddi_io_swap_put32(hp, 9051865Sdilpreet (uint32_t *)dev_addr, 9061865Sdilpreet *(uint32_t *)host_addr); 9071865Sdilpreet break; 9081865Sdilpreet /* 9091865Sdilpreet * note the 64-bit case is a dummy 9101865Sdilpreet * function - so no need to swap 9111865Sdilpreet */ 9121865Sdilpreet case sizeof (uint64_t): 9131865Sdilpreet i_ddi_io_put64(hp, 9141865Sdilpreet (uint64_t *)dev_addr, 9151865Sdilpreet *(uint64_t *)host_addr); 9161865Sdilpreet break; 9171865Sdilpreet default: 9181865Sdilpreet err = DDI_FAILURE; 9191865Sdilpreet break; 9201865Sdilpreet } 9211865Sdilpreet } else { 9221865Sdilpreet switch (size) { 9231865Sdilpreet case sizeof (uint8_t): 9241865Sdilpreet i_ddi_io_put8(hp, 9251865Sdilpreet (uint8_t *)dev_addr, 9261865Sdilpreet *(uint8_t *)host_addr); 9271865Sdilpreet break; 9281865Sdilpreet case sizeof (uint16_t): 9291865Sdilpreet i_ddi_io_put16(hp, 9301865Sdilpreet (uint16_t *)dev_addr, 9311865Sdilpreet *(uint16_t *)host_addr); 9321865Sdilpreet break; 9331865Sdilpreet case sizeof (uint32_t): 9341865Sdilpreet i_ddi_io_put32(hp, 9351865Sdilpreet (uint32_t *)dev_addr, 9361865Sdilpreet *(uint32_t *)host_addr); 9371865Sdilpreet break; 9381865Sdilpreet case sizeof (uint64_t): 9391865Sdilpreet i_ddi_io_put64(hp, 9401865Sdilpreet (uint64_t *)dev_addr, 9411865Sdilpreet *(uint64_t *)host_addr); 9421865Sdilpreet break; 9431865Sdilpreet default: 9441865Sdilpreet err = DDI_FAILURE; 9451865Sdilpreet break; 9461865Sdilpreet } 9471865Sdilpreet } 9481865Sdilpreet } else { 9491865Sdilpreet if (hdlp->ah_acc.devacc_attr_endian_flags == 9501865Sdilpreet DDI_STRUCTURE_BE_ACC) { 9511865Sdilpreet switch (size) { 9521865Sdilpreet case sizeof (uint8_t): 9531865Sdilpreet *(uint8_t *)dev_addr = 9541865Sdilpreet *(uint8_t *)host_addr; 9551865Sdilpreet break; 9561865Sdilpreet case sizeof (uint16_t): 9571865Sdilpreet *(uint16_t *)dev_addr = 9581865Sdilpreet ddi_swap16(*(uint16_t *)host_addr); 9591865Sdilpreet break; 9601865Sdilpreet case sizeof (uint32_t): 9611865Sdilpreet *(uint32_t *)dev_addr = 9621865Sdilpreet ddi_swap32(*(uint32_t *)host_addr); 9631865Sdilpreet break; 9641865Sdilpreet case sizeof (uint64_t): 9651865Sdilpreet *(uint64_t *)dev_addr = 9661865Sdilpreet ddi_swap64(*(uint64_t *)host_addr); 9671865Sdilpreet break; 9681865Sdilpreet default: 9691865Sdilpreet err = DDI_FAILURE; 9701865Sdilpreet break; 9711865Sdilpreet } 9721865Sdilpreet } else { 9731865Sdilpreet switch (size) { 9741865Sdilpreet case sizeof (uint8_t): 9751865Sdilpreet *(uint8_t *)dev_addr = 9761865Sdilpreet *(uint8_t *)host_addr; 9771865Sdilpreet break; 9781865Sdilpreet case sizeof (uint16_t): 9791865Sdilpreet *(uint16_t *)dev_addr = 9801865Sdilpreet *(uint16_t *)host_addr; 9811865Sdilpreet break; 9821865Sdilpreet case sizeof (uint32_t): 9831865Sdilpreet *(uint32_t *)dev_addr = 9841865Sdilpreet *(uint32_t *)host_addr; 9851865Sdilpreet break; 9861865Sdilpreet case sizeof (uint64_t): 9871865Sdilpreet *(uint64_t *)dev_addr = 9881865Sdilpreet *(uint64_t *)host_addr; 9891865Sdilpreet break; 9901865Sdilpreet default: 9911865Sdilpreet err = DDI_FAILURE; 9921865Sdilpreet break; 9931865Sdilpreet } 9941865Sdilpreet } 9951865Sdilpreet } 9961865Sdilpreet host_addr += size; 9971865Sdilpreet if (flags == DDI_DEV_AUTOINCR) 9981865Sdilpreet dev_addr += size; 9991865Sdilpreet } 10001865Sdilpreet return (err); 10011865Sdilpreet } 10021865Sdilpreet 10031865Sdilpreet 10041865Sdilpreet int 10051865Sdilpreet pci_fm_acc_setup(ddi_acc_hdl_t *hp, off_t offset, off_t len) 10061865Sdilpreet { 10071865Sdilpreet ddi_acc_impl_t *ap = (ddi_acc_impl_t *)hp->ah_platform_private; 10081865Sdilpreet 10091865Sdilpreet /* endian-ness check */ 10101865Sdilpreet if (hp->ah_acc.devacc_attr_endian_flags == DDI_STRUCTURE_BE_ACC) 10111865Sdilpreet return (DDI_FAILURE); 10121865Sdilpreet 10131865Sdilpreet /* 10141865Sdilpreet * range check 10151865Sdilpreet */ 10161865Sdilpreet if ((offset >= PCI_CONF_HDR_SIZE) || 10171865Sdilpreet (len > PCI_CONF_HDR_SIZE) || 10181865Sdilpreet (offset + len > PCI_CONF_HDR_SIZE)) 10191865Sdilpreet return (DDI_FAILURE); 10201865Sdilpreet 10211865Sdilpreet ap->ahi_acc_attr |= DDI_ACCATTR_CONFIG_SPACE; 10221865Sdilpreet /* 10231865Sdilpreet * always use cautious mechanism for config space gets 10241865Sdilpreet */ 10251865Sdilpreet ap->ahi_get8 = i_ddi_caut_get8; 10261865Sdilpreet ap->ahi_get16 = i_ddi_caut_get16; 10271865Sdilpreet ap->ahi_get32 = i_ddi_caut_get32; 10281865Sdilpreet ap->ahi_get64 = i_ddi_caut_get64; 10291865Sdilpreet ap->ahi_rep_get8 = i_ddi_caut_rep_get8; 10301865Sdilpreet ap->ahi_rep_get16 = i_ddi_caut_rep_get16; 10311865Sdilpreet ap->ahi_rep_get32 = i_ddi_caut_rep_get32; 10321865Sdilpreet ap->ahi_rep_get64 = i_ddi_caut_rep_get64; 10331865Sdilpreet if (hp->ah_acc.devacc_attr_access == DDI_CAUTIOUS_ACC) { 10341865Sdilpreet ap->ahi_put8 = i_ddi_caut_put8; 10351865Sdilpreet ap->ahi_put16 = i_ddi_caut_put16; 10361865Sdilpreet ap->ahi_put32 = i_ddi_caut_put32; 10371865Sdilpreet ap->ahi_put64 = i_ddi_caut_put64; 10381865Sdilpreet ap->ahi_rep_put8 = i_ddi_caut_rep_put8; 10391865Sdilpreet ap->ahi_rep_put16 = i_ddi_caut_rep_put16; 10401865Sdilpreet ap->ahi_rep_put32 = i_ddi_caut_rep_put32; 10411865Sdilpreet ap->ahi_rep_put64 = i_ddi_caut_rep_put64; 10421865Sdilpreet } else { 10431865Sdilpreet ap->ahi_put8 = pci_config_wr8; 10441865Sdilpreet ap->ahi_put16 = pci_config_wr16; 10451865Sdilpreet ap->ahi_put32 = pci_config_wr32; 10461865Sdilpreet ap->ahi_put64 = pci_config_wr64; 10471865Sdilpreet ap->ahi_rep_put8 = pci_config_rep_wr8; 10481865Sdilpreet ap->ahi_rep_put16 = pci_config_rep_wr16; 10491865Sdilpreet ap->ahi_rep_put32 = pci_config_rep_wr32; 10501865Sdilpreet ap->ahi_rep_put64 = pci_config_rep_wr64; 10511865Sdilpreet } 10521865Sdilpreet 10531865Sdilpreet /* Initialize to default check/notify functions */ 10541865Sdilpreet ap->ahi_fault_check = i_ddi_acc_fault_check; 10551865Sdilpreet ap->ahi_fault_notify = i_ddi_acc_fault_notify; 10561865Sdilpreet ap->ahi_fault = 0; 10571865Sdilpreet impl_acc_err_init(hp); 10581865Sdilpreet return (DDI_SUCCESS); 10591865Sdilpreet } 10601865Sdilpreet 10611865Sdilpreet 10621865Sdilpreet int 10631865Sdilpreet pci_common_ctlops_peek(peekpoke_ctlops_t *in_args) 10641865Sdilpreet { 10651865Sdilpreet size_t size = in_args->size; 10661865Sdilpreet uintptr_t dev_addr = in_args->dev_addr; 10671865Sdilpreet uintptr_t host_addr = in_args->host_addr; 10681865Sdilpreet ddi_acc_impl_t *hp = (ddi_acc_impl_t *)in_args->handle; 10691865Sdilpreet ddi_acc_hdl_t *hdlp = (ddi_acc_hdl_t *)in_args->handle; 10701865Sdilpreet size_t repcount = in_args->repcount; 10711865Sdilpreet uint_t flags = in_args->flags; 10721865Sdilpreet int err = DDI_SUCCESS; 10731865Sdilpreet 10741865Sdilpreet /* 10751865Sdilpreet * if no handle then this is a peek. We have to return failure here 10761865Sdilpreet * as we have no way of knowing whether this is a MEM or IO space access 10771865Sdilpreet */ 10781865Sdilpreet if (in_args->handle == NULL) 10791865Sdilpreet return (DDI_FAILURE); 10801865Sdilpreet 10811865Sdilpreet for (; repcount; repcount--) { 10821865Sdilpreet if (hp->ahi_acc_attr == DDI_ACCATTR_CONFIG_SPACE) { 10831865Sdilpreet switch (size) { 10841865Sdilpreet case sizeof (uint8_t): 10851865Sdilpreet *(uint8_t *)host_addr = pci_config_rd8(hp, 10861865Sdilpreet (uint8_t *)dev_addr); 10871865Sdilpreet break; 10881865Sdilpreet case sizeof (uint16_t): 10891865Sdilpreet *(uint16_t *)host_addr = pci_config_rd16(hp, 10901865Sdilpreet (uint16_t *)dev_addr); 10911865Sdilpreet break; 10921865Sdilpreet case sizeof (uint32_t): 10931865Sdilpreet *(uint32_t *)host_addr = pci_config_rd32(hp, 10941865Sdilpreet (uint32_t *)dev_addr); 10951865Sdilpreet break; 10961865Sdilpreet case sizeof (uint64_t): 10971865Sdilpreet *(uint64_t *)host_addr = pci_config_rd64(hp, 10981865Sdilpreet (uint64_t *)dev_addr); 10991865Sdilpreet break; 11001865Sdilpreet default: 11011865Sdilpreet err = DDI_FAILURE; 11021865Sdilpreet break; 11031865Sdilpreet } 11041865Sdilpreet } else if (hp->ahi_acc_attr & DDI_ACCATTR_IO_SPACE) { 11051865Sdilpreet if (hdlp->ah_acc.devacc_attr_endian_flags == 11061865Sdilpreet DDI_STRUCTURE_BE_ACC) { 11071865Sdilpreet switch (size) { 11081865Sdilpreet case sizeof (uint8_t): 11091865Sdilpreet *(uint8_t *)host_addr = 11101865Sdilpreet i_ddi_io_get8(hp, 11111865Sdilpreet (uint8_t *)dev_addr); 11121865Sdilpreet break; 11131865Sdilpreet case sizeof (uint16_t): 11141865Sdilpreet *(uint16_t *)host_addr = 11151865Sdilpreet i_ddi_io_swap_get16(hp, 11161865Sdilpreet (uint16_t *)dev_addr); 11171865Sdilpreet break; 11181865Sdilpreet case sizeof (uint32_t): 11191865Sdilpreet *(uint32_t *)host_addr = 11201865Sdilpreet i_ddi_io_swap_get32(hp, 11211865Sdilpreet (uint32_t *)dev_addr); 11221865Sdilpreet break; 11231865Sdilpreet /* 11241865Sdilpreet * note the 64-bit case is a dummy 11251865Sdilpreet * function - so no need to swap 11261865Sdilpreet */ 11271865Sdilpreet case sizeof (uint64_t): 11281865Sdilpreet *(uint64_t *)host_addr = 11291865Sdilpreet i_ddi_io_get64(hp, 11301865Sdilpreet (uint64_t *)dev_addr); 11311865Sdilpreet break; 11321865Sdilpreet default: 11331865Sdilpreet err = DDI_FAILURE; 11341865Sdilpreet break; 11351865Sdilpreet } 11361865Sdilpreet } else { 11371865Sdilpreet switch (size) { 11381865Sdilpreet case sizeof (uint8_t): 11391865Sdilpreet *(uint8_t *)host_addr = 11401865Sdilpreet i_ddi_io_get8(hp, 11411865Sdilpreet (uint8_t *)dev_addr); 11421865Sdilpreet break; 11431865Sdilpreet case sizeof (uint16_t): 11441865Sdilpreet *(uint16_t *)host_addr = 11451865Sdilpreet i_ddi_io_get16(hp, 11461865Sdilpreet (uint16_t *)dev_addr); 11471865Sdilpreet break; 11481865Sdilpreet case sizeof (uint32_t): 11491865Sdilpreet *(uint32_t *)host_addr = 11501865Sdilpreet i_ddi_io_get32(hp, 11511865Sdilpreet (uint32_t *)dev_addr); 11521865Sdilpreet break; 11531865Sdilpreet case sizeof (uint64_t): 11541865Sdilpreet *(uint64_t *)host_addr = 11551865Sdilpreet i_ddi_io_get64(hp, 11561865Sdilpreet (uint64_t *)dev_addr); 11571865Sdilpreet break; 11581865Sdilpreet default: 11591865Sdilpreet err = DDI_FAILURE; 11601865Sdilpreet break; 11611865Sdilpreet } 11621865Sdilpreet } 11631865Sdilpreet } else { 11641865Sdilpreet if (hdlp->ah_acc.devacc_attr_endian_flags == 11651865Sdilpreet DDI_STRUCTURE_BE_ACC) { 11661865Sdilpreet switch (in_args->size) { 11671865Sdilpreet case sizeof (uint8_t): 11681865Sdilpreet *(uint8_t *)host_addr = 11691865Sdilpreet *(uint8_t *)dev_addr; 11701865Sdilpreet break; 11711865Sdilpreet case sizeof (uint16_t): 11721865Sdilpreet *(uint16_t *)host_addr = 11731865Sdilpreet ddi_swap16(*(uint16_t *)dev_addr); 11741865Sdilpreet break; 11751865Sdilpreet case sizeof (uint32_t): 11761865Sdilpreet *(uint32_t *)host_addr = 11771865Sdilpreet ddi_swap32(*(uint32_t *)dev_addr); 11781865Sdilpreet break; 11791865Sdilpreet case sizeof (uint64_t): 11801865Sdilpreet *(uint64_t *)host_addr = 11811865Sdilpreet ddi_swap64(*(uint64_t *)dev_addr); 11821865Sdilpreet break; 11831865Sdilpreet default: 11841865Sdilpreet err = DDI_FAILURE; 11851865Sdilpreet break; 11861865Sdilpreet } 11871865Sdilpreet } else { 11881865Sdilpreet switch (in_args->size) { 11891865Sdilpreet case sizeof (uint8_t): 11901865Sdilpreet *(uint8_t *)host_addr = 11911865Sdilpreet *(uint8_t *)dev_addr; 11921865Sdilpreet break; 11931865Sdilpreet case sizeof (uint16_t): 11941865Sdilpreet *(uint16_t *)host_addr = 11951865Sdilpreet *(uint16_t *)dev_addr; 11961865Sdilpreet break; 11971865Sdilpreet case sizeof (uint32_t): 11981865Sdilpreet *(uint32_t *)host_addr = 11991865Sdilpreet *(uint32_t *)dev_addr; 12001865Sdilpreet break; 12011865Sdilpreet case sizeof (uint64_t): 12021865Sdilpreet *(uint64_t *)host_addr = 12031865Sdilpreet *(uint64_t *)dev_addr; 12041865Sdilpreet break; 12051865Sdilpreet default: 12061865Sdilpreet err = DDI_FAILURE; 12071865Sdilpreet break; 12081865Sdilpreet } 12091865Sdilpreet } 12101865Sdilpreet } 12111865Sdilpreet host_addr += size; 12121865Sdilpreet if (flags == DDI_DEV_AUTOINCR) 12131865Sdilpreet dev_addr += size; 12141865Sdilpreet } 12151865Sdilpreet return (err); 12161865Sdilpreet } 12171865Sdilpreet 12181865Sdilpreet /*ARGSUSED*/ 12191865Sdilpreet int 12201865Sdilpreet pci_common_peekpoke(dev_info_t *dip, dev_info_t *rdip, 12211865Sdilpreet ddi_ctl_enum_t ctlop, void *arg, void *result) 12221865Sdilpreet { 12231865Sdilpreet if (ctlop == DDI_CTLOPS_PEEK) 12241865Sdilpreet return (pci_common_ctlops_peek((peekpoke_ctlops_t *)arg)); 12251865Sdilpreet else 12261865Sdilpreet return (pci_common_ctlops_poke((peekpoke_ctlops_t *)arg)); 12271865Sdilpreet } 12281865Sdilpreet 12291083Sanish /* 12301083Sanish * These are the get and put functions to be shared with drivers. The 12311083Sanish * mutex locking is done inside the functions referenced, rather than 12321083Sanish * here, and is thus shared across PCI child drivers and any other 12331083Sanish * consumers of PCI config space (such as the ACPI subsystem). 12341083Sanish * 12351083Sanish * The configuration space addresses come in as pointers. This is fine on 12361083Sanish * a 32-bit system, where the VM space and configuration space are the same 12371083Sanish * size. It's not such a good idea on a 64-bit system, where memory 12381083Sanish * addresses are twice as large as configuration space addresses. At some 12391083Sanish * point in the call tree we need to take a stand and say "you are 32-bit 12401083Sanish * from this time forth", and this seems like a nice self-contained place. 12411083Sanish */ 12421083Sanish 12431083Sanish uint8_t 12441083Sanish pci_config_rd8(ddi_acc_impl_t *hdlp, uint8_t *addr) 12451083Sanish { 12461083Sanish pci_acc_cfblk_t *cfp; 12471083Sanish uint8_t rval; 12481083Sanish int reg; 12491083Sanish 12501083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 12511083Sanish 12521083Sanish reg = (int)(uintptr_t)addr; 12531083Sanish 12541083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 12551083Sanish 12561083Sanish rval = (*pci_getb_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum, 12571083Sanish reg); 12581083Sanish 12591083Sanish return (rval); 12601083Sanish } 12611083Sanish 12621083Sanish void 12631083Sanish pci_config_rep_rd8(ddi_acc_impl_t *hdlp, uint8_t *host_addr, 12641083Sanish uint8_t *dev_addr, size_t repcount, uint_t flags) 12651083Sanish { 12661083Sanish uint8_t *h, *d; 12671083Sanish 12681083Sanish h = host_addr; 12691083Sanish d = dev_addr; 12701083Sanish 12711083Sanish if (flags == DDI_DEV_AUTOINCR) 12721083Sanish for (; repcount; repcount--) 12731083Sanish *h++ = pci_config_rd8(hdlp, d++); 12741083Sanish else 12751083Sanish for (; repcount; repcount--) 12761083Sanish *h++ = pci_config_rd8(hdlp, d); 12771083Sanish } 12781083Sanish 12791083Sanish uint16_t 12801083Sanish pci_config_rd16(ddi_acc_impl_t *hdlp, uint16_t *addr) 12811083Sanish { 12821083Sanish pci_acc_cfblk_t *cfp; 12831083Sanish uint16_t rval; 12841083Sanish int reg; 12851083Sanish 12861083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 12871083Sanish 12881083Sanish reg = (int)(uintptr_t)addr; 12891083Sanish 12901083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 12911083Sanish 12921083Sanish rval = (*pci_getw_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum, 12931083Sanish reg); 12941083Sanish 12951083Sanish return (rval); 12961083Sanish } 12971083Sanish 12981083Sanish void 12991083Sanish pci_config_rep_rd16(ddi_acc_impl_t *hdlp, uint16_t *host_addr, 13001083Sanish uint16_t *dev_addr, size_t repcount, uint_t flags) 13011083Sanish { 13021083Sanish uint16_t *h, *d; 13031083Sanish 13041083Sanish h = host_addr; 13051083Sanish d = dev_addr; 13061083Sanish 13071083Sanish if (flags == DDI_DEV_AUTOINCR) 13081083Sanish for (; repcount; repcount--) 13091083Sanish *h++ = pci_config_rd16(hdlp, d++); 13101083Sanish else 13111083Sanish for (; repcount; repcount--) 13121083Sanish *h++ = pci_config_rd16(hdlp, d); 13131083Sanish } 13141083Sanish 13151083Sanish uint32_t 13161083Sanish pci_config_rd32(ddi_acc_impl_t *hdlp, uint32_t *addr) 13171083Sanish { 13181083Sanish pci_acc_cfblk_t *cfp; 13191083Sanish uint32_t rval; 13201083Sanish int reg; 13211083Sanish 13221083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 13231083Sanish 13241083Sanish reg = (int)(uintptr_t)addr; 13251083Sanish 13261083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 13271083Sanish 13281083Sanish rval = (*pci_getl_func)(cfp->c_busnum, cfp->c_devnum, 13291083Sanish cfp->c_funcnum, reg); 13301083Sanish 13311083Sanish return (rval); 13321083Sanish } 13331083Sanish 13341083Sanish void 13351083Sanish pci_config_rep_rd32(ddi_acc_impl_t *hdlp, uint32_t *host_addr, 13361083Sanish uint32_t *dev_addr, size_t repcount, uint_t flags) 13371083Sanish { 13381083Sanish uint32_t *h, *d; 13391083Sanish 13401083Sanish h = host_addr; 13411083Sanish d = dev_addr; 13421083Sanish 13431083Sanish if (flags == DDI_DEV_AUTOINCR) 13441083Sanish for (; repcount; repcount--) 13451083Sanish *h++ = pci_config_rd32(hdlp, d++); 13461083Sanish else 13471083Sanish for (; repcount; repcount--) 13481083Sanish *h++ = pci_config_rd32(hdlp, d); 13491083Sanish } 13501083Sanish 13511083Sanish 13521083Sanish void 13531083Sanish pci_config_wr8(ddi_acc_impl_t *hdlp, uint8_t *addr, uint8_t value) 13541083Sanish { 13551083Sanish pci_acc_cfblk_t *cfp; 13561083Sanish int reg; 13571083Sanish 13581083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 13591083Sanish 13601083Sanish reg = (int)(uintptr_t)addr; 13611083Sanish 13621083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 13631083Sanish 13641083Sanish (*pci_putb_func)(cfp->c_busnum, cfp->c_devnum, 13651083Sanish cfp->c_funcnum, reg, value); 13661083Sanish } 13671083Sanish 13681083Sanish void 13691083Sanish pci_config_rep_wr8(ddi_acc_impl_t *hdlp, uint8_t *host_addr, 13701083Sanish uint8_t *dev_addr, size_t repcount, uint_t flags) 13711083Sanish { 13721083Sanish uint8_t *h, *d; 13731083Sanish 13741083Sanish h = host_addr; 13751083Sanish d = dev_addr; 13761083Sanish 13771083Sanish if (flags == DDI_DEV_AUTOINCR) 13781083Sanish for (; repcount; repcount--) 13791083Sanish pci_config_wr8(hdlp, d++, *h++); 13801083Sanish else 13811083Sanish for (; repcount; repcount--) 13821083Sanish pci_config_wr8(hdlp, d, *h++); 13831083Sanish } 13841083Sanish 13851083Sanish void 13861083Sanish pci_config_wr16(ddi_acc_impl_t *hdlp, uint16_t *addr, uint16_t value) 13871083Sanish { 13881083Sanish pci_acc_cfblk_t *cfp; 13891083Sanish int reg; 13901083Sanish 13911083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 13921083Sanish 13931083Sanish reg = (int)(uintptr_t)addr; 13941083Sanish 13951083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 13961083Sanish 13971083Sanish (*pci_putw_func)(cfp->c_busnum, cfp->c_devnum, 13981083Sanish cfp->c_funcnum, reg, value); 13991083Sanish } 14001083Sanish 14011083Sanish void 14021083Sanish pci_config_rep_wr16(ddi_acc_impl_t *hdlp, uint16_t *host_addr, 14031083Sanish uint16_t *dev_addr, size_t repcount, uint_t flags) 14041083Sanish { 14051083Sanish uint16_t *h, *d; 14061083Sanish 14071083Sanish h = host_addr; 14081083Sanish d = dev_addr; 14091083Sanish 14101083Sanish if (flags == DDI_DEV_AUTOINCR) 14111083Sanish for (; repcount; repcount--) 14121083Sanish pci_config_wr16(hdlp, d++, *h++); 14131083Sanish else 14141083Sanish for (; repcount; repcount--) 14151083Sanish pci_config_wr16(hdlp, d, *h++); 14161083Sanish } 14171083Sanish 14181083Sanish void 14191083Sanish pci_config_wr32(ddi_acc_impl_t *hdlp, uint32_t *addr, uint32_t value) 14201083Sanish { 14211083Sanish pci_acc_cfblk_t *cfp; 14221083Sanish int reg; 14231083Sanish 14241083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 14251083Sanish 14261083Sanish reg = (int)(uintptr_t)addr; 14271083Sanish 14281083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 14291083Sanish 14301083Sanish (*pci_putl_func)(cfp->c_busnum, cfp->c_devnum, 14311083Sanish cfp->c_funcnum, reg, value); 14321083Sanish } 14331083Sanish 14341083Sanish void 14351083Sanish pci_config_rep_wr32(ddi_acc_impl_t *hdlp, uint32_t *host_addr, 14361083Sanish uint32_t *dev_addr, size_t repcount, uint_t flags) 14371083Sanish { 14381083Sanish uint32_t *h, *d; 14391083Sanish 14401083Sanish h = host_addr; 14411083Sanish d = dev_addr; 14421083Sanish 14431083Sanish if (flags == DDI_DEV_AUTOINCR) 14441083Sanish for (; repcount; repcount--) 14451083Sanish pci_config_wr32(hdlp, d++, *h++); 14461083Sanish else 14471083Sanish for (; repcount; repcount--) 14481083Sanish pci_config_wr32(hdlp, d, *h++); 14491083Sanish } 14501083Sanish 14511083Sanish uint64_t 14521083Sanish pci_config_rd64(ddi_acc_impl_t *hdlp, uint64_t *addr) 14531083Sanish { 14541083Sanish uint32_t lw_val; 14551083Sanish uint32_t hi_val; 14561083Sanish uint32_t *dp; 14571083Sanish uint64_t val; 14581083Sanish 14591083Sanish dp = (uint32_t *)addr; 14601083Sanish lw_val = pci_config_rd32(hdlp, dp); 14611083Sanish dp++; 14621083Sanish hi_val = pci_config_rd32(hdlp, dp); 14631083Sanish val = ((uint64_t)hi_val << 32) | lw_val; 14641083Sanish return (val); 14651083Sanish } 14661083Sanish 14671083Sanish void 14681083Sanish pci_config_wr64(ddi_acc_impl_t *hdlp, uint64_t *addr, uint64_t value) 14691083Sanish { 14701083Sanish uint32_t lw_val; 14711083Sanish uint32_t hi_val; 14721083Sanish uint32_t *dp; 14731083Sanish 14741083Sanish dp = (uint32_t *)addr; 14751083Sanish lw_val = (uint32_t)(value & 0xffffffff); 14761083Sanish hi_val = (uint32_t)(value >> 32); 14771083Sanish pci_config_wr32(hdlp, dp, lw_val); 14781083Sanish dp++; 14791083Sanish pci_config_wr32(hdlp, dp, hi_val); 14801083Sanish } 14811083Sanish 14821083Sanish void 14831083Sanish pci_config_rep_rd64(ddi_acc_impl_t *hdlp, uint64_t *host_addr, 14841083Sanish uint64_t *dev_addr, size_t repcount, uint_t flags) 14851083Sanish { 14861083Sanish if (flags == DDI_DEV_AUTOINCR) { 14871083Sanish for (; repcount; repcount--) 14881083Sanish *host_addr++ = pci_config_rd64(hdlp, dev_addr++); 14891083Sanish } else { 14901083Sanish for (; repcount; repcount--) 14911083Sanish *host_addr++ = pci_config_rd64(hdlp, dev_addr); 14921083Sanish } 14931083Sanish } 14941083Sanish 14951083Sanish void 14961083Sanish pci_config_rep_wr64(ddi_acc_impl_t *hdlp, uint64_t *host_addr, 14971083Sanish uint64_t *dev_addr, size_t repcount, uint_t flags) 14981083Sanish { 14991083Sanish if (flags == DDI_DEV_AUTOINCR) { 15001083Sanish for (; repcount; repcount--) 15011083Sanish pci_config_wr64(hdlp, host_addr++, *dev_addr++); 15021083Sanish } else { 15031083Sanish for (; repcount; repcount--) 15041083Sanish pci_config_wr64(hdlp, host_addr++, *dev_addr); 15051083Sanish } 15061083Sanish } 15071083Sanish 15081083Sanish 15091083Sanish /* 15101083Sanish * Enable Legacy PCI config space access for the following four north bridges 15111083Sanish * Host bridge: AMD HyperTransport Technology Configuration 15121083Sanish * Host bridge: AMD Address Map 15131083Sanish * Host bridge: AMD DRAM Controller 15141083Sanish * Host bridge: AMD Miscellaneous Control 15151083Sanish */ 15161083Sanish int 15171083Sanish is_amd_northbridge(dev_info_t *dip) 15181083Sanish { 15191083Sanish int vendor_id, device_id; 15201083Sanish 15211083Sanish vendor_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 15221083Sanish "vendor-id", -1); 15231083Sanish device_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 15241083Sanish "device-id", -1); 15251083Sanish 15261083Sanish if (IS_AMD_NTBRIDGE(vendor_id, device_id)) 15271083Sanish return (0); 15281083Sanish 15291083Sanish return (1); 15301083Sanish } 1531