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