1*881Sjohnny /* 2*881Sjohnny * CDDL HEADER START 3*881Sjohnny * 4*881Sjohnny * The contents of this file are subject to the terms of the 5*881Sjohnny * Common Development and Distribution License, Version 1.0 only 6*881Sjohnny * (the "License"). You may not use this file except in compliance 7*881Sjohnny * with the License. 8*881Sjohnny * 9*881Sjohnny * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*881Sjohnny * or http://www.opensolaris.org/os/licensing. 11*881Sjohnny * See the License for the specific language governing permissions 12*881Sjohnny * and limitations under the License. 13*881Sjohnny * 14*881Sjohnny * When distributing Covered Code, include this CDDL HEADER in each 15*881Sjohnny * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*881Sjohnny * If applicable, add the following below this CDDL HEADER, with the 17*881Sjohnny * fields enclosed by brackets "[]" replaced with your own identifying 18*881Sjohnny * information: Portions Copyright [yyyy] [name of copyright owner] 19*881Sjohnny * 20*881Sjohnny * CDDL HEADER END 21*881Sjohnny */ 22*881Sjohnny 23*881Sjohnny /* 24*881Sjohnny * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 25*881Sjohnny * Use is subject to license terms. 26*881Sjohnny */ 27*881Sjohnny 28*881Sjohnny #pragma ident "%Z%%M% %I% %E% SMI" 29*881Sjohnny 30*881Sjohnny /* 31*881Sjohnny * File that has code which is common between pci(7d) and npe(7d) 32*881Sjohnny * It shares the following: 33*881Sjohnny * - interrupt code 34*881Sjohnny * - pci_tools ioctl code 35*881Sjohnny * - name_child code 36*881Sjohnny * - set_parent_private_data code 37*881Sjohnny */ 38*881Sjohnny 39*881Sjohnny #include <sys/conf.h> 40*881Sjohnny #include <sys/pci.h> 41*881Sjohnny #include <sys/sunndi.h> 42*881Sjohnny #include <sys/hotplug/pci/pcihp.h> 43*881Sjohnny #include <sys/pci_intr_lib.h> 44*881Sjohnny #include <sys/psm.h> 45*881Sjohnny #include <sys/policy.h> 46*881Sjohnny #include <sys/sysmacros.h> 47*881Sjohnny #include <sys/pci_tools.h> 48*881Sjohnny #include <io/pci/pci_var.h> 49*881Sjohnny #include <io/pci/pci_tools_ext.h> 50*881Sjohnny #include <io/pci/pci_common.h> 51*881Sjohnny 52*881Sjohnny /* 53*881Sjohnny * Function prototypes 54*881Sjohnny */ 55*881Sjohnny static int pci_get_priority(dev_info_t *, ddi_intr_handle_impl_t *, int *); 56*881Sjohnny static int pci_get_nintrs(dev_info_t *, int, int *); 57*881Sjohnny static int pci_enable_intr(dev_info_t *, dev_info_t *, 58*881Sjohnny ddi_intr_handle_impl_t *, uint32_t); 59*881Sjohnny static void pci_disable_intr(dev_info_t *, dev_info_t *, 60*881Sjohnny ddi_intr_handle_impl_t *, uint32_t); 61*881Sjohnny 62*881Sjohnny /* Extern decalration for pcplusmp module */ 63*881Sjohnny extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *, 64*881Sjohnny psm_intr_op_t, int *); 65*881Sjohnny 66*881Sjohnny 67*881Sjohnny /* 68*881Sjohnny * pci_name_child: 69*881Sjohnny * 70*881Sjohnny * Assign the address portion of the node name 71*881Sjohnny */ 72*881Sjohnny int 73*881Sjohnny pci_common_name_child(dev_info_t *child, char *name, int namelen) 74*881Sjohnny { 75*881Sjohnny int dev, func, length; 76*881Sjohnny char **unit_addr; 77*881Sjohnny uint_t n; 78*881Sjohnny pci_regspec_t *pci_rp; 79*881Sjohnny 80*881Sjohnny if (ndi_dev_is_persistent_node(child) == 0) { 81*881Sjohnny /* 82*881Sjohnny * For .conf node, use "unit-address" property 83*881Sjohnny */ 84*881Sjohnny if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 85*881Sjohnny DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) != 86*881Sjohnny DDI_PROP_SUCCESS) { 87*881Sjohnny cmn_err(CE_WARN, "cannot find unit-address in %s.conf", 88*881Sjohnny ddi_get_name(child)); 89*881Sjohnny return (DDI_FAILURE); 90*881Sjohnny } 91*881Sjohnny if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 92*881Sjohnny cmn_err(CE_WARN, "unit-address property in %s.conf" 93*881Sjohnny " not well-formed", ddi_get_name(child)); 94*881Sjohnny ddi_prop_free(unit_addr); 95*881Sjohnny return (DDI_FAILURE); 96*881Sjohnny } 97*881Sjohnny (void) snprintf(name, namelen, "%s", *unit_addr); 98*881Sjohnny ddi_prop_free(unit_addr); 99*881Sjohnny return (DDI_SUCCESS); 100*881Sjohnny } 101*881Sjohnny 102*881Sjohnny if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 103*881Sjohnny "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) { 104*881Sjohnny cmn_err(CE_WARN, "cannot find reg property in %s", 105*881Sjohnny ddi_get_name(child)); 106*881Sjohnny return (DDI_FAILURE); 107*881Sjohnny } 108*881Sjohnny 109*881Sjohnny /* copy the device identifications */ 110*881Sjohnny dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 111*881Sjohnny func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); 112*881Sjohnny 113*881Sjohnny /* 114*881Sjohnny * free the memory allocated by ddi_prop_lookup_int_array 115*881Sjohnny */ 116*881Sjohnny ddi_prop_free(pci_rp); 117*881Sjohnny 118*881Sjohnny if (func != 0) { 119*881Sjohnny (void) snprintf(name, namelen, "%x,%x", dev, func); 120*881Sjohnny } else { 121*881Sjohnny (void) snprintf(name, namelen, "%x", dev); 122*881Sjohnny } 123*881Sjohnny 124*881Sjohnny return (DDI_SUCCESS); 125*881Sjohnny } 126*881Sjohnny 127*881Sjohnny /* 128*881Sjohnny * Interrupt related code: 129*881Sjohnny * 130*881Sjohnny * The following busop is common to npe and pci drivers 131*881Sjohnny * bus_introp 132*881Sjohnny */ 133*881Sjohnny 134*881Sjohnny /* 135*881Sjohnny * Create the ddi_parent_private_data for a pseudo child. 136*881Sjohnny */ 137*881Sjohnny void 138*881Sjohnny pci_common_set_parent_private_data(dev_info_t *dip) 139*881Sjohnny { 140*881Sjohnny struct ddi_parent_private_data *pdptr; 141*881Sjohnny 142*881Sjohnny pdptr = (struct ddi_parent_private_data *)kmem_zalloc( 143*881Sjohnny (sizeof (struct ddi_parent_private_data) + 144*881Sjohnny sizeof (struct intrspec)), KM_SLEEP); 145*881Sjohnny pdptr->par_intr = (struct intrspec *)(pdptr + 1); 146*881Sjohnny pdptr->par_nintr = 1; 147*881Sjohnny ddi_set_parent_data(dip, pdptr); 148*881Sjohnny } 149*881Sjohnny 150*881Sjohnny /* 151*881Sjohnny * pci_get_priority: 152*881Sjohnny * Figure out the priority of the device 153*881Sjohnny */ 154*881Sjohnny static int 155*881Sjohnny pci_get_priority(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, int *pri) 156*881Sjohnny { 157*881Sjohnny struct intrspec *ispec; 158*881Sjohnny 159*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_get_priority: dip = 0x%p, hdlp = %p\n", 160*881Sjohnny (void *)dip, (void *)hdlp)); 161*881Sjohnny 162*881Sjohnny if ((ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip, 163*881Sjohnny hdlp->ih_inum)) == NULL) { 164*881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) { 165*881Sjohnny int class = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 166*881Sjohnny DDI_PROP_DONTPASS, "class-code", -1); 167*881Sjohnny 168*881Sjohnny *pri = (class == -1) ? 1 : pci_devclass_to_ipl(class); 169*881Sjohnny pci_common_set_parent_private_data(hdlp->ih_dip); 170*881Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip, 171*881Sjohnny hdlp->ih_inum); 172*881Sjohnny return (DDI_SUCCESS); 173*881Sjohnny } 174*881Sjohnny return (DDI_FAILURE); 175*881Sjohnny } 176*881Sjohnny 177*881Sjohnny *pri = ispec->intrspec_pri; 178*881Sjohnny return (DDI_SUCCESS); 179*881Sjohnny } 180*881Sjohnny 181*881Sjohnny 182*881Sjohnny /* 183*881Sjohnny * pci_get_nintrs: 184*881Sjohnny * Figure out how many interrupts the device supports 185*881Sjohnny */ 186*881Sjohnny static int 187*881Sjohnny pci_get_nintrs(dev_info_t *dip, int type, int *nintrs) 188*881Sjohnny { 189*881Sjohnny int ret; 190*881Sjohnny 191*881Sjohnny *nintrs = 0; 192*881Sjohnny 193*881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(type)) 194*881Sjohnny ret = pci_msi_get_nintrs(dip, type, nintrs); 195*881Sjohnny else { 196*881Sjohnny ret = DDI_FAILURE; 197*881Sjohnny if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 198*881Sjohnny "interrupts", -1) != -1) { 199*881Sjohnny *nintrs = 1; 200*881Sjohnny ret = DDI_SUCCESS; 201*881Sjohnny } 202*881Sjohnny } 203*881Sjohnny 204*881Sjohnny return (ret); 205*881Sjohnny } 206*881Sjohnny 207*881Sjohnny static int pcie_pci_intr_pri_counter = 0; 208*881Sjohnny 209*881Sjohnny /* 210*881Sjohnny * pci_common_intr_ops: bus_intr_op() function for interrupt support 211*881Sjohnny */ 212*881Sjohnny int 213*881Sjohnny pci_common_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, 214*881Sjohnny ddi_intr_handle_impl_t *hdlp, void *result) 215*881Sjohnny { 216*881Sjohnny int priority = 0; 217*881Sjohnny int psm_status = 0; 218*881Sjohnny int pci_status = 0; 219*881Sjohnny int pci_rval, psm_rval = PSM_FAILURE; 220*881Sjohnny int types = 0; 221*881Sjohnny int pciepci = 0; 222*881Sjohnny int i, j; 223*881Sjohnny int behavior; 224*881Sjohnny ddi_intrspec_t isp; 225*881Sjohnny struct intrspec *ispec; 226*881Sjohnny ddi_intr_handle_impl_t tmp_hdl; 227*881Sjohnny ddi_intr_msix_t *msix_p; 228*881Sjohnny 229*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, 230*881Sjohnny "pci_common_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n", 231*881Sjohnny (void *)pdip, (void *)rdip, intr_op, (void *)hdlp)); 232*881Sjohnny 233*881Sjohnny /* Process the request */ 234*881Sjohnny switch (intr_op) { 235*881Sjohnny case DDI_INTROP_SUPPORTED_TYPES: 236*881Sjohnny /* Fixed supported by default */ 237*881Sjohnny *(int *)result = DDI_INTR_TYPE_FIXED; 238*881Sjohnny 239*881Sjohnny /* Figure out if MSI or MSI-X is supported? */ 240*881Sjohnny if (pci_msi_get_supported_type(rdip, &types) != DDI_SUCCESS) 241*881Sjohnny return (DDI_SUCCESS); 242*881Sjohnny 243*881Sjohnny if (psm_intr_ops != NULL) { 244*881Sjohnny /* MSI or MSI-X is supported, OR it in */ 245*881Sjohnny *(int *)result |= types; 246*881Sjohnny 247*881Sjohnny tmp_hdl.ih_type = *(int *)result; 248*881Sjohnny (void) (*psm_intr_ops)(rdip, &tmp_hdl, 249*881Sjohnny PSM_INTR_OP_CHECK_MSI, result); 250*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 251*881Sjohnny "rdip: 0x%p supported types: 0x%x\n", (void *)rdip, 252*881Sjohnny *(int *)result)); 253*881Sjohnny } 254*881Sjohnny break; 255*881Sjohnny case DDI_INTROP_NINTRS: 256*881Sjohnny if (pci_get_nintrs(rdip, hdlp->ih_type, result) != DDI_SUCCESS) 257*881Sjohnny return (DDI_FAILURE); 258*881Sjohnny break; 259*881Sjohnny case DDI_INTROP_ALLOC: 260*881Sjohnny /* 261*881Sjohnny * MSI or MSIX (figure out number of vectors available) 262*881Sjohnny * FIXED interrupts: just return available interrupts 263*881Sjohnny */ 264*881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && 265*881Sjohnny (psm_intr_ops != NULL) && 266*881Sjohnny (pci_get_priority(rdip, hdlp, &priority) == DDI_SUCCESS)) { 267*881Sjohnny /* 268*881Sjohnny * Following check is a special case for 'pcie_pci'. 269*881Sjohnny * This makes sure vectors with the right priority 270*881Sjohnny * are allocated for pcie_pci during ALLOC time. 271*881Sjohnny */ 272*881Sjohnny if (strcmp(ddi_driver_name(rdip), "pcie_pci") == 0) { 273*881Sjohnny hdlp->ih_pri = 274*881Sjohnny (pcie_pci_intr_pri_counter % 2) ? 4 : 7; 275*881Sjohnny pciepci = 1; 276*881Sjohnny } else 277*881Sjohnny hdlp->ih_pri = priority; 278*881Sjohnny behavior = hdlp->ih_scratch2; 279*881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 280*881Sjohnny PSM_INTR_OP_ALLOC_VECTORS, result); 281*881Sjohnny 282*881Sjohnny /* verify behavior flag and take appropriate action */ 283*881Sjohnny if ((behavior == DDI_INTR_ALLOC_STRICT) && 284*881Sjohnny (*(int *)result < hdlp->ih_scratch1)) { 285*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, 286*881Sjohnny "pci_common_intr_ops: behavior %x, " 287*881Sjohnny "couldn't get enough intrs\n", behavior)); 288*881Sjohnny hdlp->ih_scratch1 = *(int *)result; 289*881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 290*881Sjohnny PSM_INTR_OP_FREE_VECTORS, NULL); 291*881Sjohnny return (DDI_EAGAIN); 292*881Sjohnny } 293*881Sjohnny 294*881Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) { 295*881Sjohnny if (!(msix_p = i_ddi_get_msix(hdlp->ih_dip))) { 296*881Sjohnny msix_p = pci_msix_init(hdlp->ih_dip); 297*881Sjohnny if (msix_p) 298*881Sjohnny i_ddi_set_msix(hdlp->ih_dip, 299*881Sjohnny msix_p); 300*881Sjohnny } 301*881Sjohnny msix_p->msix_intrs_in_use += *(int *)result; 302*881Sjohnny } 303*881Sjohnny 304*881Sjohnny if (pciepci) { 305*881Sjohnny /* update priority in ispec */ 306*881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, 307*881Sjohnny (int)hdlp->ih_inum); 308*881Sjohnny ispec = (struct intrspec *)isp; 309*881Sjohnny if (ispec) 310*881Sjohnny ispec->intrspec_pri = hdlp->ih_pri; 311*881Sjohnny ++pcie_pci_intr_pri_counter; 312*881Sjohnny } 313*881Sjohnny 314*881Sjohnny } else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) { 315*881Sjohnny /* Figure out if this device supports MASKING */ 316*881Sjohnny pci_rval = pci_intx_get_cap(rdip, &pci_status); 317*881Sjohnny if (pci_rval == DDI_SUCCESS && pci_status) 318*881Sjohnny hdlp->ih_cap |= pci_status; 319*881Sjohnny *(int *)result = 1; /* DDI_INTR_TYPE_FIXED */ 320*881Sjohnny } else 321*881Sjohnny return (DDI_FAILURE); 322*881Sjohnny break; 323*881Sjohnny case DDI_INTROP_FREE: 324*881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && 325*881Sjohnny (psm_intr_ops != NULL)) { 326*881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 327*881Sjohnny PSM_INTR_OP_FREE_VECTORS, NULL); 328*881Sjohnny 329*881Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) { 330*881Sjohnny msix_p = i_ddi_get_msix(hdlp->ih_dip); 331*881Sjohnny if (msix_p && 332*881Sjohnny --msix_p->msix_intrs_in_use == 0) { 333*881Sjohnny pci_msix_fini(msix_p); 334*881Sjohnny i_ddi_set_msix(hdlp->ih_dip, NULL); 335*881Sjohnny } 336*881Sjohnny } 337*881Sjohnny } 338*881Sjohnny break; 339*881Sjohnny case DDI_INTROP_GETPRI: 340*881Sjohnny /* Get the priority */ 341*881Sjohnny if (pci_get_priority(rdip, hdlp, &priority) != DDI_SUCCESS) 342*881Sjohnny return (DDI_FAILURE); 343*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 344*881Sjohnny "priority = 0x%x\n", priority)); 345*881Sjohnny *(int *)result = priority; 346*881Sjohnny break; 347*881Sjohnny case DDI_INTROP_SETPRI: 348*881Sjohnny /* Validate the interrupt priority passed */ 349*881Sjohnny if (*(int *)result > LOCK_LEVEL) 350*881Sjohnny return (DDI_FAILURE); 351*881Sjohnny 352*881Sjohnny /* Ensure that PSM is all initialized */ 353*881Sjohnny if (psm_intr_ops == NULL) 354*881Sjohnny return (DDI_FAILURE); 355*881Sjohnny 356*881Sjohnny /* Change the priority */ 357*881Sjohnny if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_PRI, result) == 358*881Sjohnny PSM_FAILURE) 359*881Sjohnny return (DDI_FAILURE); 360*881Sjohnny 361*881Sjohnny /* update ispec */ 362*881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 363*881Sjohnny ispec = (struct intrspec *)isp; 364*881Sjohnny if (ispec) 365*881Sjohnny ispec->intrspec_pri = *(int *)result; 366*881Sjohnny break; 367*881Sjohnny case DDI_INTROP_ADDISR: 368*881Sjohnny /* update ispec */ 369*881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 370*881Sjohnny ispec = (struct intrspec *)isp; 371*881Sjohnny if (ispec) 372*881Sjohnny ispec->intrspec_func = hdlp->ih_cb_func; 373*881Sjohnny break; 374*881Sjohnny case DDI_INTROP_REMISR: 375*881Sjohnny /* Get the interrupt structure pointer */ 376*881Sjohnny isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 377*881Sjohnny ispec = (struct intrspec *)isp; 378*881Sjohnny if (ispec) 379*881Sjohnny ispec->intrspec_func = (uint_t (*)()) 0; 380*881Sjohnny break; 381*881Sjohnny case DDI_INTROP_GETCAP: 382*881Sjohnny /* 383*881Sjohnny * First check the config space and/or 384*881Sjohnny * MSI capability register(s) 385*881Sjohnny */ 386*881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 387*881Sjohnny pci_rval = pci_msi_get_cap(rdip, hdlp->ih_type, 388*881Sjohnny &pci_status); 389*881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 390*881Sjohnny pci_rval = pci_intx_get_cap(rdip, &pci_status); 391*881Sjohnny 392*881Sjohnny /* next check with pcplusmp */ 393*881Sjohnny if (psm_intr_ops != NULL) 394*881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 395*881Sjohnny PSM_INTR_OP_GET_CAP, &psm_status); 396*881Sjohnny 397*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned psm_rval = %x, " 398*881Sjohnny "psm_status = %x, pci_rval = %x, pci_status = %x\n", 399*881Sjohnny psm_rval, psm_status, pci_rval, pci_status)); 400*881Sjohnny 401*881Sjohnny if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) { 402*881Sjohnny *(int *)result = 0; 403*881Sjohnny return (DDI_FAILURE); 404*881Sjohnny } 405*881Sjohnny 406*881Sjohnny if (psm_rval == PSM_SUCCESS) 407*881Sjohnny *(int *)result = psm_status; 408*881Sjohnny 409*881Sjohnny if (pci_rval == DDI_SUCCESS) 410*881Sjohnny *(int *)result |= pci_status; 411*881Sjohnny 412*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned = %x\n", 413*881Sjohnny *(int *)result)); 414*881Sjohnny break; 415*881Sjohnny case DDI_INTROP_SETCAP: 416*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 417*881Sjohnny "SETCAP cap=0x%x\n", *(int *)result)); 418*881Sjohnny if (psm_intr_ops == NULL) 419*881Sjohnny return (DDI_FAILURE); 420*881Sjohnny 421*881Sjohnny if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) { 422*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "GETCAP: psm_intr_ops" 423*881Sjohnny " returned failure\n")); 424*881Sjohnny return (DDI_FAILURE); 425*881Sjohnny } 426*881Sjohnny break; 427*881Sjohnny case DDI_INTROP_ENABLE: 428*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE\n")); 429*881Sjohnny if (psm_intr_ops == NULL) 430*881Sjohnny return (DDI_FAILURE); 431*881Sjohnny 432*881Sjohnny if (pci_enable_intr(pdip, rdip, hdlp, hdlp->ih_inum) != 433*881Sjohnny DDI_SUCCESS) 434*881Sjohnny return (DDI_FAILURE); 435*881Sjohnny 436*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE " 437*881Sjohnny "vector=0x%x\n", hdlp->ih_vector)); 438*881Sjohnny break; 439*881Sjohnny case DDI_INTROP_DISABLE: 440*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE\n")); 441*881Sjohnny if (psm_intr_ops == NULL) 442*881Sjohnny return (DDI_FAILURE); 443*881Sjohnny 444*881Sjohnny pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum); 445*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE " 446*881Sjohnny "vector = %x\n", hdlp->ih_vector)); 447*881Sjohnny break; 448*881Sjohnny case DDI_INTROP_BLOCKENABLE: 449*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 450*881Sjohnny "BLOCKENABLE\n")); 451*881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_MSI) { 452*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: not MSI\n")); 453*881Sjohnny return (DDI_FAILURE); 454*881Sjohnny } 455*881Sjohnny 456*881Sjohnny /* Check if psm_intr_ops is NULL? */ 457*881Sjohnny if (psm_intr_ops == NULL) 458*881Sjohnny return (DDI_FAILURE); 459*881Sjohnny 460*881Sjohnny for (i = 0; i < hdlp->ih_scratch1; i++) { 461*881Sjohnny if (pci_enable_intr(pdip, rdip, hdlp, 462*881Sjohnny hdlp->ih_inum + i) != DDI_SUCCESS) { 463*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: " 464*881Sjohnny "pci_enable_intr failed for %d\n", i)); 465*881Sjohnny for (j = 0; j < i; j++) 466*881Sjohnny pci_disable_intr(pdip, rdip, hdlp, 467*881Sjohnny hdlp->ih_inum + j); 468*881Sjohnny return (DDI_FAILURE); 469*881Sjohnny } 470*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 471*881Sjohnny "BLOCKENABLE inum %x done\n", hdlp->ih_inum + i)); 472*881Sjohnny } 473*881Sjohnny break; 474*881Sjohnny case DDI_INTROP_BLOCKDISABLE: 475*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 476*881Sjohnny "BLOCKDISABLE\n")); 477*881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_MSI) { 478*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "BLOCKDISABLE: not MSI\n")); 479*881Sjohnny return (DDI_FAILURE); 480*881Sjohnny } 481*881Sjohnny 482*881Sjohnny /* Check if psm_intr_ops is present */ 483*881Sjohnny if (psm_intr_ops == NULL) 484*881Sjohnny return (DDI_FAILURE); 485*881Sjohnny 486*881Sjohnny for (i = 0; i < hdlp->ih_scratch1; i++) { 487*881Sjohnny pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum + i); 488*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 489*881Sjohnny "BLOCKDISABLE inum %x done\n", hdlp->ih_inum + i)); 490*881Sjohnny } 491*881Sjohnny break; 492*881Sjohnny case DDI_INTROP_SETMASK: 493*881Sjohnny case DDI_INTROP_CLRMASK: 494*881Sjohnny /* 495*881Sjohnny * First handle in the config space 496*881Sjohnny */ 497*881Sjohnny if (intr_op == DDI_INTROP_SETMASK) { 498*881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 499*881Sjohnny pci_status = pci_msi_set_mask(rdip, 500*881Sjohnny hdlp->ih_type, hdlp->ih_inum); 501*881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 502*881Sjohnny pci_status = pci_intx_set_mask(rdip); 503*881Sjohnny } else { 504*881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 505*881Sjohnny pci_status = pci_msi_clr_mask(rdip, 506*881Sjohnny hdlp->ih_type, hdlp->ih_inum); 507*881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 508*881Sjohnny pci_status = pci_intx_clr_mask(rdip); 509*881Sjohnny } 510*881Sjohnny 511*881Sjohnny /* For MSI/X; no need to check with pcplusmp */ 512*881Sjohnny if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 513*881Sjohnny return (pci_status); 514*881Sjohnny 515*881Sjohnny /* For fixed interrupts only: handle config space first */ 516*881Sjohnny if (hdlp->ih_type == DDI_INTR_TYPE_FIXED && 517*881Sjohnny pci_status == DDI_SUCCESS) 518*881Sjohnny break; 519*881Sjohnny 520*881Sjohnny /* For fixed interrupts only: confer with pcplusmp next */ 521*881Sjohnny if (psm_intr_ops != NULL) { 522*881Sjohnny /* If interrupt is shared; do nothing */ 523*881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 524*881Sjohnny PSM_INTR_OP_GET_SHARED, &psm_status); 525*881Sjohnny 526*881Sjohnny if (psm_rval == PSM_FAILURE || psm_status == 1) 527*881Sjohnny return (pci_status); 528*881Sjohnny 529*881Sjohnny /* Now, pcplusmp should try to set/clear the mask */ 530*881Sjohnny if (intr_op == DDI_INTROP_SETMASK) 531*881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 532*881Sjohnny PSM_INTR_OP_SET_MASK, NULL); 533*881Sjohnny else 534*881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 535*881Sjohnny PSM_INTR_OP_CLEAR_MASK, NULL); 536*881Sjohnny } 537*881Sjohnny return ((psm_rval == PSM_FAILURE) ? DDI_FAILURE : DDI_SUCCESS); 538*881Sjohnny case DDI_INTROP_GETPENDING: 539*881Sjohnny /* 540*881Sjohnny * First check the config space and/or 541*881Sjohnny * MSI capability register(s) 542*881Sjohnny */ 543*881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 544*881Sjohnny pci_rval = pci_msi_get_pending(rdip, hdlp->ih_type, 545*881Sjohnny hdlp->ih_inum, &pci_status); 546*881Sjohnny else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 547*881Sjohnny pci_rval = pci_intx_get_pending(rdip, &pci_status); 548*881Sjohnny 549*881Sjohnny /* On failure; next try with pcplusmp */ 550*881Sjohnny if (pci_rval != DDI_SUCCESS && psm_intr_ops != NULL) 551*881Sjohnny psm_rval = (*psm_intr_ops)(rdip, hdlp, 552*881Sjohnny PSM_INTR_OP_GET_PENDING, &psm_status); 553*881Sjohnny 554*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned " 555*881Sjohnny "psm_rval = %x, psm_status = %x, pci_rval = %x, " 556*881Sjohnny "pci_status = %x\n", psm_rval, psm_status, pci_rval, 557*881Sjohnny pci_status)); 558*881Sjohnny if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) { 559*881Sjohnny *(int *)result = 0; 560*881Sjohnny return (DDI_FAILURE); 561*881Sjohnny } 562*881Sjohnny 563*881Sjohnny if (psm_rval != PSM_FAILURE) 564*881Sjohnny *(int *)result = psm_status; 565*881Sjohnny else if (pci_rval != DDI_FAILURE) 566*881Sjohnny *(int *)result = pci_status; 567*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned = %x\n", 568*881Sjohnny *(int *)result)); 569*881Sjohnny break; 570*881Sjohnny case DDI_INTROP_NAVAIL: 571*881Sjohnny if ((psm_intr_ops != NULL) && (pci_get_priority(rdip, 572*881Sjohnny hdlp, &priority) == DDI_SUCCESS)) { 573*881Sjohnny /* Priority in the handle not initialized yet */ 574*881Sjohnny hdlp->ih_pri = priority; 575*881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, 576*881Sjohnny PSM_INTR_OP_NAVAIL_VECTORS, result); 577*881Sjohnny } else { 578*881Sjohnny *(int *)result = 1; 579*881Sjohnny } 580*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci: NAVAIL returned = %x\n", 581*881Sjohnny *(int *)result)); 582*881Sjohnny break; 583*881Sjohnny default: 584*881Sjohnny return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result)); 585*881Sjohnny } 586*881Sjohnny 587*881Sjohnny return (DDI_SUCCESS); 588*881Sjohnny } 589*881Sjohnny 590*881Sjohnny 591*881Sjohnny static int 592*881Sjohnny pci_enable_intr(dev_info_t *pdip, dev_info_t *rdip, 593*881Sjohnny ddi_intr_handle_impl_t *hdlp, uint32_t inum) 594*881Sjohnny { 595*881Sjohnny int vector; 596*881Sjohnny struct intrspec *ispec; 597*881Sjohnny 598*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: hdlp %p inum %x\n", 599*881Sjohnny (void *)hdlp, inum)); 600*881Sjohnny 601*881Sjohnny /* Translate the interrupt if needed */ 602*881Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum); 603*881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec) 604*881Sjohnny ispec->intrspec_vec = inum; 605*881Sjohnny hdlp->ih_private = (void *)ispec; 606*881Sjohnny 607*881Sjohnny /* translate the interrupt if needed */ 608*881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &vector); 609*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: priority=%x vector=%x\n", 610*881Sjohnny hdlp->ih_pri, vector)); 611*881Sjohnny 612*881Sjohnny /* Add the interrupt handler */ 613*881Sjohnny if (!add_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, 614*881Sjohnny DEVI(rdip)->devi_name, vector, hdlp->ih_cb_arg1, 615*881Sjohnny hdlp->ih_cb_arg2, rdip)) 616*881Sjohnny return (DDI_FAILURE); 617*881Sjohnny 618*881Sjohnny return (DDI_SUCCESS); 619*881Sjohnny } 620*881Sjohnny 621*881Sjohnny 622*881Sjohnny static void 623*881Sjohnny pci_disable_intr(dev_info_t *pdip, dev_info_t *rdip, 624*881Sjohnny ddi_intr_handle_impl_t *hdlp, uint32_t inum) 625*881Sjohnny { 626*881Sjohnny int vector; 627*881Sjohnny struct intrspec *ispec; 628*881Sjohnny 629*881Sjohnny DDI_INTR_NEXDBG((CE_CONT, "pci_disable_intr: \n")); 630*881Sjohnny ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum); 631*881Sjohnny if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec) 632*881Sjohnny ispec->intrspec_vec = inum; 633*881Sjohnny hdlp->ih_private = (void *)ispec; 634*881Sjohnny 635*881Sjohnny /* translate the interrupt if needed */ 636*881Sjohnny (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &vector); 637*881Sjohnny 638*881Sjohnny /* Disable the interrupt handler */ 639*881Sjohnny rem_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, vector); 640*881Sjohnny } 641*881Sjohnny 642*881Sjohnny /* 643*881Sjohnny * Miscellaneous library function 644*881Sjohnny */ 645*881Sjohnny int 646*881Sjohnny pci_common_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp) 647*881Sjohnny { 648*881Sjohnny int i; 649*881Sjohnny int number; 650*881Sjohnny int assigned_addr_len; 651*881Sjohnny uint_t phys_hi = pci_rp->pci_phys_hi; 652*881Sjohnny pci_regspec_t *assigned_addr; 653*881Sjohnny 654*881Sjohnny if (((phys_hi & PCI_REG_ADDR_M) == PCI_ADDR_CONFIG) || 655*881Sjohnny (phys_hi & PCI_RELOCAT_B)) 656*881Sjohnny return (DDI_SUCCESS); 657*881Sjohnny 658*881Sjohnny /* 659*881Sjohnny * the "reg" property specifies relocatable, get and interpret the 660*881Sjohnny * "assigned-addresses" property. 661*881Sjohnny */ 662*881Sjohnny if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 663*881Sjohnny "assigned-addresses", (int **)&assigned_addr, 664*881Sjohnny (uint_t *)&assigned_addr_len) != DDI_PROP_SUCCESS) 665*881Sjohnny return (DDI_FAILURE); 666*881Sjohnny 667*881Sjohnny /* 668*881Sjohnny * Scan the "assigned-addresses" for one that matches the specified 669*881Sjohnny * "reg" property entry. 670*881Sjohnny */ 671*881Sjohnny phys_hi &= PCI_CONF_ADDR_MASK; 672*881Sjohnny number = assigned_addr_len / (sizeof (pci_regspec_t) / sizeof (int)); 673*881Sjohnny for (i = 0; i < number; i++) { 674*881Sjohnny if ((assigned_addr[i].pci_phys_hi & PCI_CONF_ADDR_MASK) == 675*881Sjohnny phys_hi) { 676*881Sjohnny pci_rp->pci_phys_mid = assigned_addr[i].pci_phys_mid; 677*881Sjohnny pci_rp->pci_phys_low = assigned_addr[i].pci_phys_low; 678*881Sjohnny ddi_prop_free(assigned_addr); 679*881Sjohnny return (DDI_SUCCESS); 680*881Sjohnny } 681*881Sjohnny } 682*881Sjohnny 683*881Sjohnny ddi_prop_free(assigned_addr); 684*881Sjohnny return (DDI_FAILURE); 685*881Sjohnny } 686*881Sjohnny 687*881Sjohnny 688*881Sjohnny /* 689*881Sjohnny * For pci_tools 690*881Sjohnny */ 691*881Sjohnny 692*881Sjohnny int 693*881Sjohnny pci_common_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg, 694*881Sjohnny int mode, cred_t *credp, int *rvalp) 695*881Sjohnny { 696*881Sjohnny int rv = ENOTTY; 697*881Sjohnny 698*881Sjohnny minor_t minor = getminor(dev); 699*881Sjohnny 700*881Sjohnny switch (PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(minor)) { 701*881Sjohnny case PCI_TOOL_REG_MINOR_NUM: 702*881Sjohnny 703*881Sjohnny switch (cmd) { 704*881Sjohnny case PCITOOL_DEVICE_SET_REG: 705*881Sjohnny case PCITOOL_DEVICE_GET_REG: 706*881Sjohnny 707*881Sjohnny /* Require full privileges. */ 708*881Sjohnny if (secpolicy_kmdb(credp)) 709*881Sjohnny rv = EPERM; 710*881Sjohnny else 711*881Sjohnny rv = pcitool_dev_reg_ops(dip, (void *)arg, 712*881Sjohnny cmd, mode); 713*881Sjohnny break; 714*881Sjohnny 715*881Sjohnny case PCITOOL_NEXUS_SET_REG: 716*881Sjohnny case PCITOOL_NEXUS_GET_REG: 717*881Sjohnny 718*881Sjohnny /* Require full privileges. */ 719*881Sjohnny if (secpolicy_kmdb(credp)) 720*881Sjohnny rv = EPERM; 721*881Sjohnny else 722*881Sjohnny rv = pcitool_bus_reg_ops(dip, (void *)arg, 723*881Sjohnny cmd, mode); 724*881Sjohnny break; 725*881Sjohnny } 726*881Sjohnny break; 727*881Sjohnny 728*881Sjohnny case PCI_TOOL_INTR_MINOR_NUM: 729*881Sjohnny 730*881Sjohnny switch (cmd) { 731*881Sjohnny case PCITOOL_DEVICE_SET_INTR: 732*881Sjohnny 733*881Sjohnny /* Require PRIV_SYS_RES_CONFIG, same as psradm */ 734*881Sjohnny if (secpolicy_ponline(credp)) { 735*881Sjohnny rv = EPERM; 736*881Sjohnny break; 737*881Sjohnny } 738*881Sjohnny 739*881Sjohnny /*FALLTHRU*/ 740*881Sjohnny /* These require no special privileges. */ 741*881Sjohnny case PCITOOL_DEVICE_GET_INTR: 742*881Sjohnny case PCITOOL_DEVICE_NUM_INTR: 743*881Sjohnny rv = pcitool_intr_admn(dip, (void *)arg, cmd, mode); 744*881Sjohnny break; 745*881Sjohnny } 746*881Sjohnny break; 747*881Sjohnny 748*881Sjohnny /* 749*881Sjohnny * All non-PCItool ioctls go through here, including: 750*881Sjohnny * devctl ioctls with minor number PCIHP_DEVCTL_MINOR and 751*881Sjohnny * those for attachment points with where minor number is the 752*881Sjohnny * device number. 753*881Sjohnny */ 754*881Sjohnny default: 755*881Sjohnny rv = (pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, 756*881Sjohnny credp, rvalp); 757*881Sjohnny break; 758*881Sjohnny } 759*881Sjohnny 760*881Sjohnny return (rv); 761*881Sjohnny } 762