103831d35Sstevel /* 203831d35Sstevel * CDDL HEADER START 303831d35Sstevel * 403831d35Sstevel * The contents of this file are subject to the terms of the 503831d35Sstevel * Common Development and Distribution License (the "License"). 603831d35Sstevel * You may not use this file except in compliance with the License. 703831d35Sstevel * 803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 903831d35Sstevel * or http://www.opensolaris.org/os/licensing. 1003831d35Sstevel * See the License for the specific language governing permissions 1103831d35Sstevel * and limitations under the License. 1203831d35Sstevel * 1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each 1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the 1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 1803831d35Sstevel * 1903831d35Sstevel * CDDL HEADER END 2003831d35Sstevel */ 2103831d35Sstevel 2203831d35Sstevel /* 2343d5cd3dSjohnlev * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 2403831d35Sstevel * Use is subject to license terms. 2503831d35Sstevel */ 2603831d35Sstevel 2703831d35Sstevel #include <sys/types.h> 2803831d35Sstevel #include <sys/conf.h> 2903831d35Sstevel #include <sys/ddi.h> 3003831d35Sstevel #include <sys/sunddi.h> 3103831d35Sstevel #include <sys/modctl.h> 3203831d35Sstevel #include <sys/sunndi.h> 3303831d35Sstevel #include <sys/ddi_impldefs.h> 3403831d35Sstevel #include <sys/obpdefs.h> 3503831d35Sstevel #include <sys/cmn_err.h> 3603831d35Sstevel #include <sys/errno.h> 3703831d35Sstevel #include <sys/kmem.h> 3803831d35Sstevel #include <sys/debug.h> 3903831d35Sstevel #include <sys/sysmacros.h> 4003831d35Sstevel #include <sys/autoconf.h> 4103831d35Sstevel #include <sys/stat.h> 4203831d35Sstevel #include <sys/serengeti.h> 4303831d35Sstevel #include <sys/ssm.h> 4403831d35Sstevel #include <sys/sgsbbc_mailbox.h> 4503831d35Sstevel #include <sys/sgevents.h> 4603831d35Sstevel #include <sys/sysevent.h> 4703831d35Sstevel #include <sys/sysevent/dr.h> 4803831d35Sstevel #include <sys/sysevent/eventdefs.h> 4903831d35Sstevel #include <sys/ndi_impldefs.h> 5003831d35Sstevel #include <sys/ddifm.h> 5103831d35Sstevel #include <sys/ndifm.h> 5203831d35Sstevel #include <sys/sbd_ioctl.h> 5303831d35Sstevel 5403831d35Sstevel /* Useful debugging Stuff */ 5503831d35Sstevel #include <sys/nexusdebug.h> 5603831d35Sstevel 5703831d35Sstevel /* 5803831d35Sstevel * module ssm.c 5903831d35Sstevel * 6003831d35Sstevel * This module is a nexus driver designed to support the ssm nexus driver 6103831d35Sstevel * and all children below it. This driver does not handle any of the 6203831d35Sstevel * DDI functions passed up to it by the ssm driver, but instead allows 6303831d35Sstevel * them to bubble up to the root node. 6403831d35Sstevel */ 6503831d35Sstevel 6603831d35Sstevel 6703831d35Sstevel /* 6803831d35Sstevel * Function prototypes 6903831d35Sstevel */ 7003831d35Sstevel extern int plat_max_boards(); 7103831d35Sstevel 7203831d35Sstevel static int 7303831d35Sstevel ssm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result); 7403831d35Sstevel 7503831d35Sstevel static int 7603831d35Sstevel ssm_attach(dev_info_t *, ddi_attach_cmd_t); 7703831d35Sstevel 7803831d35Sstevel static int 7903831d35Sstevel ssm_detach(dev_info_t *, ddi_detach_cmd_t); 8003831d35Sstevel 8103831d35Sstevel static int 8203831d35Sstevel ssm_open(dev_t *, int, int, cred_t *); 8303831d35Sstevel 8403831d35Sstevel static int 8503831d35Sstevel ssm_close(dev_t, int, int, cred_t *); 8603831d35Sstevel 8703831d35Sstevel static int 8803831d35Sstevel ssm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 8903831d35Sstevel 9003831d35Sstevel static int 9103831d35Sstevel ssm_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *); 9203831d35Sstevel 9303831d35Sstevel static int 9403831d35Sstevel ssm_make_nodes(dev_info_t *dip, int instance, int ssm_nodeid); 9503831d35Sstevel 9603831d35Sstevel static int 9703831d35Sstevel ssm_generate_event(int node, int board, int hint); 9803831d35Sstevel 9903831d35Sstevel /* 10003831d35Sstevel * FMA error callback 10103831d35Sstevel * Register error handling callback with our parent. We will just call 10203831d35Sstevel * our children's error callbacks and return their status. 10303831d35Sstevel */ 10403831d35Sstevel static int 10503831d35Sstevel ssm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data); 10603831d35Sstevel 10703831d35Sstevel /* 10803831d35Sstevel * fm_init busop to initialize our children 10903831d35Sstevel */ 11003831d35Sstevel static int 11103831d35Sstevel ssm_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap, 11203831d35Sstevel ddi_iblock_cookie_t *ibc); 11303831d35Sstevel 11403831d35Sstevel /* 11503831d35Sstevel * init/fini routines to alloc/dealloc fm structures and 11603831d35Sstevel * register/unregister our callback. 11703831d35Sstevel */ 11803831d35Sstevel static void 11903831d35Sstevel ssm_fm_init(struct ssm_soft_state *softsp); 12003831d35Sstevel 12103831d35Sstevel static void 12203831d35Sstevel ssm_fm_fini(struct ssm_soft_state *softsp); 12303831d35Sstevel 12403831d35Sstevel /* 12503831d35Sstevel * DR event handlers 12603831d35Sstevel * We want to register the event handlers once for all instances. In the 12703831d35Sstevel * other hand we have register them after the sbbc has been attached. 12803831d35Sstevel * event_initialize gives us the logic of only registering the events only 12903831d35Sstevel * once 13003831d35Sstevel */ 13103831d35Sstevel int event_initialized = 0; 13203831d35Sstevel uint_t ssm_dr_event_handler(char *); 13303831d35Sstevel 13403831d35Sstevel /* 13503831d35Sstevel * Event lock and state 13603831d35Sstevel */ 13703831d35Sstevel static kmutex_t ssm_event_lock; 13803831d35Sstevel int ssm_event_state; 13903831d35Sstevel 14003831d35Sstevel /* 14103831d35Sstevel * DR event msg and payload 14203831d35Sstevel */ 14303831d35Sstevel static sbbc_msg_t event_msg; 14403831d35Sstevel static sg_system_fru_descriptor_t payload; 14503831d35Sstevel 14603831d35Sstevel struct ssm_node2inst { 14703831d35Sstevel int nodeid; /* serengeti node #, NOT prom nodeid */ 14803831d35Sstevel int inst; 14903831d35Sstevel struct ssm_node2inst *next; 15003831d35Sstevel }; 15103831d35Sstevel static kmutex_t ssm_node2inst_lock; 15203831d35Sstevel static struct ssm_node2inst ssm_node2inst_map = {-1, -1, NULL}; 15303831d35Sstevel 15403831d35Sstevel 15503831d35Sstevel /* 15603831d35Sstevel * Configuration data structures 15703831d35Sstevel */ 15803831d35Sstevel static struct bus_ops ssm_bus_ops = { 15903831d35Sstevel BUSO_REV, 16003831d35Sstevel ddi_bus_map, /* map */ 16103831d35Sstevel 0, /* get_intrspec */ 16203831d35Sstevel 0, /* add_intrspec */ 16303831d35Sstevel 0, /* remove_intrspec */ 16403831d35Sstevel i_ddi_map_fault, /* map_fault */ 16503831d35Sstevel ddi_dma_map, /* dma_map */ 16603831d35Sstevel ddi_dma_allochdl, 16703831d35Sstevel ddi_dma_freehdl, 16803831d35Sstevel ddi_dma_bindhdl, 16903831d35Sstevel ddi_dma_unbindhdl, 17003831d35Sstevel ddi_dma_flush, 17103831d35Sstevel ddi_dma_win, 17203831d35Sstevel ddi_dma_mctl, /* dma_ctl */ 17303831d35Sstevel ssm_ctlops, /* ctl */ 17403831d35Sstevel ddi_bus_prop_op, /* prop_op */ 17503831d35Sstevel ndi_busop_get_eventcookie, 17603831d35Sstevel ndi_busop_add_eventcall, 17703831d35Sstevel ndi_busop_remove_eventcall, 17803831d35Sstevel ndi_post_event, 17903831d35Sstevel 0, 18003831d35Sstevel 0, 18103831d35Sstevel 0, 18203831d35Sstevel ssm_fm_init_child, 18303831d35Sstevel NULL, 18403831d35Sstevel NULL, 18503831d35Sstevel NULL, 18603831d35Sstevel 0, 18703831d35Sstevel i_ddi_intr_ops 18803831d35Sstevel }; 18903831d35Sstevel 19003831d35Sstevel static struct cb_ops ssm_cb_ops = { 19103831d35Sstevel ssm_open, /* open */ 19203831d35Sstevel ssm_close, /* close */ 19303831d35Sstevel nodev, /* strategy */ 19403831d35Sstevel nodev, /* print */ 19503831d35Sstevel nodev, /* dump */ 19603831d35Sstevel nodev, /* read */ 19703831d35Sstevel nodev, /* write */ 19803831d35Sstevel ssm_ioctl, /* ioctl */ 19903831d35Sstevel nodev, /* devmap */ 20003831d35Sstevel nodev, /* mmap */ 20103831d35Sstevel nodev, /* segmap */ 20203831d35Sstevel nochpoll, /* poll */ 20303831d35Sstevel ddi_prop_op, /* cb_prop_op */ 20403831d35Sstevel NULL, /* streamtab */ 20503831d35Sstevel D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 20603831d35Sstevel CB_REV, /* rev */ 20703831d35Sstevel nodev, /* int (*cb_aread)() */ 20803831d35Sstevel nodev /* int (*cb_awrite)() */ 20903831d35Sstevel }; 21003831d35Sstevel 21103831d35Sstevel static struct dev_ops ssm_ops = { 21203831d35Sstevel DEVO_REV, /* devo_rev, */ 21303831d35Sstevel 0, /* refcnt */ 21403831d35Sstevel ssm_info, /* getinfo */ 21503831d35Sstevel nulldev, /* identify */ 21603831d35Sstevel nulldev, /* probe */ 21703831d35Sstevel ssm_attach, /* attach */ 21803831d35Sstevel ssm_detach, /* detach */ 21903831d35Sstevel nulldev, /* reset */ 22003831d35Sstevel &ssm_cb_ops, /* driver operations */ 22103831d35Sstevel &ssm_bus_ops, /* bus_ops */ 22203831d35Sstevel nulldev /* power */ 22303831d35Sstevel }; 22403831d35Sstevel 22503831d35Sstevel /* 22603831d35Sstevel * Driver globals 22703831d35Sstevel */ 22803831d35Sstevel static void *ssm_softstates; /* ssm soft state hook */ 22903831d35Sstevel 23003831d35Sstevel extern struct mod_ops mod_driverops; 23103831d35Sstevel 23203831d35Sstevel static struct modldrv modldrv = { 23303831d35Sstevel &mod_driverops, /* Type of module. This one is a driver */ 23403831d35Sstevel "SSM Nexus v1.8", /* name of module */ 23503831d35Sstevel &ssm_ops, /* driver ops */ 23603831d35Sstevel }; 23703831d35Sstevel 23803831d35Sstevel static struct modlinkage modlinkage = { 23903831d35Sstevel MODREV_1, /* rev */ 24003831d35Sstevel (void *)&modldrv, 24103831d35Sstevel NULL 24203831d35Sstevel }; 24303831d35Sstevel 24403831d35Sstevel static int ssm_loaded_sbd = FALSE; 24503831d35Sstevel kmutex_t ssm_lock; 24603831d35Sstevel static int init_child(dev_info_t *child); 24703831d35Sstevel 24803831d35Sstevel /* 24903831d35Sstevel * These are the module initialization routines. 25003831d35Sstevel */ 25103831d35Sstevel 25203831d35Sstevel int 25303831d35Sstevel _init(void) 25403831d35Sstevel { 25503831d35Sstevel int error; 25603831d35Sstevel 25703831d35Sstevel #if defined(DEBUG) 25803831d35Sstevel debug_print_level = 0x0; 25903831d35Sstevel #endif 26003831d35Sstevel 26103831d35Sstevel /* Initialize soft state pointer. */ 26203831d35Sstevel if ((error = ddi_soft_state_init(&ssm_softstates, 263*447a706eSVijay Balakrishna, SG-RPE sizeof (struct ssm_soft_state), SSM_MAX_INSTANCES)) != 0) 26403831d35Sstevel return (error); 26503831d35Sstevel 26603831d35Sstevel /* Install the module. */ 26703831d35Sstevel error = mod_install(&modlinkage); 26803831d35Sstevel if (error != 0) 26903831d35Sstevel ddi_soft_state_fini(&ssm_softstates); 27003831d35Sstevel 27103831d35Sstevel mutex_init(&ssm_lock, NULL, MUTEX_DRIVER, NULL); 27203831d35Sstevel 27303831d35Sstevel return (error); 27403831d35Sstevel } 27503831d35Sstevel 27603831d35Sstevel int 27703831d35Sstevel _fini(void) 27803831d35Sstevel { 27903831d35Sstevel int error; 28003831d35Sstevel 28103831d35Sstevel /* Remove the module. */ 28203831d35Sstevel if ((error = mod_remove(&modlinkage)) != 0) 28303831d35Sstevel return (error); 28403831d35Sstevel 28503831d35Sstevel /* 28603831d35Sstevel * Unregister the event handler 28703831d35Sstevel */ 28803831d35Sstevel sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC, ssm_dr_event_handler); 28903831d35Sstevel mutex_destroy(&ssm_event_lock); 29003831d35Sstevel 29103831d35Sstevel /* Free the soft state info. */ 29203831d35Sstevel ddi_soft_state_fini(&ssm_softstates); 29303831d35Sstevel mutex_destroy(&ssm_lock); 29403831d35Sstevel 29503831d35Sstevel return (0); 29603831d35Sstevel } 29703831d35Sstevel 29803831d35Sstevel int 29903831d35Sstevel _info(struct modinfo *modinfop) 30003831d35Sstevel { 30103831d35Sstevel return (mod_info(&modlinkage, modinfop)); 30203831d35Sstevel } 30303831d35Sstevel 30403831d35Sstevel /* device driver entry points */ 30503831d35Sstevel 30603831d35Sstevel /* 30703831d35Sstevel * info entry point: 30803831d35Sstevel */ 30903831d35Sstevel 31003831d35Sstevel /* ARGSUSED */ 31103831d35Sstevel static int 31203831d35Sstevel ssm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 31303831d35Sstevel { 31403831d35Sstevel dev_t dev; 31503831d35Sstevel int instance; 31603831d35Sstevel 31703831d35Sstevel if (infocmd == DDI_INFO_DEVT2INSTANCE) { 31803831d35Sstevel dev = (dev_t)arg; 31903831d35Sstevel instance = (getminor(dev) >> SSM_INSTANCE_SHIFT); 32003831d35Sstevel *result = (void *)(uintptr_t)instance; 32103831d35Sstevel return (DDI_SUCCESS); 32203831d35Sstevel } 32303831d35Sstevel return (DDI_FAILURE); 32403831d35Sstevel } 32503831d35Sstevel 32603831d35Sstevel /* 32703831d35Sstevel * attach entry point: 32803831d35Sstevel */ 32903831d35Sstevel 33003831d35Sstevel static int 33103831d35Sstevel ssm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 33203831d35Sstevel { 33303831d35Sstevel int instance; 33403831d35Sstevel struct ssm_soft_state *softsp; 33503831d35Sstevel struct ssm_node2inst *prev, *sp, *tsp; 33603831d35Sstevel 33703831d35Sstevel DPRINTF(SSM_ATTACH_DEBUG, ("ssm_attach\n")); 33803831d35Sstevel 33903831d35Sstevel switch (cmd) { 34003831d35Sstevel case DDI_ATTACH: 34103831d35Sstevel break; 34203831d35Sstevel 34303831d35Sstevel case DDI_RESUME: 34403831d35Sstevel return (DDI_SUCCESS); 34503831d35Sstevel 34603831d35Sstevel default: 34703831d35Sstevel return (DDI_FAILURE); 34803831d35Sstevel } 34903831d35Sstevel 35003831d35Sstevel instance = ddi_get_instance(devi); 35103831d35Sstevel 35203831d35Sstevel if (ddi_soft_state_zalloc(ssm_softstates, instance) != DDI_SUCCESS) 35303831d35Sstevel return (DDI_FAILURE); 35403831d35Sstevel 35503831d35Sstevel softsp = ddi_get_soft_state(ssm_softstates, instance); 35603831d35Sstevel 35703831d35Sstevel /* Set the dip in the soft state */ 35803831d35Sstevel softsp->dip = devi; 35903831d35Sstevel softsp->top_node = devi; 36003831d35Sstevel mutex_init(&softsp->ssm_sft_lock, NULL, MUTEX_DRIVER, NULL); 36103831d35Sstevel 36203831d35Sstevel DPRINTF(SSM_ATTACH_DEBUG, ("ssm-%d: devi= 0x%p, softsp=0x%p\n", 36303831d35Sstevel instance, devi, softsp)); 36403831d35Sstevel 36503831d35Sstevel if ((softsp->ssm_nodeid = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->dip, 36603831d35Sstevel DDI_PROP_DONTPASS, "nodeid", -1)) == -1) { 36703831d35Sstevel cmn_err(CE_WARN, "ssm%d: unable to retrieve %s property", 36803831d35Sstevel instance, "nodeid"); 36903831d35Sstevel ddi_soft_state_free(ssm_softstates, instance); 37003831d35Sstevel return (DDI_FAILURE); 37103831d35Sstevel } 37203831d35Sstevel 37303831d35Sstevel /* nothing to suspend/resume here */ 37403831d35Sstevel (void) ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP, 37503831d35Sstevel "pm-hardware-state", (caddr_t)"no-suspend-resume", 37603831d35Sstevel strlen("no-suspend-resume") + 1); 37703831d35Sstevel 37803831d35Sstevel #if DEBUG 37903831d35Sstevel if (ddi_create_minor_node(devi, "debug", S_IFCHR, instance, 38003831d35Sstevel DDI_NT_NEXUS, 0) != DDI_SUCCESS) { 38103831d35Sstevel ddi_soft_state_free(ssm_softstates, instance); 38203831d35Sstevel return (DDI_FAILURE); 38303831d35Sstevel } 38403831d35Sstevel #endif 38503831d35Sstevel 38603831d35Sstevel if (ssm_make_nodes(devi, instance, softsp->ssm_nodeid)) { 387*447a706eSVijay Balakrishna, SG-RPE cmn_err(CE_WARN, "ssm:%s:%d: failed to make nodes", 388*447a706eSVijay Balakrishna, SG-RPE ddi_driver_name(devi), instance); 38903831d35Sstevel ddi_remove_minor_node(devi, NULL); 39003831d35Sstevel ddi_soft_state_free(ssm_softstates, instance); 39103831d35Sstevel return (DDI_FAILURE); 39203831d35Sstevel } 39303831d35Sstevel ssm_fm_init(softsp); 39403831d35Sstevel ddi_report_dev(devi); 39503831d35Sstevel 39603831d35Sstevel if (event_initialized == 0) { 39703831d35Sstevel int rv; 39803831d35Sstevel /* 39903831d35Sstevel * Register DR event handler 40003831d35Sstevel */ 40103831d35Sstevel mutex_init(&ssm_event_lock, NULL, MUTEX_DRIVER, NULL); 40203831d35Sstevel event_msg.msg_buf = (caddr_t)&payload; 40303831d35Sstevel event_msg.msg_len = sizeof (payload); 40403831d35Sstevel 40503831d35Sstevel rv = sbbc_mbox_reg_intr(MBOX_EVENT_GENERIC, 40603831d35Sstevel ssm_dr_event_handler, &event_msg, 40703831d35Sstevel (uint_t *)&ssm_event_state, &ssm_event_lock); 40803831d35Sstevel 40903831d35Sstevel if (rv == EINVAL) 41003831d35Sstevel event_initialized = 1; 41103831d35Sstevel } 41203831d35Sstevel 41303831d35Sstevel /* 41403831d35Sstevel * Preallocate to avoid sleeping with ssm_node2inst_lock held - 41503831d35Sstevel * low level interrupts use this mutex. 41603831d35Sstevel */ 41703831d35Sstevel tsp = kmem_zalloc(sizeof (struct ssm_node2inst), KM_SLEEP); 41803831d35Sstevel 41903831d35Sstevel mutex_enter(&ssm_node2inst_lock); 42003831d35Sstevel 42103831d35Sstevel for (prev = NULL, sp = &ssm_node2inst_map; sp != NULL; 42203831d35Sstevel prev = sp, sp = sp->next) { 42303831d35Sstevel ASSERT(sp->inst != instance); 42403831d35Sstevel ASSERT(sp->nodeid != softsp->ssm_nodeid); 42503831d35Sstevel if (sp->inst == -1) 42603831d35Sstevel break; 42703831d35Sstevel } 42803831d35Sstevel 42903831d35Sstevel if (sp == NULL) { 43003831d35Sstevel ASSERT(prev->next == NULL); 43103831d35Sstevel sp = prev->next = tsp; 43203831d35Sstevel tsp = NULL; 43303831d35Sstevel sp->next = NULL; 43403831d35Sstevel } 43503831d35Sstevel 43603831d35Sstevel sp->inst = instance; 43703831d35Sstevel sp->nodeid = softsp->ssm_nodeid; 43803831d35Sstevel 43903831d35Sstevel mutex_exit(&ssm_node2inst_lock); 44003831d35Sstevel 44103831d35Sstevel if (tsp != NULL) 44203831d35Sstevel kmem_free(tsp, sizeof (struct ssm_node2inst)); 44303831d35Sstevel 44403831d35Sstevel return (DDI_SUCCESS); 44503831d35Sstevel } 44603831d35Sstevel 44703831d35Sstevel /* 44803831d35Sstevel * detach entry point: 44903831d35Sstevel */ 45003831d35Sstevel /*ARGSUSED*/ 45103831d35Sstevel static int 45203831d35Sstevel ssm_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 45303831d35Sstevel { 45403831d35Sstevel int instance, rv; 45503831d35Sstevel int (*sbd_teardown_instance) (int, caddr_t); 45603831d35Sstevel ssm_sbdp_info_t sbdp_info; 45703831d35Sstevel struct ssm_soft_state *softsp; 45803831d35Sstevel struct ssm_node2inst *prev, *sp; 45903831d35Sstevel 46003831d35Sstevel instance = ddi_get_instance(devi); 46103831d35Sstevel softsp = ddi_get_soft_state(ssm_softstates, instance); 46203831d35Sstevel 46303831d35Sstevel if (softsp == NULL) { 46403831d35Sstevel cmn_err(CE_WARN, 465*447a706eSVijay Balakrishna, SG-RPE "ssm_open bad instance number %d", instance); 46603831d35Sstevel return (ENXIO); 46703831d35Sstevel } 46803831d35Sstevel 46903831d35Sstevel instance = ddi_get_instance(devi); 47003831d35Sstevel 47103831d35Sstevel switch (cmd) { 47203831d35Sstevel case DDI_DETACH: 47303831d35Sstevel ddi_remove_minor_node(devi, NULL); 47403831d35Sstevel 47503831d35Sstevel sbd_teardown_instance = (int (*) (int, caddr_t)) 476*447a706eSVijay Balakrishna, SG-RPE modlookup("misc/sbd", "sbd_teardown_instance"); 47703831d35Sstevel 47803831d35Sstevel if (!sbd_teardown_instance) { 47903831d35Sstevel cmn_err(CE_WARN, "cannot find sbd_teardown_instance"); 48003831d35Sstevel return (DDI_FAILURE); 48103831d35Sstevel } 48203831d35Sstevel 48303831d35Sstevel sbdp_info.instance = instance; 48403831d35Sstevel sbdp_info.wnode = softsp->ssm_nodeid; 48503831d35Sstevel rv = (*sbd_teardown_instance)(instance, (caddr_t)&sbdp_info); 48603831d35Sstevel 48703831d35Sstevel if (rv != DDI_SUCCESS) { 48803831d35Sstevel cmn_err(CE_WARN, "cannot run sbd_teardown_instance"); 48903831d35Sstevel return (DDI_FAILURE); 49003831d35Sstevel } 49103831d35Sstevel ssm_fm_fini(softsp); 49203831d35Sstevel mutex_destroy(&softsp->ssm_sft_lock); 49303831d35Sstevel ddi_soft_state_free(ssm_softstates, instance); 49403831d35Sstevel 49503831d35Sstevel mutex_enter(&ssm_node2inst_lock); 49603831d35Sstevel for (prev = NULL, sp = &ssm_node2inst_map; sp != NULL; 49703831d35Sstevel prev = sp, sp = sp->next) { 49803831d35Sstevel /* Only the head of the list can persist if unused */ 49903831d35Sstevel ASSERT(prev == NULL || sp->inst != -1); 50003831d35Sstevel if (sp->inst == instance) 50103831d35Sstevel break; 50203831d35Sstevel } 50303831d35Sstevel ASSERT(sp != NULL); 50403831d35Sstevel 50503831d35Sstevel if (sp != &ssm_node2inst_map) { 50603831d35Sstevel prev->next = sp->next; 50703831d35Sstevel kmem_free(sp, sizeof (struct ssm_node2inst)); 50803831d35Sstevel } else { 50903831d35Sstevel /* 51003831d35Sstevel * Invalidate the head element, but retain the rest 51103831d35Sstevel * of the list - "next" is still valid. 51203831d35Sstevel */ 51303831d35Sstevel 51403831d35Sstevel sp->nodeid = -1; 51503831d35Sstevel sp->inst = -1; 51603831d35Sstevel } 51703831d35Sstevel mutex_exit(&ssm_node2inst_lock); 51803831d35Sstevel 51903831d35Sstevel return (DDI_SUCCESS); 52003831d35Sstevel 52103831d35Sstevel case DDI_SUSPEND: 52203831d35Sstevel return (DDI_SUCCESS); 52303831d35Sstevel 52403831d35Sstevel default: 52503831d35Sstevel return (DDI_FAILURE); 52603831d35Sstevel } 52703831d35Sstevel } 52803831d35Sstevel 52903831d35Sstevel extern void make_ddi_ppd(dev_info_t *, struct ddi_parent_private_data **); 53003831d35Sstevel extern struct ddi_parent_private_data *init_regspec_64(dev_info_t *); 53103831d35Sstevel 53203831d35Sstevel static int 53303831d35Sstevel name_child(dev_info_t *child, char *name, int namelen) 53403831d35Sstevel { 53503831d35Sstevel struct regspec *rp; 53603831d35Sstevel struct ddi_parent_private_data *pdptr; 53703831d35Sstevel int portid = 0; 53803831d35Sstevel int regbase = -1; 53903831d35Sstevel extern uint_t root_phys_addr_lo_mask; 54003831d35Sstevel 54103831d35Sstevel make_ddi_ppd(child, &pdptr); 54203831d35Sstevel ddi_set_parent_data(child, pdptr); 54303831d35Sstevel 54403831d35Sstevel name[0] = '\0'; 54503831d35Sstevel if (sparc_pd_getnreg(child) == 0) 54603831d35Sstevel return (DDI_SUCCESS); 54703831d35Sstevel 54803831d35Sstevel rp = sparc_pd_getreg(child, 0); 54903831d35Sstevel 55003831d35Sstevel portid = ddi_prop_get_int(DDI_DEV_T_ANY, child, 55103831d35Sstevel DDI_PROP_DONTPASS, "portid", -1); 55203831d35Sstevel if (portid == -1) { 55303831d35Sstevel cmn_err(CE_WARN, "could not find portid property in %s", 55403831d35Sstevel DEVI(child)->devi_node_name); 55503831d35Sstevel } else { 55603831d35Sstevel regbase = rp->regspec_addr & root_phys_addr_lo_mask; 55703831d35Sstevel } 55803831d35Sstevel (void) snprintf(name, namelen, "%x,%x", portid, regbase); 55903831d35Sstevel return (DDI_SUCCESS); 56003831d35Sstevel } 56103831d35Sstevel 56203831d35Sstevel static int 56303831d35Sstevel init_child(dev_info_t *child) 56403831d35Sstevel { 56503831d35Sstevel char name[MAXNAMELEN]; 56603831d35Sstevel 56703831d35Sstevel (void) name_child(child, name, MAXNAMELEN); 56803831d35Sstevel ddi_set_name_addr(child, name); 56903831d35Sstevel if ((ndi_dev_is_persistent_node(child) == 0) && 57003831d35Sstevel (ndi_merge_node(child, name_child) == DDI_SUCCESS)) { 57103831d35Sstevel impl_ddi_sunbus_removechild(child); 57203831d35Sstevel return (DDI_FAILURE); 57303831d35Sstevel } 57403831d35Sstevel 57503831d35Sstevel (void) init_regspec_64(child); 57603831d35Sstevel return (DDI_SUCCESS); 57703831d35Sstevel } 57803831d35Sstevel 57903831d35Sstevel /* 58003831d35Sstevel * Control ops entry point: 58103831d35Sstevel * 58203831d35Sstevel * Requests handled completely: 58303831d35Sstevel * DDI_CTLOPS_INITCHILD 58403831d35Sstevel * DDI_CTLOPS_UNINITCHILD 58503831d35Sstevel * DDI_CTLOPS_REPORTDEV 58603831d35Sstevel * All others are passed to the parent. 58703831d35Sstevel * The name of the ssm node is ssm@nodeid,0. 58803831d35Sstevel * ssm is the equivalent of rootnex. 58903831d35Sstevel */ 59003831d35Sstevel static int 59103831d35Sstevel ssm_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, void *arg, 59203831d35Sstevel void *result) 59303831d35Sstevel { 59403831d35Sstevel int rval; 59503831d35Sstevel 59603831d35Sstevel switch (op) { 59703831d35Sstevel case DDI_CTLOPS_INITCHILD: { 59803831d35Sstevel DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_INITCHILD\n")); 59903831d35Sstevel return (init_child((dev_info_t *)arg)); 60003831d35Sstevel } 60103831d35Sstevel 60203831d35Sstevel case DDI_CTLOPS_UNINITCHILD: { 60303831d35Sstevel DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_UNINITCHILD\n")); 60403831d35Sstevel impl_ddi_sunbus_removechild((dev_info_t *)arg); 60503831d35Sstevel return (DDI_SUCCESS); 60603831d35Sstevel } 60703831d35Sstevel 60803831d35Sstevel case DDI_CTLOPS_REPORTDEV: { 60903831d35Sstevel char buf[80]; 61003831d35Sstevel char *p = buf; 61103831d35Sstevel dev_info_t *parent; 61203831d35Sstevel int portid; 61303831d35Sstevel 61403831d35Sstevel DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_REPORTDEV\n")); 61503831d35Sstevel parent = ddi_get_parent(rdip); 61603831d35Sstevel 61703831d35Sstevel (void) sprintf(p, "%s%d at %s%d", DEVI(rdip)->devi_name, 61803831d35Sstevel DEVI(rdip)->devi_instance, ddi_get_name(parent), 61903831d35Sstevel ddi_get_instance(parent)); 62003831d35Sstevel p += strlen(p); 62103831d35Sstevel 62203831d35Sstevel /* Fetch Safari Extended Agent ID of this device. */ 62303831d35Sstevel portid = (int)ddi_getprop(DDI_DEV_T_ANY, rdip, 62403831d35Sstevel DDI_PROP_DONTPASS, "portid", -1); 62503831d35Sstevel 62603831d35Sstevel /* 62703831d35Sstevel * If this is one of the ssm children it will have 62803831d35Sstevel * portid property and its parent will be ssm. 62903831d35Sstevel * In this case report Node number and Safari id. 63003831d35Sstevel */ 63103831d35Sstevel if (portid != -1 && 63203831d35Sstevel strcmp("ssm", ddi_get_name(parent)) == 0) { 63303831d35Sstevel struct regspec *rp; 63403831d35Sstevel int node; 63503831d35Sstevel int safid; 63603831d35Sstevel int n; 63703831d35Sstevel 63803831d35Sstevel rp = sparc_pd_getreg(rdip, 0); 63903831d35Sstevel n = sparc_pd_getnreg(rdip); 64003831d35Sstevel ASSERT(n > 0); 64103831d35Sstevel 64203831d35Sstevel node = SG_PORTID_TO_NODEID(portid); 64303831d35Sstevel safid = SG_PORTID_TO_SAFARI_ID(portid); 64403831d35Sstevel 64503831d35Sstevel (void) strcpy(p, ": "); 64603831d35Sstevel p += strlen(p); 64703831d35Sstevel 64803831d35Sstevel (void) sprintf(p, "Node %d Safari id %d 0x%x%s", 64903831d35Sstevel node, safid, 65003831d35Sstevel rp->regspec_addr, 65103831d35Sstevel (n > 1 ? "" : " ...")); 65203831d35Sstevel p += strlen(p); 65303831d35Sstevel } 65403831d35Sstevel 65503831d35Sstevel cmn_err(CE_CONT, "?%s\n", buf); 65603831d35Sstevel rval = DDI_SUCCESS; 65703831d35Sstevel 65803831d35Sstevel break; 65903831d35Sstevel } 66003831d35Sstevel 66103831d35Sstevel default: 66203831d35Sstevel rval = ddi_ctlops(dip, rdip, op, arg, result); 66303831d35Sstevel 66403831d35Sstevel break; 66503831d35Sstevel } 66603831d35Sstevel 66703831d35Sstevel return (rval); 66803831d35Sstevel } 66903831d35Sstevel 67003831d35Sstevel /*ARGSUSED*/ 67103831d35Sstevel static int 67203831d35Sstevel ssm_make_nodes(dev_info_t *dip, int instance, int ssm_nodeid) 67303831d35Sstevel { 67403831d35Sstevel int rv; 67503831d35Sstevel minor_t minor_num, bd; 67603831d35Sstevel auto char filename[20]; 67703831d35Sstevel 67803831d35Sstevel for (bd = 0; bd < plat_max_boards(); bd++) { 67903831d35Sstevel if (SG_BOARD_IS_CPU_TYPE(bd)) 68003831d35Sstevel (void) sprintf(filename, "N%d.SB%d", ssm_nodeid, bd); 68103831d35Sstevel else 68203831d35Sstevel (void) sprintf(filename, "N%d.IB%d", ssm_nodeid, bd); 68303831d35Sstevel 68403831d35Sstevel minor_num = (instance << SSM_INSTANCE_SHIFT) | bd; 68503831d35Sstevel 68603831d35Sstevel rv = ddi_create_minor_node(dip, filename, S_IFCHR, 687*447a706eSVijay Balakrishna, SG-RPE minor_num, DDI_NT_SBD_ATTACHMENT_POINT, NULL); 68803831d35Sstevel if (rv == DDI_FAILURE) { 68903831d35Sstevel cmn_err(CE_WARN, 690*447a706eSVijay Balakrishna, SG-RPE "ssm_make_nodes:%d: failed to create " 691*447a706eSVijay Balakrishna, SG-RPE "minor node (%s, 0x%x)", 692*447a706eSVijay Balakrishna, SG-RPE instance, filename, minor_num); 69303831d35Sstevel return (-1); 69403831d35Sstevel } 69503831d35Sstevel } 69603831d35Sstevel 69703831d35Sstevel return (0); 69803831d35Sstevel } 69903831d35Sstevel 70003831d35Sstevel 70103831d35Sstevel /* ARGSUSED */ 70203831d35Sstevel static int 70303831d35Sstevel ssm_open(dev_t *devi, int flags, int otyp, cred_t *credp) 70403831d35Sstevel { 70503831d35Sstevel struct ssm_soft_state *softsp; 70603831d35Sstevel minor_t board, instance; 70703831d35Sstevel int (*sbd_setup_instance)(int, dev_info_t *, int, int, caddr_t); 70803831d35Sstevel ssm_sbdp_info_t sbdp_info; 70903831d35Sstevel int rv; 71003831d35Sstevel 71103831d35Sstevel instance = (getminor(*devi) >> SSM_INSTANCE_SHIFT); 71203831d35Sstevel 71303831d35Sstevel softsp = ddi_get_soft_state(ssm_softstates, instance); 71403831d35Sstevel if (softsp == NULL) { 71503831d35Sstevel cmn_err(CE_WARN, "ssm_open bad instance number %d", instance); 71603831d35Sstevel return (ENXIO); 71703831d35Sstevel } 71803831d35Sstevel 71903831d35Sstevel board = (getminor(*devi) & SSM_BOARD_MASK); 72003831d35Sstevel 72103831d35Sstevel if (board < 0 || board > plat_max_boards()) { 72203831d35Sstevel return (ENXIO); 72303831d35Sstevel } 72403831d35Sstevel 72503831d35Sstevel mutex_enter(&ssm_lock); 72603831d35Sstevel if (instance == 0 && ssm_loaded_sbd == FALSE) { 72703831d35Sstevel 72803831d35Sstevel if (modload("misc", "sbd") == -1) { 72903831d35Sstevel cmn_err(CE_WARN, "ssm_open: cannot load sbd"); 73003831d35Sstevel mutex_exit(&ssm_lock); 73103831d35Sstevel return (EIO); 73203831d35Sstevel } 73303831d35Sstevel ssm_loaded_sbd = TRUE; 73403831d35Sstevel } 73503831d35Sstevel mutex_exit(&ssm_lock); 73603831d35Sstevel 73703831d35Sstevel mutex_enter(&softsp->ssm_sft_lock); 73803831d35Sstevel if (softsp->initialized == FALSE) { 73903831d35Sstevel 74003831d35Sstevel if (softsp->top_node == NULL) { 74103831d35Sstevel cmn_err(CE_WARN, "cannot find ssm top dnode"); 74203831d35Sstevel mutex_exit(&softsp->ssm_sft_lock); 74303831d35Sstevel return (EIO); 74403831d35Sstevel } 74503831d35Sstevel 74603831d35Sstevel sbd_setup_instance = (int (*)(int, dev_info_t *, int, int, 747*447a706eSVijay Balakrishna, SG-RPE caddr_t))modlookup("misc/sbd", "sbd_setup_instance"); 74803831d35Sstevel 74903831d35Sstevel if (!sbd_setup_instance) { 75003831d35Sstevel cmn_err(CE_WARN, "cannot find sbd_setup_instance"); 75103831d35Sstevel mutex_exit(&softsp->ssm_sft_lock); 75203831d35Sstevel return (EIO); 75303831d35Sstevel } 75403831d35Sstevel 75503831d35Sstevel sbdp_info.instance = instance; 75603831d35Sstevel sbdp_info.wnode = softsp->ssm_nodeid; 75703831d35Sstevel 75803831d35Sstevel rv = (*sbd_setup_instance)(instance, softsp->top_node, 759*447a706eSVijay Balakrishna, SG-RPE plat_max_boards(), softsp->ssm_nodeid, 760*447a706eSVijay Balakrishna, SG-RPE (caddr_t)&sbdp_info); 76103831d35Sstevel if (rv != DDI_SUCCESS) { 76203831d35Sstevel cmn_err(CE_WARN, "cannot run sbd_setup_instance"); 76303831d35Sstevel mutex_exit(&softsp->ssm_sft_lock); 76403831d35Sstevel return (EIO); 76503831d35Sstevel } 76603831d35Sstevel softsp->initialized = TRUE; 76703831d35Sstevel } 76803831d35Sstevel mutex_exit(&softsp->ssm_sft_lock); 76903831d35Sstevel 77003831d35Sstevel return (DDI_SUCCESS); 77103831d35Sstevel } 77203831d35Sstevel 77303831d35Sstevel 77403831d35Sstevel /* ARGSUSED */ 77503831d35Sstevel static int 77603831d35Sstevel ssm_close(dev_t dev, int flags, int otyp, cred_t *credp) 77703831d35Sstevel { 77803831d35Sstevel struct ssm_soft_state *softsp; 77903831d35Sstevel minor_t board, instance; 78003831d35Sstevel 78103831d35Sstevel instance = (getminor(dev) >> SSM_INSTANCE_SHIFT); 78203831d35Sstevel 78303831d35Sstevel softsp = ddi_get_soft_state(ssm_softstates, instance); 78403831d35Sstevel if (softsp == NULL) 78503831d35Sstevel return (ENXIO); 78603831d35Sstevel 78703831d35Sstevel board = (getminor(dev) & SSM_BOARD_MASK); 78803831d35Sstevel 78903831d35Sstevel if (board < 0 || board > plat_max_boards()) 79003831d35Sstevel return (ENXIO); 79103831d35Sstevel 79203831d35Sstevel return (DDI_SUCCESS); 79303831d35Sstevel } 79403831d35Sstevel 79503831d35Sstevel /* ARGSUSED */ 79603831d35Sstevel static int 79703831d35Sstevel ssm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 79803831d35Sstevel int *rvalp) 79903831d35Sstevel { 80003831d35Sstevel struct ssm_soft_state *softsp; 80103831d35Sstevel char *addr; 80203831d35Sstevel struct devctl_iocdata *dcp; 80303831d35Sstevel int instance, rv = 0; 80403831d35Sstevel int (*sbd_ioctl) (dev_t, int, intptr_t, int, char *); 80503831d35Sstevel 80603831d35Sstevel instance = (getminor(dev) >> SSM_INSTANCE_SHIFT); 80703831d35Sstevel softsp = ddi_get_soft_state(ssm_softstates, instance); 80803831d35Sstevel if (softsp == NULL) 80903831d35Sstevel return (ENXIO); 81003831d35Sstevel 81103831d35Sstevel switch (cmd) { 81203831d35Sstevel 81303831d35Sstevel case DEVCTL_BUS_CONFIGURE: 81403831d35Sstevel /* 81503831d35Sstevel * read devctl ioctl data 81603831d35Sstevel */ 81703831d35Sstevel if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) 81803831d35Sstevel return (EFAULT); 81903831d35Sstevel 82003831d35Sstevel addr = ndi_dc_getaddr(dcp); 82103831d35Sstevel cmn_err(CE_NOTE, 82203831d35Sstevel "DEVCTL_BUS_CONFIGURE: device id is %s\n", addr); 82303831d35Sstevel ndi_dc_freehdl(dcp); 82403831d35Sstevel break; 82503831d35Sstevel 82603831d35Sstevel case DEVCTL_BUS_UNCONFIGURE: 82703831d35Sstevel if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) 82803831d35Sstevel return (EFAULT); 82903831d35Sstevel 83003831d35Sstevel addr = ndi_dc_getaddr(dcp); 83103831d35Sstevel cmn_err(CE_NOTE, 83203831d35Sstevel "DEVCTL_BUS_UNCONFIGURE: device id is %s\n", addr); 83303831d35Sstevel ndi_dc_freehdl(dcp); 83403831d35Sstevel break; 83503831d35Sstevel 83603831d35Sstevel #ifdef DEBUG 83703831d35Sstevel case SSM_TEARDOWN_SBD: { 83803831d35Sstevel ssm_sbdp_info_t sbdp_info; 83903831d35Sstevel int (*sbd_teardown_instance) (int, caddr_t); 84003831d35Sstevel sbd_teardown_instance = (int (*) (int, caddr_t)) 841*447a706eSVijay Balakrishna, SG-RPE modlookup("misc/sbd", "sbd_teardown_instance"); 84203831d35Sstevel 84303831d35Sstevel if (!sbd_teardown_instance) { 84403831d35Sstevel cmn_err(CE_WARN, "cannot find sbd_teardown_instance"); 84503831d35Sstevel return (EFAULT); 84603831d35Sstevel } 84703831d35Sstevel 84803831d35Sstevel sbdp_info.instance = instance; 84903831d35Sstevel sbdp_info.wnode = softsp->ssm_nodeid; 85003831d35Sstevel rv = (*sbd_teardown_instance)(instance, (caddr_t)&sbdp_info); 85103831d35Sstevel if (rv != DDI_SUCCESS) { 85203831d35Sstevel cmn_err(CE_WARN, "cannot run sbd_teardown_instance"); 85303831d35Sstevel return (EFAULT); 85403831d35Sstevel } 85503831d35Sstevel 85603831d35Sstevel ssm_loaded_sbd = FALSE; 85703831d35Sstevel softsp->initialized = FALSE; 85803831d35Sstevel } 85903831d35Sstevel #endif 86003831d35Sstevel 86103831d35Sstevel 86203831d35Sstevel default: { 86303831d35Sstevel char event = 0; 86403831d35Sstevel 865*447a706eSVijay Balakrishna, SG-RPE sbd_ioctl = (int (*) (dev_t, int, intptr_t, int, char *)) 866*447a706eSVijay Balakrishna, SG-RPE modlookup("misc/sbd", "sbd_ioctl"); 86703831d35Sstevel 86803831d35Sstevel if (sbd_ioctl) 86903831d35Sstevel rv = (*sbd_ioctl) (dev, cmd, arg, mode, &event); 87003831d35Sstevel else { 87103831d35Sstevel cmn_err(CE_WARN, "cannot find sbd_ioctl"); 87203831d35Sstevel return (ENXIO); 87303831d35Sstevel } 87403831d35Sstevel /* 87503831d35Sstevel * Check to see if we need to send an event 87603831d35Sstevel */ 87703831d35Sstevel if (event == 1) { 87803831d35Sstevel int slot; 87903831d35Sstevel int hint = SE_NO_HINT; 88003831d35Sstevel 88103831d35Sstevel if (rv == 0) { 88203831d35Sstevel if (cmd == SBD_CMD_CONNECT || 88303831d35Sstevel cmd == SBD_CMD_CONFIGURE) 88403831d35Sstevel hint = SE_HINT_INSERT; 88503831d35Sstevel else if (cmd == SBD_CMD_UNCONFIGURE || 88603831d35Sstevel cmd == SBD_CMD_DISCONNECT) 88703831d35Sstevel hint = SE_HINT_REMOVE; 88803831d35Sstevel } 88903831d35Sstevel 89003831d35Sstevel slot = (getminor(dev) & SSM_BOARD_MASK); 89103831d35Sstevel ssm_generate_event(softsp->ssm_nodeid, slot, hint); 89203831d35Sstevel } 89303831d35Sstevel break; 89403831d35Sstevel } 89503831d35Sstevel } 89603831d35Sstevel 89703831d35Sstevel return (rv); 89803831d35Sstevel } 89903831d35Sstevel 90003831d35Sstevel void 90103831d35Sstevel ssm_get_attch_pnt(int node, int board, char *attach_pnt) 90203831d35Sstevel { 90303831d35Sstevel struct ssm_node2inst *sp; 90403831d35Sstevel 90503831d35Sstevel /* 90603831d35Sstevel * Hold this mutex, until we are done so that ssm dip 90703831d35Sstevel * doesn't detach. 90803831d35Sstevel */ 90903831d35Sstevel mutex_enter(&ssm_node2inst_lock); 91003831d35Sstevel 91103831d35Sstevel for (sp = &ssm_node2inst_map; sp != NULL; sp = sp->next) { 91203831d35Sstevel if (sp->inst == -1) 91303831d35Sstevel continue; 91403831d35Sstevel if (sp->nodeid == node) 91503831d35Sstevel break; 91603831d35Sstevel } 91703831d35Sstevel 91803831d35Sstevel if (sp == NULL) { 91903831d35Sstevel /* We didn't find the ssm dip, return failure */ 92003831d35Sstevel attach_pnt[0] = '\0'; 92103831d35Sstevel mutex_exit(&ssm_node2inst_lock); 92203831d35Sstevel return; 92303831d35Sstevel } 92403831d35Sstevel 92503831d35Sstevel /* 92603831d35Sstevel * we have the instance, and the board, construct the attch pnt 92703831d35Sstevel */ 92803831d35Sstevel if (SG_BOARD_IS_CPU_TYPE(board)) 92903831d35Sstevel (void) sprintf(attach_pnt, "ssm%d:N%d.SB%d", 93003831d35Sstevel sp->inst, node, board); 93103831d35Sstevel else 93203831d35Sstevel (void) sprintf(attach_pnt, "ssm%d:N%d.IB%d", 93303831d35Sstevel sp->inst, node, board); 93403831d35Sstevel 93503831d35Sstevel mutex_exit(&ssm_node2inst_lock); 93603831d35Sstevel } 93703831d35Sstevel 93803831d35Sstevel /* 93903831d35Sstevel * Generate an event to sysevent 94003831d35Sstevel */ 94103831d35Sstevel static int 94203831d35Sstevel ssm_generate_event(int node, int board, int hint) 94303831d35Sstevel { 94403831d35Sstevel sysevent_t *ev; 94503831d35Sstevel sysevent_id_t eid; 94603831d35Sstevel int rv = 0; 94703831d35Sstevel sysevent_value_t evnt_val; 94803831d35Sstevel sysevent_attr_list_t *evnt_attr_list = NULL; 94903831d35Sstevel char attach_pnt[MAXPATHLEN]; 95003831d35Sstevel 95103831d35Sstevel 95203831d35Sstevel attach_pnt[0] = '\0'; 95303831d35Sstevel ssm_get_attch_pnt(node, board, attach_pnt); 95403831d35Sstevel 95503831d35Sstevel if (attach_pnt[0] == '\0') 95603831d35Sstevel return (-1); 95703831d35Sstevel 95803831d35Sstevel ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, EP_DDI, 95903831d35Sstevel KM_SLEEP); 96003831d35Sstevel evnt_val.value_type = SE_DATA_TYPE_STRING; 96103831d35Sstevel evnt_val.value.sv_string = attach_pnt; 96203831d35Sstevel 96303831d35Sstevel rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val, KM_SLEEP); 96403831d35Sstevel if (rv != 0) { 96503831d35Sstevel cmn_err(CE_WARN, "Failed to add attr [%s] for %s event", 96603831d35Sstevel DR_AP_ID, EC_DR); 96703831d35Sstevel sysevent_free(ev); 96803831d35Sstevel return (rv); 96903831d35Sstevel } 97003831d35Sstevel 97103831d35Sstevel /* 97203831d35Sstevel * Add the hint 97303831d35Sstevel */ 97403831d35Sstevel evnt_val.value_type = SE_DATA_TYPE_STRING; 97503831d35Sstevel evnt_val.value.sv_string = SE_HINT2STR(hint); 97603831d35Sstevel 97703831d35Sstevel rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val, KM_SLEEP); 97803831d35Sstevel if (rv != 0) { 97903831d35Sstevel cmn_err(CE_WARN, "Failed to add attr [%s] for %s event", 98003831d35Sstevel DR_HINT, EC_DR); 98103831d35Sstevel sysevent_free_attr(evnt_attr_list); 98203831d35Sstevel sysevent_free(ev); 98303831d35Sstevel return (-1); 98403831d35Sstevel } 98503831d35Sstevel 98603831d35Sstevel if (sysevent_attach_attributes(ev, evnt_attr_list) != 0) { 98703831d35Sstevel cmn_err(CE_WARN, "Failed to attach attr list for %s event", 98803831d35Sstevel EC_DR); 98903831d35Sstevel sysevent_free_attr(evnt_attr_list); 99003831d35Sstevel sysevent_free(ev); 99103831d35Sstevel return (-1); 99203831d35Sstevel } 99303831d35Sstevel 99403831d35Sstevel rv = log_sysevent(ev, KM_NOSLEEP, &eid); 99503831d35Sstevel if (rv != 0) { 99603831d35Sstevel cmn_err(CE_WARN, "ssm_dr_event_handler: failed to log event"); 99703831d35Sstevel } 99803831d35Sstevel 99903831d35Sstevel sysevent_free(ev); 100003831d35Sstevel 100103831d35Sstevel return (rv); 100203831d35Sstevel } 100303831d35Sstevel 100403831d35Sstevel /* 100503831d35Sstevel * DR Event Handler 100603831d35Sstevel */ 100703831d35Sstevel uint_t 100803831d35Sstevel ssm_dr_event_handler(char *arg) 100903831d35Sstevel { 101003831d35Sstevel sg_system_fru_descriptor_t *fdp; 101103831d35Sstevel int hint; 101203831d35Sstevel 101303831d35Sstevel 101403831d35Sstevel fdp = (sg_system_fru_descriptor_t *)(((sbbc_msg_t *)arg)->msg_buf); 101503831d35Sstevel if (fdp == NULL) { 101603831d35Sstevel DPRINTF(SSM_EVENT_DEBUG, 101703831d35Sstevel ("ssm_dr_event_handler: ARG is null\n")); 101803831d35Sstevel return (DDI_INTR_CLAIMED); 101903831d35Sstevel } 102003831d35Sstevel #ifdef DEBUG 102103831d35Sstevel DPRINTF(SSM_EVENT_DEBUG, ("ssm_dr_event_handler called\n")); 102203831d35Sstevel DPRINTF(SSM_EVENT_DEBUG, ("\tnode\t%d\n", fdp->node)); 102303831d35Sstevel DPRINTF(SSM_EVENT_DEBUG, ("\tslot\t%d\n", fdp->slot)); 102403831d35Sstevel DPRINTF(SSM_EVENT_DEBUG, ("\tparent_hdl\t0x%lx\n", fdp->parent_hdl)); 102503831d35Sstevel DPRINTF(SSM_EVENT_DEBUG, ("\tchild_hdl\t0x%lx\n", fdp->child_hdl)); 102603831d35Sstevel DPRINTF(SSM_EVENT_DEBUG, ("\tevent_details\t%s\n", 102703831d35Sstevel EVNT2STR(fdp->event_details))); 102803831d35Sstevel #endif 102903831d35Sstevel 103003831d35Sstevel switch (fdp->event_details) { 103103831d35Sstevel case SG_EVT_BOARD_ABSENT: 103203831d35Sstevel hint = SE_HINT_REMOVE; 103303831d35Sstevel break; 103403831d35Sstevel case SG_EVT_BOARD_PRESENT: 103503831d35Sstevel hint = SE_HINT_INSERT; 103603831d35Sstevel break; 103703831d35Sstevel default: 103803831d35Sstevel hint = SE_NO_HINT; 103903831d35Sstevel break; 104003831d35Sstevel 104103831d35Sstevel } 104203831d35Sstevel 104303831d35Sstevel ssm_generate_event(fdp->node, fdp->slot, hint); 104403831d35Sstevel 104503831d35Sstevel return (DDI_INTR_CLAIMED); 104603831d35Sstevel } 104703831d35Sstevel 104803831d35Sstevel /* 104903831d35Sstevel * Initialize our FMA resources 105003831d35Sstevel */ 105103831d35Sstevel static void 105203831d35Sstevel ssm_fm_init(struct ssm_soft_state *softsp) 105303831d35Sstevel { 105403831d35Sstevel softsp->ssm_fm_cap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE | 105503831d35Sstevel DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE; 105603831d35Sstevel 105703831d35Sstevel /* 105803831d35Sstevel * Request or capability level and get our parents capability 105903831d35Sstevel * and ibc. 106003831d35Sstevel */ 106103831d35Sstevel ddi_fm_init(softsp->dip, &softsp->ssm_fm_cap, &softsp->ssm_fm_ibc); 106203831d35Sstevel ASSERT((softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE) && 106303831d35Sstevel (softsp->ssm_fm_cap & DDI_FM_ERRCB_CAPABLE)); 106403831d35Sstevel /* 106503831d35Sstevel * Register error callback with our parent. 106603831d35Sstevel */ 106703831d35Sstevel ddi_fm_handler_register(softsp->dip, ssm_err_callback, NULL); 106803831d35Sstevel } 106903831d35Sstevel 107003831d35Sstevel /* 107103831d35Sstevel * Breakdown our FMA resources 107203831d35Sstevel */ 107303831d35Sstevel static void 107403831d35Sstevel ssm_fm_fini(struct ssm_soft_state *softsp) 107503831d35Sstevel { 107603831d35Sstevel /* 107703831d35Sstevel * Clean up allocated fm structures 107803831d35Sstevel */ 107903831d35Sstevel ASSERT(softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE); 108003831d35Sstevel ddi_fm_handler_unregister(softsp->dip); 108103831d35Sstevel ddi_fm_fini(softsp->dip); 108203831d35Sstevel } 108303831d35Sstevel 108403831d35Sstevel /* 108503831d35Sstevel * Initialize FMA resources for children devices. Called when 108603831d35Sstevel * child calls ddi_fm_init(). 108703831d35Sstevel */ 108803831d35Sstevel /*ARGSUSED*/ 108903831d35Sstevel static int 109003831d35Sstevel ssm_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap, 109103831d35Sstevel ddi_iblock_cookie_t *ibc) 109203831d35Sstevel { 109303831d35Sstevel struct ssm_soft_state *softsp = ddi_get_soft_state(ssm_softstates, 1094*447a706eSVijay Balakrishna, SG-RPE ddi_get_instance(dip)); 109503831d35Sstevel 109603831d35Sstevel *ibc = softsp->ssm_fm_ibc; 109703831d35Sstevel return (softsp->ssm_fm_cap); 109803831d35Sstevel } 109903831d35Sstevel 110003831d35Sstevel /* 110103831d35Sstevel * FMA registered error callback 110203831d35Sstevel */ 110303831d35Sstevel /*ARGSUSED*/ 110403831d35Sstevel static int 110503831d35Sstevel ssm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data) 110603831d35Sstevel { 110703831d35Sstevel /* Call our children error handlers */ 110803831d35Sstevel return (ndi_fm_handler_dispatch(dip, NULL, derr)); 110903831d35Sstevel } 1110