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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 /* 28 * Starcat PCI SBBC Nexus Driver. 29 * 30 * This source code's compiled binary runs on both a Starcat System 31 * Controller (SSC) and a Starcat Domain. One of the SBBC hardware 32 * registers is read during attach(9e) in order to determine which 33 * environment the driver is executing on. 34 * 35 * On both the SSC and the Domain, this driver provides nexus driver 36 * services to its Device Tree children. Note that the children in 37 * each environment are not necessarily the same. 38 * 39 * This driver allows one concurrent open(2) of its associated device 40 * (/dev/sbbc0). The client uses the file descriptor to issue 41 * ioctl(2)'s in order to read and write from the 2MB (PCI) space 42 * reserved for "SBBC Internal Registers". Among other things, 43 * these registers consist of command/control/status registers for 44 * devices such as Console Bus, I2C, EPLD, IOSRAM, and JTAG. The 2MB 45 * space is very sparse; EINVAL is returned if a reserved or unaligned 46 * address is specified in the ioctl(2). 47 * 48 * Note that the 2MB region reserved for SBBC Internal Registers is 49 * a subset of the 128MB of PCI address space addressable by the SBBC 50 * ASIC. Address space outside of the 2MB (such as the 64MB reserved 51 * for the Console Bus) is not accessible via this driver. 52 * 53 * Also, note that the SBBC Internal Registers are only read and 54 * written by the SSC; no process on the Domain accesses these 55 * registers. As a result, the registers are unmapped (when running 56 * on the Domain) near the end of attach(9e) processing. This conserves 57 * kernel virtual address space resources (as one instance of the driver 58 * is created for each Domain-side IO assembly). (To be complete, only 59 * one instance of the driver is created on the SSC). 60 */ 61 62 #include <sys/types.h> 63 64 #include <sys/conf.h> /* req. by dev_ops flags MTSAFE etc. */ 65 #include <sys/ddi.h> 66 #include <sys/sunddi.h> 67 #include <sys/ddi_impldefs.h> 68 #include <sys/ddi_subrdefs.h> 69 #include <sys/pci.h> 70 #include <sys/pci/pci_nexus.h> 71 #include <sys/autoconf.h> 72 #include <sys/cmn_err.h> 73 #include <sys/param.h> 74 #include <sys/errno.h> 75 #include <sys/kmem.h> 76 #include <sys/debug.h> 77 #include <sys/sysmacros.h> 78 #include <sys/machsystm.h> 79 #include <sys/modctl.h> 80 #include <sys/stat.h> 81 82 83 #include <sys/sbbcreg.h> /* hw description */ 84 #include <sys/sbbcvar.h> /* driver description */ 85 #include <sys/sbbcio.h> /* ioctl description */ 86 87 #define getprop(dip, name, addr, intp) \ 88 ddi_getlongprop(DDI_DEV_T_ANY, (dip), DDI_PROP_DONTPASS, \ 89 (name), (caddr_t)(addr), (intp)) 90 91 /* driver entry point fn definitions */ 92 static int sbbc_open(dev_t *, int, int, cred_t *); 93 static int sbbc_close(dev_t, int, int, cred_t *); 94 static int sbbc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 95 96 /* configuration entry point fn definitions */ 97 static int sbbc_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 98 static int sbbc_attach(dev_info_t *, ddi_attach_cmd_t); 99 static int sbbc_detach(dev_info_t *, ddi_detach_cmd_t); 100 101 /* local utility routines */ 102 /* 103 * NOTE - sbbc_offset_valid contains detailed address information taken from 104 * the Serengeti Architecture Programmer's Reference Manual. If any 105 * changes are made to the SBBC registers, this routine may need to be 106 * updated. 107 */ 108 static int sbbc_offset_valid(uint32_t offset); 109 110 /* 111 * function prototypes for bus ops routines: 112 */ 113 static int sbbc_busmap(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 114 off_t offset, off_t len, caddr_t *addrp); 115 static int sbbc_ctlops(dev_info_t *dip, dev_info_t *rdip, 116 ddi_ctl_enum_t op, void *arg, void *result); 117 118 static int sbbc_intr_ops(dev_info_t *dip, dev_info_t *rdip, 119 ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); 120 static int sbbc_add_intr_impl(dev_info_t *dip, dev_info_t *rdip, 121 ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); 122 static int sbbc_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip, 123 ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); 124 static int sbbc_update_intr_state(dev_info_t *dip, dev_info_t *rdip, 125 ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); 126 127 static int sbbc_apply_range(struct sbbcsoft *sbbc_p, dev_info_t *rdip, 128 sbbc_child_regspec_t *child_rp, pci_regspec_t *rp); 129 130 static int sbbc_init(struct sbbcsoft *); 131 132 static uint_t sbbc_intr_wrapper(caddr_t arg); 133 134 static int sbbc_get_ranges(struct sbbcsoft *); 135 static int sbbc_config4pci(struct sbbcsoft *); 136 static int sbbc_initchild(dev_info_t *, dev_info_t *, dev_info_t *); 137 static int sbbc_uninitchild(dev_info_t *, dev_info_t *); 138 static void sbbc_remove_reg_maps(struct sbbcsoft *); 139 140 /* debugging functions */ 141 #ifdef DEBUG 142 uint32_t sbbc_dbg_flags = 0x0; 143 static void sbbc_dbg(uint32_t flag, dev_info_t *dip, char *fmt, 144 uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5); 145 static void sbbc_dump_devid(dev_info_t *, struct sbbcsoft *, int instance); 146 #endif 147 148 /* 149 * For tracing, allocate space for the trace buffer 150 */ 151 #if defined(SBBC_TRACE) 152 struct sbbctrace sbbctrace_buffer[NSBBCTRACE+1]; 153 struct sbbctrace *sbbctrace_ptr; 154 int sbbctrace_count; 155 #endif 156 157 /* 158 * Local declarations and variables 159 */ 160 161 static void *sbbcsoft_statep; 162 163 /* Determines whether driver is executing on System Controller or Domain */ 164 int sbbc_scmode = FALSE; 165 166 /* 167 * ops stuff. 168 */ 169 static struct bus_ops sbbc_bus_ops = { 170 BUSO_REV, 171 sbbc_busmap, 172 0, 173 0, 174 0, 175 NULL, /* (*bus_map_fault)() */ 176 ddi_no_dma_map, 177 ddi_no_dma_allochdl, 178 ddi_no_dma_freehdl, /* (*bus_dma_freehdl)() */ 179 ddi_no_dma_bindhdl, /* (*bus_dma_bindhdl)() */ 180 ddi_no_dma_unbindhdl, /* (*bus_dma_unbindhdl)() */ 181 ddi_no_dma_flush, /* (*bus_dma_flush)() */ 182 ddi_no_dma_win, /* (*bus_dma_win)() */ 183 ddi_no_dma_mctl, /* (*bus_dma_ctl)() */ 184 sbbc_ctlops, 185 ddi_bus_prop_op, 186 0, /* (*bus_get_eventcookie)(); */ 187 0, /* (*bus_add_eventcall)(); */ 188 0, /* (*bus_remove_eventcall)(); */ 189 0, /* (*bus_post_event)(); */ 190 0, /* (*bus_intr_ctl)(); */ 191 0, /* (*bus_config)(); */ 192 0, /* (*bus_unconfig)(); */ 193 0, /* (*bus_fm_init)(); */ 194 0, /* (*bus_fm_fini)(); */ 195 0, /* (*bus_fm_access_enter)(); */ 196 0, /* (*bus_fm_access_exit)(); */ 197 0, /* (*bus_power)(); */ 198 sbbc_intr_ops /* (*bus_intr_op)(); */ 199 }; 200 201 /* 202 * cb_ops 203 */ 204 static struct cb_ops sbbc_cb_ops = { 205 sbbc_open, /* cb_open */ 206 sbbc_close, /* cb_close */ 207 nodev, /* cb_strategy */ 208 nodev, /* cb_print */ 209 nodev, /* cb_dump */ 210 nodev, /* cb_read */ 211 nodev, /* cb_write */ 212 sbbc_ioctl, /* cb_ioctl */ 213 nodev, /* cb_devmap */ 214 nodev, /* cb_mmap */ 215 nodev, /* cb_segmap */ 216 nochpoll, /* cb_chpoll */ 217 ddi_prop_op, /* cb_prop_op */ 218 NULL, /* cb_stream */ 219 (int)(D_NEW | D_MP) /* cb_flag */ 220 }; 221 222 /* 223 * Declare ops vectors for auto configuration. 224 */ 225 struct dev_ops sbbc_ops = { 226 DEVO_REV, /* devo_rev */ 227 0, /* devo_refcnt */ 228 sbbc_getinfo, /* devo_getinfo */ 229 nulldev, /* devo_identify */ 230 nulldev, /* devo_probe */ 231 sbbc_attach, /* devo_attach */ 232 sbbc_detach, /* devo_detach */ 233 nodev, /* devo_reset */ 234 &sbbc_cb_ops, /* devo_cb_ops */ 235 &sbbc_bus_ops, /* devo_bus_ops */ 236 nulldev, /* devo_power */ 237 ddi_quiesce_not_supported, /* devo_quiesce */ 238 }; 239 240 /* 241 * Loadable module support. 242 */ 243 extern struct mod_ops mod_driverops; 244 245 static struct modldrv sbbcmodldrv = { 246 &mod_driverops, /* type of module - driver */ 247 "PCI Sbbc Nexus Driver", 248 &sbbc_ops, 249 }; 250 251 static struct modlinkage sbbcmodlinkage = { 252 MODREV_1, 253 &sbbcmodldrv, 254 NULL 255 }; 256 257 int 258 _init(void) 259 { 260 int error; 261 262 if ((error = ddi_soft_state_init(&sbbcsoft_statep, 263 sizeof (struct sbbcsoft), 1)) != 0) 264 return (error); 265 if ((error = mod_install(&sbbcmodlinkage)) != 0) 266 ddi_soft_state_fini(&sbbcsoft_statep); 267 268 return (error); 269 } 270 271 int 272 _fini(void) 273 { 274 int error; 275 276 if ((error = mod_remove(&sbbcmodlinkage)) == 0) 277 ddi_soft_state_fini(&sbbcsoft_statep); 278 279 return (error); 280 } 281 282 int 283 _info(struct modinfo *modinfop) 284 { 285 return (mod_info(&sbbcmodlinkage, modinfop)); 286 } 287 288 static int 289 sbbc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 290 { 291 int instance; 292 char name[32]; 293 struct sbbcsoft *sbbcsoftp; 294 struct ddi_device_acc_attr attr; 295 uint32_t sbbc_id_reg; 296 297 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 298 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 299 attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC; 300 301 /* initialize tracing */ 302 SBBCTRACEINIT(); 303 304 SBBC_DBG0(SBBC_DBG_ATTACH, dip, "Attaching\n"); 305 306 instance = ddi_get_instance(dip); 307 switch (cmd) { 308 case DDI_ATTACH: 309 break; 310 case DDI_RESUME: 311 if (!(sbbcsoftp = 312 ddi_get_soft_state(sbbcsoft_statep, instance))) { 313 cmn_err(CE_WARN, "sbbc_attach:resume: unable " 314 "to acquire sbbcsoftp for instance %d", 315 instance); 316 return (DDI_FAILURE); 317 } 318 mutex_enter(&sbbcsoftp->umutex); 319 if (!sbbcsoftp->suspended) { 320 mutex_exit(&sbbcsoftp->umutex); 321 return (DDI_FAILURE); 322 } 323 sbbcsoftp->suspended = 0; 324 mutex_exit(&sbbcsoftp->umutex); 325 return (DDI_SUCCESS); 326 327 default: 328 return (DDI_FAILURE); 329 } 330 331 if (ddi_soft_state_zalloc(sbbcsoft_statep, instance) != 0) { 332 cmn_err(CE_WARN, "sbbc_attach: Unable to allocate statep " 333 "for instance %d", instance); 334 return (DDI_FAILURE); 335 } 336 337 sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance); 338 339 if (sbbcsoftp == NULL) { 340 cmn_err(CE_WARN, "sbbc_attach: Unable to acquire " 341 "sbbcsoftp for instance %d", instance); 342 ddi_soft_state_free(sbbcsoft_statep, instance); 343 return (DDI_FAILURE); 344 } 345 346 sbbcsoftp->instance = instance; 347 sbbcsoftp->dip = dip; 348 sbbcsoftp->oflag = FALSE; 349 350 /* 351 * Read our ranges property from OBP to map children space. 352 * And setup the internal structure for a later use when 353 * a child gets initialized. 354 */ 355 if (sbbc_get_ranges(sbbcsoftp)) { 356 cmn_err(CE_WARN, "sbbc_attach: Unable to read sbbc " 357 "ranges from OBP %d", instance); 358 ddi_soft_state_free(sbbcsoft_statep, instance); 359 return (DDI_FAILURE); 360 } 361 362 if (sbbc_config4pci(sbbcsoftp)) { 363 cmn_err(CE_WARN, "sbbc_attach: Unable to configure " 364 "sbbc on PCI %d", instance); 365 kmem_free(sbbcsoftp->rangep, sbbcsoftp->range_len); 366 ddi_soft_state_free(sbbcsoft_statep, instance); 367 return (DDI_FAILURE); 368 } 369 370 mutex_init(&sbbcsoftp->umutex, NULL, MUTEX_DRIVER, (void *)NULL); 371 mutex_init(&sbbcsoftp->sbbc_intr_mutex, NULL, 372 MUTEX_DRIVER, (void *)NULL); 373 374 /* Map SBBC's Internal Registers */ 375 if (ddi_regs_map_setup(dip, 1, (caddr_t *)&sbbcsoftp->pci_sbbc_map, 376 offsetof(struct pci_sbbc, sbbc_internal_regs), 377 sizeof (struct sbbc_regs_map), &attr, 378 &sbbcsoftp->pci_sbbc_map_handle) != DDI_SUCCESS) { 379 cmn_err(CE_WARN, "(%d):sbbc_attach failed to map sbbc_reg", 380 instance); 381 goto failed; 382 } 383 384 SBBC_DBG1(SBBC_DBG_ATTACH, dip, "Mapped sbbc at %lx\n", 385 sbbcsoftp->pci_sbbc_map); 386 #ifdef DEBUG 387 sbbc_dump_devid(dip, sbbcsoftp, instance); 388 #endif 389 /* 390 * Read a hardware register to determine if we are executing on 391 * a Starcat System Controller or a Starcat Domain. 392 */ 393 sbbc_id_reg = ddi_get32(sbbcsoftp->pci_sbbc_map_handle, 394 &sbbcsoftp->pci_sbbc_map->device_conf); 395 396 if (sbbc_id_reg & SBBC_SC_MODE) { 397 sbbc_scmode = TRUE; 398 SBBC_DBG1(SBBC_DBG_ATTACH, dip, "SBBC(%d) nexus running " 399 "in System Controller Mode.\n", instance); 400 401 /* initialize SBBC ASIC */ 402 if (!sbbc_init(sbbcsoftp)) { 403 goto failed; 404 } 405 } else { 406 sbbc_scmode = FALSE; 407 SBBC_DBG1(SBBC_DBG_ATTACH, dip, "SBBC(%d) nexus " 408 "running in Domain Mode.\n", instance); 409 410 /* initialize SBBC ASIC before we unmap registers */ 411 if (!sbbc_init(sbbcsoftp)) { 412 goto failed; 413 } 414 415 /* 416 * Access to SBBC registers is no longer needed. Unmap 417 * the registers to conserve kernel virtual address space. 418 */ 419 SBBC_DBG1(SBBC_DBG_ATTACH, dip, "SBBC(%d): unmap " 420 "SBBC registers\n", instance); 421 sbbc_remove_reg_maps(sbbcsoftp); 422 sbbcsoftp->pci_sbbc_map = NULL; 423 } 424 425 (void) sprintf(name, "sbbc%d", instance); 426 427 if (ddi_create_minor_node(dip, name, S_IFCHR, instance, NULL, 428 0) == DDI_FAILURE) { 429 ddi_remove_minor_node(dip, NULL); 430 goto failed; 431 } 432 433 ddi_report_dev(dip); 434 435 SBBC_DBG0(SBBC_DBG_ATTACH, dip, "Attached successfully\n"); 436 437 return (DDI_SUCCESS); 438 439 failed: 440 mutex_destroy(&sbbcsoftp->sbbc_intr_mutex); 441 mutex_destroy(&sbbcsoftp->umutex); 442 443 sbbc_remove_reg_maps(sbbcsoftp); 444 kmem_free(sbbcsoftp->rangep, sbbcsoftp->range_len); 445 ddi_soft_state_free(sbbcsoft_statep, instance); 446 447 SBBC_DBG0(SBBC_DBG_ATTACH, dip, "Attach failed\n"); 448 449 return (DDI_FAILURE); 450 } 451 452 static int 453 sbbc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 454 { 455 int instance; 456 struct sbbcsoft *sbbcsoftp; 457 458 SBBCTRACE(sbbc_detach, 'DETA', dip); 459 460 instance = ddi_get_instance(dip); 461 462 switch (cmd) { 463 case DDI_DETACH: 464 break; 465 466 case DDI_SUSPEND: 467 if (!(sbbcsoftp = 468 ddi_get_soft_state(sbbcsoft_statep, instance))) { 469 cmn_err(CE_WARN, 470 "sbbc_detach: unable to get softstate %p", 471 (void *)sbbcsoftp); 472 return (DDI_FAILURE); 473 } 474 mutex_enter(&sbbcsoftp->umutex); 475 if (sbbcsoftp->suspended) { 476 mutex_exit(&sbbcsoftp->umutex); 477 return (DDI_FAILURE); 478 } 479 sbbcsoftp->suspended = 1; 480 mutex_exit(&sbbcsoftp->umutex); 481 return (DDI_SUCCESS); 482 483 default: 484 return (DDI_FAILURE); 485 } 486 487 if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance))) { 488 cmn_err(CE_WARN, "sbbc_detach: unable to get softstate %p", 489 (void *)sbbcsoftp); 490 return (DDI_FAILURE); 491 } 492 493 ddi_remove_minor_node(dip, NULL); 494 495 mutex_destroy(&sbbcsoftp->sbbc_intr_mutex); 496 mutex_destroy(&sbbcsoftp->umutex); 497 498 sbbc_remove_reg_maps(sbbcsoftp); 499 kmem_free(sbbcsoftp->rangep, sbbcsoftp->range_len); 500 501 ddi_soft_state_free(sbbcsoft_statep, instance); 502 503 return (DDI_SUCCESS); 504 505 } 506 507 508 /* 509 * Translate child's address into parents. 510 */ 511 static int 512 sbbc_busmap(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 513 off_t off, off_t len, caddr_t *addrp) 514 { 515 struct sbbcsoft *sbbcsoftp; 516 sbbc_child_regspec_t *child_rp, *child_regs; 517 pci_regspec_t pci_reg; 518 ddi_map_req_t p_map_request; 519 int rnumber, i, n; 520 int rval = DDI_SUCCESS; 521 int instance; 522 523 SBBC_DBG4(SBBC_DBG_BUSMAP, dip, 524 "mapping child %s, type %llx, off %llx, len %llx\n", 525 ddi_driver_name(rdip), mp->map_type, off, len); 526 527 SBBCTRACE(sbbc_busmap, 'BMAP', mp); 528 529 /* 530 * Handle the mapping according to its type. 531 */ 532 instance = ddi_get_instance(dip); 533 if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance))) 534 return (DDI_FAILURE); 535 536 switch (mp->map_type) { 537 case DDI_MT_REGSPEC: 538 539 /* 540 * We assume the register specification is in sbbc format. 541 * We must convert it into a PCI format regspec and pass 542 * the request to our parent. 543 */ 544 child_rp = (sbbc_child_regspec_t *)mp->map_obj.rp; 545 break; 546 547 case DDI_MT_RNUMBER: 548 549 /* 550 * map_type 0 551 * Get the "reg" property from the device node and convert 552 * it to our parent's format. 553 */ 554 rnumber = mp->map_obj.rnumber; 555 556 /* get the requester's reg property */ 557 if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 558 "reg", (caddr_t)&child_regs, &i) != DDI_SUCCESS) { 559 cmn_err(CE_WARN, 560 "SBBC: couldn't get %s ranges property %d", 561 ddi_get_name(sbbcsoftp->dip), instance); 562 return (DDI_ME_RNUMBER_RANGE); 563 } 564 n = i / sizeof (sbbc_child_regspec_t); 565 566 if (rnumber < 0 || rnumber >= n) { 567 kmem_free(child_regs, i); 568 return (DDI_ME_RNUMBER_RANGE); 569 } 570 child_rp = &child_regs[rnumber]; 571 break; 572 573 default: 574 return (DDI_ME_INVAL); 575 576 } 577 578 /* Adjust our reg property with offset and length */ 579 child_rp->addr_low += off; 580 581 if (len) 582 child_rp->size = len; 583 584 /* 585 * Combine this reg prop. into our parents PCI address using the ranges 586 * property. 587 */ 588 rval = sbbc_apply_range(sbbcsoftp, rdip, child_rp, &pci_reg); 589 590 if (mp->map_type == DDI_MT_RNUMBER) 591 kmem_free(child_regs, i); 592 593 if (rval != DDI_SUCCESS) 594 return (rval); 595 596 p_map_request = *mp; 597 p_map_request.map_type = DDI_MT_REGSPEC; 598 p_map_request.map_obj.rp = (struct regspec *)&pci_reg; 599 600 /* Send it to PCI nexus to map into the PCI space */ 601 rval = ddi_map(dip, &p_map_request, 0, 0, addrp); 602 603 return (rval); 604 605 } 606 607 608 /* new intr_ops structure */ 609 static int 610 sbbc_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 611 ddi_intr_handle_impl_t *hdlp, void *result) 612 { 613 int ret = DDI_SUCCESS; 614 615 switch (intr_op) { 616 case DDI_INTROP_GETCAP: 617 *(int *)result = DDI_INTR_FLAG_LEVEL; 618 break; 619 case DDI_INTROP_ALLOC: 620 *(int *)result = hdlp->ih_scratch1; 621 break; 622 case DDI_INTROP_FREE: 623 break; 624 case DDI_INTROP_GETPRI: 625 if (hdlp->ih_pri == 0) { 626 hdlp->ih_pri = 0x1; 627 628 cmn_err(CE_WARN, "%s%d assigning default interrupt " 629 "level %d for device %s%d", ddi_driver_name(dip), 630 ddi_get_instance(dip), hdlp->ih_pri, 631 ddi_driver_name(rdip), ddi_get_instance(rdip)); 632 } 633 634 *(int *)result = hdlp->ih_pri; 635 636 break; 637 case DDI_INTROP_ADDISR: 638 ret = sbbc_add_intr_impl(dip, rdip, intr_op, hdlp, result); 639 break; 640 case DDI_INTROP_REMISR: 641 ret = sbbc_remove_intr_impl(dip, rdip, intr_op, hdlp, result); 642 break; 643 case DDI_INTROP_ENABLE: 644 ret = sbbc_update_intr_state(dip, rdip, intr_op, hdlp, &result); 645 break; 646 case DDI_INTROP_DISABLE: 647 ret = sbbc_update_intr_state(dip, rdip, intr_op, hdlp, &result); 648 break; 649 case DDI_INTROP_NINTRS: 650 case DDI_INTROP_NAVAIL: 651 *(int *)result = i_ddi_get_intx_nintrs(rdip); 652 break; 653 case DDI_INTROP_SUPPORTED_TYPES: 654 /* PCI nexus driver supports only fixed interrupts */ 655 *(int *)result = i_ddi_get_intx_nintrs(rdip) ? 656 DDI_INTR_TYPE_FIXED : 0; 657 break; 658 default: 659 ret = DDI_ENOTSUP; 660 break; 661 } 662 663 return (ret); 664 } 665 666 667 static int 668 sbbc_add_intr_impl(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 669 ddi_intr_handle_impl_t *hdlp, void *result) 670 { 671 sbbcsoft_t *sbbcsoftp; 672 sbbc_child_intr_t *childintr; 673 int instance, i, rval = DDI_SUCCESS; 674 675 SBBC_DBG2(SBBC_DBG_INTR, dip, 676 "add: rdip 0x%llx hdlp 0x%llx\n", rdip, hdlp); 677 678 /* insert the sbbc isr wrapper instead */ 679 instance = ddi_get_instance(dip); 680 if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance))) 681 return (DDI_FAILURE); 682 683 childintr = kmem_zalloc(sizeof (struct sbbc_child_intr), KM_SLEEP); 684 685 childintr->name = ddi_get_name(rdip); 686 childintr->inum = hdlp->ih_inum; 687 childintr->intr_handler = hdlp->ih_cb_func; 688 childintr->arg1 = hdlp->ih_cb_arg1; 689 childintr->arg2 = hdlp->ih_cb_arg2; 690 childintr->status = SBBC_INTR_STATE_DISABLE; 691 692 for (i = 0; i < MAX_SBBC_DEVICES; i++) { 693 if (sbbcsoftp->child_intr[i] == NULL) { 694 sbbcsoftp->child_intr[i] = childintr; 695 break; 696 } 697 } 698 699 DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, 700 (ddi_intr_handler_t *)sbbc_intr_wrapper, 701 (caddr_t)sbbcsoftp, NULL); 702 703 if ((rval = i_ddi_intr_ops(dip, rdip, intr_op, 704 hdlp, result)) != DDI_SUCCESS) { 705 cmn_err(CE_WARN, "sbbc%d: failed to add intr for %s", 706 instance, ddi_get_name(rdip)); 707 kmem_free(childintr, sizeof (struct sbbc_child_intr)); 708 if (i < MAX_SBBC_DEVICES) 709 sbbcsoftp->child_intr[i] = NULL; 710 } 711 712 /* 713 * Restore original interrupt handler 714 * and arguments in interrupt handle. 715 */ 716 DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, childintr->intr_handler, 717 childintr->arg1, childintr->arg2); 718 719 return (rval); 720 } 721 722 static int 723 sbbc_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 724 ddi_intr_handle_impl_t *hdlp, void *result) 725 { 726 sbbcsoft_t *sbbcsoftp; 727 sbbc_child_intr_t *childintr; 728 int instance, i, rval = DDI_SUCCESS; 729 730 SBBC_DBG2(SBBC_DBG_INTR, dip, 731 "remove: rdip 0x%llx hdlp 0x%llx\n", rdip, hdlp); 732 733 instance = ddi_get_instance(dip); 734 if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance))) 735 return (DDI_FAILURE); 736 737 /* remove the sbbc isr wrapper instead */ 738 for (i = 0; i < MAX_SBBC_DEVICES; i++) { 739 if (sbbcsoftp->child_intr[i]) { 740 childintr = sbbcsoftp->child_intr[i]; 741 if (childintr->status == SBBC_INTR_STATE_DISABLE && 742 childintr->name == ddi_get_name(rdip)) { 743 /* put back child's inum */ 744 hdlp->ih_inum = childintr->inum; 745 break; 746 } 747 } 748 } 749 750 if (i >= MAX_SBBC_DEVICES) { 751 cmn_err(CE_WARN, "sbbc%d:obound failed to remove intr for %s", 752 instance, ddi_get_name(rdip)); 753 return (DDI_FAILURE); 754 } 755 756 if ((rval = i_ddi_intr_ops(dip, rdip, intr_op, 757 hdlp, result)) != DDI_SUCCESS) { 758 cmn_err(CE_WARN, "sbbc%d: failed to remove intr for %s", 759 instance, ddi_get_name(rdip)); 760 return (rval); 761 } 762 763 kmem_free(childintr, sizeof (struct sbbc_child_intr)); 764 sbbcsoftp->child_intr[i] = NULL; 765 766 return (rval); 767 } 768 769 770 static int 771 sbbc_update_intr_state(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 772 ddi_intr_handle_impl_t *hdlp, void *result) 773 { 774 sbbcsoft_t *sbbcsoftp; 775 sbbc_child_intr_t *childintr; 776 int instance, i; 777 int ret = DDI_SUCCESS; 778 779 SBBC_DBG2(SBBC_DBG_INTR, dip, "sbbc_update_intr_state: " 780 "rdip 0x%llx hdlp 0x%llx state 0x%x\n", rdip, hdlp); 781 782 instance = ddi_get_instance(dip); 783 if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance))) 784 return (DDI_FAILURE); 785 786 for (i = 0; i < MAX_SBBC_DEVICES; i++) { 787 if (sbbcsoftp->child_intr[i]) { 788 childintr = sbbcsoftp->child_intr[i]; 789 if (childintr->name == ddi_get_name(rdip)) 790 break; 791 } 792 } 793 794 if (i >= MAX_SBBC_DEVICES) { 795 cmn_err(CE_WARN, "sbbc%d: failed to update intr state for %s", 796 instance, ddi_get_name(rdip)); 797 return (DDI_FAILURE); 798 } 799 800 if ((ret = i_ddi_intr_ops(dip, rdip, intr_op, 801 hdlp, result)) != DDI_SUCCESS) { 802 cmn_err(CE_WARN, "sbbc%d: failed to update intr state for %s", 803 instance, ddi_get_name(rdip)); 804 return (ret); 805 } 806 807 /* Update the interrupt state */ 808 childintr->status = (intr_op == DDI_INTROP_ENABLE) ? 809 SBBC_INTR_STATE_ENABLE : SBBC_INTR_STATE_DISABLE; 810 811 return (ret); 812 } 813 814 815 /* 816 * This entry point is called before a child's probe or attach is called. 817 * The arg pointer points to child's dev_info_t structure. 818 */ 819 static int 820 sbbc_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, 821 void *arg, void *result) 822 { 823 sbbc_child_regspec_t *child_rp; 824 int i, n; 825 826 SBBC_DBG3(SBBC_DBG_CTLOPS, dip, 827 "Initializing %s, arg %x, op %x\n", 828 ddi_driver_name(rdip), arg, op); 829 830 SBBCTRACE(sbbc_ctlops, 'CTLO', arg); 831 832 switch (op) { 833 case DDI_CTLOPS_INITCHILD: { 834 return (sbbc_initchild(dip, rdip, (dev_info_t *)arg)); 835 } 836 837 case DDI_CTLOPS_UNINITCHILD: { 838 return (sbbc_uninitchild(rdip, (dev_info_t *)arg)); 839 } 840 841 case DDI_CTLOPS_REPORTDEV: 842 843 cmn_err(CE_CONT, "?%s%d at %s%d: offset %s\n", 844 ddi_driver_name(rdip), ddi_get_instance(rdip), 845 ddi_driver_name(dip), ddi_get_instance(dip), 846 ddi_get_name_addr(rdip)); 847 return (DDI_SUCCESS); 848 849 case DDI_CTLOPS_REGSIZE: 850 851 if (getprop(rdip, "reg", &child_rp, &i) != DDI_SUCCESS) { 852 return (DDI_FAILURE); 853 } 854 n = i / sizeof (sbbc_child_regspec_t); 855 if (*(int *)arg < 0 || *(int *)arg >= n) { 856 kmem_free(child_rp, i); 857 return (DDI_FAILURE); 858 } 859 *((off_t *)result) = child_rp[*(int *)arg].size; 860 kmem_free(child_rp, i); 861 return (DDI_SUCCESS); 862 863 case DDI_CTLOPS_NREGS: 864 865 if (getprop(rdip, "reg", &child_rp, &i) != DDI_SUCCESS) { 866 return (DDI_FAILURE); 867 } 868 *((uint_t *)result) = i / sizeof (sbbc_child_regspec_t); 869 kmem_free(child_rp, i); 870 return (DDI_SUCCESS); 871 } 872 873 /* 874 * Now pass the request up to our parent. 875 */ 876 SBBC_DBG0(SBBC_DBG_CTLOPS, dip, "Calling ddi_ctlops\n"); 877 878 return (ddi_ctlops(dip, rdip, op, arg, result)); 879 } 880 881 882 /* 883 * The following routine uses ranges property, that was read earlier, and 884 * takes child's reg property, and computes the complete address and size 885 * for the PCI parent to map. 886 */ 887 static int 888 sbbc_apply_range(struct sbbcsoft *sbbc_p, dev_info_t *rdip, 889 sbbc_child_regspec_t *child_rp, pci_regspec_t *rp) 890 { 891 int b; 892 int rval = DDI_SUCCESS; 893 struct sbbc_pci_rangespec *rangep = sbbc_p->rangep; 894 int nrange = sbbc_p->range_cnt; 895 896 SBBC_DBG4(SBBC_DBG_MAPRANGES, rdip, 897 "Applying ranges for %s, rangep %llx, child_rp %llx, range %x\n", 898 ddi_driver_name(rdip), sbbc_p->rangep, child_rp, nrange); 899 900 SBBCTRACE(sbbc_apply_range, 'APPL', sbbc_p); 901 902 for (b = 0; b < nrange; ++b, ++rangep) { 903 904 /* Make sure the correct range is being mapped */ 905 if (child_rp->addr_hi == rangep->sbbc_phys_hi) 906 /* See if we fit in this range */ 907 if ((child_rp->addr_low >= 908 rangep->sbbc_phys_low) && 909 ((child_rp->addr_low + child_rp->size - 1) 910 <= (rangep->sbbc_phys_low + 911 rangep->rng_size - 1))) { 912 uint_t addr_offset = child_rp->addr_low - 913 rangep->sbbc_phys_low; 914 /* 915 * Use the range entry to translate 916 * the SBBC physical address into the 917 * parents PCI space. 918 */ 919 rp->pci_phys_hi = 920 rangep->pci_phys_hi; 921 rp->pci_phys_mid = rangep->pci_phys_mid; 922 rp->pci_phys_low = 923 rangep->pci_phys_low + addr_offset; 924 rp->pci_size_hi = 0; 925 rp->pci_size_low = 926 min(child_rp->size, (rangep->rng_size - 927 addr_offset)); 928 929 break; 930 } 931 } 932 933 if (b == nrange) { 934 cmn_err(CE_WARN, "out_of_range %s", ddi_get_name(rdip)); 935 return (DDI_ME_REGSPEC_RANGE); 936 } 937 938 return (rval); 939 } 940 941 942 /* 943 * The following routine reads sbbc's ranges property from OBP and sets up 944 * its soft structure with it. 945 */ 946 static int 947 sbbc_get_ranges(struct sbbcsoft *sbbcsoftp) 948 { 949 struct sbbc_pci_rangespec *rangep; 950 int range_len, nrange; 951 952 if (ddi_getlongprop(DDI_DEV_T_ANY, sbbcsoftp->dip, DDI_PROP_DONTPASS, 953 "ranges", (caddr_t)&rangep, &range_len) != DDI_SUCCESS) { 954 cmn_err(CE_WARN, "SBBC: couldn't get %s ranges property %d", 955 ddi_get_name(sbbcsoftp->dip), sbbcsoftp->instance); 956 return (DDI_ME_REGSPEC_RANGE); 957 } 958 959 nrange = range_len / sizeof (struct sbbc_pci_rangespec); 960 961 if (!nrange) { 962 kmem_free(rangep, range_len); 963 return (DDI_FAILURE); 964 } 965 966 /* setup the soft structure with ranges info. */ 967 sbbcsoftp->rangep = rangep; 968 sbbcsoftp->range_cnt = nrange; 969 sbbcsoftp->range_len = range_len; 970 971 return (DDI_SUCCESS); 972 } 973 974 975 /* 976 * Configure the SBBC for PCI 977 */ 978 static int 979 sbbc_config4pci(struct sbbcsoft *sbbcsoftp) 980 { 981 ddi_acc_handle_t conf_handle; 982 uint16_t comm, vendid, devid, stat; 983 uint8_t revid; 984 985 #ifdef DEBUG 986 if (sbbc_dbg_flags & SBBC_DBG_PCICONF) { 987 cmn_err(CE_CONT, 988 "sbbc_config4pci: sbbcsoftp %p\n", (void *)sbbcsoftp); 989 } 990 #endif 991 if (pci_config_setup(sbbcsoftp->dip, &conf_handle) != DDI_SUCCESS) 992 return (1); 993 994 vendid = pci_config_get16(conf_handle, PCI_CONF_VENID); 995 devid = pci_config_get16(conf_handle, PCI_CONF_DEVID); 996 comm = pci_config_get16(conf_handle, PCI_CONF_COMM); 997 stat = pci_config_get16(conf_handle, PCI_CONF_STAT); 998 revid = pci_config_get8(conf_handle, PCI_CONF_REVID); 999 1000 #ifdef DEBUG 1001 if (sbbc_dbg_flags & SBBC_DBG_PCICONF) { 1002 cmn_err(CE_CONT, 1003 "SBBC vendid %x, devid %x, comm %x, stat %x, revid %x\n", 1004 vendid, devid, comm, stat, revid); 1005 } 1006 #endif 1007 comm = (PCI_COMM_ME | PCI_COMM_MAE | PCI_COMM_SERR_ENABLE | 1008 PCI_COMM_PARITY_DETECT); 1009 1010 pci_config_put16(conf_handle, PCI_CONF_COMM, comm); 1011 1012 comm = pci_config_get16(conf_handle, PCI_CONF_COMM); 1013 1014 #ifdef DEBUG 1015 if (sbbc_dbg_flags & SBBC_DBG_PCICONF) { 1016 cmn_err(CE_CONT, "comm %x\n", comm); 1017 } 1018 #endif 1019 pci_config_teardown(&conf_handle); 1020 1021 return (0); 1022 } 1023 1024 1025 /* ARGSUSED0 */ 1026 int 1027 sbbc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 1028 { 1029 dev_t dev = (dev_t)arg; 1030 struct sbbcsoft *sbbcsoftp; 1031 int instance, ret; 1032 1033 instance = getminor(dev); 1034 1035 SBBCTRACE(sbbc_getinfo, 'GINF', instance); 1036 1037 switch (infocmd) { 1038 case DDI_INFO_DEVT2DEVINFO: 1039 sbbcsoftp = (struct sbbcsoft *) 1040 ddi_get_soft_state(sbbcsoft_statep, instance); 1041 if (sbbcsoftp == NULL) { 1042 *result = (void *) NULL; 1043 ret = DDI_FAILURE; 1044 } else { 1045 *result = sbbcsoftp->dip; 1046 ret = DDI_SUCCESS; 1047 } 1048 break; 1049 case DDI_INFO_DEVT2INSTANCE: 1050 *result = (void *)(uintptr_t)instance; 1051 ret = DDI_SUCCESS; 1052 break; 1053 default: 1054 ret = DDI_FAILURE; 1055 break; 1056 } 1057 1058 return (ret); 1059 } 1060 1061 /*ARGSUSED1*/ 1062 static int 1063 sbbc_open(dev_t *dev, int flag, int otype, cred_t *credp) 1064 { 1065 struct sbbcsoft *sbbcsoftp; 1066 int instance; 1067 1068 /* check privilege of caller process */ 1069 if (drv_priv(credp)) { 1070 return (EPERM); 1071 } 1072 1073 instance = getminor(*dev); 1074 if (instance < 0) 1075 return (ENXIO); 1076 sbbcsoftp = (struct sbbcsoft *)ddi_get_soft_state(sbbcsoft_statep, 1077 instance); 1078 SBBCTRACE(sbbc_open, 'OPEN', sbbcsoftp); 1079 1080 if (sbbcsoftp == NULL) 1081 return (ENXIO); 1082 1083 mutex_enter(&sbbcsoftp->umutex); 1084 1085 /* check for exclusive access */ 1086 if ((sbbcsoftp->oflag == TRUE)) { 1087 mutex_exit(&sbbcsoftp->umutex); 1088 return (EBUSY); 1089 } 1090 sbbcsoftp->oflag = TRUE; 1091 1092 mutex_exit(&sbbcsoftp->umutex); 1093 1094 return (0); 1095 } 1096 1097 /*ARGSUSED1*/ 1098 static int 1099 sbbc_close(dev_t dev, int flag, int otype, cred_t *credp) 1100 { 1101 struct sbbcsoft *sbbcsoftp; 1102 int instance; 1103 1104 instance = getminor(dev); 1105 if (instance < 0) 1106 return (ENXIO); 1107 sbbcsoftp = (struct sbbcsoft *)ddi_get_soft_state(sbbcsoft_statep, 1108 instance); 1109 /* wait till all output activity has ceased */ 1110 1111 mutex_enter(&sbbcsoftp->umutex); 1112 1113 SBBCTRACE(sbbc_close, 'CLOS', sbbcsoftp); 1114 1115 sbbcsoftp->oflag = FALSE; 1116 1117 mutex_exit(&sbbcsoftp->umutex); 1118 1119 return (0); 1120 } 1121 1122 /*ARGSUSED2*/ 1123 static int 1124 sbbc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 1125 int *rvalp) 1126 { 1127 struct sbbcsoft *sbbcsoftp; 1128 1129 SBBCTRACE(sbbc_ioctl, 'IOCT', arg); 1130 1131 sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, getminor(dev)); 1132 1133 if (sbbcsoftp == NULL) { 1134 return (ENXIO); 1135 } 1136 1137 switch (cmd) { 1138 case SBBC_SBBCREG_WR: 1139 { 1140 struct ssc_sbbc_regio sbbcregs; 1141 uint64_t offset; 1142 1143 if (sbbc_scmode == FALSE) { 1144 /* then we're executing on Domain; Writes not allowed */ 1145 return (EINVAL); 1146 } 1147 1148 if (arg == (intptr_t)NULL) { 1149 return (ENXIO); 1150 } 1151 1152 if (ddi_copyin((caddr_t)arg, (caddr_t)&sbbcregs, 1153 sizeof (struct ssc_sbbc_regio), mode)) { 1154 cmn_err(CE_WARN, "sbbc_ioctl: copyin failed arg %p", 1155 (void *)arg); 1156 return (EFAULT); 1157 } 1158 1159 /* 1160 * Bug #4287186: SBBC driver on cp1500 doesn't check length for 1161 * reads or writes 1162 * Note that I've also added a check to make sure the offset is 1163 * valid, since misaligned (i.e. not on 16-byte boundary) 1164 * accesses or accesses to "Reserved" register offsets are 1165 * treated as unmapped by the SBBC. 1166 */ 1167 if ((sbbcregs.len != 4) || 1168 !sbbc_offset_valid(sbbcregs.offset)) { 1169 return (EINVAL); 1170 } 1171 1172 offset = (uint64_t)sbbcsoftp->pci_sbbc_map; 1173 offset += sbbcregs.offset; 1174 ddi_put32(sbbcsoftp->pci_sbbc_map_handle, (uint32_t *)offset, 1175 sbbcregs.value); 1176 } 1177 break; 1178 case SBBC_SBBCREG_RD: 1179 { 1180 struct ssc_sbbc_regio sbbcregs; 1181 uint64_t offset; 1182 1183 if (sbbc_scmode == FALSE) { 1184 /* then we're executing on Domain; Reads not allowed */ 1185 return (EINVAL); 1186 } 1187 1188 if (arg == (intptr_t)NULL) { 1189 return (ENXIO); 1190 } 1191 1192 if (ddi_copyin((caddr_t)arg, (caddr_t)&sbbcregs, 1193 sizeof (struct ssc_sbbc_regio), mode)) { 1194 cmn_err(CE_WARN, "sbbc_ioctl: copyin failed arg %p", 1195 (void *)arg); 1196 return (EFAULT); 1197 } 1198 1199 /* 1200 * Bug #4287186: SBBC driver on cp1500 doesn't check length for 1201 * reads or writes 1202 * Note that I've also added a check to make sure the offset is 1203 * valid, since misaligned (i.e. not on 16-byte boundary) 1204 * accesses or accesses to "Reserved" register offsets are 1205 * treated as unmapped by the SBBC. 1206 */ 1207 if ((sbbcregs.len != 4) || 1208 !sbbc_offset_valid(sbbcregs.offset)) { 1209 return (EINVAL); 1210 } 1211 1212 offset = (uint64_t)sbbcsoftp->pci_sbbc_map; 1213 offset += sbbcregs.offset; 1214 1215 sbbcregs.value = ddi_get32(sbbcsoftp->pci_sbbc_map_handle, 1216 (uint32_t *)offset); 1217 1218 if (ddi_copyout((caddr_t)&sbbcregs.value, 1219 &((struct ssc_sbbc_regio *)arg)->value, 1220 sbbcregs.len, mode)) { 1221 cmn_err(CE_WARN, "sbbc_ioctl:copyout failed arg %p", 1222 (void *)arg); 1223 return (EFAULT); 1224 } 1225 } 1226 break; 1227 default: 1228 cmn_err(CE_WARN, "sbbc_ioctl:Illegal command 0x%08x", cmd); 1229 return (ENOTTY); 1230 } 1231 1232 return (DDI_SUCCESS); 1233 } 1234 1235 static void 1236 sbbc_remove_reg_maps(struct sbbcsoft *sbbcsoftp) 1237 { 1238 SBBCTRACE(sbbc_remove_reg_maps, 'RMAP', sbbcsoftp); 1239 if (sbbcsoftp->pci_sbbc_map_handle) 1240 ddi_regs_map_free(&sbbcsoftp->pci_sbbc_map_handle); 1241 } 1242 1243 1244 static int 1245 sbbc_init(struct sbbcsoft *sbbcsoftp) 1246 { 1247 /* Mask all the interrupts until we are ready. */ 1248 ddi_put32(sbbcsoftp->pci_sbbc_map_handle, 1249 &sbbcsoftp->pci_sbbc_map->sys_intr_enable, 1250 0x00000000); 1251 1252 return (1); 1253 } 1254 1255 /* 1256 * The following routine is a generic routine to initialize any child of 1257 * sbbc nexus driver information into parent private data structure. 1258 */ 1259 /* ARGSUSED0 */ 1260 static int 1261 sbbc_initchild(dev_info_t *dip, dev_info_t *rdip, dev_info_t *child) 1262 { 1263 sbbc_child_regspec_t *child_rp; 1264 int reglen, slot; 1265 char name[10]; 1266 1267 SBBC_DBG1(SBBC_DBG_INITCHILD, dip, "Initializing %s\n", 1268 ddi_driver_name(rdip)); 1269 1270 /* 1271 * Initialize a child 1272 * Set the address portion of the node name based on the 1273 * address/offset. 1274 */ 1275 if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 1276 "reg", (caddr_t)&child_rp, ®len) != DDI_SUCCESS) { 1277 if (strcmp(ddi_node_name(child), "hotplug-controller") == 0) { 1278 slot = 1; 1279 (void) sprintf(name, "%x", slot); 1280 ddi_set_name_addr(child, name); 1281 return (DDI_SUCCESS); 1282 } 1283 return (DDI_FAILURE); 1284 } 1285 1286 SBBC_DBG3(SBBC_DBG_INITCHILD, dip, "hi 0x%x, low 0x%x, size 0x%x\n", 1287 child_rp->addr_hi, child_rp->addr_low, child_rp->size); 1288 1289 (void) sprintf(name, "%x,%x", child_rp->addr_hi, child_rp->addr_low); 1290 1291 /* 1292 * set child's addresses from the reg property into parent private 1293 * data structure. 1294 */ 1295 ddi_set_name_addr(child, name); 1296 kmem_free(child_rp, reglen); 1297 1298 ddi_set_parent_data(child, NULL); 1299 1300 return (DDI_SUCCESS); 1301 } 1302 1303 1304 /* ARGSUSED0 */ 1305 static int 1306 sbbc_uninitchild(dev_info_t *rdip, dev_info_t *child) 1307 { 1308 1309 SBBC_DBG1(SBBC_DBG_UNINITCHILD, rdip, "Uninitializing %s\n", 1310 ddi_driver_name(rdip)); 1311 1312 ddi_set_name_addr(child, NULL); 1313 ddi_remove_minor_node(child, NULL); 1314 impl_rem_dev_props(child); 1315 1316 return (DDI_SUCCESS); 1317 1318 } 1319 1320 1321 /* 1322 * The following routine is an interrupt service routine that is used 1323 * as a wrapper to all the children requiring interrupt services. 1324 */ 1325 static uint_t 1326 sbbc_intr_wrapper(caddr_t arg) 1327 { 1328 1329 struct sbbcsoft *sbbcsoftp = (struct sbbcsoft *)arg; 1330 int i, rval; 1331 1332 SBBC_DBG1(SBBC_DBG_INTR, sbbcsoftp->dip, "Isr arg 0x%llx\n", arg); 1333 1334 mutex_enter(&sbbcsoftp->sbbc_intr_mutex); 1335 1336 for (i = 0; i < MAX_SBBC_DEVICES; i++) { 1337 /* 1338 * Check the interrupt status reg. to determine the cause. 1339 */ 1340 /* 1341 * Check the error status reg. to determine the cause. 1342 */ 1343 if (sbbcsoftp->child_intr[i] && 1344 sbbcsoftp->child_intr[i]->status == 1345 SBBC_INTR_STATE_ENABLE) { 1346 /* 1347 * Dispatch the children interrupt service routines and 1348 * look for someone to claim. 1349 */ 1350 rval = sbbcsoftp->child_intr[i]->intr_handler( 1351 sbbcsoftp->child_intr[i]->arg1, 1352 sbbcsoftp->child_intr[i]->arg2); 1353 1354 if (rval == DDI_INTR_CLAIMED) { 1355 mutex_exit(&sbbcsoftp->sbbc_intr_mutex); 1356 return (rval); 1357 } 1358 } 1359 } 1360 1361 mutex_exit(&sbbcsoftp->sbbc_intr_mutex); 1362 1363 /* for now do not claim since we know its not enabled */ 1364 return (DDI_INTR_UNCLAIMED); 1365 } 1366 1367 1368 /* 1369 * This function checks an SBBC register offset to make sure that it is properly 1370 * aligned (i.e. on a 16-byte boundary) and that it corresponds to an accessible 1371 * register. Since the SBBC treates accesses to unaligned or reserved addresses 1372 * as unmapped, failing to check for these would leave a loophole that could be 1373 * used to crash the system. 1374 */ 1375 static int 1376 sbbc_offset_valid(uint32_t offset) 1377 { 1378 /* 1379 * Check for proper alignment first. 1380 */ 1381 if ((offset % 16) != 0) { 1382 return (0); 1383 } 1384 1385 /* 1386 * Now start checking for the various reserved ranges. 1387 * While sticking a bunch of constants in the code (rather than 1388 * #define'd values) is usually best avoided, it would probably 1389 * do more harm than good here. These values were taken from the 1390 * Serengeti Architecture Programmer's Reference Manual dated 1391 * August 10, 1999, pages 2-99 through 2-103. While there are 1392 * various "clever" ways this check could be performed that would 1393 * be slightly more efficient, arranging the code in this fashion 1394 * should maximize maintainability. 1395 */ 1396 if (((offset >= 0x001a0) && (offset <= 0x001ff)) || 1397 ((offset >= 0x002a0) && (offset <= 0x002ff)) || 1398 ((offset >= 0x00350) && (offset <= 0x003ff)) || 1399 ((offset >= 0x00500) && (offset <= 0x00fff)) || 1400 ((offset >= 0x01160) && (offset <= 0x011ff)) || 1401 ((offset >= 0x01210) && (offset <= 0x017ff)) || 1402 ((offset >= 0x01810) && (offset <= 0x01fff)) || 1403 ((offset >= 0x02030) && (offset <= 0x022ff)) || 1404 ((offset >= 0x02340) && (offset <= 0x03fff)) || 1405 ((offset >= 0x04030) && (offset <= 0x05fff)) || 1406 ((offset >= 0x060a0) && (offset <= 0x060ff)) || 1407 (offset == 0x06120) || 1408 ((offset >= 0x06190) && (offset <= 0x061ff)) || 1409 ((offset >= 0x06230) && (offset <= 0x062f0)) || 1410 (offset > 0x06320)) { 1411 return (0); 1412 } 1413 1414 return (1); 1415 } 1416 1417 #ifdef DEBUG 1418 void 1419 sbbc_dbg(uint32_t flag, dev_info_t *dip, char *fmt, 1420 uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5) 1421 { 1422 char *s = NULL; 1423 1424 if (sbbc_dbg_flags && ((sbbc_dbg_flags & flag) == flag)) { 1425 switch (flag) { 1426 case SBBC_DBG_ATTACH: 1427 s = "attach"; 1428 break; 1429 case SBBC_DBG_DETACH: 1430 s = "detach"; 1431 break; 1432 case SBBC_DBG_CTLOPS: 1433 s = "ctlops"; 1434 break; 1435 case SBBC_DBG_INITCHILD: 1436 s = "initchild"; 1437 break; 1438 case SBBC_DBG_UNINITCHILD: 1439 s = "uninitchild"; 1440 break; 1441 case SBBC_DBG_BUSMAP: 1442 s = "busmap"; 1443 break; 1444 case SBBC_DBG_INTR: 1445 s = "intr"; 1446 break; 1447 case SBBC_DBG_INTROPS: 1448 s = "intr_ops"; 1449 break; 1450 case SBBC_DBG_PCICONF: 1451 s = "pciconfig"; 1452 break; 1453 case SBBC_DBG_MAPRANGES: 1454 s = "mapranges"; 1455 break; 1456 case SBBC_DBG_PROPERTIES: 1457 s = "properties"; 1458 break; 1459 case SBBC_DBG_OPEN: 1460 s = "open"; 1461 break; 1462 case SBBC_DBG_CLOSE: 1463 s = "close"; 1464 break; 1465 case SBBC_DBG_IOCTL: 1466 s = "ioctl"; 1467 break; 1468 default: 1469 s = "Unknown debug flag"; 1470 break; 1471 } 1472 1473 cmn_err(CE_CONT, "%s_%s(%d): ", ddi_driver_name(dip), s, 1474 ddi_get_instance(dip)); 1475 cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5); 1476 } 1477 } 1478 1479 /* 1480 * Dump the SBBC chip's Device ID Register 1481 */ 1482 static void sbbc_dump_devid(dev_info_t *dip, struct sbbcsoft *sbbcsoftp, 1483 int instance) 1484 { 1485 uint32_t sbbc_id_reg; 1486 uint16_t sbbc_id_reg_partid; 1487 uint16_t sbbc_id_reg_manfid; 1488 1489 sbbc_id_reg = ddi_get32(sbbcsoftp->pci_sbbc_map_handle, 1490 (uint32_t *)&sbbcsoftp->pci_sbbc_map->devid); 1491 1492 sbbc_id_reg_partid = ((sbbc_id_reg << 4) >> 16); 1493 sbbc_id_reg_manfid = ((sbbc_id_reg << 20) >> 21); 1494 1495 SBBC_DBG4(SBBC_DBG_ATTACH, dip, 1496 "FOUND SBBC(%d) Version %x, Partid %x, Manfid %x\n", 1497 instance, (sbbc_id_reg >> 28), sbbc_id_reg_partid, 1498 sbbc_id_reg_manfid); 1499 } 1500 1501 #endif /* DEBUG */ 1502