11708Sstevel /*
21708Sstevel * CDDL HEADER START
31708Sstevel *
41708Sstevel * The contents of this file are subject to the terms of the
51708Sstevel * Common Development and Distribution License (the "License").
61708Sstevel * You may not use this file except in compliance with the License.
71708Sstevel *
81708Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel * or http://www.opensolaris.org/os/licensing.
101708Sstevel * See the License for the specific language governing permissions
111708Sstevel * and limitations under the License.
121708Sstevel *
131708Sstevel * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel *
191708Sstevel * CDDL HEADER END
201708Sstevel */
211708Sstevel
221708Sstevel /*
23*11311SSurya.Prakki@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
241708Sstevel * Use is subject to license terms.
251708Sstevel */
261708Sstevel #include <sys/types.h>
271708Sstevel #include <sys/conf.h>
281708Sstevel #include <sys/ddi.h>
291708Sstevel #include <sys/sunddi.h>
301708Sstevel #include <sys/modctl.h>
311708Sstevel #include <sys/sunndi.h>
321708Sstevel #include <sys/ddi_impldefs.h>
331708Sstevel #include <sys/obpdefs.h>
341708Sstevel #include <sys/cmn_err.h>
351708Sstevel #include <sys/errno.h>
361708Sstevel #include <sys/kmem.h>
371708Sstevel #include <sys/debug.h>
381708Sstevel #include <sys/sysmacros.h>
391708Sstevel #include <sys/autoconf.h>
401708Sstevel #include <sys/stat.h>
411708Sstevel #include <sys/serengeti.h>
421708Sstevel #include <sys/ssm.h>
431708Sstevel #include <sys/sgsbbc_mailbox.h>
441708Sstevel #include <sys/sgevents.h>
451708Sstevel #include <sys/sysevent.h>
461708Sstevel #include <sys/sysevent/dr.h>
471708Sstevel #include <sys/sysevent/eventdefs.h>
481708Sstevel #include <sys/ndi_impldefs.h>
491708Sstevel #include <sys/ddifm.h>
501708Sstevel #include <sys/ndifm.h>
511708Sstevel #include <sys/sbd_ioctl.h>
521708Sstevel
531708Sstevel /* Useful debugging Stuff */
541708Sstevel #include <sys/nexusdebug.h>
551708Sstevel
561708Sstevel /*
571708Sstevel * module ssm.c
581708Sstevel *
591708Sstevel * This module is a nexus driver designed to support the ssm nexus driver
601708Sstevel * and all children below it. This driver does not handle any of the
611708Sstevel * DDI functions passed up to it by the ssm driver, but instead allows
621708Sstevel * them to bubble up to the root node.
631708Sstevel */
641708Sstevel
651708Sstevel
661708Sstevel /*
671708Sstevel * Function prototypes
681708Sstevel */
691708Sstevel extern int plat_max_boards();
701708Sstevel
711708Sstevel static int
721708Sstevel ssm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result);
731708Sstevel
741708Sstevel static int
751708Sstevel ssm_attach(dev_info_t *, ddi_attach_cmd_t);
761708Sstevel
771708Sstevel static int
781708Sstevel ssm_detach(dev_info_t *, ddi_detach_cmd_t);
791708Sstevel
801708Sstevel static int
811708Sstevel ssm_open(dev_t *, int, int, cred_t *);
821708Sstevel
831708Sstevel static int
841708Sstevel ssm_close(dev_t, int, int, cred_t *);
851708Sstevel
861708Sstevel static int
871708Sstevel ssm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
881708Sstevel
891708Sstevel static int
901708Sstevel ssm_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *);
911708Sstevel
921708Sstevel static int
931708Sstevel ssm_make_nodes(dev_info_t *dip, int instance, int ssm_nodeid);
941708Sstevel
951708Sstevel static int
961708Sstevel ssm_generate_event(int node, int board, int hint);
971708Sstevel
981708Sstevel /*
991708Sstevel * FMA error callback
1001708Sstevel * Register error handling callback with our parent. We will just call
1011708Sstevel * our children's error callbacks and return their status.
1021708Sstevel */
1031708Sstevel static int
1041708Sstevel ssm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data);
1051708Sstevel
1061708Sstevel /*
1071708Sstevel * fm_init busop to initialize our children
1081708Sstevel */
1091708Sstevel static int
1101708Sstevel ssm_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap,
1111708Sstevel ddi_iblock_cookie_t *ibc);
1121708Sstevel
1131708Sstevel /*
1141708Sstevel * init/fini routines to alloc/dealloc fm structures and
1151708Sstevel * register/unregister our callback.
1161708Sstevel */
1171708Sstevel static void
1181708Sstevel ssm_fm_init(struct ssm_soft_state *softsp);
1191708Sstevel
1201708Sstevel static void
1211708Sstevel ssm_fm_fini(struct ssm_soft_state *softsp);
1221708Sstevel
1231708Sstevel /*
1241708Sstevel * DR event handlers
1251708Sstevel * We want to register the event handlers once for all instances. In the
1261708Sstevel * other hand we have register them after the sbbc has been attached.
1271708Sstevel * event_initialize gives us the logic of only registering the events only
1281708Sstevel * once
1291708Sstevel */
1301708Sstevel int event_initialized = 0;
1311708Sstevel uint_t ssm_dr_event_handler(char *);
1321708Sstevel
1331708Sstevel /*
1341708Sstevel * Event lock and state
1351708Sstevel */
1361708Sstevel static kmutex_t ssm_event_lock;
1371708Sstevel int ssm_event_state;
1381708Sstevel
1391708Sstevel /*
1401708Sstevel * DR event msg and payload
1411708Sstevel */
1421708Sstevel static sbbc_msg_t event_msg;
1431708Sstevel static sg_system_fru_descriptor_t payload;
1441708Sstevel
1451708Sstevel struct ssm_node2inst {
1461708Sstevel int nodeid; /* serengeti node #, NOT prom nodeid */
1471708Sstevel int inst;
1481708Sstevel struct ssm_node2inst *next;
1491708Sstevel };
1501708Sstevel static kmutex_t ssm_node2inst_lock;
1511708Sstevel static struct ssm_node2inst ssm_node2inst_map = {-1, -1, NULL};
1521708Sstevel
1531708Sstevel
1541708Sstevel /*
1551708Sstevel * Configuration data structures
1561708Sstevel */
1571708Sstevel static struct bus_ops ssm_bus_ops = {
1581708Sstevel BUSO_REV,
1591708Sstevel ddi_bus_map, /* map */
1601708Sstevel 0, /* get_intrspec */
1611708Sstevel 0, /* add_intrspec */
1621708Sstevel 0, /* remove_intrspec */
1631708Sstevel i_ddi_map_fault, /* map_fault */
1641708Sstevel ddi_dma_map, /* dma_map */
1651708Sstevel ddi_dma_allochdl,
1661708Sstevel ddi_dma_freehdl,
1671708Sstevel ddi_dma_bindhdl,
1681708Sstevel ddi_dma_unbindhdl,
1691708Sstevel ddi_dma_flush,
1701708Sstevel ddi_dma_win,
1711708Sstevel ddi_dma_mctl, /* dma_ctl */
1721708Sstevel ssm_ctlops, /* ctl */
1731708Sstevel ddi_bus_prop_op, /* prop_op */
1741708Sstevel ndi_busop_get_eventcookie,
1751708Sstevel ndi_busop_add_eventcall,
1761708Sstevel ndi_busop_remove_eventcall,
1771708Sstevel ndi_post_event,
1781708Sstevel 0,
1791708Sstevel 0,
1801708Sstevel 0,
1811708Sstevel ssm_fm_init_child,
1821708Sstevel NULL,
1831708Sstevel NULL,
1841708Sstevel NULL,
1851708Sstevel 0,
1861708Sstevel i_ddi_intr_ops
1871708Sstevel };
1881708Sstevel
1891708Sstevel static struct cb_ops ssm_cb_ops = {
1901708Sstevel ssm_open, /* open */
1911708Sstevel ssm_close, /* close */
1921708Sstevel nodev, /* strategy */
1931708Sstevel nodev, /* print */
1941708Sstevel nodev, /* dump */
1951708Sstevel nodev, /* read */
1961708Sstevel nodev, /* write */
1971708Sstevel ssm_ioctl, /* ioctl */
1981708Sstevel nodev, /* devmap */
1991708Sstevel nodev, /* mmap */
2001708Sstevel nodev, /* segmap */
2011708Sstevel nochpoll, /* poll */
2021708Sstevel ddi_prop_op, /* cb_prop_op */
2031708Sstevel NULL, /* streamtab */
2041708Sstevel D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
2051708Sstevel CB_REV, /* rev */
2061708Sstevel nodev, /* int (*cb_aread)() */
2071708Sstevel nodev /* int (*cb_awrite)() */
2081708Sstevel };
2091708Sstevel
2101708Sstevel static struct dev_ops ssm_ops = {
2111708Sstevel DEVO_REV, /* devo_rev, */
2121708Sstevel 0, /* refcnt */
2131708Sstevel ssm_info, /* getinfo */
2141708Sstevel nulldev, /* identify */
2151708Sstevel nulldev, /* probe */
2161708Sstevel ssm_attach, /* attach */
2171708Sstevel ssm_detach, /* detach */
2181708Sstevel nulldev, /* reset */
2191708Sstevel &ssm_cb_ops, /* driver operations */
2201708Sstevel &ssm_bus_ops, /* bus_ops */
2217656SSherry.Moore@Sun.COM nulldev, /* power */
2227656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* quiesce */
2231708Sstevel };
2241708Sstevel
2251708Sstevel /*
2261708Sstevel * Driver globals
2271708Sstevel */
2281708Sstevel static void *ssm_softstates; /* ssm soft state hook */
2291708Sstevel
2301708Sstevel extern struct mod_ops mod_driverops;
2311708Sstevel
2321708Sstevel static struct modldrv modldrv = {
2331708Sstevel &mod_driverops, /* Type of module. This one is a driver */
2347656SSherry.Moore@Sun.COM "SSM Nexus", /* name of module */
2351708Sstevel &ssm_ops, /* driver ops */
2361708Sstevel };
2371708Sstevel
2381708Sstevel static struct modlinkage modlinkage = {
2391708Sstevel MODREV_1, /* rev */
2401708Sstevel (void *)&modldrv,
2411708Sstevel NULL
2421708Sstevel };
2431708Sstevel
2441708Sstevel static int ssm_loaded_sbd = FALSE;
2451708Sstevel kmutex_t ssm_lock;
2461708Sstevel static int init_child(dev_info_t *child);
2471708Sstevel
2481708Sstevel /*
2491708Sstevel * These are the module initialization routines.
2501708Sstevel */
2511708Sstevel
2521708Sstevel int
_init(void)2531708Sstevel _init(void)
2541708Sstevel {
2551708Sstevel int error;
2561708Sstevel
2571708Sstevel #if defined(DEBUG)
2581708Sstevel debug_print_level = 0x0;
2591708Sstevel #endif
2601708Sstevel
2611708Sstevel /* Initialize soft state pointer. */
2621708Sstevel if ((error = ddi_soft_state_init(&ssm_softstates,
2637578SVijay.Balakrishna@Sun.COM sizeof (struct ssm_soft_state), SSM_MAX_INSTANCES)) != 0)
2641708Sstevel return (error);
2651708Sstevel
2661708Sstevel /* Install the module. */
2671708Sstevel error = mod_install(&modlinkage);
2681708Sstevel if (error != 0)
2691708Sstevel ddi_soft_state_fini(&ssm_softstates);
2701708Sstevel
2711708Sstevel mutex_init(&ssm_lock, NULL, MUTEX_DRIVER, NULL);
2721708Sstevel
2731708Sstevel return (error);
2741708Sstevel }
2751708Sstevel
2761708Sstevel int
_fini(void)2771708Sstevel _fini(void)
2781708Sstevel {
2791708Sstevel int error;
2801708Sstevel
2811708Sstevel /* Remove the module. */
2821708Sstevel if ((error = mod_remove(&modlinkage)) != 0)
2831708Sstevel return (error);
2841708Sstevel
2851708Sstevel /*
2861708Sstevel * Unregister the event handler
2871708Sstevel */
288*11311SSurya.Prakki@Sun.COM (void) sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC, ssm_dr_event_handler);
2891708Sstevel mutex_destroy(&ssm_event_lock);
2901708Sstevel
2911708Sstevel /* Free the soft state info. */
2921708Sstevel ddi_soft_state_fini(&ssm_softstates);
2931708Sstevel mutex_destroy(&ssm_lock);
2941708Sstevel
2951708Sstevel return (0);
2961708Sstevel }
2971708Sstevel
2981708Sstevel int
_info(struct modinfo * modinfop)2991708Sstevel _info(struct modinfo *modinfop)
3001708Sstevel {
3011708Sstevel return (mod_info(&modlinkage, modinfop));
3021708Sstevel }
3031708Sstevel
3041708Sstevel /* device driver entry points */
3051708Sstevel
3061708Sstevel /*
3071708Sstevel * info entry point:
3081708Sstevel */
3091708Sstevel
3101708Sstevel /* ARGSUSED */
3111708Sstevel static int
ssm_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)3121708Sstevel ssm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
3131708Sstevel {
3141708Sstevel dev_t dev;
3151708Sstevel int instance;
3161708Sstevel
3171708Sstevel if (infocmd == DDI_INFO_DEVT2INSTANCE) {
3181708Sstevel dev = (dev_t)arg;
3191708Sstevel instance = (getminor(dev) >> SSM_INSTANCE_SHIFT);
3201708Sstevel *result = (void *)(uintptr_t)instance;
3211708Sstevel return (DDI_SUCCESS);
3221708Sstevel }
3231708Sstevel return (DDI_FAILURE);
3241708Sstevel }
3251708Sstevel
3261708Sstevel /*
3271708Sstevel * attach entry point:
3281708Sstevel */
3291708Sstevel
3301708Sstevel static int
ssm_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)3311708Sstevel ssm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
3321708Sstevel {
3331708Sstevel int instance;
3341708Sstevel struct ssm_soft_state *softsp;
3351708Sstevel struct ssm_node2inst *prev, *sp, *tsp;
3361708Sstevel
3371708Sstevel DPRINTF(SSM_ATTACH_DEBUG, ("ssm_attach\n"));
3381708Sstevel
3391708Sstevel switch (cmd) {
3401708Sstevel case DDI_ATTACH:
3411708Sstevel break;
3421708Sstevel
3431708Sstevel case DDI_RESUME:
3441708Sstevel return (DDI_SUCCESS);
3451708Sstevel
3461708Sstevel default:
3471708Sstevel return (DDI_FAILURE);
3481708Sstevel }
3491708Sstevel
3501708Sstevel instance = ddi_get_instance(devi);
3511708Sstevel
3521708Sstevel if (ddi_soft_state_zalloc(ssm_softstates, instance) != DDI_SUCCESS)
3531708Sstevel return (DDI_FAILURE);
3541708Sstevel
3551708Sstevel softsp = ddi_get_soft_state(ssm_softstates, instance);
3561708Sstevel
3571708Sstevel /* Set the dip in the soft state */
3581708Sstevel softsp->dip = devi;
3591708Sstevel softsp->top_node = devi;
3601708Sstevel mutex_init(&softsp->ssm_sft_lock, NULL, MUTEX_DRIVER, NULL);
3611708Sstevel
3621708Sstevel DPRINTF(SSM_ATTACH_DEBUG, ("ssm-%d: devi= 0x%p, softsp=0x%p\n",
363*11311SSurya.Prakki@Sun.COM instance, (void *)devi, (void *)softsp));
3641708Sstevel
3651708Sstevel if ((softsp->ssm_nodeid = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->dip,
3661708Sstevel DDI_PROP_DONTPASS, "nodeid", -1)) == -1) {
3671708Sstevel cmn_err(CE_WARN, "ssm%d: unable to retrieve %s property",
3681708Sstevel instance, "nodeid");
3691708Sstevel ddi_soft_state_free(ssm_softstates, instance);
3701708Sstevel return (DDI_FAILURE);
3711708Sstevel }
3721708Sstevel
3731708Sstevel /* nothing to suspend/resume here */
3741708Sstevel (void) ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP,
3751708Sstevel "pm-hardware-state", (caddr_t)"no-suspend-resume",
3761708Sstevel strlen("no-suspend-resume") + 1);
3771708Sstevel
3781708Sstevel #if DEBUG
3791708Sstevel if (ddi_create_minor_node(devi, "debug", S_IFCHR, instance,
3801708Sstevel DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
3811708Sstevel ddi_soft_state_free(ssm_softstates, instance);
3821708Sstevel return (DDI_FAILURE);
3831708Sstevel }
3841708Sstevel #endif
3851708Sstevel
3861708Sstevel if (ssm_make_nodes(devi, instance, softsp->ssm_nodeid)) {
3877578SVijay.Balakrishna@Sun.COM cmn_err(CE_WARN, "ssm:%s:%d: failed to make nodes",
3887578SVijay.Balakrishna@Sun.COM ddi_driver_name(devi), instance);
3891708Sstevel ddi_remove_minor_node(devi, NULL);
3901708Sstevel ddi_soft_state_free(ssm_softstates, instance);
3911708Sstevel return (DDI_FAILURE);
3921708Sstevel }
3931708Sstevel ssm_fm_init(softsp);
3941708Sstevel ddi_report_dev(devi);
3951708Sstevel
3961708Sstevel if (event_initialized == 0) {
3971708Sstevel int rv;
3981708Sstevel /*
3991708Sstevel * Register DR event handler
4001708Sstevel */
4011708Sstevel mutex_init(&ssm_event_lock, NULL, MUTEX_DRIVER, NULL);
4021708Sstevel event_msg.msg_buf = (caddr_t)&payload;
4031708Sstevel event_msg.msg_len = sizeof (payload);
4041708Sstevel
4051708Sstevel rv = sbbc_mbox_reg_intr(MBOX_EVENT_GENERIC,
4061708Sstevel ssm_dr_event_handler, &event_msg,
4071708Sstevel (uint_t *)&ssm_event_state, &ssm_event_lock);
4081708Sstevel
4091708Sstevel if (rv == EINVAL)
4101708Sstevel event_initialized = 1;
4111708Sstevel }
4121708Sstevel
4131708Sstevel /*
4141708Sstevel * Preallocate to avoid sleeping with ssm_node2inst_lock held -
4151708Sstevel * low level interrupts use this mutex.
4161708Sstevel */
4171708Sstevel tsp = kmem_zalloc(sizeof (struct ssm_node2inst), KM_SLEEP);
4181708Sstevel
4191708Sstevel mutex_enter(&ssm_node2inst_lock);
4201708Sstevel
4211708Sstevel for (prev = NULL, sp = &ssm_node2inst_map; sp != NULL;
4221708Sstevel prev = sp, sp = sp->next) {
4231708Sstevel ASSERT(sp->inst != instance);
4241708Sstevel ASSERT(sp->nodeid != softsp->ssm_nodeid);
4251708Sstevel if (sp->inst == -1)
4261708Sstevel break;
4271708Sstevel }
4281708Sstevel
4291708Sstevel if (sp == NULL) {
4301708Sstevel ASSERT(prev->next == NULL);
4311708Sstevel sp = prev->next = tsp;
4321708Sstevel tsp = NULL;
4331708Sstevel sp->next = NULL;
4341708Sstevel }
4351708Sstevel
4361708Sstevel sp->inst = instance;
4371708Sstevel sp->nodeid = softsp->ssm_nodeid;
4381708Sstevel
4391708Sstevel mutex_exit(&ssm_node2inst_lock);
4401708Sstevel
4411708Sstevel if (tsp != NULL)
4421708Sstevel kmem_free(tsp, sizeof (struct ssm_node2inst));
4431708Sstevel
4441708Sstevel return (DDI_SUCCESS);
4451708Sstevel }
4461708Sstevel
4471708Sstevel /*
4481708Sstevel * detach entry point:
4491708Sstevel */
4501708Sstevel /*ARGSUSED*/
4511708Sstevel static int
ssm_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)4521708Sstevel ssm_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
4531708Sstevel {
4541708Sstevel int instance, rv;
4551708Sstevel int (*sbd_teardown_instance) (int, caddr_t);
4561708Sstevel ssm_sbdp_info_t sbdp_info;
4571708Sstevel struct ssm_soft_state *softsp;
4581708Sstevel struct ssm_node2inst *prev, *sp;
4591708Sstevel
4601708Sstevel instance = ddi_get_instance(devi);
4611708Sstevel softsp = ddi_get_soft_state(ssm_softstates, instance);
4621708Sstevel
4631708Sstevel if (softsp == NULL) {
4641708Sstevel cmn_err(CE_WARN,
4657578SVijay.Balakrishna@Sun.COM "ssm_open bad instance number %d", instance);
4661708Sstevel return (ENXIO);
4671708Sstevel }
4681708Sstevel
4691708Sstevel instance = ddi_get_instance(devi);
4701708Sstevel
4711708Sstevel switch (cmd) {
4721708Sstevel case DDI_DETACH:
4731708Sstevel ddi_remove_minor_node(devi, NULL);
4741708Sstevel
4751708Sstevel sbd_teardown_instance = (int (*) (int, caddr_t))
4767578SVijay.Balakrishna@Sun.COM modlookup("misc/sbd", "sbd_teardown_instance");
4771708Sstevel
4781708Sstevel if (!sbd_teardown_instance) {
4791708Sstevel cmn_err(CE_WARN, "cannot find sbd_teardown_instance");
4801708Sstevel return (DDI_FAILURE);
4811708Sstevel }
4821708Sstevel
4831708Sstevel sbdp_info.instance = instance;
4841708Sstevel sbdp_info.wnode = softsp->ssm_nodeid;
4851708Sstevel rv = (*sbd_teardown_instance)(instance, (caddr_t)&sbdp_info);
4861708Sstevel
4871708Sstevel if (rv != DDI_SUCCESS) {
4881708Sstevel cmn_err(CE_WARN, "cannot run sbd_teardown_instance");
4891708Sstevel return (DDI_FAILURE);
4901708Sstevel }
4911708Sstevel ssm_fm_fini(softsp);
4921708Sstevel mutex_destroy(&softsp->ssm_sft_lock);
4931708Sstevel ddi_soft_state_free(ssm_softstates, instance);
4941708Sstevel
4951708Sstevel mutex_enter(&ssm_node2inst_lock);
4961708Sstevel for (prev = NULL, sp = &ssm_node2inst_map; sp != NULL;
4971708Sstevel prev = sp, sp = sp->next) {
4981708Sstevel /* Only the head of the list can persist if unused */
4991708Sstevel ASSERT(prev == NULL || sp->inst != -1);
5001708Sstevel if (sp->inst == instance)
5011708Sstevel break;
5021708Sstevel }
5031708Sstevel ASSERT(sp != NULL);
5041708Sstevel
5051708Sstevel if (sp != &ssm_node2inst_map) {
5061708Sstevel prev->next = sp->next;
5071708Sstevel kmem_free(sp, sizeof (struct ssm_node2inst));
5081708Sstevel } else {
5091708Sstevel /*
5101708Sstevel * Invalidate the head element, but retain the rest
5111708Sstevel * of the list - "next" is still valid.
5121708Sstevel */
5131708Sstevel
5141708Sstevel sp->nodeid = -1;
5151708Sstevel sp->inst = -1;
5161708Sstevel }
5171708Sstevel mutex_exit(&ssm_node2inst_lock);
5181708Sstevel
5191708Sstevel return (DDI_SUCCESS);
5201708Sstevel
5211708Sstevel case DDI_SUSPEND:
5221708Sstevel return (DDI_SUCCESS);
5231708Sstevel
5241708Sstevel default:
5251708Sstevel return (DDI_FAILURE);
5261708Sstevel }
5271708Sstevel }
5281708Sstevel
5291708Sstevel extern void make_ddi_ppd(dev_info_t *, struct ddi_parent_private_data **);
5301708Sstevel extern struct ddi_parent_private_data *init_regspec_64(dev_info_t *);
5311708Sstevel
5321708Sstevel static int
name_child(dev_info_t * child,char * name,int namelen)5331708Sstevel name_child(dev_info_t *child, char *name, int namelen)
5341708Sstevel {
5351708Sstevel struct regspec *rp;
5361708Sstevel struct ddi_parent_private_data *pdptr;
5371708Sstevel int portid = 0;
5381708Sstevel int regbase = -1;
5391708Sstevel extern uint_t root_phys_addr_lo_mask;
5401708Sstevel
5411708Sstevel make_ddi_ppd(child, &pdptr);
5421708Sstevel ddi_set_parent_data(child, pdptr);
5431708Sstevel
5441708Sstevel name[0] = '\0';
5451708Sstevel if (sparc_pd_getnreg(child) == 0)
5461708Sstevel return (DDI_SUCCESS);
5471708Sstevel
5481708Sstevel rp = sparc_pd_getreg(child, 0);
5491708Sstevel
5501708Sstevel portid = ddi_prop_get_int(DDI_DEV_T_ANY, child,
5511708Sstevel DDI_PROP_DONTPASS, "portid", -1);
5521708Sstevel if (portid == -1) {
5531708Sstevel cmn_err(CE_WARN, "could not find portid property in %s",
5541708Sstevel DEVI(child)->devi_node_name);
5551708Sstevel } else {
5561708Sstevel regbase = rp->regspec_addr & root_phys_addr_lo_mask;
5571708Sstevel }
5581708Sstevel (void) snprintf(name, namelen, "%x,%x", portid, regbase);
5591708Sstevel return (DDI_SUCCESS);
5601708Sstevel }
5611708Sstevel
5621708Sstevel static int
init_child(dev_info_t * child)5631708Sstevel init_child(dev_info_t *child)
5641708Sstevel {
5651708Sstevel char name[MAXNAMELEN];
5661708Sstevel
5671708Sstevel (void) name_child(child, name, MAXNAMELEN);
5681708Sstevel ddi_set_name_addr(child, name);
5691708Sstevel if ((ndi_dev_is_persistent_node(child) == 0) &&
5701708Sstevel (ndi_merge_node(child, name_child) == DDI_SUCCESS)) {
5711708Sstevel impl_ddi_sunbus_removechild(child);
5721708Sstevel return (DDI_FAILURE);
5731708Sstevel }
5741708Sstevel
5751708Sstevel (void) init_regspec_64(child);
5761708Sstevel return (DDI_SUCCESS);
5771708Sstevel }
5781708Sstevel
5791708Sstevel /*
5801708Sstevel * Control ops entry point:
5811708Sstevel *
5821708Sstevel * Requests handled completely:
5831708Sstevel * DDI_CTLOPS_INITCHILD
5841708Sstevel * DDI_CTLOPS_UNINITCHILD
5851708Sstevel * DDI_CTLOPS_REPORTDEV
5861708Sstevel * All others are passed to the parent.
5871708Sstevel * The name of the ssm node is ssm@nodeid,0.
5881708Sstevel * ssm is the equivalent of rootnex.
5891708Sstevel */
5901708Sstevel static int
ssm_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)5911708Sstevel ssm_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, void *arg,
5921708Sstevel void *result)
5931708Sstevel {
5941708Sstevel int rval;
5951708Sstevel
5961708Sstevel switch (op) {
5971708Sstevel case DDI_CTLOPS_INITCHILD: {
5981708Sstevel DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_INITCHILD\n"));
5991708Sstevel return (init_child((dev_info_t *)arg));
6001708Sstevel }
6011708Sstevel
6021708Sstevel case DDI_CTLOPS_UNINITCHILD: {
6031708Sstevel DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_UNINITCHILD\n"));
6041708Sstevel impl_ddi_sunbus_removechild((dev_info_t *)arg);
6051708Sstevel return (DDI_SUCCESS);
6061708Sstevel }
6071708Sstevel
6081708Sstevel case DDI_CTLOPS_REPORTDEV: {
6091708Sstevel char buf[80];
6101708Sstevel char *p = buf;
6111708Sstevel dev_info_t *parent;
6121708Sstevel int portid;
6131708Sstevel
6141708Sstevel DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_REPORTDEV\n"));
6151708Sstevel parent = ddi_get_parent(rdip);
6161708Sstevel
6171708Sstevel (void) sprintf(p, "%s%d at %s%d", DEVI(rdip)->devi_name,
6181708Sstevel DEVI(rdip)->devi_instance, ddi_get_name(parent),
6191708Sstevel ddi_get_instance(parent));
6201708Sstevel p += strlen(p);
6211708Sstevel
6221708Sstevel /* Fetch Safari Extended Agent ID of this device. */
6231708Sstevel portid = (int)ddi_getprop(DDI_DEV_T_ANY, rdip,
6241708Sstevel DDI_PROP_DONTPASS, "portid", -1);
6251708Sstevel
6261708Sstevel /*
6271708Sstevel * If this is one of the ssm children it will have
6281708Sstevel * portid property and its parent will be ssm.
6291708Sstevel * In this case report Node number and Safari id.
6301708Sstevel */
6311708Sstevel if (portid != -1 &&
6321708Sstevel strcmp("ssm", ddi_get_name(parent)) == 0) {
6331708Sstevel struct regspec *rp;
6341708Sstevel int node;
6351708Sstevel int safid;
6361708Sstevel int n;
6371708Sstevel
6381708Sstevel rp = sparc_pd_getreg(rdip, 0);
6391708Sstevel n = sparc_pd_getnreg(rdip);
6401708Sstevel ASSERT(n > 0);
6411708Sstevel
6421708Sstevel node = SG_PORTID_TO_NODEID(portid);
6431708Sstevel safid = SG_PORTID_TO_SAFARI_ID(portid);
6441708Sstevel
6451708Sstevel (void) strcpy(p, ": ");
6461708Sstevel p += strlen(p);
6471708Sstevel
6481708Sstevel (void) sprintf(p, "Node %d Safari id %d 0x%x%s",
6491708Sstevel node, safid,
6501708Sstevel rp->regspec_addr,
6511708Sstevel (n > 1 ? "" : " ..."));
6521708Sstevel p += strlen(p);
6531708Sstevel }
6541708Sstevel
6551708Sstevel cmn_err(CE_CONT, "?%s\n", buf);
6561708Sstevel rval = DDI_SUCCESS;
6571708Sstevel
6581708Sstevel break;
6591708Sstevel }
6601708Sstevel
6611708Sstevel default:
6621708Sstevel rval = ddi_ctlops(dip, rdip, op, arg, result);
6631708Sstevel
6641708Sstevel break;
6651708Sstevel }
6661708Sstevel
6671708Sstevel return (rval);
6681708Sstevel }
6691708Sstevel
6701708Sstevel /*ARGSUSED*/
6711708Sstevel static int
ssm_make_nodes(dev_info_t * dip,int instance,int ssm_nodeid)6721708Sstevel ssm_make_nodes(dev_info_t *dip, int instance, int ssm_nodeid)
6731708Sstevel {
6741708Sstevel int rv;
6751708Sstevel minor_t minor_num, bd;
6761708Sstevel auto char filename[20];
6771708Sstevel
6781708Sstevel for (bd = 0; bd < plat_max_boards(); bd++) {
6791708Sstevel if (SG_BOARD_IS_CPU_TYPE(bd))
6801708Sstevel (void) sprintf(filename, "N%d.SB%d", ssm_nodeid, bd);
6811708Sstevel else
6821708Sstevel (void) sprintf(filename, "N%d.IB%d", ssm_nodeid, bd);
6831708Sstevel
6841708Sstevel minor_num = (instance << SSM_INSTANCE_SHIFT) | bd;
6851708Sstevel
6861708Sstevel rv = ddi_create_minor_node(dip, filename, S_IFCHR,
6877578SVijay.Balakrishna@Sun.COM minor_num, DDI_NT_SBD_ATTACHMENT_POINT, NULL);
6881708Sstevel if (rv == DDI_FAILURE) {
6891708Sstevel cmn_err(CE_WARN,
6907578SVijay.Balakrishna@Sun.COM "ssm_make_nodes:%d: failed to create "
6917578SVijay.Balakrishna@Sun.COM "minor node (%s, 0x%x)",
6927578SVijay.Balakrishna@Sun.COM instance, filename, minor_num);
6931708Sstevel return (-1);
6941708Sstevel }
6951708Sstevel }
6961708Sstevel
6971708Sstevel return (0);
6981708Sstevel }
6991708Sstevel
7001708Sstevel
7011708Sstevel /* ARGSUSED */
7021708Sstevel static int
ssm_open(dev_t * devi,int flags,int otyp,cred_t * credp)7031708Sstevel ssm_open(dev_t *devi, int flags, int otyp, cred_t *credp)
7041708Sstevel {
7051708Sstevel struct ssm_soft_state *softsp;
7061708Sstevel minor_t board, instance;
7071708Sstevel int (*sbd_setup_instance)(int, dev_info_t *, int, int, caddr_t);
7081708Sstevel ssm_sbdp_info_t sbdp_info;
7091708Sstevel int rv;
7101708Sstevel
7111708Sstevel instance = (getminor(*devi) >> SSM_INSTANCE_SHIFT);
7121708Sstevel
7131708Sstevel softsp = ddi_get_soft_state(ssm_softstates, instance);
7141708Sstevel if (softsp == NULL) {
7151708Sstevel cmn_err(CE_WARN, "ssm_open bad instance number %d", instance);
7161708Sstevel return (ENXIO);
7171708Sstevel }
7181708Sstevel
7191708Sstevel board = (getminor(*devi) & SSM_BOARD_MASK);
7201708Sstevel
7211708Sstevel if (board < 0 || board > plat_max_boards()) {
7221708Sstevel return (ENXIO);
7231708Sstevel }
7241708Sstevel
7251708Sstevel mutex_enter(&ssm_lock);
7261708Sstevel if (instance == 0 && ssm_loaded_sbd == FALSE) {
7271708Sstevel
7281708Sstevel if (modload("misc", "sbd") == -1) {
7291708Sstevel cmn_err(CE_WARN, "ssm_open: cannot load sbd");
7301708Sstevel mutex_exit(&ssm_lock);
7311708Sstevel return (EIO);
7321708Sstevel }
7331708Sstevel ssm_loaded_sbd = TRUE;
7341708Sstevel }
7351708Sstevel mutex_exit(&ssm_lock);
7361708Sstevel
7371708Sstevel mutex_enter(&softsp->ssm_sft_lock);
7381708Sstevel if (softsp->initialized == FALSE) {
7391708Sstevel
7401708Sstevel if (softsp->top_node == NULL) {
7411708Sstevel cmn_err(CE_WARN, "cannot find ssm top dnode");
7421708Sstevel mutex_exit(&softsp->ssm_sft_lock);
7431708Sstevel return (EIO);
7441708Sstevel }
7451708Sstevel
7461708Sstevel sbd_setup_instance = (int (*)(int, dev_info_t *, int, int,
7477578SVijay.Balakrishna@Sun.COM caddr_t))modlookup("misc/sbd", "sbd_setup_instance");
7481708Sstevel
7491708Sstevel if (!sbd_setup_instance) {
7501708Sstevel cmn_err(CE_WARN, "cannot find sbd_setup_instance");
7511708Sstevel mutex_exit(&softsp->ssm_sft_lock);
7521708Sstevel return (EIO);
7531708Sstevel }
7541708Sstevel
7551708Sstevel sbdp_info.instance = instance;
7561708Sstevel sbdp_info.wnode = softsp->ssm_nodeid;
7571708Sstevel
7581708Sstevel rv = (*sbd_setup_instance)(instance, softsp->top_node,
7597578SVijay.Balakrishna@Sun.COM plat_max_boards(), softsp->ssm_nodeid,
7607578SVijay.Balakrishna@Sun.COM (caddr_t)&sbdp_info);
7611708Sstevel if (rv != DDI_SUCCESS) {
7621708Sstevel cmn_err(CE_WARN, "cannot run sbd_setup_instance");
7631708Sstevel mutex_exit(&softsp->ssm_sft_lock);
7641708Sstevel return (EIO);
7651708Sstevel }
7661708Sstevel softsp->initialized = TRUE;
7671708Sstevel }
7681708Sstevel mutex_exit(&softsp->ssm_sft_lock);
7691708Sstevel
7701708Sstevel return (DDI_SUCCESS);
7711708Sstevel }
7721708Sstevel
7731708Sstevel
7741708Sstevel /* ARGSUSED */
7751708Sstevel static int
ssm_close(dev_t dev,int flags,int otyp,cred_t * credp)7761708Sstevel ssm_close(dev_t dev, int flags, int otyp, cred_t *credp)
7771708Sstevel {
7781708Sstevel struct ssm_soft_state *softsp;
7791708Sstevel minor_t board, instance;
7801708Sstevel
7811708Sstevel instance = (getminor(dev) >> SSM_INSTANCE_SHIFT);
7821708Sstevel
7831708Sstevel softsp = ddi_get_soft_state(ssm_softstates, instance);
7841708Sstevel if (softsp == NULL)
7851708Sstevel return (ENXIO);
7861708Sstevel
7871708Sstevel board = (getminor(dev) & SSM_BOARD_MASK);
7881708Sstevel
7891708Sstevel if (board < 0 || board > plat_max_boards())
7901708Sstevel return (ENXIO);
7911708Sstevel
7921708Sstevel return (DDI_SUCCESS);
7931708Sstevel }
7941708Sstevel
7951708Sstevel /* ARGSUSED */
7961708Sstevel static int
ssm_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)7971708Sstevel ssm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
7981708Sstevel int *rvalp)
7991708Sstevel {
8001708Sstevel struct ssm_soft_state *softsp;
8011708Sstevel char *addr;
8021708Sstevel struct devctl_iocdata *dcp;
8031708Sstevel int instance, rv = 0;
8041708Sstevel int (*sbd_ioctl) (dev_t, int, intptr_t, int, char *);
8051708Sstevel
8061708Sstevel instance = (getminor(dev) >> SSM_INSTANCE_SHIFT);
8071708Sstevel softsp = ddi_get_soft_state(ssm_softstates, instance);
8081708Sstevel if (softsp == NULL)
8091708Sstevel return (ENXIO);
8101708Sstevel
8111708Sstevel switch (cmd) {
8121708Sstevel
8131708Sstevel case DEVCTL_BUS_CONFIGURE:
8141708Sstevel /*
8151708Sstevel * read devctl ioctl data
8161708Sstevel */
8171708Sstevel if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
8181708Sstevel return (EFAULT);
8191708Sstevel
8201708Sstevel addr = ndi_dc_getaddr(dcp);
8211708Sstevel cmn_err(CE_NOTE,
8221708Sstevel "DEVCTL_BUS_CONFIGURE: device id is %s\n", addr);
8231708Sstevel ndi_dc_freehdl(dcp);
8241708Sstevel break;
8251708Sstevel
8261708Sstevel case DEVCTL_BUS_UNCONFIGURE:
8271708Sstevel if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
8281708Sstevel return (EFAULT);
8291708Sstevel
8301708Sstevel addr = ndi_dc_getaddr(dcp);
8311708Sstevel cmn_err(CE_NOTE,
8321708Sstevel "DEVCTL_BUS_UNCONFIGURE: device id is %s\n", addr);
8331708Sstevel ndi_dc_freehdl(dcp);
8341708Sstevel break;
8351708Sstevel
8361708Sstevel #ifdef DEBUG
8371708Sstevel case SSM_TEARDOWN_SBD: {
8381708Sstevel ssm_sbdp_info_t sbdp_info;
8391708Sstevel int (*sbd_teardown_instance) (int, caddr_t);
8401708Sstevel sbd_teardown_instance = (int (*) (int, caddr_t))
8417578SVijay.Balakrishna@Sun.COM modlookup("misc/sbd", "sbd_teardown_instance");
8421708Sstevel
8431708Sstevel if (!sbd_teardown_instance) {
8441708Sstevel cmn_err(CE_WARN, "cannot find sbd_teardown_instance");
8451708Sstevel return (EFAULT);
8461708Sstevel }
8471708Sstevel
8481708Sstevel sbdp_info.instance = instance;
8491708Sstevel sbdp_info.wnode = softsp->ssm_nodeid;
8501708Sstevel rv = (*sbd_teardown_instance)(instance, (caddr_t)&sbdp_info);
8511708Sstevel if (rv != DDI_SUCCESS) {
8521708Sstevel cmn_err(CE_WARN, "cannot run sbd_teardown_instance");
8531708Sstevel return (EFAULT);
8541708Sstevel }
8551708Sstevel
8561708Sstevel ssm_loaded_sbd = FALSE;
8571708Sstevel softsp->initialized = FALSE;
8581708Sstevel }
8591708Sstevel #endif
8601708Sstevel
8611708Sstevel
8621708Sstevel default: {
8631708Sstevel char event = 0;
8641708Sstevel
8657578SVijay.Balakrishna@Sun.COM sbd_ioctl = (int (*) (dev_t, int, intptr_t, int, char *))
8667578SVijay.Balakrishna@Sun.COM modlookup("misc/sbd", "sbd_ioctl");
8671708Sstevel
8681708Sstevel if (sbd_ioctl)
8691708Sstevel rv = (*sbd_ioctl) (dev, cmd, arg, mode, &event);
8701708Sstevel else {
8711708Sstevel cmn_err(CE_WARN, "cannot find sbd_ioctl");
8721708Sstevel return (ENXIO);
8731708Sstevel }
8741708Sstevel /*
8751708Sstevel * Check to see if we need to send an event
8761708Sstevel */
8771708Sstevel if (event == 1) {
8781708Sstevel int slot;
8791708Sstevel int hint = SE_NO_HINT;
8801708Sstevel
8811708Sstevel if (rv == 0) {
8821708Sstevel if (cmd == SBD_CMD_CONNECT ||
8831708Sstevel cmd == SBD_CMD_CONFIGURE)
8841708Sstevel hint = SE_HINT_INSERT;
8851708Sstevel else if (cmd == SBD_CMD_UNCONFIGURE ||
8861708Sstevel cmd == SBD_CMD_DISCONNECT)
8871708Sstevel hint = SE_HINT_REMOVE;
8881708Sstevel }
8891708Sstevel
8901708Sstevel slot = (getminor(dev) & SSM_BOARD_MASK);
891*11311SSurya.Prakki@Sun.COM (void) ssm_generate_event(softsp->ssm_nodeid, slot,
892*11311SSurya.Prakki@Sun.COM hint);
8931708Sstevel }
8941708Sstevel break;
8951708Sstevel }
8961708Sstevel }
8971708Sstevel
8981708Sstevel return (rv);
8991708Sstevel }
9001708Sstevel
9011708Sstevel void
ssm_get_attch_pnt(int node,int board,char * attach_pnt)9021708Sstevel ssm_get_attch_pnt(int node, int board, char *attach_pnt)
9031708Sstevel {
9041708Sstevel struct ssm_node2inst *sp;
9051708Sstevel
9061708Sstevel /*
9071708Sstevel * Hold this mutex, until we are done so that ssm dip
9081708Sstevel * doesn't detach.
9091708Sstevel */
9101708Sstevel mutex_enter(&ssm_node2inst_lock);
9111708Sstevel
9121708Sstevel for (sp = &ssm_node2inst_map; sp != NULL; sp = sp->next) {
9131708Sstevel if (sp->inst == -1)
9141708Sstevel continue;
9151708Sstevel if (sp->nodeid == node)
9161708Sstevel break;
9171708Sstevel }
9181708Sstevel
9191708Sstevel if (sp == NULL) {
9201708Sstevel /* We didn't find the ssm dip, return failure */
9211708Sstevel attach_pnt[0] = '\0';
9221708Sstevel mutex_exit(&ssm_node2inst_lock);
9231708Sstevel return;
9241708Sstevel }
9251708Sstevel
9261708Sstevel /*
9271708Sstevel * we have the instance, and the board, construct the attch pnt
9281708Sstevel */
9291708Sstevel if (SG_BOARD_IS_CPU_TYPE(board))
9301708Sstevel (void) sprintf(attach_pnt, "ssm%d:N%d.SB%d",
9311708Sstevel sp->inst, node, board);
9321708Sstevel else
9331708Sstevel (void) sprintf(attach_pnt, "ssm%d:N%d.IB%d",
9341708Sstevel sp->inst, node, board);
9351708Sstevel
9361708Sstevel mutex_exit(&ssm_node2inst_lock);
9371708Sstevel }
9381708Sstevel
9391708Sstevel /*
9401708Sstevel * Generate an event to sysevent
9411708Sstevel */
9421708Sstevel static int
ssm_generate_event(int node,int board,int hint)9431708Sstevel ssm_generate_event(int node, int board, int hint)
9441708Sstevel {
9451708Sstevel sysevent_t *ev;
9461708Sstevel sysevent_id_t eid;
9471708Sstevel int rv = 0;
9481708Sstevel sysevent_value_t evnt_val;
9491708Sstevel sysevent_attr_list_t *evnt_attr_list = NULL;
9501708Sstevel char attach_pnt[MAXPATHLEN];
9511708Sstevel
9521708Sstevel
9531708Sstevel attach_pnt[0] = '\0';
9541708Sstevel ssm_get_attch_pnt(node, board, attach_pnt);
9551708Sstevel
9561708Sstevel if (attach_pnt[0] == '\0')
9571708Sstevel return (-1);
9581708Sstevel
9591708Sstevel ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, EP_DDI,
9601708Sstevel KM_SLEEP);
9611708Sstevel evnt_val.value_type = SE_DATA_TYPE_STRING;
9621708Sstevel evnt_val.value.sv_string = attach_pnt;
9631708Sstevel
9641708Sstevel rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val, KM_SLEEP);
9651708Sstevel if (rv != 0) {
9661708Sstevel cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
9671708Sstevel DR_AP_ID, EC_DR);
9681708Sstevel sysevent_free(ev);
9691708Sstevel return (rv);
9701708Sstevel }
9711708Sstevel
9721708Sstevel /*
9731708Sstevel * Add the hint
9741708Sstevel */
9751708Sstevel evnt_val.value_type = SE_DATA_TYPE_STRING;
9761708Sstevel evnt_val.value.sv_string = SE_HINT2STR(hint);
9771708Sstevel
9781708Sstevel rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val, KM_SLEEP);
9791708Sstevel if (rv != 0) {
9801708Sstevel cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
9811708Sstevel DR_HINT, EC_DR);
9821708Sstevel sysevent_free_attr(evnt_attr_list);
9831708Sstevel sysevent_free(ev);
9841708Sstevel return (-1);
9851708Sstevel }
9861708Sstevel
9871708Sstevel if (sysevent_attach_attributes(ev, evnt_attr_list) != 0) {
9881708Sstevel cmn_err(CE_WARN, "Failed to attach attr list for %s event",
9891708Sstevel EC_DR);
9901708Sstevel sysevent_free_attr(evnt_attr_list);
9911708Sstevel sysevent_free(ev);
9921708Sstevel return (-1);
9931708Sstevel }
9941708Sstevel
9951708Sstevel rv = log_sysevent(ev, KM_NOSLEEP, &eid);
9961708Sstevel if (rv != 0) {
9971708Sstevel cmn_err(CE_WARN, "ssm_dr_event_handler: failed to log event");
9981708Sstevel }
9991708Sstevel
10001708Sstevel sysevent_free(ev);
10011708Sstevel
10021708Sstevel return (rv);
10031708Sstevel }
10041708Sstevel
10051708Sstevel /*
10061708Sstevel * DR Event Handler
10071708Sstevel */
10081708Sstevel uint_t
ssm_dr_event_handler(char * arg)10091708Sstevel ssm_dr_event_handler(char *arg)
10101708Sstevel {
10111708Sstevel sg_system_fru_descriptor_t *fdp;
10121708Sstevel int hint;
10131708Sstevel
10141708Sstevel
10151708Sstevel fdp = (sg_system_fru_descriptor_t *)(((sbbc_msg_t *)arg)->msg_buf);
10161708Sstevel if (fdp == NULL) {
10171708Sstevel DPRINTF(SSM_EVENT_DEBUG,
10181708Sstevel ("ssm_dr_event_handler: ARG is null\n"));
10191708Sstevel return (DDI_INTR_CLAIMED);
10201708Sstevel }
10211708Sstevel #ifdef DEBUG
10221708Sstevel DPRINTF(SSM_EVENT_DEBUG, ("ssm_dr_event_handler called\n"));
10231708Sstevel DPRINTF(SSM_EVENT_DEBUG, ("\tnode\t%d\n", fdp->node));
10241708Sstevel DPRINTF(SSM_EVENT_DEBUG, ("\tslot\t%d\n", fdp->slot));
10251708Sstevel DPRINTF(SSM_EVENT_DEBUG, ("\tparent_hdl\t0x%lx\n", fdp->parent_hdl));
10261708Sstevel DPRINTF(SSM_EVENT_DEBUG, ("\tchild_hdl\t0x%lx\n", fdp->child_hdl));
10271708Sstevel DPRINTF(SSM_EVENT_DEBUG, ("\tevent_details\t%s\n",
10281708Sstevel EVNT2STR(fdp->event_details)));
10291708Sstevel #endif
10301708Sstevel
10311708Sstevel switch (fdp->event_details) {
10321708Sstevel case SG_EVT_BOARD_ABSENT:
10331708Sstevel hint = SE_HINT_REMOVE;
10341708Sstevel break;
10351708Sstevel case SG_EVT_BOARD_PRESENT:
10361708Sstevel hint = SE_HINT_INSERT;
10371708Sstevel break;
10381708Sstevel default:
10391708Sstevel hint = SE_NO_HINT;
10401708Sstevel break;
10411708Sstevel
10421708Sstevel }
10431708Sstevel
1044*11311SSurya.Prakki@Sun.COM (void) ssm_generate_event(fdp->node, fdp->slot, hint);
10451708Sstevel
10461708Sstevel return (DDI_INTR_CLAIMED);
10471708Sstevel }
10481708Sstevel
10491708Sstevel /*
10501708Sstevel * Initialize our FMA resources
10511708Sstevel */
10521708Sstevel static void
ssm_fm_init(struct ssm_soft_state * softsp)10531708Sstevel ssm_fm_init(struct ssm_soft_state *softsp)
10541708Sstevel {
10551708Sstevel softsp->ssm_fm_cap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE |
10561708Sstevel DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
10571708Sstevel
10581708Sstevel /*
10591708Sstevel * Request or capability level and get our parents capability
10601708Sstevel * and ibc.
10611708Sstevel */
10621708Sstevel ddi_fm_init(softsp->dip, &softsp->ssm_fm_cap, &softsp->ssm_fm_ibc);
10631708Sstevel ASSERT((softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE) &&
10641708Sstevel (softsp->ssm_fm_cap & DDI_FM_ERRCB_CAPABLE));
10651708Sstevel /*
10661708Sstevel * Register error callback with our parent.
10671708Sstevel */
10681708Sstevel ddi_fm_handler_register(softsp->dip, ssm_err_callback, NULL);
10691708Sstevel }
10701708Sstevel
10711708Sstevel /*
10721708Sstevel * Breakdown our FMA resources
10731708Sstevel */
10741708Sstevel static void
ssm_fm_fini(struct ssm_soft_state * softsp)10751708Sstevel ssm_fm_fini(struct ssm_soft_state *softsp)
10761708Sstevel {
10771708Sstevel /*
10781708Sstevel * Clean up allocated fm structures
10791708Sstevel */
10801708Sstevel ASSERT(softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE);
10811708Sstevel ddi_fm_handler_unregister(softsp->dip);
10821708Sstevel ddi_fm_fini(softsp->dip);
10831708Sstevel }
10841708Sstevel
10851708Sstevel /*
10861708Sstevel * Initialize FMA resources for children devices. Called when
10871708Sstevel * child calls ddi_fm_init().
10881708Sstevel */
10891708Sstevel /*ARGSUSED*/
10901708Sstevel static int
ssm_fm_init_child(dev_info_t * dip,dev_info_t * tdip,int cap,ddi_iblock_cookie_t * ibc)10911708Sstevel ssm_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap,
10921708Sstevel ddi_iblock_cookie_t *ibc)
10931708Sstevel {
10941708Sstevel struct ssm_soft_state *softsp = ddi_get_soft_state(ssm_softstates,
10957578SVijay.Balakrishna@Sun.COM ddi_get_instance(dip));
10961708Sstevel
10971708Sstevel *ibc = softsp->ssm_fm_ibc;
10981708Sstevel return (softsp->ssm_fm_cap);
10991708Sstevel }
11001708Sstevel
11011708Sstevel /*
11021708Sstevel * FMA registered error callback
11031708Sstevel */
11041708Sstevel /*ARGSUSED*/
11051708Sstevel static int
ssm_err_callback(dev_info_t * dip,ddi_fm_error_t * derr,const void * impl_data)11061708Sstevel ssm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data)
11071708Sstevel {
11081708Sstevel /* Call our children error handlers */
11091708Sstevel return (ndi_fm_handler_dispatch(dip, NULL, derr));
11101708Sstevel }
1111