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