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 #include <sys/pci_cfgspace.h> 55 #include <sys/pci_impl.h> 56 57 /* 58 * Function prototypes 59 */ 60 static int pci_get_priority(dev_info_t *, ddi_intr_handle_impl_t *, int *); 61 static int pci_get_nintrs(dev_info_t *, int, int *); 62 static int pci_enable_intr(dev_info_t *, dev_info_t *, 63 ddi_intr_handle_impl_t *, uint32_t); 64 static void pci_disable_intr(dev_info_t *, dev_info_t *, 65 ddi_intr_handle_impl_t *, uint32_t); 66 67 /* Extern decalration for pcplusmp module */ 68 extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *, 69 psm_intr_op_t, int *); 70 71 72 /* 73 * pci_name_child: 74 * 75 * Assign the address portion of the node name 76 */ 77 int 78 pci_common_name_child(dev_info_t *child, char *name, int namelen) 79 { 80 int dev, func, length; 81 char **unit_addr; 82 uint_t n; 83 pci_regspec_t *pci_rp; 84 85 if (ndi_dev_is_persistent_node(child) == 0) { 86 /* 87 * For .conf node, use "unit-address" property 88 */ 89 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 90 DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) != 91 DDI_PROP_SUCCESS) { 92 cmn_err(CE_WARN, "cannot find unit-address in %s.conf", 93 ddi_get_name(child)); 94 return (DDI_FAILURE); 95 } 96 if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 97 cmn_err(CE_WARN, "unit-address property in %s.conf" 98 " not well-formed", ddi_get_name(child)); 99 ddi_prop_free(unit_addr); 100 return (DDI_FAILURE); 101 } 102 (void) snprintf(name, namelen, "%s", *unit_addr); 103 ddi_prop_free(unit_addr); 104 return (DDI_SUCCESS); 105 } 106 107 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 108 "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) { 109 cmn_err(CE_WARN, "cannot find reg property in %s", 110 ddi_get_name(child)); 111 return (DDI_FAILURE); 112 } 113 114 /* copy the device identifications */ 115 dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 116 func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); 117 118 /* 119 * free the memory allocated by ddi_prop_lookup_int_array 120 */ 121 ddi_prop_free(pci_rp); 122 123 if (func != 0) { 124 (void) snprintf(name, namelen, "%x,%x", dev, func); 125 } else { 126 (void) snprintf(name, namelen, "%x", dev); 127 } 128 129 return (DDI_SUCCESS); 130 } 131 132 /* 133 * Interrupt related code: 134 * 135 * The following busop is common to npe and pci drivers 136 * bus_introp 137 */ 138 139 /* 140 * Create the ddi_parent_private_data for a pseudo child. 141 */ 142 void 143 pci_common_set_parent_private_data(dev_info_t *dip) 144 { 145 struct ddi_parent_private_data *pdptr; 146 147 pdptr = (struct ddi_parent_private_data *)kmem_zalloc( 148 (sizeof (struct ddi_parent_private_data) + 149 sizeof (struct intrspec)), KM_SLEEP); 150 pdptr->par_intr = (struct intrspec *)(pdptr + 1); 151 pdptr->par_nintr = 1; 152 ddi_set_parent_data(dip, pdptr); 153 } 154 155 /* 156 * pci_get_priority: 157 * Figure out the priority of the device 158 */ 159 static int 160 pci_get_priority(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, int *pri) 161 { 162 struct intrspec *ispec; 163 164 DDI_INTR_NEXDBG((CE_CONT, "pci_get_priority: dip = 0x%p, hdlp = %p\n", 165 (void *)dip, (void *)hdlp)); 166 167 if ((ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip, 168 hdlp->ih_inum)) == NULL) { 169 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) { 170 int class = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 171 DDI_PROP_DONTPASS, "class-code", -1); 172 173 *pri = (class == -1) ? 1 : pci_devclass_to_ipl(class); 174 pci_common_set_parent_private_data(hdlp->ih_dip); 175 ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip, 176 hdlp->ih_inum); 177 return (DDI_SUCCESS); 178 } 179 return (DDI_FAILURE); 180 } 181 182 *pri = ispec->intrspec_pri; 183 return (DDI_SUCCESS); 184 } 185 186 187 /* 188 * pci_get_nintrs: 189 * Figure out how many interrupts the device supports 190 */ 191 static int 192 pci_get_nintrs(dev_info_t *dip, int type, int *nintrs) 193 { 194 int ret; 195 196 *nintrs = 0; 197 198 if (DDI_INTR_IS_MSI_OR_MSIX(type)) 199 ret = pci_msi_get_nintrs(dip, type, nintrs); 200 else { 201 ret = DDI_FAILURE; 202 if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 203 "interrupts", -1) != -1) { 204 *nintrs = 1; 205 ret = DDI_SUCCESS; 206 } 207 } 208 209 return (ret); 210 } 211 212 static int pcie_pci_intr_pri_counter = 0; 213 214 /* 215 * pci_common_intr_ops: bus_intr_op() function for interrupt support 216 */ 217 int 218 pci_common_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, 219 ddi_intr_handle_impl_t *hdlp, void *result) 220 { 221 int priority = 0; 222 int psm_status = 0; 223 int pci_status = 0; 224 int pci_rval, psm_rval = PSM_FAILURE; 225 int types = 0; 226 int pciepci = 0; 227 int i, j; 228 int behavior; 229 ddi_intrspec_t isp; 230 struct intrspec *ispec; 231 ddi_intr_handle_impl_t tmp_hdl; 232 ddi_intr_msix_t *msix_p; 233 ihdl_plat_t *ihdl_plat_datap; 234 235 DDI_INTR_NEXDBG((CE_CONT, 236 "pci_common_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n", 237 (void *)pdip, (void *)rdip, intr_op, (void *)hdlp)); 238 239 /* Process the request */ 240 switch (intr_op) { 241 case DDI_INTROP_SUPPORTED_TYPES: 242 /* Fixed supported by default */ 243 *(int *)result = DDI_INTR_TYPE_FIXED; 244 245 /* Figure out if MSI or MSI-X is supported? */ 246 if (pci_msi_get_supported_type(rdip, &types) != DDI_SUCCESS) 247 return (DDI_SUCCESS); 248 249 if (psm_intr_ops != NULL) { 250 /* MSI or MSI-X is supported, OR it in */ 251 *(int *)result |= types; 252 253 tmp_hdl.ih_type = *(int *)result; 254 (void) (*psm_intr_ops)(rdip, &tmp_hdl, 255 PSM_INTR_OP_CHECK_MSI, result); 256 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 257 "rdip: 0x%p supported types: 0x%x\n", (void *)rdip, 258 *(int *)result)); 259 } 260 break; 261 case DDI_INTROP_NINTRS: 262 if (pci_get_nintrs(rdip, hdlp->ih_type, result) != DDI_SUCCESS) 263 return (DDI_FAILURE); 264 break; 265 case DDI_INTROP_ALLOC: 266 /* 267 * MSI or MSIX (figure out number of vectors available) 268 * FIXED interrupts: just return available interrupts 269 */ 270 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && 271 (psm_intr_ops != NULL) && 272 (pci_get_priority(rdip, hdlp, &priority) == DDI_SUCCESS)) { 273 /* 274 * Following check is a special case for 'pcie_pci'. 275 * This makes sure vectors with the right priority 276 * are allocated for pcie_pci during ALLOC time. 277 */ 278 if (strcmp(ddi_driver_name(rdip), "pcie_pci") == 0) { 279 hdlp->ih_pri = 280 (pcie_pci_intr_pri_counter % 2) ? 4 : 7; 281 pciepci = 1; 282 } else 283 hdlp->ih_pri = priority; 284 behavior = hdlp->ih_scratch2; 285 (void) (*psm_intr_ops)(rdip, hdlp, 286 PSM_INTR_OP_ALLOC_VECTORS, result); 287 288 /* verify behavior flag and take appropriate action */ 289 if ((behavior == DDI_INTR_ALLOC_STRICT) && 290 (*(int *)result < hdlp->ih_scratch1)) { 291 DDI_INTR_NEXDBG((CE_CONT, 292 "pci_common_intr_ops: behavior %x, " 293 "couldn't get enough intrs\n", behavior)); 294 hdlp->ih_scratch1 = *(int *)result; 295 (void) (*psm_intr_ops)(rdip, hdlp, 296 PSM_INTR_OP_FREE_VECTORS, NULL); 297 return (DDI_EAGAIN); 298 } 299 300 if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) { 301 if (!(msix_p = i_ddi_get_msix(hdlp->ih_dip))) { 302 msix_p = pci_msix_init(hdlp->ih_dip); 303 if (msix_p) 304 i_ddi_set_msix(hdlp->ih_dip, 305 msix_p); 306 } 307 msix_p->msix_intrs_in_use += *(int *)result; 308 } 309 310 if (pciepci) { 311 /* update priority in ispec */ 312 isp = pci_intx_get_ispec(pdip, rdip, 313 (int)hdlp->ih_inum); 314 ispec = (struct intrspec *)isp; 315 if (ispec) 316 ispec->intrspec_pri = hdlp->ih_pri; 317 ++pcie_pci_intr_pri_counter; 318 } 319 320 } else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) { 321 /* Figure out if this device supports MASKING */ 322 pci_rval = pci_intx_get_cap(rdip, &pci_status); 323 if (pci_rval == DDI_SUCCESS && pci_status) 324 hdlp->ih_cap |= pci_status; 325 *(int *)result = 1; /* DDI_INTR_TYPE_FIXED */ 326 } else 327 return (DDI_FAILURE); 328 break; 329 case DDI_INTROP_FREE: 330 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && 331 (psm_intr_ops != NULL)) { 332 (void) (*psm_intr_ops)(rdip, hdlp, 333 PSM_INTR_OP_FREE_VECTORS, NULL); 334 335 if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) { 336 msix_p = i_ddi_get_msix(hdlp->ih_dip); 337 if (msix_p && 338 --msix_p->msix_intrs_in_use == 0) { 339 pci_msix_fini(msix_p); 340 i_ddi_set_msix(hdlp->ih_dip, NULL); 341 } 342 } 343 } 344 break; 345 case DDI_INTROP_GETPRI: 346 /* Get the priority */ 347 if (pci_get_priority(rdip, hdlp, &priority) != DDI_SUCCESS) 348 return (DDI_FAILURE); 349 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 350 "priority = 0x%x\n", priority)); 351 *(int *)result = priority; 352 break; 353 case DDI_INTROP_SETPRI: 354 /* Validate the interrupt priority passed */ 355 if (*(int *)result > LOCK_LEVEL) 356 return (DDI_FAILURE); 357 358 /* Ensure that PSM is all initialized */ 359 if (psm_intr_ops == NULL) 360 return (DDI_FAILURE); 361 362 /* Change the priority */ 363 if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_PRI, result) == 364 PSM_FAILURE) 365 return (DDI_FAILURE); 366 367 /* update ispec */ 368 isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 369 ispec = (struct intrspec *)isp; 370 if (ispec) 371 ispec->intrspec_pri = *(int *)result; 372 break; 373 case DDI_INTROP_ADDISR: 374 /* update ispec */ 375 isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 376 ispec = (struct intrspec *)isp; 377 if (ispec) { 378 ispec->intrspec_func = hdlp->ih_cb_func; 379 ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 380 pci_kstat_create(&ihdl_plat_datap->ip_ksp, pdip, hdlp); 381 } 382 break; 383 case DDI_INTROP_REMISR: 384 /* Get the interrupt structure pointer */ 385 isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 386 ispec = (struct intrspec *)isp; 387 if (ispec) { 388 ispec->intrspec_func = (uint_t (*)()) 0; 389 ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 390 if (ihdl_plat_datap->ip_ksp != NULL) 391 pci_kstat_delete(ihdl_plat_datap->ip_ksp); 392 } 393 break; 394 case DDI_INTROP_GETCAP: 395 /* 396 * First check the config space and/or 397 * MSI capability register(s) 398 */ 399 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 400 pci_rval = pci_msi_get_cap(rdip, hdlp->ih_type, 401 &pci_status); 402 else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 403 pci_rval = pci_intx_get_cap(rdip, &pci_status); 404 405 /* next check with pcplusmp */ 406 if (psm_intr_ops != NULL) 407 psm_rval = (*psm_intr_ops)(rdip, hdlp, 408 PSM_INTR_OP_GET_CAP, &psm_status); 409 410 DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned psm_rval = %x, " 411 "psm_status = %x, pci_rval = %x, pci_status = %x\n", 412 psm_rval, psm_status, pci_rval, pci_status)); 413 414 if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) { 415 *(int *)result = 0; 416 return (DDI_FAILURE); 417 } 418 419 if (psm_rval == PSM_SUCCESS) 420 *(int *)result = psm_status; 421 422 if (pci_rval == DDI_SUCCESS) 423 *(int *)result |= pci_status; 424 425 DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned = %x\n", 426 *(int *)result)); 427 break; 428 case DDI_INTROP_SETCAP: 429 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 430 "SETCAP cap=0x%x\n", *(int *)result)); 431 if (psm_intr_ops == NULL) 432 return (DDI_FAILURE); 433 434 if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) { 435 DDI_INTR_NEXDBG((CE_CONT, "GETCAP: psm_intr_ops" 436 " returned failure\n")); 437 return (DDI_FAILURE); 438 } 439 break; 440 case DDI_INTROP_ENABLE: 441 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE\n")); 442 if (psm_intr_ops == NULL) 443 return (DDI_FAILURE); 444 445 if (pci_enable_intr(pdip, rdip, hdlp, hdlp->ih_inum) != 446 DDI_SUCCESS) 447 return (DDI_FAILURE); 448 449 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE " 450 "vector=0x%x\n", hdlp->ih_vector)); 451 break; 452 case DDI_INTROP_DISABLE: 453 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE\n")); 454 if (psm_intr_ops == NULL) 455 return (DDI_FAILURE); 456 457 pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum); 458 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE " 459 "vector = %x\n", hdlp->ih_vector)); 460 break; 461 case DDI_INTROP_BLOCKENABLE: 462 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 463 "BLOCKENABLE\n")); 464 if (hdlp->ih_type != DDI_INTR_TYPE_MSI) { 465 DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: not MSI\n")); 466 return (DDI_FAILURE); 467 } 468 469 /* Check if psm_intr_ops is NULL? */ 470 if (psm_intr_ops == NULL) 471 return (DDI_FAILURE); 472 473 for (i = 0; i < hdlp->ih_scratch1; i++) { 474 if (pci_enable_intr(pdip, rdip, hdlp, 475 hdlp->ih_inum + i) != DDI_SUCCESS) { 476 DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: " 477 "pci_enable_intr failed for %d\n", i)); 478 for (j = 0; j < i; j++) 479 pci_disable_intr(pdip, rdip, hdlp, 480 hdlp->ih_inum + j); 481 return (DDI_FAILURE); 482 } 483 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 484 "BLOCKENABLE inum %x done\n", hdlp->ih_inum + i)); 485 } 486 break; 487 case DDI_INTROP_BLOCKDISABLE: 488 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 489 "BLOCKDISABLE\n")); 490 if (hdlp->ih_type != DDI_INTR_TYPE_MSI) { 491 DDI_INTR_NEXDBG((CE_CONT, "BLOCKDISABLE: not MSI\n")); 492 return (DDI_FAILURE); 493 } 494 495 /* Check if psm_intr_ops is present */ 496 if (psm_intr_ops == NULL) 497 return (DDI_FAILURE); 498 499 for (i = 0; i < hdlp->ih_scratch1; i++) { 500 pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum + i); 501 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 502 "BLOCKDISABLE inum %x done\n", hdlp->ih_inum + i)); 503 } 504 break; 505 case DDI_INTROP_SETMASK: 506 case DDI_INTROP_CLRMASK: 507 /* 508 * First handle in the config space 509 */ 510 if (intr_op == DDI_INTROP_SETMASK) { 511 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 512 pci_status = pci_msi_set_mask(rdip, 513 hdlp->ih_type, hdlp->ih_inum); 514 else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 515 pci_status = pci_intx_set_mask(rdip); 516 } else { 517 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 518 pci_status = pci_msi_clr_mask(rdip, 519 hdlp->ih_type, hdlp->ih_inum); 520 else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 521 pci_status = pci_intx_clr_mask(rdip); 522 } 523 524 /* For MSI/X; no need to check with pcplusmp */ 525 if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 526 return (pci_status); 527 528 /* For fixed interrupts only: handle config space first */ 529 if (hdlp->ih_type == DDI_INTR_TYPE_FIXED && 530 pci_status == DDI_SUCCESS) 531 break; 532 533 /* For fixed interrupts only: confer with pcplusmp next */ 534 if (psm_intr_ops != NULL) { 535 /* If interrupt is shared; do nothing */ 536 psm_rval = (*psm_intr_ops)(rdip, hdlp, 537 PSM_INTR_OP_GET_SHARED, &psm_status); 538 539 if (psm_rval == PSM_FAILURE || psm_status == 1) 540 return (pci_status); 541 542 /* Now, pcplusmp should try to set/clear the mask */ 543 if (intr_op == DDI_INTROP_SETMASK) 544 psm_rval = (*psm_intr_ops)(rdip, hdlp, 545 PSM_INTR_OP_SET_MASK, NULL); 546 else 547 psm_rval = (*psm_intr_ops)(rdip, hdlp, 548 PSM_INTR_OP_CLEAR_MASK, NULL); 549 } 550 return ((psm_rval == PSM_FAILURE) ? DDI_FAILURE : DDI_SUCCESS); 551 case DDI_INTROP_GETPENDING: 552 /* 553 * First check the config space and/or 554 * MSI capability register(s) 555 */ 556 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 557 pci_rval = pci_msi_get_pending(rdip, hdlp->ih_type, 558 hdlp->ih_inum, &pci_status); 559 else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 560 pci_rval = pci_intx_get_pending(rdip, &pci_status); 561 562 /* On failure; next try with pcplusmp */ 563 if (pci_rval != DDI_SUCCESS && psm_intr_ops != NULL) 564 psm_rval = (*psm_intr_ops)(rdip, hdlp, 565 PSM_INTR_OP_GET_PENDING, &psm_status); 566 567 DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned " 568 "psm_rval = %x, psm_status = %x, pci_rval = %x, " 569 "pci_status = %x\n", psm_rval, psm_status, pci_rval, 570 pci_status)); 571 if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) { 572 *(int *)result = 0; 573 return (DDI_FAILURE); 574 } 575 576 if (psm_rval != PSM_FAILURE) 577 *(int *)result = psm_status; 578 else if (pci_rval != DDI_FAILURE) 579 *(int *)result = pci_status; 580 DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned = %x\n", 581 *(int *)result)); 582 break; 583 case DDI_INTROP_NAVAIL: 584 if ((psm_intr_ops != NULL) && (pci_get_priority(rdip, 585 hdlp, &priority) == DDI_SUCCESS)) { 586 /* Priority in the handle not initialized yet */ 587 hdlp->ih_pri = priority; 588 (void) (*psm_intr_ops)(rdip, hdlp, 589 PSM_INTR_OP_NAVAIL_VECTORS, result); 590 } else { 591 *(int *)result = 1; 592 } 593 DDI_INTR_NEXDBG((CE_CONT, "pci: NAVAIL returned = %x\n", 594 *(int *)result)); 595 break; 596 default: 597 return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result)); 598 } 599 600 return (DDI_SUCCESS); 601 } 602 603 int 604 pci_get_intr_from_vecirq(apic_get_intr_t *intrinfo_p, 605 int vecirq, boolean_t is_irq) 606 { 607 ddi_intr_handle_impl_t get_info_ii_hdl; 608 609 if (is_irq) 610 intrinfo_p->avgi_req_flags |= PSMGI_INTRBY_IRQ; 611 612 /* 613 * For this locally-declared and used handle, ih_private will contain a 614 * pointer to apic_get_intr_t, not an ihdl_plat_t as used for 615 * global interrupt handling. 616 */ 617 get_info_ii_hdl.ih_private = intrinfo_p; 618 get_info_ii_hdl.ih_vector = (ushort_t)vecirq; 619 620 if ((*psm_intr_ops)(NULL, &get_info_ii_hdl, 621 PSM_INTR_OP_GET_INTR, NULL) == PSM_FAILURE) 622 return (DDI_FAILURE); 623 624 return (DDI_SUCCESS); 625 } 626 627 628 int 629 pci_get_cpu_from_vecirq(int vecirq, boolean_t is_irq) 630 { 631 int rval; 632 633 apic_get_intr_t intrinfo; 634 intrinfo.avgi_req_flags = PSMGI_REQ_CPUID; 635 rval = pci_get_intr_from_vecirq(&intrinfo, vecirq, is_irq); 636 637 if (rval == DDI_SUCCESS) 638 return (intrinfo.avgi_cpu_id); 639 else 640 return (-1); 641 } 642 643 644 static int 645 pci_enable_intr(dev_info_t *pdip, dev_info_t *rdip, 646 ddi_intr_handle_impl_t *hdlp, uint32_t inum) 647 { 648 struct intrspec *ispec; 649 int irq; 650 ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 651 652 DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: hdlp %p inum %x\n", 653 (void *)hdlp, inum)); 654 655 /* Translate the interrupt if needed */ 656 ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum); 657 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec) 658 ispec->intrspec_vec = inum; 659 ihdl_plat_datap->ip_ispecp = ispec; 660 661 /* translate the interrupt if needed */ 662 (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq); 663 DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: priority=%x irq=%x\n", 664 hdlp->ih_pri, irq)); 665 666 /* Add the interrupt handler */ 667 if (!add_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, 668 DEVI(rdip)->devi_name, irq, hdlp->ih_cb_arg1, 669 hdlp->ih_cb_arg2, &ihdl_plat_datap->ip_ticks, rdip)) 670 return (DDI_FAILURE); 671 672 /* Note this really is an irq. */ 673 hdlp->ih_vector = (ushort_t)irq; 674 675 return (DDI_SUCCESS); 676 } 677 678 679 static void 680 pci_disable_intr(dev_info_t *pdip, dev_info_t *rdip, 681 ddi_intr_handle_impl_t *hdlp, uint32_t inum) 682 { 683 int irq; 684 struct intrspec *ispec; 685 ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 686 687 DDI_INTR_NEXDBG((CE_CONT, "pci_disable_intr: \n")); 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 822 823 /* 824 * These are the get and put functions to be shared with drivers. The 825 * mutex locking is done inside the functions referenced, rather than 826 * here, and is thus shared across PCI child drivers and any other 827 * consumers of PCI config space (such as the ACPI subsystem). 828 * 829 * The configuration space addresses come in as pointers. This is fine on 830 * a 32-bit system, where the VM space and configuration space are the same 831 * size. It's not such a good idea on a 64-bit system, where memory 832 * addresses are twice as large as configuration space addresses. At some 833 * point in the call tree we need to take a stand and say "you are 32-bit 834 * from this time forth", and this seems like a nice self-contained place. 835 */ 836 837 uint8_t 838 pci_config_rd8(ddi_acc_impl_t *hdlp, uint8_t *addr) 839 { 840 pci_acc_cfblk_t *cfp; 841 uint8_t rval; 842 int reg; 843 844 ASSERT64(((uintptr_t)addr >> 32) == 0); 845 846 reg = (int)(uintptr_t)addr; 847 848 cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 849 850 rval = (*pci_getb_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum, 851 reg); 852 853 return (rval); 854 } 855 856 void 857 pci_config_rep_rd8(ddi_acc_impl_t *hdlp, uint8_t *host_addr, 858 uint8_t *dev_addr, size_t repcount, uint_t flags) 859 { 860 uint8_t *h, *d; 861 862 h = host_addr; 863 d = dev_addr; 864 865 if (flags == DDI_DEV_AUTOINCR) 866 for (; repcount; repcount--) 867 *h++ = pci_config_rd8(hdlp, d++); 868 else 869 for (; repcount; repcount--) 870 *h++ = pci_config_rd8(hdlp, d); 871 } 872 873 uint16_t 874 pci_config_rd16(ddi_acc_impl_t *hdlp, uint16_t *addr) 875 { 876 pci_acc_cfblk_t *cfp; 877 uint16_t rval; 878 int reg; 879 880 ASSERT64(((uintptr_t)addr >> 32) == 0); 881 882 reg = (int)(uintptr_t)addr; 883 884 cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 885 886 rval = (*pci_getw_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum, 887 reg); 888 889 return (rval); 890 } 891 892 void 893 pci_config_rep_rd16(ddi_acc_impl_t *hdlp, uint16_t *host_addr, 894 uint16_t *dev_addr, size_t repcount, uint_t flags) 895 { 896 uint16_t *h, *d; 897 898 h = host_addr; 899 d = dev_addr; 900 901 if (flags == DDI_DEV_AUTOINCR) 902 for (; repcount; repcount--) 903 *h++ = pci_config_rd16(hdlp, d++); 904 else 905 for (; repcount; repcount--) 906 *h++ = pci_config_rd16(hdlp, d); 907 } 908 909 uint32_t 910 pci_config_rd32(ddi_acc_impl_t *hdlp, uint32_t *addr) 911 { 912 pci_acc_cfblk_t *cfp; 913 uint32_t rval; 914 int reg; 915 916 ASSERT64(((uintptr_t)addr >> 32) == 0); 917 918 reg = (int)(uintptr_t)addr; 919 920 cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 921 922 rval = (*pci_getl_func)(cfp->c_busnum, cfp->c_devnum, 923 cfp->c_funcnum, reg); 924 925 return (rval); 926 } 927 928 void 929 pci_config_rep_rd32(ddi_acc_impl_t *hdlp, uint32_t *host_addr, 930 uint32_t *dev_addr, size_t repcount, uint_t flags) 931 { 932 uint32_t *h, *d; 933 934 h = host_addr; 935 d = dev_addr; 936 937 if (flags == DDI_DEV_AUTOINCR) 938 for (; repcount; repcount--) 939 *h++ = pci_config_rd32(hdlp, d++); 940 else 941 for (; repcount; repcount--) 942 *h++ = pci_config_rd32(hdlp, d); 943 } 944 945 946 void 947 pci_config_wr8(ddi_acc_impl_t *hdlp, uint8_t *addr, uint8_t value) 948 { 949 pci_acc_cfblk_t *cfp; 950 int reg; 951 952 ASSERT64(((uintptr_t)addr >> 32) == 0); 953 954 reg = (int)(uintptr_t)addr; 955 956 cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 957 958 (*pci_putb_func)(cfp->c_busnum, cfp->c_devnum, 959 cfp->c_funcnum, reg, value); 960 } 961 962 void 963 pci_config_rep_wr8(ddi_acc_impl_t *hdlp, uint8_t *host_addr, 964 uint8_t *dev_addr, size_t repcount, uint_t flags) 965 { 966 uint8_t *h, *d; 967 968 h = host_addr; 969 d = dev_addr; 970 971 if (flags == DDI_DEV_AUTOINCR) 972 for (; repcount; repcount--) 973 pci_config_wr8(hdlp, d++, *h++); 974 else 975 for (; repcount; repcount--) 976 pci_config_wr8(hdlp, d, *h++); 977 } 978 979 void 980 pci_config_wr16(ddi_acc_impl_t *hdlp, uint16_t *addr, uint16_t value) 981 { 982 pci_acc_cfblk_t *cfp; 983 int reg; 984 985 ASSERT64(((uintptr_t)addr >> 32) == 0); 986 987 reg = (int)(uintptr_t)addr; 988 989 cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 990 991 (*pci_putw_func)(cfp->c_busnum, cfp->c_devnum, 992 cfp->c_funcnum, reg, value); 993 } 994 995 void 996 pci_config_rep_wr16(ddi_acc_impl_t *hdlp, uint16_t *host_addr, 997 uint16_t *dev_addr, size_t repcount, uint_t flags) 998 { 999 uint16_t *h, *d; 1000 1001 h = host_addr; 1002 d = dev_addr; 1003 1004 if (flags == DDI_DEV_AUTOINCR) 1005 for (; repcount; repcount--) 1006 pci_config_wr16(hdlp, d++, *h++); 1007 else 1008 for (; repcount; repcount--) 1009 pci_config_wr16(hdlp, d, *h++); 1010 } 1011 1012 void 1013 pci_config_wr32(ddi_acc_impl_t *hdlp, uint32_t *addr, uint32_t value) 1014 { 1015 pci_acc_cfblk_t *cfp; 1016 int reg; 1017 1018 ASSERT64(((uintptr_t)addr >> 32) == 0); 1019 1020 reg = (int)(uintptr_t)addr; 1021 1022 cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 1023 1024 (*pci_putl_func)(cfp->c_busnum, cfp->c_devnum, 1025 cfp->c_funcnum, reg, value); 1026 } 1027 1028 void 1029 pci_config_rep_wr32(ddi_acc_impl_t *hdlp, uint32_t *host_addr, 1030 uint32_t *dev_addr, size_t repcount, uint_t flags) 1031 { 1032 uint32_t *h, *d; 1033 1034 h = host_addr; 1035 d = dev_addr; 1036 1037 if (flags == DDI_DEV_AUTOINCR) 1038 for (; repcount; repcount--) 1039 pci_config_wr32(hdlp, d++, *h++); 1040 else 1041 for (; repcount; repcount--) 1042 pci_config_wr32(hdlp, d, *h++); 1043 } 1044 1045 uint64_t 1046 pci_config_rd64(ddi_acc_impl_t *hdlp, uint64_t *addr) 1047 { 1048 uint32_t lw_val; 1049 uint32_t hi_val; 1050 uint32_t *dp; 1051 uint64_t val; 1052 1053 dp = (uint32_t *)addr; 1054 lw_val = pci_config_rd32(hdlp, dp); 1055 dp++; 1056 hi_val = pci_config_rd32(hdlp, dp); 1057 val = ((uint64_t)hi_val << 32) | lw_val; 1058 return (val); 1059 } 1060 1061 void 1062 pci_config_wr64(ddi_acc_impl_t *hdlp, uint64_t *addr, uint64_t value) 1063 { 1064 uint32_t lw_val; 1065 uint32_t hi_val; 1066 uint32_t *dp; 1067 1068 dp = (uint32_t *)addr; 1069 lw_val = (uint32_t)(value & 0xffffffff); 1070 hi_val = (uint32_t)(value >> 32); 1071 pci_config_wr32(hdlp, dp, lw_val); 1072 dp++; 1073 pci_config_wr32(hdlp, dp, hi_val); 1074 } 1075 1076 void 1077 pci_config_rep_rd64(ddi_acc_impl_t *hdlp, uint64_t *host_addr, 1078 uint64_t *dev_addr, size_t repcount, uint_t flags) 1079 { 1080 if (flags == DDI_DEV_AUTOINCR) { 1081 for (; repcount; repcount--) 1082 *host_addr++ = pci_config_rd64(hdlp, dev_addr++); 1083 } else { 1084 for (; repcount; repcount--) 1085 *host_addr++ = pci_config_rd64(hdlp, dev_addr); 1086 } 1087 } 1088 1089 void 1090 pci_config_rep_wr64(ddi_acc_impl_t *hdlp, uint64_t *host_addr, 1091 uint64_t *dev_addr, size_t repcount, uint_t flags) 1092 { 1093 if (flags == DDI_DEV_AUTOINCR) { 1094 for (; repcount; repcount--) 1095 pci_config_wr64(hdlp, host_addr++, *dev_addr++); 1096 } else { 1097 for (; repcount; repcount--) 1098 pci_config_wr64(hdlp, host_addr++, *dev_addr); 1099 } 1100 } 1101 1102 1103 /* 1104 * Enable Legacy PCI config space access for the following four north bridges 1105 * Host bridge: AMD HyperTransport Technology Configuration 1106 * Host bridge: AMD Address Map 1107 * Host bridge: AMD DRAM Controller 1108 * Host bridge: AMD Miscellaneous Control 1109 */ 1110 int 1111 is_amd_northbridge(dev_info_t *dip) 1112 { 1113 int vendor_id, device_id; 1114 1115 vendor_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1116 "vendor-id", -1); 1117 device_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1118 "device-id", -1); 1119 1120 if (IS_AMD_NTBRIDGE(vendor_id, device_id)) 1121 return (0); 1122 1123 return (1); 1124 } 1125