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 #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 (*sbd_setup_instance)(int, dev_info_t *, int, int, caddr_t); 713 ssm_sbdp_info_t sbdp_info; 714 int rv; 715 716 instance = (getminor(*devi) >> SSM_INSTANCE_SHIFT); 717 718 softsp = ddi_get_soft_state(ssm_softstates, instance); 719 if (softsp == NULL) { 720 cmn_err(CE_WARN, "ssm_open bad instance number %d", instance); 721 return (ENXIO); 722 } 723 724 board = (getminor(*devi) & SSM_BOARD_MASK); 725 726 if (board < 0 || board > plat_max_boards()) { 727 return (ENXIO); 728 } 729 730 mutex_enter(&ssm_lock); 731 if (instance == 0 && ssm_loaded_sbd == FALSE) { 732 733 if (modload("misc", "sbd") == -1) { 734 cmn_err(CE_WARN, "ssm_open: cannot load sbd"); 735 mutex_exit(&ssm_lock); 736 return (EIO); 737 } 738 ssm_loaded_sbd = TRUE; 739 } 740 mutex_exit(&ssm_lock); 741 742 mutex_enter(&softsp->ssm_sft_lock); 743 if (softsp->initialized == FALSE) { 744 745 if (softsp->top_node == NULL) { 746 cmn_err(CE_WARN, "cannot find ssm top dnode"); 747 mutex_exit(&softsp->ssm_sft_lock); 748 return (EIO); 749 } 750 751 sbd_setup_instance = (int (*)(int, dev_info_t *, int, int, 752 caddr_t))modgetsymvalue("sbd_setup_instance", 0); 753 754 if (!sbd_setup_instance) { 755 cmn_err(CE_WARN, "cannot find sbd_setup_instance"); 756 mutex_exit(&softsp->ssm_sft_lock); 757 return (EIO); 758 } 759 760 sbdp_info.instance = instance; 761 sbdp_info.wnode = softsp->ssm_nodeid; 762 763 rv = (*sbd_setup_instance)(instance, softsp->top_node, 764 plat_max_boards(), softsp->ssm_nodeid, 765 (caddr_t)&sbdp_info); 766 if (rv != DDI_SUCCESS) { 767 cmn_err(CE_WARN, "cannot run sbd_setup_instance"); 768 mutex_exit(&softsp->ssm_sft_lock); 769 return (EIO); 770 } 771 softsp->initialized = TRUE; 772 } 773 mutex_exit(&softsp->ssm_sft_lock); 774 775 return (DDI_SUCCESS); 776 } 777 778 779 /* ARGSUSED */ 780 static int 781 ssm_close(dev_t dev, int flags, int otyp, cred_t *credp) 782 { 783 struct ssm_soft_state *softsp; 784 minor_t board, instance; 785 786 instance = (getminor(dev) >> SSM_INSTANCE_SHIFT); 787 788 softsp = ddi_get_soft_state(ssm_softstates, instance); 789 if (softsp == NULL) 790 return (ENXIO); 791 792 board = (getminor(dev) & SSM_BOARD_MASK); 793 794 if (board < 0 || board > plat_max_boards()) 795 return (ENXIO); 796 797 return (DDI_SUCCESS); 798 } 799 800 /* ARGSUSED */ 801 static int 802 ssm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 803 int *rvalp) 804 { 805 struct ssm_soft_state *softsp; 806 char *addr; 807 struct devctl_iocdata *dcp; 808 int instance, rv = 0; 809 int (*sbd_ioctl) (dev_t, int, intptr_t, int, char *); 810 811 instance = (getminor(dev) >> SSM_INSTANCE_SHIFT); 812 softsp = ddi_get_soft_state(ssm_softstates, instance); 813 if (softsp == NULL) 814 return (ENXIO); 815 816 switch (cmd) { 817 818 case DEVCTL_BUS_CONFIGURE: 819 /* 820 * read devctl ioctl data 821 */ 822 if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) 823 return (EFAULT); 824 825 addr = ndi_dc_getaddr(dcp); 826 cmn_err(CE_NOTE, 827 "DEVCTL_BUS_CONFIGURE: device id is %s\n", addr); 828 ndi_dc_freehdl(dcp); 829 break; 830 831 case DEVCTL_BUS_UNCONFIGURE: 832 if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) 833 return (EFAULT); 834 835 addr = ndi_dc_getaddr(dcp); 836 cmn_err(CE_NOTE, 837 "DEVCTL_BUS_UNCONFIGURE: device id is %s\n", addr); 838 ndi_dc_freehdl(dcp); 839 break; 840 841 #ifdef DEBUG 842 case SSM_TEARDOWN_SBD: { 843 ssm_sbdp_info_t sbdp_info; 844 int (*sbd_teardown_instance) (int, caddr_t); 845 sbd_teardown_instance = (int (*) (int, caddr_t)) 846 modgetsymvalue("sbd_teardown_instance", 0); 847 848 if (!sbd_teardown_instance) { 849 cmn_err(CE_WARN, "cannot find sbd_teardown_instance"); 850 return (EFAULT); 851 } 852 853 sbdp_info.instance = instance; 854 sbdp_info.wnode = softsp->ssm_nodeid; 855 rv = (*sbd_teardown_instance)(instance, (caddr_t)&sbdp_info); 856 if (rv != DDI_SUCCESS) { 857 cmn_err(CE_WARN, "cannot run sbd_teardown_instance"); 858 return (EFAULT); 859 } 860 861 ssm_loaded_sbd = FALSE; 862 softsp->initialized = FALSE; 863 } 864 #endif 865 866 867 default: { 868 char event = 0; 869 870 sbd_ioctl = (int (*) 871 (dev_t, int, intptr_t, int, char *)) 872 modgetsymvalue("sbd_ioctl", 0); 873 874 if (sbd_ioctl) 875 rv = (*sbd_ioctl) (dev, cmd, arg, mode, &event); 876 else { 877 cmn_err(CE_WARN, "cannot find sbd_ioctl"); 878 return (ENXIO); 879 } 880 /* 881 * Check to see if we need to send an event 882 */ 883 if (event == 1) { 884 int slot; 885 int hint = SE_NO_HINT; 886 887 if (rv == 0) { 888 if (cmd == SBD_CMD_CONNECT || 889 cmd == SBD_CMD_CONFIGURE) 890 hint = SE_HINT_INSERT; 891 else if (cmd == SBD_CMD_UNCONFIGURE || 892 cmd == SBD_CMD_DISCONNECT) 893 hint = SE_HINT_REMOVE; 894 } 895 896 slot = (getminor(dev) & SSM_BOARD_MASK); 897 ssm_generate_event(softsp->ssm_nodeid, slot, hint); 898 } 899 break; 900 } 901 } 902 903 return (rv); 904 } 905 906 void 907 ssm_get_attch_pnt(int node, int board, char *attach_pnt) 908 { 909 struct ssm_node2inst *sp; 910 911 /* 912 * Hold this mutex, until we are done so that ssm dip 913 * doesn't detach. 914 */ 915 mutex_enter(&ssm_node2inst_lock); 916 917 for (sp = &ssm_node2inst_map; sp != NULL; sp = sp->next) { 918 if (sp->inst == -1) 919 continue; 920 if (sp->nodeid == node) 921 break; 922 } 923 924 if (sp == NULL) { 925 /* We didn't find the ssm dip, return failure */ 926 attach_pnt[0] = '\0'; 927 mutex_exit(&ssm_node2inst_lock); 928 return; 929 } 930 931 /* 932 * we have the instance, and the board, construct the attch pnt 933 */ 934 if (SG_BOARD_IS_CPU_TYPE(board)) 935 (void) sprintf(attach_pnt, "ssm%d:N%d.SB%d", 936 sp->inst, node, board); 937 else 938 (void) sprintf(attach_pnt, "ssm%d:N%d.IB%d", 939 sp->inst, node, board); 940 941 mutex_exit(&ssm_node2inst_lock); 942 } 943 944 /* 945 * Generate an event to sysevent 946 */ 947 static int 948 ssm_generate_event(int node, int board, int hint) 949 { 950 sysevent_t *ev; 951 sysevent_id_t eid; 952 int rv = 0; 953 sysevent_value_t evnt_val; 954 sysevent_attr_list_t *evnt_attr_list = NULL; 955 char attach_pnt[MAXPATHLEN]; 956 957 958 attach_pnt[0] = '\0'; 959 ssm_get_attch_pnt(node, board, attach_pnt); 960 961 if (attach_pnt[0] == '\0') 962 return (-1); 963 964 ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, EP_DDI, 965 KM_SLEEP); 966 evnt_val.value_type = SE_DATA_TYPE_STRING; 967 evnt_val.value.sv_string = attach_pnt; 968 969 rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val, KM_SLEEP); 970 if (rv != 0) { 971 cmn_err(CE_WARN, "Failed to add attr [%s] for %s event", 972 DR_AP_ID, EC_DR); 973 sysevent_free(ev); 974 return (rv); 975 } 976 977 /* 978 * Add the hint 979 */ 980 evnt_val.value_type = SE_DATA_TYPE_STRING; 981 evnt_val.value.sv_string = SE_HINT2STR(hint); 982 983 rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val, KM_SLEEP); 984 if (rv != 0) { 985 cmn_err(CE_WARN, "Failed to add attr [%s] for %s event", 986 DR_HINT, EC_DR); 987 sysevent_free_attr(evnt_attr_list); 988 sysevent_free(ev); 989 return (-1); 990 } 991 992 if (sysevent_attach_attributes(ev, evnt_attr_list) != 0) { 993 cmn_err(CE_WARN, "Failed to attach attr list for %s event", 994 EC_DR); 995 sysevent_free_attr(evnt_attr_list); 996 sysevent_free(ev); 997 return (-1); 998 } 999 1000 rv = log_sysevent(ev, KM_NOSLEEP, &eid); 1001 if (rv != 0) { 1002 cmn_err(CE_WARN, "ssm_dr_event_handler: failed to log event"); 1003 } 1004 1005 sysevent_free(ev); 1006 1007 return (rv); 1008 } 1009 1010 /* 1011 * DR Event Handler 1012 */ 1013 uint_t 1014 ssm_dr_event_handler(char *arg) 1015 { 1016 sg_system_fru_descriptor_t *fdp; 1017 int hint; 1018 1019 1020 fdp = (sg_system_fru_descriptor_t *)(((sbbc_msg_t *)arg)->msg_buf); 1021 if (fdp == NULL) { 1022 DPRINTF(SSM_EVENT_DEBUG, 1023 ("ssm_dr_event_handler: ARG is null\n")); 1024 return (DDI_INTR_CLAIMED); 1025 } 1026 #ifdef DEBUG 1027 DPRINTF(SSM_EVENT_DEBUG, ("ssm_dr_event_handler called\n")); 1028 DPRINTF(SSM_EVENT_DEBUG, ("\tnode\t%d\n", fdp->node)); 1029 DPRINTF(SSM_EVENT_DEBUG, ("\tslot\t%d\n", fdp->slot)); 1030 DPRINTF(SSM_EVENT_DEBUG, ("\tparent_hdl\t0x%lx\n", fdp->parent_hdl)); 1031 DPRINTF(SSM_EVENT_DEBUG, ("\tchild_hdl\t0x%lx\n", fdp->child_hdl)); 1032 DPRINTF(SSM_EVENT_DEBUG, ("\tevent_details\t%s\n", 1033 EVNT2STR(fdp->event_details))); 1034 #endif 1035 1036 switch (fdp->event_details) { 1037 case SG_EVT_BOARD_ABSENT: 1038 hint = SE_HINT_REMOVE; 1039 break; 1040 case SG_EVT_BOARD_PRESENT: 1041 hint = SE_HINT_INSERT; 1042 break; 1043 default: 1044 hint = SE_NO_HINT; 1045 break; 1046 1047 } 1048 1049 ssm_generate_event(fdp->node, fdp->slot, hint); 1050 1051 return (DDI_INTR_CLAIMED); 1052 } 1053 1054 /* 1055 * Initialize our FMA resources 1056 */ 1057 static void 1058 ssm_fm_init(struct ssm_soft_state *softsp) 1059 { 1060 softsp->ssm_fm_cap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE | 1061 DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE; 1062 1063 /* 1064 * Request or capability level and get our parents capability 1065 * and ibc. 1066 */ 1067 ddi_fm_init(softsp->dip, &softsp->ssm_fm_cap, &softsp->ssm_fm_ibc); 1068 ASSERT((softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE) && 1069 (softsp->ssm_fm_cap & DDI_FM_ERRCB_CAPABLE)); 1070 /* 1071 * Register error callback with our parent. 1072 */ 1073 ddi_fm_handler_register(softsp->dip, ssm_err_callback, NULL); 1074 } 1075 1076 /* 1077 * Breakdown our FMA resources 1078 */ 1079 static void 1080 ssm_fm_fini(struct ssm_soft_state *softsp) 1081 { 1082 /* 1083 * Clean up allocated fm structures 1084 */ 1085 ASSERT(softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE); 1086 ddi_fm_handler_unregister(softsp->dip); 1087 ddi_fm_fini(softsp->dip); 1088 } 1089 1090 /* 1091 * Initialize FMA resources for children devices. Called when 1092 * child calls ddi_fm_init(). 1093 */ 1094 /*ARGSUSED*/ 1095 static int 1096 ssm_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap, 1097 ddi_iblock_cookie_t *ibc) 1098 { 1099 struct ssm_soft_state *softsp = ddi_get_soft_state(ssm_softstates, 1100 ddi_get_instance(dip)); 1101 1102 *ibc = softsp->ssm_fm_ibc; 1103 return (softsp->ssm_fm_cap); 1104 } 1105 1106 /* 1107 * FMA registered error callback 1108 */ 1109 /*ARGSUSED*/ 1110 static int 1111 ssm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data) 1112 { 1113 /* Call our children error handlers */ 1114 return (ndi_fm_handler_dispatch(dip, NULL, derr)); 1115 } 1116