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