summaryrefslogtreecommitdiff
path: root/usr/src/uts/sun4u/montecarlo/io/hsc.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/sun4u/montecarlo/io/hsc.c')
-rw-r--r--usr/src/uts/sun4u/montecarlo/io/hsc.c2167
1 files changed, 2167 insertions, 0 deletions
diff --git a/usr/src/uts/sun4u/montecarlo/io/hsc.c b/usr/src/uts/sun4u/montecarlo/io/hsc.c
new file mode 100644
index 0000000000..503e6136a9
--- /dev/null
+++ b/usr/src/uts/sun4u/montecarlo/io/hsc.c
@@ -0,0 +1,2167 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * MonteCarlo HotSwap Controller functionality
+ */
+
+#include <sys/types.h>
+#include <sys/stropts.h>
+#include <sys/stream.h>
+#include <sys/strsun.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/errno.h>
+#include <sys/cpuvar.h>
+#include <sys/open.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/modctl.h>
+#include <sys/promif.h>
+#include <sys/hotplug/hpcsvc.h>
+
+#include <sys/hscimpl.h>
+#include <sys/hsc.h>
+
+#include <sys/mct_topology.h>
+#include <sys/scsbioctl.h>
+#include <sys/scsb.h>
+
+#define HOTSWAP_MODE_PROP "hotswap-mode"
+#define ALARM_CARD_ON_SLOT 1
+#define SCSB_HSC_FORCE_REMOVE 1 /* force remove enum intr handler */
+
+/* TUNABLE PARAMETERS. Some are Debug Only. Please take care when using. */
+
+/*
+ * Set this flag to 1, to enable full hotswap mode at boot time.
+ * Since HPS is threaded, it is not recommended that we set this flag
+ * to 1 because enabling full hotswap interrupt can invoke the ENUM
+ * event handler accessing the slot data structure which may have not
+ * been initialized in the hotplug framework since the HPS may not yet
+ * have called the slot registration function with the bus nexus.
+ */
+static int scsb_hsc_enable_fhs = 0;
+
+/*
+ * Every time a slot is registered with the hotswap framework, the
+ * framework calls back. This variable keeps a count on how many
+ * callbacks are done.
+ */
+static int scsb_hsc_numReg = 0;
+/*
+ * When this flag is set, the board is taken offline (put in reset) after
+ * a unconfigure operation, in Basic Hotswap mode.
+ */
+static int scsb_hsc_bhs_slot_reset = 1;
+/*
+ * When this flag is set, we take the board to reset after unconfigure
+ * operation when operating in full hotswap mode.
+ */
+static int scsb_hsc_fhs_slot_reset = 1;
+/*
+ * Implementation of this counter will work only on Montecarlo since
+ * the ENUM# Interrupt line is not shared with other interrupts.
+ * When the hardware routing changes, then there may be need to remove
+ * or change this functionality.
+ * This functionality is provided so that a bad or non friendly full hotswap
+ * board does not hang the system in full hotswap mode. Atleast the
+ * intent is that! Eventually Solaris kernel will provide similar support
+ * for recovering from a stuck interrupt line. Till then, lets do this.
+ */
+static int scsb_hsc_max_intr_count = 8;
+/*
+ * Since the hardware does not support enabling/disabling ENUM#, the
+ * following flag can be used for imitating that behaviour.
+ * Currently we can set this flag and use the remove op to remove the
+ * interrupt handler from the system. Care must be taken when using this
+ * function since trying to remove the interrupt handler when the interrupts
+ * are pending may hang the system permanently.
+ * Since the hardware does not support this functionality, we adopt this
+ * approach for debugs.
+ */
+static int scsb_hsc_enum_switch = 0;
+
+/*
+ * When the board loses Healthy# at runtime (with the board being configured),
+ * cPCI specs states that a Reset has to be asserted immediately.
+ * We dont do this currently, until satellite processor support is given
+ * and the implications of such a act is fully understood.
+ * To adopt the cPCI specs recommendation, set this flag to 1.
+ */
+static int scsb_hsc_healthy_reset = 0;
+
+/*
+ * According to PCI 2.2 specification, once a board comes out of PCI_RST#,
+ * it may take upto 2^25 clock cycles to respond to config cycles. For
+ * montecarlo using a 33MHz cPCI bus, it's around 1.024 s. The variable
+ * will specify the time in ms to wait before attempting config access.
+ */
+static int scsb_connect_delay = 1025;
+
+/*
+ * slot map property for MC should be
+ *
+ * hsc-slot-map="/pci@1f,0/pci@1/pci@1","15","2",
+ * "/pci@1f,0/pci@1/pci@1","14","3",
+ * "/pci@1f,0/pci@1/pci@1","13","4",
+ * "/pci@1f,0/pci@1/pci@1","12","5"
+ * "/pci@1f,0/pci@1/pci@1","11","6"
+ * "/pci@1f,0/pci@1/pci@1","10","7"
+ * "/pci@1f,0/pci@1/pci@1","8","8";
+ *
+ * slot map property for Tonga should be
+ * hsc-slot-map="/pci@1f,0/pci@1/pci@1","8","1"
+ * "/pci@1f,0/pci@1/pci@1", "15", "2"
+ * "/pci@1f,0/pci@1/pci@1", "14", "4"
+ * "/pci@1f,0/pci@1/pci@1", "13", "5"
+ *
+ * Please note that the CPU slot number is 3 for Tonga.
+ */
+
+/*
+ * Services we require from the SCSB
+ */
+extern int scsb_get_slot_state(void *, int, int *);
+extern int scsb_read_bhealthy(scsb_state_t *scsb);
+extern int scsb_read_slot_health(scsb_state_t *scsb, int pslotnum);
+extern int scsb_connect_slot(void *, int, int);
+extern int scsb_disconnect_slot(void *, int, int);
+
+static void *hsc_state;
+
+static uint_t hsc_enum_intr(char *);
+static hsc_slot_t *hsc_get_slot_info(hsc_state_t *, int);
+static int scsb_enable_enum(hsc_state_t *);
+static int scsb_disable_enum(hsc_state_t *, int);
+static int atoi(const char *);
+static int isdigit(int);
+static hsc_slot_t *hsc_find_slot(int);
+static void hsc_led_op(hsc_slot_t *, int, hpc_led_t, hpc_led_state_t);
+static int hsc_led_state(hsc_slot_t *, int, hpc_led_info_t *);
+static int scsb_hsc_disable_slot(hsc_slot_t *);
+static int scsb_hsc_enable_slot(hsc_slot_t *);
+#ifndef lint
+static int hsc_clear_all_enum(hsc_state_t *);
+#endif
+static int hsc_slot_register(hsc_state_t *, char *, uint16_t, uint_t,
+ boolean_t);
+static int hsc_slot_unregister(int);
+static int scsb_hsc_init_slot_state(hsc_state_t *, hsc_slot_t *);
+static int hsc_slot_autoconnect(hsc_slot_t *);
+
+static hpc_slot_ops_t *hsc_slotops;
+static hsc_slot_t *hsc_slot_list; /* linked list of slots */
+
+/*
+ * This mutex protects the following variables:
+ * hsc_slot_list
+ */
+static kmutex_t hsc_mutex;
+
+
+/* ARGSUSED */
+static int
+hsc_connect(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags)
+{
+ hsc_slot_t *hsp = (hsc_slot_t *)ops_arg;
+ int rc, rstate;
+ hsc_state_t *hsc;
+
+ DEBUG2("hsc_connect: slot %d, healthy %d", hsp->hs_slot_number,
+ hsp->hs_board_healthy);
+
+ if (!(hsp->hs_flags & (HSC_ENABLED|HSC_SLOT_ENABLED)))
+ return (HPC_ERR_FAILED);
+ /* if SCB hotswapped, do not allow connect operations */
+ if (hsp->hs_flags & HSC_SCB_HOTSWAPPED)
+ return (HPC_ERR_FAILED);
+ /*
+ * if previous occupant stayed configured, do not allow another
+ * occupant to be connected.
+ * This behaviour is an indication that the slot state
+ * is not clean.
+ */
+ if (hsp->hs_flags & HSC_SLOT_BAD_STATE) {
+ /*
+ * In the current implementation, we turn both fault
+ * and active LEDs to ON state in this situation.
+ */
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
+ HPC_LED_ON);
+ return (HPC_ERR_FAILED);
+ }
+ /*
+ * Get the actual status from the i2c bus
+ */
+ rc = scsb_get_slot_state(hsp->hs_hpchandle, hsp->hs_slot_number,
+ &rstate);
+ if (rc != DDI_SUCCESS)
+ return (HPC_ERR_FAILED);
+
+ hsp->hs_slot_state = rstate;
+ if (hsp->hs_slot_state == HPC_SLOT_EMPTY) {
+#ifdef DEBUG
+ cmn_err(CE_CONT,
+ "?hsc_connect: slot %d is empty\n",
+ hsp->hs_slot_number);
+#endif
+ return (HPC_ERR_FAILED);
+ }
+
+ if (hsp->hs_slot_state == HPC_SLOT_CONNECTED)
+ return (HPC_SUCCESS);
+
+ rc = HPC_SUCCESS;
+ /*
+ * call scsb to connect the slot. This also makes sure board is healthy
+ */
+ if (scsb_connect_slot(hsp->hs_hpchandle, hsp->hs_slot_number,
+ hsp->hs_board_healthy) != DDI_SUCCESS) {
+ DEBUG1("hsc_connect: slot %d connection failed",
+ hsp->hs_slot_number);
+ rc = HPC_ERR_FAILED;
+ } else {
+ if (hsp->hs_slot_state != HPC_SLOT_CONNECTED) {
+ if (hsp->hs_board_healthy == B_FALSE) {
+ cmn_err(CE_NOTE, "HEALTHY# not asserted on "
+ " slot %d", hsp->hs_slot_number);
+ return (HPC_ERR_FAILED);
+ }
+ hsc = hsp->hsc;
+ hsc->hsp_last = hsp;
+ if (scsb_reset_slot(hsp->hs_hpchandle,
+ hsp->hs_slot_number, SCSB_UNRESET_SLOT) != 0) {
+
+ return (HPC_ERR_FAILED);
+ }
+ /*
+ * Unresetting a board may have caused an interrupt
+ * burst in case of non friendly boards. So it is
+ * important to make sure that the ISR has not
+ * put this board back to disconnect state.
+ */
+ delay(1);
+ if (hsp->hs_flags & HSC_ENUM_FAILED) {
+ hsp->hs_flags &= ~HSC_ENUM_FAILED;
+ return (HPC_ERR_FAILED);
+ }
+ DEBUG1("hsc_connect: slot %d connected",
+ hsp->hs_slot_number);
+ rc = HPC_SUCCESS;
+ hsp->hs_slot_state = HPC_SLOT_CONNECTED;
+ (void) hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
+ HPC_FAULT_LED, HPC_LED_OFF);
+ }
+ }
+
+ /*
+ * PCI 2.2 specs recommend that the probe software wait
+ * for upto 2^25 PCI clock cycles after deassertion of
+ * PCI_RST# before the board is able to respond to config
+ * cycles. So, before we return, we wait for ~1 sec.
+ */
+ delay(drv_usectohz(scsb_connect_delay * 1000));
+ return (rc);
+}
+
+
+/* ARGSUSED */
+static int
+hsc_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags)
+{
+ hsc_slot_t *hsp = (hsc_slot_t *)ops_arg;
+ hsc_state_t *hsc;
+#ifdef DEBUG
+ static const char func[] = "hsc_disconnect";
+#endif
+
+ DEBUG1("hsc_disconnect: slot %d", hsp->hs_slot_number);
+
+ if (hsp->hs_board_configured) {
+#ifdef DEBUG
+ cmn_err(CE_NOTE,
+ "%s: cannot disconnect configured board in slot %d",
+ func, hsp->hs_slot_number);
+#endif
+ return (HPC_ERR_FAILED);
+ }
+
+ if (hsp->hs_slot_state == HPC_SLOT_EMPTY) {
+#ifdef DEBUG
+ cmn_err(CE_NOTE, "%s: slot %d is empty",
+ func, hsp->hs_slot_number);
+#endif
+ return (HPC_SUCCESS);
+ }
+
+ if (hsp->hs_slot_state == HPC_SLOT_DISCONNECTED) {
+ /*
+ * if already disconnected, just return success
+ * Duplicate disconnect messages should not be failed!
+ */
+ return (HPC_SUCCESS);
+ }
+ /* if SCB hotswapped, do not allow disconnect operations */
+ if (hsp->hs_flags & HSC_SCB_HOTSWAPPED)
+ return (HPC_ERR_FAILED);
+
+ /* call scsb to disconnect the slot */
+ if (scsb_disconnect_slot(hsp->hs_hpchandle, B_TRUE, hsp->hs_slot_number)
+ != DDI_SUCCESS)
+ return (HPC_ERR_FAILED);
+ hsc = hsp->hsc;
+ if (hsc->hsp_last == hsp)
+ hsc->hsp_last = NULL;
+
+ return (HPC_SUCCESS);
+}
+
+
+/*
+ * In the cPCI world, this operation is not applicable.
+ * However, we use this function to enable full hotswap mode in debug mode.
+ */
+/* ARGSUSED */
+static int
+hsc_insert(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags)
+{
+ hsc_slot_t *hsp = (hsc_slot_t *)ops_arg;
+
+ if (scsb_hsc_enum_switch &&
+ (scsb_enable_enum(hsp->hsc) == DDI_SUCCESS)) {
+ return (HPC_SUCCESS);
+ }
+ return (HPC_ERR_NOTSUPPORTED);
+}
+
+
+/*
+ * In the cPCI world, this operation is not applicable.
+ * However, we use this function to disable full hotswap mode in debug mode.
+ */
+/* ARGSUSED */
+static int
+hsc_remove(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags)
+{
+ hsc_slot_t *hsp = (hsc_slot_t *)ops_arg;
+
+ if (scsb_hsc_enum_switch &&
+ (scsb_disable_enum(hsp->hsc, SCSB_HSC_FORCE_REMOVE)
+ == DDI_SUCCESS)) {
+ hsp->hs_flags &= ~HSC_ENUM_FAILED;
+ return (HPC_SUCCESS);
+ }
+ return (HPC_ERR_NOTSUPPORTED);
+}
+
+static void
+hsc_led_op(hsc_slot_t *hsp, int cmd, hpc_led_t led, hpc_led_state_t led_state)
+{
+ hpc_led_info_t ledinfo;
+
+ ledinfo.led = led;
+ ledinfo.state = led_state;
+ (void) hsc_led_state(hsp, cmd, &ledinfo);
+}
+
+static int
+hsc_led_state(hsc_slot_t *hsp, int cmd, hpc_led_info_t *hlip)
+{
+ hpc_led_state_t *hlsp;
+ scsb_uinfo_t sunit;
+ int res;
+
+ DEBUG3("hsc_led_state: slot %d, led %x, state %x",
+ hsp->hs_slot_number, hlip->led, hlip->state);
+
+ sunit.unit_type = SLOT;
+ sunit.unit_number = hsp->hs_slot_number;
+ /*
+ * We ignore operations on LEDs that we don't support
+ */
+ switch (hlip->led) {
+ case HPC_FAULT_LED:
+ sunit.led_type = NOK;
+ hlsp = &hsp->hs_fault_led_state;
+ break;
+ case HPC_ACTIVE_LED:
+ sunit.led_type = OK;
+ hlsp = &hsp->hs_active_led_state;
+ break;
+ default:
+ return (HPC_ERR_NOTSUPPORTED);
+ }
+
+ switch (hlip->state) {
+ case HPC_LED_BLINK:
+ sunit.unit_state = BLINK;
+ if (hlip->led != HPC_ACTIVE_LED)
+ return (HPC_ERR_NOTSUPPORTED);
+ break;
+ case HPC_LED_ON:
+ sunit.unit_state = ON;
+ break;
+ case HPC_LED_OFF:
+ sunit.unit_state = OFF;
+ break;
+ default:
+ break;
+ }
+
+ switch (cmd) {
+ case HPC_CTRL_SET_LED_STATE:
+ res = scsb_led_set(hsp->hs_hpchandle, &sunit, sunit.led_type);
+ if (res != 0)
+ return (HPC_ERR_FAILED);
+ *hlsp = (hpc_led_state_t)sunit.unit_state;
+ break;
+
+ case HPC_CTRL_GET_LED_STATE:
+ res = scsb_led_get(hsp->hs_hpchandle, &sunit, sunit.led_type);
+ if (res)
+ return (HPC_ERR_FAILED);
+ /* hlip->state = sunit.unit_state; */
+ break;
+
+ default:
+ return (HPC_ERR_INVALID);
+ }
+
+ return (HPC_SUCCESS);
+
+}
+
+
+static int
+hsc_get_slot_state(hsc_slot_t *hsp, hpc_slot_state_t *hssp)
+{
+ int rstate = 0;
+ int rc;
+#ifdef DEBUG
+ int orstate; /* original rstate */
+#endif
+
+ DEBUG1("hsc_get_slot_state: slot %d", hsp->hs_slot_number);
+ rc = scsb_get_slot_state(hsp->hs_hpchandle, hsp->hs_slot_number,
+ &rstate);
+ if (rc != DDI_SUCCESS)
+ return (HPC_ERR_FAILED);
+#ifdef DEBUG
+ orstate = hsp->hs_slot_state;
+#endif
+ hsp->hs_slot_state = rstate;
+ switch (hsp->hs_slot_state) {
+ case HPC_SLOT_EMPTY:
+ DEBUG0("empty");
+ break;
+ case HPC_SLOT_CONNECTED:
+ DEBUG0("connected");
+ break;
+ case HPC_SLOT_DISCONNECTED:
+ DEBUG0("disconnected");
+ break;
+ }
+
+ *hssp = hsp->hs_slot_state;
+
+ /* doing get-state above may have caused a freeze operation */
+ if ((hsp->hs_flags & HSC_SCB_HOTSWAPPED) &&
+ (rstate == HPC_SLOT_DISCONNECTED)) {
+ /* freeze puts disconnected boards to connected state */
+ *hssp = HPC_SLOT_CONNECTED;
+#if 0
+ /* in FHS, deassertion of reset may have configured the board */
+ if (hsp->hs_board_configured == B_TRUE) {
+ hsp->hs_slot_state = *hssp;
+ }
+#endif
+ }
+#ifdef DEBUG
+ /* a SCB hotswap may have forced a state change on the receptacle */
+ if (orstate != *hssp) {
+ cmn_err(CE_NOTE, "hsc_get_state: slot%d state change due"
+ " to SCB hotswap!", hsp->hs_slot_number);
+ }
+#endif
+ return (HPC_SUCCESS);
+}
+
+
+static int
+hsc_set_config_state(hsc_slot_t *hsp, int cmd)
+{
+ hsc_state_t *hsc = hsp->hsc;
+
+ DEBUG1("hsc_set_config_state: slot %d", hsp->hs_slot_number);
+
+ switch (cmd) {
+ case HPC_CTRL_DEV_CONFIGURED:
+ /*
+ * Closing of the Ejector switch in configured/busy state can
+ * cause duplicate CONFIGURED messages to come down.
+ * Make sure our LED states are fine.
+ */
+ if (hsp->hs_board_configured == B_TRUE) {
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
+ HPC_LED_ON);
+ break;
+ }
+ hsp->hs_board_configured = B_TRUE;
+ hsp->hs_board_configuring = B_FALSE;
+ if ((hsc->state & HSC_ATTACHED) == HSC_ATTACHED &&
+ hsp->hs_flags & HSC_ALARM_CARD_PRES)
+ scsb_hsc_ac_op(hsp->hs_hpchandle,
+ hsp->hs_slot_number, SCSB_HSC_AC_CONFIGURED);
+ /* LED must be OFF on the occupant. */
+ (void) hpc_slot_event_notify(hsp->hs_slot_handle,
+ HPC_EVENT_SLOT_BLUE_LED_OFF, 0);
+ if (hsp->hs_flags & HSC_AUTOCFG)
+ (void) hpc_slot_event_notify(hsp->hs_slot_handle,
+ HPC_EVENT_ENABLE_ENUM, 0);
+ else
+ (void) hpc_slot_event_notify(hsp->hs_slot_handle,
+ HPC_EVENT_DISABLE_ENUM, 0);
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
+ HPC_LED_ON);
+ if (hsc->hsp_last == hsp)
+ hsc->hsp_last = NULL;
+ break;
+ case HPC_CTRL_DEV_UNCONFIGURED:
+ hsp->hs_board_configured = B_FALSE;
+ hsp->hs_board_unconfiguring = B_FALSE;
+ hsp->hs_flags &= ~HSC_SLOT_BAD_STATE;
+ if (hsp->hs_flags & HSC_ALARM_CARD_PRES)
+ scsb_hsc_ac_op(hsp->hs_hpchandle,
+ hsp->hs_slot_number, SCSB_HSC_AC_UNCONFIGURED);
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
+ HPC_LED_BLINK);
+ if (((hsc->state & HSC_ENUM_ENABLED) &&
+ scsb_hsc_fhs_slot_reset) ||
+ (((hsc->state & HSC_ENUM_ENABLED) != HSC_ENUM_ENABLED) &&
+ scsb_hsc_bhs_slot_reset) ||
+ ((hsp->hs_flags & HSC_AUTOCFG) !=
+ HSC_AUTOCFG)) {
+ if (scsb_reset_slot(hsp->hs_hpchandle,
+ hsp->hs_slot_number, SCSB_RESET_SLOT) == 0) {
+
+ hsp->hs_slot_state = HPC_SLOT_DISCONNECTED;
+ hsp->hs_board_healthy = B_FALSE;
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
+ HPC_FAULT_LED, HPC_LED_ON);
+ }
+ }
+ break;
+ case HPC_CTRL_DEV_CONFIG_FAILURE:
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
+ HPC_LED_BLINK);
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
+ HPC_FAULT_LED, HPC_LED_ON);
+ break;
+ case HPC_CTRL_DEV_UNCONFIG_FAILURE:
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
+ HPC_LED_ON);
+ break;
+ case HPC_CTRL_DEV_CONFIG_START:
+ case HPC_CTRL_DEV_UNCONFIG_START:
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
+ HPC_LED_OFF);
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
+ HPC_LED_BLINK);
+ break;
+ default:
+ return (HPC_ERR_INVALID);
+ }
+
+ if (cmd != HPC_CTRL_DEV_CONFIG_START &&
+ cmd != HPC_CTRL_DEV_UNCONFIG_START &&
+ hsc->regDone == B_FALSE &&
+ scsb_hsc_numReg < hsc->n_registered_occupants) {
+ scsb_hsc_numReg++;
+
+ /*
+ * If the callback is invoked for all registered slots,
+ * enable ENUM.
+ */
+ if (((hsc->state & HSC_ATTACHED) == HSC_ATTACHED) &&
+ (scsb_hsc_numReg == hsc->n_registered_occupants)) {
+ hsc->regDone = B_TRUE;
+ if (hsc->hotswap_mode == HSC_HOTSWAP_MODE_FULL) {
+#ifdef DEBUG
+ cmn_err(CE_CONT, "%s%d: Enabling full hotswap"
+ ":%d non-empty slots\n",
+ ddi_driver_name(hsc->dip),
+ ddi_get_instance(hsc->dip),
+ hsc->n_registered_occupants);
+#endif
+ if (scsb_enable_enum(hsc) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s#%d: Cannot enable "
+ "Full Hotswap",
+ ddi_driver_name(hsc->dip),
+ ddi_get_instance(hsc->dip));
+
+ return (HPC_ERR_FAILED);
+ }
+ }
+ }
+ }
+
+ return (HPC_SUCCESS);
+}
+
+
+/*ARGSUSED*/
+static int
+hsc_get_board_type(hsc_slot_t *hsp, hpc_board_type_t *hbtp)
+{
+ *hbtp = hsp->hs_board_type;
+ return (HPC_SUCCESS);
+}
+
+
+/* ARGSUSED */
+static int
+hsc_autoconfig(hsc_slot_t *hsp, int cmd)
+{
+ int res = HPC_SUCCESS, enum_disable = B_TRUE, i;
+ char slotautocfg_prop[18];
+ hsc_state_t *hsc;
+
+ DEBUG1("hsc_autoconfig: slot %d", hsp->hs_slot_number);
+ sprintf(slotautocfg_prop, "slot%d-autoconfig", hsp->hs_slot_number);
+
+ if (cmd == HPC_CTRL_ENABLE_AUTOCFG) {
+ hsp->hs_flags |= HSC_AUTOCFG;
+ ddi_prop_update_string(DDI_DEV_T_NONE, hsp->hsc->dip,
+ slotautocfg_prop, "enabled");
+ if ((res = scsb_enable_enum(hsp->hsc)) == DDI_SUCCESS) {
+ (void) hpc_slot_event_notify(hsp->hs_slot_handle,
+ HPC_EVENT_ENABLE_ENUM, 0);
+ }
+ } else {
+ ddi_prop_update_string(DDI_DEV_T_NONE, hsp->hsc->dip,
+ slotautocfg_prop, "disabled");
+ hsp->hs_flags &= ~HSC_AUTOCFG;
+ hsc = hsp->hsc;
+ if (hsc->state & HSC_ATTACHED) {
+ (void) hpc_slot_event_notify(hsp->hs_slot_handle,
+ HPC_EVENT_DISABLE_ENUM, 0);
+ for (i = 0; i < hsc->slot_table_size; i++) {
+ hsc_slot_t *thsp;
+ int slotnum;
+
+ slotnum = hsc->slot_table_prop[i].pslotnum;
+ thsp = hsc_find_slot(slotnum);
+ if (thsp == NULL) {
+ cmn_err(CE_WARN, "%s#%d: hsc_autocfg:"
+ "No Slot Info for slot %d",
+ ddi_driver_name(hsc->dip),
+ ddi_get_instance(hsc->dip),
+ slotnum);
+ continue;
+ }
+ if (thsp->hs_flags & HSC_AUTOCFG) {
+ enum_disable = B_FALSE;
+ break;
+ }
+ }
+ if (enum_disable == B_TRUE)
+ scsb_disable_enum(hsc, SCSB_HSC_FORCE_REMOVE);
+ }
+ }
+ return (res);
+}
+
+
+/*
+ * This function is invoked to enable/disable a slot
+ */
+/* ARGSUSED */
+#ifndef lint
+static int
+hsc_slot_enable(hsc_slot_t *hsp, boolean_t enabled)
+{
+ scsb_uinfo_t sunit;
+ int res;
+
+ DEBUG1("hsc_slot_enable: slot %d", hsp->hs_slot_number);
+
+ sunit.unit_type = SLOT;
+ sunit.unit_number = hsp->hs_slot_number;
+ if (enabled)
+ sunit.unit_state = ON;
+ else
+ sunit.unit_state = OFF;
+
+ res = scsb_reset_unit(hsp->hs_hpchandle, &sunit);
+ if (res == 0)
+ return (HPC_SUCCESS);
+ else if (res == EINVAL)
+ return (HPC_ERR_INVALID);
+ else
+ return (HPC_ERR_FAILED);
+}
+#endif
+
+
+/*ARGSUSED*/
+static int
+hsc_control(caddr_t ops_arg, hpc_slot_t slot_hdl, int request, caddr_t arg)
+{
+ hsc_slot_t *hsp = (hsc_slot_t *)ops_arg;
+ int rc = HPC_SUCCESS;
+
+ DEBUG2("hsc_control: slot %d, op=%x\n", hsp->hs_slot_number, request);
+
+ switch (request) {
+ case HPC_CTRL_GET_LED_STATE:
+ return (hsc_led_state(hsp,
+ HPC_CTRL_GET_LED_STATE, (hpc_led_info_t *)arg));
+
+ case HPC_CTRL_SET_LED_STATE:
+ return (hsc_led_state(hsp,
+ HPC_CTRL_SET_LED_STATE, (hpc_led_info_t *)arg));
+
+ case HPC_CTRL_GET_SLOT_STATE:
+ return (hsc_get_slot_state(hsp, (hpc_slot_state_t *)arg));
+
+ case HPC_CTRL_DEV_CONFIGURED:
+ return (hsc_set_config_state(hsp, HPC_CTRL_DEV_CONFIGURED));
+
+ case HPC_CTRL_DEV_UNCONFIGURED:
+ return (hsc_set_config_state(hsp, HPC_CTRL_DEV_UNCONFIGURED));
+
+ case HPC_CTRL_DEV_CONFIG_FAILURE:
+ return (hsc_set_config_state(hsp, HPC_CTRL_DEV_CONFIG_FAILURE));
+
+ case HPC_CTRL_DEV_UNCONFIG_FAILURE:
+ return (hsc_set_config_state(hsp,
+ HPC_CTRL_DEV_UNCONFIG_FAILURE));
+
+ case HPC_CTRL_DEV_CONFIG_START:
+ case HPC_CTRL_DEV_UNCONFIG_START:
+ return (hsc_set_config_state(hsp, request));
+
+ case HPC_CTRL_GET_BOARD_TYPE:
+ return (hsc_get_board_type(hsp, (hpc_board_type_t *)arg));
+
+ case HPC_CTRL_DISABLE_AUTOCFG:
+ return (hsc_autoconfig(hsp, HPC_CTRL_DISABLE_AUTOCFG));
+
+ case HPC_CTRL_ENABLE_AUTOCFG:
+ return (hsc_autoconfig(hsp, HPC_CTRL_ENABLE_AUTOCFG));
+
+ case HPC_CTRL_DISABLE_SLOT:
+ /*
+ * No hardware support for disabling the slot.
+ * Just imitate a disable_autoconfig operation for now
+ */
+ if (hsp->hs_board_configured == B_TRUE)
+ return (HPC_ERR_FAILED);
+ if (scsb_hsc_disable_slot(hsp) != DDI_SUCCESS)
+ rc = HPC_ERR_FAILED;
+ return (rc);
+
+ case HPC_CTRL_ENABLE_SLOT:
+ if (scsb_hsc_enable_slot(hsp) != DDI_SUCCESS)
+ rc = HPC_ERR_FAILED;
+ return (rc);
+
+ case HPC_CTRL_ENABLE_ENUM:
+ return (scsb_enable_enum(hsp->hsc));
+
+ case HPC_CTRL_DISABLE_ENUM:
+ return (scsb_disable_enum(hsp->hsc, 0));
+
+ default:
+ return (HPC_ERR_INVALID);
+ }
+}
+
+static int
+scsb_hsc_disable_slot(hsc_slot_t *hsp)
+{
+ int rc;
+ char slot_disable_prop[18];
+
+ DEBUG1("hsc_disable_slot: slot %d", hsp->hs_slot_number);
+ sprintf(slot_disable_prop, "slot%d-status", hsp->hs_slot_number);
+
+ rc = scsb_reset_slot(hsp->hs_hpchandle, hsp->hs_slot_number,
+ SCSB_RESET_SLOT);
+ if (rc == DDI_SUCCESS) {
+ hsc_autoconfig(hsp, HPC_CTRL_DISABLE_AUTOCFG);
+ hsp->hs_flags &= ~HSC_SLOT_ENABLED;
+ ddi_prop_update_string(DDI_DEV_T_NONE, hsp->hsc->dip,
+ slot_disable_prop, "disabled");
+ } else
+ rc = DDI_FAILURE;
+ return (rc);
+}
+
+static int
+scsb_hsc_enable_slot(hsc_slot_t *hsp)
+{
+ int rc;
+ char slot_disable_prop[18];
+
+ DEBUG1("hsc_disable_slot: slot %d", hsp->hs_slot_number);
+ sprintf(slot_disable_prop, "slot%d-status", hsp->hs_slot_number);
+
+ rc = scsb_reset_slot(hsp->hs_hpchandle, hsp->hs_slot_number,
+ SCSB_UNRESET_SLOT);
+ if (rc == DDI_SUCCESS) {
+ hsc_autoconfig(hsp, HPC_CTRL_ENABLE_AUTOCFG);
+ hsp->hs_flags |= HSC_SLOT_ENABLED;
+ ddi_prop_remove(DDI_DEV_T_NONE, hsp->hsc->dip,
+ slot_disable_prop);
+ } else
+ rc = HPC_ERR_FAILED;
+ return (rc);
+}
+
+#define NEW(type) (type *) kmem_zalloc(sizeof (type), KM_SLEEP)
+
+static hsc_slot_t *
+hsc_alloc_slot(
+ uint16_t device_number,
+ int slot_number,
+ boolean_t board_in_slot)
+{
+ hpc_slot_info_t *hsip;
+ hsc_slot_t *hsp = NEW(hsc_slot_t);
+
+ DEBUG2("hsc_alloc_slot: slot %d %s", slot_number,
+ board_in_slot ? "occupied" : "empty");
+
+ if (hsp == NULL) {
+ cmn_err(CE_NOTE,
+ "hsc_alloc_slot: allocation failed for slot %d",
+ slot_number);
+ return (NULL);
+ }
+
+ hsip = &hsp->hs_info;
+
+ hsip->version = HPC_SLOT_INFO_VERSION;
+ hsip->slot_type = HPC_SLOT_TYPE_CPCI;
+ hsip->pci_dev_num = device_number;
+ hsip->pci_slot_capabilities = 0;
+ hsip->slot_flags = HPC_SLOT_CREATE_DEVLINK;
+ /*
+ * Note: the name *must* be 'pci' so that the correct cfgadm plug-in
+ * library is selected
+ */
+ sprintf(hsip->pci_slot_name, "cpci_slot%d", slot_number);
+
+ /*
+ * We assume that the following LED settings reflect
+ * the hardware state.
+ * After we register the slot, we will be invoked by the nexus
+ * if the slot is occupied, and we will turn on the LED then.
+ */
+ hsp->hs_active_led_state = HPC_LED_OFF;
+ hsp->hs_fault_led_state = HPC_LED_OFF;
+
+ hsp->hs_board_configured = B_FALSE;
+ hsp->hs_board_healthy = B_FALSE;
+ hsp->hs_board_type = HPC_BOARD_UNKNOWN;
+
+ hsp->hs_flags = HSC_ENABLED | HSC_SLOT_ENABLED;
+ hsp->hs_slot_number = slot_number;
+
+ /*
+ * we should just set this to connected,
+ * as MC slots are always connected.
+ */
+ if (board_in_slot)
+ hsp->hs_slot_state = HPC_SLOT_CONNECTED;
+ else
+ hsp->hs_slot_state = HPC_SLOT_EMPTY;
+
+ return (hsp);
+}
+
+
+static void
+hsc_free_slot(hsc_slot_t *hsp)
+{
+ DEBUG0("hsc_free_slot");
+
+ kmem_free(hsp, sizeof (*hsp));
+}
+
+
+/*
+ * This function is invoked to register a slot
+ */
+static int
+hsc_slot_register(
+ hsc_state_t *hsc,
+ char *bus_path, /* PCI nexus pathname */
+ uint16_t device_number, /* PCI device number */
+ uint_t slot_number, /* physical slot number */
+ boolean_t board_in_slot) /* receptacle status */
+{
+ int rc = HPC_SUCCESS;
+ hsc_slot_t *hsp;
+
+ DEBUG2("hsc_slot_register: slot number %d, device number %d",
+ slot_number, device_number);
+
+ hsp = hsc_alloc_slot(device_number, slot_number,
+ board_in_slot);
+
+ if (hsp == NULL) {
+#ifdef DEBUG
+ cmn_err(CE_NOTE, "hsc_slot_register: hsc_alloc_slot failed");
+#endif
+ return (HPC_ERR_FAILED);
+ }
+
+ hsp->hs_hpchandle = hsc->scsb_handle; /* handle for call backs */
+ hsp->hsc = hsc;
+
+ rc = scsb_hsc_init_slot_state(hsc, hsp);
+ if (rc != DDI_SUCCESS)
+ return (HPC_ERR_FAILED);
+
+ /* slot autoconfiguration by default. */
+ if (hsc->hotswap_mode == HSC_HOTSWAP_MODE_FULL)
+ (void) hsc_autoconfig(hsp, HPC_CTRL_ENABLE_AUTOCFG);
+ else
+ (void) hsc_autoconfig(hsp, HPC_CTRL_DISABLE_AUTOCFG);
+
+ /*
+ * Append to our list
+ */
+ mutex_enter(&hsc_mutex);
+ hsp->hs_next = hsc_slot_list;
+ hsc_slot_list = hsp;
+ mutex_exit(&hsc_mutex);
+
+ rc = hpc_slot_register(hsc->dip,
+ bus_path,
+ &hsp->hs_info,
+ &hsp->hs_slot_handle, /* return value */
+ hsc_slotops,
+ (caddr_t)hsp,
+ 0);
+
+ if (rc != HPC_SUCCESS) {
+ cmn_err(CE_WARN, "%s#%d: failed to register slot %s:%d",
+ ddi_driver_name(hsc->dip), ddi_get_instance(hsc->dip),
+ bus_path, device_number);
+ hsc_free_slot(hsp);
+ return (rc);
+ }
+
+ DEBUG0("hsc_slot_register: hpc_slot_register successful");
+
+ return (rc);
+}
+
+
+static int
+hsc_slot_unregister(int slot_number)
+{
+ hsc_slot_t *hsp, *prev;
+
+ DEBUG1("hsc_slot_unregister: slot number %d", slot_number);
+
+ mutex_enter(&hsc_mutex);
+ hsp = prev = NULL;
+ for (hsp = hsc_slot_list; hsp != NULL; hsp = hsp->hs_next) {
+ if (hsp->hs_slot_number == slot_number) {
+ if (prev == NULL) /* first entry */
+ hsc_slot_list = hsc_slot_list->hs_next;
+ else
+ prev->hs_next = hsp->hs_next;
+ hsp->hs_next = NULL;
+ break;
+ }
+ prev = hsp;
+ }
+ mutex_exit(&hsc_mutex);
+
+ if (hsp != NULL) {
+ hpc_slot_unregister(&hsp->hs_slot_handle);
+ if ((hsp->hsc->state & HSC_ATTACHED) != HSC_ATTACHED &&
+ hsp->hs_slot_state != HPC_SLOT_EMPTY) {
+ hsp->hsc->n_registered_occupants--;
+ }
+ hsc_free_slot(hsp);
+ return (0);
+ }
+ return (1);
+}
+
+static int
+scsb_hsc_init_slot_state(hsc_state_t *hsc, hsc_slot_t *hsp)
+{
+ int rc, rstate;
+ int slot_number = hsp->hs_slot_number;
+ scsb_state_t *scsb = (scsb_state_t *)hsc->scsb_handle;
+
+ rc = scsb_get_slot_state(hsc->scsb_handle, slot_number, &rstate);
+ if (rc != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ /*
+ * Set the healthy status for this slot
+ */
+ hsp->hs_board_healthy = scsb_read_slot_health(scsb, slot_number);
+ hsp->hs_slot_state = rstate;
+ switch (rstate) {
+ case HPC_SLOT_EMPTY:
+ /*
+ * this will clear any state differences between
+ * SCB Freeze operations.
+ */
+ hsp->hs_slot_state = HPC_SLOT_EMPTY;
+ /* slot empty. */
+ scsb_reset_slot(hsc->scsb_handle, slot_number,
+ SCSB_RESET_SLOT);
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
+ HPC_LED_OFF);
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
+ HPC_LED_OFF);
+ break;
+ case HPC_SLOT_DISCONNECTED:
+ /*
+ * this will clear any state differences between
+ * SCB Freeze operations.
+ */
+ hsp->hs_slot_state = HPC_SLOT_DISCONNECTED;
+ /* check recovery from SCB freeze */
+ if (hsp->hs_board_configured != B_TRUE) {
+ /*
+ * Force a disconnect just in case there are
+ * differences between healthy and reset states.
+ */
+ scsb_reset_slot(hsc->scsb_handle, slot_number,
+ SCSB_RESET_SLOT);
+ /*
+ * Slot in reset. OBP has not probed this
+ * device. Hence it is ok to remove this board.
+ */
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
+ HPC_ACTIVE_LED, HPC_LED_BLINK);
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
+ HPC_FAULT_LED, HPC_LED_ON);
+ break;
+ }
+ /*FALLTHROUGH*/
+ case HPC_SLOT_CONNECTED:
+ /*
+ * this will clear any state differences between
+ * SCB Freeze operations.
+ */
+ hsp->hs_slot_state = HPC_SLOT_CONNECTED;
+ /*
+ * OBP should have probed this device, unless
+ * it was plugged in during the boot operation
+ * before the driver was loaded. In any case,
+ * no assumption is made and hence we take
+ * the conservative approach by keeping fault
+ * led off so board removal is not allowed.
+ */
+ if (hsp->hs_board_configured == B_TRUE)
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
+ HPC_ACTIVE_LED, HPC_LED_ON);
+ else
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
+ HPC_ACTIVE_LED, HPC_LED_BLINK);
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
+ HPC_LED_OFF);
+ /*
+ * Netra ct alarm card hotswap support
+ */
+ if (slot_number == scsb->ac_slotnum &&
+ scsb->scsb_hsc_state & SCSB_ALARM_CARD_PRES) {
+ hsp->hs_flags |= HSC_ALARM_CARD_PRES;
+ DEBUG0("Xscsb_hsc_init_slot_state: "
+ "set HSC_ALARM_CARD_PRES");
+ }
+ break;
+ default:
+ break;
+ }
+ return (rc);
+}
+
+static hsc_slot_t *
+hsc_get_slot_info(hsc_state_t *hsc, int pci_devno)
+{
+ int i;
+
+ for (i = 0; i < hsc->slot_table_size; i++) {
+
+ if (hsc->slot_table_prop[i].pci_devno == pci_devno)
+ return ((hsc_slot_t *)hsc_find_slot(
+ hsc->slot_table_prop[i].pslotnum));
+ }
+ return (NULL);
+}
+
+static hsc_slot_t *
+hsc_find_slot(int slot_number)
+{
+ hsc_slot_t *hsp;
+
+ mutex_enter(&hsc_mutex);
+ for (hsp = hsc_slot_list; hsp != NULL; hsp = hsp->hs_next) {
+ if (hsp->hs_slot_number == slot_number)
+ break;
+ }
+ mutex_exit(&hsc_mutex);
+ return (hsp);
+}
+
+
+/*
+ * This function is invoked by the SCSB when an interrupt
+ * happens to indicate that a board has been inserted-in/removed-from
+ * the specified slot.
+ */
+int
+hsc_slot_occupancy(int slot_number, boolean_t occupied, int flags, int healthy)
+{
+ static const char func[] = "hsc_slot_occupancy";
+ hsc_slot_t *hsp;
+ int rc = DDI_SUCCESS;
+
+ DEBUG4("hsc_slot_occupancy: slot %d %s, ac=%d, healthy=%d",
+ slot_number, occupied ? "occupied" : "not occupied",
+ (flags == ALARM_CARD_ON_SLOT) ? 1:0, healthy);
+
+ hsp = hsc_find_slot(slot_number);
+
+ if (hsp == NULL) {
+ cmn_err(CE_NOTE,
+ "%s: cannot map slot number %d to a hsc_slot_t",
+ func, slot_number);
+ return (DDI_FAILURE);
+ }
+
+ hsp->hs_board_healthy = healthy;
+ if (occupied) {
+ /*
+ * A board was just inserted. We are disconnected at this point.
+ */
+ if (hsp->hs_slot_state == HPC_SLOT_EMPTY)
+ hsp->hs_board_type = HPC_BOARD_CPCI_HS;
+ hsp->hs_slot_state = HPC_SLOT_DISCONNECTED;
+ if (flags == ALARM_CARD_ON_SLOT) {
+ hsp->hs_flags |= HSC_ALARM_CARD_PRES;
+ DEBUG0("Xhsc_slot_occupancy: set HSC_ALARM_CARD_PRES");
+ }
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
+ HPC_LED_ON);
+ /*
+ * if previous occupant stayed configured, do not allow another
+ * occupant to be connected.
+ * So as soon as the board is plugged in, we turn both LEDs On.
+ * This behaviour is an indication that the slot state
+ * is not clean.
+ */
+ if (hsp->hs_flags & HSC_SLOT_BAD_STATE) {
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
+ HPC_LED_ON);
+ return (DDI_SUCCESS);
+ }
+
+ /* Do not allow connect if slot is disabled */
+ if ((hsp->hs_flags & HSC_SLOT_ENABLED) != HSC_SLOT_ENABLED)
+ return (DDI_SUCCESS);
+ /* if no healthy, we stay disconnected. */
+ if (healthy == B_FALSE) {
+ return (DDI_SUCCESS);
+ }
+ rc = hsc_slot_autoconnect(hsp);
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
+ HPC_LED_BLINK);
+ } else {
+ /*
+ * A board was just removed
+ */
+ hsp->hs_slot_state = HPC_SLOT_EMPTY;
+ hsp->hs_board_type = HPC_BOARD_UNKNOWN;
+ hsp->hs_flags &= ~HSC_ENUM_FAILED;
+ if (hsp->hs_flags & HSC_ALARM_CARD_PRES) {
+ hsp->hs_flags &= ~HSC_ALARM_CARD_PRES;
+ DEBUG0("Xhsc_slot_occupancy:clear HSC_ALARM_CARD_PRES");
+ }
+ if (hsp->hs_board_configured == B_TRUE) {
+ (void) hpc_slot_event_notify(hsp->hs_slot_handle,
+ HPC_EVENT_SLOT_NOT_HEALTHY, 0);
+ cmn_err(CE_WARN, "%s#%d: ALERT! Surprise Removal "
+ " on Slot %d, Occupant Online!!",
+ ddi_driver_name(hsp->hsc->dip),
+ ddi_get_instance(hsp->hsc->dip),
+ slot_number);
+ cmn_err(CE_WARN, "%s#%d: ALERT! System now in "
+ " Inconsistent State! Slot disabled. Halt!",
+ ddi_driver_name(hsp->hsc->dip),
+ ddi_get_instance(hsp->hsc->dip));
+ /* Slot in reset and disabled */
+ scsb_hsc_disable_slot(hsp);
+ hsp->hs_flags |= HSC_SLOT_BAD_STATE;
+ /* the following works for P1.0 only. */
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
+ HPC_LED_ON);
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
+ HPC_LED_ON);
+ } else {
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
+ HPC_LED_OFF);
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
+ HPC_LED_OFF);
+ }
+ }
+ return (rc);
+}
+
+
+/*
+ * This function is invoked by the SCSB when the health status of
+ * a board changes.
+ */
+/*ARGSUSED*/
+int
+scsb_hsc_board_healthy(int slot_number, boolean_t healthy)
+{
+ hsc_slot_t *hsp;
+ hsc_state_t *hsc;
+
+ DEBUG2("hsc_board_healthy: slot %d = %d\n", slot_number, healthy);
+
+ hsp = hsc_find_slot(slot_number);
+ if (hsp == NULL) {
+ cmn_err(CE_NOTE, "hsc_board_healthy: No Slot Info.");
+ return (DDI_FAILURE);
+ }
+
+ hsc = hsp->hsc;
+ if (hsp->hs_slot_state == HPC_SLOT_EMPTY) {
+#ifdef DEBUG
+ cmn_err(CE_NOTE, "%s#%d: Healthy# %s on "
+ "empty slot %d", ddi_driver_name(hsc->dip),
+ ddi_get_instance(hsc->dip),
+ healthy == B_TRUE ? "On" : "Off", slot_number);
+#endif
+ return (DDI_FAILURE);
+ }
+ if (hsp->hs_slot_state == HPC_SLOT_DISCONNECTED) {
+ DEBUG2("healthy %s on disconnected slot %d\n",
+ healthy == B_TRUE ? "On":"Off", slot_number);
+ /*
+ * Connect the slot if board healthy and in autoconfig mode.
+ */
+ hsp->hs_board_healthy = healthy;
+ if (healthy == B_TRUE)
+ return (hsc_slot_autoconnect(hsp));
+ }
+
+ /*
+ * the board is connected. The result could be seviour depending
+ * on the occupant state.
+ */
+ if (healthy == B_TRUE) {
+ if (hsp->hs_board_healthy != B_TRUE) {
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
+ HPC_LED_OFF);
+ /* Regained HEALTHY# at Run Time...!!! */
+ cmn_err(CE_NOTE, "%s#%d: slot %d Occupant "
+ "%s, Regained HEALTHY#!",
+ ddi_driver_name(hsc->dip),
+ ddi_get_instance(hsc->dip), slot_number,
+ hsp->hs_board_configured == B_TRUE ?
+ "configured" : "Unconfigured");
+ (void) hpc_slot_event_notify(hsp->hs_slot_handle,
+ HPC_EVENT_SLOT_HEALTHY_OK, 0);
+ }
+ } else {
+ if (hsp->hs_board_configured == B_TRUE) {
+ /* Lost HEALTHY# at Run Time...Serious Condition. */
+ cmn_err(CE_WARN, "%s#%d: ALERT! Lost HEALTHY#"
+ " on Slot %d, Occupant %s",
+ ddi_driver_name(hsc->dip),
+ ddi_get_instance(hsc->dip), slot_number,
+ hsp->hs_board_configured == B_TRUE ?
+ "Online!!!" : "Offline");
+ (void) hpc_slot_event_notify(hsp->hs_slot_handle,
+ HPC_EVENT_SLOT_NOT_HEALTHY, 0);
+ }
+ if ((hsp->hs_board_configured != B_TRUE) ||
+ scsb_hsc_healthy_reset) {
+ if (scsb_reset_slot(hsp->hs_hpchandle,
+ slot_number, SCSB_RESET_SLOT) == 0) {
+ /* signal Ok to remove board. */
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
+ HPC_FAULT_LED, HPC_LED_ON);
+ cmn_err(CE_WARN, "%s#%d: Slot %d "
+ "successfully taken offline",
+ ddi_driver_name(hsc->dip),
+ ddi_get_instance(hsc->dip),
+ slot_number);
+ }
+ }
+ }
+ hsp->hs_board_healthy = healthy;
+ return (DDI_SUCCESS);
+}
+
+static int
+hsc_slot_autoconnect(hsc_slot_t *hsp)
+{
+ hsc_state_t *hsc = hsp->hsc;
+ int rc = DDI_SUCCESS;
+ /*
+ * Keep slot in reset unless autoconfiguration is enabled
+ * Ie. for Basic Hotswap mode, we stay disconnected at
+ * insertion. For full hotswap mode, we automatically
+ * go into connected state at insertion, so that occupant
+ * autoconfiguration is possible.
+ */
+ if (((hsc->state & HSC_ENUM_ENABLED) == HSC_ENUM_ENABLED) &&
+ (hsp->hs_flags & HSC_AUTOCFG)) {
+ /* this statement must be here before unreset. */
+ hsc->hsp_last = hsp;
+ if ((rc = scsb_reset_slot(hsp->hs_hpchandle,
+ hsp->hs_slot_number, SCSB_UNRESET_SLOT)) == 0) {
+
+ hsp->hs_slot_state = HPC_SLOT_CONNECTED;
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
+ HPC_FAULT_LED, HPC_LED_OFF);
+ } else {
+ hsc->hsp_last = NULL;
+ rc = DDI_FAILURE;
+ }
+ }
+ return (rc);
+}
+
+/*
+ * The SCSB code should invoke this function from its _init() function.
+ */
+int
+hsc_init()
+{
+ int rc;
+
+ rc = ddi_soft_state_init(&hsc_state, sizeof (hsc_state_t), 1);
+ if (rc != 0)
+ return (rc);
+
+ hsc_slotops = hpc_alloc_slot_ops(KM_SLEEP);
+
+ hsc_slotops->hpc_version = HPC_SLOT_OPS_VERSION;
+ hsc_slotops->hpc_op_connect = hsc_connect;
+ hsc_slotops->hpc_op_disconnect = hsc_disconnect;
+ hsc_slotops->hpc_op_insert = hsc_insert;
+ hsc_slotops->hpc_op_remove = hsc_remove;
+ hsc_slotops->hpc_op_control = hsc_control;
+
+ return (DDI_SUCCESS);
+}
+
+
+/*
+ * The SCSB code should invoke this function from its _fini() function.
+ */
+int
+hsc_fini()
+{
+ if (hsc_slotops != NULL) {
+ hpc_free_slot_ops(hsc_slotops);
+ hsc_slotops = NULL;
+ }
+ ddi_soft_state_fini(&hsc_state);
+ return (DDI_SUCCESS);
+}
+
+static int
+scsb_enable_enum(hsc_state_t *hsc)
+{
+ DEBUG0("hsc: Enable ENUM#\n");
+
+ if ((hsc->state & HSC_ENUM_ENABLED) == HSC_ENUM_ENABLED)
+ return (DDI_SUCCESS);
+ if ((hsc->state & HSC_ATTACHED) != HSC_ATTACHED)
+ return (DDI_FAILURE);
+
+ if (ddi_add_intr(hsc->dip, 1, NULL, NULL,
+ hsc_enum_intr, (caddr_t)hsc) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s#%d: failed ENUM# interrupt registration",
+ ddi_driver_name(hsc->dip), ddi_get_instance(hsc->dip));
+ return (DDI_FAILURE);
+ }
+ cmn_err(CE_CONT, "?%s%d: Successfully Upgraded to "
+ "Full Hotswap Mode\n", ddi_driver_name(hsc->dip),
+ ddi_get_instance(hsc->dip));
+ hsc->state |= HSC_ENUM_ENABLED;
+ ddi_prop_update_string(DDI_DEV_T_NONE, hsc->dip, HOTSWAP_MODE_PROP,
+ "full");
+ return (DDI_SUCCESS);
+
+}
+
+/*ARGSUSED*/
+static int
+scsb_disable_enum(hsc_state_t *hsc, int op)
+{
+
+ DEBUG0("hsc: Disable ENUM#\n");
+ if (op == SCSB_HSC_FORCE_REMOVE) {
+ /*
+ * Clear all pending interrupts before unregistering
+ * the interrupt. Otherwise the system will hang.
+ *
+ * Due to the hang problem, we'll not turn off or disable
+ * interrupts because if there's a non-friendly full hotswap
+ * device out there, the ENUM# will be kept asserted and
+ * hence hsc_clear_all_enum() can never deassert ENUM#.
+ * So the system will hang.
+ */
+ if ((hsc->state & HSC_ENUM_ENABLED) == HSC_ENUM_ENABLED) {
+ /* hsc_clear_all_enum(hsc); */
+ ddi_remove_intr(hsc->dip, 1, NULL);
+ hsc->state &= ~HSC_ENUM_ENABLED;
+ cmn_err(CE_CONT, "?%s%d: Successfully Downgraded to "
+ "Basic Hotswap Mode\n",
+ ddi_driver_name(hsc->dip),
+ ddi_get_instance(hsc->dip));
+ }
+ ddi_prop_update_string(DDI_DEV_T_NONE, hsc->dip,
+ HOTSWAP_MODE_PROP, "basic");
+ return (DDI_SUCCESS);
+ } else
+ /* No programming interface for disabling ENUM# on MC/Tonga */
+ return (HPC_ERR_NOTSUPPORTED);
+}
+
+#ifndef lint
+static int
+hsc_clear_all_enum(hsc_state_t *hsc)
+{
+ int i, rc;
+ hsc_slot_t *hsp;
+
+ for (i = 0; i < hsc->slot_table_size; i++) {
+
+ hsp = hsc_find_slot(hsc->slot_table_prop[i].pslotnum);
+ if (hsp == NULL)
+ continue;
+ rc = hpc_slot_event_notify(hsp->hs_slot_handle,
+ HPC_EVENT_CLEAR_ENUM,
+ HPC_EVENT_SYNCHRONOUS);
+ if (rc == HPC_EVENT_UNCLAIMED)
+ break; /* no pending interrupts across the bus */
+ DEBUG1("Pending Intr on slot %d\n",
+ hsc->slot_table_prop[i].pslotnum);
+ }
+ return (0);
+}
+#endif
+
+int
+scsb_hsc_attach(dev_info_t *dip, void *scsb_handle, int instance)
+{
+ int i, n, prop_len;
+ int prom_prop = 0; /* default: OS property gives slot-table */
+ int rc;
+ char *hotswap_model;
+ hsc_state_t *hsc;
+ scsb_state_t *scsb = (scsb_state_t *)scsb_handle;
+ caddr_t hpc_slot_table_data, s;
+ int hpc_slot_table_size;
+ hsc_prom_slot_table_t *hpstp;
+ int rstate;
+
+ DEBUG0("hsc_attach: enter\n");
+ /*
+ * To get the slot information,
+ * The OBP defines the 'slot-table' property. But the OS
+ * can override it with 'hsc-slot-map' property
+ * through the .conf file.
+ * Since the formats are different, 2 different property names
+ * are chosen.
+ * The OBP property format is
+ * <phandle>,<pci-devno>,<phys-slotno>,<ga-bits>
+ * The OS property format is (ga-bits is not used however)
+ * <busnexus-path>,<pci-devno>,<phys-slotno>,<ga-bits>
+ */
+ rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "hsc-slot-map", (caddr_t)&hpc_slot_table_data,
+ &hpc_slot_table_size);
+ if (rc != DDI_PROP_SUCCESS) {
+ prom_prop = 1;
+ rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "slot-table", (caddr_t)&hpc_slot_table_data,
+ &hpc_slot_table_size);
+ if (rc != DDI_PROP_SUCCESS) {
+ cmn_err(CE_WARN, "%s#%d: 'slot-table' property "
+ "missing!", ddi_driver_name(dip),
+ ddi_get_instance(dip));
+ return (DDI_FAILURE);
+ }
+ }
+ rc = ddi_soft_state_zalloc(hsc_state, instance);
+ if (rc != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ hsc = (hsc_state_t *)ddi_get_soft_state(hsc_state, instance);
+ hsc->scsb_handle = scsb_handle;
+ hsc->dip = dip;
+ hsc->instance = instance;
+ hsc->n_registered_occupants = 0;
+ hsc->regDone = B_FALSE;
+ /* hsc->slot_info = hsc_slot_list; */
+
+ /*
+ * Check whether the system should be in basic or full
+ * hotswap mode. The PROM property always says full, so
+ * look at the .conf file property whether this is "full"
+ */
+ if (scsb_hsc_enable_fhs) {
+ hsc->hotswap_mode = HSC_HOTSWAP_MODE_FULL;
+ } else {
+ hsc->hotswap_mode = HSC_HOTSWAP_MODE_BASIC;
+ }
+
+ rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "default-hotswap-mode", (caddr_t)&hotswap_model, &prop_len);
+
+ if (rc == DDI_PROP_SUCCESS) {
+ if (strcmp(hotswap_model, "full") == 0) {
+ hsc->hotswap_mode = HSC_HOTSWAP_MODE_FULL;
+ } else if (strcmp(hotswap_model, "basic") == 0) {
+ hsc->hotswap_mode = HSC_HOTSWAP_MODE_BASIC;
+ }
+
+ kmem_free(hotswap_model, prop_len);
+ }
+
+ /*
+ * Determine the size of the slot table from the property and
+ * allocate the slot table arrary..Decoding is different for
+ * OS and PROM property.
+ */
+ if (!prom_prop) { /* OS .conf property */
+ for (i = 0, n = 0; i < hpc_slot_table_size; i++) {
+ if (hpc_slot_table_data[i] == 0) {
+ n++;
+ }
+ }
+
+ /* There should be four elements per entry */
+ if (n % 4) {
+ cmn_err(CE_WARN, "%s#%d: bad format for "
+ "slot-table(%d)", ddi_driver_name(dip),
+ ddi_get_instance(dip), n);
+ kmem_free(hpc_slot_table_data, hpc_slot_table_size);
+ ddi_soft_state_free(hsc_state, instance);
+ return (DDI_FAILURE);
+ }
+
+ hsc->slot_table_size = n / 4;
+ } else {
+ hsc->slot_table_size = hpc_slot_table_size /
+ sizeof (hsc_prom_slot_table_t);
+ n = hpc_slot_table_size % sizeof (hsc_prom_slot_table_t);
+ if (n) {
+ cmn_err(CE_WARN, "%s#%d: bad format for "
+ "slot-table(%d)", ddi_driver_name(dip),
+ ddi_get_instance(dip), hpc_slot_table_size);
+ kmem_free(hpc_slot_table_data, hpc_slot_table_size);
+ ddi_soft_state_free(hsc_state, instance);
+ return (DDI_FAILURE);
+ }
+ }
+
+ /*
+ * Netract800 FTC (formerly known as CFTM) workaround.
+ * Leave Slot 2 out of the HS table if FTC is present in Slot 2
+ */
+ if (scsb->scsb_hsc_state & SCSB_HSC_CTC_PRES) {
+ hsc->slot_table_size -= 1;
+ }
+ DEBUG1("hsc_attach: %d hotplug slots on bus\n", hsc->slot_table_size);
+ /*
+ * Create enough space for each slot table entry
+ * based on how many entries in the property
+ */
+ hsc->slot_table_prop = (hsc_slot_table_t *)
+ kmem_zalloc(hsc->slot_table_size *
+ sizeof (hsc_slot_table_t), KM_SLEEP);
+
+ if (!prom_prop) {
+ s = hpc_slot_table_data;
+ for (i = 0; i < hsc->slot_table_size; i++) {
+
+ char *nexus, *pcidev, *phys_slotname, *ga;
+
+ /* Pick off pointer to nexus path or PROM handle */
+ nexus = s;
+ while (*s != NULL)
+ s++;
+ s++;
+
+ /* Pick off pointer to the pci device number */
+ pcidev = s;
+ while (*s != NULL)
+ s++;
+ s++;
+
+ /* Pick off physical slot no */
+ phys_slotname = s;
+ while (*s != NULL)
+ s++;
+ s++;
+
+ /* Pick off GA bits which we dont use for now. */
+ ga = s;
+ while (*s != NULL)
+ s++;
+ s++;
+
+ if (scsb->scsb_hsc_state & SCSB_HSC_CTC_PRES &&
+ atoi(phys_slotname) == SC_MC_CTC_SLOT) {
+ --i;
+ continue;
+ }
+ hsc->slot_table_prop[i].pslotnum = atoi(phys_slotname);
+ hsc->slot_table_prop[i].ga = atoi(ga);
+ hsc->slot_table_prop[i].pci_devno = atoi(pcidev);
+ strcpy(hsc->slot_table_prop[i].nexus, nexus);
+ }
+ } else {
+ hpstp = (hsc_prom_slot_table_t *)hpc_slot_table_data;
+ for (i = 0; i < hsc->slot_table_size; i++, hpstp++) {
+ if (scsb->scsb_hsc_state & SCSB_HSC_CTC_PRES &&
+ hpstp->pslotnum == SC_MC_CTC_SLOT) {
+ --i;
+ continue;
+ }
+ hsc->slot_table_prop[i].pslotnum = hpstp->pslotnum;
+ hsc->slot_table_prop[i].ga = hpstp->ga;
+ hsc->slot_table_prop[i].pci_devno = hpstp->pci_devno;
+
+ if (prom_phandle_to_path((uint_t)hpstp->phandle,
+ hsc->slot_table_prop[i].nexus,
+ sizeof (hsc->slot_table_prop[i].nexus))
+ == -1) {
+ cmn_err(CE_WARN, "%s#%d: Cannot get phandle "
+ "to nexus path", ddi_driver_name(dip),
+ ddi_get_instance(dip));
+ kmem_free(hsc->slot_table_prop,
+ (hsc->slot_table_size *
+ sizeof (hsc_slot_table_t)));
+ kmem_free(hpc_slot_table_data,
+ hpc_slot_table_size);
+ ddi_soft_state_free(hsc_state, instance);
+ return (DDI_FAILURE);
+ }
+ }
+ }
+
+ /* keep healthy register cache uptodate before reading slot state */
+ if (scsb_read_bhealthy(scsb_handle) != 0) {
+ cmn_err(CE_WARN, "%s#%d: hsc_attach: Cannot read "
+ "Healthy Registers", ddi_driver_name(dip),
+ ddi_get_instance(dip));
+ kmem_free(hsc->slot_table_prop,
+ (hsc->slot_table_size *
+ sizeof (hsc_slot_table_t)));
+ kmem_free(hpc_slot_table_data,
+ hpc_slot_table_size);
+ ddi_soft_state_free(hsc_state, instance);
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Before we start registering the slots, calculate how many
+ * slots are occupied.
+ */
+
+ for (i = 0; i < hsc->slot_table_size; i++) {
+ if (scsb_get_slot_state(scsb_handle,
+ hsc->slot_table_prop[i].pslotnum, &rstate) !=
+ DDI_SUCCESS)
+ return (rc);
+ if (rstate != HPC_SLOT_EMPTY)
+ hsc->n_registered_occupants++;
+ }
+
+ mutex_init(&hsc->hsc_mutex, NULL, MUTEX_DRIVER, NULL);
+ for (i = 0; i < hsc->slot_table_size; i++) {
+
+ DEBUG2("Registering on nexus [%s] cPCI device [%d]\n",
+ hsc->slot_table_prop[i].nexus,
+ hsc->slot_table_prop[i].pci_devno);
+
+ if (hsc_slot_register(hsc, hsc->slot_table_prop[i].nexus,
+ hsc->slot_table_prop[i].pci_devno,
+ hsc->slot_table_prop[i].pslotnum, B_FALSE) !=
+ HPC_SUCCESS) {
+
+ cmn_err(CE_WARN, "%s#%d: Slot Registration Failure",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+ while (i) {
+ i--;
+ n = hsc->slot_table_prop[i].pslotnum;
+ if (hsc_slot_unregister(n) != 0) {
+ cmn_err(CE_WARN,
+ "%s#%d: failed to unregister"
+ " slot %d",
+ ddi_driver_name(dip),
+ ddi_get_instance(dip), n);
+
+ }
+ }
+ mutex_destroy(&hsc->hsc_mutex);
+ kmem_free(hsc->slot_table_prop, (hsc->slot_table_size *
+ sizeof (hsc_slot_table_t)));
+ kmem_free(hpc_slot_table_data, hpc_slot_table_size);
+ ddi_soft_state_free(hsc_state, instance);
+ return (DDI_FAILURE);
+ }
+ }
+
+ hsc->hsp_last = NULL;
+ hsc->hsc_intr_counter = 0;
+ kmem_free(hpc_slot_table_data, hpc_slot_table_size);
+ ddi_prop_update_string(DDI_DEV_T_NONE, hsc->dip, HOTSWAP_MODE_PROP,
+ "basic");
+ hsc->state |= (HSC_ATTACHED|HSC_SCB_CONNECTED);
+
+ /*
+ * We enable full hotswap right here if all the slots are empty.
+ */
+ if ((hsc->regDone == B_FALSE && hsc->n_registered_occupants == 0) ||
+ scsb_hsc_numReg == hsc->n_registered_occupants) {
+ hsc->regDone = B_TRUE;
+ if (hsc->hotswap_mode == HSC_HOTSWAP_MODE_FULL) {
+ if (scsb_enable_enum(hsc) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s#%d: Cannot enable "
+ "Full Hotswap", ddi_driver_name(dip),
+ ddi_get_instance(dip));
+ }
+ }
+ }
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+int
+scsb_hsc_detach(dev_info_t *dip, void *scsb_handle, int instance)
+{
+ int i = 0;
+ hsc_state_t *hsc;
+ char slotautocfg_prop[18];
+
+ DEBUG0("hsc_detach: enter\n");
+ hsc = (hsc_state_t *)ddi_get_soft_state(hsc_state, instance);
+ if (hsc == NULL) {
+ DEBUG2("%s#%d: hsc_detach: Soft state NULL",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+ return (DDI_FAILURE);
+ }
+
+ if ((hsc->state & HSC_ATTACHED) != HSC_ATTACHED)
+ return (DDI_FAILURE);
+ /*
+ * let's unregister the hotpluggable slots with hotplug service.
+ */
+ for (i = 0; i < hsc->slot_table_size; i++) {
+
+ hsc_slot_t *hsp;
+
+ hsp = hsc_find_slot(hsc->slot_table_prop[i].pslotnum);
+ if (hsp == NULL) {
+ cmn_err(CE_WARN, "%s#%d: hsc_detach: No Slot Info",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+ } else {
+ hpc_led_info_t aledinfo; /* active led info. */
+ hpc_led_info_t fledinfo; /* fault led info. */
+
+ aledinfo.led = HPC_ACTIVE_LED;
+ aledinfo.state = HPC_LED_BLINK;
+ fledinfo.led = HPC_FAULT_LED;
+ fledinfo.state = HPC_LED_OFF;
+ (void) hsc_led_state(hsp, HPC_CTRL_SET_LED_STATE,
+ &aledinfo);
+ (void) hsc_led_state(hsp, HPC_CTRL_SET_LED_STATE,
+ &fledinfo);
+ }
+ sprintf(slotautocfg_prop, "slot%d-autoconfig",
+ hsp->hs_slot_number);
+ ddi_prop_remove(DDI_DEV_T_NONE, hsc->dip, slotautocfg_prop);
+ if (hsc_slot_unregister(hsc->slot_table_prop[i].pslotnum)
+ != 0) {
+ cmn_err(CE_NOTE, "%s#%d: failed to unregister"
+ " slot %d\n", ddi_driver_name(dip),
+ ddi_get_instance(dip),
+ hsc->slot_table_prop[i].pslotnum);
+ return (DDI_FAILURE);
+ }
+ }
+ kmem_free(hsc->slot_table_prop, (hsc->slot_table_size *
+ sizeof (hsc_slot_table_t)));
+ if ((hsc->state & HSC_ENUM_ENABLED) == HSC_ENUM_ENABLED) {
+ ddi_remove_intr(hsc->dip, 1, hsc->enum_iblock);
+ hsc->state &= ~HSC_ENUM_ENABLED;
+ }
+ mutex_destroy(&hsc->hsc_mutex);
+ ddi_prop_remove(DDI_DEV_T_NONE, hsc->dip, HOTSWAP_MODE_PROP);
+ hsc->state &= ~(HSC_ATTACHED|HSC_SCB_CONNECTED);
+ ddi_soft_state_free(hsc_state, instance);
+ return (DDI_SUCCESS);
+}
+
+/*
+ * The following function is called when the SCSB is hot extracted from
+ * the system.
+ */
+int
+scsb_hsc_freeze(dev_info_t *dip)
+{
+ hsc_state_t *hsc;
+ int instance = ddi_get_instance(dip);
+ int i;
+ hsc_slot_t *hsp;
+
+ hsc = (hsc_state_t *)ddi_get_soft_state(hsc_state, instance);
+ if (hsc == NULL) {
+ DEBUG2("%s#%d: Soft state NULL",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+ return (DDI_SUCCESS);
+ }
+ if ((hsc->state & HSC_ATTACHED) != HSC_ATTACHED)
+ return (DDI_SUCCESS);
+ hsc->state &= ~HSC_SCB_CONNECTED;
+
+ for (i = 0; i < hsc->slot_table_size; i++) {
+ hsp = hsc_find_slot(hsc->slot_table_prop[i].pslotnum);
+
+ if (hsp == NULL) {
+ cmn_err(CE_NOTE, "hsc_freeze: "
+ " Cannot map slot number %d to a hsc_slot_t",
+ hsc->slot_table_prop[i].pslotnum);
+ continue;
+ }
+ /*
+ * Since reset lines are pulled low, lets mark these
+ * slots and not allow a connect operation.
+ * Note that we still keep the slot as slot disconnected,
+ * although it is connected from the hardware standpoint.
+ * As soon as the SCB is plugged back in, we check these
+ * states and put the hardware state back to its original
+ * state.
+ */
+ if (hsp->hs_slot_state == HPC_SLOT_DISCONNECTED) {
+ cmn_err(CE_WARN, "%s#%d: Slot %d Now out of Reset!",
+ ddi_driver_name(hsc->dip),
+ ddi_get_instance(hsc->dip),
+ hsp->hs_slot_number);
+ }
+ hsp->hs_flags |= HSC_SCB_HOTSWAPPED;
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * The following function is called when the SCSB is hot inserted from
+ * the system. We must update the LED status and set the RST# registers
+ * again.
+ */
+int
+scsb_hsc_restore(dev_info_t *dip)
+{
+ int i;
+ hsc_state_t *hsc;
+ hsc_slot_t *hsp;
+ int instance = ddi_get_instance(dip);
+
+ hsc = (hsc_state_t *)ddi_get_soft_state(hsc_state, instance);
+ if (hsc == NULL) {
+ DEBUG2("%s#%d: Soft state NULL",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+ return (DDI_SUCCESS);
+ }
+
+ if ((hsc->state & HSC_ATTACHED) != HSC_ATTACHED)
+ return (DDI_SUCCESS);
+ hsc->state |= HSC_SCB_CONNECTED;
+ for (i = 0; i < hsc->slot_table_size; i++) {
+ hsp = hsc_find_slot(hsc->slot_table_prop[i].pslotnum);
+
+ if (hsp == NULL) {
+ cmn_err(CE_NOTE, "%s#%d: hsc_restore: "
+ " Cannot map slot number %d to a hsc_slot_t",
+ ddi_driver_name(hsc->dip),
+ ddi_get_instance(hsc->dip),
+ hsc->slot_table_prop[i].pslotnum);
+ continue;
+ }
+ if ((hsp->hs_slot_state == HPC_SLOT_DISCONNECTED) &&
+ (hsp->hs_board_configured == B_FALSE)) {
+ if (scsb_reset_slot(hsp->hs_hpchandle,
+ hsp->hs_slot_number,
+ SCSB_RESET_SLOT) != 0) {
+ cmn_err(CE_WARN, "%s#%d: hsc_restore: "
+ " Cannot reset disconnected slot %d",
+ ddi_driver_name(hsc->dip),
+ ddi_get_instance(hsc->dip),
+ hsp->hs_slot_number);
+ }
+ }
+
+ if (scsb_hsc_init_slot_state(hsc, hsp) != DDI_SUCCESS) {
+
+ cmn_err(CE_WARN, "%s#%d: hsc_freeze: Cannot init"
+ " slot%d state",
+ ddi_driver_name(hsc->dip),
+ ddi_get_instance(hsc->dip),
+ hsp->hs_slot_number);
+ }
+ hsp->hs_flags &= ~HSC_SCB_HOTSWAPPED;
+ }
+ return (DDI_SUCCESS);
+}
+
+#ifndef lint
+int
+scsb_hsc_freeze_check(dev_info_t *dip)
+{
+ hsc_state_t *hsc;
+ int instance = ddi_get_instance(dip);
+
+ hsc = (hsc_state_t *)ddi_get_soft_state(hsc_state, instance);
+ if (hsc == NULL) {
+ DEBUG2("%s#%d: Soft state NULL",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+ return (DDI_SUCCESS);
+ }
+ if ((hsc->state & HSC_ATTACHED) != HSC_ATTACHED)
+ return (DDI_SUCCESS);
+ return (DDI_SUCCESS);
+}
+#endif
+
+/*
+ * update info about Alarm Card insert/remove mechanism.
+ */
+void
+hsc_ac_op(int instance, int pslotnum, int op, void *arg)
+{
+ hsc_slot_t *hsp;
+ hsc_state_t *hsc;
+
+ hsc = (hsc_state_t *)ddi_get_soft_state(hsc_state, instance);
+ if (hsc == NULL) {
+ cmn_err(CE_WARN, "%s#%d: hsc_ac_op: No Soft State Info",
+ ddi_driver_name(hsc->dip), ddi_get_instance(hsc->dip));
+ return;
+ }
+
+ hsp = hsc_find_slot(pslotnum);
+ if (hsp == NULL) {
+ cmn_err(CE_WARN, "%s#%d: hsc_ac_op: No Slot Info",
+ ddi_driver_name(hsc->dip), ddi_get_instance(hsc->dip));
+ return;
+ }
+
+ switch (op) {
+ case SCSB_HSC_AC_UNCONFIGURE :
+ /*
+ * If ENUM# is enabled, then action is pending on
+ * this slot, just send a event.
+ */
+ if (hsc->state & HSC_ENUM_ENABLED)
+ hpc_slot_event_notify(hsp->hs_slot_handle,
+ HPC_EVENT_PROCESS_ENUM, 0);
+ break;
+ case SCSB_HSC_AC_GET_SLOT_INFO :
+ *(hsc_slot_t **)arg = hsp;
+ break;
+ default :
+ break;
+ }
+}
+
+static uint_t
+hsc_enum_intr(caddr_t iarg)
+{
+ int rc;
+ hsc_state_t *hsc = (hsc_state_t *)iarg;
+ hsc_slot_t *hsp;
+
+ DEBUG0("!E!");
+ if ((hsc->state & HSC_ATTACHED) == 0)
+ return (DDI_INTR_UNCLAIMED);
+
+ hsp = hsc_find_slot(hsc->slot_table_prop[0].pslotnum);
+ if (hsp == NULL) /* No slots registered */
+ return (DDI_INTR_UNCLAIMED);
+
+ /*
+ * The following must be done to clear interrupt (synchronous event).
+ * To process the interrupt, we send an asynchronous event.
+ */
+ rc = hpc_slot_event_notify(hsp->hs_slot_handle,
+ HPC_EVENT_CLEAR_ENUM,
+ HPC_EVENT_SYNCHRONOUS);
+ if (rc == HPC_EVENT_UNCLAIMED) {
+ /*
+ * possible support for handling insertion of non friendly
+ * full hotswap boards, otherwise the system hangs due
+ * to uncleared interrupt bursts.
+ */
+ DEBUG2("!E>counter %d, last op@slot %lx\n",
+ hsc->hsc_intr_counter, hsc->hsp_last);
+ hsc->hsc_intr_counter ++;
+ if (hsc->hsc_intr_counter == scsb_hsc_max_intr_count) {
+ if (!hsc->hsp_last) {
+ cmn_err(CE_WARN, "%s#%d: hsc_enum_intr: "
+ " No Last Board Insertion Info.",
+ ddi_driver_name(hsc->dip),
+ ddi_get_instance(hsc->dip));
+ hsc->hsc_intr_counter = 0;
+ return (DDI_INTR_UNCLAIMED);
+ }
+ hsp = hsc->hsp_last;
+ cmn_err(CE_WARN, "%s#%d: Bad (non friendly ?) Board "
+ "in Slot %d ? Taking it Offline.",
+ ddi_driver_name(hsc->dip),
+ ddi_get_instance(hsc->dip),
+ hsp->hs_slot_number);
+ /*
+ * this should put just inserted board back in
+ * reset, thus deasserting the ENUM# and the
+ * system hang.
+ */
+ if (scsb_reset_slot(hsp->hs_hpchandle,
+ hsp->hs_slot_number,
+ SCSB_RESET_SLOT) == 0) {
+ /* Enumeration failed on this board */
+ hsp->hs_flags |= HSC_ENUM_FAILED;
+ if (hsp->hs_board_configured == B_TRUE)
+ cmn_err(CE_WARN, "%s#%d: ALERT! System"
+ " now in Inconsistent State."
+ " Halt!",
+ ddi_driver_name(hsc->dip),
+ ddi_get_instance(hsc->dip));
+ hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
+ HPC_FAULT_LED, HPC_LED_ON);
+ }
+ hsc->hsc_intr_counter = 0;
+ }
+ return (DDI_INTR_UNCLAIMED);
+ }
+ hsc->hsc_intr_counter = 0;
+ /*
+ * if interrupt success, rc denotes the PCI device number which
+ * generated the ENUM# interrupt.
+ */
+ hsp = hsc_get_slot_info(hsc, rc);
+ if (hsp == NULL) {
+ cmn_err(CE_WARN, "%s#%d: hsc_enum_intr: no slot info for "
+ "dev %x", ddi_driver_name(hsc->dip),
+ ddi_get_instance(hsc->dip), rc);
+ return (DDI_INTR_CLAIMED); /* interrupt already cleared */
+ }
+ /* if this is Alarm Card and if it is busy, dont process event */
+ if (hsp->hs_flags & HSC_ALARM_CARD_PRES) {
+ if (scsb_hsc_ac_op(hsp->hs_hpchandle, hsp->hs_slot_number,
+ SCSB_HSC_AC_BUSY) == B_TRUE) {
+ /*
+ * Busy means we need to inform (envmond)alarmcard.so
+ * that it should save the AC configuration, stop the
+ * heartbeat, and shutdown the RSC link.
+ */
+ (void) scsb_hsc_ac_op(hsp->hs_hpchandle,
+ hsp->hs_slot_number,
+ SCSB_HSC_AC_REMOVAL_ALERT);
+ return (DDI_INTR_CLAIMED);
+ }
+ }
+ /*
+ * If SCB was swapped out, dont process ENUM#. We put this slot
+ * back in reset after SCB is inserted.
+ */
+ if ((hsp->hs_flags & HSC_SCB_HOTSWAPPED) &&
+ (hsp->hs_slot_state == HPC_SLOT_DISCONNECTED))
+ return (DDI_INTR_CLAIMED);
+
+ hpc_slot_event_notify(hsp->hs_slot_handle, HPC_EVENT_PROCESS_ENUM, 0);
+ return (DDI_INTR_CLAIMED);
+}
+/*
+ * A routine to convert a number (represented as a string) to
+ * the integer value it represents.
+ */
+
+static int
+isdigit(int ch)
+{
+ return (ch >= '0' && ch <= '9');
+}
+
+#define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
+#define bad(val) (val == NULL || !isdigit(*val))
+
+static int
+atoi(const char *p)
+{
+ int n;
+ int c, neg = 0;
+
+ if (!isdigit(c = *p)) {
+ while (isspace(c))
+ c = *++p;
+ switch (c) {
+ case '-':
+ neg++;
+ /* FALLTHROUGH */
+ case '+':
+ c = *++p;
+ }
+ if (!isdigit(c))
+ return (0);
+ }
+ for (n = '0' - c; isdigit(c = *++p); ) {
+ n *= 10; /* two steps to avoid unnecessary overflow */
+ n += '0' - c; /* accum neg to avoid surprises at MAX */
+ }
+ return (neg ? n : -n);
+}