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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/types.h> 28 #include <sys/conf.h> 29 #include <sys/ddi.h> 30 #include <sys/sunddi.h> 31 #include <sys/modctl.h> 32 #include <sys/sunndi.h> 33 #include <sys/ddi_impldefs.h> 34 #include <sys/obpdefs.h> 35 #include <sys/cmn_err.h> 36 #include <sys/errno.h> 37 #include <sys/kmem.h> 38 #include <sys/debug.h> 39 #include <sys/sysmacros.h> 40 #include <sys/autoconf.h> 41 #include <sys/stat.h> 42 #include <sys/serengeti.h> 43 #include <sys/ssm.h> 44 #include <sys/sgsbbc_mailbox.h> 45 #include <sys/sgevents.h> 46 #include <sys/sysevent.h> 47 #include <sys/sysevent/dr.h> 48 #include <sys/sysevent/eventdefs.h> 49 #include <sys/ndi_impldefs.h> 50 #include <sys/ddifm.h> 51 #include <sys/ndifm.h> 52 #include <sys/sbd_ioctl.h> 53 54 /* Useful debugging Stuff */ 55 #include <sys/nexusdebug.h> 56 57 /* 58 * module ssm.c 59 * 60 * This module is a nexus driver designed to support the ssm nexus driver 61 * and all children below it. This driver does not handle any of the 62 * DDI functions passed up to it by the ssm driver, but instead allows 63 * them to bubble up to the root node. 64 */ 65 66 67 /* 68 * Function prototypes 69 */ 70 extern int plat_max_boards(); 71 72 static int 73 ssm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result); 74 75 static int 76 ssm_attach(dev_info_t *, ddi_attach_cmd_t); 77 78 static int 79 ssm_detach(dev_info_t *, ddi_detach_cmd_t); 80 81 static int 82 ssm_open(dev_t *, int, int, cred_t *); 83 84 static int 85 ssm_close(dev_t, int, int, cred_t *); 86 87 static int 88 ssm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 89 90 static int 91 ssm_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *); 92 93 static int 94 ssm_make_nodes(dev_info_t *dip, int instance, int ssm_nodeid); 95 96 static int 97 ssm_generate_event(int node, int board, int hint); 98 99 /* 100 * FMA error callback 101 * Register error handling callback with our parent. We will just call 102 * our children's error callbacks and return their status. 103 */ 104 static int 105 ssm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data); 106 107 /* 108 * fm_init busop to initialize our children 109 */ 110 static int 111 ssm_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap, 112 ddi_iblock_cookie_t *ibc); 113 114 /* 115 * init/fini routines to alloc/dealloc fm structures and 116 * register/unregister our callback. 117 */ 118 static void 119 ssm_fm_init(struct ssm_soft_state *softsp); 120 121 static void 122 ssm_fm_fini(struct ssm_soft_state *softsp); 123 124 /* 125 * DR event handlers 126 * We want to register the event handlers once for all instances. In the 127 * other hand we have register them after the sbbc has been attached. 128 * event_initialize gives us the logic of only registering the events only 129 * once 130 */ 131 int event_initialized = 0; 132 uint_t ssm_dr_event_handler(char *); 133 134 /* 135 * Event lock and state 136 */ 137 static kmutex_t ssm_event_lock; 138 int ssm_event_state; 139 140 /* 141 * DR event msg and payload 142 */ 143 static sbbc_msg_t event_msg; 144 static sg_system_fru_descriptor_t payload; 145 146 struct ssm_node2inst { 147 int nodeid; /* serengeti node #, NOT prom nodeid */ 148 int inst; 149 struct ssm_node2inst *next; 150 }; 151 static kmutex_t ssm_node2inst_lock; 152 static struct ssm_node2inst ssm_node2inst_map = {-1, -1, NULL}; 153 154 155 /* 156 * Configuration data structures 157 */ 158 static struct bus_ops ssm_bus_ops = { 159 BUSO_REV, 160 ddi_bus_map, /* map */ 161 0, /* get_intrspec */ 162 0, /* add_intrspec */ 163 0, /* remove_intrspec */ 164 i_ddi_map_fault, /* map_fault */ 165 ddi_dma_map, /* dma_map */ 166 ddi_dma_allochdl, 167 ddi_dma_freehdl, 168 ddi_dma_bindhdl, 169 ddi_dma_unbindhdl, 170 ddi_dma_flush, 171 ddi_dma_win, 172 ddi_dma_mctl, /* dma_ctl */ 173 ssm_ctlops, /* ctl */ 174 ddi_bus_prop_op, /* prop_op */ 175 ndi_busop_get_eventcookie, 176 ndi_busop_add_eventcall, 177 ndi_busop_remove_eventcall, 178 ndi_post_event, 179 0, 180 0, 181 0, 182 ssm_fm_init_child, 183 NULL, 184 NULL, 185 NULL, 186 0, 187 i_ddi_intr_ops 188 }; 189 190 static struct cb_ops ssm_cb_ops = { 191 ssm_open, /* open */ 192 ssm_close, /* close */ 193 nodev, /* strategy */ 194 nodev, /* print */ 195 nodev, /* dump */ 196 nodev, /* read */ 197 nodev, /* write */ 198 ssm_ioctl, /* ioctl */ 199 nodev, /* devmap */ 200 nodev, /* mmap */ 201 nodev, /* segmap */ 202 nochpoll, /* poll */ 203 ddi_prop_op, /* cb_prop_op */ 204 NULL, /* streamtab */ 205 D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 206 CB_REV, /* rev */ 207 nodev, /* int (*cb_aread)() */ 208 nodev /* int (*cb_awrite)() */ 209 }; 210 211 static struct dev_ops ssm_ops = { 212 DEVO_REV, /* devo_rev, */ 213 0, /* refcnt */ 214 ssm_info, /* getinfo */ 215 nulldev, /* identify */ 216 nulldev, /* probe */ 217 ssm_attach, /* attach */ 218 ssm_detach, /* detach */ 219 nulldev, /* reset */ 220 &ssm_cb_ops, /* driver operations */ 221 &ssm_bus_ops, /* bus_ops */ 222 nulldev /* power */ 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 v1.8", /* 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 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, devi, 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 ssm_generate_event(softsp->ssm_nodeid, slot, hint); 892 } 893 break; 894 } 895 } 896 897 return (rv); 898 } 899 900 void 901 ssm_get_attch_pnt(int node, int board, char *attach_pnt) 902 { 903 struct ssm_node2inst *sp; 904 905 /* 906 * Hold this mutex, until we are done so that ssm dip 907 * doesn't detach. 908 */ 909 mutex_enter(&ssm_node2inst_lock); 910 911 for (sp = &ssm_node2inst_map; sp != NULL; sp = sp->next) { 912 if (sp->inst == -1) 913 continue; 914 if (sp->nodeid == node) 915 break; 916 } 917 918 if (sp == NULL) { 919 /* We didn't find the ssm dip, return failure */ 920 attach_pnt[0] = '\0'; 921 mutex_exit(&ssm_node2inst_lock); 922 return; 923 } 924 925 /* 926 * we have the instance, and the board, construct the attch pnt 927 */ 928 if (SG_BOARD_IS_CPU_TYPE(board)) 929 (void) sprintf(attach_pnt, "ssm%d:N%d.SB%d", 930 sp->inst, node, board); 931 else 932 (void) sprintf(attach_pnt, "ssm%d:N%d.IB%d", 933 sp->inst, node, board); 934 935 mutex_exit(&ssm_node2inst_lock); 936 } 937 938 /* 939 * Generate an event to sysevent 940 */ 941 static int 942 ssm_generate_event(int node, int board, int hint) 943 { 944 sysevent_t *ev; 945 sysevent_id_t eid; 946 int rv = 0; 947 sysevent_value_t evnt_val; 948 sysevent_attr_list_t *evnt_attr_list = NULL; 949 char attach_pnt[MAXPATHLEN]; 950 951 952 attach_pnt[0] = '\0'; 953 ssm_get_attch_pnt(node, board, attach_pnt); 954 955 if (attach_pnt[0] == '\0') 956 return (-1); 957 958 ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, EP_DDI, 959 KM_SLEEP); 960 evnt_val.value_type = SE_DATA_TYPE_STRING; 961 evnt_val.value.sv_string = attach_pnt; 962 963 rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val, KM_SLEEP); 964 if (rv != 0) { 965 cmn_err(CE_WARN, "Failed to add attr [%s] for %s event", 966 DR_AP_ID, EC_DR); 967 sysevent_free(ev); 968 return (rv); 969 } 970 971 /* 972 * Add the hint 973 */ 974 evnt_val.value_type = SE_DATA_TYPE_STRING; 975 evnt_val.value.sv_string = SE_HINT2STR(hint); 976 977 rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val, KM_SLEEP); 978 if (rv != 0) { 979 cmn_err(CE_WARN, "Failed to add attr [%s] for %s event", 980 DR_HINT, EC_DR); 981 sysevent_free_attr(evnt_attr_list); 982 sysevent_free(ev); 983 return (-1); 984 } 985 986 if (sysevent_attach_attributes(ev, evnt_attr_list) != 0) { 987 cmn_err(CE_WARN, "Failed to attach attr list for %s event", 988 EC_DR); 989 sysevent_free_attr(evnt_attr_list); 990 sysevent_free(ev); 991 return (-1); 992 } 993 994 rv = log_sysevent(ev, KM_NOSLEEP, &eid); 995 if (rv != 0) { 996 cmn_err(CE_WARN, "ssm_dr_event_handler: failed to log event"); 997 } 998 999 sysevent_free(ev); 1000 1001 return (rv); 1002 } 1003 1004 /* 1005 * DR Event Handler 1006 */ 1007 uint_t 1008 ssm_dr_event_handler(char *arg) 1009 { 1010 sg_system_fru_descriptor_t *fdp; 1011 int hint; 1012 1013 1014 fdp = (sg_system_fru_descriptor_t *)(((sbbc_msg_t *)arg)->msg_buf); 1015 if (fdp == NULL) { 1016 DPRINTF(SSM_EVENT_DEBUG, 1017 ("ssm_dr_event_handler: ARG is null\n")); 1018 return (DDI_INTR_CLAIMED); 1019 } 1020 #ifdef DEBUG 1021 DPRINTF(SSM_EVENT_DEBUG, ("ssm_dr_event_handler called\n")); 1022 DPRINTF(SSM_EVENT_DEBUG, ("\tnode\t%d\n", fdp->node)); 1023 DPRINTF(SSM_EVENT_DEBUG, ("\tslot\t%d\n", fdp->slot)); 1024 DPRINTF(SSM_EVENT_DEBUG, ("\tparent_hdl\t0x%lx\n", fdp->parent_hdl)); 1025 DPRINTF(SSM_EVENT_DEBUG, ("\tchild_hdl\t0x%lx\n", fdp->child_hdl)); 1026 DPRINTF(SSM_EVENT_DEBUG, ("\tevent_details\t%s\n", 1027 EVNT2STR(fdp->event_details))); 1028 #endif 1029 1030 switch (fdp->event_details) { 1031 case SG_EVT_BOARD_ABSENT: 1032 hint = SE_HINT_REMOVE; 1033 break; 1034 case SG_EVT_BOARD_PRESENT: 1035 hint = SE_HINT_INSERT; 1036 break; 1037 default: 1038 hint = SE_NO_HINT; 1039 break; 1040 1041 } 1042 1043 ssm_generate_event(fdp->node, fdp->slot, hint); 1044 1045 return (DDI_INTR_CLAIMED); 1046 } 1047 1048 /* 1049 * Initialize our FMA resources 1050 */ 1051 static void 1052 ssm_fm_init(struct ssm_soft_state *softsp) 1053 { 1054 softsp->ssm_fm_cap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE | 1055 DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE; 1056 1057 /* 1058 * Request or capability level and get our parents capability 1059 * and ibc. 1060 */ 1061 ddi_fm_init(softsp->dip, &softsp->ssm_fm_cap, &softsp->ssm_fm_ibc); 1062 ASSERT((softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE) && 1063 (softsp->ssm_fm_cap & DDI_FM_ERRCB_CAPABLE)); 1064 /* 1065 * Register error callback with our parent. 1066 */ 1067 ddi_fm_handler_register(softsp->dip, ssm_err_callback, NULL); 1068 } 1069 1070 /* 1071 * Breakdown our FMA resources 1072 */ 1073 static void 1074 ssm_fm_fini(struct ssm_soft_state *softsp) 1075 { 1076 /* 1077 * Clean up allocated fm structures 1078 */ 1079 ASSERT(softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE); 1080 ddi_fm_handler_unregister(softsp->dip); 1081 ddi_fm_fini(softsp->dip); 1082 } 1083 1084 /* 1085 * Initialize FMA resources for children devices. Called when 1086 * child calls ddi_fm_init(). 1087 */ 1088 /*ARGSUSED*/ 1089 static int 1090 ssm_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap, 1091 ddi_iblock_cookie_t *ibc) 1092 { 1093 struct ssm_soft_state *softsp = ddi_get_soft_state(ssm_softstates, 1094 ddi_get_instance(dip)); 1095 1096 *ibc = softsp->ssm_fm_ibc; 1097 return (softsp->ssm_fm_cap); 1098 } 1099 1100 /* 1101 * FMA registered error callback 1102 */ 1103 /*ARGSUSED*/ 1104 static int 1105 ssm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data) 1106 { 1107 /* Call our children error handlers */ 1108 return (ndi_fm_handler_dispatch(dip, NULL, derr)); 1109 } 1110