1881Sjohnny /* 2881Sjohnny * CDDL HEADER START 3881Sjohnny * 4881Sjohnny * The contents of this file are subject to the terms of the 5881Sjohnny * Common Development and Distribution License, Version 1.0 only 6881Sjohnny * (the "License"). You may not use this file except in compliance 7881Sjohnny * with the License. 8881Sjohnny * 9881Sjohnny * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10881Sjohnny * or http://www.opensolaris.org/os/licensing. 11881Sjohnny * See the License for the specific language governing permissions 12881Sjohnny * and limitations under the License. 13881Sjohnny * 14881Sjohnny * When distributing Covered Code, include this CDDL HEADER in each 15881Sjohnny * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16881Sjohnny * If applicable, add the following below this CDDL HEADER, with the 17881Sjohnny * fields enclosed by brackets "[]" replaced with your own identifying 18881Sjohnny * information: Portions Copyright [yyyy] [name of copyright owner] 19881Sjohnny * 20881Sjohnny * CDDL HEADER END 21881Sjohnny */ 22881Sjohnny 23881Sjohnny /* 24881Sjohnny * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 25881Sjohnny * Use is subject to license terms. 26881Sjohnny */ 27881Sjohnny 28881Sjohnny #pragma ident "%Z%%M% %I% %E% SMI" 29881Sjohnny 30881Sjohnny /* 31881Sjohnny * File that has code which is common between pci(7d) and npe(7d) 32881Sjohnny * It shares the following: 33881Sjohnny * - interrupt code 34881Sjohnny * - pci_tools ioctl code 35881Sjohnny * - name_child code 36881Sjohnny * - set_parent_private_data code 37881Sjohnny */ 38881Sjohnny 39881Sjohnny #include <sys/conf.h> 40881Sjohnny #include <sys/pci.h> 41881Sjohnny #include <sys/sunndi.h> 42916Sschwartz #include <sys/mach_intr.h> 43881Sjohnny #include <sys/hotplug/pci/pcihp.h> 44881Sjohnny #include <sys/pci_intr_lib.h> 45881Sjohnny #include <sys/psm.h> 46881Sjohnny #include <sys/policy.h> 47881Sjohnny #include <sys/sysmacros.h> 48916Sschwartz #include <sys/clock.h> 49916Sschwartz #include <io/pcplusmp/apic.h> 50881Sjohnny #include <sys/pci_tools.h> 51881Sjohnny #include <io/pci/pci_var.h> 52881Sjohnny #include <io/pci/pci_tools_ext.h> 53881Sjohnny #include <io/pci/pci_common.h> 541083Sanish #include <sys/pci_cfgspace.h> 551083Sanish #include <sys/pci_impl.h> 56881Sjohnny 57881Sjohnny /* 58881Sjohnny * Function prototypes 59881Sjohnny */ 60881Sjohnny static int pci_get_priority(dev_info_t *, ddi_intr_handle_impl_t *, int *); 61881Sjohnny static int pci_get_nintrs(dev_info_t *, int, int *); 62881Sjohnny static int pci_enable_intr(dev_info_t *, dev_info_t *, 63881Sjohnny ddi_intr_handle_impl_t *, uint32_t); 64881Sjohnny static void pci_disable_intr(dev_info_t *, dev_info_t *, 65881Sjohnny ddi_intr_handle_impl_t *, uint32_t); 66881Sjohnny 67881Sjohnny /* Extern decalration for pcplusmp module */ 68881Sjohnny extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *, 69881Sjohnny psm_intr_op_t, int *); 70881Sjohnny 71881Sjohnny 72881Sjohnny /* 73881Sjohnny * pci_name_child: 74881Sjohnny * 75881Sjohnny * Assign the address portion of the node name 76881Sjohnny */ 77881Sjohnny int 78881Sjohnny pci_common_name_child(dev_info_t *child, char *name, int namelen) 79881Sjohnny { 80881Sjohnny int dev, func, length; 81881Sjohnny char **unit_addr; 82881Sjohnny uint_t n; 83881Sjohnny pci_regspec_t *pci_rp; 84881Sjohnny 85881Sjohnny if (ndi_dev_is_persistent_node(child) == 0) { 86881Sjohnny /* 87881Sjohnny * For .conf node, use "unit-address" property 88881Sjohnny */ 89881Sjohnny if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 90881Sjohnny DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) != 91881Sjohnny DDI_PROP_SUCCESS) { 92881Sjohnny cmn_err(CE_WARN, "cannot find unit-address in %s.conf", 93881Sjohnny ddi_get_name(child)); 94881Sjohnny return (DDI_FAILURE); 95881Sjohnny } 96881Sjohnny if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 97881Sjohnny cmn_err(CE_WARN, "unit-address property in %s.conf" 98881Sjohnny " not well-formed", ddi_get_name(child)); 99881Sjohnny ddi_prop_free(unit_addr); 100881Sjohnny return (DDI_FAILURE); 101881Sjohnny } 102881Sjohnny (void) snprintf(name, namelen, "%s", *unit_addr); 103881Sjohnny ddi_prop_free(unit_addr); 104881Sjohnny return (DDI_SUCCESS); 105881Sjohnny } 106881Sjohnny 107881Sjohnny if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 108881Sjohnny "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) { 109881Sjohnny cmn_err(CE_WARN, "cannot find reg property in %s", 110881Sjohnny ddi_get_name(child)); 111881Sjohnny return (DDI_FAILURE); 112881Sjohnny } 113881Sjohnny 114881Sjohnny /* copy the device identifications */ 115881Sjohnny dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 116881Sjohnny func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); 117881Sjohnny 118881Sjohnny /* 119881Sjohnny * free the memory allocated by ddi_prop_lookup_int_array 120881Sjohnny */ 121881Sjohnny ddi_prop_free(pci_rp); 122881Sjohnny 123881Sjohnny if (func != 0) { 124881Sjohnny (void) snprintf(name, namelen, "%x,%x", dev, func); 125881Sjohnny } else { 126881Sjohnny (void) snprintf(name, namelen, "%x", dev); 127881Sjohnny } 128881Sjohnny 129881Sjohnny return (DDI_SUCCESS); 130881Sjohnny } 131881Sjohnny 132881Sjohnny /* 133881Sjohnny * Interrupt related code: 134881Sjohnny * 135881Sjohnny * The following busop is common to npe and pci drivers 136881Sjohnny * bus_introp 137881Sjohnny */ 138881Sjohnny 139881Sjohnny /* 140881Sjohnny * Create the ddi_parent_private_data for a pseudo child. 141881Sjohnny */ 142881Sjohnny void 143881Sjohnny pci_common_set_parent_private_data(dev_info_t *dip) 144881Sjohnny { 145881Sjohnny struct ddi_parent_private_data *pdptr; 146881Sjohnny 147881Sjohnny pdptr = (struct ddi_parent_private_data *)kmem_zalloc( 148881Sjohnny (sizeof (struct ddi_parent_private_data) + 149881Sjohnny sizeof (struct intrspec)), KM_SLEEP); 150881Sjohnny pdptr->par_intr = (struct intrspec *)(pdptr + 1); 151881Sjohnny pdptr->par_nintr = 1; 152881Sjohnny ddi_set_parent_data(dip, pdptr); 153881Sjohnny } 154881Sjohnny 155881Sjohnny /* 156881Sjohnny * pci_get_priority: 157881Sjohnny * Figure out the priority of the device 158881Sjohnny */ 159881Sjohnny static int 160881Sjohnny pci_get_priority(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, int *pri) 161881Sjohnny { 162881Sjohnny struct intrspec *ispec; 163881Sjohnny 164881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_get_priority: dip = 0x%p, hdlp = %p\n", 165881Sjohnny (void *)dip, (void *)hdlp)); 166881Sjohnny 167881Sjohnny if ((ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip, 168881Sjohnny hdlp->ih_inum)) == NULL) { 169881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) { 170881Sjohnny int class = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 171881Sjohnny DDI_PROP_DONTPASS, "class-code", -1); 172881Sjohnny 173881Sjohnny *pri = (class == -1) ? 1 : pci_devclass_to_ipl(class); 174881Sjohnny pci_common_set_parent_private_data(hdlp->ih_dip); 175881Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip, 176881Sjohnny hdlp->ih_inum); 177881Sjohnny return (DDI_SUCCESS); 178881Sjohnny } 179881Sjohnny return (DDI_FAILURE); 180881Sjohnny } 181881Sjohnny 182881Sjohnny *pri = ispec->intrspec_pri; 183881Sjohnny return (DDI_SUCCESS); 184881Sjohnny } 185881Sjohnny 186881Sjohnny 187881Sjohnny /* 188881Sjohnny * pci_get_nintrs: 189881Sjohnny * Figure out how many interrupts the device supports 190881Sjohnny */ 191881Sjohnny static int 192881Sjohnny pci_get_nintrs(dev_info_t *dip, int type, int *nintrs) 193881Sjohnny { 194881Sjohnny int ret; 195881Sjohnny 196881Sjohnny *nintrs = 0; 197881Sjohnny 198881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(type)) 199881Sjohnny ret = pci_msi_get_nintrs(dip, type, nintrs); 200881Sjohnny else { 201881Sjohnny ret = DDI_FAILURE; 202881Sjohnny if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 203881Sjohnny "interrupts", -1) != -1) { 204881Sjohnny *nintrs = 1; 205881Sjohnny ret = DDI_SUCCESS; 206881Sjohnny } 207881Sjohnny } 208881Sjohnny 209881Sjohnny return (ret); 210881Sjohnny } 211881Sjohnny 212881Sjohnny static int pcie_pci_intr_pri_counter = 0; 213881Sjohnny 214881Sjohnny /* 215881Sjohnny * pci_common_intr_ops: bus_intr_op() function for interrupt support 216881Sjohnny */ 217881Sjohnny int 218881Sjohnny pci_common_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, 219881Sjohnny ddi_intr_handle_impl_t *hdlp, void *result) 220881Sjohnny { 221881Sjohnny int priority = 0; 222881Sjohnny int psm_status = 0; 223881Sjohnny int pci_status = 0; 224881Sjohnny int pci_rval, psm_rval = PSM_FAILURE; 225881Sjohnny int types = 0; 226881Sjohnny int pciepci = 0; 227881Sjohnny int i, j; 228881Sjohnny int behavior; 229881Sjohnny ddi_intrspec_t isp; 230881Sjohnny struct intrspec *ispec; 231881Sjohnny ddi_intr_handle_impl_t tmp_hdl; 232881Sjohnny ddi_intr_msix_t *msix_p; 233*1087Sschwartz ihdl_plat_t *ihdl_plat_datap; 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; 284881Sjohnny behavior = 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; 377*1087Sschwartz if (ispec) { 378881Sjohnny ispec->intrspec_func = hdlp->ih_cb_func; 379*1087Sschwartz ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 380*1087Sschwartz pci_kstat_create(&ihdl_plat_datap->ip_ksp, pdip, hdlp); 381*1087Sschwartz } 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; 387*1087Sschwartz if (ispec) { 388881Sjohnny ispec->intrspec_func = (uint_t (*)()) 0; 389*1087Sschwartz ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 390*1087Sschwartz if (ihdl_plat_datap->ip_ksp != NULL) 391*1087Sschwartz pci_kstat_delete(ihdl_plat_datap->ip_ksp); 392*1087Sschwartz } 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 473881Sjohnny for (i = 0; i < hdlp->ih_scratch1; i++) { 474881Sjohnny if (pci_enable_intr(pdip, rdip, hdlp, 475881Sjohnny hdlp->ih_inum + i) != DDI_SUCCESS) { 476881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: " 477881Sjohnny "pci_enable_intr failed for %d\n", i)); 478881Sjohnny for (j = 0; j < i; j++) 479881Sjohnny pci_disable_intr(pdip, rdip, hdlp, 480881Sjohnny hdlp->ih_inum + j); 481881Sjohnny return (DDI_FAILURE); 482881Sjohnny } 483881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 484881Sjohnny "BLOCKENABLE inum %x done\n", hdlp->ih_inum + i)); 485881Sjohnny } 486881Sjohnny break; 487881Sjohnny case DDI_INTROP_BLOCKDISABLE: 488881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 489881Sjohnny "BLOCKDISABLE\n")); 490881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_MSI) { 491881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKDISABLE: not MSI\n")); 492881Sjohnny return (DDI_FAILURE); 493881Sjohnny } 494881Sjohnny 495881Sjohnny /* Check if psm_intr_ops is present */ 496881Sjohnny if (psm_intr_ops == NULL) 497881Sjohnny return (DDI_FAILURE); 498881Sjohnny 499881Sjohnny for (i = 0; i < hdlp->ih_scratch1; i++) { 500881Sjohnny pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum + i); 501881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 502881Sjohnny "BLOCKDISABLE inum %x done\n", hdlp->ih_inum + i)); 503881Sjohnny } 504881Sjohnny break; 505881Sjohnny case DDI_INTROP_SETMASK: 506881Sjohnny case DDI_INTROP_CLRMASK: 507881Sjohnny /* 508881Sjohnny * First handle in the config space 509881Sjohnny */ 510881Sjohnny if (intr_op == DDI_INTROP_SETMASK) { 511881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 512881Sjohnny pci_status = pci_msi_set_mask(rdip, 513881Sjohnny hdlp->ih_type, hdlp->ih_inum); 514881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 515881Sjohnny pci_status = pci_intx_set_mask(rdip); 516881Sjohnny } else { 517881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 518881Sjohnny pci_status = pci_msi_clr_mask(rdip, 519881Sjohnny hdlp->ih_type, hdlp->ih_inum); 520881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 521881Sjohnny pci_status = pci_intx_clr_mask(rdip); 522881Sjohnny } 523881Sjohnny 524881Sjohnny /* For MSI/X; no need to check with pcplusmp */ 525881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 526881Sjohnny return (pci_status); 527881Sjohnny 528881Sjohnny /* For fixed interrupts only: handle config space first */ 529881Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_FIXED && 530881Sjohnny pci_status == DDI_SUCCESS) 531881Sjohnny break; 532881Sjohnny 533881Sjohnny /* For fixed interrupts only: confer with pcplusmp next */ 534881Sjohnny if (psm_intr_ops != NULL) { 535881Sjohnny /* If interrupt is shared; do nothing */ 536881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 537881Sjohnny PSM_INTR_OP_GET_SHARED, &psm_status); 538881Sjohnny 539881Sjohnny if (psm_rval == PSM_FAILURE || psm_status == 1) 540881Sjohnny return (pci_status); 541881Sjohnny 542881Sjohnny /* Now, pcplusmp should try to set/clear the mask */ 543881Sjohnny if (intr_op == DDI_INTROP_SETMASK) 544881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 545881Sjohnny PSM_INTR_OP_SET_MASK, NULL); 546881Sjohnny else 547881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 548881Sjohnny PSM_INTR_OP_CLEAR_MASK, NULL); 549881Sjohnny } 550881Sjohnny return ((psm_rval == PSM_FAILURE) ? DDI_FAILURE : DDI_SUCCESS); 551881Sjohnny case DDI_INTROP_GETPENDING: 552881Sjohnny /* 553881Sjohnny * First check the config space and/or 554881Sjohnny * MSI capability register(s) 555881Sjohnny */ 556881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 557881Sjohnny pci_rval = pci_msi_get_pending(rdip, hdlp->ih_type, 558881Sjohnny hdlp->ih_inum, &pci_status); 559881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 560881Sjohnny pci_rval = pci_intx_get_pending(rdip, &pci_status); 561881Sjohnny 562881Sjohnny /* On failure; next try with pcplusmp */ 563881Sjohnny if (pci_rval != DDI_SUCCESS && psm_intr_ops != NULL) 564881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 565881Sjohnny PSM_INTR_OP_GET_PENDING, &psm_status); 566881Sjohnny 567881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned " 568881Sjohnny "psm_rval = %x, psm_status = %x, pci_rval = %x, " 569881Sjohnny "pci_status = %x\n", psm_rval, psm_status, pci_rval, 570881Sjohnny pci_status)); 571881Sjohnny if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) { 572881Sjohnny *(int *)result = 0; 573881Sjohnny return (DDI_FAILURE); 574881Sjohnny } 575881Sjohnny 576881Sjohnny if (psm_rval != PSM_FAILURE) 577881Sjohnny *(int *)result = psm_status; 578881Sjohnny else if (pci_rval != DDI_FAILURE) 579881Sjohnny *(int *)result = pci_status; 580881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned = %x\n", 581881Sjohnny *(int *)result)); 582881Sjohnny break; 583881Sjohnny case DDI_INTROP_NAVAIL: 584881Sjohnny if ((psm_intr_ops != NULL) && (pci_get_priority(rdip, 585881Sjohnny hdlp, &priority) == DDI_SUCCESS)) { 586881Sjohnny /* Priority in the handle not initialized yet */ 587881Sjohnny hdlp->ih_pri = priority; 588881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 589881Sjohnny PSM_INTR_OP_NAVAIL_VECTORS, result); 590881Sjohnny } else { 591881Sjohnny *(int *)result = 1; 592881Sjohnny } 593881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: NAVAIL returned = %x\n", 594881Sjohnny *(int *)result)); 595881Sjohnny break; 596881Sjohnny default: 597881Sjohnny return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result)); 598881Sjohnny } 599881Sjohnny 600881Sjohnny return (DDI_SUCCESS); 601881Sjohnny } 602881Sjohnny 603916Sschwartz int 604916Sschwartz pci_get_intr_from_vecirq(apic_get_intr_t *intrinfo_p, 605916Sschwartz int vecirq, boolean_t is_irq) 606916Sschwartz { 607916Sschwartz ddi_intr_handle_impl_t get_info_ii_hdl; 608916Sschwartz 609916Sschwartz if (is_irq) 610916Sschwartz intrinfo_p->avgi_req_flags |= PSMGI_INTRBY_IRQ; 611916Sschwartz 612916Sschwartz /* 613916Sschwartz * For this locally-declared and used handle, ih_private will contain a 614916Sschwartz * pointer to apic_get_intr_t, not an ihdl_plat_t as used for 615916Sschwartz * global interrupt handling. 616916Sschwartz */ 617916Sschwartz get_info_ii_hdl.ih_private = intrinfo_p; 618916Sschwartz get_info_ii_hdl.ih_vector = (ushort_t)vecirq; 619916Sschwartz 620916Sschwartz if ((*psm_intr_ops)(NULL, &get_info_ii_hdl, 621916Sschwartz PSM_INTR_OP_GET_INTR, NULL) == PSM_FAILURE) 622916Sschwartz return (DDI_FAILURE); 623916Sschwartz 624916Sschwartz return (DDI_SUCCESS); 625916Sschwartz } 626916Sschwartz 627916Sschwartz 628916Sschwartz int 629916Sschwartz pci_get_cpu_from_vecirq(int vecirq, boolean_t is_irq) 630916Sschwartz { 631916Sschwartz int rval; 632916Sschwartz 633916Sschwartz apic_get_intr_t intrinfo; 634916Sschwartz intrinfo.avgi_req_flags = PSMGI_REQ_CPUID; 635916Sschwartz rval = pci_get_intr_from_vecirq(&intrinfo, vecirq, is_irq); 636916Sschwartz 637916Sschwartz if (rval == DDI_SUCCESS) 638916Sschwartz return (intrinfo.avgi_cpu_id); 639916Sschwartz else 640916Sschwartz return (-1); 641916Sschwartz } 642916Sschwartz 643881Sjohnny 644881Sjohnny static int 645881Sjohnny pci_enable_intr(dev_info_t *pdip, dev_info_t *rdip, 646881Sjohnny ddi_intr_handle_impl_t *hdlp, uint32_t inum) 647881Sjohnny { 648881Sjohnny struct intrspec *ispec; 649916Sschwartz int irq; 650916Sschwartz ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 651881Sjohnny 652881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: hdlp %p inum %x\n", 653881Sjohnny (void *)hdlp, inum)); 654881Sjohnny 655881Sjohnny /* Translate the interrupt if needed */ 656881Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum); 657881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec) 658881Sjohnny ispec->intrspec_vec = inum; 659916Sschwartz ihdl_plat_datap->ip_ispecp = ispec; 660881Sjohnny 661881Sjohnny /* translate the interrupt if needed */ 662916Sschwartz (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq); 663916Sschwartz DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: priority=%x irq=%x\n", 664916Sschwartz hdlp->ih_pri, irq)); 665881Sjohnny 666881Sjohnny /* Add the interrupt handler */ 667881Sjohnny if (!add_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, 668916Sschwartz DEVI(rdip)->devi_name, irq, hdlp->ih_cb_arg1, 669916Sschwartz hdlp->ih_cb_arg2, &ihdl_plat_datap->ip_ticks, rdip)) 670881Sjohnny return (DDI_FAILURE); 671881Sjohnny 672916Sschwartz /* Note this really is an irq. */ 673916Sschwartz hdlp->ih_vector = (ushort_t)irq; 674916Sschwartz 675881Sjohnny return (DDI_SUCCESS); 676881Sjohnny } 677881Sjohnny 678881Sjohnny 679881Sjohnny static void 680881Sjohnny pci_disable_intr(dev_info_t *pdip, dev_info_t *rdip, 681881Sjohnny ddi_intr_handle_impl_t *hdlp, uint32_t inum) 682881Sjohnny { 683916Sschwartz int irq; 684881Sjohnny struct intrspec *ispec; 685916Sschwartz ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 686881Sjohnny 687881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_disable_intr: \n")); 688881Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum); 689881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec) 690881Sjohnny ispec->intrspec_vec = inum; 691916Sschwartz ihdl_plat_datap->ip_ispecp = ispec; 692881Sjohnny 693881Sjohnny /* translate the interrupt if needed */ 694916Sschwartz (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq); 695881Sjohnny 696881Sjohnny /* Disable the interrupt handler */ 697916Sschwartz rem_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, irq); 698916Sschwartz ihdl_plat_datap->ip_ispecp = NULL; 699881Sjohnny } 700881Sjohnny 701881Sjohnny /* 702881Sjohnny * Miscellaneous library function 703881Sjohnny */ 704881Sjohnny int 705881Sjohnny pci_common_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp) 706881Sjohnny { 707881Sjohnny int i; 708881Sjohnny int number; 709881Sjohnny int assigned_addr_len; 710881Sjohnny uint_t phys_hi = pci_rp->pci_phys_hi; 711881Sjohnny pci_regspec_t *assigned_addr; 712881Sjohnny 713881Sjohnny if (((phys_hi & PCI_REG_ADDR_M) == PCI_ADDR_CONFIG) || 714881Sjohnny (phys_hi & PCI_RELOCAT_B)) 715881Sjohnny return (DDI_SUCCESS); 716881Sjohnny 717881Sjohnny /* 718881Sjohnny * the "reg" property specifies relocatable, get and interpret the 719881Sjohnny * "assigned-addresses" property. 720881Sjohnny */ 721881Sjohnny if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 722881Sjohnny "assigned-addresses", (int **)&assigned_addr, 723881Sjohnny (uint_t *)&assigned_addr_len) != DDI_PROP_SUCCESS) 724881Sjohnny return (DDI_FAILURE); 725881Sjohnny 726881Sjohnny /* 727881Sjohnny * Scan the "assigned-addresses" for one that matches the specified 728881Sjohnny * "reg" property entry. 729881Sjohnny */ 730881Sjohnny phys_hi &= PCI_CONF_ADDR_MASK; 731881Sjohnny number = assigned_addr_len / (sizeof (pci_regspec_t) / sizeof (int)); 732881Sjohnny for (i = 0; i < number; i++) { 733881Sjohnny if ((assigned_addr[i].pci_phys_hi & PCI_CONF_ADDR_MASK) == 734881Sjohnny phys_hi) { 735881Sjohnny pci_rp->pci_phys_mid = assigned_addr[i].pci_phys_mid; 736881Sjohnny pci_rp->pci_phys_low = assigned_addr[i].pci_phys_low; 737881Sjohnny ddi_prop_free(assigned_addr); 738881Sjohnny return (DDI_SUCCESS); 739881Sjohnny } 740881Sjohnny } 741881Sjohnny 742881Sjohnny ddi_prop_free(assigned_addr); 743881Sjohnny return (DDI_FAILURE); 744881Sjohnny } 745881Sjohnny 746881Sjohnny 747881Sjohnny /* 748881Sjohnny * For pci_tools 749881Sjohnny */ 750881Sjohnny 751881Sjohnny int 752881Sjohnny pci_common_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg, 753881Sjohnny int mode, cred_t *credp, int *rvalp) 754881Sjohnny { 755881Sjohnny int rv = ENOTTY; 756881Sjohnny 757881Sjohnny minor_t minor = getminor(dev); 758881Sjohnny 759881Sjohnny switch (PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(minor)) { 760881Sjohnny case PCI_TOOL_REG_MINOR_NUM: 761881Sjohnny 762881Sjohnny switch (cmd) { 763881Sjohnny case PCITOOL_DEVICE_SET_REG: 764881Sjohnny case PCITOOL_DEVICE_GET_REG: 765881Sjohnny 766881Sjohnny /* Require full privileges. */ 767881Sjohnny if (secpolicy_kmdb(credp)) 768881Sjohnny rv = EPERM; 769881Sjohnny else 770881Sjohnny rv = pcitool_dev_reg_ops(dip, (void *)arg, 771881Sjohnny cmd, mode); 772881Sjohnny break; 773881Sjohnny 774881Sjohnny case PCITOOL_NEXUS_SET_REG: 775881Sjohnny case PCITOOL_NEXUS_GET_REG: 776881Sjohnny 777881Sjohnny /* Require full privileges. */ 778881Sjohnny if (secpolicy_kmdb(credp)) 779881Sjohnny rv = EPERM; 780881Sjohnny else 781881Sjohnny rv = pcitool_bus_reg_ops(dip, (void *)arg, 782881Sjohnny cmd, mode); 783881Sjohnny break; 784881Sjohnny } 785881Sjohnny break; 786881Sjohnny 787881Sjohnny case PCI_TOOL_INTR_MINOR_NUM: 788881Sjohnny 789881Sjohnny switch (cmd) { 790881Sjohnny case PCITOOL_DEVICE_SET_INTR: 791881Sjohnny 792881Sjohnny /* Require PRIV_SYS_RES_CONFIG, same as psradm */ 793881Sjohnny if (secpolicy_ponline(credp)) { 794881Sjohnny rv = EPERM; 795881Sjohnny break; 796881Sjohnny } 797881Sjohnny 798881Sjohnny /*FALLTHRU*/ 799881Sjohnny /* These require no special privileges. */ 800881Sjohnny case PCITOOL_DEVICE_GET_INTR: 801881Sjohnny case PCITOOL_DEVICE_NUM_INTR: 802881Sjohnny rv = pcitool_intr_admn(dip, (void *)arg, cmd, mode); 803881Sjohnny break; 804881Sjohnny } 805881Sjohnny break; 806881Sjohnny 807881Sjohnny /* 808881Sjohnny * All non-PCItool ioctls go through here, including: 809881Sjohnny * devctl ioctls with minor number PCIHP_DEVCTL_MINOR and 810881Sjohnny * those for attachment points with where minor number is the 811881Sjohnny * device number. 812881Sjohnny */ 813881Sjohnny default: 814881Sjohnny rv = (pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, 815881Sjohnny credp, rvalp); 816881Sjohnny break; 817881Sjohnny } 818881Sjohnny 819881Sjohnny return (rv); 820881Sjohnny } 8211083Sanish 8221083Sanish 8231083Sanish /* 8241083Sanish * These are the get and put functions to be shared with drivers. The 8251083Sanish * mutex locking is done inside the functions referenced, rather than 8261083Sanish * here, and is thus shared across PCI child drivers and any other 8271083Sanish * consumers of PCI config space (such as the ACPI subsystem). 8281083Sanish * 8291083Sanish * The configuration space addresses come in as pointers. This is fine on 8301083Sanish * a 32-bit system, where the VM space and configuration space are the same 8311083Sanish * size. It's not such a good idea on a 64-bit system, where memory 8321083Sanish * addresses are twice as large as configuration space addresses. At some 8331083Sanish * point in the call tree we need to take a stand and say "you are 32-bit 8341083Sanish * from this time forth", and this seems like a nice self-contained place. 8351083Sanish */ 8361083Sanish 8371083Sanish uint8_t 8381083Sanish pci_config_rd8(ddi_acc_impl_t *hdlp, uint8_t *addr) 8391083Sanish { 8401083Sanish pci_acc_cfblk_t *cfp; 8411083Sanish uint8_t rval; 8421083Sanish int reg; 8431083Sanish 8441083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 8451083Sanish 8461083Sanish reg = (int)(uintptr_t)addr; 8471083Sanish 8481083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 8491083Sanish 8501083Sanish rval = (*pci_getb_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum, 8511083Sanish reg); 8521083Sanish 8531083Sanish return (rval); 8541083Sanish } 8551083Sanish 8561083Sanish void 8571083Sanish pci_config_rep_rd8(ddi_acc_impl_t *hdlp, uint8_t *host_addr, 8581083Sanish uint8_t *dev_addr, size_t repcount, uint_t flags) 8591083Sanish { 8601083Sanish uint8_t *h, *d; 8611083Sanish 8621083Sanish h = host_addr; 8631083Sanish d = dev_addr; 8641083Sanish 8651083Sanish if (flags == DDI_DEV_AUTOINCR) 8661083Sanish for (; repcount; repcount--) 8671083Sanish *h++ = pci_config_rd8(hdlp, d++); 8681083Sanish else 8691083Sanish for (; repcount; repcount--) 8701083Sanish *h++ = pci_config_rd8(hdlp, d); 8711083Sanish } 8721083Sanish 8731083Sanish uint16_t 8741083Sanish pci_config_rd16(ddi_acc_impl_t *hdlp, uint16_t *addr) 8751083Sanish { 8761083Sanish pci_acc_cfblk_t *cfp; 8771083Sanish uint16_t rval; 8781083Sanish int reg; 8791083Sanish 8801083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 8811083Sanish 8821083Sanish reg = (int)(uintptr_t)addr; 8831083Sanish 8841083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 8851083Sanish 8861083Sanish rval = (*pci_getw_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum, 8871083Sanish reg); 8881083Sanish 8891083Sanish return (rval); 8901083Sanish } 8911083Sanish 8921083Sanish void 8931083Sanish pci_config_rep_rd16(ddi_acc_impl_t *hdlp, uint16_t *host_addr, 8941083Sanish uint16_t *dev_addr, size_t repcount, uint_t flags) 8951083Sanish { 8961083Sanish uint16_t *h, *d; 8971083Sanish 8981083Sanish h = host_addr; 8991083Sanish d = dev_addr; 9001083Sanish 9011083Sanish if (flags == DDI_DEV_AUTOINCR) 9021083Sanish for (; repcount; repcount--) 9031083Sanish *h++ = pci_config_rd16(hdlp, d++); 9041083Sanish else 9051083Sanish for (; repcount; repcount--) 9061083Sanish *h++ = pci_config_rd16(hdlp, d); 9071083Sanish } 9081083Sanish 9091083Sanish uint32_t 9101083Sanish pci_config_rd32(ddi_acc_impl_t *hdlp, uint32_t *addr) 9111083Sanish { 9121083Sanish pci_acc_cfblk_t *cfp; 9131083Sanish uint32_t rval; 9141083Sanish int reg; 9151083Sanish 9161083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 9171083Sanish 9181083Sanish reg = (int)(uintptr_t)addr; 9191083Sanish 9201083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 9211083Sanish 9221083Sanish rval = (*pci_getl_func)(cfp->c_busnum, cfp->c_devnum, 9231083Sanish cfp->c_funcnum, reg); 9241083Sanish 9251083Sanish return (rval); 9261083Sanish } 9271083Sanish 9281083Sanish void 9291083Sanish pci_config_rep_rd32(ddi_acc_impl_t *hdlp, uint32_t *host_addr, 9301083Sanish uint32_t *dev_addr, size_t repcount, uint_t flags) 9311083Sanish { 9321083Sanish uint32_t *h, *d; 9331083Sanish 9341083Sanish h = host_addr; 9351083Sanish d = dev_addr; 9361083Sanish 9371083Sanish if (flags == DDI_DEV_AUTOINCR) 9381083Sanish for (; repcount; repcount--) 9391083Sanish *h++ = pci_config_rd32(hdlp, d++); 9401083Sanish else 9411083Sanish for (; repcount; repcount--) 9421083Sanish *h++ = pci_config_rd32(hdlp, d); 9431083Sanish } 9441083Sanish 9451083Sanish 9461083Sanish void 9471083Sanish pci_config_wr8(ddi_acc_impl_t *hdlp, uint8_t *addr, uint8_t value) 9481083Sanish { 9491083Sanish pci_acc_cfblk_t *cfp; 9501083Sanish int reg; 9511083Sanish 9521083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 9531083Sanish 9541083Sanish reg = (int)(uintptr_t)addr; 9551083Sanish 9561083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 9571083Sanish 9581083Sanish (*pci_putb_func)(cfp->c_busnum, cfp->c_devnum, 9591083Sanish cfp->c_funcnum, reg, value); 9601083Sanish } 9611083Sanish 9621083Sanish void 9631083Sanish pci_config_rep_wr8(ddi_acc_impl_t *hdlp, uint8_t *host_addr, 9641083Sanish uint8_t *dev_addr, size_t repcount, uint_t flags) 9651083Sanish { 9661083Sanish uint8_t *h, *d; 9671083Sanish 9681083Sanish h = host_addr; 9691083Sanish d = dev_addr; 9701083Sanish 9711083Sanish if (flags == DDI_DEV_AUTOINCR) 9721083Sanish for (; repcount; repcount--) 9731083Sanish pci_config_wr8(hdlp, d++, *h++); 9741083Sanish else 9751083Sanish for (; repcount; repcount--) 9761083Sanish pci_config_wr8(hdlp, d, *h++); 9771083Sanish } 9781083Sanish 9791083Sanish void 9801083Sanish pci_config_wr16(ddi_acc_impl_t *hdlp, uint16_t *addr, uint16_t value) 9811083Sanish { 9821083Sanish pci_acc_cfblk_t *cfp; 9831083Sanish int reg; 9841083Sanish 9851083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 9861083Sanish 9871083Sanish reg = (int)(uintptr_t)addr; 9881083Sanish 9891083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 9901083Sanish 9911083Sanish (*pci_putw_func)(cfp->c_busnum, cfp->c_devnum, 9921083Sanish cfp->c_funcnum, reg, value); 9931083Sanish } 9941083Sanish 9951083Sanish void 9961083Sanish pci_config_rep_wr16(ddi_acc_impl_t *hdlp, uint16_t *host_addr, 9971083Sanish uint16_t *dev_addr, size_t repcount, uint_t flags) 9981083Sanish { 9991083Sanish uint16_t *h, *d; 10001083Sanish 10011083Sanish h = host_addr; 10021083Sanish d = dev_addr; 10031083Sanish 10041083Sanish if (flags == DDI_DEV_AUTOINCR) 10051083Sanish for (; repcount; repcount--) 10061083Sanish pci_config_wr16(hdlp, d++, *h++); 10071083Sanish else 10081083Sanish for (; repcount; repcount--) 10091083Sanish pci_config_wr16(hdlp, d, *h++); 10101083Sanish } 10111083Sanish 10121083Sanish void 10131083Sanish pci_config_wr32(ddi_acc_impl_t *hdlp, uint32_t *addr, uint32_t value) 10141083Sanish { 10151083Sanish pci_acc_cfblk_t *cfp; 10161083Sanish int reg; 10171083Sanish 10181083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 10191083Sanish 10201083Sanish reg = (int)(uintptr_t)addr; 10211083Sanish 10221083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 10231083Sanish 10241083Sanish (*pci_putl_func)(cfp->c_busnum, cfp->c_devnum, 10251083Sanish cfp->c_funcnum, reg, value); 10261083Sanish } 10271083Sanish 10281083Sanish void 10291083Sanish pci_config_rep_wr32(ddi_acc_impl_t *hdlp, uint32_t *host_addr, 10301083Sanish uint32_t *dev_addr, size_t repcount, uint_t flags) 10311083Sanish { 10321083Sanish uint32_t *h, *d; 10331083Sanish 10341083Sanish h = host_addr; 10351083Sanish d = dev_addr; 10361083Sanish 10371083Sanish if (flags == DDI_DEV_AUTOINCR) 10381083Sanish for (; repcount; repcount--) 10391083Sanish pci_config_wr32(hdlp, d++, *h++); 10401083Sanish else 10411083Sanish for (; repcount; repcount--) 10421083Sanish pci_config_wr32(hdlp, d, *h++); 10431083Sanish } 10441083Sanish 10451083Sanish uint64_t 10461083Sanish pci_config_rd64(ddi_acc_impl_t *hdlp, uint64_t *addr) 10471083Sanish { 10481083Sanish uint32_t lw_val; 10491083Sanish uint32_t hi_val; 10501083Sanish uint32_t *dp; 10511083Sanish uint64_t val; 10521083Sanish 10531083Sanish dp = (uint32_t *)addr; 10541083Sanish lw_val = pci_config_rd32(hdlp, dp); 10551083Sanish dp++; 10561083Sanish hi_val = pci_config_rd32(hdlp, dp); 10571083Sanish val = ((uint64_t)hi_val << 32) | lw_val; 10581083Sanish return (val); 10591083Sanish } 10601083Sanish 10611083Sanish void 10621083Sanish pci_config_wr64(ddi_acc_impl_t *hdlp, uint64_t *addr, uint64_t value) 10631083Sanish { 10641083Sanish uint32_t lw_val; 10651083Sanish uint32_t hi_val; 10661083Sanish uint32_t *dp; 10671083Sanish 10681083Sanish dp = (uint32_t *)addr; 10691083Sanish lw_val = (uint32_t)(value & 0xffffffff); 10701083Sanish hi_val = (uint32_t)(value >> 32); 10711083Sanish pci_config_wr32(hdlp, dp, lw_val); 10721083Sanish dp++; 10731083Sanish pci_config_wr32(hdlp, dp, hi_val); 10741083Sanish } 10751083Sanish 10761083Sanish void 10771083Sanish pci_config_rep_rd64(ddi_acc_impl_t *hdlp, uint64_t *host_addr, 10781083Sanish uint64_t *dev_addr, size_t repcount, uint_t flags) 10791083Sanish { 10801083Sanish if (flags == DDI_DEV_AUTOINCR) { 10811083Sanish for (; repcount; repcount--) 10821083Sanish *host_addr++ = pci_config_rd64(hdlp, dev_addr++); 10831083Sanish } else { 10841083Sanish for (; repcount; repcount--) 10851083Sanish *host_addr++ = pci_config_rd64(hdlp, dev_addr); 10861083Sanish } 10871083Sanish } 10881083Sanish 10891083Sanish void 10901083Sanish pci_config_rep_wr64(ddi_acc_impl_t *hdlp, uint64_t *host_addr, 10911083Sanish uint64_t *dev_addr, size_t repcount, uint_t flags) 10921083Sanish { 10931083Sanish if (flags == DDI_DEV_AUTOINCR) { 10941083Sanish for (; repcount; repcount--) 10951083Sanish pci_config_wr64(hdlp, host_addr++, *dev_addr++); 10961083Sanish } else { 10971083Sanish for (; repcount; repcount--) 10981083Sanish pci_config_wr64(hdlp, host_addr++, *dev_addr); 10991083Sanish } 11001083Sanish } 11011083Sanish 11021083Sanish 11031083Sanish /* 11041083Sanish * Enable Legacy PCI config space access for the following four north bridges 11051083Sanish * Host bridge: AMD HyperTransport Technology Configuration 11061083Sanish * Host bridge: AMD Address Map 11071083Sanish * Host bridge: AMD DRAM Controller 11081083Sanish * Host bridge: AMD Miscellaneous Control 11091083Sanish */ 11101083Sanish int 11111083Sanish is_amd_northbridge(dev_info_t *dip) 11121083Sanish { 11131083Sanish int vendor_id, device_id; 11141083Sanish 11151083Sanish vendor_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 11161083Sanish "vendor-id", -1); 11171083Sanish device_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 11181083Sanish "device-id", -1); 11191083Sanish 11201083Sanish if (IS_AMD_NTBRIDGE(vendor_id, device_id)) 11211083Sanish return (0); 11221083Sanish 11231083Sanish return (1); 11241083Sanish } 1125