103831d35Sstevel /*
203831d35Sstevel * CDDL HEADER START
303831d35Sstevel *
403831d35Sstevel * The contents of this file are subject to the terms of the
503831d35Sstevel * Common Development and Distribution License (the "License").
603831d35Sstevel * You may not use this file except in compliance with the License.
703831d35Sstevel *
803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel * See the License for the specific language governing permissions
1103831d35Sstevel * and limitations under the License.
1203831d35Sstevel *
1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel *
1903831d35Sstevel * CDDL HEADER END
2003831d35Sstevel */
2103831d35Sstevel
2203831d35Sstevel /*
2307d06da5SSurya Prakki * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2403831d35Sstevel * Use is subject to license terms.
2503831d35Sstevel */
26*cd21e7c5SGarrett D'Amore /*
27*cd21e7c5SGarrett D'Amore * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
28*cd21e7c5SGarrett D'Amore */
2903831d35Sstevel #include <sys/types.h>
3003831d35Sstevel #include <sys/conf.h>
3103831d35Sstevel #include <sys/ddi.h>
3203831d35Sstevel #include <sys/sunddi.h>
3303831d35Sstevel #include <sys/modctl.h>
3403831d35Sstevel #include <sys/sunndi.h>
3503831d35Sstevel #include <sys/ddi_impldefs.h>
3603831d35Sstevel #include <sys/obpdefs.h>
3703831d35Sstevel #include <sys/cmn_err.h>
3803831d35Sstevel #include <sys/errno.h>
3903831d35Sstevel #include <sys/kmem.h>
4003831d35Sstevel #include <sys/debug.h>
4103831d35Sstevel #include <sys/sysmacros.h>
4203831d35Sstevel #include <sys/autoconf.h>
4303831d35Sstevel #include <sys/stat.h>
4403831d35Sstevel #include <sys/serengeti.h>
4503831d35Sstevel #include <sys/ssm.h>
4603831d35Sstevel #include <sys/sgsbbc_mailbox.h>
4703831d35Sstevel #include <sys/sgevents.h>
4803831d35Sstevel #include <sys/sysevent.h>
4903831d35Sstevel #include <sys/sysevent/dr.h>
5003831d35Sstevel #include <sys/sysevent/eventdefs.h>
5103831d35Sstevel #include <sys/ndi_impldefs.h>
5203831d35Sstevel #include <sys/ddifm.h>
5303831d35Sstevel #include <sys/ndifm.h>
5403831d35Sstevel #include <sys/sbd_ioctl.h>
5503831d35Sstevel
5603831d35Sstevel /* Useful debugging Stuff */
5703831d35Sstevel #include <sys/nexusdebug.h>
5803831d35Sstevel
5903831d35Sstevel /*
6003831d35Sstevel * module ssm.c
6103831d35Sstevel *
6203831d35Sstevel * This module is a nexus driver designed to support the ssm nexus driver
6303831d35Sstevel * and all children below it. This driver does not handle any of the
6403831d35Sstevel * DDI functions passed up to it by the ssm driver, but instead allows
6503831d35Sstevel * them to bubble up to the root node.
6603831d35Sstevel */
6703831d35Sstevel
6803831d35Sstevel
6903831d35Sstevel /*
7003831d35Sstevel * Function prototypes
7103831d35Sstevel */
7203831d35Sstevel extern int plat_max_boards();
7303831d35Sstevel
7403831d35Sstevel static int
7503831d35Sstevel ssm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result);
7603831d35Sstevel
7703831d35Sstevel static int
7803831d35Sstevel ssm_attach(dev_info_t *, ddi_attach_cmd_t);
7903831d35Sstevel
8003831d35Sstevel static int
8103831d35Sstevel ssm_detach(dev_info_t *, ddi_detach_cmd_t);
8203831d35Sstevel
8303831d35Sstevel static int
8403831d35Sstevel ssm_open(dev_t *, int, int, cred_t *);
8503831d35Sstevel
8603831d35Sstevel static int
8703831d35Sstevel ssm_close(dev_t, int, int, cred_t *);
8803831d35Sstevel
8903831d35Sstevel static int
9003831d35Sstevel ssm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
9103831d35Sstevel
9203831d35Sstevel static int
9303831d35Sstevel ssm_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *);
9403831d35Sstevel
9503831d35Sstevel static int
9603831d35Sstevel ssm_make_nodes(dev_info_t *dip, int instance, int ssm_nodeid);
9703831d35Sstevel
9803831d35Sstevel static int
9903831d35Sstevel ssm_generate_event(int node, int board, int hint);
10003831d35Sstevel
10103831d35Sstevel /*
10203831d35Sstevel * FMA error callback
10303831d35Sstevel * Register error handling callback with our parent. We will just call
10403831d35Sstevel * our children's error callbacks and return their status.
10503831d35Sstevel */
10603831d35Sstevel static int
10703831d35Sstevel ssm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data);
10803831d35Sstevel
10903831d35Sstevel /*
11003831d35Sstevel * fm_init busop to initialize our children
11103831d35Sstevel */
11203831d35Sstevel static int
11303831d35Sstevel ssm_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap,
11403831d35Sstevel ddi_iblock_cookie_t *ibc);
11503831d35Sstevel
11603831d35Sstevel /*
11703831d35Sstevel * init/fini routines to alloc/dealloc fm structures and
11803831d35Sstevel * register/unregister our callback.
11903831d35Sstevel */
12003831d35Sstevel static void
12103831d35Sstevel ssm_fm_init(struct ssm_soft_state *softsp);
12203831d35Sstevel
12303831d35Sstevel static void
12403831d35Sstevel ssm_fm_fini(struct ssm_soft_state *softsp);
12503831d35Sstevel
12603831d35Sstevel /*
12703831d35Sstevel * DR event handlers
12803831d35Sstevel * We want to register the event handlers once for all instances. In the
12903831d35Sstevel * other hand we have register them after the sbbc has been attached.
13003831d35Sstevel * event_initialize gives us the logic of only registering the events only
13103831d35Sstevel * once
13203831d35Sstevel */
13303831d35Sstevel int event_initialized = 0;
13403831d35Sstevel uint_t ssm_dr_event_handler(char *);
13503831d35Sstevel
13603831d35Sstevel /*
13703831d35Sstevel * Event lock and state
13803831d35Sstevel */
13903831d35Sstevel static kmutex_t ssm_event_lock;
14003831d35Sstevel int ssm_event_state;
14103831d35Sstevel
14203831d35Sstevel /*
14303831d35Sstevel * DR event msg and payload
14403831d35Sstevel */
14503831d35Sstevel static sbbc_msg_t event_msg;
14603831d35Sstevel static sg_system_fru_descriptor_t payload;
14703831d35Sstevel
14803831d35Sstevel struct ssm_node2inst {
14903831d35Sstevel int nodeid; /* serengeti node #, NOT prom nodeid */
15003831d35Sstevel int inst;
15103831d35Sstevel struct ssm_node2inst *next;
15203831d35Sstevel };
15303831d35Sstevel static kmutex_t ssm_node2inst_lock;
15403831d35Sstevel static struct ssm_node2inst ssm_node2inst_map = {-1, -1, NULL};
15503831d35Sstevel
15603831d35Sstevel
15703831d35Sstevel /*
15803831d35Sstevel * Configuration data structures
15903831d35Sstevel */
16003831d35Sstevel static struct bus_ops ssm_bus_ops = {
16103831d35Sstevel BUSO_REV,
16203831d35Sstevel ddi_bus_map, /* map */
16303831d35Sstevel 0, /* get_intrspec */
16403831d35Sstevel 0, /* add_intrspec */
16503831d35Sstevel 0, /* remove_intrspec */
16603831d35Sstevel i_ddi_map_fault, /* map_fault */
167*cd21e7c5SGarrett D'Amore 0, /* dma_map */
16803831d35Sstevel ddi_dma_allochdl,
16903831d35Sstevel ddi_dma_freehdl,
17003831d35Sstevel ddi_dma_bindhdl,
17103831d35Sstevel ddi_dma_unbindhdl,
17203831d35Sstevel ddi_dma_flush,
17303831d35Sstevel ddi_dma_win,
17403831d35Sstevel ddi_dma_mctl, /* dma_ctl */
17503831d35Sstevel ssm_ctlops, /* ctl */
17603831d35Sstevel ddi_bus_prop_op, /* prop_op */
17703831d35Sstevel ndi_busop_get_eventcookie,
17803831d35Sstevel ndi_busop_add_eventcall,
17903831d35Sstevel ndi_busop_remove_eventcall,
18003831d35Sstevel ndi_post_event,
18103831d35Sstevel 0,
18203831d35Sstevel 0,
18303831d35Sstevel 0,
18403831d35Sstevel ssm_fm_init_child,
18503831d35Sstevel NULL,
18603831d35Sstevel NULL,
18703831d35Sstevel NULL,
18803831d35Sstevel 0,
18903831d35Sstevel i_ddi_intr_ops
19003831d35Sstevel };
19103831d35Sstevel
19203831d35Sstevel static struct cb_ops ssm_cb_ops = {
19303831d35Sstevel ssm_open, /* open */
19403831d35Sstevel ssm_close, /* close */
19503831d35Sstevel nodev, /* strategy */
19603831d35Sstevel nodev, /* print */
19703831d35Sstevel nodev, /* dump */
19803831d35Sstevel nodev, /* read */
19903831d35Sstevel nodev, /* write */
20003831d35Sstevel ssm_ioctl, /* ioctl */
20103831d35Sstevel nodev, /* devmap */
20203831d35Sstevel nodev, /* mmap */
20303831d35Sstevel nodev, /* segmap */
20403831d35Sstevel nochpoll, /* poll */
20503831d35Sstevel ddi_prop_op, /* cb_prop_op */
20603831d35Sstevel NULL, /* streamtab */
20703831d35Sstevel D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
20803831d35Sstevel CB_REV, /* rev */
20903831d35Sstevel nodev, /* int (*cb_aread)() */
21003831d35Sstevel nodev /* int (*cb_awrite)() */
21103831d35Sstevel };
21203831d35Sstevel
21303831d35Sstevel static struct dev_ops ssm_ops = {
21403831d35Sstevel DEVO_REV, /* devo_rev, */
21503831d35Sstevel 0, /* refcnt */
21603831d35Sstevel ssm_info, /* getinfo */
21703831d35Sstevel nulldev, /* identify */
21803831d35Sstevel nulldev, /* probe */
21903831d35Sstevel ssm_attach, /* attach */
22003831d35Sstevel ssm_detach, /* detach */
22103831d35Sstevel nulldev, /* reset */
22203831d35Sstevel &ssm_cb_ops, /* driver operations */
22303831d35Sstevel &ssm_bus_ops, /* bus_ops */
22419397407SSherry Moore nulldev, /* power */
22519397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */
22603831d35Sstevel };
22703831d35Sstevel
22803831d35Sstevel /*
22903831d35Sstevel * Driver globals
23003831d35Sstevel */
23103831d35Sstevel static void *ssm_softstates; /* ssm soft state hook */
23203831d35Sstevel
23303831d35Sstevel extern struct mod_ops mod_driverops;
23403831d35Sstevel
23503831d35Sstevel static struct modldrv modldrv = {
23603831d35Sstevel &mod_driverops, /* Type of module. This one is a driver */
23719397407SSherry Moore "SSM Nexus", /* name of module */
23803831d35Sstevel &ssm_ops, /* driver ops */
23903831d35Sstevel };
24003831d35Sstevel
24103831d35Sstevel static struct modlinkage modlinkage = {
24203831d35Sstevel MODREV_1, /* rev */
24303831d35Sstevel (void *)&modldrv,
24403831d35Sstevel NULL
24503831d35Sstevel };
24603831d35Sstevel
24703831d35Sstevel static int ssm_loaded_sbd = FALSE;
24803831d35Sstevel kmutex_t ssm_lock;
24903831d35Sstevel static int init_child(dev_info_t *child);
25003831d35Sstevel
25103831d35Sstevel /*
25203831d35Sstevel * These are the module initialization routines.
25303831d35Sstevel */
25403831d35Sstevel
25503831d35Sstevel int
_init(void)25603831d35Sstevel _init(void)
25703831d35Sstevel {
25803831d35Sstevel int error;
25903831d35Sstevel
26003831d35Sstevel #if defined(DEBUG)
26103831d35Sstevel debug_print_level = 0x0;
26203831d35Sstevel #endif
26303831d35Sstevel
26403831d35Sstevel /* Initialize soft state pointer. */
26503831d35Sstevel if ((error = ddi_soft_state_init(&ssm_softstates,
266447a706eSVijay Balakrishna, SG-RPE sizeof (struct ssm_soft_state), SSM_MAX_INSTANCES)) != 0)
26703831d35Sstevel return (error);
26803831d35Sstevel
26903831d35Sstevel /* Install the module. */
27003831d35Sstevel error = mod_install(&modlinkage);
27103831d35Sstevel if (error != 0)
27203831d35Sstevel ddi_soft_state_fini(&ssm_softstates);
27303831d35Sstevel
27403831d35Sstevel mutex_init(&ssm_lock, NULL, MUTEX_DRIVER, NULL);
27503831d35Sstevel
27603831d35Sstevel return (error);
27703831d35Sstevel }
27803831d35Sstevel
27903831d35Sstevel int
_fini(void)28003831d35Sstevel _fini(void)
28103831d35Sstevel {
28203831d35Sstevel int error;
28303831d35Sstevel
28403831d35Sstevel /* Remove the module. */
28503831d35Sstevel if ((error = mod_remove(&modlinkage)) != 0)
28603831d35Sstevel return (error);
28703831d35Sstevel
28803831d35Sstevel /*
28903831d35Sstevel * Unregister the event handler
29003831d35Sstevel */
29107d06da5SSurya Prakki (void) sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC, ssm_dr_event_handler);
29203831d35Sstevel mutex_destroy(&ssm_event_lock);
29303831d35Sstevel
29403831d35Sstevel /* Free the soft state info. */
29503831d35Sstevel ddi_soft_state_fini(&ssm_softstates);
29603831d35Sstevel mutex_destroy(&ssm_lock);
29703831d35Sstevel
29803831d35Sstevel return (0);
29903831d35Sstevel }
30003831d35Sstevel
30103831d35Sstevel int
_info(struct modinfo * modinfop)30203831d35Sstevel _info(struct modinfo *modinfop)
30303831d35Sstevel {
30403831d35Sstevel return (mod_info(&modlinkage, modinfop));
30503831d35Sstevel }
30603831d35Sstevel
30703831d35Sstevel /* device driver entry points */
30803831d35Sstevel
30903831d35Sstevel /*
31003831d35Sstevel * info entry point:
31103831d35Sstevel */
31203831d35Sstevel
31303831d35Sstevel /* ARGSUSED */
31403831d35Sstevel static int
ssm_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)31503831d35Sstevel ssm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
31603831d35Sstevel {
31703831d35Sstevel dev_t dev;
31803831d35Sstevel int instance;
31903831d35Sstevel
32003831d35Sstevel if (infocmd == DDI_INFO_DEVT2INSTANCE) {
32103831d35Sstevel dev = (dev_t)arg;
32203831d35Sstevel instance = (getminor(dev) >> SSM_INSTANCE_SHIFT);
32303831d35Sstevel *result = (void *)(uintptr_t)instance;
32403831d35Sstevel return (DDI_SUCCESS);
32503831d35Sstevel }
32603831d35Sstevel return (DDI_FAILURE);
32703831d35Sstevel }
32803831d35Sstevel
32903831d35Sstevel /*
33003831d35Sstevel * attach entry point:
33103831d35Sstevel */
33203831d35Sstevel
33303831d35Sstevel static int
ssm_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)33403831d35Sstevel ssm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
33503831d35Sstevel {
33603831d35Sstevel int instance;
33703831d35Sstevel struct ssm_soft_state *softsp;
33803831d35Sstevel struct ssm_node2inst *prev, *sp, *tsp;
33903831d35Sstevel
34003831d35Sstevel DPRINTF(SSM_ATTACH_DEBUG, ("ssm_attach\n"));
34103831d35Sstevel
34203831d35Sstevel switch (cmd) {
34303831d35Sstevel case DDI_ATTACH:
34403831d35Sstevel break;
34503831d35Sstevel
34603831d35Sstevel case DDI_RESUME:
34703831d35Sstevel return (DDI_SUCCESS);
34803831d35Sstevel
34903831d35Sstevel default:
35003831d35Sstevel return (DDI_FAILURE);
35103831d35Sstevel }
35203831d35Sstevel
35303831d35Sstevel instance = ddi_get_instance(devi);
35403831d35Sstevel
35503831d35Sstevel if (ddi_soft_state_zalloc(ssm_softstates, instance) != DDI_SUCCESS)
35603831d35Sstevel return (DDI_FAILURE);
35703831d35Sstevel
35803831d35Sstevel softsp = ddi_get_soft_state(ssm_softstates, instance);
35903831d35Sstevel
36003831d35Sstevel /* Set the dip in the soft state */
36103831d35Sstevel softsp->dip = devi;
36203831d35Sstevel softsp->top_node = devi;
36303831d35Sstevel mutex_init(&softsp->ssm_sft_lock, NULL, MUTEX_DRIVER, NULL);
36403831d35Sstevel
36503831d35Sstevel DPRINTF(SSM_ATTACH_DEBUG, ("ssm-%d: devi= 0x%p, softsp=0x%p\n",
36607d06da5SSurya Prakki instance, (void *)devi, (void *)softsp));
36703831d35Sstevel
36803831d35Sstevel if ((softsp->ssm_nodeid = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->dip,
36903831d35Sstevel DDI_PROP_DONTPASS, "nodeid", -1)) == -1) {
37003831d35Sstevel cmn_err(CE_WARN, "ssm%d: unable to retrieve %s property",
37103831d35Sstevel instance, "nodeid");
37203831d35Sstevel ddi_soft_state_free(ssm_softstates, instance);
37303831d35Sstevel return (DDI_FAILURE);
37403831d35Sstevel }
37503831d35Sstevel
37603831d35Sstevel /* nothing to suspend/resume here */
37703831d35Sstevel (void) ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP,
37803831d35Sstevel "pm-hardware-state", (caddr_t)"no-suspend-resume",
37903831d35Sstevel strlen("no-suspend-resume") + 1);
38003831d35Sstevel
38103831d35Sstevel #if DEBUG
38203831d35Sstevel if (ddi_create_minor_node(devi, "debug", S_IFCHR, instance,
38303831d35Sstevel DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
38403831d35Sstevel ddi_soft_state_free(ssm_softstates, instance);
38503831d35Sstevel return (DDI_FAILURE);
38603831d35Sstevel }
38703831d35Sstevel #endif
38803831d35Sstevel
38903831d35Sstevel if (ssm_make_nodes(devi, instance, softsp->ssm_nodeid)) {
390447a706eSVijay Balakrishna, SG-RPE cmn_err(CE_WARN, "ssm:%s:%d: failed to make nodes",
39103831d35Sstevel ddi_driver_name(devi), instance);
39203831d35Sstevel ddi_remove_minor_node(devi, NULL);
39303831d35Sstevel ddi_soft_state_free(ssm_softstates, instance);
39403831d35Sstevel return (DDI_FAILURE);
39503831d35Sstevel }
39603831d35Sstevel ssm_fm_init(softsp);
39703831d35Sstevel ddi_report_dev(devi);
39803831d35Sstevel
39903831d35Sstevel if (event_initialized == 0) {
40003831d35Sstevel int rv;
40103831d35Sstevel /*
40203831d35Sstevel * Register DR event handler
40303831d35Sstevel */
40403831d35Sstevel mutex_init(&ssm_event_lock, NULL, MUTEX_DRIVER, NULL);
40503831d35Sstevel event_msg.msg_buf = (caddr_t)&payload;
40603831d35Sstevel event_msg.msg_len = sizeof (payload);
40703831d35Sstevel
40803831d35Sstevel rv = sbbc_mbox_reg_intr(MBOX_EVENT_GENERIC,
40903831d35Sstevel ssm_dr_event_handler, &event_msg,
41003831d35Sstevel (uint_t *)&ssm_event_state, &ssm_event_lock);
41103831d35Sstevel
41203831d35Sstevel if (rv == EINVAL)
41303831d35Sstevel event_initialized = 1;
41403831d35Sstevel }
41503831d35Sstevel
41603831d35Sstevel /*
41703831d35Sstevel * Preallocate to avoid sleeping with ssm_node2inst_lock held -
41803831d35Sstevel * low level interrupts use this mutex.
41903831d35Sstevel */
42003831d35Sstevel tsp = kmem_zalloc(sizeof (struct ssm_node2inst), KM_SLEEP);
42103831d35Sstevel
42203831d35Sstevel mutex_enter(&ssm_node2inst_lock);
42303831d35Sstevel
42403831d35Sstevel for (prev = NULL, sp = &ssm_node2inst_map; sp != NULL;
42503831d35Sstevel prev = sp, sp = sp->next) {
42603831d35Sstevel ASSERT(sp->inst != instance);
42703831d35Sstevel ASSERT(sp->nodeid != softsp->ssm_nodeid);
42803831d35Sstevel if (sp->inst == -1)
42903831d35Sstevel break;
43003831d35Sstevel }
43103831d35Sstevel
43203831d35Sstevel if (sp == NULL) {
43303831d35Sstevel ASSERT(prev->next == NULL);
43403831d35Sstevel sp = prev->next = tsp;
43503831d35Sstevel tsp = NULL;
43603831d35Sstevel sp->next = NULL;
43703831d35Sstevel }
43803831d35Sstevel
43903831d35Sstevel sp->inst = instance;
44003831d35Sstevel sp->nodeid = softsp->ssm_nodeid;
44103831d35Sstevel
44203831d35Sstevel mutex_exit(&ssm_node2inst_lock);
44303831d35Sstevel
44403831d35Sstevel if (tsp != NULL)
44503831d35Sstevel kmem_free(tsp, sizeof (struct ssm_node2inst));
44603831d35Sstevel
44703831d35Sstevel return (DDI_SUCCESS);
44803831d35Sstevel }
44903831d35Sstevel
45003831d35Sstevel /*
45103831d35Sstevel * detach entry point:
45203831d35Sstevel */
45303831d35Sstevel /*ARGSUSED*/
45403831d35Sstevel static int
ssm_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)45503831d35Sstevel ssm_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
45603831d35Sstevel {
45703831d35Sstevel int instance, rv;
45803831d35Sstevel int (*sbd_teardown_instance) (int, caddr_t);
45903831d35Sstevel ssm_sbdp_info_t sbdp_info;
46003831d35Sstevel struct ssm_soft_state *softsp;
46103831d35Sstevel struct ssm_node2inst *prev, *sp;
46203831d35Sstevel
46303831d35Sstevel instance = ddi_get_instance(devi);
46403831d35Sstevel softsp = ddi_get_soft_state(ssm_softstates, instance);
46503831d35Sstevel
46603831d35Sstevel if (softsp == NULL) {
46703831d35Sstevel cmn_err(CE_WARN,
46803831d35Sstevel "ssm_open bad instance number %d", instance);
46903831d35Sstevel return (ENXIO);
47003831d35Sstevel }
47103831d35Sstevel
47203831d35Sstevel instance = ddi_get_instance(devi);
47303831d35Sstevel
47403831d35Sstevel switch (cmd) {
47503831d35Sstevel case DDI_DETACH:
47603831d35Sstevel ddi_remove_minor_node(devi, NULL);
47703831d35Sstevel
47803831d35Sstevel sbd_teardown_instance = (int (*) (int, caddr_t))
479447a706eSVijay Balakrishna, SG-RPE modlookup("misc/sbd", "sbd_teardown_instance");
48003831d35Sstevel
48103831d35Sstevel if (!sbd_teardown_instance) {
48203831d35Sstevel cmn_err(CE_WARN, "cannot find sbd_teardown_instance");
48303831d35Sstevel return (DDI_FAILURE);
48403831d35Sstevel }
48503831d35Sstevel
48603831d35Sstevel sbdp_info.instance = instance;
48703831d35Sstevel sbdp_info.wnode = softsp->ssm_nodeid;
48803831d35Sstevel rv = (*sbd_teardown_instance)(instance, (caddr_t)&sbdp_info);
48903831d35Sstevel
49003831d35Sstevel if (rv != DDI_SUCCESS) {
49103831d35Sstevel cmn_err(CE_WARN, "cannot run sbd_teardown_instance");
49203831d35Sstevel return (DDI_FAILURE);
49303831d35Sstevel }
49403831d35Sstevel ssm_fm_fini(softsp);
49503831d35Sstevel mutex_destroy(&softsp->ssm_sft_lock);
49603831d35Sstevel ddi_soft_state_free(ssm_softstates, instance);
49703831d35Sstevel
49803831d35Sstevel mutex_enter(&ssm_node2inst_lock);
49903831d35Sstevel for (prev = NULL, sp = &ssm_node2inst_map; sp != NULL;
50003831d35Sstevel prev = sp, sp = sp->next) {
50103831d35Sstevel /* Only the head of the list can persist if unused */
50203831d35Sstevel ASSERT(prev == NULL || sp->inst != -1);
50303831d35Sstevel if (sp->inst == instance)
50403831d35Sstevel break;
50503831d35Sstevel }
50603831d35Sstevel ASSERT(sp != NULL);
50703831d35Sstevel
50803831d35Sstevel if (sp != &ssm_node2inst_map) {
50903831d35Sstevel prev->next = sp->next;
51003831d35Sstevel kmem_free(sp, sizeof (struct ssm_node2inst));
51103831d35Sstevel } else {
51203831d35Sstevel /*
51303831d35Sstevel * Invalidate the head element, but retain the rest
51403831d35Sstevel * of the list - "next" is still valid.
51503831d35Sstevel */
51603831d35Sstevel
51703831d35Sstevel sp->nodeid = -1;
51803831d35Sstevel sp->inst = -1;
51903831d35Sstevel }
52003831d35Sstevel mutex_exit(&ssm_node2inst_lock);
52103831d35Sstevel
52203831d35Sstevel return (DDI_SUCCESS);
52303831d35Sstevel
52403831d35Sstevel case DDI_SUSPEND:
52503831d35Sstevel return (DDI_SUCCESS);
52603831d35Sstevel
52703831d35Sstevel default:
52803831d35Sstevel return (DDI_FAILURE);
52903831d35Sstevel }
53003831d35Sstevel }
53103831d35Sstevel
53203831d35Sstevel extern void make_ddi_ppd(dev_info_t *, struct ddi_parent_private_data **);
53303831d35Sstevel extern struct ddi_parent_private_data *init_regspec_64(dev_info_t *);
53403831d35Sstevel
53503831d35Sstevel static int
name_child(dev_info_t * child,char * name,int namelen)53603831d35Sstevel name_child(dev_info_t *child, char *name, int namelen)
53703831d35Sstevel {
53803831d35Sstevel struct regspec *rp;
53903831d35Sstevel struct ddi_parent_private_data *pdptr;
54003831d35Sstevel int portid = 0;
54103831d35Sstevel int regbase = -1;
54203831d35Sstevel extern uint_t root_phys_addr_lo_mask;
54303831d35Sstevel
54403831d35Sstevel make_ddi_ppd(child, &pdptr);
54503831d35Sstevel ddi_set_parent_data(child, pdptr);
54603831d35Sstevel
54703831d35Sstevel name[0] = '\0';
54803831d35Sstevel if (sparc_pd_getnreg(child) == 0)
54903831d35Sstevel return (DDI_SUCCESS);
55003831d35Sstevel
55103831d35Sstevel rp = sparc_pd_getreg(child, 0);
55203831d35Sstevel
55303831d35Sstevel portid = ddi_prop_get_int(DDI_DEV_T_ANY, child,
55403831d35Sstevel DDI_PROP_DONTPASS, "portid", -1);
55503831d35Sstevel if (portid == -1) {
55603831d35Sstevel cmn_err(CE_WARN, "could not find portid property in %s",
55703831d35Sstevel DEVI(child)->devi_node_name);
55803831d35Sstevel } else {
55903831d35Sstevel regbase = rp->regspec_addr & root_phys_addr_lo_mask;
56003831d35Sstevel }
56103831d35Sstevel (void) snprintf(name, namelen, "%x,%x", portid, regbase);
56203831d35Sstevel return (DDI_SUCCESS);
56303831d35Sstevel }
56403831d35Sstevel
56503831d35Sstevel static int
init_child(dev_info_t * child)56603831d35Sstevel init_child(dev_info_t *child)
56703831d35Sstevel {
56803831d35Sstevel char name[MAXNAMELEN];
56903831d35Sstevel
57003831d35Sstevel (void) name_child(child, name, MAXNAMELEN);
57103831d35Sstevel ddi_set_name_addr(child, name);
57203831d35Sstevel if ((ndi_dev_is_persistent_node(child) == 0) &&
57303831d35Sstevel (ndi_merge_node(child, name_child) == DDI_SUCCESS)) {
57403831d35Sstevel impl_ddi_sunbus_removechild(child);
57503831d35Sstevel return (DDI_FAILURE);
57603831d35Sstevel }
57703831d35Sstevel
57803831d35Sstevel (void) init_regspec_64(child);
57903831d35Sstevel return (DDI_SUCCESS);
58003831d35Sstevel }
58103831d35Sstevel
58203831d35Sstevel /*
58303831d35Sstevel * Control ops entry point:
58403831d35Sstevel *
58503831d35Sstevel * Requests handled completely:
58603831d35Sstevel * DDI_CTLOPS_INITCHILD
58703831d35Sstevel * DDI_CTLOPS_UNINITCHILD
58803831d35Sstevel * DDI_CTLOPS_REPORTDEV
58903831d35Sstevel * All others are passed to the parent.
59003831d35Sstevel * The name of the ssm node is ssm@nodeid,0.
59103831d35Sstevel * ssm is the equivalent of rootnex.
59203831d35Sstevel */
59303831d35Sstevel static int
ssm_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)59403831d35Sstevel ssm_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, void *arg,
59503831d35Sstevel void *result)
59603831d35Sstevel {
59703831d35Sstevel int rval;
59803831d35Sstevel
59903831d35Sstevel switch (op) {
60003831d35Sstevel case DDI_CTLOPS_INITCHILD: {
60103831d35Sstevel DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_INITCHILD\n"));
60203831d35Sstevel return (init_child((dev_info_t *)arg));
60303831d35Sstevel }
60403831d35Sstevel
60503831d35Sstevel case DDI_CTLOPS_UNINITCHILD: {
60603831d35Sstevel DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_UNINITCHILD\n"));
60703831d35Sstevel impl_ddi_sunbus_removechild((dev_info_t *)arg);
60803831d35Sstevel return (DDI_SUCCESS);
60903831d35Sstevel }
61003831d35Sstevel
61103831d35Sstevel case DDI_CTLOPS_REPORTDEV: {
61203831d35Sstevel char buf[80];
61303831d35Sstevel char *p = buf;
61403831d35Sstevel dev_info_t *parent;
61503831d35Sstevel int portid;
61603831d35Sstevel
61703831d35Sstevel DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_REPORTDEV\n"));
61803831d35Sstevel parent = ddi_get_parent(rdip);
61903831d35Sstevel
62003831d35Sstevel (void) sprintf(p, "%s%d at %s%d", DEVI(rdip)->devi_name,
62103831d35Sstevel DEVI(rdip)->devi_instance, ddi_get_name(parent),
62203831d35Sstevel ddi_get_instance(parent));
62303831d35Sstevel p += strlen(p);
62403831d35Sstevel
62503831d35Sstevel /* Fetch Safari Extended Agent ID of this device. */
62603831d35Sstevel portid = (int)ddi_getprop(DDI_DEV_T_ANY, rdip,
62703831d35Sstevel DDI_PROP_DONTPASS, "portid", -1);
62803831d35Sstevel
62903831d35Sstevel /*
63003831d35Sstevel * If this is one of the ssm children it will have
63103831d35Sstevel * portid property and its parent will be ssm.
63203831d35Sstevel * In this case report Node number and Safari id.
63303831d35Sstevel */
63403831d35Sstevel if (portid != -1 &&
63503831d35Sstevel strcmp("ssm", ddi_get_name(parent)) == 0) {
63603831d35Sstevel struct regspec *rp;
63703831d35Sstevel int node;
63803831d35Sstevel int safid;
63903831d35Sstevel int n;
64003831d35Sstevel
64103831d35Sstevel rp = sparc_pd_getreg(rdip, 0);
64203831d35Sstevel n = sparc_pd_getnreg(rdip);
64303831d35Sstevel ASSERT(n > 0);
64403831d35Sstevel
64503831d35Sstevel node = SG_PORTID_TO_NODEID(portid);
64603831d35Sstevel safid = SG_PORTID_TO_SAFARI_ID(portid);
64703831d35Sstevel
64803831d35Sstevel (void) strcpy(p, ": ");
64903831d35Sstevel p += strlen(p);
65003831d35Sstevel
65103831d35Sstevel (void) sprintf(p, "Node %d Safari id %d 0x%x%s",
65203831d35Sstevel node, safid,
65303831d35Sstevel rp->regspec_addr,
65403831d35Sstevel (n > 1 ? "" : " ..."));
65503831d35Sstevel p += strlen(p);
65603831d35Sstevel }
65703831d35Sstevel
65803831d35Sstevel cmn_err(CE_CONT, "?%s\n", buf);
65903831d35Sstevel rval = DDI_SUCCESS;
66003831d35Sstevel
66103831d35Sstevel break;
66203831d35Sstevel }
66303831d35Sstevel
66403831d35Sstevel default:
66503831d35Sstevel rval = ddi_ctlops(dip, rdip, op, arg, result);
66603831d35Sstevel
66703831d35Sstevel break;
66803831d35Sstevel }
66903831d35Sstevel
67003831d35Sstevel return (rval);
67103831d35Sstevel }
67203831d35Sstevel
67303831d35Sstevel /*ARGSUSED*/
67403831d35Sstevel static int
ssm_make_nodes(dev_info_t * dip,int instance,int ssm_nodeid)67503831d35Sstevel ssm_make_nodes(dev_info_t *dip, int instance, int ssm_nodeid)
67603831d35Sstevel {
67703831d35Sstevel int rv;
67803831d35Sstevel minor_t minor_num, bd;
67903831d35Sstevel auto char filename[20];
68003831d35Sstevel
68103831d35Sstevel for (bd = 0; bd < plat_max_boards(); bd++) {
68203831d35Sstevel if (SG_BOARD_IS_CPU_TYPE(bd))
68303831d35Sstevel (void) sprintf(filename, "N%d.SB%d", ssm_nodeid, bd);
68403831d35Sstevel else
68503831d35Sstevel (void) sprintf(filename, "N%d.IB%d", ssm_nodeid, bd);
68603831d35Sstevel
68703831d35Sstevel minor_num = (instance << SSM_INSTANCE_SHIFT) | bd;
68803831d35Sstevel
68903831d35Sstevel rv = ddi_create_minor_node(dip, filename, S_IFCHR,
690447a706eSVijay Balakrishna, SG-RPE minor_num, DDI_NT_SBD_ATTACHMENT_POINT, NULL);
69103831d35Sstevel if (rv == DDI_FAILURE) {
69203831d35Sstevel cmn_err(CE_WARN,
69303831d35Sstevel "ssm_make_nodes:%d: failed to create "
69403831d35Sstevel "minor node (%s, 0x%x)",
69503831d35Sstevel instance, filename, minor_num);
69603831d35Sstevel return (-1);
69703831d35Sstevel }
69803831d35Sstevel }
69903831d35Sstevel
70003831d35Sstevel return (0);
70103831d35Sstevel }
70203831d35Sstevel
70303831d35Sstevel
70403831d35Sstevel /* ARGSUSED */
70503831d35Sstevel static int
ssm_open(dev_t * devi,int flags,int otyp,cred_t * credp)70603831d35Sstevel ssm_open(dev_t *devi, int flags, int otyp, cred_t *credp)
70703831d35Sstevel {
70803831d35Sstevel struct ssm_soft_state *softsp;
70903831d35Sstevel minor_t board, instance;
71003831d35Sstevel int (*sbd_setup_instance)(int, dev_info_t *, int, int, caddr_t);
71103831d35Sstevel ssm_sbdp_info_t sbdp_info;
71203831d35Sstevel int rv;
71303831d35Sstevel
71403831d35Sstevel instance = (getminor(*devi) >> SSM_INSTANCE_SHIFT);
71503831d35Sstevel
71603831d35Sstevel softsp = ddi_get_soft_state(ssm_softstates, instance);
71703831d35Sstevel if (softsp == NULL) {
71803831d35Sstevel cmn_err(CE_WARN, "ssm_open bad instance number %d", instance);
71903831d35Sstevel return (ENXIO);
72003831d35Sstevel }
72103831d35Sstevel
72203831d35Sstevel board = (getminor(*devi) & SSM_BOARD_MASK);
72303831d35Sstevel
72403831d35Sstevel if (board < 0 || board > plat_max_boards()) {
72503831d35Sstevel return (ENXIO);
72603831d35Sstevel }
72703831d35Sstevel
72803831d35Sstevel mutex_enter(&ssm_lock);
72903831d35Sstevel if (instance == 0 && ssm_loaded_sbd == FALSE) {
73003831d35Sstevel
73103831d35Sstevel if (modload("misc", "sbd") == -1) {
73203831d35Sstevel cmn_err(CE_WARN, "ssm_open: cannot load sbd");
73303831d35Sstevel mutex_exit(&ssm_lock);
73403831d35Sstevel return (EIO);
73503831d35Sstevel }
73603831d35Sstevel ssm_loaded_sbd = TRUE;
73703831d35Sstevel }
73803831d35Sstevel mutex_exit(&ssm_lock);
73903831d35Sstevel
74003831d35Sstevel mutex_enter(&softsp->ssm_sft_lock);
74103831d35Sstevel if (softsp->initialized == FALSE) {
74203831d35Sstevel
74303831d35Sstevel if (softsp->top_node == NULL) {
74403831d35Sstevel cmn_err(CE_WARN, "cannot find ssm top dnode");
74503831d35Sstevel mutex_exit(&softsp->ssm_sft_lock);
74603831d35Sstevel return (EIO);
74703831d35Sstevel }
74803831d35Sstevel
74903831d35Sstevel sbd_setup_instance = (int (*)(int, dev_info_t *, int, int,
750447a706eSVijay Balakrishna, SG-RPE caddr_t))modlookup("misc/sbd", "sbd_setup_instance");
75103831d35Sstevel
75203831d35Sstevel if (!sbd_setup_instance) {
75303831d35Sstevel cmn_err(CE_WARN, "cannot find sbd_setup_instance");
75403831d35Sstevel mutex_exit(&softsp->ssm_sft_lock);
75503831d35Sstevel return (EIO);
75603831d35Sstevel }
75703831d35Sstevel
75803831d35Sstevel sbdp_info.instance = instance;
75903831d35Sstevel sbdp_info.wnode = softsp->ssm_nodeid;
76003831d35Sstevel
76103831d35Sstevel rv = (*sbd_setup_instance)(instance, softsp->top_node,
76203831d35Sstevel plat_max_boards(), softsp->ssm_nodeid,
76303831d35Sstevel (caddr_t)&sbdp_info);
76403831d35Sstevel if (rv != DDI_SUCCESS) {
76503831d35Sstevel cmn_err(CE_WARN, "cannot run sbd_setup_instance");
76603831d35Sstevel mutex_exit(&softsp->ssm_sft_lock);
76703831d35Sstevel return (EIO);
76803831d35Sstevel }
76903831d35Sstevel softsp->initialized = TRUE;
77003831d35Sstevel }
77103831d35Sstevel mutex_exit(&softsp->ssm_sft_lock);
77203831d35Sstevel
77303831d35Sstevel return (DDI_SUCCESS);
77403831d35Sstevel }
77503831d35Sstevel
77603831d35Sstevel
77703831d35Sstevel /* ARGSUSED */
77803831d35Sstevel static int
ssm_close(dev_t dev,int flags,int otyp,cred_t * credp)77903831d35Sstevel ssm_close(dev_t dev, int flags, int otyp, cred_t *credp)
78003831d35Sstevel {
78103831d35Sstevel struct ssm_soft_state *softsp;
78203831d35Sstevel minor_t board, instance;
78303831d35Sstevel
78403831d35Sstevel instance = (getminor(dev) >> SSM_INSTANCE_SHIFT);
78503831d35Sstevel
78603831d35Sstevel softsp = ddi_get_soft_state(ssm_softstates, instance);
78703831d35Sstevel if (softsp == NULL)
78803831d35Sstevel return (ENXIO);
78903831d35Sstevel
79003831d35Sstevel board = (getminor(dev) & SSM_BOARD_MASK);
79103831d35Sstevel
79203831d35Sstevel if (board < 0 || board > plat_max_boards())
79303831d35Sstevel return (ENXIO);
79403831d35Sstevel
79503831d35Sstevel return (DDI_SUCCESS);
79603831d35Sstevel }
79703831d35Sstevel
79803831d35Sstevel /* ARGSUSED */
79903831d35Sstevel static int
ssm_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)80003831d35Sstevel ssm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
80103831d35Sstevel int *rvalp)
80203831d35Sstevel {
80303831d35Sstevel struct ssm_soft_state *softsp;
80403831d35Sstevel char *addr;
80503831d35Sstevel struct devctl_iocdata *dcp;
80603831d35Sstevel int instance, rv = 0;
80703831d35Sstevel int (*sbd_ioctl) (dev_t, int, intptr_t, int, char *);
80803831d35Sstevel
80903831d35Sstevel instance = (getminor(dev) >> SSM_INSTANCE_SHIFT);
81003831d35Sstevel softsp = ddi_get_soft_state(ssm_softstates, instance);
81103831d35Sstevel if (softsp == NULL)
81203831d35Sstevel return (ENXIO);
81303831d35Sstevel
81403831d35Sstevel switch (cmd) {
81503831d35Sstevel
81603831d35Sstevel case DEVCTL_BUS_CONFIGURE:
81703831d35Sstevel /*
81803831d35Sstevel * read devctl ioctl data
81903831d35Sstevel */
82003831d35Sstevel if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
82103831d35Sstevel return (EFAULT);
82203831d35Sstevel
82303831d35Sstevel addr = ndi_dc_getaddr(dcp);
82403831d35Sstevel cmn_err(CE_NOTE,
82503831d35Sstevel "DEVCTL_BUS_CONFIGURE: device id is %s\n", addr);
82603831d35Sstevel ndi_dc_freehdl(dcp);
82703831d35Sstevel break;
82803831d35Sstevel
82903831d35Sstevel case DEVCTL_BUS_UNCONFIGURE:
83003831d35Sstevel if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
83103831d35Sstevel return (EFAULT);
83203831d35Sstevel
83303831d35Sstevel addr = ndi_dc_getaddr(dcp);
83403831d35Sstevel cmn_err(CE_NOTE,
83503831d35Sstevel "DEVCTL_BUS_UNCONFIGURE: device id is %s\n", addr);
83603831d35Sstevel ndi_dc_freehdl(dcp);
83703831d35Sstevel break;
83803831d35Sstevel
83903831d35Sstevel #ifdef DEBUG
84003831d35Sstevel case SSM_TEARDOWN_SBD: {
84103831d35Sstevel ssm_sbdp_info_t sbdp_info;
84203831d35Sstevel int (*sbd_teardown_instance) (int, caddr_t);
84303831d35Sstevel sbd_teardown_instance = (int (*) (int, caddr_t))
844447a706eSVijay Balakrishna, SG-RPE modlookup("misc/sbd", "sbd_teardown_instance");
84503831d35Sstevel
84603831d35Sstevel if (!sbd_teardown_instance) {
84703831d35Sstevel cmn_err(CE_WARN, "cannot find sbd_teardown_instance");
84803831d35Sstevel return (EFAULT);
84903831d35Sstevel }
85003831d35Sstevel
85103831d35Sstevel sbdp_info.instance = instance;
85203831d35Sstevel sbdp_info.wnode = softsp->ssm_nodeid;
85303831d35Sstevel rv = (*sbd_teardown_instance)(instance, (caddr_t)&sbdp_info);
85403831d35Sstevel if (rv != DDI_SUCCESS) {
85503831d35Sstevel cmn_err(CE_WARN, "cannot run sbd_teardown_instance");
85603831d35Sstevel return (EFAULT);
85703831d35Sstevel }
85803831d35Sstevel
85903831d35Sstevel ssm_loaded_sbd = FALSE;
86003831d35Sstevel softsp->initialized = FALSE;
86103831d35Sstevel }
86203831d35Sstevel #endif
86303831d35Sstevel
86403831d35Sstevel
86503831d35Sstevel default: {
86603831d35Sstevel char event = 0;
86703831d35Sstevel
868447a706eSVijay Balakrishna, SG-RPE sbd_ioctl = (int (*) (dev_t, int, intptr_t, int, char *))
869447a706eSVijay Balakrishna, SG-RPE modlookup("misc/sbd", "sbd_ioctl");
87003831d35Sstevel
87103831d35Sstevel if (sbd_ioctl)
87203831d35Sstevel rv = (*sbd_ioctl) (dev, cmd, arg, mode, &event);
87303831d35Sstevel else {
87403831d35Sstevel cmn_err(CE_WARN, "cannot find sbd_ioctl");
87503831d35Sstevel return (ENXIO);
87603831d35Sstevel }
87703831d35Sstevel /*
87803831d35Sstevel * Check to see if we need to send an event
87903831d35Sstevel */
88003831d35Sstevel if (event == 1) {
88103831d35Sstevel int slot;
88203831d35Sstevel int hint = SE_NO_HINT;
88303831d35Sstevel
88403831d35Sstevel if (rv == 0) {
88503831d35Sstevel if (cmd == SBD_CMD_CONNECT ||
88603831d35Sstevel cmd == SBD_CMD_CONFIGURE)
88703831d35Sstevel hint = SE_HINT_INSERT;
88803831d35Sstevel else if (cmd == SBD_CMD_UNCONFIGURE ||
88903831d35Sstevel cmd == SBD_CMD_DISCONNECT)
89003831d35Sstevel hint = SE_HINT_REMOVE;
89103831d35Sstevel }
89203831d35Sstevel
89303831d35Sstevel slot = (getminor(dev) & SSM_BOARD_MASK);
89407d06da5SSurya Prakki (void) ssm_generate_event(softsp->ssm_nodeid, slot,
89507d06da5SSurya Prakki hint);
89603831d35Sstevel }
89703831d35Sstevel break;
89803831d35Sstevel }
89903831d35Sstevel }
90003831d35Sstevel
90103831d35Sstevel return (rv);
90203831d35Sstevel }
90303831d35Sstevel
90403831d35Sstevel void
ssm_get_attch_pnt(int node,int board,char * attach_pnt)90503831d35Sstevel ssm_get_attch_pnt(int node, int board, char *attach_pnt)
90603831d35Sstevel {
90703831d35Sstevel struct ssm_node2inst *sp;
90803831d35Sstevel
90903831d35Sstevel /*
91003831d35Sstevel * Hold this mutex, until we are done so that ssm dip
91103831d35Sstevel * doesn't detach.
91203831d35Sstevel */
91303831d35Sstevel mutex_enter(&ssm_node2inst_lock);
91403831d35Sstevel
91503831d35Sstevel for (sp = &ssm_node2inst_map; sp != NULL; sp = sp->next) {
91603831d35Sstevel if (sp->inst == -1)
91703831d35Sstevel continue;
91803831d35Sstevel if (sp->nodeid == node)
91903831d35Sstevel break;
92003831d35Sstevel }
92103831d35Sstevel
92203831d35Sstevel if (sp == NULL) {
92303831d35Sstevel /* We didn't find the ssm dip, return failure */
92403831d35Sstevel attach_pnt[0] = '\0';
92503831d35Sstevel mutex_exit(&ssm_node2inst_lock);
92603831d35Sstevel return;
92703831d35Sstevel }
92803831d35Sstevel
92903831d35Sstevel /*
93003831d35Sstevel * we have the instance, and the board, construct the attch pnt
93103831d35Sstevel */
93203831d35Sstevel if (SG_BOARD_IS_CPU_TYPE(board))
93303831d35Sstevel (void) sprintf(attach_pnt, "ssm%d:N%d.SB%d",
93403831d35Sstevel sp->inst, node, board);
93503831d35Sstevel else
93603831d35Sstevel (void) sprintf(attach_pnt, "ssm%d:N%d.IB%d",
93703831d35Sstevel sp->inst, node, board);
93803831d35Sstevel
93903831d35Sstevel mutex_exit(&ssm_node2inst_lock);
94003831d35Sstevel }
94103831d35Sstevel
94203831d35Sstevel /*
94303831d35Sstevel * Generate an event to sysevent
94403831d35Sstevel */
94503831d35Sstevel static int
ssm_generate_event(int node,int board,int hint)94603831d35Sstevel ssm_generate_event(int node, int board, int hint)
94703831d35Sstevel {
94803831d35Sstevel sysevent_t *ev;
94903831d35Sstevel sysevent_id_t eid;
95003831d35Sstevel int rv = 0;
95103831d35Sstevel sysevent_value_t evnt_val;
95203831d35Sstevel sysevent_attr_list_t *evnt_attr_list = NULL;
95303831d35Sstevel char attach_pnt[MAXPATHLEN];
95403831d35Sstevel
95503831d35Sstevel
95603831d35Sstevel attach_pnt[0] = '\0';
95703831d35Sstevel ssm_get_attch_pnt(node, board, attach_pnt);
95803831d35Sstevel
95903831d35Sstevel if (attach_pnt[0] == '\0')
96003831d35Sstevel return (-1);
96103831d35Sstevel
96203831d35Sstevel ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, EP_DDI,
96303831d35Sstevel KM_SLEEP);
96403831d35Sstevel evnt_val.value_type = SE_DATA_TYPE_STRING;
96503831d35Sstevel evnt_val.value.sv_string = attach_pnt;
96603831d35Sstevel
96703831d35Sstevel rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val, KM_SLEEP);
96803831d35Sstevel if (rv != 0) {
96903831d35Sstevel cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
97003831d35Sstevel DR_AP_ID, EC_DR);
97103831d35Sstevel sysevent_free(ev);
97203831d35Sstevel return (rv);
97303831d35Sstevel }
97403831d35Sstevel
97503831d35Sstevel /*
97603831d35Sstevel * Add the hint
97703831d35Sstevel */
97803831d35Sstevel evnt_val.value_type = SE_DATA_TYPE_STRING;
97903831d35Sstevel evnt_val.value.sv_string = SE_HINT2STR(hint);
98003831d35Sstevel
98103831d35Sstevel rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val, KM_SLEEP);
98203831d35Sstevel if (rv != 0) {
98303831d35Sstevel cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
98403831d35Sstevel DR_HINT, EC_DR);
98503831d35Sstevel sysevent_free_attr(evnt_attr_list);
98603831d35Sstevel sysevent_free(ev);
98703831d35Sstevel return (-1);
98803831d35Sstevel }
98903831d35Sstevel
99003831d35Sstevel if (sysevent_attach_attributes(ev, evnt_attr_list) != 0) {
99103831d35Sstevel cmn_err(CE_WARN, "Failed to attach attr list for %s event",
99203831d35Sstevel EC_DR);
99303831d35Sstevel sysevent_free_attr(evnt_attr_list);
99403831d35Sstevel sysevent_free(ev);
99503831d35Sstevel return (-1);
99603831d35Sstevel }
99703831d35Sstevel
99803831d35Sstevel rv = log_sysevent(ev, KM_NOSLEEP, &eid);
99903831d35Sstevel if (rv != 0) {
100003831d35Sstevel cmn_err(CE_WARN, "ssm_dr_event_handler: failed to log event");
100103831d35Sstevel }
100203831d35Sstevel
100303831d35Sstevel sysevent_free(ev);
100403831d35Sstevel
100503831d35Sstevel return (rv);
100603831d35Sstevel }
100703831d35Sstevel
100803831d35Sstevel /*
100903831d35Sstevel * DR Event Handler
101003831d35Sstevel */
101103831d35Sstevel uint_t
ssm_dr_event_handler(char * arg)101203831d35Sstevel ssm_dr_event_handler(char *arg)
101303831d35Sstevel {
101403831d35Sstevel sg_system_fru_descriptor_t *fdp;
101503831d35Sstevel int hint;
101603831d35Sstevel
101703831d35Sstevel
101803831d35Sstevel fdp = (sg_system_fru_descriptor_t *)(((sbbc_msg_t *)arg)->msg_buf);
101903831d35Sstevel if (fdp == NULL) {
102003831d35Sstevel DPRINTF(SSM_EVENT_DEBUG,
102103831d35Sstevel ("ssm_dr_event_handler: ARG is null\n"));
102203831d35Sstevel return (DDI_INTR_CLAIMED);
102303831d35Sstevel }
102403831d35Sstevel #ifdef DEBUG
102503831d35Sstevel DPRINTF(SSM_EVENT_DEBUG, ("ssm_dr_event_handler called\n"));
102603831d35Sstevel DPRINTF(SSM_EVENT_DEBUG, ("\tnode\t%d\n", fdp->node));
102703831d35Sstevel DPRINTF(SSM_EVENT_DEBUG, ("\tslot\t%d\n", fdp->slot));
102803831d35Sstevel DPRINTF(SSM_EVENT_DEBUG, ("\tparent_hdl\t0x%lx\n", fdp->parent_hdl));
102903831d35Sstevel DPRINTF(SSM_EVENT_DEBUG, ("\tchild_hdl\t0x%lx\n", fdp->child_hdl));
103003831d35Sstevel DPRINTF(SSM_EVENT_DEBUG, ("\tevent_details\t%s\n",
103103831d35Sstevel EVNT2STR(fdp->event_details)));
103203831d35Sstevel #endif
103303831d35Sstevel
103403831d35Sstevel switch (fdp->event_details) {
103503831d35Sstevel case SG_EVT_BOARD_ABSENT:
103603831d35Sstevel hint = SE_HINT_REMOVE;
103703831d35Sstevel break;
103803831d35Sstevel case SG_EVT_BOARD_PRESENT:
103903831d35Sstevel hint = SE_HINT_INSERT;
104003831d35Sstevel break;
104103831d35Sstevel default:
104203831d35Sstevel hint = SE_NO_HINT;
104303831d35Sstevel break;
104403831d35Sstevel
104503831d35Sstevel }
104603831d35Sstevel
104707d06da5SSurya Prakki (void) ssm_generate_event(fdp->node, fdp->slot, hint);
104803831d35Sstevel
104903831d35Sstevel return (DDI_INTR_CLAIMED);
105003831d35Sstevel }
105103831d35Sstevel
105203831d35Sstevel /*
105303831d35Sstevel * Initialize our FMA resources
105403831d35Sstevel */
105503831d35Sstevel static void
ssm_fm_init(struct ssm_soft_state * softsp)105603831d35Sstevel ssm_fm_init(struct ssm_soft_state *softsp)
105703831d35Sstevel {
105803831d35Sstevel softsp->ssm_fm_cap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE |
105903831d35Sstevel DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
106003831d35Sstevel
106103831d35Sstevel /*
106203831d35Sstevel * Request or capability level and get our parents capability
106303831d35Sstevel * and ibc.
106403831d35Sstevel */
106503831d35Sstevel ddi_fm_init(softsp->dip, &softsp->ssm_fm_cap, &softsp->ssm_fm_ibc);
106603831d35Sstevel ASSERT((softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE) &&
106703831d35Sstevel (softsp->ssm_fm_cap & DDI_FM_ERRCB_CAPABLE));
106803831d35Sstevel /*
106903831d35Sstevel * Register error callback with our parent.
107003831d35Sstevel */
107103831d35Sstevel ddi_fm_handler_register(softsp->dip, ssm_err_callback, NULL);
107203831d35Sstevel }
107303831d35Sstevel
107403831d35Sstevel /*
107503831d35Sstevel * Breakdown our FMA resources
107603831d35Sstevel */
107703831d35Sstevel static void
ssm_fm_fini(struct ssm_soft_state * softsp)107803831d35Sstevel ssm_fm_fini(struct ssm_soft_state *softsp)
107903831d35Sstevel {
108003831d35Sstevel /*
108103831d35Sstevel * Clean up allocated fm structures
108203831d35Sstevel */
108303831d35Sstevel ASSERT(softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE);
108403831d35Sstevel ddi_fm_handler_unregister(softsp->dip);
108503831d35Sstevel ddi_fm_fini(softsp->dip);
108603831d35Sstevel }
108703831d35Sstevel
108803831d35Sstevel /*
108903831d35Sstevel * Initialize FMA resources for children devices. Called when
109003831d35Sstevel * child calls ddi_fm_init().
109103831d35Sstevel */
109203831d35Sstevel /*ARGSUSED*/
109303831d35Sstevel static int
ssm_fm_init_child(dev_info_t * dip,dev_info_t * tdip,int cap,ddi_iblock_cookie_t * ibc)109403831d35Sstevel ssm_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap,
109503831d35Sstevel ddi_iblock_cookie_t *ibc)
109603831d35Sstevel {
109703831d35Sstevel struct ssm_soft_state *softsp = ddi_get_soft_state(ssm_softstates,
109803831d35Sstevel ddi_get_instance(dip));
109903831d35Sstevel
110003831d35Sstevel *ibc = softsp->ssm_fm_ibc;
110103831d35Sstevel return (softsp->ssm_fm_cap);
110203831d35Sstevel }
110303831d35Sstevel
110403831d35Sstevel /*
110503831d35Sstevel * FMA registered error callback
110603831d35Sstevel */
110703831d35Sstevel /*ARGSUSED*/
110803831d35Sstevel static int
ssm_err_callback(dev_info_t * dip,ddi_fm_error_t * derr,const void * impl_data)110903831d35Sstevel ssm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data)
111003831d35Sstevel {
111103831d35Sstevel /* Call our children error handlers */
111203831d35Sstevel return (ndi_fm_handler_dispatch(dip, NULL, derr));
111303831d35Sstevel }
1114