/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * * Serengeti CompactPCI Hot Swap Controller Driver. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Debug flags */ int sghsc_configure_ack = 0; int cpci_enable = 1; #ifdef DEBUG #define SGHSC_DEBUG #endif #ifdef SGHSC_DEBUG int sghsc_debug = 0; #define DEBUGF(level, args) \ { if (sghsc_debug >= (level)) cmn_err args; } #define DEBUGON sghsc_debug = 3 #define DEBUGOFF sghsc_debug = 0 #else #define DEBUGF(level, args) /* nothing */ #define DEBUGON #define DEBUGOFF #endif /* * Global data */ static void *sghsc_state; /* soft state */ static sghsc_rb_head_t sghsc_rb_header; /* ring buffer header */ /* * Definitions for events thread (outside interrupt context), mutex and * condition variable. */ static kthread_t *sghsc_event_thread; static kmutex_t sghsc_event_thread_mutex; static kcondvar_t sghsc_event_thread_cv; static boolean_t sghsc_event_thread_exit = B_FALSE; static struct cb_ops sghsc_cb_ops = { nodev, /* open */ nodev, /* close */ nodev, /* strategy */ nodev, /* print */ nodev, /* dump */ nodev, /* read */ nodev, /* write */ nodev, /* ioctl */ nodev, /* devmap */ nodev, /* mmap */ nodev, /* segmap */ nochpoll, /* poll */ ddi_prop_op, /* prop_op */ 0, /* streamtab */ D_NEW | D_MP, /* Driver compatibility flag */ CB_REV, /* rev */ nodev, /* int (*cb_aread)() */ nodev /* int (*cb_awrite)() */ }; /* * Function prototype for dev_ops */ static int sghsc_attach(dev_info_t *, ddi_attach_cmd_t); static int sghsc_detach(dev_info_t *, ddi_detach_cmd_t); static struct dev_ops sghsc_dev_ops = { DEVO_REV, /* devo_rev, */ 0, /* refcnt */ nulldev, /* get_dev_info */ nulldev, /* identify */ nulldev, /* probe */ sghsc_attach, /* attach */ sghsc_detach, /* detach */ nodev, /* reset */ &sghsc_cb_ops, /* driver operations */ (struct bus_ops *)0, /* no bus operations */ NULL, /* power */ ddi_quiesce_not_needed, /* quiesce */ }; static struct modldrv modldrv = { &mod_driverops, "Serengeti CompactPCI HSC", &sghsc_dev_ops, }; static struct modlinkage modlinkage = { MODREV_1, &modldrv, NULL }; /* * Function prototype for HP support */ static int sghsc_connect(caddr_t, hpc_slot_t slot, void *, uint_t); static int sghsc_disconnect(caddr_t, hpc_slot_t, void *, uint_t); static int sghsc_control(caddr_t, hpc_slot_t, int, caddr_t); /* * Function prototypes for internal functions */ static int sghsc_register_slots(sghsc_t *, int); static int sghsc_get_slotnum(sghsc_t *, hpc_slot_t); static int sghsc_scctl(int, int, int, int, int *); static void sghsc_freemem(sghsc_t *); static hpc_slot_t sghsc_find_sloth(int, int, int); static sghsc_t *sghsc_find_softstate(int, int, int); static int sghsc_led_state(sghsc_t *, hpc_slot_t, int, hpc_led_info_t *); static void sghsc_rb_setup(sghsc_rb_head_t *); static void sghsc_rb_teardown(sghsc_rb_head_t *); static int sghsc_rb_get(sghsc_rb_head_t *, sghsc_event_t *); static int sghsc_rb_put(sghsc_rb_head_t *, sghsc_event_t *); /* * Patchable timeout value */ int sghsc_mbx_timeout = SGHSC_MBX_TIMEOUT; /* * Data for self-identification. This will help enumerate all soft states. */ static int sghsc_maxinst; /* * Six slot boat and four slot boats are different in topology (slot to * bus assignment) and here we should have 2 separate maps (the first 3 * slots have the same topology). The map is in the "delta" form. Logical * slots correspond to indexes in the map. */ static sdesc_t four_slot_wib_bd[] = { 0, 6, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 0 - Schizo0/A */ 1, 0, 2, 0, /* logical/physical slot 1 - paroli2 */ 1, 0, 0, 0, /* logical/physical slot 2 - paroli0 */ 0, 7, 1, HPC_SLOT_TYPE_CPCI /* logical/physical slot 3 - Schizo0/B */ }; static sdesc_t four_slot_bd[] = { 0, 6, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 0 - Schizo0/A */ 1, 6, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 1 - Schizo1/A */ 0, 7, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 2 - Schizo0/B */ 1, 7, 1, HPC_SLOT_TYPE_CPCI /* logical/physical slot 3 - Schizo1/B */ }; static sdesc_t six_slot_wib_bd[] = { 0, 6, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 0 - Schizo0/A */ 1, 0, 2, 0, /* logical/physical slot 1 - paroli2 */ 1, 0, 0, 0, /* logical/physical slot 2 - paroli0 */ 0, 7, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 3 - Schizo0/B */ 0, 7, 2, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 4 - Schizo0/B */ 0, 7, 3, HPC_SLOT_TYPE_CPCI /* logical/physical slot 5 - Schizo0/B */ }; static sdesc_t six_slot_bd[] = { 0, 6, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 0 - Schizo0/A */ 1, 6, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 1 - Schizo1/A */ 0, 7, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 2 - Schizo0/B */ 0, 7, 2, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 3 - Schizo0/B */ 1, 7, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 4 - Schizo1/B */ 1, 7, 2, HPC_SLOT_TYPE_CPCI /* logical/physical slot 5 - Schizo1/B */ }; /* * DR event handlers * We want to register the event handlers once for all instances. In the * other hand we have register them after the sghsc has been attached. * event_initialize gives us the logic of only registering the events only * once. The event thread will do all the work when called from interrupts. */ int sghsc_event_init = 0; static uint_t sghsc_event_handler(char *); static void sghsc_event_thread_code(void); /* * DR event msg and payload */ static sbbc_msg_t event_msg; static sghsc_event_t payload; /* * Event lock and state */ static kmutex_t sghsc_event_lock; int sghsc_event_state; int _init(void) { int error; sghsc_maxinst = 0; if ((error = ddi_soft_state_init(&sghsc_state, sizeof (sghsc_t), 1)) != 0) return (error); if ((error = mod_install(&modlinkage)) != 0) { ddi_soft_state_fini(&sghsc_state); return (error); } sghsc_rb_header.buf = NULL; mutex_init(&sghsc_event_thread_mutex, NULL, MUTEX_DRIVER, NULL); cv_init(&sghsc_event_thread_cv, NULL, CV_DRIVER, NULL); return (error); } int _fini(void) { int error; if ((error = mod_remove(&modlinkage)) != 0) return (error); /* * Unregister the event handler */ (void) sbbc_mbox_unreg_intr(MBOX_EVENT_CPCI_ENUM, sghsc_event_handler); mutex_destroy(&sghsc_event_lock); /* * Kill the event thread if it is running. */ if (sghsc_event_thread != NULL) { mutex_enter(&sghsc_event_thread_mutex); sghsc_event_thread_exit = B_TRUE; /* * Goes to the thread at once. */ cv_signal(&sghsc_event_thread_cv); /* * Waiting for the response from the thread. */ cv_wait(&sghsc_event_thread_cv, &sghsc_event_thread_mutex); mutex_exit(&sghsc_event_thread_mutex); sghsc_event_thread = NULL; } mutex_destroy(&sghsc_event_thread_mutex); cv_destroy(&sghsc_event_thread_cv); /* * tear down shared, global ring buffer now that it is safe to * do so because sghsc_event_handler has been unregistered and * sghsc_event_thread_code has exited */ sghsc_rb_teardown(&sghsc_rb_header); sghsc_maxinst = 0; ddi_soft_state_fini(&sghsc_state); return (0); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } /* * sghsc_attach() */ /* ARGSUSED */ static int sghsc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { sghsc_t *sghsc; uint_t instance; uint_t portid; int rc; int board_type = 0; instance = ddi_get_instance(dip); switch (cmd) { case DDI_RESUME: return (DDI_SUCCESS); case DDI_ATTACH: break; default: cmn_err(CE_WARN, "sghsc%d: unsupported cmd %d", instance, cmd); return (DDI_FAILURE); } DEBUGF(1, (CE_NOTE, "attach sghsc driver. ")); /* Fetch Safari Extended Agent ID of this device. */ portid = (uint_t)ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "portid", -1); if (!SG_PORTID_IS_IO_TYPE(portid)) { cmn_err(CE_WARN, "sghsc%d: property %s out of bounds %d\n", instance, "portid", portid); return (DDI_FAILURE); } if (ddi_soft_state_zalloc(sghsc_state, instance) != DDI_SUCCESS) return (DDI_FAILURE); sghsc = (sghsc_t *)ddi_get_soft_state(sghsc_state, instance); sghsc->sghsc_dip = dip; sghsc->sghsc_instance = instance; sghsc->sghsc_board = SG_PORTID_TO_BOARD_NUM(portid); sghsc->sghsc_node_id = SG_PORTID_TO_NODEID(portid); sghsc->sghsc_portid = portid; ddi_set_driver_private(dip, sghsc); mutex_init(SGHSC_MUTEX(sghsc), NULL, MUTEX_DRIVER, NULL); rc = sghsc_scctl(SGHSC_GET_NUM_SLOTS, sghsc->sghsc_node_id, sghsc->sghsc_board, 0, (int *)&sghsc->sghsc_num_slots); if (rc) { cmn_err(CE_WARN, "sghsc%d: unable to size node %d / board %d", instance, sghsc->sghsc_node_id, sghsc->sghsc_board); goto cleanup_stage2; } DEBUGF(1, (CE_NOTE, "sghsc%d: node %d / board %d has %d slots", instance, sghsc->sghsc_node_id, sghsc->sghsc_board, sghsc->sghsc_num_slots)); switch (sghsc->sghsc_num_slots) { case 4: case 6: rc = 0; break; default: rc = -1; break; } if (rc) { cmn_err(CE_WARN, "sghsc%d: wrong num of slots %d for node %d" " / board %d", instance, sghsc->sghsc_num_slots, sghsc->sghsc_node_id, sghsc->sghsc_board); goto cleanup_stage2; } rc = sghsc_scctl(SGHSC_GET_CPCI_BOARD_TYPE, sghsc->sghsc_node_id, sghsc->sghsc_board, 0, &board_type); DEBUGF(1, (CE_NOTE, "sghsc%d: node %d / board %d is type %d", instance, sghsc->sghsc_node_id, sghsc->sghsc_board, board_type)); sghsc->sghsc_slot_table = (sghsc_slot_t *)kmem_zalloc((size_t) (sghsc->sghsc_num_slots * sizeof (sghsc_slot_t)), KM_SLEEP); if (sghsc_register_slots(sghsc, board_type) != DDI_SUCCESS) { DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_register_slots" " failed for node %d / board %d", instance, sghsc->sghsc_node_id, sghsc->sghsc_board)); goto cleanup; } if (sghsc_connect((caddr_t)sghsc, 0, 0, SGHSC_ALL_SLOTS_ENABLE) != HPC_SUCCESS) { DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_connect failed for" " node %d / board %d", instance, sghsc->sghsc_node_id, sghsc->sghsc_board)); goto cleanup; } if (sghsc_event_init == 0) { /* * allocate shared, global ring buffer before registering * sghsc_event_handler and before starting * sghsc_event_thread_code */ sghsc_rb_setup(&sghsc_rb_header); /* * Regiter cpci DR event handler * */ mutex_init(&sghsc_event_lock, NULL, MUTEX_DRIVER, NULL); event_msg.msg_buf = (caddr_t)&payload; event_msg.msg_len = sizeof (payload); rc = sbbc_mbox_reg_intr(MBOX_EVENT_CPCI_ENUM, sghsc_event_handler, &event_msg, (uint_t *)&sghsc_event_state, &sghsc_event_lock); if (rc != 0) cmn_err(CE_WARN, "sghsc%d: failed to register events" " for node %d", instance, sghsc->sghsc_node_id); sghsc_event_init = 1; /* * Create the event thread if it is not already created. */ if (sghsc_event_thread == NULL) { DEBUGF(1, (CE_NOTE, "sghsc: creating event thread" "for node %d", sghsc->sghsc_node_id)); sghsc_event_thread = thread_create(NULL, 0, sghsc_event_thread_code, NULL, 0, &p0, TS_RUN, minclsyspri); } } ddi_report_dev(dip); /* * Grossly bump up the instance counter. We may have holes inside. */ sghsc_maxinst++; sghsc->sghsc_valid = 1; return (DDI_SUCCESS); cleanup: /* * Free up allocated resources and return error * sghsc_register_slots => unregister all slots */ sghsc_freemem(sghsc); cleanup_stage2: DEBUGF(1, (CE_NOTE, "sghsc%d: attach failed for node %d", instance, sghsc->sghsc_node_id)); mutex_destroy(SGHSC_MUTEX(sghsc)); ddi_set_driver_private(dip, NULL); ddi_soft_state_free(sghsc_state, instance); return (DDI_FAILURE); } /* * detach(9E) */ /* ARGSUSED */ static int sghsc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { sghsc_t *sghsc; int instance; int i; instance = ddi_get_instance(dip); sghsc = (sghsc_t *)ddi_get_soft_state(sghsc_state, instance); if (sghsc == NULL) return (DDI_FAILURE); switch (cmd) { case DDI_DETACH: /* * We don't allow to detach in case the pci nexus * didn't run pcihp_uninit(). The buses should be * unregistered by now, otherwise slot info will be * corrupted on the next 'cfgadm'. */ for (i = 0; i < sghsc->sghsc_num_slots; i++) { if (sghsc->sghsc_slot_table[i].handle && hpc_bus_registered( sghsc->sghsc_slot_table[i].handle)) { cmn_err(CE_WARN, "sghsc: must detach buses first"); return (DDI_FAILURE); } } if (mutex_tryenter(&sghsc_event_thread_mutex) == 0) return (EBUSY); sghsc->sghsc_valid = 0; sghsc_freemem(sghsc); mutex_destroy(SGHSC_MUTEX(sghsc)); ddi_set_driver_private(dip, NULL); ddi_soft_state_free(sghsc_state, instance); /* * Grossly decrement the counter. We may have holes inside. */ if (instance == (sghsc_maxinst - 1)) sghsc_maxinst--; mutex_exit(&sghsc_event_thread_mutex); return (DDI_SUCCESS); case DDI_SUSPEND: return (DDI_SUCCESS); default: return (DDI_FAILURE); } } /* * Set up and register slot 0 to num_slots with hotplug * framework * Assume SGHSC_MUTEX is held * * Return val: DDI_SUCCESS * DDI_FAILURE */ static int sghsc_register_slots(sghsc_t *sghsc, int board_type) { int i; dev_info_t *dip = sghsc->sghsc_dip; hpc_slot_ops_t *slot_ops = NULL; sdesc_t *slot2bus; DEBUGF(1, (CE_NOTE, "sghsc%d: slot table has %d entries for " "node %d / board %d", sghsc->sghsc_instance, sghsc->sghsc_num_slots, sghsc->sghsc_node_id, sghsc->sghsc_board)); if ((cpci_enable == 0) || (sg_prom_cpci_dr_check() != 0)) return (DDI_SUCCESS); if (sghsc->sghsc_slot_table == NULL) return (DDI_FAILURE); switch (board_type) { /* * If the GET_CPCI_BOARD_TYPE request failed, board type * will be NO_BOARD_TYPE. In that case, assume it is an * io boat and make board type determination based on the * number of slots. */ case NO_BOARD_TYPE: case CPCI_BOARD: case SP_CPCI_BOARD: switch (sghsc->sghsc_num_slots) { case 4: slot2bus = four_slot_bd; break; case 6: slot2bus = six_slot_bd; break; default: cmn_err(CE_WARN, "sghsc%d: unknown size %d for" " node %d / board %d", sghsc->sghsc_instance, sghsc->sghsc_num_slots, sghsc->sghsc_node_id, sghsc->sghsc_board); break; } break; case WCI_CPCI_BOARD: slot2bus = four_slot_wib_bd; break; case WCI_SP_CPCI_BOARD: slot2bus = six_slot_wib_bd; break; default: cmn_err(CE_WARN, "sghsc%d: unknown type %d for" " node %d / board %d", sghsc->sghsc_instance, board_type, sghsc->sghsc_node_id, sghsc->sghsc_board); return (DDI_FAILURE); } /* * constructing the slot table array and register the * slot with the HPS * we don't depend on the .conf file */ for (i = 0; i < sghsc->sghsc_num_slots; i++) { char *nexuspath; hpc_slot_info_t *slot_info; uint32_t base_id; /* * Some kind of black list may be needed */ /* * Need to talk to SC and get slot info and set slot state: * 1. slot status * 2. slot capabilities * 3. LED status * 4. get bus num */ /* * fill up nexuspath, extended id is used instead of the * local one, the node id is encoded in the path twice. */ base_id = sghsc->sghsc_portid & SGHSC_SAFARI_ID_EVEN; nexuspath = sghsc->sghsc_slot_table[i].nexus_path; (void) sprintf(nexuspath, SGHSC_PATH, sghsc->sghsc_node_id, (base_id + slot2bus[i].agent_delta), slot2bus[i].off); sghsc->sghsc_slot_table[i].pci_device_num = slot2bus[i].pcidev; /* * fill up slot_info */ slot_info = &sghsc->sghsc_slot_table[i].slot_info; slot_info->version = HPC_SLOT_INFO_VERSION; slot_info->slot_type = slot2bus[i].slot_type; /* capabilities need to be discovered via SC */ slot_info->pci_slot_capabilities = HPC_SLOT_64BITS; slot_info->pci_dev_num = slot2bus[i].pcidev; (void) sprintf(slot_info->pci_slot_name, "sg%dslot%d", sghsc->sghsc_board, i); DEBUGF(1, (CE_NOTE, "pci_slot_name is %s at pci_dev_num %d" " on node %d / board %d", slot_info->pci_slot_name, slot_info->pci_dev_num, sghsc->sghsc_node_id, sghsc->sghsc_board)); /* * allocate and fill up slot_ops */ slot_ops = hpc_alloc_slot_ops(KM_SLEEP); sghsc->sghsc_slot_table[i].slot_ops = slot_ops; /* assign slot ops for HPS */ slot_ops->hpc_version = HPC_SLOT_OPS_VERSION; slot_ops->hpc_op_connect = sghsc_connect; slot_ops->hpc_op_disconnect = sghsc_disconnect; slot_ops->hpc_op_insert = nodev; slot_ops->hpc_op_remove = nodev; slot_ops->hpc_op_control = sghsc_control; /* * HA (Full Hot Swap) is the default mode of operation * but the type of the board is set conservstively as * sghsc has no way of knowing it. The HP Framwork will * overwrite the value set at boot time. */ sghsc->sghsc_slot_table[i].flags = SGHSC_SLOT_AUTO_CFG_EN; sghsc->sghsc_slot_table[i].board_type = HPC_BOARD_UNKNOWN; /* Only register CPCI slots */ if (slot_info->slot_type != HPC_SLOT_TYPE_CPCI) { DEBUGF(1, (CE_NOTE, "sghsc_register_slots: " "slot %d is non-cpci", i)); continue; } /* * register slots */ if ((hpc_slot_register(dip, nexuspath, slot_info, &sghsc->sghsc_slot_table[i].handle, slot_ops, (caddr_t)sghsc, 0)) != 0) { /* * return failure and let attach() * do the cleanup */ cmn_err(CE_WARN, "sghsc%d: Slot <%s> failed during HPS" " registration process for node %d / board %d", sghsc->sghsc_instance, slot_info->pci_slot_name, sghsc->sghsc_node_id, sghsc->sghsc_board); return (DDI_FAILURE); } } DEBUGF(1, (CE_NOTE, "sghsc registered successfully for" " node %d / board %d", sghsc->sghsc_node_id, sghsc->sghsc_board)); return (DDI_SUCCESS); } /* * Connecting a slot or all slots * State Diagram: * states * hw bits EMPTY DISCONNECT CONNECT * slot_enable NO NO YES * card_present NO YES YES * slot_switch N/A NO/YES YES * * Return val: HPC_SUCCESS if the slot(s) are enabled * HPC_ERR_FAILED if the slot can't be enabled */ /* ARGSUSED */ static int sghsc_connect(caddr_t op_arg, hpc_slot_t sloth, void *data, uint_t flag) { int i = 0; sghsc_t *sghsc = (sghsc_t *)op_arg; int rc; int result; int slot_num = sghsc_get_slotnum(sghsc, sloth); switch (flag) { case SGHSC_ALL_SLOTS_ENABLE: for (i = 0; i < sghsc->sghsc_num_slots; i++) { /* * All slots will be marked 'empty' as HP Framework * will try to connect those which have no kernel node. */ sghsc->sghsc_slot_table[i].slot_status = HPC_SLOT_EMPTY; } return (HPC_SUCCESS); } if (slot_num == -1) return (HPC_ERR_INVALID); SGHSC_MUTEX_ENTER(sghsc); DEBUGF(1, (CE_NOTE, "sghsc%d: connecting logical slot%d for" " node %d / board %d", sghsc->sghsc_instance, slot_num, sghsc->sghsc_node_id, sghsc->sghsc_board)); /* * Powering an empty slot is highly illegal so far * (before SC implemented a constant poll). Otherwise * it breaks ddi framework and HP. The workaround * is to check for a card first. */ rc = sghsc_scctl(SGHSC_GET_SLOT_STATUS, sghsc->sghsc_node_id, sghsc->sghsc_board, slot_num, &result); if (rc == ETIMEDOUT) { SGHSC_MUTEX_EXIT(sghsc); return (HPC_ERR_FAILED); } if (rc) { cmn_err(CE_NOTE, "sghsc%d: unable to stat slot %d for" " node %d / board %d", sghsc->sghsc_instance, slot_num, sghsc->sghsc_node_id, sghsc->sghsc_board); sghsc->sghsc_slot_table[i].slot_status = HPC_SLOT_UNKNOWN; SGHSC_MUTEX_EXIT(sghsc); return (HPC_ERR_FAILED); } if ((result >> CPCI_STAT_SLOT_EMPTY_SHIFT) & ONE_BIT) { sghsc->sghsc_slot_table[i].slot_status = HPC_SLOT_EMPTY; SGHSC_MUTEX_EXIT(sghsc); return (HPC_ERR_FAILED); } rc = sghsc_scctl(SGHSC_SET_SLOT_POWER_ON, sghsc->sghsc_node_id, sghsc->sghsc_board, slot_num, &result); if (rc) { cmn_err(CE_WARN, "sghsc%d: unable to poweron slot %d for" " node %d / board %d", sghsc->sghsc_instance, slot_num, sghsc->sghsc_node_id, sghsc->sghsc_board); SGHSC_MUTEX_EXIT(sghsc); return (HPC_ERR_FAILED); } else { sghsc->sghsc_slot_table[slot_num].slot_status = HPC_SLOT_CONNECTED; } SGHSC_MUTEX_EXIT(sghsc); return (HPC_SUCCESS); } /* * Disconnecting a slot or slots * * return: HPC_SUCCESS if slot(s) are successfully disconnected * HPC_ERR_FAILED if slot(s) can't be disconnected * */ /* ARGSUSED */ static int sghsc_disconnect(caddr_t op_arg, hpc_slot_t sloth, void *data, uint_t flag) { sghsc_t *sghsc = (sghsc_t *)op_arg; int rc; int result; int slot_num = sghsc_get_slotnum(sghsc, sloth); switch (flag) { case SGHSC_ALL_SLOTS_DISABLE: return (HPC_SUCCESS); } if (slot_num == -1) return (HPC_ERR_INVALID); SGHSC_MUTEX_ENTER(sghsc); /* * Disconnecting an empty or disconnected slot * does't make sense. */ if (sghsc->sghsc_slot_table[slot_num].slot_status != HPC_SLOT_CONNECTED) { SGHSC_MUTEX_EXIT(sghsc); return (HPC_SUCCESS); } rc = sghsc_scctl(SGHSC_SET_SLOT_POWER_OFF, sghsc->sghsc_node_id, sghsc->sghsc_board, slot_num, &result); if (rc) { cmn_err(CE_WARN, "sghsc%d: unable to poweroff slot %d for" " node %d / board %d", sghsc->sghsc_instance, slot_num, sghsc->sghsc_node_id, sghsc->sghsc_board); SGHSC_MUTEX_EXIT(sghsc); return (HPC_ERR_FAILED); } else { sghsc->sghsc_slot_table[slot_num].slot_status = HPC_SLOT_DISCONNECTED; } SGHSC_MUTEX_EXIT(sghsc); return (HPC_SUCCESS); } /* * Entry point from the hotplug framework to do * the main hotplug operations * Return val: HPC_SUCCESS success on ops * HPC_NOT_SUPPORTED not supported feature * HPC_ERR_FAILED ops failed */ /*ARGSUSED*/ static int sghsc_control(caddr_t op_arg, hpc_slot_t sloth, int request, caddr_t arg) { sghsc_t *sghsc = (sghsc_t *)op_arg; int slot = sghsc_get_slotnum(sghsc, sloth); int error = HPC_SUCCESS; int rc; int result; if ((sghsc == NULL) || (slot < 0) || (slot >= sghsc->sghsc_num_slots)) { cmn_err(CE_WARN, "sghsc%d: sghsc_control fails with slot = %d" " max = %d, sloth = 0x%p for node %d / board %d", sghsc->sghsc_instance, slot, sghsc->sghsc_num_slots, sloth, sghsc->sghsc_node_id, sghsc->sghsc_board); return (HPC_ERR_INVALID); } SGHSC_MUTEX_ENTER(sghsc); switch (request) { case HPC_CTRL_GET_LED_STATE: { /* arg == hpc_led_info_t */ hpc_led_info_t *ledinfo; ledinfo = (hpc_led_info_t *)arg; DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_control" " HPC_CTRL_GET_LED_STATE for node %d / board %d slot %d", sghsc->sghsc_instance, sghsc->sghsc_node_id, sghsc->sghsc_board, slot)); switch (ledinfo->led) { case HPC_POWER_LED: case HPC_ATTN_LED: case HPC_FAULT_LED: case HPC_ACTIVE_LED: error = sghsc_led_state(sghsc, sloth, HPC_CTRL_GET_LED_STATE, ledinfo); break; default: cmn_err(CE_WARN, "sghsc%d: sghsc_control" " HPC_CTRL_GET_LED_STATE " " unknown led state %d for node %d / board %d" " slot handle 0x%p", sghsc->sghsc_instance, ledinfo->led, sghsc->sghsc_node_id, sghsc->sghsc_board, sloth); error = HPC_ERR_NOTSUPPORTED; break; } break; } case HPC_CTRL_SET_LED_STATE: { /* arg == hpc_led_info_t */ hpc_led_info_t *ledinfo; ledinfo = (hpc_led_info_t *)arg; DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_control" " HPC_CTRL_SET_LED_STATE for node %d / board %d slot %d", sghsc->sghsc_instance, sghsc->sghsc_node_id, sghsc->sghsc_board, slot)); switch (ledinfo->led) { case HPC_POWER_LED: case HPC_ATTN_LED: case HPC_FAULT_LED: case HPC_ACTIVE_LED: DEBUGF(1, (CE_NOTE, "sghsc:" " LED writing not supported ")); break; default: DEBUGF(1, (CE_NOTE, "sghsc:" " LED not supported ")); error = HPC_ERR_NOTSUPPORTED; } break; } case HPC_CTRL_GET_SLOT_STATE: { DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_control" " HPC_CTRL_GET_SLOT_STATE for node %d / board %d slot %d", sghsc->sghsc_instance, sghsc->sghsc_node_id, sghsc->sghsc_board, slot)); /* * Send mailbox cmd to SC to query the latest state */ rc = sghsc_scctl(SGHSC_GET_SLOT_STATUS, sghsc->sghsc_node_id, sghsc->sghsc_board, slot, &result); if (rc == ETIMEDOUT) { error = HPC_ERR_FAILED; break; } if (rc) { cmn_err(CE_NOTE, "sghsc%d: unable to stat slot %d for " "node %d / board %d", sghsc->sghsc_instance, slot, sghsc->sghsc_node_id, sghsc->sghsc_board); sghsc->sghsc_slot_table[slot].slot_status = HPC_SLOT_UNKNOWN; *(hpc_slot_state_t *)arg = HPC_SLOT_UNKNOWN; break; } /* * Update the cached state if needed. Initally all * slots are marked as empty for the Hot Plug Framwork. */ if ((result >> CPCI_STAT_SLOT_EMPTY_SHIFT) & ONE_BIT) { sghsc->sghsc_slot_table[slot].slot_status = HPC_SLOT_EMPTY; } else if ((result >> CPCI_STAT_POWER_ON_SHIFT) & ONE_BIT) { sghsc->sghsc_slot_table[slot].slot_status = HPC_SLOT_CONNECTED; } else if (sghsc->sghsc_slot_table[slot].slot_status == HPC_SLOT_EMPTY || sghsc->sghsc_slot_table[slot].slot_status == HPC_SLOT_UNKNOWN) { sghsc->sghsc_slot_table[slot].slot_status = HPC_SLOT_DISCONNECTED; } /* * No change */ *(hpc_slot_state_t *)arg = sghsc->sghsc_slot_table[slot].slot_status; break; } case HPC_CTRL_DEV_CONFIGURED: DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_control" " HPC_CTRL_DEV_CONFIGURED for node %d / board %d slot %d", sghsc->sghsc_instance, sghsc->sghsc_node_id, sghsc->sghsc_board, slot)); if (sghsc_configure_ack) cmn_err(CE_NOTE, "sghsc%d:" " node %d / board %d slot %d configured", sghsc->sghsc_instance, sghsc->sghsc_node_id, sghsc->sghsc_board, slot); /* * This is important to tell SC: * "start looking for ENUMs" */ if (sghsc->sghsc_slot_table[slot].flags & SGHSC_SLOT_AUTO_CFG_EN) (void) sghsc_scctl(SGHSC_SET_ENUM_CLEARED, sghsc->sghsc_node_id, sghsc->sghsc_board, slot, &result); break; case HPC_CTRL_DEV_UNCONFIGURED: /* * due to unclean drivers, unconfigure may leave * some state on card, configure may actually * use these invalid values. therefore, may force * disconnect. */ DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_control " "HPC_CTRL_DEV_UNCONFIGURED for node %d / board %d slot %d", sghsc->sghsc_instance, sghsc->sghsc_node_id, sghsc->sghsc_board, slot)); SGHSC_MUTEX_EXIT(sghsc); if (sghsc_disconnect(op_arg, sloth, 0, 0) != HPC_SUCCESS) { DEBUGF(1, (CE_NOTE, "sghsc_control: " "disconnect failed")); error = HPC_ERR_FAILED; } cmn_err(CE_NOTE, "sghsc%d: node %d / board %d " "slot %d unconfigured", sghsc->sghsc_instance, sghsc->sghsc_node_id, sghsc->sghsc_board, slot); return (error); case HPC_CTRL_GET_BOARD_TYPE: { /* arg = hpc_board_type_t */ DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_control" " HPC_CTRL_GET_BOARD_TYPE for node %d / board %d slot %d", sghsc->sghsc_instance, sghsc->sghsc_node_id, sghsc->sghsc_board, slot)); *(hpc_board_type_t *)arg = sghsc->sghsc_slot_table[slot].board_type; break; } case HPC_CTRL_ENABLE_AUTOCFG: DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_control" " HPC_CTRL_ENABLE_AUTOCFG for node %d / board %d slot %d", sghsc->sghsc_instance, sghsc->sghsc_node_id, sghsc->sghsc_board, slot)); sghsc->sghsc_slot_table[slot].flags |= SGHSC_SLOT_AUTO_CFG_EN; (void) hpc_slot_event_notify(sloth, HPC_EVENT_ENABLE_ENUM, HPC_EVENT_NORMAL); /* * Tell SC to start looking for ENUMs on this slot. */ rc = sghsc_scctl(SGHSC_SET_ENUM_CLEARED, sghsc->sghsc_node_id, sghsc->sghsc_board, slot, &result); if (rc) cmn_err(CE_WARN, "sghsc%d: unable to arm ENUM for" " node %d / board %d, slot %d", sghsc->sghsc_instance, sghsc->sghsc_node_id, sghsc->sghsc_board, slot); break; case HPC_CTRL_DISABLE_AUTOCFG: DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_control" " HPC_CTRL_DISABLE_AUTOCFG for node %d / board %d slot %d", sghsc->sghsc_instance, sghsc->sghsc_node_id, sghsc->sghsc_board, slot)); sghsc->sghsc_slot_table[slot].flags &= ~SGHSC_SLOT_AUTO_CFG_EN; (void) hpc_slot_event_notify(sloth, HPC_EVENT_DISABLE_ENUM, HPC_EVENT_NORMAL); break; case HPC_CTRL_DISABLE_SLOT: case HPC_CTRL_ENABLE_SLOT: break; /* need to add support for enable/disable_ENUM */ case HPC_CTRL_DISABLE_ENUM: case HPC_CTRL_ENABLE_ENUM: default: DEBUGF(1, (CE_CONT, "sghsc%d: sghsc_control " "request (0x%x) not supported", sghsc->sghsc_instance, request)); /* invalid request */ error = HPC_ERR_NOTSUPPORTED; } SGHSC_MUTEX_EXIT(sghsc); return (error); } /* * Read/write slot's led * Assume MUTEX_HELD * * return: HPC_SUCCESS if the led's status is avaiable, * SC return status otherwise. */ static int sghsc_led_state(sghsc_t *sghsc, hpc_slot_t sloth, int op, hpc_led_info_t *ledinfo) { int rval; int slot_num; int result; slot_num = sghsc_get_slotnum(sghsc, sloth); rval = sghsc_scctl(SGHSC_GET_SLOT_STATUS, sghsc->sghsc_node_id, sghsc->sghsc_board, slot_num, &result); if (rval != HPC_SUCCESS) return (rval); switch (op) { case HPC_CTRL_GET_LED_STATE: switch (ledinfo->led) { case HPC_POWER_LED: if ((result >> CPCI_STAT_LED_POWER_SHIFT) & ONE_BIT) ledinfo->state = HPC_LED_ON; else ledinfo->state = HPC_LED_OFF; break; case HPC_ATTN_LED: case HPC_FAULT_LED: if ((result >> CPCI_STAT_LED_FAULT_SHIFT) & ONE_BIT) ledinfo->state = HPC_LED_ON; else ledinfo->state = HPC_LED_OFF; break; case HPC_ACTIVE_LED: if ((result >> CPCI_STAT_LED_HP_SHIFT) & ONE_BIT) ledinfo->state = HPC_LED_ON; else ledinfo->state = HPC_LED_OFF; break; } break; case HPC_CTRL_SET_LED_STATE: return (HPC_ERR_NOTSUPPORTED); } return (HPC_SUCCESS); } /* * sghsc_get_slotnum() * get slot number from the slot handle * returns non-negative value to indicate slot number * -1 for failure */ static int sghsc_get_slotnum(sghsc_t *sghsc, hpc_slot_t sloth) { int i; if (sloth == NULL || sghsc == NULL) return (-1); for (i = 0; i < sghsc->sghsc_num_slots; i++) { if (sghsc->sghsc_slot_table[i].handle == sloth) return (i); } return (-1); } /* * sghsc_scctl() * mailbox interface * * return result code from mailbox operation */ static int sghsc_scctl(int cmd, int node_id, int board, int slot, int *resultp) { int ret = 0xbee; bitcmd_info_t cmd_info, *cmd_infop = &cmd_info; bitcmd_resp_t cmd_info_r, *cmd_info_r_p = &cmd_info_r; sbbc_msg_t request, *reqp = &request; sbbc_msg_t response, *resp = &response; cmd_infop->cmd_id = 0x01234567; cmd_infop->node_id = node_id; cmd_infop->board = board; cmd_infop->slot = slot; reqp->msg_type.type = CPCI_MBOX; reqp->msg_status = 0xeeeeffff; reqp->msg_len = sizeof (cmd_info); reqp->msg_bytes = 8; reqp->msg_buf = (caddr_t)cmd_infop; reqp->msg_data[0] = 0; reqp->msg_data[1] = 0; bzero(resp, sizeof (*resp)); bzero(cmd_info_r_p, sizeof (*cmd_info_r_p)); resp->msg_buf = (caddr_t)cmd_info_r_p; resp->msg_len = sizeof (cmd_info_r); resp->msg_type.type = CPCI_MBOX; resp->msg_bytes = 8; resp->msg_status = 0xddddffff; switch (cmd) { case SGHSC_GET_SLOT_STATUS: reqp->msg_type.sub_type = CPCI_GET_SLOT_STATUS; resp->msg_type.sub_type = CPCI_GET_SLOT_STATUS; reqp->msg_len -= 4; break; case SGHSC_GET_NUM_SLOTS: reqp->msg_type.sub_type = CPCI_GET_NUM_SLOTS; resp->msg_type.sub_type = CPCI_GET_NUM_SLOTS; reqp->msg_len -= 8; break; case SGHSC_SET_SLOT_STATUS_RESET: reqp->msg_type.sub_type = CPCI_SET_SLOT_STATUS; resp->msg_type.sub_type = CPCI_SET_SLOT_STATUS; cmd_infop->info = CPCI_SET_STATUS_SLOT_RESET; break; case SGHSC_SET_SLOT_STATUS_READY: reqp->msg_type.sub_type = CPCI_SET_SLOT_STATUS; resp->msg_type.sub_type = CPCI_SET_SLOT_STATUS; cmd_infop->info = CPCI_SET_STATUS_SLOT_READY; break; case SGHSC_SET_SLOT_FAULT_LED_ON: reqp->msg_type.sub_type = CPCI_SET_SLOT_FAULT_LED; resp->msg_type.sub_type = CPCI_SET_SLOT_FAULT_LED; cmd_infop->info = CPCI_SET_FAULT_LED_ON; break; case SGHSC_SET_SLOT_FAULT_LED_OFF: reqp->msg_type.sub_type = CPCI_SET_SLOT_FAULT_LED; resp->msg_type.sub_type = CPCI_SET_SLOT_FAULT_LED; cmd_infop->info = CPCI_SET_FAULT_LED_OFF; break; case SGHSC_SET_SLOT_FAULT_LED_KEEP: reqp->msg_type.sub_type = CPCI_SET_SLOT_FAULT_LED; resp->msg_type.sub_type = CPCI_SET_SLOT_FAULT_LED; cmd_infop->info = CPCI_SET_FAULT_LED_KEEP; break; case SGHSC_SET_SLOT_FAULT_LED_TOGGLE: reqp->msg_type.sub_type = CPCI_SET_SLOT_FAULT_LED; resp->msg_type.sub_type = CPCI_SET_SLOT_FAULT_LED; cmd_infop->info = CPCI_SET_FAULT_LED_TOGGLE; break; case SGHSC_SET_SLOT_POWER_OFF: reqp->msg_type.sub_type = CPCI_SET_SLOT_POWER; resp->msg_type.sub_type = CPCI_SET_SLOT_POWER; cmd_infop->info = CPCI_POWER_OFF; break; case SGHSC_SET_SLOT_POWER_ON: reqp->msg_type.sub_type = CPCI_SET_SLOT_POWER; resp->msg_type.sub_type = CPCI_SET_SLOT_POWER; cmd_infop->info = CPCI_POWER_ON; break; case SGHSC_GET_CPCI_BOARD_TYPE: reqp->msg_type.sub_type = CPCI_BOARD_TYPE; resp->msg_type.sub_type = CPCI_BOARD_TYPE; reqp->msg_len -= 8; break; case SGHSC_SET_ENUM_CLEARED: reqp->msg_type.sub_type = CPCI_SET_ENUM_CLEARED; resp->msg_type.sub_type = CPCI_SET_ENUM_CLEARED; break; default: cmn_err(CE_WARN, "sghsc: unrecognized action code 0x%x\n", cmd); } DEBUGF(1, (CE_NOTE, "sghsc: sending mbox command type=%d subtype=0x%x size=%d buf=%p", reqp->msg_type.type, reqp->msg_type.sub_type, reqp->msg_len, (void *)reqp->msg_buf)); DEBUGF(1, (CE_NOTE, "sghsc: sending buf cmd_id=0x%x node_id=0x%x board=0x%x " "slot=0x%x info=0x%x", cmd_infop->cmd_id, cmd_infop->node_id, cmd_infop->board, cmd_infop->slot, cmd_infop->info)); ret = sbbc_mbox_request_response(reqp, resp, sghsc_mbx_timeout); /* * The resp->msg_status field may contain an SC error or a common * error such as ETIMEDOUT. */ if ((ret != 0) || (resp->msg_status != SG_MBOX_STATUS_SUCCESS)) { DEBUGF(1, (CE_NOTE, "sghsc: mailbox command error = 0x%x, " "status = 0x%x", ret, resp->msg_status)); return (-1); } DEBUGF(1, (CE_NOTE, "sghsc: reply request status=0x%x", reqp->msg_status)); DEBUGF(1, (CE_NOTE, "sghsc: reply resp status=0x%x", resp->msg_status)); DEBUGF(1, (CE_NOTE, "sghsc: reply buf cmd_id=0x%x result=0x%x\n", cmd_info_r_p->cmd_id, cmd_info_r_p->result)); #ifdef DEBUG_EXTENDED if (cmd == SGHSC_GET_NUM_SLOTS) { DEBUGF(1, (CE_NOTE, "sghsc: node %d / board %d has %d slots", cmd_infop->node_id, cmd_infop->board, cmd_info_r_p->result)); *resultp = cmd_info_r_p->result; return (0); } if ((cmd_info_r_p->result >> CPCI_STAT_POWER_ON_SHIFT) & ONE_BIT) DEBUGF(1, (CE_NOTE, "sghsc: cpower on")); if ((cmd_info_r_p->result >> CPCI_STAT_LED_POWER_SHIFT) & ONE_BIT) DEBUGF(1, (CE_NOTE, "sghsc: power led on")); if ((cmd_info_r_p->result >> CPCI_STAT_LED_FAULT_SHIFT) & ONE_BIT) DEBUGF(1, (CE_NOTE, "sghsc: fault led on")); if ((cmd_info_r_p->result >> CPCI_STAT_LED_HP_SHIFT) & ONE_BIT) DEBUGF(1, (CE_NOTE, "sghsc: remove(hp) led on")); if ((cmd_info_r_p->result >> CPCI_STAT_SLOT_EMPTY_SHIFT) & ONE_BIT) DEBUGF(1, (CE_NOTE, "sghsc: slot empty")); tmp = ((cmd_info_r_p->result >> CPCI_STAT_HOT_SWAP_STATUS_SHIFT) & THREE_BITS); if (tmp) DEBUGF(1, (CE_NOTE, "sghsc: slot condition(hot swap status) is 0x%x", tmp)); if (cmd_info_r_p->result & CPCI_GET_STAT_SLOT_HZ_CAP) DEBUGF(1, (CE_NOTE, "sghsc: freq cap %x", cmd_info_r_p->result & CPCI_GET_STAT_SLOT_HZ_CAP)); if (cmd_info_r_p->result & CPCI_GET_STAT_SLOT_HZ_SET) DEBUGF(1, (CE_NOTE, "sghsc: freq setting %x", cmd_info_r_p->result & CPCI_GET_STAT_SLOT_HZ_SET)); if ((cmd_info_r_p->result >> CPCI_STAT_HEALTHY_SHIFT) & ONE_BIT) DEBUGF(1, (CE_NOTE, "sghsc: healthy")); if ((cmd_info_r_p->result >> CPCI_STAT_RESET_SHIFT) & ONE_BIT) DEBUGF(1, (CE_NOTE, "sghsc: in reset")); if (cmd_info_r_p->result & CPCI_GET_STAT_POWER_GOOD) DEBUGF(1, (CE_NOTE, "sghsc: power good")); if (cmd_info_r_p->result & CPCI_GET_STAT_POWER_FAULT) DEBUGF(1, (CE_NOTE, "sghsc: power fault")); if (cmd_info_r_p->result & CPCI_GET_STAT_PCI_PRESENT) DEBUGF(1, (CE_NOTE, "sghsc: pci present")); #endif *resultp = cmd_info_r_p->result; return (0); } /* * sghsc_freemem() * deallocates memory resources * */ static void sghsc_freemem(sghsc_t *sghsc) { int i; /* * Free up allocated resources * sghsc_register_slots => unregister all slots */ for (i = 0; i < sghsc->sghsc_num_slots; i++) { if (sghsc->sghsc_slot_table[i].slot_ops) hpc_free_slot_ops(sghsc->sghsc_slot_table[i].slot_ops); if (sghsc->sghsc_slot_table[i].handle) (void) hpc_slot_unregister( &sghsc->sghsc_slot_table[i].handle); } /* finally free up slot_table */ kmem_free(sghsc->sghsc_slot_table, (size_t)(sghsc->sghsc_num_slots * sizeof (sghsc_slot_t))); } /* * sghsc_find_sloth() * Find slot handle by node id, board number and slot numbert * Returns slot handle or 0 if slot not found. */ static hpc_slot_t sghsc_find_sloth(int node_id, int board, int slot) { int instance; sghsc_t *sghsc; for (instance = 0; instance < sghsc_maxinst; instance++) { sghsc = (sghsc_t *)ddi_get_soft_state(sghsc_state, instance); if (sghsc == NULL || sghsc->sghsc_node_id != node_id || sghsc->sghsc_board != board) continue; DEBUGF(1, (CE_NOTE, "sghsc_find_sloth on board %d at node %d" " slot %d", board, node_id, slot)) if (sghsc->sghsc_num_slots < (slot + 1)) { cmn_err(CE_WARN, "sghsc%d: slot data corruption at" "node %d / board %d", instance, node_id, board); return (NULL); } if (sghsc->sghsc_valid == 0) return (NULL); /* * Found matching slot, return handle. */ return (sghsc->sghsc_slot_table[slot].handle); } DEBUGF(1, (CE_WARN, "sghsc_find_sloth: slot %d not found for node %d" " / board %d", slot, node_id, board)); return (NULL); } /* * sghsc_event_handler() * Event Handler. This is what for other platforms was an interrupt * Handler servicing events. It accepts an event and signals it to * non-interrupt thread. */ uint_t sghsc_event_handler(char *arg) { sghsc_event_t *rsp_data; hpc_slot_t sloth; sghsc_t *enum_state; DEBUGF(1, (CE_NOTE, "sghsc: sghsc_event_handler called")) rsp_data = (sghsc_event_t *)(((sbbc_msg_t *)arg)->msg_buf); if (rsp_data == NULL) { cmn_err(CE_WARN, ("sghsc: sghsc_event_handler argument is null\n")); return (DDI_INTR_CLAIMED); } sloth = sghsc_find_sloth(rsp_data->node_id, rsp_data->board, rsp_data->slot); /* * On a board disconnect sghsc soft state may not exist * when the interrupt occurs. We should treat these * interrupts as noise and but them. */ if (sloth == NULL) { DEBUGF(1, (CE_WARN, "sghsc: slot info not available for" " node %d / board %d slot %d. CPCI event rejected", rsp_data->node_id, rsp_data->board, rsp_data->slot)); return (DDI_INTR_CLAIMED); } enum_state = sghsc_find_softstate(rsp_data->node_id, rsp_data->board, rsp_data->slot); if (enum_state == NULL) { cmn_err(CE_WARN, "sghsc: soft state not available for" " node %d / board %d slot %d", rsp_data->node_id, rsp_data->board, rsp_data->slot); return (DDI_INTR_UNCLAIMED); } DEBUGF(1, (CE_NOTE, "sghsc: node %d", rsp_data->node_id)); DEBUGF(1, (CE_NOTE, "sghsc: board %d", rsp_data->board)); DEBUGF(1, (CE_NOTE, "sghsc: slot %d", rsp_data->slot)); DEBUGF(1, (CE_NOTE, "sghsc: event info %d", rsp_data->info)); switch (rsp_data->info) { case SGHSC_EVENT_CARD_INSERT: DEBUGF(1, (CE_NOTE, "sghsc: card inserted node %d / board %d" " slot %d", rsp_data->node_id, rsp_data->board, rsp_data->slot)); enum_state->sghsc_slot_table[rsp_data->slot].board_type = HPC_BOARD_CPCI_HS; enum_state->sghsc_slot_table[rsp_data->slot].slot_status = HPC_SLOT_DISCONNECTED; break; case SGHSC_EVENT_CARD_REMOVE: DEBUGF(1, (CE_NOTE, "sghsc: card removed node %d / board %d" " slot %d", rsp_data->node_id, rsp_data->board, rsp_data->slot)); enum_state->sghsc_slot_table[rsp_data->slot].board_type = HPC_BOARD_UNKNOWN; enum_state->sghsc_slot_table[rsp_data->slot].slot_status = HPC_SLOT_EMPTY; return (DDI_INTR_CLAIMED); case SGHSC_EVENT_POWER_ON: DEBUGF(1, (CE_NOTE, "sghsc: power on node %d / board %d" " slot %d", rsp_data->node_id, rsp_data->board, rsp_data->slot)); return (DDI_INTR_CLAIMED); case SGHSC_EVENT_POWER_OFF: DEBUGF(1, (CE_NOTE, "sghsc: power off node %d / board %d" " slot %d", rsp_data->node_id, rsp_data->board, rsp_data->slot)); return (DDI_INTR_CLAIMED); case SGHSC_EVENT_HEALTHY_LOST: DEBUGF(1, (CE_NOTE, "sghsc: healthy lost node %d / board %d" " slot %d", rsp_data->node_id, rsp_data->board, rsp_data->slot)); return (DDI_INTR_CLAIMED); case SGHSC_EVENT_LEVER_ACTION: DEBUGF(1, (CE_NOTE, "sghsc: ENUM generated for node %d /" "board %d slot %d", rsp_data->node_id, rsp_data->board, rsp_data->slot)); break; default: DEBUGF(1, (CE_NOTE, "sghsc: unrecognized event info for" " node %d / board %d slot %d", rsp_data->node_id, rsp_data->board, rsp_data->slot)); return (DDI_INTR_CLAIMED); } /* * Signal the ENUM event to the non-interrupt thread as the Hot * Plug Framework will eventually call sghsc_control() but all * the mailbox messages are not allowed from interrupt context. */ if (sghsc_rb_put(&sghsc_rb_header, rsp_data) != DDI_SUCCESS) { cmn_err(CE_WARN, "sghsc: no space to store #ENUM info"); return (DDI_INTR_UNCLAIMED); } cv_signal(&sghsc_event_thread_cv); return (DDI_INTR_CLAIMED); } /* * sghsc_event_thread_code() * Event Thread. This is non-interrupt thread servicing #ENUM, Insert, * Remove, Power on/off, Healthy lost events. */ static void sghsc_event_thread_code(void) { int rc; int result; hpc_slot_t sloth; sghsc_t *sghsc; sghsc_event_t rsp_data; mutex_enter(&sghsc_event_thread_mutex); for (;;) { /* * Wait for Event handler to signal event or self destruction. * Assuming the mutex will be automatically reaccuired. */ cv_wait(&sghsc_event_thread_cv, &sghsc_event_thread_mutex); if (sghsc_event_thread_exit) break; /* * Pick up all the relevant events from the ring buffer. */ while (sghsc_rb_get(&sghsc_rb_header, &rsp_data) == DDI_SUCCESS) { sghsc = sghsc_find_softstate(rsp_data.node_id, rsp_data.board, rsp_data.slot); if (sghsc == NULL) continue; sloth = sghsc_find_sloth(rsp_data.node_id, rsp_data.board, rsp_data.slot); if (sloth == NULL) continue; if (!(sghsc->sghsc_slot_table[rsp_data.slot].flags & SGHSC_SLOT_AUTO_CFG_EN)) continue; /* * Insert event leads only to the electrical * connection. */ if (rsp_data.info == SGHSC_EVENT_CARD_INSERT) { rc = sghsc_connect((caddr_t)sghsc, sloth, NULL, 0); if (rc != HPC_SUCCESS) cmn_err(CE_WARN, "sghsc:" " could not connect inserted card," " node %d / board %d slot %d", rsp_data.node_id, rsp_data.board, rsp_data.slot); continue; } /* * ENUM event received. * Reset ENUM and notify SC to poll for the next one. */ rc = hpc_slot_event_notify(sloth, HPC_EVENT_CLEAR_ENUM, HPC_EVENT_SYNCHRONOUS); if (rc == HPC_EVENT_UNCLAIMED) { DEBUGF(1, (CE_WARN, "sghsc: unable to clear ENUM")); continue; } rc = sghsc_scctl(SGHSC_SET_ENUM_CLEARED, rsp_data.node_id, rsp_data.board, rsp_data.slot, &result); if (rc) { DEBUGF(1, (CE_WARN, "sghsc: unable to ACK cleared ENUM")); continue; } /* * process the ENUM. */ rc = hpc_slot_event_notify(sloth, HPC_EVENT_PROCESS_ENUM, HPC_EVENT_SYNCHRONOUS); if (rc == HPC_EVENT_UNCLAIMED) { DEBUGF(1, (CE_WARN, "sghsc: could not process ENUM")); } } } DEBUGF(1, (CE_NOTE, "sghsc: thread_exit")); cv_signal(&sghsc_event_thread_cv); mutex_exit(&sghsc_event_thread_mutex); thread_exit(); } /* * sghsc_find_softstate() * Find softstate by node id and board number. Slot number is used for * verification. * Returns board's softstate or 0 if not found. */ static sghsc_t * sghsc_find_softstate(int node_id, int board, int slot) { int instance; sghsc_t *sghsc; for (instance = 0; instance < sghsc_maxinst; instance++) { sghsc = (sghsc_t *)ddi_get_soft_state(sghsc_state, instance); if (sghsc == NULL || sghsc->sghsc_node_id != node_id || sghsc->sghsc_board != board) continue; if (sghsc->sghsc_num_slots < (slot + 1)) { cmn_err(CE_WARN, "sghsc%d: " "slot data corruption", instance); return (NULL); } if (sghsc->sghsc_valid == 0) return (NULL); /* * Found matching data, return soft state. */ return (sghsc); } cmn_err(CE_WARN, "sghsc: soft state not found"); return (NULL); } /* * sghsc_rb_setup() * Initialize the event ring buffer with a fixed size. It may require * a more elaborate scheme with buffer extension */ static void sghsc_rb_setup(sghsc_rb_head_t *rb_head) { if (rb_head->buf == NULL) { rb_head->put_idx = 0; rb_head->get_idx = 0; rb_head->size = SGHSC_RING_BUFFER_SZ; rb_head->state = SGHSC_RB_EMPTY; /* * Allocate space for event ring buffer */ rb_head->buf = (sghsc_event_t *)kmem_zalloc( sizeof (sghsc_event_t) * rb_head->size, KM_SLEEP); } } /* * sghsc_rb_teardown() * Free event ring buffer resources. */ static void sghsc_rb_teardown(sghsc_rb_head_t *rb_head) { if (rb_head->buf != NULL) { /* * Deallocate space for event ring buffer */ kmem_free(rb_head->buf, (size_t)(sizeof (sghsc_event_t) * rb_head->size)); rb_head->buf = NULL; rb_head->put_idx = 0; rb_head->get_idx = 0; rb_head->size = 0; rb_head->state = SGHSC_RB_EMPTY; } } /* * sghsc_rb_put() * Insert an event info into the event ring buffer. * Returns DDI_FAILURE if the buffer is full, DDI_SUCCESS otherwise */ static int sghsc_rb_put(sghsc_rb_head_t *rb_head, sghsc_event_t *event) { if (rb_head->state == SGHSC_RB_FULL) return (DDI_FAILURE); rb_head->buf[rb_head->put_idx] = *event; rb_head->put_idx = (rb_head->put_idx + 1) & (rb_head->size - 1); if (rb_head->put_idx == rb_head->get_idx) rb_head->state = SGHSC_RB_FULL; else rb_head->state = SGHSC_RB_FLOAT; return (DDI_SUCCESS); } /* * sghsc_rb_get() * Remove an event info from the event ring buffer. * Returns DDI_FAILURE if the buffer is empty, DDI_SUCCESS otherwise. */ static int sghsc_rb_get(sghsc_rb_head_t *rb_head, sghsc_event_t *event) { if (rb_head->state == SGHSC_RB_EMPTY) return (DDI_FAILURE); *event = rb_head->buf[rb_head->get_idx]; rb_head->get_idx = (rb_head->get_idx + 1) & (rb_head->size - 1); if (rb_head->get_idx == rb_head->put_idx) rb_head->state = SGHSC_RB_EMPTY; else rb_head->state = SGHSC_RB_FLOAT; return (DDI_SUCCESS); }