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_get_nintrs(dev_info_t *, int, int *); 61881Sjohnny static int pci_enable_intr(dev_info_t *, dev_info_t *, 62881Sjohnny ddi_intr_handle_impl_t *, uint32_t); 63881Sjohnny static void pci_disable_intr(dev_info_t *, dev_info_t *, 64881Sjohnny ddi_intr_handle_impl_t *, uint32_t); 65881Sjohnny 66881Sjohnny /* Extern decalration for pcplusmp module */ 67881Sjohnny extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *, 68881Sjohnny psm_intr_op_t, int *); 69881Sjohnny 70881Sjohnny 71881Sjohnny /* 72881Sjohnny * pci_name_child: 73881Sjohnny * 74881Sjohnny * Assign the address portion of the node name 75881Sjohnny */ 76881Sjohnny int 77881Sjohnny pci_common_name_child(dev_info_t *child, char *name, int namelen) 78881Sjohnny { 79881Sjohnny int dev, func, length; 80881Sjohnny char **unit_addr; 81881Sjohnny uint_t n; 82881Sjohnny pci_regspec_t *pci_rp; 83881Sjohnny 84881Sjohnny if (ndi_dev_is_persistent_node(child) == 0) { 85881Sjohnny /* 86881Sjohnny * For .conf node, use "unit-address" property 87881Sjohnny */ 88881Sjohnny if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 89881Sjohnny DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) != 90881Sjohnny DDI_PROP_SUCCESS) { 91881Sjohnny cmn_err(CE_WARN, "cannot find unit-address in %s.conf", 92881Sjohnny ddi_get_name(child)); 93881Sjohnny return (DDI_FAILURE); 94881Sjohnny } 95881Sjohnny if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 96881Sjohnny cmn_err(CE_WARN, "unit-address property in %s.conf" 97881Sjohnny " not well-formed", ddi_get_name(child)); 98881Sjohnny ddi_prop_free(unit_addr); 99881Sjohnny return (DDI_FAILURE); 100881Sjohnny } 101881Sjohnny (void) snprintf(name, namelen, "%s", *unit_addr); 102881Sjohnny ddi_prop_free(unit_addr); 103881Sjohnny return (DDI_SUCCESS); 104881Sjohnny } 105881Sjohnny 106881Sjohnny if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 107881Sjohnny "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) { 108881Sjohnny cmn_err(CE_WARN, "cannot find reg property in %s", 109881Sjohnny ddi_get_name(child)); 110881Sjohnny return (DDI_FAILURE); 111881Sjohnny } 112881Sjohnny 113881Sjohnny /* copy the device identifications */ 114881Sjohnny dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 115881Sjohnny func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); 116881Sjohnny 117881Sjohnny /* 118881Sjohnny * free the memory allocated by ddi_prop_lookup_int_array 119881Sjohnny */ 120881Sjohnny ddi_prop_free(pci_rp); 121881Sjohnny 122881Sjohnny if (func != 0) { 123881Sjohnny (void) snprintf(name, namelen, "%x,%x", dev, func); 124881Sjohnny } else { 125881Sjohnny (void) snprintf(name, namelen, "%x", dev); 126881Sjohnny } 127881Sjohnny 128881Sjohnny return (DDI_SUCCESS); 129881Sjohnny } 130881Sjohnny 131881Sjohnny /* 132881Sjohnny * Interrupt related code: 133881Sjohnny * 134881Sjohnny * The following busop is common to npe and pci drivers 135881Sjohnny * bus_introp 136881Sjohnny */ 137881Sjohnny 138881Sjohnny /* 139881Sjohnny * Create the ddi_parent_private_data for a pseudo child. 140881Sjohnny */ 141881Sjohnny void 142881Sjohnny pci_common_set_parent_private_data(dev_info_t *dip) 143881Sjohnny { 144881Sjohnny struct ddi_parent_private_data *pdptr; 145881Sjohnny 146881Sjohnny pdptr = (struct ddi_parent_private_data *)kmem_zalloc( 147881Sjohnny (sizeof (struct ddi_parent_private_data) + 148881Sjohnny sizeof (struct intrspec)), KM_SLEEP); 149881Sjohnny pdptr->par_intr = (struct intrspec *)(pdptr + 1); 150881Sjohnny pdptr->par_nintr = 1; 151881Sjohnny ddi_set_parent_data(dip, pdptr); 152881Sjohnny } 153881Sjohnny 154881Sjohnny /* 155881Sjohnny * pci_get_priority: 156881Sjohnny * Figure out the priority of the device 157881Sjohnny */ 158881Sjohnny static int 159881Sjohnny pci_get_priority(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, int *pri) 160881Sjohnny { 161881Sjohnny struct intrspec *ispec; 162881Sjohnny 163881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_get_priority: dip = 0x%p, hdlp = %p\n", 164881Sjohnny (void *)dip, (void *)hdlp)); 165881Sjohnny 166881Sjohnny if ((ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip, 167881Sjohnny hdlp->ih_inum)) == NULL) { 168881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) { 169881Sjohnny int class = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 170881Sjohnny DDI_PROP_DONTPASS, "class-code", -1); 171881Sjohnny 172881Sjohnny *pri = (class == -1) ? 1 : pci_devclass_to_ipl(class); 173881Sjohnny pci_common_set_parent_private_data(hdlp->ih_dip); 174881Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip, 175881Sjohnny hdlp->ih_inum); 176881Sjohnny return (DDI_SUCCESS); 177881Sjohnny } 178881Sjohnny return (DDI_FAILURE); 179881Sjohnny } 180881Sjohnny 181881Sjohnny *pri = ispec->intrspec_pri; 182881Sjohnny return (DDI_SUCCESS); 183881Sjohnny } 184881Sjohnny 185881Sjohnny 186881Sjohnny /* 187881Sjohnny * pci_get_nintrs: 188881Sjohnny * Figure out how many interrupts the device supports 189881Sjohnny */ 190881Sjohnny static int 191881Sjohnny pci_get_nintrs(dev_info_t *dip, int type, int *nintrs) 192881Sjohnny { 193881Sjohnny int ret; 194881Sjohnny 195881Sjohnny *nintrs = 0; 196881Sjohnny 197881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(type)) 198881Sjohnny ret = pci_msi_get_nintrs(dip, type, nintrs); 199881Sjohnny else { 200881Sjohnny ret = DDI_FAILURE; 201881Sjohnny if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 202881Sjohnny "interrupts", -1) != -1) { 203881Sjohnny *nintrs = 1; 204881Sjohnny ret = DDI_SUCCESS; 205881Sjohnny } 206881Sjohnny } 207881Sjohnny 208881Sjohnny return (ret); 209881Sjohnny } 210881Sjohnny 211881Sjohnny static int pcie_pci_intr_pri_counter = 0; 212881Sjohnny 213881Sjohnny /* 214881Sjohnny * pci_common_intr_ops: bus_intr_op() function for interrupt support 215881Sjohnny */ 216881Sjohnny int 217881Sjohnny pci_common_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, 218881Sjohnny ddi_intr_handle_impl_t *hdlp, void *result) 219881Sjohnny { 220881Sjohnny int priority = 0; 221881Sjohnny int psm_status = 0; 222881Sjohnny int pci_status = 0; 223881Sjohnny int pci_rval, psm_rval = PSM_FAILURE; 224881Sjohnny int types = 0; 225881Sjohnny int pciepci = 0; 2261542Sjohnny int i, j, count; 227881Sjohnny int behavior; 228881Sjohnny ddi_intrspec_t isp; 229881Sjohnny struct intrspec *ispec; 230881Sjohnny ddi_intr_handle_impl_t tmp_hdl; 231881Sjohnny ddi_intr_msix_t *msix_p; 2321087Sschwartz ihdl_plat_t *ihdl_plat_datap; 2331542Sjohnny ddi_intr_handle_t *h_array; 234881Sjohnny 235881Sjohnny DDI_INTR_NEXDBG((CE_CONT, 236881Sjohnny "pci_common_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n", 237881Sjohnny (void *)pdip, (void *)rdip, intr_op, (void *)hdlp)); 238881Sjohnny 239881Sjohnny /* Process the request */ 240881Sjohnny switch (intr_op) { 241881Sjohnny case DDI_INTROP_SUPPORTED_TYPES: 242881Sjohnny /* Fixed supported by default */ 243881Sjohnny *(int *)result = DDI_INTR_TYPE_FIXED; 244881Sjohnny 245881Sjohnny /* Figure out if MSI or MSI-X is supported? */ 246881Sjohnny if (pci_msi_get_supported_type(rdip, &types) != DDI_SUCCESS) 247881Sjohnny return (DDI_SUCCESS); 248881Sjohnny 249881Sjohnny if (psm_intr_ops != NULL) { 250881Sjohnny /* MSI or MSI-X is supported, OR it in */ 251881Sjohnny *(int *)result |= types; 252881Sjohnny 253881Sjohnny tmp_hdl.ih_type = *(int *)result; 254881Sjohnny (void) (*psm_intr_ops)(rdip, &tmp_hdl, 255881Sjohnny PSM_INTR_OP_CHECK_MSI, result); 256881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 257881Sjohnny "rdip: 0x%p supported types: 0x%x\n", (void *)rdip, 258881Sjohnny *(int *)result)); 259881Sjohnny } 260881Sjohnny break; 261881Sjohnny case DDI_INTROP_NINTRS: 262881Sjohnny if (pci_get_nintrs(rdip, hdlp->ih_type, result) != DDI_SUCCESS) 263881Sjohnny return (DDI_FAILURE); 264881Sjohnny break; 265881Sjohnny case DDI_INTROP_ALLOC: 266881Sjohnny /* 267881Sjohnny * MSI or MSIX (figure out number of vectors available) 268881Sjohnny * FIXED interrupts: just return available interrupts 269881Sjohnny */ 270881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && 271881Sjohnny (psm_intr_ops != NULL) && 272881Sjohnny (pci_get_priority(rdip, hdlp, &priority) == DDI_SUCCESS)) { 273881Sjohnny /* 274881Sjohnny * Following check is a special case for 'pcie_pci'. 275881Sjohnny * This makes sure vectors with the right priority 276881Sjohnny * are allocated for pcie_pci during ALLOC time. 277881Sjohnny */ 278881Sjohnny if (strcmp(ddi_driver_name(rdip), "pcie_pci") == 0) { 279881Sjohnny hdlp->ih_pri = 280881Sjohnny (pcie_pci_intr_pri_counter % 2) ? 4 : 7; 281881Sjohnny pciepci = 1; 282881Sjohnny } else 283881Sjohnny hdlp->ih_pri = priority; 284*1717Swesolows behavior = (int)(uintptr_t)hdlp->ih_scratch2; 285881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 286881Sjohnny PSM_INTR_OP_ALLOC_VECTORS, result); 287881Sjohnny 288881Sjohnny /* verify behavior flag and take appropriate action */ 289881Sjohnny if ((behavior == DDI_INTR_ALLOC_STRICT) && 290881Sjohnny (*(int *)result < hdlp->ih_scratch1)) { 291881Sjohnny DDI_INTR_NEXDBG((CE_CONT, 292881Sjohnny "pci_common_intr_ops: behavior %x, " 293881Sjohnny "couldn't get enough intrs\n", behavior)); 294881Sjohnny hdlp->ih_scratch1 = *(int *)result; 295881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 296881Sjohnny PSM_INTR_OP_FREE_VECTORS, NULL); 297881Sjohnny return (DDI_EAGAIN); 298881Sjohnny } 299881Sjohnny 300881Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) { 301881Sjohnny if (!(msix_p = i_ddi_get_msix(hdlp->ih_dip))) { 302881Sjohnny msix_p = pci_msix_init(hdlp->ih_dip); 303881Sjohnny if (msix_p) 304881Sjohnny i_ddi_set_msix(hdlp->ih_dip, 305881Sjohnny msix_p); 306881Sjohnny } 307881Sjohnny msix_p->msix_intrs_in_use += *(int *)result; 308881Sjohnny } 309881Sjohnny 310881Sjohnny if (pciepci) { 311881Sjohnny /* update priority in ispec */ 312881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, 313881Sjohnny (int)hdlp->ih_inum); 314881Sjohnny ispec = (struct intrspec *)isp; 315881Sjohnny if (ispec) 316881Sjohnny ispec->intrspec_pri = hdlp->ih_pri; 317881Sjohnny ++pcie_pci_intr_pri_counter; 318881Sjohnny } 319881Sjohnny 320881Sjohnny } else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) { 321881Sjohnny /* Figure out if this device supports MASKING */ 322881Sjohnny pci_rval = pci_intx_get_cap(rdip, &pci_status); 323881Sjohnny if (pci_rval == DDI_SUCCESS && pci_status) 324881Sjohnny hdlp->ih_cap |= pci_status; 325881Sjohnny *(int *)result = 1; /* DDI_INTR_TYPE_FIXED */ 326881Sjohnny } else 327881Sjohnny return (DDI_FAILURE); 328881Sjohnny break; 329881Sjohnny case DDI_INTROP_FREE: 330881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && 331881Sjohnny (psm_intr_ops != NULL)) { 332881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 333881Sjohnny PSM_INTR_OP_FREE_VECTORS, NULL); 334881Sjohnny 335881Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) { 336881Sjohnny msix_p = i_ddi_get_msix(hdlp->ih_dip); 337881Sjohnny if (msix_p && 338881Sjohnny --msix_p->msix_intrs_in_use == 0) { 339881Sjohnny pci_msix_fini(msix_p); 340881Sjohnny i_ddi_set_msix(hdlp->ih_dip, NULL); 341881Sjohnny } 342881Sjohnny } 343881Sjohnny } 344881Sjohnny break; 345881Sjohnny case DDI_INTROP_GETPRI: 346881Sjohnny /* Get the priority */ 347881Sjohnny if (pci_get_priority(rdip, hdlp, &priority) != DDI_SUCCESS) 348881Sjohnny return (DDI_FAILURE); 349881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 350881Sjohnny "priority = 0x%x\n", priority)); 351881Sjohnny *(int *)result = priority; 352881Sjohnny break; 353881Sjohnny case DDI_INTROP_SETPRI: 354881Sjohnny /* Validate the interrupt priority passed */ 355881Sjohnny if (*(int *)result > LOCK_LEVEL) 356881Sjohnny return (DDI_FAILURE); 357881Sjohnny 358881Sjohnny /* Ensure that PSM is all initialized */ 359881Sjohnny if (psm_intr_ops == NULL) 360881Sjohnny return (DDI_FAILURE); 361881Sjohnny 362881Sjohnny /* Change the priority */ 363881Sjohnny if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_PRI, result) == 364881Sjohnny PSM_FAILURE) 365881Sjohnny return (DDI_FAILURE); 366881Sjohnny 367881Sjohnny /* update ispec */ 368881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 369881Sjohnny ispec = (struct intrspec *)isp; 370881Sjohnny if (ispec) 371881Sjohnny ispec->intrspec_pri = *(int *)result; 372881Sjohnny break; 373881Sjohnny case DDI_INTROP_ADDISR: 374881Sjohnny /* update ispec */ 375881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 376881Sjohnny ispec = (struct intrspec *)isp; 3771087Sschwartz if (ispec) { 378881Sjohnny ispec->intrspec_func = hdlp->ih_cb_func; 3791087Sschwartz ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 3801087Sschwartz pci_kstat_create(&ihdl_plat_datap->ip_ksp, pdip, hdlp); 3811087Sschwartz } 382881Sjohnny break; 383881Sjohnny case DDI_INTROP_REMISR: 384881Sjohnny /* Get the interrupt structure pointer */ 385881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 386881Sjohnny ispec = (struct intrspec *)isp; 3871087Sschwartz if (ispec) { 388881Sjohnny ispec->intrspec_func = (uint_t (*)()) 0; 3891087Sschwartz ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 3901087Sschwartz if (ihdl_plat_datap->ip_ksp != NULL) 3911087Sschwartz pci_kstat_delete(ihdl_plat_datap->ip_ksp); 3921087Sschwartz } 393881Sjohnny break; 394881Sjohnny case DDI_INTROP_GETCAP: 395881Sjohnny /* 396881Sjohnny * First check the config space and/or 397881Sjohnny * MSI capability register(s) 398881Sjohnny */ 399881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 400881Sjohnny pci_rval = pci_msi_get_cap(rdip, hdlp->ih_type, 401881Sjohnny &pci_status); 402881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 403881Sjohnny pci_rval = pci_intx_get_cap(rdip, &pci_status); 404881Sjohnny 405881Sjohnny /* next check with pcplusmp */ 406881Sjohnny if (psm_intr_ops != NULL) 407881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 408881Sjohnny PSM_INTR_OP_GET_CAP, &psm_status); 409881Sjohnny 410881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned psm_rval = %x, " 411881Sjohnny "psm_status = %x, pci_rval = %x, pci_status = %x\n", 412881Sjohnny psm_rval, psm_status, pci_rval, pci_status)); 413881Sjohnny 414881Sjohnny if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) { 415881Sjohnny *(int *)result = 0; 416881Sjohnny return (DDI_FAILURE); 417881Sjohnny } 418881Sjohnny 419881Sjohnny if (psm_rval == PSM_SUCCESS) 420881Sjohnny *(int *)result = psm_status; 421881Sjohnny 422881Sjohnny if (pci_rval == DDI_SUCCESS) 423881Sjohnny *(int *)result |= pci_status; 424881Sjohnny 425881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned = %x\n", 426881Sjohnny *(int *)result)); 427881Sjohnny break; 428881Sjohnny case DDI_INTROP_SETCAP: 429881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 430881Sjohnny "SETCAP cap=0x%x\n", *(int *)result)); 431881Sjohnny if (psm_intr_ops == NULL) 432881Sjohnny return (DDI_FAILURE); 433881Sjohnny 434881Sjohnny if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) { 435881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "GETCAP: psm_intr_ops" 436881Sjohnny " returned failure\n")); 437881Sjohnny return (DDI_FAILURE); 438881Sjohnny } 439881Sjohnny break; 440881Sjohnny case DDI_INTROP_ENABLE: 441881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE\n")); 442881Sjohnny if (psm_intr_ops == NULL) 443881Sjohnny return (DDI_FAILURE); 444881Sjohnny 445881Sjohnny if (pci_enable_intr(pdip, rdip, hdlp, hdlp->ih_inum) != 446881Sjohnny DDI_SUCCESS) 447881Sjohnny return (DDI_FAILURE); 448881Sjohnny 449881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE " 450881Sjohnny "vector=0x%x\n", hdlp->ih_vector)); 451881Sjohnny break; 452881Sjohnny case DDI_INTROP_DISABLE: 453881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE\n")); 454881Sjohnny if (psm_intr_ops == NULL) 455881Sjohnny return (DDI_FAILURE); 456881Sjohnny 457881Sjohnny pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum); 458881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE " 459881Sjohnny "vector = %x\n", hdlp->ih_vector)); 460881Sjohnny break; 461881Sjohnny case DDI_INTROP_BLOCKENABLE: 462881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 463881Sjohnny "BLOCKENABLE\n")); 464881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_MSI) { 465881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: not MSI\n")); 466881Sjohnny return (DDI_FAILURE); 467881Sjohnny } 468881Sjohnny 469881Sjohnny /* Check if psm_intr_ops is NULL? */ 470881Sjohnny if (psm_intr_ops == NULL) 471881Sjohnny return (DDI_FAILURE); 472881Sjohnny 4731542Sjohnny count = hdlp->ih_scratch1; 4741542Sjohnny h_array = (ddi_intr_handle_t *)hdlp->ih_scratch2; 4751542Sjohnny for (i = 0; i < count; i++) { 4761542Sjohnny hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 477881Sjohnny if (pci_enable_intr(pdip, rdip, hdlp, 4781542Sjohnny hdlp->ih_inum) != DDI_SUCCESS) { 479881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: " 480881Sjohnny "pci_enable_intr failed for %d\n", i)); 4811542Sjohnny for (j = 0; j < i; j++) { 4821542Sjohnny hdlp = (ddi_intr_handle_impl_t *)h_array[j]; 4831542Sjohnny pci_disable_intr(pdip, rdip, hdlp, 4841542Sjohnny hdlp->ih_inum); 4851542Sjohnny } 486881Sjohnny return (DDI_FAILURE); 487881Sjohnny } 488881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 4891542Sjohnny "BLOCKENABLE inum %x done\n", hdlp->ih_inum)); 490881Sjohnny } 491881Sjohnny break; 492881Sjohnny case DDI_INTROP_BLOCKDISABLE: 493881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 494881Sjohnny "BLOCKDISABLE\n")); 495881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_MSI) { 496881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKDISABLE: not MSI\n")); 497881Sjohnny return (DDI_FAILURE); 498881Sjohnny } 499881Sjohnny 500881Sjohnny /* Check if psm_intr_ops is present */ 501881Sjohnny if (psm_intr_ops == NULL) 502881Sjohnny return (DDI_FAILURE); 503881Sjohnny 5041542Sjohnny count = hdlp->ih_scratch1; 5051542Sjohnny h_array = (ddi_intr_handle_t *)hdlp->ih_scratch2; 5061542Sjohnny for (i = 0; i < count; i++) { 5071542Sjohnny hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 5081542Sjohnny pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum); 509881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 5101542Sjohnny "BLOCKDISABLE inum %x done\n", hdlp->ih_inum)); 511881Sjohnny } 512881Sjohnny break; 513881Sjohnny case DDI_INTROP_SETMASK: 514881Sjohnny case DDI_INTROP_CLRMASK: 515881Sjohnny /* 516881Sjohnny * First handle in the config space 517881Sjohnny */ 518881Sjohnny if (intr_op == DDI_INTROP_SETMASK) { 519881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 520881Sjohnny pci_status = pci_msi_set_mask(rdip, 521881Sjohnny hdlp->ih_type, hdlp->ih_inum); 522881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 523881Sjohnny pci_status = pci_intx_set_mask(rdip); 524881Sjohnny } else { 525881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 526881Sjohnny pci_status = pci_msi_clr_mask(rdip, 527881Sjohnny hdlp->ih_type, hdlp->ih_inum); 528881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 529881Sjohnny pci_status = pci_intx_clr_mask(rdip); 530881Sjohnny } 531881Sjohnny 532881Sjohnny /* For MSI/X; no need to check with pcplusmp */ 533881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 534881Sjohnny return (pci_status); 535881Sjohnny 536881Sjohnny /* For fixed interrupts only: handle config space first */ 537881Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_FIXED && 538881Sjohnny pci_status == DDI_SUCCESS) 539881Sjohnny break; 540881Sjohnny 541881Sjohnny /* For fixed interrupts only: confer with pcplusmp next */ 542881Sjohnny if (psm_intr_ops != NULL) { 543881Sjohnny /* If interrupt is shared; do nothing */ 544881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 545881Sjohnny PSM_INTR_OP_GET_SHARED, &psm_status); 546881Sjohnny 547881Sjohnny if (psm_rval == PSM_FAILURE || psm_status == 1) 548881Sjohnny return (pci_status); 549881Sjohnny 550881Sjohnny /* Now, pcplusmp should try to set/clear the mask */ 551881Sjohnny if (intr_op == DDI_INTROP_SETMASK) 552881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 553881Sjohnny PSM_INTR_OP_SET_MASK, NULL); 554881Sjohnny else 555881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 556881Sjohnny PSM_INTR_OP_CLEAR_MASK, NULL); 557881Sjohnny } 558881Sjohnny return ((psm_rval == PSM_FAILURE) ? DDI_FAILURE : DDI_SUCCESS); 559881Sjohnny case DDI_INTROP_GETPENDING: 560881Sjohnny /* 561881Sjohnny * First check the config space and/or 562881Sjohnny * MSI capability register(s) 563881Sjohnny */ 564881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 565881Sjohnny pci_rval = pci_msi_get_pending(rdip, hdlp->ih_type, 566881Sjohnny hdlp->ih_inum, &pci_status); 567881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 568881Sjohnny pci_rval = pci_intx_get_pending(rdip, &pci_status); 569881Sjohnny 570881Sjohnny /* On failure; next try with pcplusmp */ 571881Sjohnny if (pci_rval != DDI_SUCCESS && psm_intr_ops != NULL) 572881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 573881Sjohnny PSM_INTR_OP_GET_PENDING, &psm_status); 574881Sjohnny 575881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned " 576881Sjohnny "psm_rval = %x, psm_status = %x, pci_rval = %x, " 577881Sjohnny "pci_status = %x\n", psm_rval, psm_status, pci_rval, 578881Sjohnny pci_status)); 579881Sjohnny if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) { 580881Sjohnny *(int *)result = 0; 581881Sjohnny return (DDI_FAILURE); 582881Sjohnny } 583881Sjohnny 584881Sjohnny if (psm_rval != PSM_FAILURE) 585881Sjohnny *(int *)result = psm_status; 586881Sjohnny else if (pci_rval != DDI_FAILURE) 587881Sjohnny *(int *)result = pci_status; 588881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned = %x\n", 589881Sjohnny *(int *)result)); 590881Sjohnny break; 591881Sjohnny case DDI_INTROP_NAVAIL: 592881Sjohnny if ((psm_intr_ops != NULL) && (pci_get_priority(rdip, 593881Sjohnny hdlp, &priority) == DDI_SUCCESS)) { 594881Sjohnny /* Priority in the handle not initialized yet */ 595881Sjohnny hdlp->ih_pri = priority; 596881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 597881Sjohnny PSM_INTR_OP_NAVAIL_VECTORS, result); 598881Sjohnny } else { 599881Sjohnny *(int *)result = 1; 600881Sjohnny } 601881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: NAVAIL returned = %x\n", 602881Sjohnny *(int *)result)); 603881Sjohnny break; 604881Sjohnny default: 605881Sjohnny return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result)); 606881Sjohnny } 607881Sjohnny 608881Sjohnny return (DDI_SUCCESS); 609881Sjohnny } 610881Sjohnny 611916Sschwartz int 612916Sschwartz pci_get_intr_from_vecirq(apic_get_intr_t *intrinfo_p, 613916Sschwartz int vecirq, boolean_t is_irq) 614916Sschwartz { 615916Sschwartz ddi_intr_handle_impl_t get_info_ii_hdl; 616916Sschwartz 617916Sschwartz if (is_irq) 618916Sschwartz intrinfo_p->avgi_req_flags |= PSMGI_INTRBY_IRQ; 619916Sschwartz 620916Sschwartz /* 621916Sschwartz * For this locally-declared and used handle, ih_private will contain a 622916Sschwartz * pointer to apic_get_intr_t, not an ihdl_plat_t as used for 623916Sschwartz * global interrupt handling. 624916Sschwartz */ 625916Sschwartz get_info_ii_hdl.ih_private = intrinfo_p; 626916Sschwartz get_info_ii_hdl.ih_vector = (ushort_t)vecirq; 627916Sschwartz 628916Sschwartz if ((*psm_intr_ops)(NULL, &get_info_ii_hdl, 629916Sschwartz PSM_INTR_OP_GET_INTR, NULL) == PSM_FAILURE) 630916Sschwartz return (DDI_FAILURE); 631916Sschwartz 632916Sschwartz return (DDI_SUCCESS); 633916Sschwartz } 634916Sschwartz 635916Sschwartz 636916Sschwartz int 637916Sschwartz pci_get_cpu_from_vecirq(int vecirq, boolean_t is_irq) 638916Sschwartz { 639916Sschwartz int rval; 640916Sschwartz 641916Sschwartz apic_get_intr_t intrinfo; 642916Sschwartz intrinfo.avgi_req_flags = PSMGI_REQ_CPUID; 643916Sschwartz rval = pci_get_intr_from_vecirq(&intrinfo, vecirq, is_irq); 644916Sschwartz 645916Sschwartz if (rval == DDI_SUCCESS) 646916Sschwartz return (intrinfo.avgi_cpu_id); 647916Sschwartz else 648916Sschwartz return (-1); 649916Sschwartz } 650916Sschwartz 651881Sjohnny 652881Sjohnny static int 653881Sjohnny pci_enable_intr(dev_info_t *pdip, dev_info_t *rdip, 654881Sjohnny ddi_intr_handle_impl_t *hdlp, uint32_t inum) 655881Sjohnny { 656881Sjohnny struct intrspec *ispec; 657916Sschwartz int irq; 658916Sschwartz ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 659881Sjohnny 660881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: hdlp %p inum %x\n", 661881Sjohnny (void *)hdlp, inum)); 662881Sjohnny 663881Sjohnny /* Translate the interrupt if needed */ 664881Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum); 665881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec) 666881Sjohnny ispec->intrspec_vec = inum; 667916Sschwartz ihdl_plat_datap->ip_ispecp = ispec; 668881Sjohnny 669881Sjohnny /* translate the interrupt if needed */ 670916Sschwartz (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq); 671916Sschwartz DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: priority=%x irq=%x\n", 672916Sschwartz hdlp->ih_pri, irq)); 673881Sjohnny 674881Sjohnny /* Add the interrupt handler */ 675881Sjohnny if (!add_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, 676916Sschwartz DEVI(rdip)->devi_name, irq, hdlp->ih_cb_arg1, 677916Sschwartz hdlp->ih_cb_arg2, &ihdl_plat_datap->ip_ticks, rdip)) 678881Sjohnny return (DDI_FAILURE); 679881Sjohnny 680916Sschwartz /* Note this really is an irq. */ 681916Sschwartz hdlp->ih_vector = (ushort_t)irq; 682916Sschwartz 683881Sjohnny return (DDI_SUCCESS); 684881Sjohnny } 685881Sjohnny 686881Sjohnny 687881Sjohnny static void 688881Sjohnny pci_disable_intr(dev_info_t *pdip, dev_info_t *rdip, 689881Sjohnny ddi_intr_handle_impl_t *hdlp, uint32_t inum) 690881Sjohnny { 691916Sschwartz int irq; 692881Sjohnny struct intrspec *ispec; 693916Sschwartz ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 694881Sjohnny 695881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_disable_intr: \n")); 696881Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum); 697881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec) 698881Sjohnny ispec->intrspec_vec = inum; 699916Sschwartz ihdl_plat_datap->ip_ispecp = ispec; 700881Sjohnny 701881Sjohnny /* translate the interrupt if needed */ 702916Sschwartz (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq); 703881Sjohnny 704881Sjohnny /* Disable the interrupt handler */ 705916Sschwartz rem_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, irq); 706916Sschwartz ihdl_plat_datap->ip_ispecp = NULL; 707881Sjohnny } 708881Sjohnny 709881Sjohnny /* 710881Sjohnny * Miscellaneous library function 711881Sjohnny */ 712881Sjohnny int 713881Sjohnny pci_common_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp) 714881Sjohnny { 715881Sjohnny int i; 716881Sjohnny int number; 717881Sjohnny int assigned_addr_len; 718881Sjohnny uint_t phys_hi = pci_rp->pci_phys_hi; 719881Sjohnny pci_regspec_t *assigned_addr; 720881Sjohnny 721881Sjohnny if (((phys_hi & PCI_REG_ADDR_M) == PCI_ADDR_CONFIG) || 722881Sjohnny (phys_hi & PCI_RELOCAT_B)) 723881Sjohnny return (DDI_SUCCESS); 724881Sjohnny 725881Sjohnny /* 726881Sjohnny * the "reg" property specifies relocatable, get and interpret the 727881Sjohnny * "assigned-addresses" property. 728881Sjohnny */ 729881Sjohnny if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 730881Sjohnny "assigned-addresses", (int **)&assigned_addr, 731881Sjohnny (uint_t *)&assigned_addr_len) != DDI_PROP_SUCCESS) 732881Sjohnny return (DDI_FAILURE); 733881Sjohnny 734881Sjohnny /* 735881Sjohnny * Scan the "assigned-addresses" for one that matches the specified 736881Sjohnny * "reg" property entry. 737881Sjohnny */ 738881Sjohnny phys_hi &= PCI_CONF_ADDR_MASK; 739881Sjohnny number = assigned_addr_len / (sizeof (pci_regspec_t) / sizeof (int)); 740881Sjohnny for (i = 0; i < number; i++) { 741881Sjohnny if ((assigned_addr[i].pci_phys_hi & PCI_CONF_ADDR_MASK) == 742881Sjohnny phys_hi) { 743881Sjohnny pci_rp->pci_phys_mid = assigned_addr[i].pci_phys_mid; 744881Sjohnny pci_rp->pci_phys_low = assigned_addr[i].pci_phys_low; 745881Sjohnny ddi_prop_free(assigned_addr); 746881Sjohnny return (DDI_SUCCESS); 747881Sjohnny } 748881Sjohnny } 749881Sjohnny 750881Sjohnny ddi_prop_free(assigned_addr); 751881Sjohnny return (DDI_FAILURE); 752881Sjohnny } 753881Sjohnny 754881Sjohnny 755881Sjohnny /* 756881Sjohnny * For pci_tools 757881Sjohnny */ 758881Sjohnny 759881Sjohnny int 760881Sjohnny pci_common_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg, 761881Sjohnny int mode, cred_t *credp, int *rvalp) 762881Sjohnny { 763881Sjohnny int rv = ENOTTY; 764881Sjohnny 765881Sjohnny minor_t minor = getminor(dev); 766881Sjohnny 767881Sjohnny switch (PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(minor)) { 768881Sjohnny case PCI_TOOL_REG_MINOR_NUM: 769881Sjohnny 770881Sjohnny switch (cmd) { 771881Sjohnny case PCITOOL_DEVICE_SET_REG: 772881Sjohnny case PCITOOL_DEVICE_GET_REG: 773881Sjohnny 774881Sjohnny /* Require full privileges. */ 775881Sjohnny if (secpolicy_kmdb(credp)) 776881Sjohnny rv = EPERM; 777881Sjohnny else 778881Sjohnny rv = pcitool_dev_reg_ops(dip, (void *)arg, 779881Sjohnny cmd, mode); 780881Sjohnny break; 781881Sjohnny 782881Sjohnny case PCITOOL_NEXUS_SET_REG: 783881Sjohnny case PCITOOL_NEXUS_GET_REG: 784881Sjohnny 785881Sjohnny /* Require full privileges. */ 786881Sjohnny if (secpolicy_kmdb(credp)) 787881Sjohnny rv = EPERM; 788881Sjohnny else 789881Sjohnny rv = pcitool_bus_reg_ops(dip, (void *)arg, 790881Sjohnny cmd, mode); 791881Sjohnny break; 792881Sjohnny } 793881Sjohnny break; 794881Sjohnny 795881Sjohnny case PCI_TOOL_INTR_MINOR_NUM: 796881Sjohnny 797881Sjohnny switch (cmd) { 798881Sjohnny case PCITOOL_DEVICE_SET_INTR: 799881Sjohnny 800881Sjohnny /* Require PRIV_SYS_RES_CONFIG, same as psradm */ 801881Sjohnny if (secpolicy_ponline(credp)) { 802881Sjohnny rv = EPERM; 803881Sjohnny break; 804881Sjohnny } 805881Sjohnny 806881Sjohnny /*FALLTHRU*/ 807881Sjohnny /* These require no special privileges. */ 808881Sjohnny case PCITOOL_DEVICE_GET_INTR: 809881Sjohnny case PCITOOL_DEVICE_NUM_INTR: 810881Sjohnny rv = pcitool_intr_admn(dip, (void *)arg, cmd, mode); 811881Sjohnny break; 812881Sjohnny } 813881Sjohnny break; 814881Sjohnny 815881Sjohnny /* 816881Sjohnny * All non-PCItool ioctls go through here, including: 817881Sjohnny * devctl ioctls with minor number PCIHP_DEVCTL_MINOR and 818881Sjohnny * those for attachment points with where minor number is the 819881Sjohnny * device number. 820881Sjohnny */ 821881Sjohnny default: 822881Sjohnny rv = (pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, 823881Sjohnny credp, rvalp); 824881Sjohnny break; 825881Sjohnny } 826881Sjohnny 827881Sjohnny return (rv); 828881Sjohnny } 8291083Sanish 8301083Sanish 8311083Sanish /* 8321083Sanish * These are the get and put functions to be shared with drivers. The 8331083Sanish * mutex locking is done inside the functions referenced, rather than 8341083Sanish * here, and is thus shared across PCI child drivers and any other 8351083Sanish * consumers of PCI config space (such as the ACPI subsystem). 8361083Sanish * 8371083Sanish * The configuration space addresses come in as pointers. This is fine on 8381083Sanish * a 32-bit system, where the VM space and configuration space are the same 8391083Sanish * size. It's not such a good idea on a 64-bit system, where memory 8401083Sanish * addresses are twice as large as configuration space addresses. At some 8411083Sanish * point in the call tree we need to take a stand and say "you are 32-bit 8421083Sanish * from this time forth", and this seems like a nice self-contained place. 8431083Sanish */ 8441083Sanish 8451083Sanish uint8_t 8461083Sanish pci_config_rd8(ddi_acc_impl_t *hdlp, uint8_t *addr) 8471083Sanish { 8481083Sanish pci_acc_cfblk_t *cfp; 8491083Sanish uint8_t rval; 8501083Sanish int reg; 8511083Sanish 8521083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 8531083Sanish 8541083Sanish reg = (int)(uintptr_t)addr; 8551083Sanish 8561083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 8571083Sanish 8581083Sanish rval = (*pci_getb_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum, 8591083Sanish reg); 8601083Sanish 8611083Sanish return (rval); 8621083Sanish } 8631083Sanish 8641083Sanish void 8651083Sanish pci_config_rep_rd8(ddi_acc_impl_t *hdlp, uint8_t *host_addr, 8661083Sanish uint8_t *dev_addr, size_t repcount, uint_t flags) 8671083Sanish { 8681083Sanish uint8_t *h, *d; 8691083Sanish 8701083Sanish h = host_addr; 8711083Sanish d = dev_addr; 8721083Sanish 8731083Sanish if (flags == DDI_DEV_AUTOINCR) 8741083Sanish for (; repcount; repcount--) 8751083Sanish *h++ = pci_config_rd8(hdlp, d++); 8761083Sanish else 8771083Sanish for (; repcount; repcount--) 8781083Sanish *h++ = pci_config_rd8(hdlp, d); 8791083Sanish } 8801083Sanish 8811083Sanish uint16_t 8821083Sanish pci_config_rd16(ddi_acc_impl_t *hdlp, uint16_t *addr) 8831083Sanish { 8841083Sanish pci_acc_cfblk_t *cfp; 8851083Sanish uint16_t rval; 8861083Sanish int reg; 8871083Sanish 8881083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 8891083Sanish 8901083Sanish reg = (int)(uintptr_t)addr; 8911083Sanish 8921083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 8931083Sanish 8941083Sanish rval = (*pci_getw_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum, 8951083Sanish reg); 8961083Sanish 8971083Sanish return (rval); 8981083Sanish } 8991083Sanish 9001083Sanish void 9011083Sanish pci_config_rep_rd16(ddi_acc_impl_t *hdlp, uint16_t *host_addr, 9021083Sanish uint16_t *dev_addr, size_t repcount, uint_t flags) 9031083Sanish { 9041083Sanish uint16_t *h, *d; 9051083Sanish 9061083Sanish h = host_addr; 9071083Sanish d = dev_addr; 9081083Sanish 9091083Sanish if (flags == DDI_DEV_AUTOINCR) 9101083Sanish for (; repcount; repcount--) 9111083Sanish *h++ = pci_config_rd16(hdlp, d++); 9121083Sanish else 9131083Sanish for (; repcount; repcount--) 9141083Sanish *h++ = pci_config_rd16(hdlp, d); 9151083Sanish } 9161083Sanish 9171083Sanish uint32_t 9181083Sanish pci_config_rd32(ddi_acc_impl_t *hdlp, uint32_t *addr) 9191083Sanish { 9201083Sanish pci_acc_cfblk_t *cfp; 9211083Sanish uint32_t rval; 9221083Sanish int reg; 9231083Sanish 9241083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 9251083Sanish 9261083Sanish reg = (int)(uintptr_t)addr; 9271083Sanish 9281083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 9291083Sanish 9301083Sanish rval = (*pci_getl_func)(cfp->c_busnum, cfp->c_devnum, 9311083Sanish cfp->c_funcnum, reg); 9321083Sanish 9331083Sanish return (rval); 9341083Sanish } 9351083Sanish 9361083Sanish void 9371083Sanish pci_config_rep_rd32(ddi_acc_impl_t *hdlp, uint32_t *host_addr, 9381083Sanish uint32_t *dev_addr, size_t repcount, uint_t flags) 9391083Sanish { 9401083Sanish uint32_t *h, *d; 9411083Sanish 9421083Sanish h = host_addr; 9431083Sanish d = dev_addr; 9441083Sanish 9451083Sanish if (flags == DDI_DEV_AUTOINCR) 9461083Sanish for (; repcount; repcount--) 9471083Sanish *h++ = pci_config_rd32(hdlp, d++); 9481083Sanish else 9491083Sanish for (; repcount; repcount--) 9501083Sanish *h++ = pci_config_rd32(hdlp, d); 9511083Sanish } 9521083Sanish 9531083Sanish 9541083Sanish void 9551083Sanish pci_config_wr8(ddi_acc_impl_t *hdlp, uint8_t *addr, uint8_t value) 9561083Sanish { 9571083Sanish pci_acc_cfblk_t *cfp; 9581083Sanish int reg; 9591083Sanish 9601083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 9611083Sanish 9621083Sanish reg = (int)(uintptr_t)addr; 9631083Sanish 9641083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 9651083Sanish 9661083Sanish (*pci_putb_func)(cfp->c_busnum, cfp->c_devnum, 9671083Sanish cfp->c_funcnum, reg, value); 9681083Sanish } 9691083Sanish 9701083Sanish void 9711083Sanish pci_config_rep_wr8(ddi_acc_impl_t *hdlp, uint8_t *host_addr, 9721083Sanish uint8_t *dev_addr, size_t repcount, uint_t flags) 9731083Sanish { 9741083Sanish uint8_t *h, *d; 9751083Sanish 9761083Sanish h = host_addr; 9771083Sanish d = dev_addr; 9781083Sanish 9791083Sanish if (flags == DDI_DEV_AUTOINCR) 9801083Sanish for (; repcount; repcount--) 9811083Sanish pci_config_wr8(hdlp, d++, *h++); 9821083Sanish else 9831083Sanish for (; repcount; repcount--) 9841083Sanish pci_config_wr8(hdlp, d, *h++); 9851083Sanish } 9861083Sanish 9871083Sanish void 9881083Sanish pci_config_wr16(ddi_acc_impl_t *hdlp, uint16_t *addr, uint16_t value) 9891083Sanish { 9901083Sanish pci_acc_cfblk_t *cfp; 9911083Sanish int reg; 9921083Sanish 9931083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 9941083Sanish 9951083Sanish reg = (int)(uintptr_t)addr; 9961083Sanish 9971083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 9981083Sanish 9991083Sanish (*pci_putw_func)(cfp->c_busnum, cfp->c_devnum, 10001083Sanish cfp->c_funcnum, reg, value); 10011083Sanish } 10021083Sanish 10031083Sanish void 10041083Sanish pci_config_rep_wr16(ddi_acc_impl_t *hdlp, uint16_t *host_addr, 10051083Sanish uint16_t *dev_addr, size_t repcount, uint_t flags) 10061083Sanish { 10071083Sanish uint16_t *h, *d; 10081083Sanish 10091083Sanish h = host_addr; 10101083Sanish d = dev_addr; 10111083Sanish 10121083Sanish if (flags == DDI_DEV_AUTOINCR) 10131083Sanish for (; repcount; repcount--) 10141083Sanish pci_config_wr16(hdlp, d++, *h++); 10151083Sanish else 10161083Sanish for (; repcount; repcount--) 10171083Sanish pci_config_wr16(hdlp, d, *h++); 10181083Sanish } 10191083Sanish 10201083Sanish void 10211083Sanish pci_config_wr32(ddi_acc_impl_t *hdlp, uint32_t *addr, uint32_t value) 10221083Sanish { 10231083Sanish pci_acc_cfblk_t *cfp; 10241083Sanish int reg; 10251083Sanish 10261083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 10271083Sanish 10281083Sanish reg = (int)(uintptr_t)addr; 10291083Sanish 10301083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 10311083Sanish 10321083Sanish (*pci_putl_func)(cfp->c_busnum, cfp->c_devnum, 10331083Sanish cfp->c_funcnum, reg, value); 10341083Sanish } 10351083Sanish 10361083Sanish void 10371083Sanish pci_config_rep_wr32(ddi_acc_impl_t *hdlp, uint32_t *host_addr, 10381083Sanish uint32_t *dev_addr, size_t repcount, uint_t flags) 10391083Sanish { 10401083Sanish uint32_t *h, *d; 10411083Sanish 10421083Sanish h = host_addr; 10431083Sanish d = dev_addr; 10441083Sanish 10451083Sanish if (flags == DDI_DEV_AUTOINCR) 10461083Sanish for (; repcount; repcount--) 10471083Sanish pci_config_wr32(hdlp, d++, *h++); 10481083Sanish else 10491083Sanish for (; repcount; repcount--) 10501083Sanish pci_config_wr32(hdlp, d, *h++); 10511083Sanish } 10521083Sanish 10531083Sanish uint64_t 10541083Sanish pci_config_rd64(ddi_acc_impl_t *hdlp, uint64_t *addr) 10551083Sanish { 10561083Sanish uint32_t lw_val; 10571083Sanish uint32_t hi_val; 10581083Sanish uint32_t *dp; 10591083Sanish uint64_t val; 10601083Sanish 10611083Sanish dp = (uint32_t *)addr; 10621083Sanish lw_val = pci_config_rd32(hdlp, dp); 10631083Sanish dp++; 10641083Sanish hi_val = pci_config_rd32(hdlp, dp); 10651083Sanish val = ((uint64_t)hi_val << 32) | lw_val; 10661083Sanish return (val); 10671083Sanish } 10681083Sanish 10691083Sanish void 10701083Sanish pci_config_wr64(ddi_acc_impl_t *hdlp, uint64_t *addr, uint64_t value) 10711083Sanish { 10721083Sanish uint32_t lw_val; 10731083Sanish uint32_t hi_val; 10741083Sanish uint32_t *dp; 10751083Sanish 10761083Sanish dp = (uint32_t *)addr; 10771083Sanish lw_val = (uint32_t)(value & 0xffffffff); 10781083Sanish hi_val = (uint32_t)(value >> 32); 10791083Sanish pci_config_wr32(hdlp, dp, lw_val); 10801083Sanish dp++; 10811083Sanish pci_config_wr32(hdlp, dp, hi_val); 10821083Sanish } 10831083Sanish 10841083Sanish void 10851083Sanish pci_config_rep_rd64(ddi_acc_impl_t *hdlp, uint64_t *host_addr, 10861083Sanish uint64_t *dev_addr, size_t repcount, uint_t flags) 10871083Sanish { 10881083Sanish if (flags == DDI_DEV_AUTOINCR) { 10891083Sanish for (; repcount; repcount--) 10901083Sanish *host_addr++ = pci_config_rd64(hdlp, dev_addr++); 10911083Sanish } else { 10921083Sanish for (; repcount; repcount--) 10931083Sanish *host_addr++ = pci_config_rd64(hdlp, dev_addr); 10941083Sanish } 10951083Sanish } 10961083Sanish 10971083Sanish void 10981083Sanish pci_config_rep_wr64(ddi_acc_impl_t *hdlp, uint64_t *host_addr, 10991083Sanish uint64_t *dev_addr, size_t repcount, uint_t flags) 11001083Sanish { 11011083Sanish if (flags == DDI_DEV_AUTOINCR) { 11021083Sanish for (; repcount; repcount--) 11031083Sanish pci_config_wr64(hdlp, host_addr++, *dev_addr++); 11041083Sanish } else { 11051083Sanish for (; repcount; repcount--) 11061083Sanish pci_config_wr64(hdlp, host_addr++, *dev_addr); 11071083Sanish } 11081083Sanish } 11091083Sanish 11101083Sanish 11111083Sanish /* 11121083Sanish * Enable Legacy PCI config space access for the following four north bridges 11131083Sanish * Host bridge: AMD HyperTransport Technology Configuration 11141083Sanish * Host bridge: AMD Address Map 11151083Sanish * Host bridge: AMD DRAM Controller 11161083Sanish * Host bridge: AMD Miscellaneous Control 11171083Sanish */ 11181083Sanish int 11191083Sanish is_amd_northbridge(dev_info_t *dip) 11201083Sanish { 11211083Sanish int vendor_id, device_id; 11221083Sanish 11231083Sanish vendor_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 11241083Sanish "vendor-id", -1); 11251083Sanish device_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 11261083Sanish "device-id", -1); 11271083Sanish 11281083Sanish if (IS_AMD_NTBRIDGE(vendor_id, device_id)) 11291083Sanish return (0); 11301083Sanish 11311083Sanish return (1); 11321083Sanish } 1133