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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * File that has code which is common between pci(7d) and npe(7d) 31 * It shares the following: 32 * - interrupt code 33 * - pci_tools ioctl code 34 * - name_child code 35 * - set_parent_private_data code 36 */ 37 38 #include <sys/conf.h> 39 #include <sys/pci.h> 40 #include <sys/sunndi.h> 41 #include <sys/mach_intr.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/clock.h> 48 #include <io/pcplusmp/apic.h> 49 #include <sys/pci_tools.h> 50 #include <io/pci/pci_var.h> 51 #include <io/pci/pci_tools_ext.h> 52 #include <io/pci/pci_common.h> 53 #include <sys/pci_cfgspace.h> 54 #include <sys/pci_impl.h> 55 56 /* 57 * Function prototypes 58 */ 59 static int pci_get_priority(dev_info_t *, ddi_intr_handle_impl_t *, int *); 60 static int pci_get_nintrs(dev_info_t *, int, int *); 61 static int pci_enable_intr(dev_info_t *, dev_info_t *, 62 ddi_intr_handle_impl_t *, uint32_t); 63 static void pci_disable_intr(dev_info_t *, dev_info_t *, 64 ddi_intr_handle_impl_t *, uint32_t); 65 66 /* Extern decalration for pcplusmp module */ 67 extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *, 68 psm_intr_op_t, int *); 69 70 71 /* 72 * pci_name_child: 73 * 74 * Assign the address portion of the node name 75 */ 76 int 77 pci_common_name_child(dev_info_t *child, char *name, int namelen) 78 { 79 int dev, func, length; 80 char **unit_addr; 81 uint_t n; 82 pci_regspec_t *pci_rp; 83 84 if (ndi_dev_is_persistent_node(child) == 0) { 85 /* 86 * For .conf node, use "unit-address" property 87 */ 88 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 89 DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) != 90 DDI_PROP_SUCCESS) { 91 cmn_err(CE_WARN, "cannot find unit-address in %s.conf", 92 ddi_get_name(child)); 93 return (DDI_FAILURE); 94 } 95 if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 96 cmn_err(CE_WARN, "unit-address property in %s.conf" 97 " not well-formed", ddi_get_name(child)); 98 ddi_prop_free(unit_addr); 99 return (DDI_FAILURE); 100 } 101 (void) snprintf(name, namelen, "%s", *unit_addr); 102 ddi_prop_free(unit_addr); 103 return (DDI_SUCCESS); 104 } 105 106 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 107 "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) { 108 cmn_err(CE_WARN, "cannot find reg property in %s", 109 ddi_get_name(child)); 110 return (DDI_FAILURE); 111 } 112 113 /* copy the device identifications */ 114 dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 115 func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); 116 117 /* 118 * free the memory allocated by ddi_prop_lookup_int_array 119 */ 120 ddi_prop_free(pci_rp); 121 122 if (func != 0) { 123 (void) snprintf(name, namelen, "%x,%x", dev, func); 124 } else { 125 (void) snprintf(name, namelen, "%x", dev); 126 } 127 128 return (DDI_SUCCESS); 129 } 130 131 /* 132 * Interrupt related code: 133 * 134 * The following busop is common to npe and pci drivers 135 * bus_introp 136 */ 137 138 /* 139 * Create the ddi_parent_private_data for a pseudo child. 140 */ 141 void 142 pci_common_set_parent_private_data(dev_info_t *dip) 143 { 144 struct ddi_parent_private_data *pdptr; 145 146 pdptr = (struct ddi_parent_private_data *)kmem_zalloc( 147 (sizeof (struct ddi_parent_private_data) + 148 sizeof (struct intrspec)), KM_SLEEP); 149 pdptr->par_intr = (struct intrspec *)(pdptr + 1); 150 pdptr->par_nintr = 1; 151 ddi_set_parent_data(dip, pdptr); 152 } 153 154 /* 155 * pci_get_priority: 156 * Figure out the priority of the device 157 */ 158 static int 159 pci_get_priority(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, int *pri) 160 { 161 struct intrspec *ispec; 162 163 DDI_INTR_NEXDBG((CE_CONT, "pci_get_priority: dip = 0x%p, hdlp = %p\n", 164 (void *)dip, (void *)hdlp)); 165 166 if ((ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip, 167 hdlp->ih_inum)) == NULL) { 168 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) { 169 int class = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 170 DDI_PROP_DONTPASS, "class-code", -1); 171 172 *pri = (class == -1) ? 1 : pci_devclass_to_ipl(class); 173 pci_common_set_parent_private_data(hdlp->ih_dip); 174 ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip, 175 hdlp->ih_inum); 176 return (DDI_SUCCESS); 177 } 178 return (DDI_FAILURE); 179 } 180 181 *pri = ispec->intrspec_pri; 182 return (DDI_SUCCESS); 183 } 184 185 186 /* 187 * pci_get_nintrs: 188 * Figure out how many interrupts the device supports 189 */ 190 static int 191 pci_get_nintrs(dev_info_t *dip, int type, int *nintrs) 192 { 193 int ret; 194 195 *nintrs = 0; 196 197 if (DDI_INTR_IS_MSI_OR_MSIX(type)) 198 ret = pci_msi_get_nintrs(dip, type, nintrs); 199 else { 200 ret = DDI_FAILURE; 201 if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 202 "interrupts", -1) != -1) { 203 *nintrs = 1; 204 ret = DDI_SUCCESS; 205 } 206 } 207 208 return (ret); 209 } 210 211 static int pcie_pci_intr_pri_counter = 0; 212 213 /* 214 * pci_common_intr_ops: bus_intr_op() function for interrupt support 215 */ 216 int 217 pci_common_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, 218 ddi_intr_handle_impl_t *hdlp, void *result) 219 { 220 int priority = 0; 221 int psm_status = 0; 222 int pci_status = 0; 223 int pci_rval, psm_rval = PSM_FAILURE; 224 int types = 0; 225 int pciepci = 0; 226 int i, j, count; 227 int behavior; 228 ddi_intrspec_t isp; 229 struct intrspec *ispec; 230 ddi_intr_handle_impl_t tmp_hdl; 231 ddi_intr_msix_t *msix_p; 232 ihdl_plat_t *ihdl_plat_datap; 233 ddi_intr_handle_t *h_array; 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 /* 251 * Only support MSI for now, OR it in 252 */ 253 *(int *)result |= (types & DDI_INTR_TYPE_MSI); 254 255 tmp_hdl.ih_type = *(int *)result; 256 (void) (*psm_intr_ops)(rdip, &tmp_hdl, 257 PSM_INTR_OP_CHECK_MSI, result); 258 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 259 "rdip: 0x%p supported types: 0x%x\n", (void *)rdip, 260 *(int *)result)); 261 } 262 break; 263 case DDI_INTROP_NINTRS: 264 if (pci_get_nintrs(rdip, hdlp->ih_type, result) != DDI_SUCCESS) 265 return (DDI_FAILURE); 266 break; 267 case DDI_INTROP_ALLOC: 268 /* 269 * MSI or MSIX (figure out number of vectors available) 270 * FIXED interrupts: just return available interrupts 271 */ 272 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && 273 (psm_intr_ops != NULL) && 274 (pci_get_priority(rdip, hdlp, &priority) == DDI_SUCCESS)) { 275 /* 276 * Following check is a special case for 'pcie_pci'. 277 * This makes sure vectors with the right priority 278 * are allocated for pcie_pci during ALLOC time. 279 */ 280 if (strcmp(ddi_driver_name(rdip), "pcie_pci") == 0) { 281 hdlp->ih_pri = 282 (pcie_pci_intr_pri_counter % 2) ? 4 : 7; 283 pciepci = 1; 284 } else 285 hdlp->ih_pri = priority; 286 behavior = (int)(uintptr_t)hdlp->ih_scratch2; 287 (void) (*psm_intr_ops)(rdip, hdlp, 288 PSM_INTR_OP_ALLOC_VECTORS, result); 289 290 /* verify behavior flag and take appropriate action */ 291 if ((behavior == DDI_INTR_ALLOC_STRICT) && 292 (*(int *)result < hdlp->ih_scratch1)) { 293 DDI_INTR_NEXDBG((CE_CONT, 294 "pci_common_intr_ops: behavior %x, " 295 "couldn't get enough intrs\n", behavior)); 296 hdlp->ih_scratch1 = *(int *)result; 297 (void) (*psm_intr_ops)(rdip, hdlp, 298 PSM_INTR_OP_FREE_VECTORS, NULL); 299 return (DDI_EAGAIN); 300 } 301 302 if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) { 303 if (!(msix_p = i_ddi_get_msix(hdlp->ih_dip))) { 304 msix_p = pci_msix_init(hdlp->ih_dip); 305 if (msix_p) 306 i_ddi_set_msix(hdlp->ih_dip, 307 msix_p); 308 } 309 } 310 311 if (pciepci) { 312 /* update priority in ispec */ 313 isp = pci_intx_get_ispec(pdip, rdip, 314 (int)hdlp->ih_inum); 315 ispec = (struct intrspec *)isp; 316 if (ispec) 317 ispec->intrspec_pri = hdlp->ih_pri; 318 ++pcie_pci_intr_pri_counter; 319 } 320 321 } else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) { 322 /* Figure out if this device supports MASKING */ 323 pci_rval = pci_intx_get_cap(rdip, &pci_status); 324 if (pci_rval == DDI_SUCCESS && pci_status) 325 hdlp->ih_cap |= pci_status; 326 *(int *)result = 1; /* DDI_INTR_TYPE_FIXED */ 327 } else 328 return (DDI_FAILURE); 329 break; 330 case DDI_INTROP_FREE: 331 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && 332 (psm_intr_ops != NULL)) { 333 (void) (*psm_intr_ops)(rdip, hdlp, 334 PSM_INTR_OP_FREE_VECTORS, NULL); 335 336 if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) { 337 msix_p = i_ddi_get_msix(hdlp->ih_dip); 338 if (msix_p && 339 i_ddi_intr_get_current_nintrs(hdlp->ih_dip) 340 == 0) { 341 pci_msix_fini(msix_p); 342 i_ddi_set_msix(hdlp->ih_dip, NULL); 343 } 344 } 345 } 346 break; 347 case DDI_INTROP_GETPRI: 348 /* Get the priority */ 349 if (pci_get_priority(rdip, hdlp, &priority) != DDI_SUCCESS) 350 return (DDI_FAILURE); 351 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 352 "priority = 0x%x\n", priority)); 353 *(int *)result = priority; 354 break; 355 case DDI_INTROP_SETPRI: 356 /* Validate the interrupt priority passed */ 357 if (*(int *)result > LOCK_LEVEL) 358 return (DDI_FAILURE); 359 360 /* Ensure that PSM is all initialized */ 361 if (psm_intr_ops == NULL) 362 return (DDI_FAILURE); 363 364 /* Change the priority */ 365 if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_PRI, result) == 366 PSM_FAILURE) 367 return (DDI_FAILURE); 368 369 /* update ispec */ 370 isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 371 ispec = (struct intrspec *)isp; 372 if (ispec) 373 ispec->intrspec_pri = *(int *)result; 374 break; 375 case DDI_INTROP_ADDISR: 376 /* update ispec */ 377 isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 378 ispec = (struct intrspec *)isp; 379 if (ispec) { 380 ispec->intrspec_func = hdlp->ih_cb_func; 381 ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 382 pci_kstat_create(&ihdl_plat_datap->ip_ksp, pdip, hdlp); 383 } 384 break; 385 case DDI_INTROP_REMISR: 386 /* Get the interrupt structure pointer */ 387 isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum); 388 ispec = (struct intrspec *)isp; 389 if (ispec) { 390 ispec->intrspec_func = (uint_t (*)()) 0; 391 ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 392 if (ihdl_plat_datap->ip_ksp != NULL) 393 pci_kstat_delete(ihdl_plat_datap->ip_ksp); 394 } 395 break; 396 case DDI_INTROP_GETCAP: 397 /* 398 * First check the config space and/or 399 * MSI capability register(s) 400 */ 401 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 402 pci_rval = pci_msi_get_cap(rdip, hdlp->ih_type, 403 &pci_status); 404 else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 405 pci_rval = pci_intx_get_cap(rdip, &pci_status); 406 407 /* next check with pcplusmp */ 408 if (psm_intr_ops != NULL) 409 psm_rval = (*psm_intr_ops)(rdip, hdlp, 410 PSM_INTR_OP_GET_CAP, &psm_status); 411 412 DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned psm_rval = %x, " 413 "psm_status = %x, pci_rval = %x, pci_status = %x\n", 414 psm_rval, psm_status, pci_rval, pci_status)); 415 416 if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) { 417 *(int *)result = 0; 418 return (DDI_FAILURE); 419 } 420 421 if (psm_rval == PSM_SUCCESS) 422 *(int *)result = psm_status; 423 424 if (pci_rval == DDI_SUCCESS) 425 *(int *)result |= pci_status; 426 427 DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned = %x\n", 428 *(int *)result)); 429 break; 430 case DDI_INTROP_SETCAP: 431 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 432 "SETCAP cap=0x%x\n", *(int *)result)); 433 if (psm_intr_ops == NULL) 434 return (DDI_FAILURE); 435 436 if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) { 437 DDI_INTR_NEXDBG((CE_CONT, "GETCAP: psm_intr_ops" 438 " returned failure\n")); 439 return (DDI_FAILURE); 440 } 441 break; 442 case DDI_INTROP_ENABLE: 443 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE\n")); 444 if (psm_intr_ops == NULL) 445 return (DDI_FAILURE); 446 447 if (pci_enable_intr(pdip, rdip, hdlp, hdlp->ih_inum) != 448 DDI_SUCCESS) 449 return (DDI_FAILURE); 450 451 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE " 452 "vector=0x%x\n", hdlp->ih_vector)); 453 break; 454 case DDI_INTROP_DISABLE: 455 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE\n")); 456 if (psm_intr_ops == NULL) 457 return (DDI_FAILURE); 458 459 pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum); 460 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE " 461 "vector = %x\n", hdlp->ih_vector)); 462 break; 463 case DDI_INTROP_BLOCKENABLE: 464 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 465 "BLOCKENABLE\n")); 466 if (hdlp->ih_type != DDI_INTR_TYPE_MSI) { 467 DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: not MSI\n")); 468 return (DDI_FAILURE); 469 } 470 471 /* Check if psm_intr_ops is NULL? */ 472 if (psm_intr_ops == NULL) 473 return (DDI_FAILURE); 474 475 count = hdlp->ih_scratch1; 476 h_array = (ddi_intr_handle_t *)hdlp->ih_scratch2; 477 for (i = 0; i < count; i++) { 478 hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 479 if (pci_enable_intr(pdip, rdip, hdlp, 480 hdlp->ih_inum) != DDI_SUCCESS) { 481 DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: " 482 "pci_enable_intr failed for %d\n", i)); 483 for (j = 0; j < i; j++) { 484 hdlp = (ddi_intr_handle_impl_t *)h_array[j]; 485 pci_disable_intr(pdip, rdip, hdlp, 486 hdlp->ih_inum); 487 } 488 return (DDI_FAILURE); 489 } 490 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 491 "BLOCKENABLE inum %x done\n", hdlp->ih_inum)); 492 } 493 break; 494 case DDI_INTROP_BLOCKDISABLE: 495 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 496 "BLOCKDISABLE\n")); 497 if (hdlp->ih_type != DDI_INTR_TYPE_MSI) { 498 DDI_INTR_NEXDBG((CE_CONT, "BLOCKDISABLE: not MSI\n")); 499 return (DDI_FAILURE); 500 } 501 502 /* Check if psm_intr_ops is present */ 503 if (psm_intr_ops == NULL) 504 return (DDI_FAILURE); 505 506 count = hdlp->ih_scratch1; 507 h_array = (ddi_intr_handle_t *)hdlp->ih_scratch2; 508 for (i = 0; i < count; i++) { 509 hdlp = (ddi_intr_handle_impl_t *)h_array[i]; 510 pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum); 511 DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: " 512 "BLOCKDISABLE inum %x done\n", hdlp->ih_inum)); 513 } 514 break; 515 case DDI_INTROP_SETMASK: 516 case DDI_INTROP_CLRMASK: 517 /* 518 * First handle in the config space 519 */ 520 if (intr_op == DDI_INTROP_SETMASK) { 521 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 522 pci_status = pci_msi_set_mask(rdip, 523 hdlp->ih_type, hdlp->ih_inum); 524 else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 525 pci_status = pci_intx_set_mask(rdip); 526 } else { 527 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 528 pci_status = pci_msi_clr_mask(rdip, 529 hdlp->ih_type, hdlp->ih_inum); 530 else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 531 pci_status = pci_intx_clr_mask(rdip); 532 } 533 534 /* For MSI/X; no need to check with pcplusmp */ 535 if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 536 return (pci_status); 537 538 /* For fixed interrupts only: handle config space first */ 539 if (hdlp->ih_type == DDI_INTR_TYPE_FIXED && 540 pci_status == DDI_SUCCESS) 541 break; 542 543 /* For fixed interrupts only: confer with pcplusmp next */ 544 if (psm_intr_ops != NULL) { 545 /* If interrupt is shared; do nothing */ 546 psm_rval = (*psm_intr_ops)(rdip, hdlp, 547 PSM_INTR_OP_GET_SHARED, &psm_status); 548 549 if (psm_rval == PSM_FAILURE || psm_status == 1) 550 return (pci_status); 551 552 /* Now, pcplusmp should try to set/clear the mask */ 553 if (intr_op == DDI_INTROP_SETMASK) 554 psm_rval = (*psm_intr_ops)(rdip, hdlp, 555 PSM_INTR_OP_SET_MASK, NULL); 556 else 557 psm_rval = (*psm_intr_ops)(rdip, hdlp, 558 PSM_INTR_OP_CLEAR_MASK, NULL); 559 } 560 return ((psm_rval == PSM_FAILURE) ? DDI_FAILURE : DDI_SUCCESS); 561 case DDI_INTROP_GETPENDING: 562 /* 563 * First check the config space and/or 564 * MSI capability register(s) 565 */ 566 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) 567 pci_rval = pci_msi_get_pending(rdip, hdlp->ih_type, 568 hdlp->ih_inum, &pci_status); 569 else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 570 pci_rval = pci_intx_get_pending(rdip, &pci_status); 571 572 /* On failure; next try with pcplusmp */ 573 if (pci_rval != DDI_SUCCESS && psm_intr_ops != NULL) 574 psm_rval = (*psm_intr_ops)(rdip, hdlp, 575 PSM_INTR_OP_GET_PENDING, &psm_status); 576 577 DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned " 578 "psm_rval = %x, psm_status = %x, pci_rval = %x, " 579 "pci_status = %x\n", psm_rval, psm_status, pci_rval, 580 pci_status)); 581 if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) { 582 *(int *)result = 0; 583 return (DDI_FAILURE); 584 } 585 586 if (psm_rval != PSM_FAILURE) 587 *(int *)result = psm_status; 588 else if (pci_rval != DDI_FAILURE) 589 *(int *)result = pci_status; 590 DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned = %x\n", 591 *(int *)result)); 592 break; 593 case DDI_INTROP_NAVAIL: 594 if ((psm_intr_ops != NULL) && (pci_get_priority(rdip, 595 hdlp, &priority) == DDI_SUCCESS)) { 596 /* Priority in the handle not initialized yet */ 597 hdlp->ih_pri = priority; 598 (void) (*psm_intr_ops)(rdip, hdlp, 599 PSM_INTR_OP_NAVAIL_VECTORS, result); 600 } else { 601 *(int *)result = 1; 602 } 603 DDI_INTR_NEXDBG((CE_CONT, "pci: NAVAIL returned = %x\n", 604 *(int *)result)); 605 break; 606 default: 607 return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result)); 608 } 609 610 return (DDI_SUCCESS); 611 } 612 613 int 614 pci_get_intr_from_vecirq(apic_get_intr_t *intrinfo_p, 615 int vecirq, boolean_t is_irq) 616 { 617 ddi_intr_handle_impl_t get_info_ii_hdl; 618 619 if (is_irq) 620 intrinfo_p->avgi_req_flags |= PSMGI_INTRBY_IRQ; 621 622 /* 623 * For this locally-declared and used handle, ih_private will contain a 624 * pointer to apic_get_intr_t, not an ihdl_plat_t as used for 625 * global interrupt handling. 626 */ 627 get_info_ii_hdl.ih_private = intrinfo_p; 628 get_info_ii_hdl.ih_vector = (ushort_t)vecirq; 629 630 if ((*psm_intr_ops)(NULL, &get_info_ii_hdl, 631 PSM_INTR_OP_GET_INTR, NULL) == PSM_FAILURE) 632 return (DDI_FAILURE); 633 634 return (DDI_SUCCESS); 635 } 636 637 638 int 639 pci_get_cpu_from_vecirq(int vecirq, boolean_t is_irq) 640 { 641 int rval; 642 643 apic_get_intr_t intrinfo; 644 intrinfo.avgi_req_flags = PSMGI_REQ_CPUID; 645 rval = pci_get_intr_from_vecirq(&intrinfo, vecirq, is_irq); 646 647 if (rval == DDI_SUCCESS) 648 return (intrinfo.avgi_cpu_id); 649 else 650 return (-1); 651 } 652 653 654 static int 655 pci_enable_intr(dev_info_t *pdip, dev_info_t *rdip, 656 ddi_intr_handle_impl_t *hdlp, uint32_t inum) 657 { 658 struct intrspec *ispec; 659 int irq; 660 ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 661 662 DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: hdlp %p inum %x\n", 663 (void *)hdlp, inum)); 664 665 /* Translate the interrupt if needed */ 666 ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum); 667 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec) 668 ispec->intrspec_vec = inum; 669 ihdl_plat_datap->ip_ispecp = ispec; 670 671 /* translate the interrupt if needed */ 672 (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq); 673 DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: priority=%x irq=%x\n", 674 hdlp->ih_pri, irq)); 675 676 /* Add the interrupt handler */ 677 if (!add_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, 678 DEVI(rdip)->devi_name, irq, hdlp->ih_cb_arg1, 679 hdlp->ih_cb_arg2, &ihdl_plat_datap->ip_ticks, rdip)) 680 return (DDI_FAILURE); 681 682 /* Note this really is an irq. */ 683 hdlp->ih_vector = (ushort_t)irq; 684 685 return (DDI_SUCCESS); 686 } 687 688 689 static void 690 pci_disable_intr(dev_info_t *pdip, dev_info_t *rdip, 691 ddi_intr_handle_impl_t *hdlp, uint32_t inum) 692 { 693 int irq; 694 struct intrspec *ispec; 695 ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private; 696 697 DDI_INTR_NEXDBG((CE_CONT, "pci_disable_intr: \n")); 698 ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum); 699 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec) 700 ispec->intrspec_vec = inum; 701 ihdl_plat_datap->ip_ispecp = ispec; 702 703 /* translate the interrupt if needed */ 704 (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq); 705 706 /* Disable the interrupt handler */ 707 rem_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, irq); 708 ihdl_plat_datap->ip_ispecp = NULL; 709 } 710 711 /* 712 * Miscellaneous library function 713 */ 714 int 715 pci_common_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp) 716 { 717 int i; 718 int number; 719 int assigned_addr_len; 720 uint_t phys_hi = pci_rp->pci_phys_hi; 721 pci_regspec_t *assigned_addr; 722 723 if (((phys_hi & PCI_REG_ADDR_M) == PCI_ADDR_CONFIG) || 724 (phys_hi & PCI_RELOCAT_B)) 725 return (DDI_SUCCESS); 726 727 /* 728 * the "reg" property specifies relocatable, get and interpret the 729 * "assigned-addresses" property. 730 */ 731 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 732 "assigned-addresses", (int **)&assigned_addr, 733 (uint_t *)&assigned_addr_len) != DDI_PROP_SUCCESS) 734 return (DDI_FAILURE); 735 736 /* 737 * Scan the "assigned-addresses" for one that matches the specified 738 * "reg" property entry. 739 */ 740 phys_hi &= PCI_CONF_ADDR_MASK; 741 number = assigned_addr_len / (sizeof (pci_regspec_t) / sizeof (int)); 742 for (i = 0; i < number; i++) { 743 if ((assigned_addr[i].pci_phys_hi & PCI_CONF_ADDR_MASK) == 744 phys_hi) { 745 pci_rp->pci_phys_mid = assigned_addr[i].pci_phys_mid; 746 pci_rp->pci_phys_low = assigned_addr[i].pci_phys_low; 747 ddi_prop_free(assigned_addr); 748 return (DDI_SUCCESS); 749 } 750 } 751 752 ddi_prop_free(assigned_addr); 753 return (DDI_FAILURE); 754 } 755 756 757 /* 758 * For pci_tools 759 */ 760 761 int 762 pci_common_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg, 763 int mode, cred_t *credp, int *rvalp) 764 { 765 int rv = ENOTTY; 766 767 minor_t minor = getminor(dev); 768 769 switch (PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(minor)) { 770 case PCI_TOOL_REG_MINOR_NUM: 771 772 switch (cmd) { 773 case PCITOOL_DEVICE_SET_REG: 774 case PCITOOL_DEVICE_GET_REG: 775 776 /* Require full privileges. */ 777 if (secpolicy_kmdb(credp)) 778 rv = EPERM; 779 else 780 rv = pcitool_dev_reg_ops(dip, (void *)arg, 781 cmd, mode); 782 break; 783 784 case PCITOOL_NEXUS_SET_REG: 785 case PCITOOL_NEXUS_GET_REG: 786 787 /* Require full privileges. */ 788 if (secpolicy_kmdb(credp)) 789 rv = EPERM; 790 else 791 rv = pcitool_bus_reg_ops(dip, (void *)arg, 792 cmd, mode); 793 break; 794 } 795 break; 796 797 case PCI_TOOL_INTR_MINOR_NUM: 798 799 switch (cmd) { 800 case PCITOOL_DEVICE_SET_INTR: 801 802 /* Require PRIV_SYS_RES_CONFIG, same as psradm */ 803 if (secpolicy_ponline(credp)) { 804 rv = EPERM; 805 break; 806 } 807 808 /*FALLTHRU*/ 809 /* These require no special privileges. */ 810 case PCITOOL_DEVICE_GET_INTR: 811 case PCITOOL_DEVICE_NUM_INTR: 812 rv = pcitool_intr_admn(dip, (void *)arg, cmd, mode); 813 break; 814 } 815 break; 816 817 /* 818 * All non-PCItool ioctls go through here, including: 819 * devctl ioctls with minor number PCIHP_DEVCTL_MINOR and 820 * those for attachment points with where minor number is the 821 * device number. 822 */ 823 default: 824 rv = (pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, 825 credp, rvalp); 826 break; 827 } 828 829 return (rv); 830 } 831 832 833 /* 834 * These are the get and put functions to be shared with drivers. The 835 * mutex locking is done inside the functions referenced, rather than 836 * here, and is thus shared across PCI child drivers and any other 837 * consumers of PCI config space (such as the ACPI subsystem). 838 * 839 * The configuration space addresses come in as pointers. This is fine on 840 * a 32-bit system, where the VM space and configuration space are the same 841 * size. It's not such a good idea on a 64-bit system, where memory 842 * addresses are twice as large as configuration space addresses. At some 843 * point in the call tree we need to take a stand and say "you are 32-bit 844 * from this time forth", and this seems like a nice self-contained place. 845 */ 846 847 uint8_t 848 pci_config_rd8(ddi_acc_impl_t *hdlp, uint8_t *addr) 849 { 850 pci_acc_cfblk_t *cfp; 851 uint8_t rval; 852 int reg; 853 854 ASSERT64(((uintptr_t)addr >> 32) == 0); 855 856 reg = (int)(uintptr_t)addr; 857 858 cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 859 860 rval = (*pci_getb_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum, 861 reg); 862 863 return (rval); 864 } 865 866 void 867 pci_config_rep_rd8(ddi_acc_impl_t *hdlp, uint8_t *host_addr, 868 uint8_t *dev_addr, size_t repcount, uint_t flags) 869 { 870 uint8_t *h, *d; 871 872 h = host_addr; 873 d = dev_addr; 874 875 if (flags == DDI_DEV_AUTOINCR) 876 for (; repcount; repcount--) 877 *h++ = pci_config_rd8(hdlp, d++); 878 else 879 for (; repcount; repcount--) 880 *h++ = pci_config_rd8(hdlp, d); 881 } 882 883 uint16_t 884 pci_config_rd16(ddi_acc_impl_t *hdlp, uint16_t *addr) 885 { 886 pci_acc_cfblk_t *cfp; 887 uint16_t rval; 888 int reg; 889 890 ASSERT64(((uintptr_t)addr >> 32) == 0); 891 892 reg = (int)(uintptr_t)addr; 893 894 cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 895 896 rval = (*pci_getw_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum, 897 reg); 898 899 return (rval); 900 } 901 902 void 903 pci_config_rep_rd16(ddi_acc_impl_t *hdlp, uint16_t *host_addr, 904 uint16_t *dev_addr, size_t repcount, uint_t flags) 905 { 906 uint16_t *h, *d; 907 908 h = host_addr; 909 d = dev_addr; 910 911 if (flags == DDI_DEV_AUTOINCR) 912 for (; repcount; repcount--) 913 *h++ = pci_config_rd16(hdlp, d++); 914 else 915 for (; repcount; repcount--) 916 *h++ = pci_config_rd16(hdlp, d); 917 } 918 919 uint32_t 920 pci_config_rd32(ddi_acc_impl_t *hdlp, uint32_t *addr) 921 { 922 pci_acc_cfblk_t *cfp; 923 uint32_t rval; 924 int reg; 925 926 ASSERT64(((uintptr_t)addr >> 32) == 0); 927 928 reg = (int)(uintptr_t)addr; 929 930 cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 931 932 rval = (*pci_getl_func)(cfp->c_busnum, cfp->c_devnum, 933 cfp->c_funcnum, reg); 934 935 return (rval); 936 } 937 938 void 939 pci_config_rep_rd32(ddi_acc_impl_t *hdlp, uint32_t *host_addr, 940 uint32_t *dev_addr, size_t repcount, uint_t flags) 941 { 942 uint32_t *h, *d; 943 944 h = host_addr; 945 d = dev_addr; 946 947 if (flags == DDI_DEV_AUTOINCR) 948 for (; repcount; repcount--) 949 *h++ = pci_config_rd32(hdlp, d++); 950 else 951 for (; repcount; repcount--) 952 *h++ = pci_config_rd32(hdlp, d); 953 } 954 955 956 void 957 pci_config_wr8(ddi_acc_impl_t *hdlp, uint8_t *addr, uint8_t value) 958 { 959 pci_acc_cfblk_t *cfp; 960 int reg; 961 962 ASSERT64(((uintptr_t)addr >> 32) == 0); 963 964 reg = (int)(uintptr_t)addr; 965 966 cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 967 968 (*pci_putb_func)(cfp->c_busnum, cfp->c_devnum, 969 cfp->c_funcnum, reg, value); 970 } 971 972 void 973 pci_config_rep_wr8(ddi_acc_impl_t *hdlp, uint8_t *host_addr, 974 uint8_t *dev_addr, size_t repcount, uint_t flags) 975 { 976 uint8_t *h, *d; 977 978 h = host_addr; 979 d = dev_addr; 980 981 if (flags == DDI_DEV_AUTOINCR) 982 for (; repcount; repcount--) 983 pci_config_wr8(hdlp, d++, *h++); 984 else 985 for (; repcount; repcount--) 986 pci_config_wr8(hdlp, d, *h++); 987 } 988 989 void 990 pci_config_wr16(ddi_acc_impl_t *hdlp, uint16_t *addr, uint16_t value) 991 { 992 pci_acc_cfblk_t *cfp; 993 int reg; 994 995 ASSERT64(((uintptr_t)addr >> 32) == 0); 996 997 reg = (int)(uintptr_t)addr; 998 999 cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 1000 1001 (*pci_putw_func)(cfp->c_busnum, cfp->c_devnum, 1002 cfp->c_funcnum, reg, value); 1003 } 1004 1005 void 1006 pci_config_rep_wr16(ddi_acc_impl_t *hdlp, uint16_t *host_addr, 1007 uint16_t *dev_addr, size_t repcount, uint_t flags) 1008 { 1009 uint16_t *h, *d; 1010 1011 h = host_addr; 1012 d = dev_addr; 1013 1014 if (flags == DDI_DEV_AUTOINCR) 1015 for (; repcount; repcount--) 1016 pci_config_wr16(hdlp, d++, *h++); 1017 else 1018 for (; repcount; repcount--) 1019 pci_config_wr16(hdlp, d, *h++); 1020 } 1021 1022 void 1023 pci_config_wr32(ddi_acc_impl_t *hdlp, uint32_t *addr, uint32_t value) 1024 { 1025 pci_acc_cfblk_t *cfp; 1026 int reg; 1027 1028 ASSERT64(((uintptr_t)addr >> 32) == 0); 1029 1030 reg = (int)(uintptr_t)addr; 1031 1032 cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private; 1033 1034 (*pci_putl_func)(cfp->c_busnum, cfp->c_devnum, 1035 cfp->c_funcnum, reg, value); 1036 } 1037 1038 void 1039 pci_config_rep_wr32(ddi_acc_impl_t *hdlp, uint32_t *host_addr, 1040 uint32_t *dev_addr, size_t repcount, uint_t flags) 1041 { 1042 uint32_t *h, *d; 1043 1044 h = host_addr; 1045 d = dev_addr; 1046 1047 if (flags == DDI_DEV_AUTOINCR) 1048 for (; repcount; repcount--) 1049 pci_config_wr32(hdlp, d++, *h++); 1050 else 1051 for (; repcount; repcount--) 1052 pci_config_wr32(hdlp, d, *h++); 1053 } 1054 1055 uint64_t 1056 pci_config_rd64(ddi_acc_impl_t *hdlp, uint64_t *addr) 1057 { 1058 uint32_t lw_val; 1059 uint32_t hi_val; 1060 uint32_t *dp; 1061 uint64_t val; 1062 1063 dp = (uint32_t *)addr; 1064 lw_val = pci_config_rd32(hdlp, dp); 1065 dp++; 1066 hi_val = pci_config_rd32(hdlp, dp); 1067 val = ((uint64_t)hi_val << 32) | lw_val; 1068 return (val); 1069 } 1070 1071 void 1072 pci_config_wr64(ddi_acc_impl_t *hdlp, uint64_t *addr, uint64_t value) 1073 { 1074 uint32_t lw_val; 1075 uint32_t hi_val; 1076 uint32_t *dp; 1077 1078 dp = (uint32_t *)addr; 1079 lw_val = (uint32_t)(value & 0xffffffff); 1080 hi_val = (uint32_t)(value >> 32); 1081 pci_config_wr32(hdlp, dp, lw_val); 1082 dp++; 1083 pci_config_wr32(hdlp, dp, hi_val); 1084 } 1085 1086 void 1087 pci_config_rep_rd64(ddi_acc_impl_t *hdlp, uint64_t *host_addr, 1088 uint64_t *dev_addr, size_t repcount, uint_t flags) 1089 { 1090 if (flags == DDI_DEV_AUTOINCR) { 1091 for (; repcount; repcount--) 1092 *host_addr++ = pci_config_rd64(hdlp, dev_addr++); 1093 } else { 1094 for (; repcount; repcount--) 1095 *host_addr++ = pci_config_rd64(hdlp, dev_addr); 1096 } 1097 } 1098 1099 void 1100 pci_config_rep_wr64(ddi_acc_impl_t *hdlp, uint64_t *host_addr, 1101 uint64_t *dev_addr, size_t repcount, uint_t flags) 1102 { 1103 if (flags == DDI_DEV_AUTOINCR) { 1104 for (; repcount; repcount--) 1105 pci_config_wr64(hdlp, host_addr++, *dev_addr++); 1106 } else { 1107 for (; repcount; repcount--) 1108 pci_config_wr64(hdlp, host_addr++, *dev_addr); 1109 } 1110 } 1111 1112 1113 /* 1114 * Enable Legacy PCI config space access for the following four north bridges 1115 * Host bridge: AMD HyperTransport Technology Configuration 1116 * Host bridge: AMD Address Map 1117 * Host bridge: AMD DRAM Controller 1118 * Host bridge: AMD Miscellaneous Control 1119 */ 1120 int 1121 is_amd_northbridge(dev_info_t *dip) 1122 { 1123 int vendor_id, device_id; 1124 1125 vendor_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1126 "vendor-id", -1); 1127 device_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1128 "device-id", -1); 1129 1130 if (IS_AMD_NTBRIDGE(vendor_id, device_id)) 1131 return (0); 1132 1133 return (1); 1134 } 1135