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) { 250*1725Segillett /* 251*1725Segillett * Only support MSI for now, OR it in 252*1725Segillett */ 253*1725Segillett *(int *)result |= (types & DDI_INTR_TYPE_MSI); 254881Sjohnny 255881Sjohnny tmp_hdl.ih_type = *(int *)result; 256881Sjohnny (void) (*psm_intr_ops)(rdip, &tmp_hdl, 257881Sjohnny PSM_INTR_OP_CHECK_MSI, result); 258881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 259881Sjohnny "rdip: 0x%p supported types: 0x%x\n", (void *)rdip, 260881Sjohnny *(int *)result)); 261881Sjohnny } 262881Sjohnny break; 263881Sjohnny case DDI_INTROP_NINTRS: 264881Sjohnny if (pci_get_nintrs(rdip, hdlp->ih_type, result) != DDI_SUCCESS) 265881Sjohnny return (DDI_FAILURE); 266881Sjohnny break; 267881Sjohnny case DDI_INTROP_ALLOC: 268881Sjohnny /* 269881Sjohnny * MSI or MSIX (figure out number of vectors available) 270881Sjohnny * FIXED interrupts: just return available interrupts 271881Sjohnny */ 272881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && 273881Sjohnny (psm_intr_ops != NULL) && 274881Sjohnny (pci_get_priority(rdip, hdlp, &priority) == DDI_SUCCESS)) { 275881Sjohnny /* 276881Sjohnny * Following check is a special case for 'pcie_pci'. 277881Sjohnny * This makes sure vectors with the right priority 278881Sjohnny * are allocated for pcie_pci during ALLOC time. 279881Sjohnny */ 280881Sjohnny if (strcmp(ddi_driver_name(rdip), "pcie_pci") == 0) { 281881Sjohnny hdlp->ih_pri = 282881Sjohnny (pcie_pci_intr_pri_counter % 2) ? 4 : 7; 283881Sjohnny pciepci = 1; 284881Sjohnny } else 285881Sjohnny hdlp->ih_pri = priority; 2861717Swesolows behavior = (int)(uintptr_t)hdlp->ih_scratch2; 287881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 288881Sjohnny PSM_INTR_OP_ALLOC_VECTORS, result); 289881Sjohnny 290881Sjohnny /* verify behavior flag and take appropriate action */ 291881Sjohnny if ((behavior == DDI_INTR_ALLOC_STRICT) && 292881Sjohnny (*(int *)result < hdlp->ih_scratch1)) { 293881Sjohnny DDI_INTR_NEXDBG((CE_CONT, 294881Sjohnny "pci_common_intr_ops: behavior %x, " 295881Sjohnny "couldn't get enough intrs\n", behavior)); 296881Sjohnny hdlp->ih_scratch1 = *(int *)result; 297881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 298881Sjohnny PSM_INTR_OP_FREE_VECTORS, NULL); 299881Sjohnny return (DDI_EAGAIN); 300881Sjohnny } 301881Sjohnny 302881Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) { 303881Sjohnny if (!(msix_p = i_ddi_get_msix(hdlp->ih_dip))) { 304881Sjohnny msix_p = pci_msix_init(hdlp->ih_dip); 305881Sjohnny if (msix_p) 306881Sjohnny i_ddi_set_msix(hdlp->ih_dip, 307881Sjohnny msix_p); 308881Sjohnny } 309881Sjohnny } 310881Sjohnny 311881Sjohnny if (pciepci) { 312881Sjohnny /* update priority in ispec */ 313881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, 314881Sjohnny (int)hdlp->ih_inum); 315881Sjohnny ispec = (struct intrspec *)isp; 316881Sjohnny if (ispec) 317881Sjohnny ispec->intrspec_pri = hdlp->ih_pri; 318881Sjohnny ++pcie_pci_intr_pri_counter; 319881Sjohnny } 320881Sjohnny 321881Sjohnny } else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) { 322881Sjohnny /* Figure out if this device supports MASKING */ 323881Sjohnny pci_rval = pci_intx_get_cap(rdip, &pci_status); 324881Sjohnny if (pci_rval == DDI_SUCCESS && pci_status) 325881Sjohnny hdlp->ih_cap |= pci_status; 326881Sjohnny *(int *)result = 1; /* DDI_INTR_TYPE_FIXED */ 327881Sjohnny } else 328881Sjohnny return (DDI_FAILURE); 329881Sjohnny break; 330881Sjohnny case DDI_INTROP_FREE: 331881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && 332881Sjohnny (psm_intr_ops != NULL)) { 333881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 334881Sjohnny PSM_INTR_OP_FREE_VECTORS, NULL); 335881Sjohnny 336881Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) { 337881Sjohnny msix_p = i_ddi_get_msix(hdlp->ih_dip); 338881Sjohnny if (msix_p && 339*1725Segillett i_ddi_intr_get_current_nintrs(hdlp->ih_dip) 340*1725Segillett == 0) { 341881Sjohnny pci_msix_fini(msix_p); 342881Sjohnny i_ddi_set_msix(hdlp->ih_dip, NULL); 343881Sjohnny } 344881Sjohnny } 345881Sjohnny } 346881Sjohnny break; 347881Sjohnny case DDI_INTROP_GETPRI: 348881Sjohnny /* Get the priority */ 349881Sjohnny if (pci_get_priority(rdip, hdlp, &priority) != DDI_SUCCESS) 350881Sjohnny return (DDI_FAILURE); 351881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 352881Sjohnny "priority = 0x%x\n", priority)); 353881Sjohnny *(int *)result = priority; 354881Sjohnny break; 355881Sjohnny case DDI_INTROP_SETPRI: 356881Sjohnny /* Validate the interrupt priority passed */ 357881Sjohnny if (*(int *)result > LOCK_LEVEL) 358881Sjohnny return (DDI_FAILURE); 359881Sjohnny 360881Sjohnny /* Ensure that PSM is all initialized */ 361881Sjohnny if (psm_intr_ops == NULL) 362881Sjohnny return (DDI_FAILURE); 363881Sjohnny 364881Sjohnny /* Change the priority */ 365881Sjohnny if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_PRI, result) == 366881Sjohnny PSM_FAILURE) 367881Sjohnny return (DDI_FAILURE); 368881Sjohnny 369881Sjohnny /* update ispec */ 370881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 371881Sjohnny ispec = (struct intrspec *)isp; 372881Sjohnny if (ispec) 373881Sjohnny ispec->intrspec_pri = *(int *)result; 374881Sjohnny break; 375881Sjohnny case DDI_INTROP_ADDISR: 376881Sjohnny /* update ispec */ 377881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 378881Sjohnny ispec = (struct intrspec *)isp; 3791087Sschwartz if (ispec) { 380881Sjohnny ispec->intrspec_func = hdlp->ih_cb_func; 3811087Sschwartz ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 3821087Sschwartz pci_kstat_create(&ihdl_plat_datap->ip_ksp, pdip, hdlp); 3831087Sschwartz } 384881Sjohnny break; 385881Sjohnny case DDI_INTROP_REMISR: 386881Sjohnny /* Get the interrupt structure pointer */ 387881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 388881Sjohnny ispec = (struct intrspec *)isp; 3891087Sschwartz if (ispec) { 390881Sjohnny ispec->intrspec_func = (uint_t (*)()) 0; 3911087Sschwartz ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 3921087Sschwartz if (ihdl_plat_datap->ip_ksp != NULL) 3931087Sschwartz pci_kstat_delete(ihdl_plat_datap->ip_ksp); 3941087Sschwartz } 395881Sjohnny break; 396881Sjohnny case DDI_INTROP_GETCAP: 397881Sjohnny /* 398881Sjohnny * First check the config space and/or 399881Sjohnny * MSI capability register(s) 400881Sjohnny */ 401881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 402881Sjohnny pci_rval = pci_msi_get_cap(rdip, hdlp->ih_type, 403881Sjohnny &pci_status); 404881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 405881Sjohnny pci_rval = pci_intx_get_cap(rdip, &pci_status); 406881Sjohnny 407881Sjohnny /* next check with pcplusmp */ 408881Sjohnny if (psm_intr_ops != NULL) 409881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 410881Sjohnny PSM_INTR_OP_GET_CAP, &psm_status); 411881Sjohnny 412881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned psm_rval = %x, " 413881Sjohnny "psm_status = %x, pci_rval = %x, pci_status = %x\n", 414881Sjohnny psm_rval, psm_status, pci_rval, pci_status)); 415881Sjohnny 416881Sjohnny if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) { 417881Sjohnny *(int *)result = 0; 418881Sjohnny return (DDI_FAILURE); 419881Sjohnny } 420881Sjohnny 421881Sjohnny if (psm_rval == PSM_SUCCESS) 422881Sjohnny *(int *)result = psm_status; 423881Sjohnny 424881Sjohnny if (pci_rval == DDI_SUCCESS) 425881Sjohnny *(int *)result |= pci_status; 426881Sjohnny 427881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned = %x\n", 428881Sjohnny *(int *)result)); 429881Sjohnny break; 430881Sjohnny case DDI_INTROP_SETCAP: 431881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 432881Sjohnny "SETCAP cap=0x%x\n", *(int *)result)); 433881Sjohnny if (psm_intr_ops == NULL) 434881Sjohnny return (DDI_FAILURE); 435881Sjohnny 436881Sjohnny if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) { 437881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "GETCAP: psm_intr_ops" 438881Sjohnny " returned failure\n")); 439881Sjohnny return (DDI_FAILURE); 440881Sjohnny } 441881Sjohnny break; 442881Sjohnny case DDI_INTROP_ENABLE: 443881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE\n")); 444881Sjohnny if (psm_intr_ops == NULL) 445881Sjohnny return (DDI_FAILURE); 446881Sjohnny 447881Sjohnny if (pci_enable_intr(pdip, rdip, hdlp, hdlp->ih_inum) != 448881Sjohnny DDI_SUCCESS) 449881Sjohnny return (DDI_FAILURE); 450881Sjohnny 451881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE " 452881Sjohnny "vector=0x%x\n", hdlp->ih_vector)); 453881Sjohnny break; 454881Sjohnny case DDI_INTROP_DISABLE: 455881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE\n")); 456881Sjohnny if (psm_intr_ops == NULL) 457881Sjohnny return (DDI_FAILURE); 458881Sjohnny 459881Sjohnny pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum); 460881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE " 461881Sjohnny "vector = %x\n", hdlp->ih_vector)); 462881Sjohnny break; 463881Sjohnny case DDI_INTROP_BLOCKENABLE: 464881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 465881Sjohnny "BLOCKENABLE\n")); 466881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_MSI) { 467881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: not MSI\n")); 468881Sjohnny return (DDI_FAILURE); 469881Sjohnny } 470881Sjohnny 471881Sjohnny /* Check if psm_intr_ops is NULL? */ 472881Sjohnny if (psm_intr_ops == NULL) 473881Sjohnny return (DDI_FAILURE); 474881Sjohnny 4751542Sjohnny count = hdlp->ih_scratch1; 4761542Sjohnny h_array = (ddi_intr_handle_t *)hdlp->ih_scratch2; 4771542Sjohnny for (i = 0; i < count; i++) { 4781542Sjohnny hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 479881Sjohnny if (pci_enable_intr(pdip, rdip, hdlp, 4801542Sjohnny hdlp->ih_inum) != DDI_SUCCESS) { 481881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: " 482881Sjohnny "pci_enable_intr failed for %d\n", i)); 4831542Sjohnny for (j = 0; j < i; j++) { 4841542Sjohnny hdlp = (ddi_intr_handle_impl_t *)h_array[j]; 4851542Sjohnny pci_disable_intr(pdip, rdip, hdlp, 4861542Sjohnny hdlp->ih_inum); 4871542Sjohnny } 488881Sjohnny return (DDI_FAILURE); 489881Sjohnny } 490881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 4911542Sjohnny "BLOCKENABLE inum %x done\n", hdlp->ih_inum)); 492881Sjohnny } 493881Sjohnny break; 494881Sjohnny case DDI_INTROP_BLOCKDISABLE: 495881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 496881Sjohnny "BLOCKDISABLE\n")); 497881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_MSI) { 498881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKDISABLE: not MSI\n")); 499881Sjohnny return (DDI_FAILURE); 500881Sjohnny } 501881Sjohnny 502881Sjohnny /* Check if psm_intr_ops is present */ 503881Sjohnny if (psm_intr_ops == NULL) 504881Sjohnny return (DDI_FAILURE); 505881Sjohnny 5061542Sjohnny count = hdlp->ih_scratch1; 5071542Sjohnny h_array = (ddi_intr_handle_t *)hdlp->ih_scratch2; 5081542Sjohnny for (i = 0; i < count; i++) { 5091542Sjohnny hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 5101542Sjohnny pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum); 511881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 5121542Sjohnny "BLOCKDISABLE inum %x done\n", hdlp->ih_inum)); 513881Sjohnny } 514881Sjohnny break; 515881Sjohnny case DDI_INTROP_SETMASK: 516881Sjohnny case DDI_INTROP_CLRMASK: 517881Sjohnny /* 518881Sjohnny * First handle in the config space 519881Sjohnny */ 520881Sjohnny if (intr_op == DDI_INTROP_SETMASK) { 521881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 522881Sjohnny pci_status = pci_msi_set_mask(rdip, 523881Sjohnny hdlp->ih_type, hdlp->ih_inum); 524881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 525881Sjohnny pci_status = pci_intx_set_mask(rdip); 526881Sjohnny } else { 527881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 528881Sjohnny pci_status = pci_msi_clr_mask(rdip, 529881Sjohnny hdlp->ih_type, hdlp->ih_inum); 530881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 531881Sjohnny pci_status = pci_intx_clr_mask(rdip); 532881Sjohnny } 533881Sjohnny 534881Sjohnny /* For MSI/X; no need to check with pcplusmp */ 535881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 536881Sjohnny return (pci_status); 537881Sjohnny 538881Sjohnny /* For fixed interrupts only: handle config space first */ 539881Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_FIXED && 540881Sjohnny pci_status == DDI_SUCCESS) 541881Sjohnny break; 542881Sjohnny 543881Sjohnny /* For fixed interrupts only: confer with pcplusmp next */ 544881Sjohnny if (psm_intr_ops != NULL) { 545881Sjohnny /* If interrupt is shared; do nothing */ 546881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 547881Sjohnny PSM_INTR_OP_GET_SHARED, &psm_status); 548881Sjohnny 549881Sjohnny if (psm_rval == PSM_FAILURE || psm_status == 1) 550881Sjohnny return (pci_status); 551881Sjohnny 552881Sjohnny /* Now, pcplusmp should try to set/clear the mask */ 553881Sjohnny if (intr_op == DDI_INTROP_SETMASK) 554881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 555881Sjohnny PSM_INTR_OP_SET_MASK, NULL); 556881Sjohnny else 557881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 558881Sjohnny PSM_INTR_OP_CLEAR_MASK, NULL); 559881Sjohnny } 560881Sjohnny return ((psm_rval == PSM_FAILURE) ? DDI_FAILURE : DDI_SUCCESS); 561881Sjohnny case DDI_INTROP_GETPENDING: 562881Sjohnny /* 563881Sjohnny * First check the config space and/or 564881Sjohnny * MSI capability register(s) 565881Sjohnny */ 566881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 567881Sjohnny pci_rval = pci_msi_get_pending(rdip, hdlp->ih_type, 568881Sjohnny hdlp->ih_inum, &pci_status); 569881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 570881Sjohnny pci_rval = pci_intx_get_pending(rdip, &pci_status); 571881Sjohnny 572881Sjohnny /* On failure; next try with pcplusmp */ 573881Sjohnny if (pci_rval != DDI_SUCCESS && psm_intr_ops != NULL) 574881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 575881Sjohnny PSM_INTR_OP_GET_PENDING, &psm_status); 576881Sjohnny 577881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned " 578881Sjohnny "psm_rval = %x, psm_status = %x, pci_rval = %x, " 579881Sjohnny "pci_status = %x\n", psm_rval, psm_status, pci_rval, 580881Sjohnny pci_status)); 581881Sjohnny if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) { 582881Sjohnny *(int *)result = 0; 583881Sjohnny return (DDI_FAILURE); 584881Sjohnny } 585881Sjohnny 586881Sjohnny if (psm_rval != PSM_FAILURE) 587881Sjohnny *(int *)result = psm_status; 588881Sjohnny else if (pci_rval != DDI_FAILURE) 589881Sjohnny *(int *)result = pci_status; 590881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned = %x\n", 591881Sjohnny *(int *)result)); 592881Sjohnny break; 593881Sjohnny case DDI_INTROP_NAVAIL: 594881Sjohnny if ((psm_intr_ops != NULL) && (pci_get_priority(rdip, 595881Sjohnny hdlp, &priority) == DDI_SUCCESS)) { 596881Sjohnny /* Priority in the handle not initialized yet */ 597881Sjohnny hdlp->ih_pri = priority; 598881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 599881Sjohnny PSM_INTR_OP_NAVAIL_VECTORS, result); 600881Sjohnny } else { 601881Sjohnny *(int *)result = 1; 602881Sjohnny } 603881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: NAVAIL returned = %x\n", 604881Sjohnny *(int *)result)); 605881Sjohnny break; 606881Sjohnny default: 607881Sjohnny return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result)); 608881Sjohnny } 609881Sjohnny 610881Sjohnny return (DDI_SUCCESS); 611881Sjohnny } 612881Sjohnny 613916Sschwartz int 614916Sschwartz pci_get_intr_from_vecirq(apic_get_intr_t *intrinfo_p, 615916Sschwartz int vecirq, boolean_t is_irq) 616916Sschwartz { 617916Sschwartz ddi_intr_handle_impl_t get_info_ii_hdl; 618916Sschwartz 619916Sschwartz if (is_irq) 620916Sschwartz intrinfo_p->avgi_req_flags |= PSMGI_INTRBY_IRQ; 621916Sschwartz 622916Sschwartz /* 623916Sschwartz * For this locally-declared and used handle, ih_private will contain a 624916Sschwartz * pointer to apic_get_intr_t, not an ihdl_plat_t as used for 625916Sschwartz * global interrupt handling. 626916Sschwartz */ 627916Sschwartz get_info_ii_hdl.ih_private = intrinfo_p; 628916Sschwartz get_info_ii_hdl.ih_vector = (ushort_t)vecirq; 629916Sschwartz 630916Sschwartz if ((*psm_intr_ops)(NULL, &get_info_ii_hdl, 631916Sschwartz PSM_INTR_OP_GET_INTR, NULL) == PSM_FAILURE) 632916Sschwartz return (DDI_FAILURE); 633916Sschwartz 634916Sschwartz return (DDI_SUCCESS); 635916Sschwartz } 636916Sschwartz 637916Sschwartz 638916Sschwartz int 639916Sschwartz pci_get_cpu_from_vecirq(int vecirq, boolean_t is_irq) 640916Sschwartz { 641916Sschwartz int rval; 642916Sschwartz 643916Sschwartz apic_get_intr_t intrinfo; 644916Sschwartz intrinfo.avgi_req_flags = PSMGI_REQ_CPUID; 645916Sschwartz rval = pci_get_intr_from_vecirq(&intrinfo, vecirq, is_irq); 646916Sschwartz 647916Sschwartz if (rval == DDI_SUCCESS) 648916Sschwartz return (intrinfo.avgi_cpu_id); 649916Sschwartz else 650916Sschwartz return (-1); 651916Sschwartz } 652916Sschwartz 653881Sjohnny 654881Sjohnny static int 655881Sjohnny pci_enable_intr(dev_info_t *pdip, dev_info_t *rdip, 656881Sjohnny ddi_intr_handle_impl_t *hdlp, uint32_t inum) 657881Sjohnny { 658881Sjohnny struct intrspec *ispec; 659916Sschwartz int irq; 660916Sschwartz ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 661881Sjohnny 662881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: hdlp %p inum %x\n", 663881Sjohnny (void *)hdlp, inum)); 664881Sjohnny 665881Sjohnny /* Translate the interrupt if needed */ 666881Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum); 667881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec) 668881Sjohnny ispec->intrspec_vec = inum; 669916Sschwartz ihdl_plat_datap->ip_ispecp = ispec; 670881Sjohnny 671881Sjohnny /* translate the interrupt if needed */ 672916Sschwartz (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq); 673916Sschwartz DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: priority=%x irq=%x\n", 674916Sschwartz hdlp->ih_pri, irq)); 675881Sjohnny 676881Sjohnny /* Add the interrupt handler */ 677881Sjohnny if (!add_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, 678916Sschwartz DEVI(rdip)->devi_name, irq, hdlp->ih_cb_arg1, 679916Sschwartz hdlp->ih_cb_arg2, &ihdl_plat_datap->ip_ticks, rdip)) 680881Sjohnny return (DDI_FAILURE); 681881Sjohnny 682916Sschwartz /* Note this really is an irq. */ 683916Sschwartz hdlp->ih_vector = (ushort_t)irq; 684916Sschwartz 685881Sjohnny return (DDI_SUCCESS); 686881Sjohnny } 687881Sjohnny 688881Sjohnny 689881Sjohnny static void 690881Sjohnny pci_disable_intr(dev_info_t *pdip, dev_info_t *rdip, 691881Sjohnny ddi_intr_handle_impl_t *hdlp, uint32_t inum) 692881Sjohnny { 693916Sschwartz int irq; 694881Sjohnny struct intrspec *ispec; 695916Sschwartz ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 696881Sjohnny 697881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_disable_intr: \n")); 698881Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum); 699881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec) 700881Sjohnny ispec->intrspec_vec = inum; 701916Sschwartz ihdl_plat_datap->ip_ispecp = ispec; 702881Sjohnny 703881Sjohnny /* translate the interrupt if needed */ 704916Sschwartz (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq); 705881Sjohnny 706881Sjohnny /* Disable the interrupt handler */ 707916Sschwartz rem_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, irq); 708916Sschwartz ihdl_plat_datap->ip_ispecp = NULL; 709881Sjohnny } 710881Sjohnny 711881Sjohnny /* 712881Sjohnny * Miscellaneous library function 713881Sjohnny */ 714881Sjohnny int 715881Sjohnny pci_common_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp) 716881Sjohnny { 717881Sjohnny int i; 718881Sjohnny int number; 719881Sjohnny int assigned_addr_len; 720881Sjohnny uint_t phys_hi = pci_rp->pci_phys_hi; 721881Sjohnny pci_regspec_t *assigned_addr; 722881Sjohnny 723881Sjohnny if (((phys_hi & PCI_REG_ADDR_M) == PCI_ADDR_CONFIG) || 724881Sjohnny (phys_hi & PCI_RELOCAT_B)) 725881Sjohnny return (DDI_SUCCESS); 726881Sjohnny 727881Sjohnny /* 728881Sjohnny * the "reg" property specifies relocatable, get and interpret the 729881Sjohnny * "assigned-addresses" property. 730881Sjohnny */ 731881Sjohnny if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 732881Sjohnny "assigned-addresses", (int **)&assigned_addr, 733881Sjohnny (uint_t *)&assigned_addr_len) != DDI_PROP_SUCCESS) 734881Sjohnny return (DDI_FAILURE); 735881Sjohnny 736881Sjohnny /* 737881Sjohnny * Scan the "assigned-addresses" for one that matches the specified 738881Sjohnny * "reg" property entry. 739881Sjohnny */ 740881Sjohnny phys_hi &= PCI_CONF_ADDR_MASK; 741881Sjohnny number = assigned_addr_len / (sizeof (pci_regspec_t) / sizeof (int)); 742881Sjohnny for (i = 0; i < number; i++) { 743881Sjohnny if ((assigned_addr[i].pci_phys_hi & PCI_CONF_ADDR_MASK) == 744881Sjohnny phys_hi) { 745881Sjohnny pci_rp->pci_phys_mid = assigned_addr[i].pci_phys_mid; 746881Sjohnny pci_rp->pci_phys_low = assigned_addr[i].pci_phys_low; 747881Sjohnny ddi_prop_free(assigned_addr); 748881Sjohnny return (DDI_SUCCESS); 749881Sjohnny } 750881Sjohnny } 751881Sjohnny 752881Sjohnny ddi_prop_free(assigned_addr); 753881Sjohnny return (DDI_FAILURE); 754881Sjohnny } 755881Sjohnny 756881Sjohnny 757881Sjohnny /* 758881Sjohnny * For pci_tools 759881Sjohnny */ 760881Sjohnny 761881Sjohnny int 762881Sjohnny pci_common_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg, 763881Sjohnny int mode, cred_t *credp, int *rvalp) 764881Sjohnny { 765881Sjohnny int rv = ENOTTY; 766881Sjohnny 767881Sjohnny minor_t minor = getminor(dev); 768881Sjohnny 769881Sjohnny switch (PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(minor)) { 770881Sjohnny case PCI_TOOL_REG_MINOR_NUM: 771881Sjohnny 772881Sjohnny switch (cmd) { 773881Sjohnny case PCITOOL_DEVICE_SET_REG: 774881Sjohnny case PCITOOL_DEVICE_GET_REG: 775881Sjohnny 776881Sjohnny /* Require full privileges. */ 777881Sjohnny if (secpolicy_kmdb(credp)) 778881Sjohnny rv = EPERM; 779881Sjohnny else 780881Sjohnny rv = pcitool_dev_reg_ops(dip, (void *)arg, 781881Sjohnny cmd, mode); 782881Sjohnny break; 783881Sjohnny 784881Sjohnny case PCITOOL_NEXUS_SET_REG: 785881Sjohnny case PCITOOL_NEXUS_GET_REG: 786881Sjohnny 787881Sjohnny /* Require full privileges. */ 788881Sjohnny if (secpolicy_kmdb(credp)) 789881Sjohnny rv = EPERM; 790881Sjohnny else 791881Sjohnny rv = pcitool_bus_reg_ops(dip, (void *)arg, 792881Sjohnny cmd, mode); 793881Sjohnny break; 794881Sjohnny } 795881Sjohnny break; 796881Sjohnny 797881Sjohnny case PCI_TOOL_INTR_MINOR_NUM: 798881Sjohnny 799881Sjohnny switch (cmd) { 800881Sjohnny case PCITOOL_DEVICE_SET_INTR: 801881Sjohnny 802881Sjohnny /* Require PRIV_SYS_RES_CONFIG, same as psradm */ 803881Sjohnny if (secpolicy_ponline(credp)) { 804881Sjohnny rv = EPERM; 805881Sjohnny break; 806881Sjohnny } 807881Sjohnny 808881Sjohnny /*FALLTHRU*/ 809881Sjohnny /* These require no special privileges. */ 810881Sjohnny case PCITOOL_DEVICE_GET_INTR: 811881Sjohnny case PCITOOL_DEVICE_NUM_INTR: 812881Sjohnny rv = pcitool_intr_admn(dip, (void *)arg, cmd, mode); 813881Sjohnny break; 814881Sjohnny } 815881Sjohnny break; 816881Sjohnny 817881Sjohnny /* 818881Sjohnny * All non-PCItool ioctls go through here, including: 819881Sjohnny * devctl ioctls with minor number PCIHP_DEVCTL_MINOR and 820881Sjohnny * those for attachment points with where minor number is the 821881Sjohnny * device number. 822881Sjohnny */ 823881Sjohnny default: 824881Sjohnny rv = (pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, 825881Sjohnny credp, rvalp); 826881Sjohnny break; 827881Sjohnny } 828881Sjohnny 829881Sjohnny return (rv); 830881Sjohnny } 8311083Sanish 8321083Sanish 8331083Sanish /* 8341083Sanish * These are the get and put functions to be shared with drivers. The 8351083Sanish * mutex locking is done inside the functions referenced, rather than 8361083Sanish * here, and is thus shared across PCI child drivers and any other 8371083Sanish * consumers of PCI config space (such as the ACPI subsystem). 8381083Sanish * 8391083Sanish * The configuration space addresses come in as pointers. This is fine on 8401083Sanish * a 32-bit system, where the VM space and configuration space are the same 8411083Sanish * size. It's not such a good idea on a 64-bit system, where memory 8421083Sanish * addresses are twice as large as configuration space addresses. At some 8431083Sanish * point in the call tree we need to take a stand and say "you are 32-bit 8441083Sanish * from this time forth", and this seems like a nice self-contained place. 8451083Sanish */ 8461083Sanish 8471083Sanish uint8_t 8481083Sanish pci_config_rd8(ddi_acc_impl_t *hdlp, uint8_t *addr) 8491083Sanish { 8501083Sanish pci_acc_cfblk_t *cfp; 8511083Sanish uint8_t rval; 8521083Sanish int reg; 8531083Sanish 8541083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 8551083Sanish 8561083Sanish reg = (int)(uintptr_t)addr; 8571083Sanish 8581083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 8591083Sanish 8601083Sanish rval = (*pci_getb_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum, 8611083Sanish reg); 8621083Sanish 8631083Sanish return (rval); 8641083Sanish } 8651083Sanish 8661083Sanish void 8671083Sanish pci_config_rep_rd8(ddi_acc_impl_t *hdlp, uint8_t *host_addr, 8681083Sanish uint8_t *dev_addr, size_t repcount, uint_t flags) 8691083Sanish { 8701083Sanish uint8_t *h, *d; 8711083Sanish 8721083Sanish h = host_addr; 8731083Sanish d = dev_addr; 8741083Sanish 8751083Sanish if (flags == DDI_DEV_AUTOINCR) 8761083Sanish for (; repcount; repcount--) 8771083Sanish *h++ = pci_config_rd8(hdlp, d++); 8781083Sanish else 8791083Sanish for (; repcount; repcount--) 8801083Sanish *h++ = pci_config_rd8(hdlp, d); 8811083Sanish } 8821083Sanish 8831083Sanish uint16_t 8841083Sanish pci_config_rd16(ddi_acc_impl_t *hdlp, uint16_t *addr) 8851083Sanish { 8861083Sanish pci_acc_cfblk_t *cfp; 8871083Sanish uint16_t rval; 8881083Sanish int reg; 8891083Sanish 8901083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 8911083Sanish 8921083Sanish reg = (int)(uintptr_t)addr; 8931083Sanish 8941083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 8951083Sanish 8961083Sanish rval = (*pci_getw_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum, 8971083Sanish reg); 8981083Sanish 8991083Sanish return (rval); 9001083Sanish } 9011083Sanish 9021083Sanish void 9031083Sanish pci_config_rep_rd16(ddi_acc_impl_t *hdlp, uint16_t *host_addr, 9041083Sanish uint16_t *dev_addr, size_t repcount, uint_t flags) 9051083Sanish { 9061083Sanish uint16_t *h, *d; 9071083Sanish 9081083Sanish h = host_addr; 9091083Sanish d = dev_addr; 9101083Sanish 9111083Sanish if (flags == DDI_DEV_AUTOINCR) 9121083Sanish for (; repcount; repcount--) 9131083Sanish *h++ = pci_config_rd16(hdlp, d++); 9141083Sanish else 9151083Sanish for (; repcount; repcount--) 9161083Sanish *h++ = pci_config_rd16(hdlp, d); 9171083Sanish } 9181083Sanish 9191083Sanish uint32_t 9201083Sanish pci_config_rd32(ddi_acc_impl_t *hdlp, uint32_t *addr) 9211083Sanish { 9221083Sanish pci_acc_cfblk_t *cfp; 9231083Sanish uint32_t rval; 9241083Sanish int reg; 9251083Sanish 9261083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 9271083Sanish 9281083Sanish reg = (int)(uintptr_t)addr; 9291083Sanish 9301083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 9311083Sanish 9321083Sanish rval = (*pci_getl_func)(cfp->c_busnum, cfp->c_devnum, 9331083Sanish cfp->c_funcnum, reg); 9341083Sanish 9351083Sanish return (rval); 9361083Sanish } 9371083Sanish 9381083Sanish void 9391083Sanish pci_config_rep_rd32(ddi_acc_impl_t *hdlp, uint32_t *host_addr, 9401083Sanish uint32_t *dev_addr, size_t repcount, uint_t flags) 9411083Sanish { 9421083Sanish uint32_t *h, *d; 9431083Sanish 9441083Sanish h = host_addr; 9451083Sanish d = dev_addr; 9461083Sanish 9471083Sanish if (flags == DDI_DEV_AUTOINCR) 9481083Sanish for (; repcount; repcount--) 9491083Sanish *h++ = pci_config_rd32(hdlp, d++); 9501083Sanish else 9511083Sanish for (; repcount; repcount--) 9521083Sanish *h++ = pci_config_rd32(hdlp, d); 9531083Sanish } 9541083Sanish 9551083Sanish 9561083Sanish void 9571083Sanish pci_config_wr8(ddi_acc_impl_t *hdlp, uint8_t *addr, uint8_t value) 9581083Sanish { 9591083Sanish pci_acc_cfblk_t *cfp; 9601083Sanish int reg; 9611083Sanish 9621083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 9631083Sanish 9641083Sanish reg = (int)(uintptr_t)addr; 9651083Sanish 9661083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 9671083Sanish 9681083Sanish (*pci_putb_func)(cfp->c_busnum, cfp->c_devnum, 9691083Sanish cfp->c_funcnum, reg, value); 9701083Sanish } 9711083Sanish 9721083Sanish void 9731083Sanish pci_config_rep_wr8(ddi_acc_impl_t *hdlp, uint8_t *host_addr, 9741083Sanish uint8_t *dev_addr, size_t repcount, uint_t flags) 9751083Sanish { 9761083Sanish uint8_t *h, *d; 9771083Sanish 9781083Sanish h = host_addr; 9791083Sanish d = dev_addr; 9801083Sanish 9811083Sanish if (flags == DDI_DEV_AUTOINCR) 9821083Sanish for (; repcount; repcount--) 9831083Sanish pci_config_wr8(hdlp, d++, *h++); 9841083Sanish else 9851083Sanish for (; repcount; repcount--) 9861083Sanish pci_config_wr8(hdlp, d, *h++); 9871083Sanish } 9881083Sanish 9891083Sanish void 9901083Sanish pci_config_wr16(ddi_acc_impl_t *hdlp, uint16_t *addr, uint16_t value) 9911083Sanish { 9921083Sanish pci_acc_cfblk_t *cfp; 9931083Sanish int reg; 9941083Sanish 9951083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 9961083Sanish 9971083Sanish reg = (int)(uintptr_t)addr; 9981083Sanish 9991083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 10001083Sanish 10011083Sanish (*pci_putw_func)(cfp->c_busnum, cfp->c_devnum, 10021083Sanish cfp->c_funcnum, reg, value); 10031083Sanish } 10041083Sanish 10051083Sanish void 10061083Sanish pci_config_rep_wr16(ddi_acc_impl_t *hdlp, uint16_t *host_addr, 10071083Sanish uint16_t *dev_addr, size_t repcount, uint_t flags) 10081083Sanish { 10091083Sanish uint16_t *h, *d; 10101083Sanish 10111083Sanish h = host_addr; 10121083Sanish d = dev_addr; 10131083Sanish 10141083Sanish if (flags == DDI_DEV_AUTOINCR) 10151083Sanish for (; repcount; repcount--) 10161083Sanish pci_config_wr16(hdlp, d++, *h++); 10171083Sanish else 10181083Sanish for (; repcount; repcount--) 10191083Sanish pci_config_wr16(hdlp, d, *h++); 10201083Sanish } 10211083Sanish 10221083Sanish void 10231083Sanish pci_config_wr32(ddi_acc_impl_t *hdlp, uint32_t *addr, uint32_t value) 10241083Sanish { 10251083Sanish pci_acc_cfblk_t *cfp; 10261083Sanish int reg; 10271083Sanish 10281083Sanish ASSERT64(((uintptr_t)addr >> 32) == 0); 10291083Sanish 10301083Sanish reg = (int)(uintptr_t)addr; 10311083Sanish 10321083Sanish cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 10331083Sanish 10341083Sanish (*pci_putl_func)(cfp->c_busnum, cfp->c_devnum, 10351083Sanish cfp->c_funcnum, reg, value); 10361083Sanish } 10371083Sanish 10381083Sanish void 10391083Sanish pci_config_rep_wr32(ddi_acc_impl_t *hdlp, uint32_t *host_addr, 10401083Sanish uint32_t *dev_addr, size_t repcount, uint_t flags) 10411083Sanish { 10421083Sanish uint32_t *h, *d; 10431083Sanish 10441083Sanish h = host_addr; 10451083Sanish d = dev_addr; 10461083Sanish 10471083Sanish if (flags == DDI_DEV_AUTOINCR) 10481083Sanish for (; repcount; repcount--) 10491083Sanish pci_config_wr32(hdlp, d++, *h++); 10501083Sanish else 10511083Sanish for (; repcount; repcount--) 10521083Sanish pci_config_wr32(hdlp, d, *h++); 10531083Sanish } 10541083Sanish 10551083Sanish uint64_t 10561083Sanish pci_config_rd64(ddi_acc_impl_t *hdlp, uint64_t *addr) 10571083Sanish { 10581083Sanish uint32_t lw_val; 10591083Sanish uint32_t hi_val; 10601083Sanish uint32_t *dp; 10611083Sanish uint64_t val; 10621083Sanish 10631083Sanish dp = (uint32_t *)addr; 10641083Sanish lw_val = pci_config_rd32(hdlp, dp); 10651083Sanish dp++; 10661083Sanish hi_val = pci_config_rd32(hdlp, dp); 10671083Sanish val = ((uint64_t)hi_val << 32) | lw_val; 10681083Sanish return (val); 10691083Sanish } 10701083Sanish 10711083Sanish void 10721083Sanish pci_config_wr64(ddi_acc_impl_t *hdlp, uint64_t *addr, uint64_t value) 10731083Sanish { 10741083Sanish uint32_t lw_val; 10751083Sanish uint32_t hi_val; 10761083Sanish uint32_t *dp; 10771083Sanish 10781083Sanish dp = (uint32_t *)addr; 10791083Sanish lw_val = (uint32_t)(value & 0xffffffff); 10801083Sanish hi_val = (uint32_t)(value >> 32); 10811083Sanish pci_config_wr32(hdlp, dp, lw_val); 10821083Sanish dp++; 10831083Sanish pci_config_wr32(hdlp, dp, hi_val); 10841083Sanish } 10851083Sanish 10861083Sanish void 10871083Sanish pci_config_rep_rd64(ddi_acc_impl_t *hdlp, uint64_t *host_addr, 10881083Sanish uint64_t *dev_addr, size_t repcount, uint_t flags) 10891083Sanish { 10901083Sanish if (flags == DDI_DEV_AUTOINCR) { 10911083Sanish for (; repcount; repcount--) 10921083Sanish *host_addr++ = pci_config_rd64(hdlp, dev_addr++); 10931083Sanish } else { 10941083Sanish for (; repcount; repcount--) 10951083Sanish *host_addr++ = pci_config_rd64(hdlp, dev_addr); 10961083Sanish } 10971083Sanish } 10981083Sanish 10991083Sanish void 11001083Sanish pci_config_rep_wr64(ddi_acc_impl_t *hdlp, uint64_t *host_addr, 11011083Sanish uint64_t *dev_addr, size_t repcount, uint_t flags) 11021083Sanish { 11031083Sanish if (flags == DDI_DEV_AUTOINCR) { 11041083Sanish for (; repcount; repcount--) 11051083Sanish pci_config_wr64(hdlp, host_addr++, *dev_addr++); 11061083Sanish } else { 11071083Sanish for (; repcount; repcount--) 11081083Sanish pci_config_wr64(hdlp, host_addr++, *dev_addr); 11091083Sanish } 11101083Sanish } 11111083Sanish 11121083Sanish 11131083Sanish /* 11141083Sanish * Enable Legacy PCI config space access for the following four north bridges 11151083Sanish * Host bridge: AMD HyperTransport Technology Configuration 11161083Sanish * Host bridge: AMD Address Map 11171083Sanish * Host bridge: AMD DRAM Controller 11181083Sanish * Host bridge: AMD Miscellaneous Control 11191083Sanish */ 11201083Sanish int 11211083Sanish is_amd_northbridge(dev_info_t *dip) 11221083Sanish { 11231083Sanish int vendor_id, device_id; 11241083Sanish 11251083Sanish vendor_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 11261083Sanish "vendor-id", -1); 11271083Sanish device_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 11281083Sanish "device-id", -1); 11291083Sanish 11301083Sanish if (IS_AMD_NTBRIDGE(vendor_id, device_id)) 11311083Sanish return (0); 11321083Sanish 11331083Sanish return (1); 11341083Sanish } 1135