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