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