1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/conf.h> 31 #include <sys/ddi.h> 32 #include <sys/sunddi.h> 33 #include <sys/modctl.h> 34 #include <sys/sunndi.h> 35 #include <sys/ddi_impldefs.h> 36 #include <sys/obpdefs.h> 37 #include <sys/cmn_err.h> 38 #include <sys/errno.h> 39 #include <sys/kmem.h> 40 #include <sys/debug.h> 41 #include <sys/sysmacros.h> 42 #include <sys/autoconf.h> 43 #include <sys/stat.h> 44 #include <sys/serengeti.h> 45 #include <sys/ssm.h> 46 #include <sys/sgsbbc_mailbox.h> 47 #include <sys/sgevents.h> 48 #include <sys/sysevent.h> 49 #include <sys/sysevent/dr.h> 50 #include <sys/sysevent/eventdefs.h> 51 #include <sys/ndi_impldefs.h> 52 #include <sys/ddifm.h> 53 #include <sys/ndifm.h> 54 #include <sys/sbd_ioctl.h> 55 56 /* Useful debugging Stuff */ 57 #include <sys/nexusdebug.h> 58 59 /* 60 * module ssm.c 61 * 62 * This module is a nexus driver designed to support the ssm nexus driver 63 * and all children below it. This driver does not handle any of the 64 * DDI functions passed up to it by the ssm driver, but instead allows 65 * them to bubble up to the root node. 66 */ 67 68 69 /* 70 * Function prototypes 71 */ 72 extern int plat_max_boards(); 73 74 static int 75 ssm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result); 76 77 static int 78 ssm_attach(dev_info_t *, ddi_attach_cmd_t); 79 80 static int 81 ssm_detach(dev_info_t *, ddi_detach_cmd_t); 82 83 static int 84 ssm_open(dev_t *, int, int, cred_t *); 85 86 static int 87 ssm_close(dev_t, int, int, cred_t *); 88 89 static int 90 ssm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 91 92 static int 93 ssm_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *); 94 95 static int 96 ssm_make_nodes(dev_info_t *dip, int instance, int ssm_nodeid); 97 98 static int 99 ssm_generate_event(int node, int board, int hint); 100 101 /* 102 * FMA error callback 103 * Register error handling callback with our parent. We will just call 104 * our children's error callbacks and return their status. 105 */ 106 static int 107 ssm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data); 108 109 /* 110 * fm_init busop to initialize our children 111 */ 112 static int 113 ssm_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap, 114 ddi_iblock_cookie_t *ibc); 115 116 /* 117 * init/fini routines to alloc/dealloc fm structures and 118 * register/unregister our callback. 119 */ 120 static void 121 ssm_fm_init(struct ssm_soft_state *softsp); 122 123 static void 124 ssm_fm_fini(struct ssm_soft_state *softsp); 125 126 /* 127 * DR event handlers 128 * We want to register the event handlers once for all instances. In the 129 * other hand we have register them after the sbbc has been attached. 130 * event_initialize gives us the logic of only registering the events only 131 * once 132 */ 133 int event_initialized = 0; 134 uint_t ssm_dr_event_handler(char *); 135 136 /* 137 * Event lock and state 138 */ 139 static kmutex_t ssm_event_lock; 140 int ssm_event_state; 141 142 /* 143 * DR event msg and payload 144 */ 145 static sbbc_msg_t event_msg; 146 static sg_system_fru_descriptor_t payload; 147 148 struct ssm_node2inst { 149 int nodeid; /* serengeti node #, NOT prom nodeid */ 150 int inst; 151 struct ssm_node2inst *next; 152 }; 153 static kmutex_t ssm_node2inst_lock; 154 static struct ssm_node2inst ssm_node2inst_map = {-1, -1, NULL}; 155 156 157 /* 158 * Configuration data structures 159 */ 160 static struct bus_ops ssm_bus_ops = { 161 BUSO_REV, 162 ddi_bus_map, /* map */ 163 0, /* get_intrspec */ 164 0, /* add_intrspec */ 165 0, /* remove_intrspec */ 166 i_ddi_map_fault, /* map_fault */ 167 ddi_dma_map, /* dma_map */ 168 ddi_dma_allochdl, 169 ddi_dma_freehdl, 170 ddi_dma_bindhdl, 171 ddi_dma_unbindhdl, 172 ddi_dma_flush, 173 ddi_dma_win, 174 ddi_dma_mctl, /* dma_ctl */ 175 ssm_ctlops, /* ctl */ 176 ddi_bus_prop_op, /* prop_op */ 177 ndi_busop_get_eventcookie, 178 ndi_busop_add_eventcall, 179 ndi_busop_remove_eventcall, 180 ndi_post_event, 181 0, 182 0, 183 0, 184 ssm_fm_init_child, 185 NULL, 186 NULL, 187 NULL, 188 0, 189 i_ddi_intr_ops 190 }; 191 192 static struct cb_ops ssm_cb_ops = { 193 ssm_open, /* open */ 194 ssm_close, /* close */ 195 nodev, /* strategy */ 196 nodev, /* print */ 197 nodev, /* dump */ 198 nodev, /* read */ 199 nodev, /* write */ 200 ssm_ioctl, /* ioctl */ 201 nodev, /* devmap */ 202 nodev, /* mmap */ 203 nodev, /* segmap */ 204 nochpoll, /* poll */ 205 ddi_prop_op, /* cb_prop_op */ 206 NULL, /* streamtab */ 207 D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 208 CB_REV, /* rev */ 209 nodev, /* int (*cb_aread)() */ 210 nodev /* int (*cb_awrite)() */ 211 }; 212 213 static struct dev_ops ssm_ops = { 214 DEVO_REV, /* devo_rev, */ 215 0, /* refcnt */ 216 ssm_info, /* getinfo */ 217 nulldev, /* identify */ 218 nulldev, /* probe */ 219 ssm_attach, /* attach */ 220 ssm_detach, /* detach */ 221 nulldev, /* reset */ 222 &ssm_cb_ops, /* driver operations */ 223 &ssm_bus_ops, /* bus_ops */ 224 nulldev /* power */ 225 }; 226 227 /* 228 * Driver globals 229 */ 230 static void *ssm_softstates; /* ssm soft state hook */ 231 232 extern struct mod_ops mod_driverops; 233 234 static struct modldrv modldrv = { 235 &mod_driverops, /* Type of module. This one is a driver */ 236 "SSM Nexus v1.8", /* name of module */ 237 &ssm_ops, /* driver ops */ 238 }; 239 240 static struct modlinkage modlinkage = { 241 MODREV_1, /* rev */ 242 (void *)&modldrv, 243 NULL 244 }; 245 246 static int ssm_loaded_sbd = FALSE; 247 kmutex_t ssm_lock; 248 static int init_child(dev_info_t *child); 249 250 /* 251 * These are the module initialization routines. 252 */ 253 254 int 255 _init(void) 256 { 257 int error; 258 259 #if defined(DEBUG) 260 debug_print_level = 0x0; 261 #endif 262 263 /* Initialize soft state pointer. */ 264 if ((error = ddi_soft_state_init(&ssm_softstates, 265 sizeof (struct ssm_soft_state), 266 SSM_MAX_INSTANCES)) != 0) 267 return (error); 268 269 /* Install the module. */ 270 error = mod_install(&modlinkage); 271 if (error != 0) 272 ddi_soft_state_fini(&ssm_softstates); 273 274 mutex_init(&ssm_lock, NULL, MUTEX_DRIVER, NULL); 275 276 return (error); 277 } 278 279 int 280 _fini(void) 281 { 282 int error; 283 284 /* Remove the module. */ 285 if ((error = mod_remove(&modlinkage)) != 0) 286 return (error); 287 288 /* 289 * Unregister the event handler 290 */ 291 sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC, ssm_dr_event_handler); 292 mutex_destroy(&ssm_event_lock); 293 294 /* Free the soft state info. */ 295 ddi_soft_state_fini(&ssm_softstates); 296 mutex_destroy(&ssm_lock); 297 298 return (0); 299 } 300 301 int 302 _info(struct modinfo *modinfop) 303 { 304 return (mod_info(&modlinkage, modinfop)); 305 } 306 307 /* device driver entry points */ 308 309 /* 310 * info entry point: 311 */ 312 313 /* ARGSUSED */ 314 static int 315 ssm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 316 { 317 dev_t dev; 318 int instance; 319 320 if (infocmd == DDI_INFO_DEVT2INSTANCE) { 321 dev = (dev_t)arg; 322 instance = (getminor(dev) >> SSM_INSTANCE_SHIFT); 323 *result = (void *)(uintptr_t)instance; 324 return (DDI_SUCCESS); 325 } 326 return (DDI_FAILURE); 327 } 328 329 /* 330 * attach entry point: 331 */ 332 333 static int 334 ssm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 335 { 336 int instance; 337 struct ssm_soft_state *softsp; 338 struct ssm_node2inst *prev, *sp, *tsp; 339 340 DPRINTF(SSM_ATTACH_DEBUG, ("ssm_attach\n")); 341 342 switch (cmd) { 343 case DDI_ATTACH: 344 break; 345 346 case DDI_RESUME: 347 return (DDI_SUCCESS); 348 349 default: 350 return (DDI_FAILURE); 351 } 352 353 instance = ddi_get_instance(devi); 354 355 if (ddi_soft_state_zalloc(ssm_softstates, instance) != DDI_SUCCESS) 356 return (DDI_FAILURE); 357 358 softsp = ddi_get_soft_state(ssm_softstates, instance); 359 360 /* Set the dip in the soft state */ 361 softsp->dip = devi; 362 softsp->top_node = devi; 363 mutex_init(&softsp->ssm_sft_lock, NULL, MUTEX_DRIVER, NULL); 364 365 DPRINTF(SSM_ATTACH_DEBUG, ("ssm-%d: devi= 0x%p, softsp=0x%p\n", 366 instance, devi, softsp)); 367 368 if ((softsp->ssm_nodeid = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->dip, 369 DDI_PROP_DONTPASS, "nodeid", -1)) == -1) { 370 cmn_err(CE_WARN, "ssm%d: unable to retrieve %s property", 371 instance, "nodeid"); 372 ddi_soft_state_free(ssm_softstates, instance); 373 return (DDI_FAILURE); 374 } 375 376 /* nothing to suspend/resume here */ 377 (void) ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP, 378 "pm-hardware-state", (caddr_t)"no-suspend-resume", 379 strlen("no-suspend-resume") + 1); 380 381 #if DEBUG 382 if (ddi_create_minor_node(devi, "debug", S_IFCHR, instance, 383 DDI_NT_NEXUS, 0) != DDI_SUCCESS) { 384 ddi_soft_state_free(ssm_softstates, instance); 385 return (DDI_FAILURE); 386 } 387 #endif 388 389 if (ssm_make_nodes(devi, instance, softsp->ssm_nodeid)) { 390 cmn_err(CE_WARN, 391 "ssm:%s:%d: failed to make nodes", 392 ddi_driver_name(devi), instance); 393 ddi_remove_minor_node(devi, NULL); 394 ddi_soft_state_free(ssm_softstates, instance); 395 return (DDI_FAILURE); 396 } 397 ssm_fm_init(softsp); 398 ddi_report_dev(devi); 399 400 if (event_initialized == 0) { 401 int rv; 402 /* 403 * Register DR event handler 404 */ 405 mutex_init(&ssm_event_lock, NULL, MUTEX_DRIVER, NULL); 406 event_msg.msg_buf = (caddr_t)&payload; 407 event_msg.msg_len = sizeof (payload); 408 409 rv = sbbc_mbox_reg_intr(MBOX_EVENT_GENERIC, 410 ssm_dr_event_handler, &event_msg, 411 (uint_t *)&ssm_event_state, &ssm_event_lock); 412 413 if (rv == EINVAL) 414 event_initialized = 1; 415 } 416 417 /* 418 * Preallocate to avoid sleeping with ssm_node2inst_lock held - 419 * low level interrupts use this mutex. 420 */ 421 tsp = kmem_zalloc(sizeof (struct ssm_node2inst), KM_SLEEP); 422 423 mutex_enter(&ssm_node2inst_lock); 424 425 for (prev = NULL, sp = &ssm_node2inst_map; sp != NULL; 426 prev = sp, sp = sp->next) { 427 ASSERT(sp->inst != instance); 428 ASSERT(sp->nodeid != softsp->ssm_nodeid); 429 if (sp->inst == -1) 430 break; 431 } 432 433 if (sp == NULL) { 434 ASSERT(prev->next == NULL); 435 sp = prev->next = tsp; 436 tsp = NULL; 437 sp->next = NULL; 438 } 439 440 sp->inst = instance; 441 sp->nodeid = softsp->ssm_nodeid; 442 443 mutex_exit(&ssm_node2inst_lock); 444 445 if (tsp != NULL) 446 kmem_free(tsp, sizeof (struct ssm_node2inst)); 447 448 return (DDI_SUCCESS); 449 } 450 451 /* 452 * detach entry point: 453 */ 454 /*ARGSUSED*/ 455 static int 456 ssm_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 457 { 458 int instance, rv; 459 int (*sbd_teardown_instance) (int, caddr_t); 460 ssm_sbdp_info_t sbdp_info; 461 struct ssm_soft_state *softsp; 462 struct ssm_node2inst *prev, *sp; 463 464 instance = ddi_get_instance(devi); 465 softsp = ddi_get_soft_state(ssm_softstates, instance); 466 467 if (softsp == NULL) { 468 cmn_err(CE_WARN, 469 "ssm_open bad instance number %d", instance); 470 return (ENXIO); 471 } 472 473 instance = ddi_get_instance(devi); 474 475 switch (cmd) { 476 case DDI_DETACH: 477 ddi_remove_minor_node(devi, NULL); 478 479 sbd_teardown_instance = (int (*) (int, caddr_t)) 480 modgetsymvalue("sbd_teardown_instance", 0); 481 482 if (!sbd_teardown_instance) { 483 cmn_err(CE_WARN, "cannot find sbd_teardown_instance"); 484 return (DDI_FAILURE); 485 } 486 487 sbdp_info.instance = instance; 488 sbdp_info.wnode = softsp->ssm_nodeid; 489 rv = (*sbd_teardown_instance)(instance, (caddr_t)&sbdp_info); 490 491 if (rv != DDI_SUCCESS) { 492 cmn_err(CE_WARN, "cannot run sbd_teardown_instance"); 493 return (DDI_FAILURE); 494 } 495 ssm_fm_fini(softsp); 496 mutex_destroy(&softsp->ssm_sft_lock); 497 ddi_soft_state_free(ssm_softstates, instance); 498 499 mutex_enter(&ssm_node2inst_lock); 500 for (prev = NULL, sp = &ssm_node2inst_map; sp != NULL; 501 prev = sp, sp = sp->next) { 502 /* Only the head of the list can persist if unused */ 503 ASSERT(prev == NULL || sp->inst != -1); 504 if (sp->inst == instance) 505 break; 506 } 507 ASSERT(sp != NULL); 508 509 if (sp != &ssm_node2inst_map) { 510 prev->next = sp->next; 511 kmem_free(sp, sizeof (struct ssm_node2inst)); 512 } else { 513 /* 514 * Invalidate the head element, but retain the rest 515 * of the list - "next" is still valid. 516 */ 517 518 sp->nodeid = -1; 519 sp->inst = -1; 520 } 521 mutex_exit(&ssm_node2inst_lock); 522 523 return (DDI_SUCCESS); 524 525 case DDI_SUSPEND: 526 return (DDI_SUCCESS); 527 528 default: 529 return (DDI_FAILURE); 530 } 531 } 532 533 extern void make_ddi_ppd(dev_info_t *, struct ddi_parent_private_data **); 534 extern struct ddi_parent_private_data *init_regspec_64(dev_info_t *); 535 536 static int 537 name_child(dev_info_t *child, char *name, int namelen) 538 { 539 struct regspec *rp; 540 struct ddi_parent_private_data *pdptr; 541 int portid = 0; 542 int regbase = -1; 543 extern uint_t root_phys_addr_lo_mask; 544 545 make_ddi_ppd(child, &pdptr); 546 ddi_set_parent_data(child, pdptr); 547 548 name[0] = '\0'; 549 if (sparc_pd_getnreg(child) == 0) 550 return (DDI_SUCCESS); 551 552 rp = sparc_pd_getreg(child, 0); 553 554 portid = ddi_prop_get_int(DDI_DEV_T_ANY, child, 555 DDI_PROP_DONTPASS, "portid", -1); 556 if (portid == -1) { 557 cmn_err(CE_WARN, "could not find portid property in %s", 558 DEVI(child)->devi_node_name); 559 } else { 560 regbase = rp->regspec_addr & root_phys_addr_lo_mask; 561 } 562 (void) snprintf(name, namelen, "%x,%x", portid, regbase); 563 return (DDI_SUCCESS); 564 } 565 566 static int 567 init_child(dev_info_t *child) 568 { 569 char name[MAXNAMELEN]; 570 571 (void) name_child(child, name, MAXNAMELEN); 572 ddi_set_name_addr(child, name); 573 if ((ndi_dev_is_persistent_node(child) == 0) && 574 (ndi_merge_node(child, name_child) == DDI_SUCCESS)) { 575 impl_ddi_sunbus_removechild(child); 576 return (DDI_FAILURE); 577 } 578 579 (void) init_regspec_64(child); 580 return (DDI_SUCCESS); 581 } 582 583 /* 584 * Control ops entry point: 585 * 586 * Requests handled completely: 587 * DDI_CTLOPS_INITCHILD 588 * DDI_CTLOPS_UNINITCHILD 589 * DDI_CTLOPS_REPORTDEV 590 * All others are passed to the parent. 591 * The name of the ssm node is ssm@nodeid,0. 592 * ssm is the equivalent of rootnex. 593 */ 594 static int 595 ssm_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, void *arg, 596 void *result) 597 { 598 int rval; 599 600 switch (op) { 601 case DDI_CTLOPS_INITCHILD: { 602 DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_INITCHILD\n")); 603 return (init_child((dev_info_t *)arg)); 604 } 605 606 case DDI_CTLOPS_UNINITCHILD: { 607 DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_UNINITCHILD\n")); 608 impl_ddi_sunbus_removechild((dev_info_t *)arg); 609 return (DDI_SUCCESS); 610 } 611 612 case DDI_CTLOPS_REPORTDEV: { 613 char buf[80]; 614 char *p = buf; 615 dev_info_t *parent; 616 int portid; 617 618 DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_REPORTDEV\n")); 619 parent = ddi_get_parent(rdip); 620 621 (void) sprintf(p, "%s%d at %s%d", DEVI(rdip)->devi_name, 622 DEVI(rdip)->devi_instance, ddi_get_name(parent), 623 ddi_get_instance(parent)); 624 p += strlen(p); 625 626 /* Fetch Safari Extended Agent ID of this device. */ 627 portid = (int)ddi_getprop(DDI_DEV_T_ANY, rdip, 628 DDI_PROP_DONTPASS, "portid", -1); 629 630 /* 631 * If this is one of the ssm children it will have 632 * portid property and its parent will be ssm. 633 * In this case report Node number and Safari id. 634 */ 635 if (portid != -1 && 636 strcmp("ssm", ddi_get_name(parent)) == 0) { 637 struct regspec *rp; 638 int node; 639 int safid; 640 int n; 641 642 rp = sparc_pd_getreg(rdip, 0); 643 n = sparc_pd_getnreg(rdip); 644 ASSERT(n > 0); 645 646 node = SG_PORTID_TO_NODEID(portid); 647 safid = SG_PORTID_TO_SAFARI_ID(portid); 648 649 (void) strcpy(p, ": "); 650 p += strlen(p); 651 652 (void) sprintf(p, "Node %d Safari id %d 0x%x%s", 653 node, safid, 654 rp->regspec_addr, 655 (n > 1 ? "" : " ...")); 656 p += strlen(p); 657 } 658 659 cmn_err(CE_CONT, "?%s\n", buf); 660 rval = DDI_SUCCESS; 661 662 break; 663 } 664 665 default: 666 rval = ddi_ctlops(dip, rdip, op, arg, result); 667 668 break; 669 } 670 671 return (rval); 672 } 673 674 /*ARGSUSED*/ 675 static int 676 ssm_make_nodes(dev_info_t *dip, int instance, int ssm_nodeid) 677 { 678 int rv; 679 minor_t minor_num, bd; 680 auto char filename[20]; 681 682 for (bd = 0; bd < plat_max_boards(); bd++) { 683 if (SG_BOARD_IS_CPU_TYPE(bd)) 684 (void) sprintf(filename, "N%d.SB%d", ssm_nodeid, bd); 685 else 686 (void) sprintf(filename, "N%d.IB%d", ssm_nodeid, bd); 687 688 minor_num = (instance << SSM_INSTANCE_SHIFT) | bd; 689 690 rv = ddi_create_minor_node(dip, filename, S_IFCHR, 691 minor_num, DDI_NT_SBD_ATTACHMENT_POINT, 692 NULL); 693 if (rv == DDI_FAILURE) { 694 cmn_err(CE_WARN, 695 "ssm_make_nodes:%d: failed to create " 696 "minor node (%s, 0x%x)", 697 instance, filename, minor_num); 698 return (-1); 699 } 700 } 701 702 return (0); 703 } 704 705 706 /* ARGSUSED */ 707 static int 708 ssm_open(dev_t *devi, int flags, int otyp, cred_t *credp) 709 { 710 struct ssm_soft_state *softsp; 711 minor_t board, instance; 712 int modload(char *, char *); 713 int (*sbd_setup_instance)(int, dev_info_t *, int, int, caddr_t); 714 ssm_sbdp_info_t sbdp_info; 715 int rv; 716 717 instance = (getminor(*devi) >> SSM_INSTANCE_SHIFT); 718 719 softsp = ddi_get_soft_state(ssm_softstates, instance); 720 if (softsp == NULL) { 721 cmn_err(CE_WARN, "ssm_open bad instance number %d", instance); 722 return (ENXIO); 723 } 724 725 board = (getminor(*devi) & SSM_BOARD_MASK); 726 727 if (board < 0 || board > plat_max_boards()) { 728 return (ENXIO); 729 } 730 731 mutex_enter(&ssm_lock); 732 if (instance == 0 && ssm_loaded_sbd == FALSE) { 733 734 if (modload("misc", "sbd") == -1) { 735 cmn_err(CE_WARN, "ssm_open: cannot load sbd"); 736 mutex_exit(&ssm_lock); 737 return (EIO); 738 } 739 ssm_loaded_sbd = TRUE; 740 } 741 mutex_exit(&ssm_lock); 742 743 mutex_enter(&softsp->ssm_sft_lock); 744 if (softsp->initialized == FALSE) { 745 746 if (softsp->top_node == NULL) { 747 cmn_err(CE_WARN, "cannot find ssm top dnode"); 748 mutex_exit(&softsp->ssm_sft_lock); 749 return (EIO); 750 } 751 752 sbd_setup_instance = (int (*)(int, dev_info_t *, int, int, 753 caddr_t))modgetsymvalue("sbd_setup_instance", 0); 754 755 if (!sbd_setup_instance) { 756 cmn_err(CE_WARN, "cannot find sbd_setup_instance"); 757 mutex_exit(&softsp->ssm_sft_lock); 758 return (EIO); 759 } 760 761 sbdp_info.instance = instance; 762 sbdp_info.wnode = softsp->ssm_nodeid; 763 764 rv = (*sbd_setup_instance)(instance, softsp->top_node, 765 plat_max_boards(), softsp->ssm_nodeid, 766 (caddr_t)&sbdp_info); 767 if (rv != DDI_SUCCESS) { 768 cmn_err(CE_WARN, "cannot run sbd_setup_instance"); 769 mutex_exit(&softsp->ssm_sft_lock); 770 return (EIO); 771 } 772 softsp->initialized = TRUE; 773 } 774 mutex_exit(&softsp->ssm_sft_lock); 775 776 return (DDI_SUCCESS); 777 } 778 779 780 /* ARGSUSED */ 781 static int 782 ssm_close(dev_t dev, int flags, int otyp, cred_t *credp) 783 { 784 struct ssm_soft_state *softsp; 785 minor_t board, instance; 786 787 instance = (getminor(dev) >> SSM_INSTANCE_SHIFT); 788 789 softsp = ddi_get_soft_state(ssm_softstates, instance); 790 if (softsp == NULL) 791 return (ENXIO); 792 793 board = (getminor(dev) & SSM_BOARD_MASK); 794 795 if (board < 0 || board > plat_max_boards()) 796 return (ENXIO); 797 798 return (DDI_SUCCESS); 799 } 800 801 /* ARGSUSED */ 802 static int 803 ssm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 804 int *rvalp) 805 { 806 struct ssm_soft_state *softsp; 807 char *addr; 808 struct devctl_iocdata *dcp; 809 int instance, rv = 0; 810 int (*sbd_ioctl) (dev_t, int, intptr_t, int, char *); 811 812 instance = (getminor(dev) >> SSM_INSTANCE_SHIFT); 813 softsp = ddi_get_soft_state(ssm_softstates, instance); 814 if (softsp == NULL) 815 return (ENXIO); 816 817 switch (cmd) { 818 819 case DEVCTL_BUS_CONFIGURE: 820 /* 821 * read devctl ioctl data 822 */ 823 if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) 824 return (EFAULT); 825 826 addr = ndi_dc_getaddr(dcp); 827 cmn_err(CE_NOTE, 828 "DEVCTL_BUS_CONFIGURE: device id is %s\n", addr); 829 ndi_dc_freehdl(dcp); 830 break; 831 832 case DEVCTL_BUS_UNCONFIGURE: 833 if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) 834 return (EFAULT); 835 836 addr = ndi_dc_getaddr(dcp); 837 cmn_err(CE_NOTE, 838 "DEVCTL_BUS_UNCONFIGURE: device id is %s\n", addr); 839 ndi_dc_freehdl(dcp); 840 break; 841 842 #ifdef DEBUG 843 case SSM_TEARDOWN_SBD: { 844 ssm_sbdp_info_t sbdp_info; 845 int (*sbd_teardown_instance) (int, caddr_t); 846 sbd_teardown_instance = (int (*) (int, caddr_t)) 847 modgetsymvalue("sbd_teardown_instance", 0); 848 849 if (!sbd_teardown_instance) { 850 cmn_err(CE_WARN, "cannot find sbd_teardown_instance"); 851 return (EFAULT); 852 } 853 854 sbdp_info.instance = instance; 855 sbdp_info.wnode = softsp->ssm_nodeid; 856 rv = (*sbd_teardown_instance)(instance, (caddr_t)&sbdp_info); 857 if (rv != DDI_SUCCESS) { 858 cmn_err(CE_WARN, "cannot run sbd_teardown_instance"); 859 return (EFAULT); 860 } 861 862 ssm_loaded_sbd = FALSE; 863 softsp->initialized = FALSE; 864 } 865 #endif 866 867 868 default: { 869 char event = 0; 870 871 sbd_ioctl = (int (*) 872 (dev_t, int, intptr_t, int, char *)) 873 modgetsymvalue("sbd_ioctl", 0); 874 875 if (sbd_ioctl) 876 rv = (*sbd_ioctl) (dev, cmd, arg, mode, &event); 877 else { 878 cmn_err(CE_WARN, "cannot find sbd_ioctl"); 879 return (ENXIO); 880 } 881 /* 882 * Check to see if we need to send an event 883 */ 884 if (event == 1) { 885 int slot; 886 int hint = SE_NO_HINT; 887 888 if (rv == 0) { 889 if (cmd == SBD_CMD_CONNECT || 890 cmd == SBD_CMD_CONFIGURE) 891 hint = SE_HINT_INSERT; 892 else if (cmd == SBD_CMD_UNCONFIGURE || 893 cmd == SBD_CMD_DISCONNECT) 894 hint = SE_HINT_REMOVE; 895 } 896 897 slot = (getminor(dev) & SSM_BOARD_MASK); 898 ssm_generate_event(softsp->ssm_nodeid, slot, hint); 899 } 900 break; 901 } 902 } 903 904 return (rv); 905 } 906 907 void 908 ssm_get_attch_pnt(int node, int board, char *attach_pnt) 909 { 910 struct ssm_node2inst *sp; 911 912 /* 913 * Hold this mutex, until we are done so that ssm dip 914 * doesn't detach. 915 */ 916 mutex_enter(&ssm_node2inst_lock); 917 918 for (sp = &ssm_node2inst_map; sp != NULL; sp = sp->next) { 919 if (sp->inst == -1) 920 continue; 921 if (sp->nodeid == node) 922 break; 923 } 924 925 if (sp == NULL) { 926 /* We didn't find the ssm dip, return failure */ 927 attach_pnt[0] = '\0'; 928 mutex_exit(&ssm_node2inst_lock); 929 return; 930 } 931 932 /* 933 * we have the instance, and the board, construct the attch pnt 934 */ 935 if (SG_BOARD_IS_CPU_TYPE(board)) 936 (void) sprintf(attach_pnt, "ssm%d:N%d.SB%d", 937 sp->inst, node, board); 938 else 939 (void) sprintf(attach_pnt, "ssm%d:N%d.IB%d", 940 sp->inst, node, board); 941 942 mutex_exit(&ssm_node2inst_lock); 943 } 944 945 /* 946 * Generate an event to sysevent 947 */ 948 static int 949 ssm_generate_event(int node, int board, int hint) 950 { 951 sysevent_t *ev; 952 sysevent_id_t eid; 953 int rv = 0; 954 sysevent_value_t evnt_val; 955 sysevent_attr_list_t *evnt_attr_list = NULL; 956 char attach_pnt[MAXPATHLEN]; 957 958 959 attach_pnt[0] = '\0'; 960 ssm_get_attch_pnt(node, board, attach_pnt); 961 962 if (attach_pnt[0] == '\0') 963 return (-1); 964 965 ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, EP_DDI, 966 KM_SLEEP); 967 evnt_val.value_type = SE_DATA_TYPE_STRING; 968 evnt_val.value.sv_string = attach_pnt; 969 970 rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val, KM_SLEEP); 971 if (rv != 0) { 972 cmn_err(CE_WARN, "Failed to add attr [%s] for %s event", 973 DR_AP_ID, EC_DR); 974 sysevent_free(ev); 975 return (rv); 976 } 977 978 /* 979 * Add the hint 980 */ 981 evnt_val.value_type = SE_DATA_TYPE_STRING; 982 evnt_val.value.sv_string = SE_HINT2STR(hint); 983 984 rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val, KM_SLEEP); 985 if (rv != 0) { 986 cmn_err(CE_WARN, "Failed to add attr [%s] for %s event", 987 DR_HINT, EC_DR); 988 sysevent_free_attr(evnt_attr_list); 989 sysevent_free(ev); 990 return (-1); 991 } 992 993 if (sysevent_attach_attributes(ev, evnt_attr_list) != 0) { 994 cmn_err(CE_WARN, "Failed to attach attr list for %s event", 995 EC_DR); 996 sysevent_free_attr(evnt_attr_list); 997 sysevent_free(ev); 998 return (-1); 999 } 1000 1001 rv = log_sysevent(ev, KM_NOSLEEP, &eid); 1002 if (rv != 0) { 1003 cmn_err(CE_WARN, "ssm_dr_event_handler: failed to log event"); 1004 } 1005 1006 sysevent_free(ev); 1007 1008 return (rv); 1009 } 1010 1011 /* 1012 * DR Event Handler 1013 */ 1014 uint_t 1015 ssm_dr_event_handler(char *arg) 1016 { 1017 sg_system_fru_descriptor_t *fdp; 1018 int hint; 1019 1020 1021 fdp = (sg_system_fru_descriptor_t *)(((sbbc_msg_t *)arg)->msg_buf); 1022 if (fdp == NULL) { 1023 DPRINTF(SSM_EVENT_DEBUG, 1024 ("ssm_dr_event_handler: ARG is null\n")); 1025 return (DDI_INTR_CLAIMED); 1026 } 1027 #ifdef DEBUG 1028 DPRINTF(SSM_EVENT_DEBUG, ("ssm_dr_event_handler called\n")); 1029 DPRINTF(SSM_EVENT_DEBUG, ("\tnode\t%d\n", fdp->node)); 1030 DPRINTF(SSM_EVENT_DEBUG, ("\tslot\t%d\n", fdp->slot)); 1031 DPRINTF(SSM_EVENT_DEBUG, ("\tparent_hdl\t0x%lx\n", fdp->parent_hdl)); 1032 DPRINTF(SSM_EVENT_DEBUG, ("\tchild_hdl\t0x%lx\n", fdp->child_hdl)); 1033 DPRINTF(SSM_EVENT_DEBUG, ("\tevent_details\t%s\n", 1034 EVNT2STR(fdp->event_details))); 1035 #endif 1036 1037 switch (fdp->event_details) { 1038 case SG_EVT_BOARD_ABSENT: 1039 hint = SE_HINT_REMOVE; 1040 break; 1041 case SG_EVT_BOARD_PRESENT: 1042 hint = SE_HINT_INSERT; 1043 break; 1044 default: 1045 hint = SE_NO_HINT; 1046 break; 1047 1048 } 1049 1050 ssm_generate_event(fdp->node, fdp->slot, hint); 1051 1052 return (DDI_INTR_CLAIMED); 1053 } 1054 1055 /* 1056 * Initialize our FMA resources 1057 */ 1058 static void 1059 ssm_fm_init(struct ssm_soft_state *softsp) 1060 { 1061 softsp->ssm_fm_cap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE | 1062 DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE; 1063 1064 /* 1065 * Request or capability level and get our parents capability 1066 * and ibc. 1067 */ 1068 ddi_fm_init(softsp->dip, &softsp->ssm_fm_cap, &softsp->ssm_fm_ibc); 1069 ASSERT((softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE) && 1070 (softsp->ssm_fm_cap & DDI_FM_ERRCB_CAPABLE)); 1071 /* 1072 * Register error callback with our parent. 1073 */ 1074 ddi_fm_handler_register(softsp->dip, ssm_err_callback, NULL); 1075 } 1076 1077 /* 1078 * Breakdown our FMA resources 1079 */ 1080 static void 1081 ssm_fm_fini(struct ssm_soft_state *softsp) 1082 { 1083 /* 1084 * Clean up allocated fm structures 1085 */ 1086 ASSERT(softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE); 1087 ddi_fm_handler_unregister(softsp->dip); 1088 ddi_fm_fini(softsp->dip); 1089 } 1090 1091 /* 1092 * Initialize FMA resources for children devices. Called when 1093 * child calls ddi_fm_init(). 1094 */ 1095 /*ARGSUSED*/ 1096 static int 1097 ssm_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap, 1098 ddi_iblock_cookie_t *ibc) 1099 { 1100 struct ssm_soft_state *softsp = ddi_get_soft_state(ssm_softstates, 1101 ddi_get_instance(dip)); 1102 1103 *ibc = softsp->ssm_fm_ibc; 1104 return (softsp->ssm_fm_cap); 1105 } 1106 1107 /* 1108 * FMA registered error callback 1109 */ 1110 /*ARGSUSED*/ 1111 static int 1112 ssm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data) 1113 { 1114 /* Call our children error handlers */ 1115 return (ndi_fm_handler_dispatch(dip, NULL, derr)); 1116 } 1117