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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* 27 * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved. 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 0, /* 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 ddi_quiesce_not_needed, /* quiesce */ 226 }; 227 228 /* 229 * Driver globals 230 */ 231 static void *ssm_softstates; /* ssm soft state hook */ 232 233 extern struct mod_ops mod_driverops; 234 235 static struct modldrv modldrv = { 236 &mod_driverops, /* Type of module. This one is a driver */ 237 "SSM Nexus", /* name of module */ 238 &ssm_ops, /* driver ops */ 239 }; 240 241 static struct modlinkage modlinkage = { 242 MODREV_1, /* rev */ 243 (void *)&modldrv, 244 NULL 245 }; 246 247 static int ssm_loaded_sbd = FALSE; 248 kmutex_t ssm_lock; 249 static int init_child(dev_info_t *child); 250 251 /* 252 * These are the module initialization routines. 253 */ 254 255 int 256 _init(void) 257 { 258 int error; 259 260 #if defined(DEBUG) 261 debug_print_level = 0x0; 262 #endif 263 264 /* Initialize soft state pointer. */ 265 if ((error = ddi_soft_state_init(&ssm_softstates, 266 sizeof (struct ssm_soft_state), 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 (void) 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, (void *)devi, (void *)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, "ssm:%s:%d: failed to make nodes", 391 ddi_driver_name(devi), instance); 392 ddi_remove_minor_node(devi, NULL); 393 ddi_soft_state_free(ssm_softstates, instance); 394 return (DDI_FAILURE); 395 } 396 ssm_fm_init(softsp); 397 ddi_report_dev(devi); 398 399 if (event_initialized == 0) { 400 int rv; 401 /* 402 * Register DR event handler 403 */ 404 mutex_init(&ssm_event_lock, NULL, MUTEX_DRIVER, NULL); 405 event_msg.msg_buf = (caddr_t)&payload; 406 event_msg.msg_len = sizeof (payload); 407 408 rv = sbbc_mbox_reg_intr(MBOX_EVENT_GENERIC, 409 ssm_dr_event_handler, &event_msg, 410 (uint_t *)&ssm_event_state, &ssm_event_lock); 411 412 if (rv == EINVAL) 413 event_initialized = 1; 414 } 415 416 /* 417 * Preallocate to avoid sleeping with ssm_node2inst_lock held - 418 * low level interrupts use this mutex. 419 */ 420 tsp = kmem_zalloc(sizeof (struct ssm_node2inst), KM_SLEEP); 421 422 mutex_enter(&ssm_node2inst_lock); 423 424 for (prev = NULL, sp = &ssm_node2inst_map; sp != NULL; 425 prev = sp, sp = sp->next) { 426 ASSERT(sp->inst != instance); 427 ASSERT(sp->nodeid != softsp->ssm_nodeid); 428 if (sp->inst == -1) 429 break; 430 } 431 432 if (sp == NULL) { 433 ASSERT(prev->next == NULL); 434 sp = prev->next = tsp; 435 tsp = NULL; 436 sp->next = NULL; 437 } 438 439 sp->inst = instance; 440 sp->nodeid = softsp->ssm_nodeid; 441 442 mutex_exit(&ssm_node2inst_lock); 443 444 if (tsp != NULL) 445 kmem_free(tsp, sizeof (struct ssm_node2inst)); 446 447 return (DDI_SUCCESS); 448 } 449 450 /* 451 * detach entry point: 452 */ 453 /*ARGSUSED*/ 454 static int 455 ssm_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 456 { 457 int instance, rv; 458 int (*sbd_teardown_instance) (int, caddr_t); 459 ssm_sbdp_info_t sbdp_info; 460 struct ssm_soft_state *softsp; 461 struct ssm_node2inst *prev, *sp; 462 463 instance = ddi_get_instance(devi); 464 softsp = ddi_get_soft_state(ssm_softstates, instance); 465 466 if (softsp == NULL) { 467 cmn_err(CE_WARN, 468 "ssm_open bad instance number %d", instance); 469 return (ENXIO); 470 } 471 472 instance = ddi_get_instance(devi); 473 474 switch (cmd) { 475 case DDI_DETACH: 476 ddi_remove_minor_node(devi, NULL); 477 478 sbd_teardown_instance = (int (*) (int, caddr_t)) 479 modlookup("misc/sbd", "sbd_teardown_instance"); 480 481 if (!sbd_teardown_instance) { 482 cmn_err(CE_WARN, "cannot find sbd_teardown_instance"); 483 return (DDI_FAILURE); 484 } 485 486 sbdp_info.instance = instance; 487 sbdp_info.wnode = softsp->ssm_nodeid; 488 rv = (*sbd_teardown_instance)(instance, (caddr_t)&sbdp_info); 489 490 if (rv != DDI_SUCCESS) { 491 cmn_err(CE_WARN, "cannot run sbd_teardown_instance"); 492 return (DDI_FAILURE); 493 } 494 ssm_fm_fini(softsp); 495 mutex_destroy(&softsp->ssm_sft_lock); 496 ddi_soft_state_free(ssm_softstates, instance); 497 498 mutex_enter(&ssm_node2inst_lock); 499 for (prev = NULL, sp = &ssm_node2inst_map; sp != NULL; 500 prev = sp, sp = sp->next) { 501 /* Only the head of the list can persist if unused */ 502 ASSERT(prev == NULL || sp->inst != -1); 503 if (sp->inst == instance) 504 break; 505 } 506 ASSERT(sp != NULL); 507 508 if (sp != &ssm_node2inst_map) { 509 prev->next = sp->next; 510 kmem_free(sp, sizeof (struct ssm_node2inst)); 511 } else { 512 /* 513 * Invalidate the head element, but retain the rest 514 * of the list - "next" is still valid. 515 */ 516 517 sp->nodeid = -1; 518 sp->inst = -1; 519 } 520 mutex_exit(&ssm_node2inst_lock); 521 522 return (DDI_SUCCESS); 523 524 case DDI_SUSPEND: 525 return (DDI_SUCCESS); 526 527 default: 528 return (DDI_FAILURE); 529 } 530 } 531 532 extern void make_ddi_ppd(dev_info_t *, struct ddi_parent_private_data **); 533 extern struct ddi_parent_private_data *init_regspec_64(dev_info_t *); 534 535 static int 536 name_child(dev_info_t *child, char *name, int namelen) 537 { 538 struct regspec *rp; 539 struct ddi_parent_private_data *pdptr; 540 int portid = 0; 541 int regbase = -1; 542 extern uint_t root_phys_addr_lo_mask; 543 544 make_ddi_ppd(child, &pdptr); 545 ddi_set_parent_data(child, pdptr); 546 547 name[0] = '\0'; 548 if (sparc_pd_getnreg(child) == 0) 549 return (DDI_SUCCESS); 550 551 rp = sparc_pd_getreg(child, 0); 552 553 portid = ddi_prop_get_int(DDI_DEV_T_ANY, child, 554 DDI_PROP_DONTPASS, "portid", -1); 555 if (portid == -1) { 556 cmn_err(CE_WARN, "could not find portid property in %s", 557 DEVI(child)->devi_node_name); 558 } else { 559 regbase = rp->regspec_addr & root_phys_addr_lo_mask; 560 } 561 (void) snprintf(name, namelen, "%x,%x", portid, regbase); 562 return (DDI_SUCCESS); 563 } 564 565 static int 566 init_child(dev_info_t *child) 567 { 568 char name[MAXNAMELEN]; 569 570 (void) name_child(child, name, MAXNAMELEN); 571 ddi_set_name_addr(child, name); 572 if ((ndi_dev_is_persistent_node(child) == 0) && 573 (ndi_merge_node(child, name_child) == DDI_SUCCESS)) { 574 impl_ddi_sunbus_removechild(child); 575 return (DDI_FAILURE); 576 } 577 578 (void) init_regspec_64(child); 579 return (DDI_SUCCESS); 580 } 581 582 /* 583 * Control ops entry point: 584 * 585 * Requests handled completely: 586 * DDI_CTLOPS_INITCHILD 587 * DDI_CTLOPS_UNINITCHILD 588 * DDI_CTLOPS_REPORTDEV 589 * All others are passed to the parent. 590 * The name of the ssm node is ssm@nodeid,0. 591 * ssm is the equivalent of rootnex. 592 */ 593 static int 594 ssm_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, void *arg, 595 void *result) 596 { 597 int rval; 598 599 switch (op) { 600 case DDI_CTLOPS_INITCHILD: { 601 DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_INITCHILD\n")); 602 return (init_child((dev_info_t *)arg)); 603 } 604 605 case DDI_CTLOPS_UNINITCHILD: { 606 DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_UNINITCHILD\n")); 607 impl_ddi_sunbus_removechild((dev_info_t *)arg); 608 return (DDI_SUCCESS); 609 } 610 611 case DDI_CTLOPS_REPORTDEV: { 612 char buf[80]; 613 char *p = buf; 614 dev_info_t *parent; 615 int portid; 616 617 DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_REPORTDEV\n")); 618 parent = ddi_get_parent(rdip); 619 620 (void) sprintf(p, "%s%d at %s%d", DEVI(rdip)->devi_name, 621 DEVI(rdip)->devi_instance, ddi_get_name(parent), 622 ddi_get_instance(parent)); 623 p += strlen(p); 624 625 /* Fetch Safari Extended Agent ID of this device. */ 626 portid = (int)ddi_getprop(DDI_DEV_T_ANY, rdip, 627 DDI_PROP_DONTPASS, "portid", -1); 628 629 /* 630 * If this is one of the ssm children it will have 631 * portid property and its parent will be ssm. 632 * In this case report Node number and Safari id. 633 */ 634 if (portid != -1 && 635 strcmp("ssm", ddi_get_name(parent)) == 0) { 636 struct regspec *rp; 637 int node; 638 int safid; 639 int n; 640 641 rp = sparc_pd_getreg(rdip, 0); 642 n = sparc_pd_getnreg(rdip); 643 ASSERT(n > 0); 644 645 node = SG_PORTID_TO_NODEID(portid); 646 safid = SG_PORTID_TO_SAFARI_ID(portid); 647 648 (void) strcpy(p, ": "); 649 p += strlen(p); 650 651 (void) sprintf(p, "Node %d Safari id %d 0x%x%s", 652 node, safid, 653 rp->regspec_addr, 654 (n > 1 ? "" : " ...")); 655 p += strlen(p); 656 } 657 658 cmn_err(CE_CONT, "?%s\n", buf); 659 rval = DDI_SUCCESS; 660 661 break; 662 } 663 664 default: 665 rval = ddi_ctlops(dip, rdip, op, arg, result); 666 667 break; 668 } 669 670 return (rval); 671 } 672 673 /*ARGSUSED*/ 674 static int 675 ssm_make_nodes(dev_info_t *dip, int instance, int ssm_nodeid) 676 { 677 int rv; 678 minor_t minor_num, bd; 679 auto char filename[20]; 680 681 for (bd = 0; bd < plat_max_boards(); bd++) { 682 if (SG_BOARD_IS_CPU_TYPE(bd)) 683 (void) sprintf(filename, "N%d.SB%d", ssm_nodeid, bd); 684 else 685 (void) sprintf(filename, "N%d.IB%d", ssm_nodeid, bd); 686 687 minor_num = (instance << SSM_INSTANCE_SHIFT) | bd; 688 689 rv = ddi_create_minor_node(dip, filename, S_IFCHR, 690 minor_num, DDI_NT_SBD_ATTACHMENT_POINT, NULL); 691 if (rv == DDI_FAILURE) { 692 cmn_err(CE_WARN, 693 "ssm_make_nodes:%d: failed to create " 694 "minor node (%s, 0x%x)", 695 instance, filename, minor_num); 696 return (-1); 697 } 698 } 699 700 return (0); 701 } 702 703 704 /* ARGSUSED */ 705 static int 706 ssm_open(dev_t *devi, int flags, int otyp, cred_t *credp) 707 { 708 struct ssm_soft_state *softsp; 709 minor_t board, instance; 710 int (*sbd_setup_instance)(int, dev_info_t *, int, int, caddr_t); 711 ssm_sbdp_info_t sbdp_info; 712 int rv; 713 714 instance = (getminor(*devi) >> SSM_INSTANCE_SHIFT); 715 716 softsp = ddi_get_soft_state(ssm_softstates, instance); 717 if (softsp == NULL) { 718 cmn_err(CE_WARN, "ssm_open bad instance number %d", instance); 719 return (ENXIO); 720 } 721 722 board = (getminor(*devi) & SSM_BOARD_MASK); 723 724 if (board < 0 || board > plat_max_boards()) { 725 return (ENXIO); 726 } 727 728 mutex_enter(&ssm_lock); 729 if (instance == 0 && ssm_loaded_sbd == FALSE) { 730 731 if (modload("misc", "sbd") == -1) { 732 cmn_err(CE_WARN, "ssm_open: cannot load sbd"); 733 mutex_exit(&ssm_lock); 734 return (EIO); 735 } 736 ssm_loaded_sbd = TRUE; 737 } 738 mutex_exit(&ssm_lock); 739 740 mutex_enter(&softsp->ssm_sft_lock); 741 if (softsp->initialized == FALSE) { 742 743 if (softsp->top_node == NULL) { 744 cmn_err(CE_WARN, "cannot find ssm top dnode"); 745 mutex_exit(&softsp->ssm_sft_lock); 746 return (EIO); 747 } 748 749 sbd_setup_instance = (int (*)(int, dev_info_t *, int, int, 750 caddr_t))modlookup("misc/sbd", "sbd_setup_instance"); 751 752 if (!sbd_setup_instance) { 753 cmn_err(CE_WARN, "cannot find sbd_setup_instance"); 754 mutex_exit(&softsp->ssm_sft_lock); 755 return (EIO); 756 } 757 758 sbdp_info.instance = instance; 759 sbdp_info.wnode = softsp->ssm_nodeid; 760 761 rv = (*sbd_setup_instance)(instance, softsp->top_node, 762 plat_max_boards(), softsp->ssm_nodeid, 763 (caddr_t)&sbdp_info); 764 if (rv != DDI_SUCCESS) { 765 cmn_err(CE_WARN, "cannot run sbd_setup_instance"); 766 mutex_exit(&softsp->ssm_sft_lock); 767 return (EIO); 768 } 769 softsp->initialized = TRUE; 770 } 771 mutex_exit(&softsp->ssm_sft_lock); 772 773 return (DDI_SUCCESS); 774 } 775 776 777 /* ARGSUSED */ 778 static int 779 ssm_close(dev_t dev, int flags, int otyp, cred_t *credp) 780 { 781 struct ssm_soft_state *softsp; 782 minor_t board, instance; 783 784 instance = (getminor(dev) >> SSM_INSTANCE_SHIFT); 785 786 softsp = ddi_get_soft_state(ssm_softstates, instance); 787 if (softsp == NULL) 788 return (ENXIO); 789 790 board = (getminor(dev) & SSM_BOARD_MASK); 791 792 if (board < 0 || board > plat_max_boards()) 793 return (ENXIO); 794 795 return (DDI_SUCCESS); 796 } 797 798 /* ARGSUSED */ 799 static int 800 ssm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 801 int *rvalp) 802 { 803 struct ssm_soft_state *softsp; 804 char *addr; 805 struct devctl_iocdata *dcp; 806 int instance, rv = 0; 807 int (*sbd_ioctl) (dev_t, int, intptr_t, int, char *); 808 809 instance = (getminor(dev) >> SSM_INSTANCE_SHIFT); 810 softsp = ddi_get_soft_state(ssm_softstates, instance); 811 if (softsp == NULL) 812 return (ENXIO); 813 814 switch (cmd) { 815 816 case DEVCTL_BUS_CONFIGURE: 817 /* 818 * read devctl ioctl data 819 */ 820 if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) 821 return (EFAULT); 822 823 addr = ndi_dc_getaddr(dcp); 824 cmn_err(CE_NOTE, 825 "DEVCTL_BUS_CONFIGURE: device id is %s\n", addr); 826 ndi_dc_freehdl(dcp); 827 break; 828 829 case DEVCTL_BUS_UNCONFIGURE: 830 if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) 831 return (EFAULT); 832 833 addr = ndi_dc_getaddr(dcp); 834 cmn_err(CE_NOTE, 835 "DEVCTL_BUS_UNCONFIGURE: device id is %s\n", addr); 836 ndi_dc_freehdl(dcp); 837 break; 838 839 #ifdef DEBUG 840 case SSM_TEARDOWN_SBD: { 841 ssm_sbdp_info_t sbdp_info; 842 int (*sbd_teardown_instance) (int, caddr_t); 843 sbd_teardown_instance = (int (*) (int, caddr_t)) 844 modlookup("misc/sbd", "sbd_teardown_instance"); 845 846 if (!sbd_teardown_instance) { 847 cmn_err(CE_WARN, "cannot find sbd_teardown_instance"); 848 return (EFAULT); 849 } 850 851 sbdp_info.instance = instance; 852 sbdp_info.wnode = softsp->ssm_nodeid; 853 rv = (*sbd_teardown_instance)(instance, (caddr_t)&sbdp_info); 854 if (rv != DDI_SUCCESS) { 855 cmn_err(CE_WARN, "cannot run sbd_teardown_instance"); 856 return (EFAULT); 857 } 858 859 ssm_loaded_sbd = FALSE; 860 softsp->initialized = FALSE; 861 } 862 #endif 863 864 865 default: { 866 char event = 0; 867 868 sbd_ioctl = (int (*) (dev_t, int, intptr_t, int, char *)) 869 modlookup("misc/sbd", "sbd_ioctl"); 870 871 if (sbd_ioctl) 872 rv = (*sbd_ioctl) (dev, cmd, arg, mode, &event); 873 else { 874 cmn_err(CE_WARN, "cannot find sbd_ioctl"); 875 return (ENXIO); 876 } 877 /* 878 * Check to see if we need to send an event 879 */ 880 if (event == 1) { 881 int slot; 882 int hint = SE_NO_HINT; 883 884 if (rv == 0) { 885 if (cmd == SBD_CMD_CONNECT || 886 cmd == SBD_CMD_CONFIGURE) 887 hint = SE_HINT_INSERT; 888 else if (cmd == SBD_CMD_UNCONFIGURE || 889 cmd == SBD_CMD_DISCONNECT) 890 hint = SE_HINT_REMOVE; 891 } 892 893 slot = (getminor(dev) & SSM_BOARD_MASK); 894 (void) ssm_generate_event(softsp->ssm_nodeid, slot, 895 hint); 896 } 897 break; 898 } 899 } 900 901 return (rv); 902 } 903 904 void 905 ssm_get_attch_pnt(int node, int board, char *attach_pnt) 906 { 907 struct ssm_node2inst *sp; 908 909 /* 910 * Hold this mutex, until we are done so that ssm dip 911 * doesn't detach. 912 */ 913 mutex_enter(&ssm_node2inst_lock); 914 915 for (sp = &ssm_node2inst_map; sp != NULL; sp = sp->next) { 916 if (sp->inst == -1) 917 continue; 918 if (sp->nodeid == node) 919 break; 920 } 921 922 if (sp == NULL) { 923 /* We didn't find the ssm dip, return failure */ 924 attach_pnt[0] = '\0'; 925 mutex_exit(&ssm_node2inst_lock); 926 return; 927 } 928 929 /* 930 * we have the instance, and the board, construct the attch pnt 931 */ 932 if (SG_BOARD_IS_CPU_TYPE(board)) 933 (void) sprintf(attach_pnt, "ssm%d:N%d.SB%d", 934 sp->inst, node, board); 935 else 936 (void) sprintf(attach_pnt, "ssm%d:N%d.IB%d", 937 sp->inst, node, board); 938 939 mutex_exit(&ssm_node2inst_lock); 940 } 941 942 /* 943 * Generate an event to sysevent 944 */ 945 static int 946 ssm_generate_event(int node, int board, int hint) 947 { 948 sysevent_t *ev; 949 sysevent_id_t eid; 950 int rv = 0; 951 sysevent_value_t evnt_val; 952 sysevent_attr_list_t *evnt_attr_list = NULL; 953 char attach_pnt[MAXPATHLEN]; 954 955 956 attach_pnt[0] = '\0'; 957 ssm_get_attch_pnt(node, board, attach_pnt); 958 959 if (attach_pnt[0] == '\0') 960 return (-1); 961 962 ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, EP_DDI, 963 KM_SLEEP); 964 evnt_val.value_type = SE_DATA_TYPE_STRING; 965 evnt_val.value.sv_string = attach_pnt; 966 967 rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val, KM_SLEEP); 968 if (rv != 0) { 969 cmn_err(CE_WARN, "Failed to add attr [%s] for %s event", 970 DR_AP_ID, EC_DR); 971 sysevent_free(ev); 972 return (rv); 973 } 974 975 /* 976 * Add the hint 977 */ 978 evnt_val.value_type = SE_DATA_TYPE_STRING; 979 evnt_val.value.sv_string = SE_HINT2STR(hint); 980 981 rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val, KM_SLEEP); 982 if (rv != 0) { 983 cmn_err(CE_WARN, "Failed to add attr [%s] for %s event", 984 DR_HINT, EC_DR); 985 sysevent_free_attr(evnt_attr_list); 986 sysevent_free(ev); 987 return (-1); 988 } 989 990 if (sysevent_attach_attributes(ev, evnt_attr_list) != 0) { 991 cmn_err(CE_WARN, "Failed to attach attr list for %s event", 992 EC_DR); 993 sysevent_free_attr(evnt_attr_list); 994 sysevent_free(ev); 995 return (-1); 996 } 997 998 rv = log_sysevent(ev, KM_NOSLEEP, &eid); 999 if (rv != 0) { 1000 cmn_err(CE_WARN, "ssm_dr_event_handler: failed to log event"); 1001 } 1002 1003 sysevent_free(ev); 1004 1005 return (rv); 1006 } 1007 1008 /* 1009 * DR Event Handler 1010 */ 1011 uint_t 1012 ssm_dr_event_handler(char *arg) 1013 { 1014 sg_system_fru_descriptor_t *fdp; 1015 int hint; 1016 1017 1018 fdp = (sg_system_fru_descriptor_t *)(((sbbc_msg_t *)arg)->msg_buf); 1019 if (fdp == NULL) { 1020 DPRINTF(SSM_EVENT_DEBUG, 1021 ("ssm_dr_event_handler: ARG is null\n")); 1022 return (DDI_INTR_CLAIMED); 1023 } 1024 #ifdef DEBUG 1025 DPRINTF(SSM_EVENT_DEBUG, ("ssm_dr_event_handler called\n")); 1026 DPRINTF(SSM_EVENT_DEBUG, ("\tnode\t%d\n", fdp->node)); 1027 DPRINTF(SSM_EVENT_DEBUG, ("\tslot\t%d\n", fdp->slot)); 1028 DPRINTF(SSM_EVENT_DEBUG, ("\tparent_hdl\t0x%lx\n", fdp->parent_hdl)); 1029 DPRINTF(SSM_EVENT_DEBUG, ("\tchild_hdl\t0x%lx\n", fdp->child_hdl)); 1030 DPRINTF(SSM_EVENT_DEBUG, ("\tevent_details\t%s\n", 1031 EVNT2STR(fdp->event_details))); 1032 #endif 1033 1034 switch (fdp->event_details) { 1035 case SG_EVT_BOARD_ABSENT: 1036 hint = SE_HINT_REMOVE; 1037 break; 1038 case SG_EVT_BOARD_PRESENT: 1039 hint = SE_HINT_INSERT; 1040 break; 1041 default: 1042 hint = SE_NO_HINT; 1043 break; 1044 1045 } 1046 1047 (void) ssm_generate_event(fdp->node, fdp->slot, hint); 1048 1049 return (DDI_INTR_CLAIMED); 1050 } 1051 1052 /* 1053 * Initialize our FMA resources 1054 */ 1055 static void 1056 ssm_fm_init(struct ssm_soft_state *softsp) 1057 { 1058 softsp->ssm_fm_cap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE | 1059 DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE; 1060 1061 /* 1062 * Request or capability level and get our parents capability 1063 * and ibc. 1064 */ 1065 ddi_fm_init(softsp->dip, &softsp->ssm_fm_cap, &softsp->ssm_fm_ibc); 1066 ASSERT((softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE) && 1067 (softsp->ssm_fm_cap & DDI_FM_ERRCB_CAPABLE)); 1068 /* 1069 * Register error callback with our parent. 1070 */ 1071 ddi_fm_handler_register(softsp->dip, ssm_err_callback, NULL); 1072 } 1073 1074 /* 1075 * Breakdown our FMA resources 1076 */ 1077 static void 1078 ssm_fm_fini(struct ssm_soft_state *softsp) 1079 { 1080 /* 1081 * Clean up allocated fm structures 1082 */ 1083 ASSERT(softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE); 1084 ddi_fm_handler_unregister(softsp->dip); 1085 ddi_fm_fini(softsp->dip); 1086 } 1087 1088 /* 1089 * Initialize FMA resources for children devices. Called when 1090 * child calls ddi_fm_init(). 1091 */ 1092 /*ARGSUSED*/ 1093 static int 1094 ssm_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap, 1095 ddi_iblock_cookie_t *ibc) 1096 { 1097 struct ssm_soft_state *softsp = ddi_get_soft_state(ssm_softstates, 1098 ddi_get_instance(dip)); 1099 1100 *ibc = softsp->ssm_fm_ibc; 1101 return (softsp->ssm_fm_cap); 1102 } 1103 1104 /* 1105 * FMA registered error callback 1106 */ 1107 /*ARGSUSED*/ 1108 static int 1109 ssm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data) 1110 { 1111 /* Call our children error handlers */ 1112 return (ndi_fm_handler_dispatch(dip, NULL, derr)); 1113 } 1114