xref: /onnv-gate/usr/src/uts/sun4u/serengeti/io/ssm.c (revision 11311:639e7bc0b42f)
11708Sstevel /*
21708Sstevel  * CDDL HEADER START
31708Sstevel  *
41708Sstevel  * The contents of this file are subject to the terms of the
51708Sstevel  * Common Development and Distribution License (the "License").
61708Sstevel  * You may not use this file except in compliance with the License.
71708Sstevel  *
81708Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel  * or http://www.opensolaris.org/os/licensing.
101708Sstevel  * See the License for the specific language governing permissions
111708Sstevel  * and limitations under the License.
121708Sstevel  *
131708Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel  * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel  *
191708Sstevel  * CDDL HEADER END
201708Sstevel  */
211708Sstevel 
221708Sstevel /*
23*11311SSurya.Prakki@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241708Sstevel  * Use is subject to license terms.
251708Sstevel  */
261708Sstevel #include <sys/types.h>
271708Sstevel #include <sys/conf.h>
281708Sstevel #include <sys/ddi.h>
291708Sstevel #include <sys/sunddi.h>
301708Sstevel #include <sys/modctl.h>
311708Sstevel #include <sys/sunndi.h>
321708Sstevel #include <sys/ddi_impldefs.h>
331708Sstevel #include <sys/obpdefs.h>
341708Sstevel #include <sys/cmn_err.h>
351708Sstevel #include <sys/errno.h>
361708Sstevel #include <sys/kmem.h>
371708Sstevel #include <sys/debug.h>
381708Sstevel #include <sys/sysmacros.h>
391708Sstevel #include <sys/autoconf.h>
401708Sstevel #include <sys/stat.h>
411708Sstevel #include <sys/serengeti.h>
421708Sstevel #include <sys/ssm.h>
431708Sstevel #include <sys/sgsbbc_mailbox.h>
441708Sstevel #include <sys/sgevents.h>
451708Sstevel #include <sys/sysevent.h>
461708Sstevel #include <sys/sysevent/dr.h>
471708Sstevel #include <sys/sysevent/eventdefs.h>
481708Sstevel #include <sys/ndi_impldefs.h>
491708Sstevel #include <sys/ddifm.h>
501708Sstevel #include <sys/ndifm.h>
511708Sstevel #include <sys/sbd_ioctl.h>
521708Sstevel 
531708Sstevel /* Useful debugging Stuff */
541708Sstevel #include <sys/nexusdebug.h>
551708Sstevel 
561708Sstevel /*
571708Sstevel  * module ssm.c
581708Sstevel  *
591708Sstevel  * This module is a nexus driver designed to support the ssm nexus driver
601708Sstevel  * and all children below it. This driver does not handle any of the
611708Sstevel  * DDI functions passed up to it by the ssm driver, but instead allows
621708Sstevel  * them to bubble up to the root node.
631708Sstevel  */
641708Sstevel 
651708Sstevel 
661708Sstevel /*
671708Sstevel  * Function prototypes
681708Sstevel  */
691708Sstevel extern int plat_max_boards();
701708Sstevel 
711708Sstevel static int
721708Sstevel ssm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result);
731708Sstevel 
741708Sstevel static int
751708Sstevel ssm_attach(dev_info_t *, ddi_attach_cmd_t);
761708Sstevel 
771708Sstevel static int
781708Sstevel ssm_detach(dev_info_t *, ddi_detach_cmd_t);
791708Sstevel 
801708Sstevel static int
811708Sstevel ssm_open(dev_t *, int, int, cred_t *);
821708Sstevel 
831708Sstevel static int
841708Sstevel ssm_close(dev_t, int, int, cred_t *);
851708Sstevel 
861708Sstevel static int
871708Sstevel ssm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
881708Sstevel 
891708Sstevel static int
901708Sstevel ssm_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *);
911708Sstevel 
921708Sstevel static int
931708Sstevel ssm_make_nodes(dev_info_t *dip, int instance, int ssm_nodeid);
941708Sstevel 
951708Sstevel static int
961708Sstevel ssm_generate_event(int node, int board, int hint);
971708Sstevel 
981708Sstevel /*
991708Sstevel  * FMA error callback
1001708Sstevel  * Register error handling callback with our parent. We will just call
1011708Sstevel  * our children's error callbacks and return their status.
1021708Sstevel  */
1031708Sstevel static int
1041708Sstevel ssm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data);
1051708Sstevel 
1061708Sstevel /*
1071708Sstevel  * fm_init busop to initialize our children
1081708Sstevel  */
1091708Sstevel static int
1101708Sstevel ssm_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap,
1111708Sstevel 		ddi_iblock_cookie_t *ibc);
1121708Sstevel 
1131708Sstevel /*
1141708Sstevel  * init/fini routines to alloc/dealloc fm structures and
1151708Sstevel  * register/unregister our callback.
1161708Sstevel  */
1171708Sstevel static void
1181708Sstevel ssm_fm_init(struct ssm_soft_state *softsp);
1191708Sstevel 
1201708Sstevel static void
1211708Sstevel ssm_fm_fini(struct ssm_soft_state *softsp);
1221708Sstevel 
1231708Sstevel /*
1241708Sstevel  * DR event handlers
1251708Sstevel  * We want to register the event handlers once for all instances. In the
1261708Sstevel  * other hand we have register them after the sbbc has been attached.
1271708Sstevel  * event_initialize gives us the logic of only registering the events only
1281708Sstevel  * once
1291708Sstevel  */
1301708Sstevel int event_initialized = 0;
1311708Sstevel uint_t ssm_dr_event_handler(char *);
1321708Sstevel 
1331708Sstevel /*
1341708Sstevel  * Event lock and state
1351708Sstevel  */
1361708Sstevel static kmutex_t ssm_event_lock;
1371708Sstevel int ssm_event_state;
1381708Sstevel 
1391708Sstevel /*
1401708Sstevel  * DR event msg and payload
1411708Sstevel  */
1421708Sstevel static sbbc_msg_t event_msg;
1431708Sstevel static sg_system_fru_descriptor_t payload;
1441708Sstevel 
1451708Sstevel struct ssm_node2inst {
1461708Sstevel 	int	nodeid;		/* serengeti node #, NOT prom nodeid */
1471708Sstevel 	int	inst;
1481708Sstevel 	struct ssm_node2inst *next;
1491708Sstevel };
1501708Sstevel static kmutex_t ssm_node2inst_lock;
1511708Sstevel static struct ssm_node2inst ssm_node2inst_map = {-1, -1, NULL};
1521708Sstevel 
1531708Sstevel 
1541708Sstevel /*
1551708Sstevel  * Configuration data structures
1561708Sstevel  */
1571708Sstevel static struct bus_ops ssm_bus_ops = {
1581708Sstevel 	BUSO_REV,
1591708Sstevel 	ddi_bus_map,		/* map */
1601708Sstevel 	0,			/* get_intrspec */
1611708Sstevel 	0,			/* add_intrspec */
1621708Sstevel 	0,			/* remove_intrspec */
1631708Sstevel 	i_ddi_map_fault,	/* map_fault */
1641708Sstevel 	ddi_dma_map,		/* dma_map */
1651708Sstevel 	ddi_dma_allochdl,
1661708Sstevel 	ddi_dma_freehdl,
1671708Sstevel 	ddi_dma_bindhdl,
1681708Sstevel 	ddi_dma_unbindhdl,
1691708Sstevel 	ddi_dma_flush,
1701708Sstevel 	ddi_dma_win,
1711708Sstevel 	ddi_dma_mctl,		/* dma_ctl */
1721708Sstevel 	ssm_ctlops,		/* ctl */
1731708Sstevel 	ddi_bus_prop_op,	/* prop_op */
1741708Sstevel 	ndi_busop_get_eventcookie,
1751708Sstevel 	ndi_busop_add_eventcall,
1761708Sstevel 	ndi_busop_remove_eventcall,
1771708Sstevel 	ndi_post_event,
1781708Sstevel 	0,
1791708Sstevel 	0,
1801708Sstevel 	0,
1811708Sstevel 	ssm_fm_init_child,
1821708Sstevel 	NULL,
1831708Sstevel 	NULL,
1841708Sstevel 	NULL,
1851708Sstevel 	0,
1861708Sstevel 	i_ddi_intr_ops
1871708Sstevel };
1881708Sstevel 
1891708Sstevel static struct cb_ops ssm_cb_ops = {
1901708Sstevel 	ssm_open,			/* open */
1911708Sstevel 	ssm_close,			/* close */
1921708Sstevel 	nodev,				/* strategy */
1931708Sstevel 	nodev,				/* print */
1941708Sstevel 	nodev,				/* dump */
1951708Sstevel 	nodev,				/* read */
1961708Sstevel 	nodev,				/* write */
1971708Sstevel 	ssm_ioctl,			/* ioctl */
1981708Sstevel 	nodev,				/* devmap */
1991708Sstevel 	nodev,				/* mmap */
2001708Sstevel 	nodev,				/* segmap */
2011708Sstevel 	nochpoll,			/* poll */
2021708Sstevel 	ddi_prop_op,			/* cb_prop_op */
2031708Sstevel 	NULL,				/* streamtab */
2041708Sstevel 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
2051708Sstevel 	CB_REV,				/* rev */
2061708Sstevel 	nodev,				/* int (*cb_aread)() */
2071708Sstevel 	nodev				/* int (*cb_awrite)() */
2081708Sstevel };
2091708Sstevel 
2101708Sstevel static struct dev_ops ssm_ops = {
2111708Sstevel 	DEVO_REV,		/* devo_rev, */
2121708Sstevel 	0,			/* refcnt */
2131708Sstevel 	ssm_info,		/* getinfo */
2141708Sstevel 	nulldev,		/* identify */
2151708Sstevel 	nulldev,		/* probe */
2161708Sstevel 	ssm_attach,		/* attach */
2171708Sstevel 	ssm_detach,		/* detach */
2181708Sstevel 	nulldev,		/* reset */
2191708Sstevel 	&ssm_cb_ops,		/* driver operations */
2201708Sstevel 	&ssm_bus_ops,		/* bus_ops */
2217656SSherry.Moore@Sun.COM 	nulldev,		/* power */
2227656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* quiesce */
2231708Sstevel };
2241708Sstevel 
2251708Sstevel /*
2261708Sstevel  * Driver globals
2271708Sstevel  */
2281708Sstevel static void *ssm_softstates;		/* ssm soft state hook */
2291708Sstevel 
2301708Sstevel extern struct mod_ops mod_driverops;
2311708Sstevel 
2321708Sstevel static struct modldrv modldrv = {
2331708Sstevel 	&mod_driverops,		/* Type of module.  This one is a driver */
2347656SSherry.Moore@Sun.COM 	"SSM Nexus",		/* name of module */
2351708Sstevel 	&ssm_ops,		/* driver ops */
2361708Sstevel };
2371708Sstevel 
2381708Sstevel static struct modlinkage modlinkage = {
2391708Sstevel 	MODREV_1,		/* rev */
2401708Sstevel 	(void *)&modldrv,
2411708Sstevel 	NULL
2421708Sstevel };
2431708Sstevel 
2441708Sstevel static int ssm_loaded_sbd = FALSE;
2451708Sstevel kmutex_t ssm_lock;
2461708Sstevel static int init_child(dev_info_t *child);
2471708Sstevel 
2481708Sstevel /*
2491708Sstevel  * These are the module initialization routines.
2501708Sstevel  */
2511708Sstevel 
2521708Sstevel int
_init(void)2531708Sstevel _init(void)
2541708Sstevel {
2551708Sstevel 	int error;
2561708Sstevel 
2571708Sstevel #if defined(DEBUG)
2581708Sstevel 	debug_print_level = 0x0;
2591708Sstevel #endif
2601708Sstevel 
2611708Sstevel 	/* Initialize soft state pointer. */
2621708Sstevel 	if ((error = ddi_soft_state_init(&ssm_softstates,
2637578SVijay.Balakrishna@Sun.COM 	    sizeof (struct ssm_soft_state), SSM_MAX_INSTANCES)) != 0)
2641708Sstevel 		return (error);
2651708Sstevel 
2661708Sstevel 	/* Install the module. */
2671708Sstevel 	error = mod_install(&modlinkage);
2681708Sstevel 	if (error != 0)
2691708Sstevel 		ddi_soft_state_fini(&ssm_softstates);
2701708Sstevel 
2711708Sstevel 	mutex_init(&ssm_lock, NULL, MUTEX_DRIVER, NULL);
2721708Sstevel 
2731708Sstevel 	return (error);
2741708Sstevel }
2751708Sstevel 
2761708Sstevel int
_fini(void)2771708Sstevel _fini(void)
2781708Sstevel {
2791708Sstevel 	int error;
2801708Sstevel 
2811708Sstevel 	/* Remove the module. */
2821708Sstevel 	if ((error = mod_remove(&modlinkage)) != 0)
2831708Sstevel 		return (error);
2841708Sstevel 
2851708Sstevel 	/*
2861708Sstevel 	 * Unregister the event handler
2871708Sstevel 	 */
288*11311SSurya.Prakki@Sun.COM 	(void) sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC, ssm_dr_event_handler);
2891708Sstevel 	mutex_destroy(&ssm_event_lock);
2901708Sstevel 
2911708Sstevel 	/* Free the soft state info. */
2921708Sstevel 	ddi_soft_state_fini(&ssm_softstates);
2931708Sstevel 	mutex_destroy(&ssm_lock);
2941708Sstevel 
2951708Sstevel 	return (0);
2961708Sstevel }
2971708Sstevel 
2981708Sstevel int
_info(struct modinfo * modinfop)2991708Sstevel _info(struct modinfo *modinfop)
3001708Sstevel {
3011708Sstevel 	return (mod_info(&modlinkage, modinfop));
3021708Sstevel }
3031708Sstevel 
3041708Sstevel /* device driver entry points */
3051708Sstevel 
3061708Sstevel /*
3071708Sstevel  * info entry point:
3081708Sstevel  */
3091708Sstevel 
3101708Sstevel /* ARGSUSED */
3111708Sstevel static int
ssm_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)3121708Sstevel ssm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
3131708Sstevel {
3141708Sstevel 	dev_t	dev;
3151708Sstevel 	int	instance;
3161708Sstevel 
3171708Sstevel 	if (infocmd == DDI_INFO_DEVT2INSTANCE) {
3181708Sstevel 		dev = (dev_t)arg;
3191708Sstevel 		instance = (getminor(dev) >> SSM_INSTANCE_SHIFT);
3201708Sstevel 		*result = (void *)(uintptr_t)instance;
3211708Sstevel 		return (DDI_SUCCESS);
3221708Sstevel 	}
3231708Sstevel 	return (DDI_FAILURE);
3241708Sstevel }
3251708Sstevel 
3261708Sstevel /*
3271708Sstevel  * attach entry point:
3281708Sstevel  */
3291708Sstevel 
3301708Sstevel static int
ssm_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)3311708Sstevel ssm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
3321708Sstevel {
3331708Sstevel 	int instance;
3341708Sstevel 	struct ssm_soft_state *softsp;
3351708Sstevel 	struct ssm_node2inst *prev, *sp, *tsp;
3361708Sstevel 
3371708Sstevel 	DPRINTF(SSM_ATTACH_DEBUG, ("ssm_attach\n"));
3381708Sstevel 
3391708Sstevel 	switch (cmd) {
3401708Sstevel 	case DDI_ATTACH:
3411708Sstevel 		break;
3421708Sstevel 
3431708Sstevel 	case DDI_RESUME:
3441708Sstevel 		return (DDI_SUCCESS);
3451708Sstevel 
3461708Sstevel 	default:
3471708Sstevel 		return (DDI_FAILURE);
3481708Sstevel 	}
3491708Sstevel 
3501708Sstevel 	instance = ddi_get_instance(devi);
3511708Sstevel 
3521708Sstevel 	if (ddi_soft_state_zalloc(ssm_softstates, instance) != DDI_SUCCESS)
3531708Sstevel 		return (DDI_FAILURE);
3541708Sstevel 
3551708Sstevel 	softsp = ddi_get_soft_state(ssm_softstates, instance);
3561708Sstevel 
3571708Sstevel 	/* Set the dip in the soft state */
3581708Sstevel 	softsp->dip = devi;
3591708Sstevel 	softsp->top_node = devi;
3601708Sstevel 	mutex_init(&softsp->ssm_sft_lock, NULL, MUTEX_DRIVER, NULL);
3611708Sstevel 
3621708Sstevel 	DPRINTF(SSM_ATTACH_DEBUG, ("ssm-%d: devi= 0x%p, softsp=0x%p\n",
363*11311SSurya.Prakki@Sun.COM 	    instance, (void *)devi, (void *)softsp));
3641708Sstevel 
3651708Sstevel 	if ((softsp->ssm_nodeid = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->dip,
3661708Sstevel 	    DDI_PROP_DONTPASS, "nodeid", -1)) == -1) {
3671708Sstevel 		cmn_err(CE_WARN, "ssm%d: unable to retrieve %s property",
3681708Sstevel 		    instance, "nodeid");
3691708Sstevel 		ddi_soft_state_free(ssm_softstates, instance);
3701708Sstevel 		return (DDI_FAILURE);
3711708Sstevel 	}
3721708Sstevel 
3731708Sstevel 	/* nothing to suspend/resume here */
3741708Sstevel 	(void) ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP,
3751708Sstevel 	    "pm-hardware-state", (caddr_t)"no-suspend-resume",
3761708Sstevel 	    strlen("no-suspend-resume") + 1);
3771708Sstevel 
3781708Sstevel #if DEBUG
3791708Sstevel 	if (ddi_create_minor_node(devi, "debug", S_IFCHR, instance,
3801708Sstevel 	    DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
3811708Sstevel 		ddi_soft_state_free(ssm_softstates, instance);
3821708Sstevel 		return (DDI_FAILURE);
3831708Sstevel 	}
3841708Sstevel #endif
3851708Sstevel 
3861708Sstevel 	if (ssm_make_nodes(devi, instance, softsp->ssm_nodeid)) {
3877578SVijay.Balakrishna@Sun.COM 		cmn_err(CE_WARN, "ssm:%s:%d: failed to make nodes",
3887578SVijay.Balakrishna@Sun.COM 		    ddi_driver_name(devi), instance);
3891708Sstevel 		ddi_remove_minor_node(devi, NULL);
3901708Sstevel 		ddi_soft_state_free(ssm_softstates, instance);
3911708Sstevel 		return (DDI_FAILURE);
3921708Sstevel 	}
3931708Sstevel 	ssm_fm_init(softsp);
3941708Sstevel 	ddi_report_dev(devi);
3951708Sstevel 
3961708Sstevel 	if (event_initialized == 0) {
3971708Sstevel 		int rv;
3981708Sstevel 		/*
3991708Sstevel 		 * Register DR event handler
4001708Sstevel 		 */
4011708Sstevel 		mutex_init(&ssm_event_lock,  NULL, MUTEX_DRIVER, NULL);
4021708Sstevel 		event_msg.msg_buf = (caddr_t)&payload;
4031708Sstevel 		event_msg.msg_len = sizeof (payload);
4041708Sstevel 
4051708Sstevel 		rv = sbbc_mbox_reg_intr(MBOX_EVENT_GENERIC,
4061708Sstevel 		    ssm_dr_event_handler, &event_msg,
4071708Sstevel 		    (uint_t *)&ssm_event_state, &ssm_event_lock);
4081708Sstevel 
4091708Sstevel 		if (rv == EINVAL)
4101708Sstevel 		event_initialized = 1;
4111708Sstevel 	}
4121708Sstevel 
4131708Sstevel 	/*
4141708Sstevel 	 * Preallocate to avoid sleeping with ssm_node2inst_lock held -
4151708Sstevel 	 * low level interrupts use this mutex.
4161708Sstevel 	 */
4171708Sstevel 	tsp = kmem_zalloc(sizeof (struct ssm_node2inst), KM_SLEEP);
4181708Sstevel 
4191708Sstevel 	mutex_enter(&ssm_node2inst_lock);
4201708Sstevel 
4211708Sstevel 	for (prev = NULL, sp = &ssm_node2inst_map; sp != NULL;
4221708Sstevel 	    prev = sp, sp = sp->next) {
4231708Sstevel 		ASSERT(sp->inst != instance);
4241708Sstevel 		ASSERT(sp->nodeid != softsp->ssm_nodeid);
4251708Sstevel 		if (sp->inst == -1)
4261708Sstevel 			break;
4271708Sstevel 	}
4281708Sstevel 
4291708Sstevel 	if (sp == NULL) {
4301708Sstevel 		ASSERT(prev->next == NULL);
4311708Sstevel 		sp = prev->next = tsp;
4321708Sstevel 		tsp = NULL;
4331708Sstevel 		sp->next = NULL;
4341708Sstevel 	}
4351708Sstevel 
4361708Sstevel 	sp->inst = instance;
4371708Sstevel 	sp->nodeid = softsp->ssm_nodeid;
4381708Sstevel 
4391708Sstevel 	mutex_exit(&ssm_node2inst_lock);
4401708Sstevel 
4411708Sstevel 	if (tsp != NULL)
4421708Sstevel 		kmem_free(tsp, sizeof (struct ssm_node2inst));
4431708Sstevel 
4441708Sstevel 	return (DDI_SUCCESS);
4451708Sstevel }
4461708Sstevel 
4471708Sstevel /*
4481708Sstevel  * detach entry point:
4491708Sstevel  */
4501708Sstevel /*ARGSUSED*/
4511708Sstevel static int
ssm_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)4521708Sstevel ssm_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
4531708Sstevel {
4541708Sstevel 	int instance, rv;
4551708Sstevel 	int (*sbd_teardown_instance) (int, caddr_t);
4561708Sstevel 	ssm_sbdp_info_t	sbdp_info;
4571708Sstevel 	struct ssm_soft_state *softsp;
4581708Sstevel 	struct ssm_node2inst *prev, *sp;
4591708Sstevel 
4601708Sstevel 	instance = ddi_get_instance(devi);
4611708Sstevel 	softsp = ddi_get_soft_state(ssm_softstates, instance);
4621708Sstevel 
4631708Sstevel 	if (softsp == NULL) {
4641708Sstevel 		cmn_err(CE_WARN,
4657578SVijay.Balakrishna@Sun.COM 		    "ssm_open bad instance number %d", instance);
4661708Sstevel 		return (ENXIO);
4671708Sstevel 	}
4681708Sstevel 
4691708Sstevel 	instance = ddi_get_instance(devi);
4701708Sstevel 
4711708Sstevel 	switch (cmd) {
4721708Sstevel 	case DDI_DETACH:
4731708Sstevel 		ddi_remove_minor_node(devi, NULL);
4741708Sstevel 
4751708Sstevel 		sbd_teardown_instance = (int (*) (int, caddr_t))
4767578SVijay.Balakrishna@Sun.COM 		    modlookup("misc/sbd", "sbd_teardown_instance");
4771708Sstevel 
4781708Sstevel 		if (!sbd_teardown_instance) {
4791708Sstevel 			cmn_err(CE_WARN, "cannot find sbd_teardown_instance");
4801708Sstevel 			return (DDI_FAILURE);
4811708Sstevel 		}
4821708Sstevel 
4831708Sstevel 		sbdp_info.instance = instance;
4841708Sstevel 		sbdp_info.wnode = softsp->ssm_nodeid;
4851708Sstevel 		rv = (*sbd_teardown_instance)(instance, (caddr_t)&sbdp_info);
4861708Sstevel 
4871708Sstevel 		if (rv != DDI_SUCCESS) {
4881708Sstevel 			cmn_err(CE_WARN, "cannot run sbd_teardown_instance");
4891708Sstevel 			return (DDI_FAILURE);
4901708Sstevel 		}
4911708Sstevel 		ssm_fm_fini(softsp);
4921708Sstevel 		mutex_destroy(&softsp->ssm_sft_lock);
4931708Sstevel 		ddi_soft_state_free(ssm_softstates, instance);
4941708Sstevel 
4951708Sstevel 		mutex_enter(&ssm_node2inst_lock);
4961708Sstevel 		for (prev = NULL, sp = &ssm_node2inst_map; sp != NULL;
4971708Sstevel 		    prev = sp, sp = sp->next) {
4981708Sstevel 			/* Only the head of the list can persist if unused */
4991708Sstevel 			ASSERT(prev == NULL || sp->inst != -1);
5001708Sstevel 			if (sp->inst == instance)
5011708Sstevel 				break;
5021708Sstevel 		}
5031708Sstevel 		ASSERT(sp != NULL);
5041708Sstevel 
5051708Sstevel 		if (sp != &ssm_node2inst_map) {
5061708Sstevel 			prev->next = sp->next;
5071708Sstevel 			kmem_free(sp, sizeof (struct ssm_node2inst));
5081708Sstevel 		} else {
5091708Sstevel 			/*
5101708Sstevel 			 * Invalidate the head element, but retain the rest
5111708Sstevel 			 * of the list - "next" is still valid.
5121708Sstevel 			 */
5131708Sstevel 
5141708Sstevel 			sp->nodeid = -1;
5151708Sstevel 			sp->inst = -1;
5161708Sstevel 		}
5171708Sstevel 		mutex_exit(&ssm_node2inst_lock);
5181708Sstevel 
5191708Sstevel 		return (DDI_SUCCESS);
5201708Sstevel 
5211708Sstevel 	case DDI_SUSPEND:
5221708Sstevel 		return (DDI_SUCCESS);
5231708Sstevel 
5241708Sstevel 	default:
5251708Sstevel 		return (DDI_FAILURE);
5261708Sstevel 	}
5271708Sstevel }
5281708Sstevel 
5291708Sstevel extern void make_ddi_ppd(dev_info_t *, struct ddi_parent_private_data **);
5301708Sstevel extern struct ddi_parent_private_data *init_regspec_64(dev_info_t *);
5311708Sstevel 
5321708Sstevel static int
name_child(dev_info_t * child,char * name,int namelen)5331708Sstevel name_child(dev_info_t *child, char *name, int namelen)
5341708Sstevel {
5351708Sstevel 	struct regspec *rp;
5361708Sstevel 	struct ddi_parent_private_data *pdptr;
5371708Sstevel 	int portid = 0;
5381708Sstevel 	int regbase = -1;
5391708Sstevel 	extern uint_t root_phys_addr_lo_mask;
5401708Sstevel 
5411708Sstevel 	make_ddi_ppd(child, &pdptr);
5421708Sstevel 	ddi_set_parent_data(child, pdptr);
5431708Sstevel 
5441708Sstevel 	name[0] = '\0';
5451708Sstevel 	if (sparc_pd_getnreg(child) == 0)
5461708Sstevel 		return (DDI_SUCCESS);
5471708Sstevel 
5481708Sstevel 	rp = sparc_pd_getreg(child, 0);
5491708Sstevel 
5501708Sstevel 	portid = ddi_prop_get_int(DDI_DEV_T_ANY, child,
5511708Sstevel 	    DDI_PROP_DONTPASS, "portid", -1);
5521708Sstevel 	if (portid == -1) {
5531708Sstevel 		cmn_err(CE_WARN, "could not find portid property in %s",
5541708Sstevel 		    DEVI(child)->devi_node_name);
5551708Sstevel 	} else {
5561708Sstevel 		regbase = rp->regspec_addr & root_phys_addr_lo_mask;
5571708Sstevel 	}
5581708Sstevel 	(void) snprintf(name, namelen, "%x,%x", portid, regbase);
5591708Sstevel 	return (DDI_SUCCESS);
5601708Sstevel }
5611708Sstevel 
5621708Sstevel static int
init_child(dev_info_t * child)5631708Sstevel init_child(dev_info_t *child)
5641708Sstevel {
5651708Sstevel 	char name[MAXNAMELEN];
5661708Sstevel 
5671708Sstevel 	(void) name_child(child, name, MAXNAMELEN);
5681708Sstevel 	ddi_set_name_addr(child, name);
5691708Sstevel 	if ((ndi_dev_is_persistent_node(child) == 0) &&
5701708Sstevel 	    (ndi_merge_node(child, name_child) == DDI_SUCCESS)) {
5711708Sstevel 		impl_ddi_sunbus_removechild(child);
5721708Sstevel 		return (DDI_FAILURE);
5731708Sstevel 	}
5741708Sstevel 
5751708Sstevel 	(void) init_regspec_64(child);
5761708Sstevel 	return (DDI_SUCCESS);
5771708Sstevel }
5781708Sstevel 
5791708Sstevel /*
5801708Sstevel  * Control ops entry point:
5811708Sstevel  *
5821708Sstevel  * Requests handled completely:
5831708Sstevel  *      DDI_CTLOPS_INITCHILD
5841708Sstevel  *      DDI_CTLOPS_UNINITCHILD
5851708Sstevel  * 	DDI_CTLOPS_REPORTDEV
5861708Sstevel  * All others are passed to the parent.
5871708Sstevel  * The name of the ssm node is ssm@nodeid,0.
5881708Sstevel  * ssm is the equivalent of rootnex.
5891708Sstevel  */
5901708Sstevel static int
ssm_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)5911708Sstevel ssm_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, void *arg,
5921708Sstevel     void *result)
5931708Sstevel {
5941708Sstevel 	int rval;
5951708Sstevel 
5961708Sstevel 	switch (op) {
5971708Sstevel 	case DDI_CTLOPS_INITCHILD: {
5981708Sstevel 		DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_INITCHILD\n"));
5991708Sstevel 		return (init_child((dev_info_t *)arg));
6001708Sstevel 	}
6011708Sstevel 
6021708Sstevel 	case DDI_CTLOPS_UNINITCHILD: {
6031708Sstevel 		DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_UNINITCHILD\n"));
6041708Sstevel 		impl_ddi_sunbus_removechild((dev_info_t *)arg);
6051708Sstevel 		return (DDI_SUCCESS);
6061708Sstevel 	}
6071708Sstevel 
6081708Sstevel 	case DDI_CTLOPS_REPORTDEV: {
6091708Sstevel 		char buf[80];
6101708Sstevel 		char *p = buf;
6111708Sstevel 		dev_info_t *parent;
6121708Sstevel 		int portid;
6131708Sstevel 
6141708Sstevel 		DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_REPORTDEV\n"));
6151708Sstevel 		parent = ddi_get_parent(rdip);
6161708Sstevel 
6171708Sstevel 		(void) sprintf(p, "%s%d at %s%d", DEVI(rdip)->devi_name,
6181708Sstevel 		    DEVI(rdip)->devi_instance, ddi_get_name(parent),
6191708Sstevel 		    ddi_get_instance(parent));
6201708Sstevel 		p += strlen(p);
6211708Sstevel 
6221708Sstevel 		/* Fetch Safari Extended Agent ID of this device. */
6231708Sstevel 		portid = (int)ddi_getprop(DDI_DEV_T_ANY, rdip,
6241708Sstevel 		    DDI_PROP_DONTPASS, "portid", -1);
6251708Sstevel 
6261708Sstevel 		/*
6271708Sstevel 		 * If this is one of the ssm children it will have
6281708Sstevel 		 * portid property and its parent will be ssm.
6291708Sstevel 		 * In this case report Node number and Safari id.
6301708Sstevel 		 */
6311708Sstevel 		if (portid != -1 &&
6321708Sstevel 		    strcmp("ssm", ddi_get_name(parent)) == 0) {
6331708Sstevel 			struct regspec *rp;
6341708Sstevel 			int node;
6351708Sstevel 			int safid;
6361708Sstevel 			int n;
6371708Sstevel 
6381708Sstevel 			rp = sparc_pd_getreg(rdip, 0);
6391708Sstevel 			n = sparc_pd_getnreg(rdip);
6401708Sstevel 			ASSERT(n > 0);
6411708Sstevel 
6421708Sstevel 			node  = SG_PORTID_TO_NODEID(portid);
6431708Sstevel 			safid = SG_PORTID_TO_SAFARI_ID(portid);
6441708Sstevel 
6451708Sstevel 			(void) strcpy(p, ": ");
6461708Sstevel 			p += strlen(p);
6471708Sstevel 
6481708Sstevel 			(void) sprintf(p, "Node %d Safari id %d 0x%x%s",
6491708Sstevel 			    node, safid,
6501708Sstevel 			    rp->regspec_addr,
6511708Sstevel 			    (n > 1 ? "" : " ..."));
6521708Sstevel 			p += strlen(p);
6531708Sstevel 		}
6541708Sstevel 
6551708Sstevel 		cmn_err(CE_CONT, "?%s\n", buf);
6561708Sstevel 		rval = DDI_SUCCESS;
6571708Sstevel 
6581708Sstevel 		break;
6591708Sstevel 	}
6601708Sstevel 
6611708Sstevel 	default:
6621708Sstevel 		rval = ddi_ctlops(dip, rdip, op, arg, result);
6631708Sstevel 
6641708Sstevel 		break;
6651708Sstevel 	}
6661708Sstevel 
6671708Sstevel 	return (rval);
6681708Sstevel }
6691708Sstevel 
6701708Sstevel /*ARGSUSED*/
6711708Sstevel static int
ssm_make_nodes(dev_info_t * dip,int instance,int ssm_nodeid)6721708Sstevel ssm_make_nodes(dev_info_t *dip, int instance, int ssm_nodeid)
6731708Sstevel {
6741708Sstevel 	int		rv;
6751708Sstevel 	minor_t		minor_num, bd;
6761708Sstevel 	auto char	filename[20];
6771708Sstevel 
6781708Sstevel 	for (bd = 0; bd < plat_max_boards(); bd++) {
6791708Sstevel 		if (SG_BOARD_IS_CPU_TYPE(bd))
6801708Sstevel 			(void) sprintf(filename, "N%d.SB%d", ssm_nodeid, bd);
6811708Sstevel 		else
6821708Sstevel 			(void) sprintf(filename, "N%d.IB%d", ssm_nodeid, bd);
6831708Sstevel 
6841708Sstevel 		minor_num = (instance << SSM_INSTANCE_SHIFT) | bd;
6851708Sstevel 
6861708Sstevel 		rv = ddi_create_minor_node(dip, filename, S_IFCHR,
6877578SVijay.Balakrishna@Sun.COM 		    minor_num, DDI_NT_SBD_ATTACHMENT_POINT, NULL);
6881708Sstevel 		if (rv == DDI_FAILURE) {
6891708Sstevel 			cmn_err(CE_WARN,
6907578SVijay.Balakrishna@Sun.COM 			    "ssm_make_nodes:%d: failed to create "
6917578SVijay.Balakrishna@Sun.COM 			    "minor node (%s, 0x%x)",
6927578SVijay.Balakrishna@Sun.COM 			    instance, filename, minor_num);
6931708Sstevel 			return (-1);
6941708Sstevel 		}
6951708Sstevel 	}
6961708Sstevel 
6971708Sstevel 	return (0);
6981708Sstevel }
6991708Sstevel 
7001708Sstevel 
7011708Sstevel /* ARGSUSED */
7021708Sstevel static int
ssm_open(dev_t * devi,int flags,int otyp,cred_t * credp)7031708Sstevel ssm_open(dev_t *devi, int flags, int otyp, cred_t *credp)
7041708Sstevel {
7051708Sstevel 	struct ssm_soft_state *softsp;
7061708Sstevel 	minor_t board, instance;
7071708Sstevel 	int (*sbd_setup_instance)(int, dev_info_t *, int, int, caddr_t);
7081708Sstevel 	ssm_sbdp_info_t	sbdp_info;
7091708Sstevel 	int rv;
7101708Sstevel 
7111708Sstevel 	instance = (getminor(*devi) >> SSM_INSTANCE_SHIFT);
7121708Sstevel 
7131708Sstevel 	softsp = ddi_get_soft_state(ssm_softstates, instance);
7141708Sstevel 	if (softsp == NULL) {
7151708Sstevel 		cmn_err(CE_WARN, "ssm_open bad instance number %d", instance);
7161708Sstevel 		return (ENXIO);
7171708Sstevel 	}
7181708Sstevel 
7191708Sstevel 	board = (getminor(*devi) & SSM_BOARD_MASK);
7201708Sstevel 
7211708Sstevel 	if (board < 0 || board > plat_max_boards()) {
7221708Sstevel 		return (ENXIO);
7231708Sstevel 	}
7241708Sstevel 
7251708Sstevel 	mutex_enter(&ssm_lock);
7261708Sstevel 	if (instance == 0 && ssm_loaded_sbd == FALSE) {
7271708Sstevel 
7281708Sstevel 		if (modload("misc", "sbd") == -1) {
7291708Sstevel 			cmn_err(CE_WARN, "ssm_open: cannot load sbd");
7301708Sstevel 			mutex_exit(&ssm_lock);
7311708Sstevel 			return (EIO);
7321708Sstevel 		}
7331708Sstevel 		ssm_loaded_sbd = TRUE;
7341708Sstevel 	}
7351708Sstevel 	mutex_exit(&ssm_lock);
7361708Sstevel 
7371708Sstevel 	mutex_enter(&softsp->ssm_sft_lock);
7381708Sstevel 	if (softsp->initialized == FALSE) {
7391708Sstevel 
7401708Sstevel 		if (softsp->top_node == NULL) {
7411708Sstevel 			cmn_err(CE_WARN, "cannot find ssm top dnode");
7421708Sstevel 			mutex_exit(&softsp->ssm_sft_lock);
7431708Sstevel 			return (EIO);
7441708Sstevel 		}
7451708Sstevel 
7461708Sstevel 		sbd_setup_instance = (int (*)(int, dev_info_t *, int, int,
7477578SVijay.Balakrishna@Sun.COM 		    caddr_t))modlookup("misc/sbd", "sbd_setup_instance");
7481708Sstevel 
7491708Sstevel 		if (!sbd_setup_instance) {
7501708Sstevel 			cmn_err(CE_WARN, "cannot find sbd_setup_instance");
7511708Sstevel 			mutex_exit(&softsp->ssm_sft_lock);
7521708Sstevel 			return (EIO);
7531708Sstevel 		}
7541708Sstevel 
7551708Sstevel 		sbdp_info.instance = instance;
7561708Sstevel 		sbdp_info.wnode = softsp->ssm_nodeid;
7571708Sstevel 
7581708Sstevel 		rv = (*sbd_setup_instance)(instance, softsp->top_node,
7597578SVijay.Balakrishna@Sun.COM 		    plat_max_boards(), softsp->ssm_nodeid,
7607578SVijay.Balakrishna@Sun.COM 		    (caddr_t)&sbdp_info);
7611708Sstevel 		if (rv != DDI_SUCCESS) {
7621708Sstevel 			cmn_err(CE_WARN, "cannot run sbd_setup_instance");
7631708Sstevel 			mutex_exit(&softsp->ssm_sft_lock);
7641708Sstevel 			return (EIO);
7651708Sstevel 		}
7661708Sstevel 		softsp->initialized = TRUE;
7671708Sstevel 	}
7681708Sstevel 	mutex_exit(&softsp->ssm_sft_lock);
7691708Sstevel 
7701708Sstevel 	return (DDI_SUCCESS);
7711708Sstevel }
7721708Sstevel 
7731708Sstevel 
7741708Sstevel /* ARGSUSED */
7751708Sstevel static int
ssm_close(dev_t dev,int flags,int otyp,cred_t * credp)7761708Sstevel ssm_close(dev_t dev, int flags, int otyp, cred_t *credp)
7771708Sstevel {
7781708Sstevel 	struct ssm_soft_state *softsp;
7791708Sstevel 	minor_t board, instance;
7801708Sstevel 
7811708Sstevel 	instance = (getminor(dev) >> SSM_INSTANCE_SHIFT);
7821708Sstevel 
7831708Sstevel 	softsp = ddi_get_soft_state(ssm_softstates, instance);
7841708Sstevel 	if (softsp == NULL)
7851708Sstevel 		return (ENXIO);
7861708Sstevel 
7871708Sstevel 	board = (getminor(dev) & SSM_BOARD_MASK);
7881708Sstevel 
7891708Sstevel 	if (board < 0 || board > plat_max_boards())
7901708Sstevel 		return (ENXIO);
7911708Sstevel 
7921708Sstevel 	return (DDI_SUCCESS);
7931708Sstevel }
7941708Sstevel 
7951708Sstevel /* ARGSUSED */
7961708Sstevel static int
ssm_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)7971708Sstevel ssm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
7981708Sstevel 	int *rvalp)
7991708Sstevel {
8001708Sstevel 	struct ssm_soft_state *softsp;
8011708Sstevel 	char *addr;
8021708Sstevel 	struct devctl_iocdata *dcp;
8031708Sstevel 	int instance, rv = 0;
8041708Sstevel 	int (*sbd_ioctl) (dev_t, int, intptr_t, int, char *);
8051708Sstevel 
8061708Sstevel 	instance = (getminor(dev) >> SSM_INSTANCE_SHIFT);
8071708Sstevel 	softsp = ddi_get_soft_state(ssm_softstates, instance);
8081708Sstevel 	if (softsp == NULL)
8091708Sstevel 		return (ENXIO);
8101708Sstevel 
8111708Sstevel 	switch (cmd) {
8121708Sstevel 
8131708Sstevel 	case DEVCTL_BUS_CONFIGURE:
8141708Sstevel 		/*
8151708Sstevel 		 * read devctl ioctl data
8161708Sstevel 		 */
8171708Sstevel 		if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
8181708Sstevel 			return (EFAULT);
8191708Sstevel 
8201708Sstevel 		addr = ndi_dc_getaddr(dcp);
8211708Sstevel 		cmn_err(CE_NOTE,
8221708Sstevel 		    "DEVCTL_BUS_CONFIGURE: device id is %s\n", addr);
8231708Sstevel 		ndi_dc_freehdl(dcp);
8241708Sstevel 		break;
8251708Sstevel 
8261708Sstevel 	case DEVCTL_BUS_UNCONFIGURE:
8271708Sstevel 		if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
8281708Sstevel 			return (EFAULT);
8291708Sstevel 
8301708Sstevel 		addr = ndi_dc_getaddr(dcp);
8311708Sstevel 		cmn_err(CE_NOTE,
8321708Sstevel 		    "DEVCTL_BUS_UNCONFIGURE: device id is %s\n", addr);
8331708Sstevel 		ndi_dc_freehdl(dcp);
8341708Sstevel 		break;
8351708Sstevel 
8361708Sstevel #ifdef DEBUG
8371708Sstevel 	case SSM_TEARDOWN_SBD: {
8381708Sstevel 		ssm_sbdp_info_t	sbdp_info;
8391708Sstevel 		int (*sbd_teardown_instance) (int, caddr_t);
8401708Sstevel 		sbd_teardown_instance = (int (*) (int, caddr_t))
8417578SVijay.Balakrishna@Sun.COM 		    modlookup("misc/sbd", "sbd_teardown_instance");
8421708Sstevel 
8431708Sstevel 		if (!sbd_teardown_instance) {
8441708Sstevel 			cmn_err(CE_WARN, "cannot find sbd_teardown_instance");
8451708Sstevel 			return (EFAULT);
8461708Sstevel 		}
8471708Sstevel 
8481708Sstevel 		sbdp_info.instance = instance;
8491708Sstevel 		sbdp_info.wnode = softsp->ssm_nodeid;
8501708Sstevel 		rv = (*sbd_teardown_instance)(instance, (caddr_t)&sbdp_info);
8511708Sstevel 		if (rv != DDI_SUCCESS) {
8521708Sstevel 			cmn_err(CE_WARN, "cannot run sbd_teardown_instance");
8531708Sstevel 			return (EFAULT);
8541708Sstevel 		}
8551708Sstevel 
8561708Sstevel 		ssm_loaded_sbd = FALSE;
8571708Sstevel 		softsp->initialized = FALSE;
8581708Sstevel 	}
8591708Sstevel #endif
8601708Sstevel 
8611708Sstevel 
8621708Sstevel 	default: {
8631708Sstevel 		char	event = 0;
8641708Sstevel 
8657578SVijay.Balakrishna@Sun.COM 		sbd_ioctl = (int (*) (dev_t, int, intptr_t, int, char *))
8667578SVijay.Balakrishna@Sun.COM 		    modlookup("misc/sbd", "sbd_ioctl");
8671708Sstevel 
8681708Sstevel 		if (sbd_ioctl)
8691708Sstevel 			rv = (*sbd_ioctl) (dev, cmd, arg, mode, &event);
8701708Sstevel 		else {
8711708Sstevel 			cmn_err(CE_WARN, "cannot find sbd_ioctl");
8721708Sstevel 			return (ENXIO);
8731708Sstevel 		}
8741708Sstevel 		/*
8751708Sstevel 		 * Check to see if we need to send an event
8761708Sstevel 		 */
8771708Sstevel 		if (event == 1) {
8781708Sstevel 			int slot;
8791708Sstevel 			int hint = SE_NO_HINT;
8801708Sstevel 
8811708Sstevel 			if (rv == 0) {
8821708Sstevel 				if (cmd == SBD_CMD_CONNECT ||
8831708Sstevel 				    cmd == SBD_CMD_CONFIGURE)
8841708Sstevel 					hint = SE_HINT_INSERT;
8851708Sstevel 				else if (cmd == SBD_CMD_UNCONFIGURE ||
8861708Sstevel 				    cmd == SBD_CMD_DISCONNECT)
8871708Sstevel 					hint = SE_HINT_REMOVE;
8881708Sstevel 			}
8891708Sstevel 
8901708Sstevel 			slot = (getminor(dev) & SSM_BOARD_MASK);
891*11311SSurya.Prakki@Sun.COM 			(void) ssm_generate_event(softsp->ssm_nodeid, slot,
892*11311SSurya.Prakki@Sun.COM 			    hint);
8931708Sstevel 		}
8941708Sstevel 		break;
8951708Sstevel 	}
8961708Sstevel 	}
8971708Sstevel 
8981708Sstevel 	return (rv);
8991708Sstevel }
9001708Sstevel 
9011708Sstevel void
ssm_get_attch_pnt(int node,int board,char * attach_pnt)9021708Sstevel ssm_get_attch_pnt(int node, int board, char *attach_pnt)
9031708Sstevel {
9041708Sstevel 	struct ssm_node2inst	*sp;
9051708Sstevel 
9061708Sstevel 	/*
9071708Sstevel 	 * Hold this mutex, until we are done so that ssm dip
9081708Sstevel 	 * doesn't detach.
9091708Sstevel 	 */
9101708Sstevel 	mutex_enter(&ssm_node2inst_lock);
9111708Sstevel 
9121708Sstevel 	for (sp = &ssm_node2inst_map; sp != NULL; sp = sp->next) {
9131708Sstevel 		if (sp->inst == -1)
9141708Sstevel 			continue;
9151708Sstevel 		if (sp->nodeid == node)
9161708Sstevel 			break;
9171708Sstevel 	}
9181708Sstevel 
9191708Sstevel 	if (sp == NULL) {
9201708Sstevel 		/* We didn't find the ssm dip, return failure */
9211708Sstevel 		attach_pnt[0] = '\0';
9221708Sstevel 		mutex_exit(&ssm_node2inst_lock);
9231708Sstevel 		return;
9241708Sstevel 	}
9251708Sstevel 
9261708Sstevel 	/*
9271708Sstevel 	 * we have the instance, and the board, construct the attch pnt
9281708Sstevel 	 */
9291708Sstevel 	if (SG_BOARD_IS_CPU_TYPE(board))
9301708Sstevel 		(void) sprintf(attach_pnt, "ssm%d:N%d.SB%d",
9311708Sstevel 		    sp->inst, node, board);
9321708Sstevel 	else
9331708Sstevel 		(void) sprintf(attach_pnt, "ssm%d:N%d.IB%d",
9341708Sstevel 		    sp->inst, node, board);
9351708Sstevel 
9361708Sstevel 	mutex_exit(&ssm_node2inst_lock);
9371708Sstevel }
9381708Sstevel 
9391708Sstevel /*
9401708Sstevel  * Generate an event to sysevent
9411708Sstevel  */
9421708Sstevel static int
ssm_generate_event(int node,int board,int hint)9431708Sstevel ssm_generate_event(int node, int board, int hint)
9441708Sstevel {
9451708Sstevel 	sysevent_t			*ev;
9461708Sstevel 	sysevent_id_t			eid;
9471708Sstevel 	int				rv = 0;
9481708Sstevel 	sysevent_value_t		evnt_val;
9491708Sstevel 	sysevent_attr_list_t		*evnt_attr_list = NULL;
9501708Sstevel 	char				attach_pnt[MAXPATHLEN];
9511708Sstevel 
9521708Sstevel 
9531708Sstevel 	attach_pnt[0] = '\0';
9541708Sstevel 	ssm_get_attch_pnt(node, board, attach_pnt);
9551708Sstevel 
9561708Sstevel 	if (attach_pnt[0] == '\0')
9571708Sstevel 		return (-1);
9581708Sstevel 
9591708Sstevel 	ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, EP_DDI,
9601708Sstevel 	    KM_SLEEP);
9611708Sstevel 	evnt_val.value_type = SE_DATA_TYPE_STRING;
9621708Sstevel 	evnt_val.value.sv_string = attach_pnt;
9631708Sstevel 
9641708Sstevel 	rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val, KM_SLEEP);
9651708Sstevel 	if (rv != 0) {
9661708Sstevel 		cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
9671708Sstevel 		    DR_AP_ID, EC_DR);
9681708Sstevel 		sysevent_free(ev);
9691708Sstevel 		return (rv);
9701708Sstevel 	}
9711708Sstevel 
9721708Sstevel 	/*
9731708Sstevel 	 * Add the hint
9741708Sstevel 	 */
9751708Sstevel 	evnt_val.value_type = SE_DATA_TYPE_STRING;
9761708Sstevel 	evnt_val.value.sv_string = SE_HINT2STR(hint);
9771708Sstevel 
9781708Sstevel 	rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val, KM_SLEEP);
9791708Sstevel 	if (rv != 0) {
9801708Sstevel 		cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
9811708Sstevel 		    DR_HINT, EC_DR);
9821708Sstevel 		sysevent_free_attr(evnt_attr_list);
9831708Sstevel 		sysevent_free(ev);
9841708Sstevel 		return (-1);
9851708Sstevel 	}
9861708Sstevel 
9871708Sstevel 	if (sysevent_attach_attributes(ev, evnt_attr_list) != 0) {
9881708Sstevel 		cmn_err(CE_WARN, "Failed to attach attr list for %s event",
9891708Sstevel 		    EC_DR);
9901708Sstevel 		sysevent_free_attr(evnt_attr_list);
9911708Sstevel 		sysevent_free(ev);
9921708Sstevel 		return (-1);
9931708Sstevel 	}
9941708Sstevel 
9951708Sstevel 	rv = log_sysevent(ev, KM_NOSLEEP, &eid);
9961708Sstevel 	if (rv != 0) {
9971708Sstevel 		cmn_err(CE_WARN, "ssm_dr_event_handler: failed to log event");
9981708Sstevel 	}
9991708Sstevel 
10001708Sstevel 	sysevent_free(ev);
10011708Sstevel 
10021708Sstevel 	return (rv);
10031708Sstevel }
10041708Sstevel 
10051708Sstevel /*
10061708Sstevel  * DR Event Handler
10071708Sstevel  */
10081708Sstevel uint_t
ssm_dr_event_handler(char * arg)10091708Sstevel ssm_dr_event_handler(char *arg)
10101708Sstevel {
10111708Sstevel 	sg_system_fru_descriptor_t	*fdp;
10121708Sstevel 	int				hint;
10131708Sstevel 
10141708Sstevel 
10151708Sstevel 	fdp = (sg_system_fru_descriptor_t *)(((sbbc_msg_t *)arg)->msg_buf);
10161708Sstevel 	if (fdp == NULL) {
10171708Sstevel 		DPRINTF(SSM_EVENT_DEBUG,
10181708Sstevel 		    ("ssm_dr_event_handler: ARG is null\n"));
10191708Sstevel 		return (DDI_INTR_CLAIMED);
10201708Sstevel 	}
10211708Sstevel #ifdef DEBUG
10221708Sstevel 	DPRINTF(SSM_EVENT_DEBUG, ("ssm_dr_event_handler called\n"));
10231708Sstevel 	DPRINTF(SSM_EVENT_DEBUG, ("\tnode\t%d\n", fdp->node));
10241708Sstevel 	DPRINTF(SSM_EVENT_DEBUG, ("\tslot\t%d\n", fdp->slot));
10251708Sstevel 	DPRINTF(SSM_EVENT_DEBUG, ("\tparent_hdl\t0x%lx\n", fdp->parent_hdl));
10261708Sstevel 	DPRINTF(SSM_EVENT_DEBUG, ("\tchild_hdl\t0x%lx\n", fdp->child_hdl));
10271708Sstevel 	DPRINTF(SSM_EVENT_DEBUG, ("\tevent_details\t%s\n",
10281708Sstevel 	    EVNT2STR(fdp->event_details)));
10291708Sstevel #endif
10301708Sstevel 
10311708Sstevel 	switch (fdp->event_details) {
10321708Sstevel 	case SG_EVT_BOARD_ABSENT:
10331708Sstevel 		hint = SE_HINT_REMOVE;
10341708Sstevel 		break;
10351708Sstevel 	case SG_EVT_BOARD_PRESENT:
10361708Sstevel 		hint = SE_HINT_INSERT;
10371708Sstevel 		break;
10381708Sstevel 	default:
10391708Sstevel 		hint = SE_NO_HINT;
10401708Sstevel 		break;
10411708Sstevel 
10421708Sstevel 	}
10431708Sstevel 
1044*11311SSurya.Prakki@Sun.COM 	(void) ssm_generate_event(fdp->node, fdp->slot, hint);
10451708Sstevel 
10461708Sstevel 	return (DDI_INTR_CLAIMED);
10471708Sstevel }
10481708Sstevel 
10491708Sstevel /*
10501708Sstevel  * Initialize our FMA resources
10511708Sstevel  */
10521708Sstevel static void
ssm_fm_init(struct ssm_soft_state * softsp)10531708Sstevel ssm_fm_init(struct ssm_soft_state *softsp)
10541708Sstevel {
10551708Sstevel 	softsp->ssm_fm_cap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE |
10561708Sstevel 	    DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
10571708Sstevel 
10581708Sstevel 	/*
10591708Sstevel 	 * Request or capability level and get our parents capability
10601708Sstevel 	 * and ibc.
10611708Sstevel 	 */
10621708Sstevel 	ddi_fm_init(softsp->dip, &softsp->ssm_fm_cap, &softsp->ssm_fm_ibc);
10631708Sstevel 	ASSERT((softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE) &&
10641708Sstevel 	    (softsp->ssm_fm_cap & DDI_FM_ERRCB_CAPABLE));
10651708Sstevel 	/*
10661708Sstevel 	 * Register error callback with our parent.
10671708Sstevel 	 */
10681708Sstevel 	ddi_fm_handler_register(softsp->dip, ssm_err_callback, NULL);
10691708Sstevel }
10701708Sstevel 
10711708Sstevel /*
10721708Sstevel  * Breakdown our FMA resources
10731708Sstevel  */
10741708Sstevel static void
ssm_fm_fini(struct ssm_soft_state * softsp)10751708Sstevel ssm_fm_fini(struct ssm_soft_state *softsp)
10761708Sstevel {
10771708Sstevel 	/*
10781708Sstevel 	 * Clean up allocated fm structures
10791708Sstevel 	 */
10801708Sstevel 	ASSERT(softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE);
10811708Sstevel 	ddi_fm_handler_unregister(softsp->dip);
10821708Sstevel 	ddi_fm_fini(softsp->dip);
10831708Sstevel }
10841708Sstevel 
10851708Sstevel /*
10861708Sstevel  * Initialize FMA resources for children devices. Called when
10871708Sstevel  * child calls ddi_fm_init().
10881708Sstevel  */
10891708Sstevel /*ARGSUSED*/
10901708Sstevel static int
ssm_fm_init_child(dev_info_t * dip,dev_info_t * tdip,int cap,ddi_iblock_cookie_t * ibc)10911708Sstevel ssm_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap,
10921708Sstevel 		ddi_iblock_cookie_t *ibc)
10931708Sstevel {
10941708Sstevel 	struct ssm_soft_state *softsp = ddi_get_soft_state(ssm_softstates,
10957578SVijay.Balakrishna@Sun.COM 	    ddi_get_instance(dip));
10961708Sstevel 
10971708Sstevel 	*ibc = softsp->ssm_fm_ibc;
10981708Sstevel 	return (softsp->ssm_fm_cap);
10991708Sstevel }
11001708Sstevel 
11011708Sstevel /*
11021708Sstevel  * FMA registered error callback
11031708Sstevel  */
11041708Sstevel /*ARGSUSED*/
11051708Sstevel static int
ssm_err_callback(dev_info_t * dip,ddi_fm_error_t * derr,const void * impl_data)11061708Sstevel ssm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data)
11071708Sstevel {
11081708Sstevel 	/* Call our children error handlers */
11091708Sstevel 	return (ndi_fm_handler_dispatch(dip, NULL, derr));
11101708Sstevel }
1111