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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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