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