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