ssm.c revision 7656:2621e50fdf4a
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 2008 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 */
69extern int plat_max_boards();
70
71static int
72ssm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result);
73
74static int
75ssm_attach(dev_info_t *, ddi_attach_cmd_t);
76
77static int
78ssm_detach(dev_info_t *, ddi_detach_cmd_t);
79
80static int
81ssm_open(dev_t *, int, int, cred_t *);
82
83static int
84ssm_close(dev_t, int, int, cred_t *);
85
86static int
87ssm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
88
89static int
90ssm_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *);
91
92static int
93ssm_make_nodes(dev_info_t *dip, int instance, int ssm_nodeid);
94
95static int
96ssm_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 */
103static int
104ssm_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 */
109static int
110ssm_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 */
117static void
118ssm_fm_init(struct ssm_soft_state *softsp);
119
120static void
121ssm_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 */
130int event_initialized = 0;
131uint_t ssm_dr_event_handler(char *);
132
133/*
134 * Event lock and state
135 */
136static kmutex_t ssm_event_lock;
137int ssm_event_state;
138
139/*
140 * DR event msg and payload
141 */
142static sbbc_msg_t event_msg;
143static sg_system_fru_descriptor_t payload;
144
145struct ssm_node2inst {
146	int	nodeid;		/* serengeti node #, NOT prom nodeid */
147	int	inst;
148	struct ssm_node2inst *next;
149};
150static kmutex_t ssm_node2inst_lock;
151static struct ssm_node2inst ssm_node2inst_map = {-1, -1, NULL};
152
153
154/*
155 * Configuration data structures
156 */
157static 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
189static 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
210static 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 */
228static void *ssm_softstates;		/* ssm soft state hook */
229
230extern struct mod_ops mod_driverops;
231
232static 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
238static struct modlinkage modlinkage = {
239	MODREV_1,		/* rev */
240	(void *)&modldrv,
241	NULL
242};
243
244static int ssm_loaded_sbd = FALSE;
245kmutex_t ssm_lock;
246static int init_child(dev_info_t *child);
247
248/*
249 * These are the module initialization routines.
250 */
251
252int
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
276int
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	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
298int
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 */
311static int
312ssm_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
330static int
331ssm_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, devi, 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*/
451static int
452ssm_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
529extern void make_ddi_ppd(dev_info_t *, struct ddi_parent_private_data **);
530extern struct ddi_parent_private_data *init_regspec_64(dev_info_t *);
531
532static int
533name_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
562static int
563init_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 */
590static int
591ssm_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*/
671static int
672ssm_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 */
702static int
703ssm_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 */
775static int
776ssm_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 */
796static int
797ssm_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			ssm_generate_event(softsp->ssm_nodeid, slot, hint);
892		}
893		break;
894	}
895	}
896
897	return (rv);
898}
899
900void
901ssm_get_attch_pnt(int node, int board, char *attach_pnt)
902{
903	struct ssm_node2inst	*sp;
904
905	/*
906	 * Hold this mutex, until we are done so that ssm dip
907	 * doesn't detach.
908	 */
909	mutex_enter(&ssm_node2inst_lock);
910
911	for (sp = &ssm_node2inst_map; sp != NULL; sp = sp->next) {
912		if (sp->inst == -1)
913			continue;
914		if (sp->nodeid == node)
915			break;
916	}
917
918	if (sp == NULL) {
919		/* We didn't find the ssm dip, return failure */
920		attach_pnt[0] = '\0';
921		mutex_exit(&ssm_node2inst_lock);
922		return;
923	}
924
925	/*
926	 * we have the instance, and the board, construct the attch pnt
927	 */
928	if (SG_BOARD_IS_CPU_TYPE(board))
929		(void) sprintf(attach_pnt, "ssm%d:N%d.SB%d",
930		    sp->inst, node, board);
931	else
932		(void) sprintf(attach_pnt, "ssm%d:N%d.IB%d",
933		    sp->inst, node, board);
934
935	mutex_exit(&ssm_node2inst_lock);
936}
937
938/*
939 * Generate an event to sysevent
940 */
941static int
942ssm_generate_event(int node, int board, int hint)
943{
944	sysevent_t			*ev;
945	sysevent_id_t			eid;
946	int				rv = 0;
947	sysevent_value_t		evnt_val;
948	sysevent_attr_list_t		*evnt_attr_list = NULL;
949	char				attach_pnt[MAXPATHLEN];
950
951
952	attach_pnt[0] = '\0';
953	ssm_get_attch_pnt(node, board, attach_pnt);
954
955	if (attach_pnt[0] == '\0')
956		return (-1);
957
958	ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, EP_DDI,
959	    KM_SLEEP);
960	evnt_val.value_type = SE_DATA_TYPE_STRING;
961	evnt_val.value.sv_string = attach_pnt;
962
963	rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val, KM_SLEEP);
964	if (rv != 0) {
965		cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
966		    DR_AP_ID, EC_DR);
967		sysevent_free(ev);
968		return (rv);
969	}
970
971	/*
972	 * Add the hint
973	 */
974	evnt_val.value_type = SE_DATA_TYPE_STRING;
975	evnt_val.value.sv_string = SE_HINT2STR(hint);
976
977	rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val, KM_SLEEP);
978	if (rv != 0) {
979		cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
980		    DR_HINT, EC_DR);
981		sysevent_free_attr(evnt_attr_list);
982		sysevent_free(ev);
983		return (-1);
984	}
985
986	if (sysevent_attach_attributes(ev, evnt_attr_list) != 0) {
987		cmn_err(CE_WARN, "Failed to attach attr list for %s event",
988		    EC_DR);
989		sysevent_free_attr(evnt_attr_list);
990		sysevent_free(ev);
991		return (-1);
992	}
993
994	rv = log_sysevent(ev, KM_NOSLEEP, &eid);
995	if (rv != 0) {
996		cmn_err(CE_WARN, "ssm_dr_event_handler: failed to log event");
997	}
998
999	sysevent_free(ev);
1000
1001	return (rv);
1002}
1003
1004/*
1005 * DR Event Handler
1006 */
1007uint_t
1008ssm_dr_event_handler(char *arg)
1009{
1010	sg_system_fru_descriptor_t	*fdp;
1011	int				hint;
1012
1013
1014	fdp = (sg_system_fru_descriptor_t *)(((sbbc_msg_t *)arg)->msg_buf);
1015	if (fdp == NULL) {
1016		DPRINTF(SSM_EVENT_DEBUG,
1017		    ("ssm_dr_event_handler: ARG is null\n"));
1018		return (DDI_INTR_CLAIMED);
1019	}
1020#ifdef DEBUG
1021	DPRINTF(SSM_EVENT_DEBUG, ("ssm_dr_event_handler called\n"));
1022	DPRINTF(SSM_EVENT_DEBUG, ("\tnode\t%d\n", fdp->node));
1023	DPRINTF(SSM_EVENT_DEBUG, ("\tslot\t%d\n", fdp->slot));
1024	DPRINTF(SSM_EVENT_DEBUG, ("\tparent_hdl\t0x%lx\n", fdp->parent_hdl));
1025	DPRINTF(SSM_EVENT_DEBUG, ("\tchild_hdl\t0x%lx\n", fdp->child_hdl));
1026	DPRINTF(SSM_EVENT_DEBUG, ("\tevent_details\t%s\n",
1027	    EVNT2STR(fdp->event_details)));
1028#endif
1029
1030	switch (fdp->event_details) {
1031	case SG_EVT_BOARD_ABSENT:
1032		hint = SE_HINT_REMOVE;
1033		break;
1034	case SG_EVT_BOARD_PRESENT:
1035		hint = SE_HINT_INSERT;
1036		break;
1037	default:
1038		hint = SE_NO_HINT;
1039		break;
1040
1041	}
1042
1043	ssm_generate_event(fdp->node, fdp->slot, hint);
1044
1045	return (DDI_INTR_CLAIMED);
1046}
1047
1048/*
1049 * Initialize our FMA resources
1050 */
1051static void
1052ssm_fm_init(struct ssm_soft_state *softsp)
1053{
1054	softsp->ssm_fm_cap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE |
1055	    DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
1056
1057	/*
1058	 * Request or capability level and get our parents capability
1059	 * and ibc.
1060	 */
1061	ddi_fm_init(softsp->dip, &softsp->ssm_fm_cap, &softsp->ssm_fm_ibc);
1062	ASSERT((softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE) &&
1063	    (softsp->ssm_fm_cap & DDI_FM_ERRCB_CAPABLE));
1064	/*
1065	 * Register error callback with our parent.
1066	 */
1067	ddi_fm_handler_register(softsp->dip, ssm_err_callback, NULL);
1068}
1069
1070/*
1071 * Breakdown our FMA resources
1072 */
1073static void
1074ssm_fm_fini(struct ssm_soft_state *softsp)
1075{
1076	/*
1077	 * Clean up allocated fm structures
1078	 */
1079	ASSERT(softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE);
1080	ddi_fm_handler_unregister(softsp->dip);
1081	ddi_fm_fini(softsp->dip);
1082}
1083
1084/*
1085 * Initialize FMA resources for children devices. Called when
1086 * child calls ddi_fm_init().
1087 */
1088/*ARGSUSED*/
1089static int
1090ssm_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap,
1091		ddi_iblock_cookie_t *ibc)
1092{
1093	struct ssm_soft_state *softsp = ddi_get_soft_state(ssm_softstates,
1094	    ddi_get_instance(dip));
1095
1096	*ibc = softsp->ssm_fm_ibc;
1097	return (softsp->ssm_fm_cap);
1098}
1099
1100/*
1101 * FMA registered error callback
1102 */
1103/*ARGSUSED*/
1104static int
1105ssm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data)
1106{
1107	/* Call our children error handlers */
1108	return (ndi_fm_handler_dispatch(dip, NULL, derr));
1109}
1110