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