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 /* 23*3446Smrj * 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> 48*3446Smrj #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> 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; 2402580Sanish case DDI_INTROP_NAVAIL: 241881Sjohnny case DDI_INTROP_NINTRS: 2422580Sanish if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) { 2432580Sanish if (pci_msi_get_nintrs(hdlp->ih_dip, hdlp->ih_type, 2442580Sanish result) != DDI_SUCCESS) 2452580Sanish return (DDI_FAILURE); 2462580Sanish } else { 2472580Sanish *(int *)result = i_ddi_get_intx_nintrs(hdlp->ih_dip); 2482580Sanish if (*(int *)result == 0) 2492580Sanish return (DDI_FAILURE); 2502580Sanish } 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 2993112Sanish if (*(int *)result == 0) 3003112Sanish return (DDI_INTR_NOTFOUND); 3013112Sanish 302881Sjohnny /* verify behavior flag and take appropriate action */ 303881Sjohnny if ((behavior == DDI_INTR_ALLOC_STRICT) && 304881Sjohnny (*(int *)result < hdlp->ih_scratch1)) { 305881Sjohnny DDI_INTR_NEXDBG((CE_CONT, 306881Sjohnny "pci_common_intr_ops: behavior %x, " 307881Sjohnny "couldn't get enough intrs\n", behavior)); 308881Sjohnny hdlp->ih_scratch1 = *(int *)result; 309881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 310881Sjohnny PSM_INTR_OP_FREE_VECTORS, NULL); 311881Sjohnny return (DDI_EAGAIN); 312881Sjohnny } 313881Sjohnny 314881Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) { 315881Sjohnny if (!(msix_p = i_ddi_get_msix(hdlp->ih_dip))) { 316881Sjohnny msix_p = pci_msix_init(hdlp->ih_dip); 317881Sjohnny if (msix_p) 318881Sjohnny i_ddi_set_msix(hdlp->ih_dip, 319881Sjohnny msix_p); 320881Sjohnny } 321881Sjohnny } 322881Sjohnny 323881Sjohnny if (pciepci) { 324881Sjohnny /* update priority in ispec */ 325881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, 326881Sjohnny (int)hdlp->ih_inum); 327881Sjohnny ispec = (struct intrspec *)isp; 328881Sjohnny if (ispec) 329881Sjohnny ispec->intrspec_pri = hdlp->ih_pri; 330881Sjohnny ++pcie_pci_intr_pri_counter; 331881Sjohnny } 332881Sjohnny 333881Sjohnny } else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) { 334881Sjohnny /* Figure out if this device supports MASKING */ 335881Sjohnny pci_rval = pci_intx_get_cap(rdip, &pci_status); 336881Sjohnny if (pci_rval == DDI_SUCCESS && pci_status) 337881Sjohnny hdlp->ih_cap |= pci_status; 338881Sjohnny *(int *)result = 1; /* DDI_INTR_TYPE_FIXED */ 339881Sjohnny } else 340881Sjohnny return (DDI_FAILURE); 341881Sjohnny break; 342881Sjohnny case DDI_INTROP_FREE: 343881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && 344881Sjohnny (psm_intr_ops != NULL)) { 3452018Sanish if (i_ddi_intr_get_current_nintrs(hdlp->ih_dip) - 1 == 3462018Sanish 0) { 3471997Sanish if (handle = i_ddi_get_pci_config_handle( 3481997Sanish rdip)) { 3491997Sanish (void) pci_config_teardown(&handle); 3501997Sanish i_ddi_set_pci_config_handle(rdip, NULL); 3511997Sanish } 3521997Sanish if (cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip)) 3531997Sanish i_ddi_set_msi_msix_cap_ptr(rdip, 0); 3541997Sanish } 3551997Sanish 356881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 357881Sjohnny PSM_INTR_OP_FREE_VECTORS, NULL); 358881Sjohnny 359881Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) { 360881Sjohnny msix_p = i_ddi_get_msix(hdlp->ih_dip); 361881Sjohnny if (msix_p && 3622018Sanish (i_ddi_intr_get_current_nintrs( 3632018Sanish hdlp->ih_dip) - 1) == 0) { 364881Sjohnny pci_msix_fini(msix_p); 365881Sjohnny i_ddi_set_msix(hdlp->ih_dip, NULL); 366881Sjohnny } 367881Sjohnny } 368881Sjohnny } 369881Sjohnny break; 370881Sjohnny case DDI_INTROP_GETPRI: 371881Sjohnny /* Get the priority */ 372881Sjohnny if (pci_get_priority(rdip, hdlp, &priority) != DDI_SUCCESS) 373881Sjohnny return (DDI_FAILURE); 374881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 375881Sjohnny "priority = 0x%x\n", priority)); 376881Sjohnny *(int *)result = priority; 377881Sjohnny break; 378881Sjohnny case DDI_INTROP_SETPRI: 379881Sjohnny /* Validate the interrupt priority passed */ 380881Sjohnny if (*(int *)result > LOCK_LEVEL) 381881Sjohnny return (DDI_FAILURE); 382881Sjohnny 383881Sjohnny /* Ensure that PSM is all initialized */ 384881Sjohnny if (psm_intr_ops == NULL) 385881Sjohnny return (DDI_FAILURE); 386881Sjohnny 387881Sjohnny /* Change the priority */ 388881Sjohnny if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_PRI, result) == 389881Sjohnny PSM_FAILURE) 390881Sjohnny return (DDI_FAILURE); 391881Sjohnny 392881Sjohnny /* update ispec */ 393881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 394881Sjohnny ispec = (struct intrspec *)isp; 395881Sjohnny if (ispec) 396881Sjohnny ispec->intrspec_pri = *(int *)result; 397881Sjohnny break; 398881Sjohnny case DDI_INTROP_ADDISR: 399881Sjohnny /* update ispec */ 400881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 401881Sjohnny ispec = (struct intrspec *)isp; 4021087Sschwartz if (ispec) { 403881Sjohnny ispec->intrspec_func = hdlp->ih_cb_func; 4041087Sschwartz ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 4051087Sschwartz pci_kstat_create(&ihdl_plat_datap->ip_ksp, pdip, hdlp); 4061087Sschwartz } 407881Sjohnny break; 408881Sjohnny case DDI_INTROP_REMISR: 409881Sjohnny /* Get the interrupt structure pointer */ 410881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 411881Sjohnny ispec = (struct intrspec *)isp; 4121087Sschwartz if (ispec) { 413881Sjohnny ispec->intrspec_func = (uint_t (*)()) 0; 4141087Sschwartz ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 4151087Sschwartz if (ihdl_plat_datap->ip_ksp != NULL) 4161087Sschwartz pci_kstat_delete(ihdl_plat_datap->ip_ksp); 4171087Sschwartz } 418881Sjohnny break; 419881Sjohnny case DDI_INTROP_GETCAP: 420881Sjohnny /* 421881Sjohnny * First check the config space and/or 422881Sjohnny * MSI capability register(s) 423881Sjohnny */ 424881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 425881Sjohnny pci_rval = pci_msi_get_cap(rdip, hdlp->ih_type, 426881Sjohnny &pci_status); 427881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 428881Sjohnny pci_rval = pci_intx_get_cap(rdip, &pci_status); 429881Sjohnny 430881Sjohnny /* next check with pcplusmp */ 431881Sjohnny if (psm_intr_ops != NULL) 432881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 433881Sjohnny PSM_INTR_OP_GET_CAP, &psm_status); 434881Sjohnny 435881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned psm_rval = %x, " 436881Sjohnny "psm_status = %x, pci_rval = %x, pci_status = %x\n", 437881Sjohnny psm_rval, psm_status, pci_rval, pci_status)); 438881Sjohnny 439881Sjohnny if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) { 440881Sjohnny *(int *)result = 0; 441881Sjohnny return (DDI_FAILURE); 442881Sjohnny } 443881Sjohnny 444881Sjohnny if (psm_rval == PSM_SUCCESS) 445881Sjohnny *(int *)result = psm_status; 446881Sjohnny 447881Sjohnny if (pci_rval == DDI_SUCCESS) 448881Sjohnny *(int *)result |= pci_status; 449881Sjohnny 450881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned = %x\n", 451881Sjohnny *(int *)result)); 452881Sjohnny break; 453881Sjohnny case DDI_INTROP_SETCAP: 454881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 455881Sjohnny "SETCAP cap=0x%x\n", *(int *)result)); 456881Sjohnny if (psm_intr_ops == NULL) 457881Sjohnny return (DDI_FAILURE); 458881Sjohnny 459881Sjohnny if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) { 460881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "GETCAP: psm_intr_ops" 461881Sjohnny " returned failure\n")); 462881Sjohnny return (DDI_FAILURE); 463881Sjohnny } 464881Sjohnny break; 465881Sjohnny case DDI_INTROP_ENABLE: 466881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE\n")); 467881Sjohnny if (psm_intr_ops == NULL) 468881Sjohnny return (DDI_FAILURE); 469881Sjohnny 470881Sjohnny if (pci_enable_intr(pdip, rdip, hdlp, hdlp->ih_inum) != 471881Sjohnny DDI_SUCCESS) 472881Sjohnny return (DDI_FAILURE); 473881Sjohnny 474881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE " 475881Sjohnny "vector=0x%x\n", hdlp->ih_vector)); 476881Sjohnny break; 477881Sjohnny case DDI_INTROP_DISABLE: 478881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE\n")); 479881Sjohnny if (psm_intr_ops == NULL) 480881Sjohnny return (DDI_FAILURE); 481881Sjohnny 482881Sjohnny pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum); 483881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE " 484881Sjohnny "vector = %x\n", hdlp->ih_vector)); 485881Sjohnny break; 486881Sjohnny case DDI_INTROP_BLOCKENABLE: 487881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 488881Sjohnny "BLOCKENABLE\n")); 489881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_MSI) { 490881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: not MSI\n")); 491881Sjohnny return (DDI_FAILURE); 492881Sjohnny } 493881Sjohnny 494881Sjohnny /* Check if psm_intr_ops is NULL? */ 495881Sjohnny if (psm_intr_ops == NULL) 496881Sjohnny return (DDI_FAILURE); 497881Sjohnny 4981542Sjohnny count = hdlp->ih_scratch1; 4991542Sjohnny h_array = (ddi_intr_handle_t *)hdlp->ih_scratch2; 5001542Sjohnny for (i = 0; i < count; i++) { 5011542Sjohnny hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 502881Sjohnny if (pci_enable_intr(pdip, rdip, hdlp, 5031542Sjohnny hdlp->ih_inum) != DDI_SUCCESS) { 504881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: " 505881Sjohnny "pci_enable_intr failed for %d\n", i)); 5061542Sjohnny for (j = 0; j < i; j++) { 5071542Sjohnny hdlp = (ddi_intr_handle_impl_t *)h_array[j]; 5081542Sjohnny pci_disable_intr(pdip, rdip, hdlp, 5091542Sjohnny hdlp->ih_inum); 5101542Sjohnny } 511881Sjohnny return (DDI_FAILURE); 512881Sjohnny } 513881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 5141542Sjohnny "BLOCKENABLE inum %x done\n", hdlp->ih_inum)); 515881Sjohnny } 516881Sjohnny break; 517881Sjohnny case DDI_INTROP_BLOCKDISABLE: 518881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 519881Sjohnny "BLOCKDISABLE\n")); 520881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_MSI) { 521881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKDISABLE: not MSI\n")); 522881Sjohnny return (DDI_FAILURE); 523881Sjohnny } 524881Sjohnny 525881Sjohnny /* Check if psm_intr_ops is present */ 526881Sjohnny if (psm_intr_ops == NULL) 527881Sjohnny return (DDI_FAILURE); 528881Sjohnny 5291542Sjohnny count = hdlp->ih_scratch1; 5301542Sjohnny h_array = (ddi_intr_handle_t *)hdlp->ih_scratch2; 5311542Sjohnny for (i = 0; i < count; i++) { 5321542Sjohnny hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 5331542Sjohnny pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum); 534881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 5351542Sjohnny "BLOCKDISABLE inum %x done\n", hdlp->ih_inum)); 536881Sjohnny } 537881Sjohnny break; 538881Sjohnny case DDI_INTROP_SETMASK: 539881Sjohnny case DDI_INTROP_CLRMASK: 540881Sjohnny /* 541881Sjohnny * First handle in the config space 542881Sjohnny */ 543881Sjohnny if (intr_op == DDI_INTROP_SETMASK) { 544881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 545881Sjohnny pci_status = pci_msi_set_mask(rdip, 546881Sjohnny hdlp->ih_type, hdlp->ih_inum); 547881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 548881Sjohnny pci_status = pci_intx_set_mask(rdip); 549881Sjohnny } else { 550881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 551881Sjohnny pci_status = pci_msi_clr_mask(rdip, 552881Sjohnny hdlp->ih_type, hdlp->ih_inum); 553881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 554881Sjohnny pci_status = pci_intx_clr_mask(rdip); 555881Sjohnny } 556881Sjohnny 557881Sjohnny /* For MSI/X; no need to check with pcplusmp */ 558881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 559881Sjohnny return (pci_status); 560881Sjohnny 561881Sjohnny /* For fixed interrupts only: handle config space first */ 562881Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_FIXED && 563881Sjohnny pci_status == DDI_SUCCESS) 564881Sjohnny break; 565881Sjohnny 566881Sjohnny /* For fixed interrupts only: confer with pcplusmp next */ 567881Sjohnny if (psm_intr_ops != NULL) { 568881Sjohnny /* If interrupt is shared; do nothing */ 569881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 570881Sjohnny PSM_INTR_OP_GET_SHARED, &psm_status); 571881Sjohnny 572881Sjohnny if (psm_rval == PSM_FAILURE || psm_status == 1) 573881Sjohnny return (pci_status); 574881Sjohnny 575881Sjohnny /* Now, pcplusmp should try to set/clear the mask */ 576881Sjohnny if (intr_op == DDI_INTROP_SETMASK) 577881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 578881Sjohnny PSM_INTR_OP_SET_MASK, NULL); 579881Sjohnny else 580881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 581881Sjohnny PSM_INTR_OP_CLEAR_MASK, NULL); 582881Sjohnny } 583881Sjohnny return ((psm_rval == PSM_FAILURE) ? DDI_FAILURE : DDI_SUCCESS); 584881Sjohnny case DDI_INTROP_GETPENDING: 585881Sjohnny /* 586881Sjohnny * First check the config space and/or 587881Sjohnny * MSI capability register(s) 588881Sjohnny */ 589881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 590881Sjohnny pci_rval = pci_msi_get_pending(rdip, hdlp->ih_type, 591881Sjohnny hdlp->ih_inum, &pci_status); 592881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 593881Sjohnny pci_rval = pci_intx_get_pending(rdip, &pci_status); 594881Sjohnny 595881Sjohnny /* On failure; next try with pcplusmp */ 596881Sjohnny if (pci_rval != DDI_SUCCESS && psm_intr_ops != NULL) 597881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 598881Sjohnny PSM_INTR_OP_GET_PENDING, &psm_status); 599881Sjohnny 600881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned " 601881Sjohnny "psm_rval = %x, psm_status = %x, pci_rval = %x, " 602881Sjohnny "pci_status = %x\n", psm_rval, psm_status, pci_rval, 603881Sjohnny pci_status)); 604881Sjohnny if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) { 605881Sjohnny *(int *)result = 0; 606881Sjohnny return (DDI_FAILURE); 607881Sjohnny } 608881Sjohnny 609881Sjohnny if (psm_rval != PSM_FAILURE) 610881Sjohnny *(int *)result = psm_status; 611881Sjohnny else if (pci_rval != DDI_FAILURE) 612881Sjohnny *(int *)result = pci_status; 613881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned = %x\n", 614881Sjohnny *(int *)result)); 615881Sjohnny break; 616881Sjohnny default: 617881Sjohnny return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result)); 618881Sjohnny } 619881Sjohnny 620881Sjohnny return (DDI_SUCCESS); 621881Sjohnny } 622881Sjohnny 623916Sschwartz int 624916Sschwartz pci_get_intr_from_vecirq(apic_get_intr_t *intrinfo_p, 625916Sschwartz int vecirq, boolean_t is_irq) 626916Sschwartz { 627916Sschwartz ddi_intr_handle_impl_t get_info_ii_hdl; 628916Sschwartz 629916Sschwartz if (is_irq) 630916Sschwartz intrinfo_p->avgi_req_flags |= PSMGI_INTRBY_IRQ; 631916Sschwartz 632916Sschwartz /* 633916Sschwartz * For this locally-declared and used handle, ih_private will contain a 634916Sschwartz * pointer to apic_get_intr_t, not an ihdl_plat_t as used for 635916Sschwartz * global interrupt handling. 636916Sschwartz */ 637916Sschwartz get_info_ii_hdl.ih_private = intrinfo_p; 638916Sschwartz get_info_ii_hdl.ih_vector = (ushort_t)vecirq; 639916Sschwartz 640916Sschwartz if ((*psm_intr_ops)(NULL, &get_info_ii_hdl, 641916Sschwartz PSM_INTR_OP_GET_INTR, NULL) == PSM_FAILURE) 642916Sschwartz return (DDI_FAILURE); 643916Sschwartz 644916Sschwartz return (DDI_SUCCESS); 645916Sschwartz } 646916Sschwartz 647916Sschwartz 648916Sschwartz int 649916Sschwartz pci_get_cpu_from_vecirq(int vecirq, boolean_t is_irq) 650916Sschwartz { 651916Sschwartz int rval; 652916Sschwartz 653916Sschwartz apic_get_intr_t intrinfo; 654916Sschwartz intrinfo.avgi_req_flags = PSMGI_REQ_CPUID; 655916Sschwartz rval = pci_get_intr_from_vecirq(&intrinfo, vecirq, is_irq); 656916Sschwartz 657916Sschwartz if (rval == DDI_SUCCESS) 658916Sschwartz return (intrinfo.avgi_cpu_id); 659916Sschwartz else 660916Sschwartz return (-1); 661916Sschwartz } 662916Sschwartz 663881Sjohnny 664881Sjohnny static int 665881Sjohnny pci_enable_intr(dev_info_t *pdip, dev_info_t *rdip, 666881Sjohnny ddi_intr_handle_impl_t *hdlp, uint32_t inum) 667881Sjohnny { 668881Sjohnny struct intrspec *ispec; 669916Sschwartz int irq; 670916Sschwartz ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 671881Sjohnny 672881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: hdlp %p inum %x\n", 673881Sjohnny (void *)hdlp, inum)); 674881Sjohnny 675881Sjohnny /* Translate the interrupt if needed */ 676881Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum); 6772288Sanish if (ispec == NULL) 6782288Sanish return (DDI_FAILURE); 6792288Sanish if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 680881Sjohnny ispec->intrspec_vec = inum; 681916Sschwartz ihdl_plat_datap->ip_ispecp = ispec; 682881Sjohnny 683881Sjohnny /* translate the interrupt if needed */ 684916Sschwartz (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq); 685916Sschwartz DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: priority=%x irq=%x\n", 686916Sschwartz hdlp->ih_pri, irq)); 687881Sjohnny 688881Sjohnny /* Add the interrupt handler */ 689881Sjohnny if (!add_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, 690916Sschwartz DEVI(rdip)->devi_name, irq, hdlp->ih_cb_arg1, 691916Sschwartz hdlp->ih_cb_arg2, &ihdl_plat_datap->ip_ticks, rdip)) 692881Sjohnny return (DDI_FAILURE); 693881Sjohnny 694916Sschwartz /* Note this really is an irq. */ 695916Sschwartz hdlp->ih_vector = (ushort_t)irq; 696916Sschwartz 697881Sjohnny return (DDI_SUCCESS); 698881Sjohnny } 699881Sjohnny 700881Sjohnny 701881Sjohnny static void 702881Sjohnny pci_disable_intr(dev_info_t *pdip, dev_info_t *rdip, 703881Sjohnny ddi_intr_handle_impl_t *hdlp, uint32_t inum) 704881Sjohnny { 705916Sschwartz int irq; 706881Sjohnny struct intrspec *ispec; 707916Sschwartz ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 708881Sjohnny 709881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_disable_intr: \n")); 710881Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum); 7112288Sanish if (ispec == NULL) 7122288Sanish return; 7132288Sanish if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 714881Sjohnny ispec->intrspec_vec = inum; 715916Sschwartz ihdl_plat_datap->ip_ispecp = ispec; 716881Sjohnny 717881Sjohnny /* translate the interrupt if needed */ 718916Sschwartz (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq); 719881Sjohnny 720881Sjohnny /* Disable the interrupt handler */ 721916Sschwartz rem_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, irq); 722916Sschwartz ihdl_plat_datap->ip_ispecp = NULL; 723881Sjohnny } 724881Sjohnny 725881Sjohnny /* 726881Sjohnny * Miscellaneous library function 727881Sjohnny */ 728881Sjohnny int 729881Sjohnny pci_common_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp) 730881Sjohnny { 731881Sjohnny int i; 732881Sjohnny int number; 733881Sjohnny int assigned_addr_len; 734881Sjohnny uint_t phys_hi = pci_rp->pci_phys_hi; 735881Sjohnny pci_regspec_t *assigned_addr; 736881Sjohnny 737881Sjohnny if (((phys_hi & PCI_REG_ADDR_M) == PCI_ADDR_CONFIG) || 738881Sjohnny (phys_hi & PCI_RELOCAT_B)) 739881Sjohnny return (DDI_SUCCESS); 740881Sjohnny 741881Sjohnny /* 742881Sjohnny * the "reg" property specifies relocatable, get and interpret the 743881Sjohnny * "assigned-addresses" property. 744881Sjohnny */ 745881Sjohnny if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 746881Sjohnny "assigned-addresses", (int **)&assigned_addr, 747881Sjohnny (uint_t *)&assigned_addr_len) != DDI_PROP_SUCCESS) 748881Sjohnny return (DDI_FAILURE); 749881Sjohnny 750881Sjohnny /* 751881Sjohnny * Scan the "assigned-addresses" for one that matches the specified 752881Sjohnny * "reg" property entry. 753881Sjohnny */ 754881Sjohnny phys_hi &= PCI_CONF_ADDR_MASK; 755881Sjohnny number = assigned_addr_len / (sizeof (pci_regspec_t) / sizeof (int)); 756881Sjohnny for (i = 0; i < number; i++) { 757881Sjohnny if ((assigned_addr[i].pci_phys_hi & PCI_CONF_ADDR_MASK) == 758881Sjohnny phys_hi) { 759881Sjohnny pci_rp->pci_phys_mid = assigned_addr[i].pci_phys_mid; 760881Sjohnny pci_rp->pci_phys_low = assigned_addr[i].pci_phys_low; 761881Sjohnny ddi_prop_free(assigned_addr); 762881Sjohnny return (DDI_SUCCESS); 763881Sjohnny } 764881Sjohnny } 765881Sjohnny 766881Sjohnny ddi_prop_free(assigned_addr); 767881Sjohnny return (DDI_FAILURE); 768881Sjohnny } 769881Sjohnny 770881Sjohnny 771881Sjohnny /* 772881Sjohnny * For pci_tools 773881Sjohnny */ 774881Sjohnny 775881Sjohnny int 776881Sjohnny pci_common_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg, 777881Sjohnny int mode, cred_t *credp, int *rvalp) 778881Sjohnny { 779881Sjohnny int rv = ENOTTY; 780881Sjohnny 781881Sjohnny minor_t minor = getminor(dev); 782881Sjohnny 783881Sjohnny switch (PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(minor)) { 784881Sjohnny case PCI_TOOL_REG_MINOR_NUM: 785881Sjohnny 786881Sjohnny switch (cmd) { 787881Sjohnny case PCITOOL_DEVICE_SET_REG: 788881Sjohnny case PCITOOL_DEVICE_GET_REG: 789881Sjohnny 790881Sjohnny /* Require full privileges. */ 791881Sjohnny if (secpolicy_kmdb(credp)) 792881Sjohnny rv = EPERM; 793881Sjohnny else 794881Sjohnny rv = pcitool_dev_reg_ops(dip, (void *)arg, 795881Sjohnny cmd, mode); 796881Sjohnny break; 797881Sjohnny 798881Sjohnny case PCITOOL_NEXUS_SET_REG: 799881Sjohnny case PCITOOL_NEXUS_GET_REG: 800881Sjohnny 801881Sjohnny /* Require full privileges. */ 802881Sjohnny if (secpolicy_kmdb(credp)) 803881Sjohnny rv = EPERM; 804881Sjohnny else 805881Sjohnny rv = pcitool_bus_reg_ops(dip, (void *)arg, 806881Sjohnny cmd, mode); 807881Sjohnny break; 808881Sjohnny } 809881Sjohnny break; 810881Sjohnny 811881Sjohnny case PCI_TOOL_INTR_MINOR_NUM: 812881Sjohnny 813881Sjohnny switch (cmd) { 814881Sjohnny case PCITOOL_DEVICE_SET_INTR: 815881Sjohnny 816881Sjohnny /* Require PRIV_SYS_RES_CONFIG, same as psradm */ 817881Sjohnny if (secpolicy_ponline(credp)) { 818881Sjohnny rv = EPERM; 819881Sjohnny break; 820881Sjohnny } 821881Sjohnny 822881Sjohnny /*FALLTHRU*/ 823881Sjohnny /* These require no special privileges. */ 824881Sjohnny case PCITOOL_DEVICE_GET_INTR: 825881Sjohnny case PCITOOL_DEVICE_NUM_INTR: 826881Sjohnny rv = pcitool_intr_admn(dip, (void *)arg, cmd, mode); 827881Sjohnny break; 828881Sjohnny } 829881Sjohnny break; 830881Sjohnny 831881Sjohnny /* 832881Sjohnny * All non-PCItool ioctls go through here, including: 833881Sjohnny * devctl ioctls with minor number PCIHP_DEVCTL_MINOR and 834881Sjohnny * those for attachment points with where minor number is the 835881Sjohnny * device number. 836881Sjohnny */ 837881Sjohnny default: 838881Sjohnny rv = (pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, 839881Sjohnny credp, rvalp); 840881Sjohnny break; 841881Sjohnny } 842881Sjohnny 843881Sjohnny return (rv); 844881Sjohnny } 8451083Sanish 8461083Sanish 8471865Sdilpreet int 8481865Sdilpreet pci_common_ctlops_poke(peekpoke_ctlops_t *in_args) 8491865Sdilpreet { 8501865Sdilpreet size_t size = in_args->size; 8511865Sdilpreet uintptr_t dev_addr = in_args->dev_addr; 8521865Sdilpreet uintptr_t host_addr = in_args->host_addr; 8531865Sdilpreet ddi_acc_impl_t *hp = (ddi_acc_impl_t *)in_args->handle; 8541865Sdilpreet ddi_acc_hdl_t *hdlp = (ddi_acc_hdl_t *)in_args->handle; 8551865Sdilpreet size_t repcount = in_args->repcount; 8561865Sdilpreet uint_t flags = in_args->flags; 8571865Sdilpreet int err = DDI_SUCCESS; 8581865Sdilpreet 8591865Sdilpreet /* 8601865Sdilpreet * if no handle then this is a poke. We have to return failure here 8611865Sdilpreet * as we have no way of knowing whether this is a MEM or IO space access 8621865Sdilpreet */ 8631865Sdilpreet if (in_args->handle == NULL) 8641865Sdilpreet return (DDI_FAILURE); 8651865Sdilpreet 8661865Sdilpreet /* 8671865Sdilpreet * rest of this function is actually for cautious puts 8681865Sdilpreet */ 8691865Sdilpreet for (; repcount; repcount--) { 8701865Sdilpreet if (hp->ahi_acc_attr == DDI_ACCATTR_CONFIG_SPACE) { 8711865Sdilpreet switch (size) { 8721865Sdilpreet case sizeof (uint8_t): 8731865Sdilpreet pci_config_wr8(hp, (uint8_t *)dev_addr, 8741865Sdilpreet *(uint8_t *)host_addr); 8751865Sdilpreet break; 8761865Sdilpreet case sizeof (uint16_t): 8771865Sdilpreet pci_config_wr16(hp, (uint16_t *)dev_addr, 8781865Sdilpreet *(uint16_t *)host_addr); 8791865Sdilpreet break; 8801865Sdilpreet case sizeof (uint32_t): 8811865Sdilpreet pci_config_wr32(hp, (uint32_t *)dev_addr, 8821865Sdilpreet *(uint32_t *)host_addr); 8831865Sdilpreet break; 8841865Sdilpreet case sizeof (uint64_t): 8851865Sdilpreet pci_config_wr64(hp, (uint64_t *)dev_addr, 8861865Sdilpreet *(uint64_t *)host_addr); 8871865Sdilpreet break; 8881865Sdilpreet default: 8891865Sdilpreet err = DDI_FAILURE; 8901865Sdilpreet break; 8911865Sdilpreet } 8921865Sdilpreet } else if (hp->ahi_acc_attr & DDI_ACCATTR_IO_SPACE) { 8931865Sdilpreet if (hdlp->ah_acc.devacc_attr_endian_flags == 8941865Sdilpreet DDI_STRUCTURE_BE_ACC) { 8951865Sdilpreet switch (size) { 8961865Sdilpreet case sizeof (uint8_t): 8971865Sdilpreet i_ddi_io_put8(hp, 8981865Sdilpreet (uint8_t *)dev_addr, 8991865Sdilpreet *(uint8_t *)host_addr); 9001865Sdilpreet break; 9011865Sdilpreet case sizeof (uint16_t): 9021865Sdilpreet i_ddi_io_swap_put16(hp, 9031865Sdilpreet (uint16_t *)dev_addr, 9041865Sdilpreet *(uint16_t *)host_addr); 9051865Sdilpreet break; 9061865Sdilpreet case sizeof (uint32_t): 9071865Sdilpreet i_ddi_io_swap_put32(hp, 9081865Sdilpreet (uint32_t *)dev_addr, 9091865Sdilpreet *(uint32_t *)host_addr); 9101865Sdilpreet break; 9111865Sdilpreet /* 9121865Sdilpreet * note the 64-bit case is a dummy 9131865Sdilpreet * function - so no need to swap 9141865Sdilpreet */ 9151865Sdilpreet case sizeof (uint64_t): 9161865Sdilpreet i_ddi_io_put64(hp, 9171865Sdilpreet (uint64_t *)dev_addr, 9181865Sdilpreet *(uint64_t *)host_addr); 9191865Sdilpreet break; 9201865Sdilpreet default: 9211865Sdilpreet err = DDI_FAILURE; 9221865Sdilpreet break; 9231865Sdilpreet } 9241865Sdilpreet } else { 9251865Sdilpreet switch (size) { 9261865Sdilpreet case sizeof (uint8_t): 9271865Sdilpreet i_ddi_io_put8(hp, 9281865Sdilpreet (uint8_t *)dev_addr, 9291865Sdilpreet *(uint8_t *)host_addr); 9301865Sdilpreet break; 9311865Sdilpreet case sizeof (uint16_t): 9321865Sdilpreet i_ddi_io_put16(hp, 9331865Sdilpreet (uint16_t *)dev_addr, 9341865Sdilpreet *(uint16_t *)host_addr); 9351865Sdilpreet break; 9361865Sdilpreet case sizeof (uint32_t): 9371865Sdilpreet i_ddi_io_put32(hp, 9381865Sdilpreet (uint32_t *)dev_addr, 9391865Sdilpreet *(uint32_t *)host_addr); 9401865Sdilpreet break; 9411865Sdilpreet case sizeof (uint64_t): 9421865Sdilpreet i_ddi_io_put64(hp, 9431865Sdilpreet (uint64_t *)dev_addr, 9441865Sdilpreet *(uint64_t *)host_addr); 9451865Sdilpreet break; 9461865Sdilpreet default: 9471865Sdilpreet err = DDI_FAILURE; 9481865Sdilpreet break; 9491865Sdilpreet } 9501865Sdilpreet } 9511865Sdilpreet } else { 9521865Sdilpreet if (hdlp->ah_acc.devacc_attr_endian_flags == 9531865Sdilpreet DDI_STRUCTURE_BE_ACC) { 9541865Sdilpreet switch (size) { 9551865Sdilpreet case sizeof (uint8_t): 9561865Sdilpreet *(uint8_t *)dev_addr = 9571865Sdilpreet *(uint8_t *)host_addr; 9581865Sdilpreet break; 9591865Sdilpreet case sizeof (uint16_t): 9601865Sdilpreet *(uint16_t *)dev_addr = 9611865Sdilpreet ddi_swap16(*(uint16_t *)host_addr); 9621865Sdilpreet break; 9631865Sdilpreet case sizeof (uint32_t): 9641865Sdilpreet *(uint32_t *)dev_addr = 9651865Sdilpreet ddi_swap32(*(uint32_t *)host_addr); 9661865Sdilpreet break; 9671865Sdilpreet case sizeof (uint64_t): 9681865Sdilpreet *(uint64_t *)dev_addr = 9691865Sdilpreet ddi_swap64(*(uint64_t *)host_addr); 9701865Sdilpreet break; 9711865Sdilpreet default: 9721865Sdilpreet err = DDI_FAILURE; 9731865Sdilpreet break; 9741865Sdilpreet } 9751865Sdilpreet } else { 9761865Sdilpreet switch (size) { 9771865Sdilpreet case sizeof (uint8_t): 9781865Sdilpreet *(uint8_t *)dev_addr = 9791865Sdilpreet *(uint8_t *)host_addr; 9801865Sdilpreet break; 9811865Sdilpreet case sizeof (uint16_t): 9821865Sdilpreet *(uint16_t *)dev_addr = 9831865Sdilpreet *(uint16_t *)host_addr; 9841865Sdilpreet break; 9851865Sdilpreet case sizeof (uint32_t): 9861865Sdilpreet *(uint32_t *)dev_addr = 9871865Sdilpreet *(uint32_t *)host_addr; 9881865Sdilpreet break; 9891865Sdilpreet case sizeof (uint64_t): 9901865Sdilpreet *(uint64_t *)dev_addr = 9911865Sdilpreet *(uint64_t *)host_addr; 9921865Sdilpreet break; 9931865Sdilpreet default: 9941865Sdilpreet err = DDI_FAILURE; 9951865Sdilpreet break; 9961865Sdilpreet } 9971865Sdilpreet } 9981865Sdilpreet } 9991865Sdilpreet host_addr += size; 10001865Sdilpreet if (flags == DDI_DEV_AUTOINCR) 10011865Sdilpreet dev_addr += size; 10021865Sdilpreet } 10031865Sdilpreet return (err); 10041865Sdilpreet } 10051865Sdilpreet 10061865Sdilpreet 10071865Sdilpreet int 10081865Sdilpreet pci_fm_acc_setup(ddi_acc_hdl_t *hp, off_t offset, off_t len) 10091865Sdilpreet { 10101865Sdilpreet ddi_acc_impl_t *ap = (ddi_acc_impl_t *)hp->ah_platform_private; 10111865Sdilpreet 10121865Sdilpreet /* endian-ness check */ 10131865Sdilpreet if (hp->ah_acc.devacc_attr_endian_flags == DDI_STRUCTURE_BE_ACC) 10141865Sdilpreet return (DDI_FAILURE); 10151865Sdilpreet 10161865Sdilpreet /* 10171865Sdilpreet * range check 10181865Sdilpreet */ 10191865Sdilpreet if ((offset >= PCI_CONF_HDR_SIZE) || 10201865Sdilpreet (len > PCI_CONF_HDR_SIZE) || 10211865Sdilpreet (offset + len > PCI_CONF_HDR_SIZE)) 10221865Sdilpreet return (DDI_FAILURE); 10231865Sdilpreet 10241865Sdilpreet ap->ahi_acc_attr |= DDI_ACCATTR_CONFIG_SPACE; 10251865Sdilpreet /* 10261865Sdilpreet * always use cautious mechanism for config space gets 10271865Sdilpreet */ 10281865Sdilpreet ap->ahi_get8 = i_ddi_caut_get8; 10291865Sdilpreet ap->ahi_get16 = i_ddi_caut_get16; 10301865Sdilpreet ap->ahi_get32 = i_ddi_caut_get32; 10311865Sdilpreet ap->ahi_get64 = i_ddi_caut_get64; 10321865Sdilpreet ap->ahi_rep_get8 = i_ddi_caut_rep_get8; 10331865Sdilpreet ap->ahi_rep_get16 = i_ddi_caut_rep_get16; 10341865Sdilpreet ap->ahi_rep_get32 = i_ddi_caut_rep_get32; 10351865Sdilpreet ap->ahi_rep_get64 = i_ddi_caut_rep_get64; 10361865Sdilpreet if (hp->ah_acc.devacc_attr_access == DDI_CAUTIOUS_ACC) { 10371865Sdilpreet ap->ahi_put8 = i_ddi_caut_put8; 10381865Sdilpreet ap->ahi_put16 = i_ddi_caut_put16; 10391865Sdilpreet ap->ahi_put32 = i_ddi_caut_put32; 10401865Sdilpreet ap->ahi_put64 = i_ddi_caut_put64; 10411865Sdilpreet ap->ahi_rep_put8 = i_ddi_caut_rep_put8; 10421865Sdilpreet ap->ahi_rep_put16 = i_ddi_caut_rep_put16; 10431865Sdilpreet ap->ahi_rep_put32 = i_ddi_caut_rep_put32; 10441865Sdilpreet ap->ahi_rep_put64 = i_ddi_caut_rep_put64; 10451865Sdilpreet } else { 10461865Sdilpreet ap->ahi_put8 = pci_config_wr8; 10471865Sdilpreet ap->ahi_put16 = pci_config_wr16; 10481865Sdilpreet ap->ahi_put32 = pci_config_wr32; 10491865Sdilpreet ap->ahi_put64 = pci_config_wr64; 10501865Sdilpreet ap->ahi_rep_put8 = pci_config_rep_wr8; 10511865Sdilpreet ap->ahi_rep_put16 = pci_config_rep_wr16; 10521865Sdilpreet ap->ahi_rep_put32 = pci_config_rep_wr32; 10531865Sdilpreet ap->ahi_rep_put64 = pci_config_rep_wr64; 10541865Sdilpreet } 10551865Sdilpreet 10561865Sdilpreet /* Initialize to default check/notify functions */ 10571865Sdilpreet ap->ahi_fault_check = i_ddi_acc_fault_check; 10581865Sdilpreet ap->ahi_fault_notify = i_ddi_acc_fault_notify; 10591865Sdilpreet ap->ahi_fault = 0; 10601865Sdilpreet impl_acc_err_init(hp); 10611865Sdilpreet return (DDI_SUCCESS); 10621865Sdilpreet } 10631865Sdilpreet 10641865Sdilpreet 10651865Sdilpreet int 10661865Sdilpreet pci_common_ctlops_peek(peekpoke_ctlops_t *in_args) 10671865Sdilpreet { 10681865Sdilpreet size_t size = in_args->size; 10691865Sdilpreet uintptr_t dev_addr = in_args->dev_addr; 10701865Sdilpreet uintptr_t host_addr = in_args->host_addr; 10711865Sdilpreet ddi_acc_impl_t *hp = (ddi_acc_impl_t *)in_args->handle; 10721865Sdilpreet ddi_acc_hdl_t *hdlp = (ddi_acc_hdl_t *)in_args->handle; 10731865Sdilpreet size_t repcount = in_args->repcount; 10741865Sdilpreet uint_t flags = in_args->flags; 10751865Sdilpreet int err = DDI_SUCCESS; 10761865Sdilpreet 10771865Sdilpreet /* 10781865Sdilpreet * if no handle then this is a peek. We have to return failure here 10791865Sdilpreet * as we have no way of knowing whether this is a MEM or IO space access 10801865Sdilpreet */ 10811865Sdilpreet if (in_args->handle == NULL) 10821865Sdilpreet return (DDI_FAILURE); 10831865Sdilpreet 10841865Sdilpreet for (; repcount; repcount--) { 10851865Sdilpreet if (hp->ahi_acc_attr == DDI_ACCATTR_CONFIG_SPACE) { 10861865Sdilpreet switch (size) { 10871865Sdilpreet case sizeof (uint8_t): 10881865Sdilpreet *(uint8_t *)host_addr = pci_config_rd8(hp, 10891865Sdilpreet (uint8_t *)dev_addr); 10901865Sdilpreet break; 10911865Sdilpreet case sizeof (uint16_t): 10921865Sdilpreet *(uint16_t *)host_addr = pci_config_rd16(hp, 10931865Sdilpreet (uint16_t *)dev_addr); 10941865Sdilpreet break; 10951865Sdilpreet case sizeof (uint32_t): 10961865Sdilpreet *(uint32_t *)host_addr = pci_config_rd32(hp, 10971865Sdilpreet (uint32_t *)dev_addr); 10981865Sdilpreet break; 10991865Sdilpreet case sizeof (uint64_t): 11001865Sdilpreet *(uint64_t *)host_addr = pci_config_rd64(hp, 11011865Sdilpreet (uint64_t *)dev_addr); 11021865Sdilpreet break; 11031865Sdilpreet default: 11041865Sdilpreet err = DDI_FAILURE; 11051865Sdilpreet break; 11061865Sdilpreet } 11071865Sdilpreet } else if (hp->ahi_acc_attr & DDI_ACCATTR_IO_SPACE) { 11081865Sdilpreet if (hdlp->ah_acc.devacc_attr_endian_flags == 11091865Sdilpreet DDI_STRUCTURE_BE_ACC) { 11101865Sdilpreet switch (size) { 11111865Sdilpreet case sizeof (uint8_t): 11121865Sdilpreet *(uint8_t *)host_addr = 11131865Sdilpreet i_ddi_io_get8(hp, 11141865Sdilpreet (uint8_t *)dev_addr); 11151865Sdilpreet break; 11161865Sdilpreet case sizeof (uint16_t): 11171865Sdilpreet *(uint16_t *)host_addr = 11181865Sdilpreet i_ddi_io_swap_get16(hp, 11191865Sdilpreet (uint16_t *)dev_addr); 11201865Sdilpreet break; 11211865Sdilpreet case sizeof (uint32_t): 11221865Sdilpreet *(uint32_t *)host_addr = 11231865Sdilpreet i_ddi_io_swap_get32(hp, 11241865Sdilpreet (uint32_t *)dev_addr); 11251865Sdilpreet break; 11261865Sdilpreet /* 11271865Sdilpreet * note the 64-bit case is a dummy 11281865Sdilpreet * function - so no need to swap 11291865Sdilpreet */ 11301865Sdilpreet case sizeof (uint64_t): 11311865Sdilpreet *(uint64_t *)host_addr = 11321865Sdilpreet i_ddi_io_get64(hp, 11331865Sdilpreet (uint64_t *)dev_addr); 11341865Sdilpreet break; 11351865Sdilpreet default: 11361865Sdilpreet err = DDI_FAILURE; 11371865Sdilpreet break; 11381865Sdilpreet } 11391865Sdilpreet } else { 11401865Sdilpreet switch (size) { 11411865Sdilpreet case sizeof (uint8_t): 11421865Sdilpreet *(uint8_t *)host_addr = 11431865Sdilpreet i_ddi_io_get8(hp, 11441865Sdilpreet (uint8_t *)dev_addr); 11451865Sdilpreet break; 11461865Sdilpreet case sizeof (uint16_t): 11471865Sdilpreet *(uint16_t *)host_addr = 11481865Sdilpreet i_ddi_io_get16(hp, 11491865Sdilpreet (uint16_t *)dev_addr); 11501865Sdilpreet break; 11511865Sdilpreet case sizeof (uint32_t): 11521865Sdilpreet *(uint32_t *)host_addr = 11531865Sdilpreet i_ddi_io_get32(hp, 11541865Sdilpreet (uint32_t *)dev_addr); 11551865Sdilpreet break; 11561865Sdilpreet case sizeof (uint64_t): 11571865Sdilpreet *(uint64_t *)host_addr = 11581865Sdilpreet i_ddi_io_get64(hp, 11591865Sdilpreet (uint64_t *)dev_addr); 11601865Sdilpreet break; 11611865Sdilpreet default: 11621865Sdilpreet err = DDI_FAILURE; 11631865Sdilpreet break; 11641865Sdilpreet } 11651865Sdilpreet } 11661865Sdilpreet } else { 11671865Sdilpreet if (hdlp->ah_acc.devacc_attr_endian_flags == 11681865Sdilpreet DDI_STRUCTURE_BE_ACC) { 11691865Sdilpreet switch (in_args->size) { 11701865Sdilpreet case sizeof (uint8_t): 11711865Sdilpreet *(uint8_t *)host_addr = 11721865Sdilpreet *(uint8_t *)dev_addr; 11731865Sdilpreet break; 11741865Sdilpreet case sizeof (uint16_t): 11751865Sdilpreet *(uint16_t *)host_addr = 11761865Sdilpreet ddi_swap16(*(uint16_t *)dev_addr); 11771865Sdilpreet break; 11781865Sdilpreet case sizeof (uint32_t): 11791865Sdilpreet *(uint32_t *)host_addr = 11801865Sdilpreet ddi_swap32(*(uint32_t *)dev_addr); 11811865Sdilpreet break; 11821865Sdilpreet case sizeof (uint64_t): 11831865Sdilpreet *(uint64_t *)host_addr = 11841865Sdilpreet ddi_swap64(*(uint64_t *)dev_addr); 11851865Sdilpreet break; 11861865Sdilpreet default: 11871865Sdilpreet err = DDI_FAILURE; 11881865Sdilpreet break; 11891865Sdilpreet } 11901865Sdilpreet } else { 11911865Sdilpreet switch (in_args->size) { 11921865Sdilpreet case sizeof (uint8_t): 11931865Sdilpreet *(uint8_t *)host_addr = 11941865Sdilpreet *(uint8_t *)dev_addr; 11951865Sdilpreet break; 11961865Sdilpreet case sizeof (uint16_t): 11971865Sdilpreet *(uint16_t *)host_addr = 11981865Sdilpreet *(uint16_t *)dev_addr; 11991865Sdilpreet break; 12001865Sdilpreet case sizeof (uint32_t): 12011865Sdilpreet *(uint32_t *)host_addr = 12021865Sdilpreet *(uint32_t *)dev_addr; 12031865Sdilpreet break; 12041865Sdilpreet case sizeof (uint64_t): 12051865Sdilpreet *(uint64_t *)host_addr = 12061865Sdilpreet *(uint64_t *)dev_addr; 12071865Sdilpreet break; 12081865Sdilpreet default: 12091865Sdilpreet err = DDI_FAILURE; 12101865Sdilpreet break; 12111865Sdilpreet } 12121865Sdilpreet } 12131865Sdilpreet } 12141865Sdilpreet host_addr += size; 12151865Sdilpreet if (flags == DDI_DEV_AUTOINCR) 12161865Sdilpreet dev_addr += size; 12171865Sdilpreet } 12181865Sdilpreet return (err); 12191865Sdilpreet } 12201865Sdilpreet 12211865Sdilpreet /*ARGSUSED*/ 12221865Sdilpreet int 12231865Sdilpreet pci_common_peekpoke(dev_info_t *dip, dev_info_t *rdip, 12241865Sdilpreet ddi_ctl_enum_t ctlop, void *arg, void *result) 12251865Sdilpreet { 12261865Sdilpreet if (ctlop == DDI_CTLOPS_PEEK) 12271865Sdilpreet return (pci_common_ctlops_peek((peekpoke_ctlops_t *)arg)); 12281865Sdilpreet else 12291865Sdilpreet return (pci_common_ctlops_poke((peekpoke_ctlops_t *)arg)); 12301865Sdilpreet } 12311865Sdilpreet 12321083Sanish /* 12331083Sanish * These are the get and put functions to be shared with drivers. The 12341083Sanish * mutex locking is done inside the functions referenced, rather than 12351083Sanish * here, and is thus shared across PCI child drivers and any other 12361083Sanish * consumers of PCI config space (such as the ACPI subsystem). 12371083Sanish * 12381083Sanish * The configuration space addresses come in as pointers. This is fine on 12391083Sanish * a 32-bit system, where the VM space and configuration space are the same 12401083Sanish * size. It's not such a good idea on a 64-bit system, where memory 12411083Sanish * addresses are twice as large as configuration space addresses. At some 12421083Sanish * point in the call tree we need to take a stand and say "you are 32-bit 12431083Sanish * from this time forth", and this seems like a nice self-contained place. 12441083Sanish */ 12451083Sanish 12461083Sanish uint8_t 12471083Sanish pci_config_rd8(ddi_acc_impl_t *hdlp, uint8_t *addr) 12481083Sanish { 12491083Sanish pci_acc_cfblk_t *cfp; 12501083Sanish uint8_t rval; 12511083Sanish int reg; 12521083Sanish 12531083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 12541083Sanish 12551083Sanish reg = (int)(uintptr_t)addr; 12561083Sanish 12571083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 12581083Sanish 12591083Sanish rval = (*pci_getb_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum, 12601083Sanish reg); 12611083Sanish 12621083Sanish return (rval); 12631083Sanish } 12641083Sanish 12651083Sanish void 12661083Sanish pci_config_rep_rd8(ddi_acc_impl_t *hdlp, uint8_t *host_addr, 12671083Sanish uint8_t *dev_addr, size_t repcount, uint_t flags) 12681083Sanish { 12691083Sanish uint8_t *h, *d; 12701083Sanish 12711083Sanish h = host_addr; 12721083Sanish d = dev_addr; 12731083Sanish 12741083Sanish if (flags == DDI_DEV_AUTOINCR) 12751083Sanish for (; repcount; repcount--) 12761083Sanish *h++ = pci_config_rd8(hdlp, d++); 12771083Sanish else 12781083Sanish for (; repcount; repcount--) 12791083Sanish *h++ = pci_config_rd8(hdlp, d); 12801083Sanish } 12811083Sanish 12821083Sanish uint16_t 12831083Sanish pci_config_rd16(ddi_acc_impl_t *hdlp, uint16_t *addr) 12841083Sanish { 12851083Sanish pci_acc_cfblk_t *cfp; 12861083Sanish uint16_t rval; 12871083Sanish int reg; 12881083Sanish 12891083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 12901083Sanish 12911083Sanish reg = (int)(uintptr_t)addr; 12921083Sanish 12931083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 12941083Sanish 12951083Sanish rval = (*pci_getw_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum, 12961083Sanish reg); 12971083Sanish 12981083Sanish return (rval); 12991083Sanish } 13001083Sanish 13011083Sanish void 13021083Sanish pci_config_rep_rd16(ddi_acc_impl_t *hdlp, uint16_t *host_addr, 13031083Sanish uint16_t *dev_addr, size_t repcount, uint_t flags) 13041083Sanish { 13051083Sanish uint16_t *h, *d; 13061083Sanish 13071083Sanish h = host_addr; 13081083Sanish d = dev_addr; 13091083Sanish 13101083Sanish if (flags == DDI_DEV_AUTOINCR) 13111083Sanish for (; repcount; repcount--) 13121083Sanish *h++ = pci_config_rd16(hdlp, d++); 13131083Sanish else 13141083Sanish for (; repcount; repcount--) 13151083Sanish *h++ = pci_config_rd16(hdlp, d); 13161083Sanish } 13171083Sanish 13181083Sanish uint32_t 13191083Sanish pci_config_rd32(ddi_acc_impl_t *hdlp, uint32_t *addr) 13201083Sanish { 13211083Sanish pci_acc_cfblk_t *cfp; 13221083Sanish uint32_t rval; 13231083Sanish int reg; 13241083Sanish 13251083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 13261083Sanish 13271083Sanish reg = (int)(uintptr_t)addr; 13281083Sanish 13291083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 13301083Sanish 13311083Sanish rval = (*pci_getl_func)(cfp->c_busnum, cfp->c_devnum, 13321083Sanish cfp->c_funcnum, reg); 13331083Sanish 13341083Sanish return (rval); 13351083Sanish } 13361083Sanish 13371083Sanish void 13381083Sanish pci_config_rep_rd32(ddi_acc_impl_t *hdlp, uint32_t *host_addr, 13391083Sanish uint32_t *dev_addr, size_t repcount, uint_t flags) 13401083Sanish { 13411083Sanish uint32_t *h, *d; 13421083Sanish 13431083Sanish h = host_addr; 13441083Sanish d = dev_addr; 13451083Sanish 13461083Sanish if (flags == DDI_DEV_AUTOINCR) 13471083Sanish for (; repcount; repcount--) 13481083Sanish *h++ = pci_config_rd32(hdlp, d++); 13491083Sanish else 13501083Sanish for (; repcount; repcount--) 13511083Sanish *h++ = pci_config_rd32(hdlp, d); 13521083Sanish } 13531083Sanish 13541083Sanish 13551083Sanish void 13561083Sanish pci_config_wr8(ddi_acc_impl_t *hdlp, uint8_t *addr, uint8_t value) 13571083Sanish { 13581083Sanish pci_acc_cfblk_t *cfp; 13591083Sanish int reg; 13601083Sanish 13611083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 13621083Sanish 13631083Sanish reg = (int)(uintptr_t)addr; 13641083Sanish 13651083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 13661083Sanish 13671083Sanish (*pci_putb_func)(cfp->c_busnum, cfp->c_devnum, 13681083Sanish cfp->c_funcnum, reg, value); 13691083Sanish } 13701083Sanish 13711083Sanish void 13721083Sanish pci_config_rep_wr8(ddi_acc_impl_t *hdlp, uint8_t *host_addr, 13731083Sanish uint8_t *dev_addr, size_t repcount, uint_t flags) 13741083Sanish { 13751083Sanish uint8_t *h, *d; 13761083Sanish 13771083Sanish h = host_addr; 13781083Sanish d = dev_addr; 13791083Sanish 13801083Sanish if (flags == DDI_DEV_AUTOINCR) 13811083Sanish for (; repcount; repcount--) 13821083Sanish pci_config_wr8(hdlp, d++, *h++); 13831083Sanish else 13841083Sanish for (; repcount; repcount--) 13851083Sanish pci_config_wr8(hdlp, d, *h++); 13861083Sanish } 13871083Sanish 13881083Sanish void 13891083Sanish pci_config_wr16(ddi_acc_impl_t *hdlp, uint16_t *addr, uint16_t value) 13901083Sanish { 13911083Sanish pci_acc_cfblk_t *cfp; 13921083Sanish int reg; 13931083Sanish 13941083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 13951083Sanish 13961083Sanish reg = (int)(uintptr_t)addr; 13971083Sanish 13981083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 13991083Sanish 14001083Sanish (*pci_putw_func)(cfp->c_busnum, cfp->c_devnum, 14011083Sanish cfp->c_funcnum, reg, value); 14021083Sanish } 14031083Sanish 14041083Sanish void 14051083Sanish pci_config_rep_wr16(ddi_acc_impl_t *hdlp, uint16_t *host_addr, 14061083Sanish uint16_t *dev_addr, size_t repcount, uint_t flags) 14071083Sanish { 14081083Sanish uint16_t *h, *d; 14091083Sanish 14101083Sanish h = host_addr; 14111083Sanish d = dev_addr; 14121083Sanish 14131083Sanish if (flags == DDI_DEV_AUTOINCR) 14141083Sanish for (; repcount; repcount--) 14151083Sanish pci_config_wr16(hdlp, d++, *h++); 14161083Sanish else 14171083Sanish for (; repcount; repcount--) 14181083Sanish pci_config_wr16(hdlp, d, *h++); 14191083Sanish } 14201083Sanish 14211083Sanish void 14221083Sanish pci_config_wr32(ddi_acc_impl_t *hdlp, uint32_t *addr, uint32_t value) 14231083Sanish { 14241083Sanish pci_acc_cfblk_t *cfp; 14251083Sanish int reg; 14261083Sanish 14271083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 14281083Sanish 14291083Sanish reg = (int)(uintptr_t)addr; 14301083Sanish 14311083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 14321083Sanish 14331083Sanish (*pci_putl_func)(cfp->c_busnum, cfp->c_devnum, 14341083Sanish cfp->c_funcnum, reg, value); 14351083Sanish } 14361083Sanish 14371083Sanish void 14381083Sanish pci_config_rep_wr32(ddi_acc_impl_t *hdlp, uint32_t *host_addr, 14391083Sanish uint32_t *dev_addr, size_t repcount, uint_t flags) 14401083Sanish { 14411083Sanish uint32_t *h, *d; 14421083Sanish 14431083Sanish h = host_addr; 14441083Sanish d = dev_addr; 14451083Sanish 14461083Sanish if (flags == DDI_DEV_AUTOINCR) 14471083Sanish for (; repcount; repcount--) 14481083Sanish pci_config_wr32(hdlp, d++, *h++); 14491083Sanish else 14501083Sanish for (; repcount; repcount--) 14511083Sanish pci_config_wr32(hdlp, d, *h++); 14521083Sanish } 14531083Sanish 14541083Sanish uint64_t 14551083Sanish pci_config_rd64(ddi_acc_impl_t *hdlp, uint64_t *addr) 14561083Sanish { 14571083Sanish uint32_t lw_val; 14581083Sanish uint32_t hi_val; 14591083Sanish uint32_t *dp; 14601083Sanish uint64_t val; 14611083Sanish 14621083Sanish dp = (uint32_t *)addr; 14631083Sanish lw_val = pci_config_rd32(hdlp, dp); 14641083Sanish dp++; 14651083Sanish hi_val = pci_config_rd32(hdlp, dp); 14661083Sanish val = ((uint64_t)hi_val << 32) | lw_val; 14671083Sanish return (val); 14681083Sanish } 14691083Sanish 14701083Sanish void 14711083Sanish pci_config_wr64(ddi_acc_impl_t *hdlp, uint64_t *addr, uint64_t value) 14721083Sanish { 14731083Sanish uint32_t lw_val; 14741083Sanish uint32_t hi_val; 14751083Sanish uint32_t *dp; 14761083Sanish 14771083Sanish dp = (uint32_t *)addr; 14781083Sanish lw_val = (uint32_t)(value & 0xffffffff); 14791083Sanish hi_val = (uint32_t)(value >> 32); 14801083Sanish pci_config_wr32(hdlp, dp, lw_val); 14811083Sanish dp++; 14821083Sanish pci_config_wr32(hdlp, dp, hi_val); 14831083Sanish } 14841083Sanish 14851083Sanish void 14861083Sanish pci_config_rep_rd64(ddi_acc_impl_t *hdlp, uint64_t *host_addr, 14871083Sanish uint64_t *dev_addr, size_t repcount, uint_t flags) 14881083Sanish { 14891083Sanish if (flags == DDI_DEV_AUTOINCR) { 14901083Sanish for (; repcount; repcount--) 14911083Sanish *host_addr++ = pci_config_rd64(hdlp, dev_addr++); 14921083Sanish } else { 14931083Sanish for (; repcount; repcount--) 14941083Sanish *host_addr++ = pci_config_rd64(hdlp, dev_addr); 14951083Sanish } 14961083Sanish } 14971083Sanish 14981083Sanish void 14991083Sanish pci_config_rep_wr64(ddi_acc_impl_t *hdlp, uint64_t *host_addr, 15001083Sanish uint64_t *dev_addr, size_t repcount, uint_t flags) 15011083Sanish { 15021083Sanish if (flags == DDI_DEV_AUTOINCR) { 15031083Sanish for (; repcount; repcount--) 15041083Sanish pci_config_wr64(hdlp, host_addr++, *dev_addr++); 15051083Sanish } else { 15061083Sanish for (; repcount; repcount--) 15071083Sanish pci_config_wr64(hdlp, host_addr++, *dev_addr); 15081083Sanish } 15091083Sanish } 15101083Sanish 15111083Sanish 15121083Sanish /* 15131083Sanish * Enable Legacy PCI config space access for the following four north bridges 15141083Sanish * Host bridge: AMD HyperTransport Technology Configuration 15151083Sanish * Host bridge: AMD Address Map 15161083Sanish * Host bridge: AMD DRAM Controller 15171083Sanish * Host bridge: AMD Miscellaneous Control 15181083Sanish */ 15191083Sanish int 15201083Sanish is_amd_northbridge(dev_info_t *dip) 15211083Sanish { 15221083Sanish int vendor_id, device_id; 15231083Sanish 15241083Sanish vendor_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 15251083Sanish "vendor-id", -1); 15261083Sanish device_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 15271083Sanish "device-id", -1); 15281083Sanish 15291083Sanish if (IS_AMD_NTBRIDGE(vendor_id, device_id)) 15301083Sanish return (0); 15311083Sanish 15321083Sanish return (1); 15331083Sanish } 1534