xref: /illumos-gate/usr/src/uts/sun4u/serengeti/io/ssm.c (revision 447a706e)
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