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