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