summaryrefslogtreecommitdiff
path: root/usr/src/uts/sun4u/daktari/io/hpc3130_dak.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/sun4u/daktari/io/hpc3130_dak.c')
-rw-r--r--usr/src/uts/sun4u/daktari/io/hpc3130_dak.c2093
1 files changed, 2093 insertions, 0 deletions
diff --git a/usr/src/uts/sun4u/daktari/io/hpc3130_dak.c b/usr/src/uts/sun4u/daktari/io/hpc3130_dak.c
new file mode 100644
index 0000000000..8ef835625b
--- /dev/null
+++ b/usr/src/uts/sun4u/daktari/io/hpc3130_dak.c
@@ -0,0 +1,2093 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Daktari platform specific hotplug controller. This
+ * driver exports the same interfaces to user space
+ * as the generic hpc3130 driver. It adds specific
+ * functionality found on Daktari, such as slot button
+ * and platform specific LED displays. Placed in
+ * the daktari specific platform directory, it will
+ * be loaded instead of the generic module.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/cmn_err.h>
+#include <sys/kmem.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/note.h>
+#include <sys/hotplug/hpctrl.h>
+#include <sys/hotplug/hpcsvc.h>
+#include <sys/i2c/clients/hpc3130.h>
+#include <sys/hpc3130_events.h>
+#include <sys/daktari.h>
+#include <sys/hpc3130_dak.h>
+
+#ifdef DEBUG
+static int hpc3130debug = 0;
+
+#define D1CMN_ERR(ARGS) if (hpc3130debug & 0x1) cmn_err ARGS;
+#define D2CMN_ERR(ARGS) if (hpc3130debug & 0x2) cmn_err ARGS;
+
+#else
+
+#define D1CMN_ERR(ARGS)
+#define D2CMN_ERR(ARGS)
+
+#endif /* DEBUG */
+
+#define HPC3130_REG(offset, slot) ((offset) + ((slot)*8))
+#define HPC3130_PIL 1
+struct tuple {
+ uint8_t reg;
+ uint8_t val;
+};
+
+struct connect_command {
+ boolean_t set_bit;
+ uint8_t value;
+};
+
+struct tuple pci_sequence [] =
+{
+ {HPC3130_GCR, HPC3130_AUTO2_SEQ},
+ {HPC3130_INTERRUPT, HPC3130_PWRGOOD |
+ HPC3130_DETECT0 | HPC3130_PRSNT1 | HPC3130_PRSNT2},
+ {HPC3130_EVENT_STATUS, 0xff},
+ {HPC3130_NO_REGISTER, 0},
+};
+
+struct tuple cpu_sequence [] =
+{
+ {HPC3130_INTERRUPT,
+ HPC3130_PRSNT1 | HPC3130_DETECT0},
+ {HPC3130_EVENT_STATUS, 0xff},
+ {HPC3130_NO_REGISTER, 0},
+};
+
+struct connect_command connect_sequence [] =
+{
+ {B_TRUE, HPC3130_SLOTREQ64},
+ {B_FALSE, HPC3130_SLOTRST},
+ {B_FALSE, HPC3130_CLKON},
+ {B_FALSE, HPC3130_REQ64},
+ {B_FALSE, HPC3130_SLOTREQ64},
+ {B_TRUE, HPC3130_SLOTRST},
+ {B_FALSE, HPC3130_BUS_CTL},
+};
+
+#define HPC3130_CONNECT_SEQ_COUNT (sizeof (connect_sequence)/ \
+ sizeof (struct connect_command))
+
+struct xlate_entry {
+ char *nexus;
+ int pcidev;
+};
+/*
+ * The order here is significant. Its the order
+ * of appearance of slots from bottom to top
+ * on a Sun-Fire-880
+ */
+static struct xlate_entry slot_translate[] =
+{
+ {"/pci@8,700000", 5}, /* PCI0 */
+ {"/pci@8,700000", 4}, /* PCI1 */
+ {"/pci@8,700000", 3}, /* PCI2 */
+ {"/pci@8,700000", 2}, /* PCI3 */
+
+ {"/pci@9,700000", 4}, /* PCI4 */
+ {"/pci@9,700000", 3}, /* PCI5 */
+ {"/pci@9,700000", 2}, /* PCI6 */
+
+ {"/pci@9,600000", 2}, /* PCI7 */
+ {"/pci@9,600000", 1} /* PCI8 */
+};
+
+#define HPC3130_LOOKUP_SLOTS (sizeof (slot_translate)/ \
+ sizeof (struct xlate_entry))
+
+static int control_slot_control = HPC3130_SLOT_CONTROL_ENABLE;
+
+hpc3130_unit_t *hpc3130soft_statep;
+
+static int hpc3130_atoi(const char *);
+int hpc3130_lookup_slot(char *, int);
+
+static int hpc3130_init(dev_info_t *, struct tuple *);
+static uint_t hpc3130_hard_intr(caddr_t);
+
+static int hpc3130_cpu_init(hpc3130_unit_t *, int, i2c_client_hdl_t);
+static int hpc3130_debounce_status(i2c_client_hdl_t, int, uint8_t *);
+static int hpc3130_read(i2c_client_hdl_t, uint8_t, uint8_t, uint8_t *);
+static int hpc3130_write(i2c_client_hdl_t, uint8_t, uint8_t, uint8_t);
+static int hpc3130_rw(i2c_client_hdl_t, uint8_t, boolean_t, uint8_t *);
+
+static int hpc3130_do_attach(dev_info_t *);
+static int hpc3130_do_detach(dev_info_t *);
+static int hpc3130_do_resume(void);
+static int hpc3130_do_suspend();
+static int hpc3130_get(intptr_t, int, hpc3130_unit_t *, int);
+static int hpc3130_set(intptr_t, int, hpc3130_unit_t *, int);
+
+static int hpc3130_slot_connect(caddr_t, hpc_slot_t, void *, uint_t);
+static int hpc3130_slot_disconnect(caddr_t, hpc_slot_t, void *, uint_t);
+static int hpc3130_verify_slot_power(hpc3130_unit_t *, i2c_client_hdl_t,
+ uint8_t, char *, boolean_t);
+static int hpc3130_slot_insert(caddr_t, hpc_slot_t, void *, uint_t);
+static int hpc3130_slot_remove(caddr_t, hpc_slot_t, void *, uint_t);
+static int hpc3130_slot_control(caddr_t, hpc_slot_t, int, caddr_t);
+/*
+ * cb ops
+ */
+static int hpc3130_open(dev_t *, int, int, cred_t *);
+static int hpc3130_close(dev_t, int, int, cred_t *);
+static int hpc3130_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+static int hpc3130_poll(dev_t dev, short events, int anyyet, short
+ *reventsp, struct pollhead **phpp);
+
+static struct cb_ops hpc3130_cbops = {
+ hpc3130_open, /* open */
+ hpc3130_close, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ hpc3130_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ hpc3130_poll, /* poll */
+ ddi_prop_op, /* cb_prop_op */
+ NULL, /* streamtab */
+ D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
+ CB_REV, /* rev */
+ nodev, /* int (*cb_aread)() */
+ nodev /* int (*cb_awrite)() */
+};
+
+/*
+ * dev ops
+ */
+static int hpc3130_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
+ void **result);
+static int hpc3130_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+static int hpc3130_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+
+static struct dev_ops hpc3130_ops = {
+ DEVO_REV,
+ 0,
+ hpc3130_info,
+ nulldev,
+ nulldev,
+ hpc3130_attach,
+ hpc3130_detach,
+ nodev,
+ &hpc3130_cbops,
+ NULL
+};
+
+extern struct mod_ops mod_driverops;
+
+static struct modldrv hpc3130_modldrv = {
+ &mod_driverops, /* type of module - driver */
+ "Hotplug controller driver %I%",
+ &hpc3130_ops
+};
+
+static struct modlinkage hpc3130_modlinkage = {
+ MODREV_1,
+ &hpc3130_modldrv,
+ 0
+};
+
+int
+_init(void)
+{
+ int error;
+
+ error = mod_install(&hpc3130_modlinkage);
+
+ if (!error)
+ (void) ddi_soft_state_init((void *)&hpc3130soft_statep,
+ sizeof (hpc3130_unit_t), 4);
+ return (error);
+}
+
+int
+_fini(void)
+{
+ int error;
+
+ error = mod_remove(&hpc3130_modlinkage);
+ if (!error)
+ ddi_soft_state_fini((void *)&hpc3130soft_statep);
+
+ return (error);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&hpc3130_modlinkage, modinfop));
+}
+
+static int
+hpc3130_open(dev_t *devp, int flags, int otyp, cred_t *credp)
+{
+ _NOTE(ARGUNUSED(credp))
+ hpc3130_unit_t *unitp;
+ int instance;
+ int error = 0;
+
+ if (otyp != OTYP_CHR) {
+ return (EINVAL);
+ }
+
+ instance = MINOR_TO_INST(getminor(*devp));
+
+ unitp = (hpc3130_unit_t *)
+ ddi_get_soft_state(hpc3130soft_statep, instance);
+
+ if (unitp == NULL) {
+ return (ENXIO);
+ }
+
+ mutex_enter(&unitp->hpc3130_mutex);
+
+ if (flags & FEXCL) {
+ if (unitp->hpc3130_oflag != 0) {
+ error = EBUSY;
+ } else {
+ unitp->hpc3130_oflag = FEXCL;
+ }
+ } else {
+ if (unitp->hpc3130_oflag == FEXCL) {
+ error = EBUSY;
+ } else {
+ unitp->hpc3130_oflag = FOPEN;
+ }
+ }
+
+ mutex_exit(&unitp->hpc3130_mutex);
+
+ return (error);
+}
+
+static int
+hpc3130_close(dev_t dev, int flags, int otyp, cred_t *credp)
+{
+ _NOTE(ARGUNUSED(flags, otyp, credp))
+ hpc3130_unit_t *unitp;
+ int instance;
+
+ instance = MINOR_TO_INST(getminor(dev));
+
+ unitp = (hpc3130_unit_t *)
+ ddi_get_soft_state(hpc3130soft_statep, instance);
+
+ if (unitp == NULL) {
+ return (ENXIO);
+ }
+
+ mutex_enter(&unitp->hpc3130_mutex);
+
+ unitp->hpc3130_oflag = 0;
+
+ mutex_exit(&unitp->hpc3130_mutex);
+ return (DDI_SUCCESS);
+}
+
+static int
+hpc3130_get(intptr_t arg, int reg, hpc3130_unit_t *unitp, int mode)
+{
+ i2c_transfer_t *i2c_tran_pointer;
+ int err = DDI_SUCCESS;
+
+ if (arg == NULL) {
+ D2CMN_ERR((CE_WARN, "ioctl: arg passed in to "
+ "ioctl = NULL"));
+ return (EINVAL);
+ }
+ (void) i2c_transfer_alloc(unitp->hpc3130_hdl, &i2c_tran_pointer,
+ 1, 1, I2C_SLEEP);
+ if (i2c_tran_pointer == NULL) {
+ D2CMN_ERR((CE_WARN, "Failed in HPC3130_GET_STATUS"
+ " i2c_tran_pointer not allocated"));
+ return (ENOMEM);
+ }
+
+ i2c_tran_pointer->i2c_flags = I2C_WR_RD;
+ i2c_tran_pointer->i2c_wbuf[0] = (uchar_t)reg;
+
+ err = i2c_transfer(unitp->hpc3130_hdl, i2c_tran_pointer);
+ if (err) {
+ D2CMN_ERR((CE_WARN, "Failed in HPC3130_GET_STATUS"
+ " i2c_trasfer routine"));
+ i2c_transfer_free(unitp->hpc3130_hdl, i2c_tran_pointer);
+ return (err);
+ }
+ D1CMN_ERR((CE_NOTE, "The i2c_rbuf contains %x",
+ i2c_tran_pointer->i2c_rbuf[0]));
+
+ if (ddi_copyout((caddr_t)i2c_tran_pointer->i2c_rbuf,
+ (caddr_t)arg,
+ sizeof (uint8_t), mode) != DDI_SUCCESS) {
+ D2CMN_ERR((CE_WARN, "Failed in HPC3130_GET_STATUS"
+ " ddi_copyout routine"));
+ err = EFAULT;
+ }
+ i2c_transfer_free(unitp->hpc3130_hdl, i2c_tran_pointer);
+ return (err);
+}
+
+static int
+hpc3130_set(intptr_t arg, int reg, hpc3130_unit_t *unitp, int mode)
+{
+ i2c_transfer_t *i2c_tran_pointer;
+ int err = DDI_SUCCESS;
+ uint8_t passin_byte;
+
+ if (arg == NULL) {
+ D2CMN_ERR((CE_WARN, "ioctl: arg passed in to "
+ "ioctl = NULL"));
+ return (EINVAL);
+ }
+ if (ddi_copyin((caddr_t)arg, (caddr_t)&passin_byte,
+ sizeof (uint8_t), mode) != DDI_SUCCESS) {
+ D2CMN_ERR((CE_WARN, "Failed in HPC3130_SET_CONTROL "
+ "ddi_copyin routine"));
+
+ return (EFAULT);
+ }
+ (void) i2c_transfer_alloc(unitp->hpc3130_hdl, &i2c_tran_pointer,
+ 2, 0, I2C_SLEEP);
+ if (i2c_tran_pointer == NULL) {
+ D2CMN_ERR((CE_WARN, "Failed in "
+ "HPC3130_SET_CONTROL i2c_tran_pointer not allocated"));
+
+ return (ENOMEM);
+ }
+
+ i2c_tran_pointer->i2c_flags = I2C_WR;
+ i2c_tran_pointer->i2c_wbuf[0] = (uchar_t)reg;
+ i2c_tran_pointer->i2c_wbuf[1] = passin_byte;
+
+ err = i2c_transfer(unitp->hpc3130_hdl, i2c_tran_pointer);
+ i2c_transfer_free(unitp->hpc3130_hdl, i2c_tran_pointer);
+
+ return (err);
+}
+
+static int
+hpc3130_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
+{
+ _NOTE(ARGUNUSED(credp, rvalp))
+ hpc3130_unit_t *unitp;
+ int err = DDI_SUCCESS;
+ i2c_transfer_t *i2c_tran_pointer;
+ i2c_reg_t ioctl_reg;
+ int port = MINOR_TO_PORT(getminor(dev));
+ int instance = MINOR_TO_INST(getminor(dev));
+ hpc3130_slot_table_entry_t *ste;
+
+ if (!(port >= 0 && port < HPC3130_MAX_SLOT)) {
+ return (EINVAL);
+ }
+
+ unitp = (hpc3130_unit_t *)
+ ddi_get_soft_state(hpc3130soft_statep, instance);
+
+ if (unitp == NULL) {
+ D1CMN_ERR((CE_WARN, "unitp not filled"));
+ return (ENOMEM);
+ }
+
+ mutex_enter(&unitp->hpc3130_mutex);
+
+ ste = &unitp->hpc3130_slot_table[port];
+
+ D2CMN_ERR((CE_NOTE, "ioctl: port = %d instance = %d",
+ port, instance));
+
+ switch (cmd) {
+ case HPC3130_GET_STATUS:
+ err = hpc3130_get(arg, HPC3130_HP_STATUS_REG(port), unitp,
+ mode);
+ break;
+
+ case HPC3130_GET_CONTROL:
+ err = hpc3130_get(arg, HPC3130_HP_CONTROL_REG(port), unitp,
+ mode);
+ break;
+
+ case HPC3130_SET_CONTROL:
+ if (control_slot_control == HPC3130_SLOT_CONTROL_DISABLE) {
+ cmn_err(CE_WARN, "Cannot change control register.");
+ err = EINVAL;
+ break;
+ }
+ err = hpc3130_set(arg, HPC3130_HP_CONTROL_REG(port), unitp,
+ mode);
+ break;
+
+ case HPC3130_GET_EVENT_STATUS:
+ err = hpc3130_get(arg, HPC3130_INTERRUPT_STATUS_REG(port),
+ unitp, mode);
+ break;
+
+ case HPC3130_SET_EVENT_STATUS:
+ err = hpc3130_set(arg, HPC3130_INTERRUPT_STATUS_REG(port),
+ unitp, mode);
+ break;
+
+ case HPC3130_GET_GENERAL_CONFIG:
+ err = hpc3130_get(arg, HPC3130_GENERAL_CONFIG_REG(port),
+ unitp, mode);
+ break;
+
+ case HPC3130_SET_GENERAL_CONFIG:
+ err = hpc3130_set(arg, HPC3130_GENERAL_CONFIG_REG(port),
+ unitp, mode);
+ break;
+
+ case HPC3130_GET_INDICATOR_CONTROL:
+ err = hpc3130_get(arg, HPC3130_ATTENTION_INDICATOR(port),
+ unitp, mode);
+ break;
+
+ case HPC3130_SET_INDICATOR_CONTROL:
+ err = hpc3130_set(arg, HPC3130_ATTENTION_INDICATOR(port),
+ unitp, mode);
+ break;
+
+ case HPC3130_GET_EVENT_ENABLE:
+ err = hpc3130_get(arg, HPC3130_INTERRUPT_ENABLE_REG(port),
+ unitp, mode);
+ break;
+
+ case HPC3130_SET_EVENT_ENABLE:
+ err = hpc3130_set(arg, HPC3130_INTERRUPT_ENABLE_REG(port),
+ unitp, mode);
+ break;
+
+ case HPC3130_ENABLE_SLOT_CONTROL:
+ control_slot_control = HPC3130_SLOT_CONTROL_ENABLE;
+ D2CMN_ERR((CE_NOTE, "Set the control_slot_control variable to"
+ "HPC3130_SLOT_CONTROL_ENABLE"));
+ break;
+
+ case HPC3130_DISABLE_SLOT_CONTROL:
+ control_slot_control = HPC3130_SLOT_CONTROL_DISABLE;
+ D2CMN_ERR((CE_NOTE, "Set the control_slot_control variable to"
+ "HPC3130_SLOT_CONTROL_DISABLE"));
+ break;
+
+ case I2C_GET_REG:
+ if (arg == NULL) {
+ D2CMN_ERR((CE_WARN, "ioctl: arg passed in to "
+ "ioctl = NULL"));
+ err = EINVAL;
+ break;
+ }
+ if (ddi_copyin((caddr_t)arg, (caddr_t)&ioctl_reg,
+ sizeof (i2c_reg_t), mode) != DDI_SUCCESS) {
+ D2CMN_ERR((CE_WARN, "Failed in I2C_GET_REG "
+ "ddi_copyin routine"));
+ err = EFAULT;
+ break;
+ }
+ (void) i2c_transfer_alloc(unitp->hpc3130_hdl, &i2c_tran_pointer,
+ 1, 1, I2C_SLEEP);
+ if (i2c_tran_pointer == NULL) {
+ D2CMN_ERR((CE_WARN, "Failed in I2C_GET_REG "
+ "i2c_tran_pointer not allocated"));
+ err = ENOMEM;
+ break;
+ }
+
+ i2c_tran_pointer->i2c_flags = I2C_WR_RD;
+ i2c_tran_pointer->i2c_wbuf[0] = ioctl_reg.reg_num;
+
+ err = i2c_transfer(unitp->hpc3130_hdl, i2c_tran_pointer);
+ if (err) {
+ D2CMN_ERR((CE_WARN, "Failed in I2C_GET_REG "
+ "i2c_transfer routine"));
+ i2c_transfer_free(unitp->hpc3130_hdl, i2c_tran_pointer);
+ break;
+ }
+ ioctl_reg.reg_value = i2c_tran_pointer->i2c_rbuf[0];
+ if (ddi_copyout((caddr_t)&ioctl_reg, (caddr_t)arg,
+ sizeof (i2c_reg_t), mode) != DDI_SUCCESS) {
+ D2CMN_ERR((CE_WARN, "Failed in I2C_GET_REG "
+ "ddi_copyout routine"));
+ err = EFAULT;
+ }
+
+ i2c_transfer_free(unitp->hpc3130_hdl, i2c_tran_pointer);
+ break;
+
+ case I2C_SET_REG:
+ if (arg == NULL) {
+ D2CMN_ERR((CE_WARN, "ioctl: arg passed in to "
+ "ioctl = NULL"));
+ err = EINVAL;
+ break;
+ }
+ if (ddi_copyin((caddr_t)arg, (caddr_t)&ioctl_reg,
+ sizeof (i2c_reg_t), mode) != DDI_SUCCESS) {
+ D2CMN_ERR((CE_WARN, "Failed in I2C_SET_REG "
+ "ddi_copyin routine"));
+ err = EFAULT;
+ break;
+ }
+ (void) i2c_transfer_alloc(unitp->hpc3130_hdl, &i2c_tran_pointer,
+ 2, 0, I2C_SLEEP);
+ if (i2c_tran_pointer == NULL) {
+ D2CMN_ERR((CE_WARN, "Failed in I2C_GET_REG "
+ "i2c_tran_pointer not allocated"));
+ err = ENOMEM;
+ break;
+ }
+
+ i2c_tran_pointer->i2c_flags = I2C_WR;
+ i2c_tran_pointer->i2c_wbuf[0] = ioctl_reg.reg_num;
+ i2c_tran_pointer->i2c_wbuf[1] = (uchar_t)ioctl_reg.reg_value;
+
+ err = i2c_transfer(unitp->hpc3130_hdl, i2c_tran_pointer);
+ if (err) {
+ D2CMN_ERR((CE_WARN, "Failed in I2C_SET_REG "
+ "i2c_transfer routine"));
+ i2c_transfer_free(unitp->hpc3130_hdl, i2c_tran_pointer);
+ break;
+ }
+
+ i2c_transfer_free(unitp->hpc3130_hdl, i2c_tran_pointer);
+ break;
+
+ case HPC3130_GET_EVENT: {
+ struct hpc3130_event ev;
+
+ bzero(&ev, sizeof (struct hpc3130_event));
+
+ if (unitp->slots_are == HPC3130_SLOT_TYPE_SBD) {
+ DAK_GET_SBD_APID(ev.name, sizeof (ev.name), port);
+ } else {
+ snprintf(ev.name, HPC3130_NAME_MAX,
+ "/devices%s:", ste->nexus);
+ ASSERT(strlen(ev.name) < HPC3130_NAME_MAX - 1);
+ DAK_GET_PCI_APID(ev.name + strlen(ev.name),
+ HPC3130_NAME_MAX - strlen(ev.name),
+ hpc3130_lookup_slot(ste->nexus,
+ ste->hpc3130_slot_info.pci_dev_num));
+ }
+
+ if (unitp->events[port] & HPC3130_IEVENT_OCCUPANCY) {
+ unitp->events[port] &= ~HPC3130_IEVENT_OCCUPANCY;
+ ev.id = (unitp->present[port] == B_FALSE ?
+ HPC3130_EVENT_REMOVAL :
+ HPC3130_EVENT_INSERTION);
+ } else if (unitp->events[port] & HPC3130_IEVENT_POWER) {
+ unitp->events[port] &= ~HPC3130_IEVENT_POWER;
+ ev.id = (unitp->power[port] == B_TRUE ?
+ HPC3130_EVENT_POWERON :
+ HPC3130_EVENT_POWEROFF);
+ } else if (unitp->events[port] & HPC3130_IEVENT_BUTTON) {
+ unitp->events[port] &= ~HPC3130_IEVENT_BUTTON;
+ ev.id = HPC3130_EVENT_BUTTON;
+ } else if (unitp->events[port] & HPC3130_IEVENT_FAULT) {
+ unitp->events[port] &= ~HPC3130_IEVENT_FAULT;
+ ev.id = (unitp->fault_led[port] == HPC3130_ATTN_ON ?
+ HPC3130_LED_FAULT_ON :
+ HPC3130_LED_FAULT_OFF);
+ } else if (unitp->events[port] & HPC3130_IEVENT_OK2REM) {
+ unitp->events[port] &= ~HPC3130_IEVENT_OK2REM;
+ ev.id = (unitp->ok2rem_led[port] == HPC3130_ATTN_ON ?
+ HPC3130_LED_REMOVABLE_ON :
+ HPC3130_LED_REMOVABLE_OFF);
+ }
+
+ D1CMN_ERR((CE_NOTE,
+ "sending EVENT: ap_id=%s, event=%d", ev.name, ev.id));
+
+ if (ddi_copyout((caddr_t)&ev, (caddr_t)arg,
+ sizeof (struct hpc3130_event), mode) != DDI_SUCCESS) {
+ D1CMN_ERR((CE_WARN, "Failed in hpc3130_ioctl"
+ " ddi_copyout routine"));
+ err = EFAULT;
+ }
+ break;
+ }
+ case HPC3130_CONF_DR: {
+ uint8_t offset;
+ int dr_conf;
+
+ if (ddi_copyin((caddr_t)arg, (caddr_t)&dr_conf,
+ sizeof (int), mode) != DDI_SUCCESS) {
+ D2CMN_ERR((CE_WARN, "Failed in HPC3130_CONF_DR "
+ "ddi_copyin routine"))
+ err = EFAULT;
+ break;
+ }
+
+ offset = ste->callback_info.offset;
+
+ unitp->enabled[offset] =
+ (dr_conf == HPC3130_DR_DISABLE ? B_FALSE : B_TRUE);
+
+ break;
+ }
+ default:
+ D2CMN_ERR((CE_WARN, "Invalid IOCTL cmd: %x", cmd));
+ err = EINVAL;
+ }
+
+ mutex_exit(&unitp->hpc3130_mutex);
+ return (err);
+}
+
+static int
+hpc3130_poll(dev_t dev, short events, int anyyet, short
+ *reventsp, struct pollhead **phpp)
+{
+ _NOTE(ARGUNUSED(events))
+ hpc3130_unit_t *unitp;
+ int port = MINOR_TO_PORT(getminor(dev));
+ int instance = MINOR_TO_INST(getminor(dev));
+
+ if (!(port >= 0 && port < HPC3130_MAX_SLOT)) {
+ return (EINVAL);
+ }
+ unitp = (hpc3130_unit_t *)
+ ddi_get_soft_state(hpc3130soft_statep, instance);
+
+ mutex_enter(&unitp->hpc3130_mutex);
+ if (unitp->events[port]) {
+ *reventsp = POLLIN;
+ } else {
+ *reventsp = 0;
+ if (!anyyet)
+ *phpp = &unitp->pollhead[port];
+ }
+ mutex_exit(&unitp->hpc3130_mutex);
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+hpc3130_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ dev_t dev;
+ int instance;
+
+ if (infocmd == DDI_INFO_DEVT2INSTANCE) {
+ dev = (dev_t)arg;
+ instance = MINOR_TO_INST(getminor(dev));
+ *result = (void *)(uintptr_t)instance;
+ return (DDI_SUCCESS);
+ }
+ return (DDI_FAILURE);
+}
+
+static int
+hpc3130_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_ATTACH:
+ return (hpc3130_do_attach(dip));
+ case DDI_RESUME:
+ return (hpc3130_do_resume());
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+static int
+hpc3130_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_DETACH:
+ return (hpc3130_do_detach(dip));
+ case DDI_SUSPEND:
+ return (hpc3130_do_suspend());
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+static int
+hpc3130_do_attach(dev_info_t *dip)
+{
+ hpc3130_unit_t *hpc3130_p;
+ char *s;
+ char *nexus;
+ char *pcidev;
+ char *reg_offset;
+ int r, i, n, j;
+ char name[MAXNAMELEN];
+ minor_t minor_number;
+ int hpc3130_pil = HPC3130_PIL;
+ int instance = ddi_get_instance(dip);
+
+ /*
+ * Allocate the soft state structure for this instance.
+ */
+ r = ddi_soft_state_zalloc(hpc3130soft_statep, instance);
+ if (r != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+
+ hpc3130_p =
+ (hpc3130_unit_t *)ddi_get_soft_state(hpc3130soft_statep, instance);
+ ASSERT(hpc3130_p);
+
+ if (ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
+ "interrupt-priorities", (caddr_t)&hpc3130_pil,
+ sizeof (hpc3130_pil)) != DDI_PROP_SUCCESS) {
+ goto failout0;
+ }
+
+ if (ddi_intr_hilevel(dip, 0)) {
+ cmn_err(CE_WARN, "High level interrupt not supported");
+ goto failout0;
+ }
+
+ /*
+ * Get the "slot-table" property which defines the list of
+ * hot-pluggable slots for this controller along with the
+ * corresponding bus nexus node and device identification
+ * for each slot.
+ */
+ r = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "slot-table", (caddr_t)&hpc3130_p->hpc3130_slot_table_data,
+ &hpc3130_p->hpc3130_slot_table_size);
+
+ switch (r) {
+ case DDI_PROP_SUCCESS:
+ break;
+ case DDI_PROP_NOT_FOUND:
+ cmn_err(CE_WARN,
+ "couldn't find slot-table property");
+ return (DDI_FAILURE);
+ case DDI_PROP_UNDEFINED:
+ cmn_err(CE_WARN,
+ "slot-table undefined");
+ return (DDI_FAILURE);
+ case DDI_PROP_NO_MEMORY:
+ cmn_err(CE_WARN,
+ "can't allocate memory for slot-table");
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Determine the size of the slot table from the OBP property and
+ * allocate the slot table arrary..
+ */
+ for (i = 0, n = 0; i < hpc3130_p->hpc3130_slot_table_size; i++) {
+ if (hpc3130_p->hpc3130_slot_table_data[i] == 0) {
+ n++;
+ }
+ }
+
+ D1CMN_ERR((CE_NOTE, "hpc3130_attach(): slot table has %d entries", n));
+
+ /*
+ * There should be HPC3130_TABLE_COLUMNS elements per entry
+ */
+ if (n % HPC3130_TABLE_COLUMNS) {
+ cmn_err(CE_WARN, "bad format in slot-table");
+ goto failout1;
+ }
+
+ hpc3130_p->dip = dip;
+ hpc3130_p->hpc3130_slot_table_length = n / HPC3130_TABLE_COLUMNS;
+
+ if (ddi_get_iblock_cookie(dip, 0, &hpc3130_p->ic_trap_cookie) !=
+ DDI_SUCCESS) {
+ cmn_err(CE_WARN, "ddi_get_iblock_cookie FAILED");
+ goto failout1;
+ }
+
+ mutex_init(&hpc3130_p->hpc3130_mutex, NULL, MUTEX_DRIVER,
+ (void *)hpc3130_p->ic_trap_cookie);
+ /*
+ * Create enough space for each slot table entry
+ * based on how many entries in the property
+ */
+ hpc3130_p->hpc3130_slot_table = (hpc3130_slot_table_entry_t *)
+ kmem_zalloc(hpc3130_p->hpc3130_slot_table_length *
+ sizeof (hpc3130_slot_table_entry_t), KM_SLEEP);
+
+ /*
+ * Setup to talk to the i2c nexus
+ */
+ if (i2c_client_register(dip, &hpc3130_p->hpc3130_hdl) != I2C_SUCCESS) {
+ cmn_err(CE_WARN, "failed to register as i2c client");
+ goto failout2;
+ }
+
+ s = hpc3130_p->hpc3130_slot_table_data;
+ for (i = 0; i < hpc3130_p->hpc3130_slot_table_length; i++) {
+ hpc3130_slot_table_entry_t *ste;
+
+ /* Pick off pointer to nexus path */
+ nexus = s;
+ s = s + strlen(s) + 1;
+
+ /* Pick off pointer to 3130 register offset */
+ reg_offset = s;
+ s = s + strlen(s) + 1;
+
+ /* Pick off pointer to the device number */
+ pcidev = s;
+
+ s = s + strlen(s) + 1;
+
+ j = hpc3130_atoi(reg_offset);
+
+ if (j < 0 || j >= HPC3130_MAX_SLOT) {
+ cmn_err(CE_WARN,
+ "invalid register offset value");
+ goto failout3;
+ }
+
+ ste = &hpc3130_p->hpc3130_slot_table[j];
+
+ strcpy(ste->nexus, nexus);
+
+ if (strncmp(ste->nexus, "/pci", 4) == 0) {
+
+ ste->hpc3130_slot_info.pci_dev_num =
+ hpc3130_atoi(pcidev);
+
+ DAK_GET_PCI_APID(ste->hpc3130_slot_info.pci_slot_name,
+ PCI_SLOT_NAME_LEN,
+ hpc3130_lookup_slot(ste->nexus,
+ hpc3130_atoi(pcidev)));
+
+ ste->hpc3130_slot_info.slot_type = HPC_SLOT_TYPE_PCI;
+ ste->hpc3130_slot_info.slot_flags =
+ HPC_SLOT_CREATE_DEVLINK;
+ hpc3130_p->slots_are = HPC3130_SLOT_TYPE_PCI;
+
+ } else {
+
+ ste->hpc3130_slot_info.sbd_slot_num =
+ hpc3130_atoi(reg_offset);
+
+ ste->hpc3130_slot_info.slot_type = HPC_SLOT_TYPE_SBD;
+
+ hpc3130_p->slots_are = HPC3130_SLOT_TYPE_SBD;
+ }
+
+ hpc3130_p->present[j] = B_FALSE;
+ hpc3130_p->enabled[j] = B_TRUE;
+
+ /*
+ * The "callback_info" structure of the slot_table is what gets
+ * passed back in the callback routines. All that is needed
+ * at that point is the device handle and the register offset
+ * within it the chip it represents.
+ */
+ ste->callback_info.handle = (caddr_t)hpc3130_p->hpc3130_hdl;
+
+ ste->callback_info.offset = hpc3130_atoi(reg_offset);
+
+ ste->callback_info.statep = (caddr_t)hpc3130_p;
+ }
+
+ hpc3130_p->hpc3130_slot_ops = hpc_alloc_slot_ops(KM_SLEEP);
+ hpc3130_p->hpc3130_slot_ops->hpc_version = 0;
+
+ hpc3130_p->hpc3130_slot_ops->hpc_op_connect = hpc3130_slot_connect;
+ hpc3130_p->hpc3130_slot_ops->hpc_op_disconnect =
+ hpc3130_slot_disconnect;
+ hpc3130_p->hpc3130_slot_ops->hpc_op_insert = hpc3130_slot_insert;
+ hpc3130_p->hpc3130_slot_ops->hpc_op_remove = hpc3130_slot_remove;
+ hpc3130_p->hpc3130_slot_ops->hpc_op_control = hpc3130_slot_control;
+
+ cv_init(&hpc3130_p->hpc3130_cond, NULL, CV_DEFAULT, NULL);
+
+ if (hpc3130_init(dip, (hpc3130_p->slots_are == HPC3130_SLOT_TYPE_SBD) ?
+ cpu_sequence : pci_sequence) != DDI_SUCCESS) {
+ goto failout4;
+ }
+
+ if (ddi_add_intr(dip, 0, &hpc3130_p->ic_trap_cookie,
+ NULL, hpc3130_hard_intr,
+ (caddr_t)hpc3130_p) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "failed to add interrupt");
+ goto failout4;
+ }
+
+ /*
+ * Register with the "services" module
+ */
+ for (i = 0; i < hpc3130_p->hpc3130_slot_table_length; i++) {
+ hpc3130_slot_table_entry_t *ste =
+ &hpc3130_p->hpc3130_slot_table[i];
+ hpc3130_p->power[i] = B_TRUE;
+ if (ste->callback_info.handle != NULL) {
+ hpc_slot_register(dip, ste->nexus,
+ &ste->hpc3130_slot_info,
+ &ste->hpc3130_slot_handle,
+ hpc3130_p->hpc3130_slot_ops,
+ (caddr_t)&ste->callback_info, 0);
+ }
+ }
+
+ (void) snprintf(hpc3130_p->hpc3130_name,
+ sizeof (hpc3130_p->hpc3130_name),
+ "%s%d", ddi_node_name(dip), instance);
+
+ for (i = 0; i < HPC3130_MAX_SLOT; i++) {
+ (void) snprintf(name, MAXNAMELEN, "port_%d", i);
+ minor_number = INST_TO_MINOR(instance) |
+ PORT_TO_MINOR(I2C_PORT(i));
+ if (ddi_create_minor_node(dip, name, S_IFCHR, minor_number,
+ "ddi_i2c:controller", NULL) == DDI_FAILURE) {
+ D1CMN_ERR((CE_WARN, "ddi_create_minor_node failed "
+ "for %s", name));
+ ddi_remove_intr(dip, 0u,
+ hpc3130_p->ic_trap_cookie);
+ goto failout4;
+ }
+ }
+
+ return (DDI_SUCCESS);
+
+failout4:
+ hpc_free_slot_ops(hpc3130_p->hpc3130_slot_ops);
+failout3:
+ i2c_client_unregister(hpc3130_p->hpc3130_hdl);
+failout2:
+ mutex_destroy(&hpc3130_p->hpc3130_mutex);
+ kmem_free(hpc3130_p->hpc3130_slot_table,
+ hpc3130_p->hpc3130_slot_table_length *
+ sizeof (hpc3130_slot_table_entry_t));
+failout1:
+ kmem_free(hpc3130_p->hpc3130_slot_table_data,
+ hpc3130_p->hpc3130_slot_table_size);
+failout0:
+ ddi_soft_state_free(hpc3130soft_statep, instance);
+
+ return (DDI_FAILURE);
+}
+
+static int
+hpc3130_do_resume()
+{
+ return (DDI_SUCCESS);
+}
+
+static int
+hpc3130_do_suspend()
+{
+ return (DDI_SUCCESS);
+}
+
+static int
+hpc3130_do_detach(dev_info_t *dip)
+{
+ int i;
+ int instance = ddi_get_instance(dip);
+ hpc3130_unit_t *hpc3130_p;
+
+ hpc3130_p = (hpc3130_unit_t *)ddi_get_soft_state(hpc3130soft_statep,
+ instance);
+ if (hpc3130_p == NULL)
+ return (ENXIO);
+
+ i2c_client_unregister(hpc3130_p->hpc3130_hdl);
+
+ ddi_remove_intr(dip, 0u, hpc3130_p->ic_trap_cookie);
+
+ cv_destroy(&hpc3130_p->hpc3130_cond);
+
+ for (i = 0; i < hpc3130_p->hpc3130_slot_table_length; i++) {
+ hpc_slot_unregister(
+ &hpc3130_p->hpc3130_slot_table[i].hpc3130_slot_handle);
+ }
+
+ kmem_free(hpc3130_p->hpc3130_slot_table,
+ hpc3130_p->hpc3130_slot_table_length *
+ sizeof (hpc3130_slot_table_entry_t));
+
+ kmem_free(hpc3130_p->hpc3130_slot_table_data,
+ hpc3130_p->hpc3130_slot_table_size);
+
+ hpc_free_slot_ops(hpc3130_p->hpc3130_slot_ops);
+
+ mutex_destroy(&hpc3130_p->hpc3130_mutex);
+
+ ddi_soft_state_free(hpc3130soft_statep, instance);
+
+ return (DDI_SUCCESS);
+}
+
+int
+hpc3130_set_led(hpc3130_unit_t *unitp, int slot, int led, uint8_t value)
+{
+ i2c_client_hdl_t handle = unitp->hpc3130_hdl;
+ uint8_t old;
+ uint8_t new;
+
+ if (hpc3130_read(handle, HPC3130_ATTEN, slot, &old) != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+ new = (old & ~HPC3130_ATTN_MASK(led)) |
+ value << HPC3130_ATTN_SHIFT(led);
+
+ D1CMN_ERR((CE_NOTE, "setting led %d to %x", led, value));
+
+ if (hpc3130_write(handle, HPC3130_ATTEN, slot, new) != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+
+ if ((value == HPC3130_ATTN_OFF || value == HPC3130_ATTN_ON) &&
+ ((old & HPC3130_ATTN_MASK(led)) !=
+ (new & HPC3130_ATTN_MASK(led)))) {
+ /*
+ * We're turning a LED on or off (i.e., not blinking), and
+ * the value actually did change.
+ */
+ if (led == HPC3130_LED_OK2REM) {
+ unitp->events[slot] |= HPC3130_IEVENT_OK2REM;
+ unitp->ok2rem_led[slot] = value;
+ D1CMN_ERR((CE_NOTE,
+ "recording IEVENT_OK2REM slot=%d, val=%d",
+ slot, value));
+ } else {
+ unitp->events[slot] |= HPC3130_IEVENT_FAULT;
+ unitp->fault_led[slot] = value;
+ D1CMN_ERR((CE_NOTE,
+ "recording IEVENT_FAULT slot=%d, val=%d",
+ slot, value));
+ }
+ ASSERT(MUTEX_HELD(&unitp->hpc3130_mutex));
+ mutex_exit(&unitp->hpc3130_mutex);
+ pollwakeup(&unitp->pollhead[slot], POLLIN);
+ mutex_enter(&unitp->hpc3130_mutex);
+ }
+ return (DDI_SUCCESS);
+}
+
+int
+hpc3130_get_led(i2c_client_hdl_t handle, int slot,
+ int led, uint8_t *value)
+{
+ uint8_t temp;
+
+ if (hpc3130_read(handle, HPC3130_ATTEN, slot, &temp) != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+
+ *value = (temp & HPC3130_ATTN_MASK(led)) >> HPC3130_ATTN_SHIFT(led);
+ return (DDI_SUCCESS);
+}
+
+static int
+hpc3130_write(i2c_client_hdl_t handle, uint8_t offset,
+ uint8_t port, uint8_t data)
+{
+ ASSERT(port >= 0 && port < HPC3130_MAX_SLOT);
+ ASSERT(handle);
+
+ return (hpc3130_rw(handle,
+ HPC3130_REG(offset, port), B_TRUE, &data));
+}
+
+static int
+hpc3130_read(i2c_client_hdl_t handle, uint8_t offset,
+ uint8_t port, uint8_t *data)
+{
+ ASSERT(port >= 0 && port < HPC3130_MAX_SLOT);
+ ASSERT(handle);
+
+ return (hpc3130_rw(handle,
+ HPC3130_REG(offset, port), B_FALSE, data));
+}
+
+static int
+hpc3130_rw(i2c_client_hdl_t handle, uint8_t reg,
+ boolean_t write, uint8_t *data)
+{
+ i2c_transfer_t *i2c_tran_pointer;
+ int err;
+ int rlen;
+ int wlen;
+
+ if (write == B_TRUE) {
+ wlen = 2;
+ rlen = 0;
+ } else {
+ wlen = 1;
+ rlen = 1;
+ }
+
+ (void) i2c_transfer_alloc(handle,
+ &i2c_tran_pointer, wlen, rlen, I2C_SLEEP);
+
+ if (i2c_tran_pointer == NULL) {
+ D1CMN_ERR((CE_WARN, "Failed in hpc3130_rw: "
+ "no transfer structure 0x%x", reg));
+ return (DDI_FAILURE);
+ }
+ i2c_tran_pointer->i2c_wbuf[0] = reg;
+ if (write == B_TRUE) {
+ i2c_tran_pointer->i2c_flags = I2C_WR;
+ i2c_tran_pointer->i2c_wbuf[1] = *data;
+ } else {
+ i2c_tran_pointer->i2c_flags = I2C_WR_RD;
+ }
+
+ err = i2c_transfer(handle, i2c_tran_pointer);
+ if (err) {
+ D1CMN_ERR((CE_WARN, "Failed in hpc3130_rw: "
+ "no I2C data transfered 0x%x", reg));
+ (void) i2c_transfer_free(handle, i2c_tran_pointer);
+ return (DDI_FAILURE);
+ }
+
+ if (write == B_FALSE)
+ *data = i2c_tran_pointer->i2c_rbuf[0];
+
+ (void) i2c_transfer_free(handle, i2c_tran_pointer);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Put the hot plug controller(s) in proper mode for further
+ * operations.
+ */
+static int
+hpc3130_init(dev_info_t *dip,
+ struct tuple *init_sequence)
+{
+
+ int slot;
+ i2c_client_hdl_t handle;
+ hpc3130_unit_t *hpc3130_p;
+ int instance = ddi_get_instance(dip);
+ int error = DDI_FAILURE;
+ struct tuple *tp;
+
+ hpc3130_p =
+ (hpc3130_unit_t *)ddi_get_soft_state(hpc3130soft_statep,
+ instance);
+ ASSERT(hpc3130_p);
+
+ mutex_enter(&hpc3130_p->hpc3130_mutex);
+
+ handle = hpc3130_p->hpc3130_hdl;
+
+ for (slot = 0; slot < HPC3130_MAX_SLOT; slot++) {
+ tp = init_sequence;
+ while (tp->reg != HPC3130_NO_REGISTER) {
+ if (hpc3130_write(handle, tp->reg, slot,
+ tp->val) != DDI_SUCCESS) {
+ goto out;
+ }
+ tp++;
+ }
+ /*
+ * CPU slots need some special initialization
+ * attention.
+ */
+ if (hpc3130_p->slots_are == HPC3130_SLOT_TYPE_SBD) {
+ if (hpc3130_cpu_init(hpc3130_p, slot, handle)
+ != DDI_SUCCESS) {
+ goto out;
+ }
+ }
+ }
+ error = DDI_SUCCESS;
+out:
+ mutex_exit(&hpc3130_p->hpc3130_mutex);
+
+ return (error);
+}
+
+/*
+ * When the TI 3130 produces an interrupt,
+ * this routine is called to sort it out.
+ */
+static uint_t
+hpc3130_hard_intr(caddr_t arg)
+{
+ uint8_t interrupt;
+ uint8_t status;
+ uint8_t slot;
+ i2c_client_hdl_t handle;
+ hpc3130_slot_type_t slot_type;
+ uint_t rc = DDI_INTR_UNCLAIMED;
+
+ hpc3130_unit_t *hpc3130_p = (hpc3130_unit_t *)arg;
+ ASSERT(hpc3130_p);
+
+ mutex_enter(&hpc3130_p->hpc3130_mutex);
+
+ slot_type = hpc3130_p->slots_are;
+ handle = hpc3130_p->hpc3130_hdl;
+
+ for (slot = 0; slot < HPC3130_MAX_SLOT; slot++) {
+
+ /*
+ * Read the interrupt event register - see
+ * which event(s) took place.
+ */
+ if (hpc3130_read(handle, HPC3130_EVENT_STATUS, slot,
+ &interrupt)) {
+ continue;
+ }
+
+ if (interrupt == 0)
+ continue;
+
+ rc = DDI_INTR_CLAIMED;
+
+ if (hpc3130_debounce_status(handle,
+ slot, &status) != DDI_SUCCESS) {
+ continue;
+ }
+
+ if (interrupt & HPC3130_PWRGOOD) {
+ hpc3130_p->power[slot] = B_FALSE;
+ if (!(status & HPC3130_PWRGOOD)) {
+ hpc3130_p->power[slot] = B_TRUE;
+ }
+ cv_signal(&hpc3130_p->hpc3130_cond);
+ hpc3130_p->events[slot] |= HPC3130_IEVENT_POWER;
+ }
+
+ if (interrupt & HPC3130_DETECT0) {
+ if (slot_type == HPC3130_SLOT_TYPE_SBD) {
+ boolean_t present = !(status&HPC3130_DETECT0);
+
+ /* Turn ON/OFF OK-to-remove LED */
+ (void) hpc3130_set_led(hpc3130_p,
+ slot,
+ HPC3130_LED_OK2REM,
+ (present ? HPC3130_ATTN_ON :
+ HPC3130_ATTN_OFF));
+ if (!present) {
+ /* Clear the FAULT LED on removal */
+ (void) hpc3130_set_led(hpc3130_p,
+ slot,
+ HPC3130_LED_FAULT,
+ HPC3130_ATTN_OFF);
+ }
+
+ hpc3130_p->present[slot] = present;
+ hpc3130_p->events[slot] |=
+ HPC3130_IEVENT_OCCUPANCY;
+ } else {
+ ASSERT(slot_type == HPC3130_SLOT_TYPE_PCI);
+
+ if (!(status & HPC3130_DETECT0)) {
+ /*
+ * Event on the downward
+ * stroke of the button.
+ */
+ hpc3130_p->events[slot] |=
+ HPC3130_IEVENT_BUTTON;
+ }
+ }
+ }
+
+ if (interrupt & (HPC3130_PRSNT1 | HPC3130_PRSNT2)) {
+ if (slot_type == HPC3130_SLOT_TYPE_SBD) {
+ if (!(status & HPC3130_PRSNT1)) {
+ /*
+ * Event only on the downward
+ * stroke of the button.
+ */
+ hpc3130_p->events[slot] |=
+ HPC3130_IEVENT_BUTTON;
+ }
+ } else {
+ ASSERT(slot_type == HPC3130_SLOT_TYPE_PCI);
+ if ((status & (HPC3130_PRSNT1 |
+ HPC3130_PRSNT2)) ==
+ (HPC3130_PRSNT1 | HPC3130_PRSNT2)) {
+
+ hpc3130_p->present[slot] = B_FALSE;
+
+ /* Turn OFF Fault LED */
+ (void) hpc3130_set_led(hpc3130_p,
+ slot,
+ HPC3130_LED_FAULT,
+ HPC3130_ATTN_OFF);
+ /* Turn OFF OK-to-remove LED */
+ (void) hpc3130_set_led(hpc3130_p,
+ slot,
+ HPC3130_LED_OK2REM,
+ HPC3130_ATTN_OFF);
+ } else {
+
+ hpc3130_p->present[slot] = B_TRUE;
+
+ /* Turn ON OK-to-remove LED */
+ (void) hpc3130_set_led(hpc3130_p,
+ slot,
+ HPC3130_LED_OK2REM,
+ HPC3130_ATTN_ON);
+ }
+
+ hpc3130_p->events[slot] |=
+ HPC3130_IEVENT_OCCUPANCY;
+ }
+ }
+ if (hpc3130_p->events[slot] &&
+ (hpc3130_p->present[slot] == B_TRUE)) {
+ mutex_exit(&hpc3130_p->hpc3130_mutex);
+ pollwakeup(&hpc3130_p->pollhead[slot], POLLIN);
+ mutex_enter(&hpc3130_p->hpc3130_mutex);
+ }
+ (void) hpc3130_write(handle, HPC3130_EVENT_STATUS,
+ slot, interrupt);
+ }
+
+ mutex_exit(&hpc3130_p->hpc3130_mutex);
+
+ return (rc);
+}
+
+static int
+hpc3130_cpu_init(hpc3130_unit_t *hpc3130_p, int slot, i2c_client_hdl_t handle)
+{
+ uint8_t slot_status;
+ uint8_t control_reg;
+
+ int result = HPC_ERR_FAILED;
+
+ if (hpc3130_read(handle, HPC3130_STATUS, slot,
+ &slot_status)) {
+ goto out;
+ }
+
+ if (hpc3130_read(handle, HPC3130_CONTROL, slot,
+ &control_reg)) {
+ goto out;
+ }
+
+ /*
+ * For the CPU slots, the DETECT[0] pin on the HPC3130
+ * goes low when a CPU module is in the slot. Pulled
+ * high otherwise.
+ */
+ if (slot_status & HPC3130_DETECT0) {
+ D1CMN_ERR((CE_NOTE, "hpc3130_cpu_init(): "
+ "[0x%x]Power off....[%d]",
+ slot_status, slot));
+ control_reg = control_reg & ~HPC3130_SLTPWRCTL;
+ } else {
+ D1CMN_ERR((CE_NOTE, "hpc3130_cpu_init(): "
+ "[0x%x]Power LEFT on!!!....[%d]",
+ slot_status, slot));
+ hpc3130_p->present[slot] = B_TRUE;
+ control_reg = control_reg | HPC3130_SLTPWRCTL;
+
+ }
+
+ /*
+ * Set the control register accordingly
+ */
+ if (hpc3130_write(handle, HPC3130_CONTROL,
+ slot, control_reg) != DDI_SUCCESS) {
+ goto out;
+ }
+
+ result = DDI_SUCCESS;
+out:
+
+ return (result);
+}
+
+static int
+hpc3130_debounce_status(i2c_client_hdl_t handle,
+ int slot, uint8_t *status)
+{
+ int count, limit;
+ uint8_t old;
+
+ ASSERT(status);
+
+ /*
+ * Get HPC3130_DEBOUNCE_COUNT consecutive equal
+ * readings from the status register
+ */
+
+ count = 0; limit = 0; old = 0xff;
+ do {
+ if (hpc3130_read(handle, HPC3130_STATUS,
+ slot, status)) {
+ return (DDI_FAILURE);
+ }
+ if (old != *status) {
+ count = 0;
+ } else {
+ count += 1;
+ }
+
+ limit += 1;
+ old = *status;
+
+ } while (count < HPC3130_DEBOUNCE_COUNT &&
+ limit < HPC3130_DEBOUNCE_LIMIT);
+
+ if (limit == HPC3130_DEBOUNCE_LIMIT) {
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+static int
+hpc3130_slot_connect(caddr_t ops_arg, hpc_slot_t slot_hdl,
+ void *data, uint_t flags)
+{
+ _NOTE(ARGUNUSED(slot_hdl, data, flags))
+ uint8_t control;
+ uint8_t offset;
+ uint8_t config;
+ uint8_t status;
+ hpc3130_unit_t *hpc3130_p;
+ i2c_client_hdl_t handle;
+ int i;
+ int result = HPC_ERR_FAILED;
+ hpc3130_slot_type_t slot_type;
+ hpc3130_slot_table_entry_t *ste;
+ char phys_slot[MAXPATHLEN];
+ boolean_t needs_to_be_powered_off = B_FALSE;
+
+ hpc3130_callback_arg_t *info_p = (hpc3130_callback_arg_t *)ops_arg;
+
+ /*
+ * Callback parameter has specific device handle and offset
+ * information in it.
+ */
+
+ hpc3130_p = (hpc3130_unit_t *)info_p->statep;
+ ASSERT(hpc3130_p);
+
+ mutex_enter(&hpc3130_p->hpc3130_mutex);
+
+ handle = (i2c_client_hdl_t)info_p->handle;
+ offset = info_p->offset;
+
+ ste = &hpc3130_p->hpc3130_slot_table[offset];
+
+ if (hpc3130_p->slots_are == HPC3130_SLOT_TYPE_SBD) {
+ DAK_GET_SBD_APID(phys_slot, MAXPATHLEN, offset);
+ } else {
+ DAK_GET_PCI_APID(phys_slot, MAXPATHLEN,
+ hpc3130_lookup_slot(ste->nexus,
+ ste->hpc3130_slot_info.pci_dev_num));
+ }
+
+ ASSERT(ste->hpc3130_slot_handle != NULL);
+
+ slot_type = hpc3130_p->slots_are;
+
+ if (hpc3130_p->enabled[offset] == B_FALSE) {
+ cmn_err(CE_WARN, "hot-plug disabled on %s", phys_slot);
+ goto out;
+ }
+
+ /* Return (do nothing) if power already applied */
+ if (hpc3130_p->power[offset] == B_TRUE) {
+ D1CMN_ERR((CE_NOTE, "Slot power already on %s", phys_slot));
+ mutex_exit(&hpc3130_p->hpc3130_mutex);
+ return (HPC_SUCCESS);
+ }
+
+ if (hpc3130_read(handle, HPC3130_STATUS, offset,
+ &status)) {
+ goto out;
+ }
+
+ /* Read the slot control register to get current value */
+ if (hpc3130_read(handle, HPC3130_CONTROL, offset,
+ &control)) {
+ goto out;
+ }
+
+ if (slot_type == HPC3130_SLOT_TYPE_SBD) {
+
+ D1CMN_ERR((CE_NOTE, "CPU connect %d control=%x status=%x",
+ offset, control, status));
+
+ control = control | HPC3130_SLTPWRCTL;
+ if (hpc3130_write(handle, HPC3130_CONTROL, offset,
+ control) != DDI_SUCCESS) {
+ goto out;
+ }
+
+ } else {
+
+ D1CMN_ERR((CE_NOTE, "PCI connect %d", offset));
+
+ /*
+ * PCI needs special sequencing of the control signals.
+ */
+
+ if (hpc3130_read(handle, HPC3130_GCR, offset,
+ &config)) {
+ goto out;
+ }
+
+ /* Assert RST to comply with PCI spec. */
+ control &= ~HPC3130_SLOTRST;
+ if (hpc3130_write(handle, HPC3130_CONTROL, offset,
+ control) != DDI_SUCCESS) {
+ goto out;
+ }
+ drv_usecwait(HPC3130_ADEQUATE_PAUSE);
+
+ /* Send the power on signal and verify the result */
+ control = control | HPC3130_SLTPWRCTL;
+ if ((hpc3130_write(handle, HPC3130_CONTROL, offset,
+ control) != DDI_SUCCESS) ||
+ (hpc3130_verify_slot_power(hpc3130_p, handle, offset,
+ phys_slot, B_TRUE) == HPC_ERR_FAILED)) {
+ goto out;
+ }
+
+ /* The slot is now powered on. */
+
+ drv_usecwait(HPC3130_ADEQUATE_PAUSE);
+
+ /* Extinguish the "OK-to-remove" indicator */
+ (void) hpc3130_set_led(hpc3130_p, offset, HPC3130_LED_OK2REM,
+ HPC3130_ATTN_OFF);
+
+ /*
+ * Perform bus/card speed check functions.
+ */
+ if (hpc3130_read(handle, HPC3130_STATUS, offset, &status)) {
+ goto out;
+ }
+ if ((config & HPC3130_SYSM66STAT) &&
+ !(status & HPC3130_M66EN)) {
+ cmn_err(CE_WARN, "66Mhz bus can't accept "
+ "33Mhz card in %s", phys_slot);
+ needs_to_be_powered_off = B_TRUE;
+ goto out;
+ }
+ if (!(config & HPC3130_SYSM66STAT) &&
+ (status & HPC3130_M66EN)) {
+ cmn_err(CE_NOTE, "66Mhz capable card throttled "
+ "back to 33Mhz in %s", phys_slot);
+ }
+
+ /*
+ * Send the connect sequence (see struct connect_sequence)
+ */
+ for (i = 0; i < HPC3130_CONNECT_SEQ_COUNT; i++) {
+ if (connect_sequence[i].set_bit == B_TRUE) {
+ control |= connect_sequence[i].value;
+ } else {
+ control &= ~connect_sequence[i].value;
+ }
+ if (hpc3130_write(handle, HPC3130_CONTROL, offset,
+ control) != DDI_SUCCESS) {
+ goto out;
+ }
+ drv_usecwait(HPC3130_ADEQUATE_PAUSE);
+ }
+ }
+
+ hpc_slot_event_notify(ste->hpc3130_slot_handle,
+ HPC_EVENT_SLOT_POWER_ON, 0);
+
+ /* Flash the "fault" indicator */
+ (void) hpc3130_set_led(hpc3130_p, offset, HPC3130_LED_FAULT,
+ HPC3130_ATTN_SLO);
+
+ result = HPC_SUCCESS;
+
+out:
+ if (needs_to_be_powered_off == B_TRUE) {
+ /*
+ * We are in an error state where the slot is powered on, and
+ * it must be powered off.
+ */
+
+ /* Send the power off signal and verify the result */
+ control = control & ~HPC3130_SLTPWRCTL;
+ if ((hpc3130_write(handle, HPC3130_CONTROL, offset,
+ control) == DDI_SUCCESS) &&
+ (hpc3130_verify_slot_power(hpc3130_p, handle, offset,
+ phys_slot, B_FALSE) == HPC_SUCCESS)) {
+ /* Re-light "OK-to-remove" LED */
+ (void) hpc3130_set_led(hpc3130_p, offset,
+ HPC3130_LED_OK2REM, HPC3130_ATTN_ON);
+ }
+ }
+
+ mutex_exit(&hpc3130_p->hpc3130_mutex);
+
+ return (result);
+}
+
+
+static int
+hpc3130_slot_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl,
+ void *data, uint_t flags)
+{
+ _NOTE(ARGUNUSED(slot_hdl, data, flags))
+ uint8_t control;
+ uint8_t offset;
+ i2c_client_hdl_t handle;
+ hpc3130_unit_t *hpc3130_p;
+ int result = HPC_ERR_FAILED;
+ hpc3130_slot_type_t slot_type;
+ hpc3130_slot_table_entry_t *ste;
+ char phys_slot[MAXPATHLEN];
+
+ hpc3130_callback_arg_t *info_p = (hpc3130_callback_arg_t *)ops_arg;
+
+ /*
+ * Callback parameter has specific device handle and offset
+ * information in it.
+ */
+ hpc3130_p = (hpc3130_unit_t *)info_p->statep;
+ ASSERT(hpc3130_p);
+
+ mutex_enter(&hpc3130_p->hpc3130_mutex);
+
+ handle = (i2c_client_hdl_t)info_p->handle;
+ offset = info_p->offset;
+
+ ASSERT(handle == hpc3130_p->hpc3130_hdl);
+
+ ste = &hpc3130_p->hpc3130_slot_table[offset];
+
+ if (hpc3130_p->slots_are == HPC3130_SLOT_TYPE_SBD) {
+ DAK_GET_SBD_APID(phys_slot, MAXPATHLEN, offset);
+ } else {
+ DAK_GET_PCI_APID(phys_slot, MAXPATHLEN,
+ hpc3130_lookup_slot(ste->nexus,
+ ste->hpc3130_slot_info.pci_dev_num));
+ }
+
+ ASSERT(ste->hpc3130_slot_handle != NULL);
+
+ slot_type = hpc3130_p->slots_are;
+
+ /*
+ * Read the slot control register to get current value
+ */
+ if (hpc3130_read(handle, HPC3130_CONTROL, offset,
+ &control)) {
+ goto out;
+ }
+
+ if (slot_type == HPC3130_SLOT_TYPE_SBD) {
+
+ D1CMN_ERR((CE_NOTE, "CPU disconnect %d", offset));
+
+ control = control & ~HPC3130_SLTPWRCTL;
+ /*
+ * Write out the modified control register
+ */
+ if (hpc3130_write(handle, HPC3130_CONTROL, offset,
+ control) != DDI_SUCCESS) {
+ goto out;
+ }
+ } else {
+
+ D1CMN_ERR((CE_NOTE, "PCI disconnect %d", offset));
+
+ control &= ~HPC3130_SLOTRST;
+ if (hpc3130_write(handle, HPC3130_CONTROL, offset,
+ control) != DDI_SUCCESS) {
+ goto out;
+ }
+
+ control |= HPC3130_BUS_CTL;
+ if (hpc3130_write(handle, HPC3130_CONTROL, offset,
+ control) != DDI_SUCCESS) {
+ goto out;
+ }
+ }
+
+ D1CMN_ERR((CE_WARN, "disconnect present[%d]==%d",
+ offset, hpc3130_p->present[offset]));
+
+ if (hpc3130_verify_slot_power(hpc3130_p, handle, offset,
+ phys_slot, B_FALSE) == HPC_ERR_FAILED) {
+ goto out;
+ }
+
+ hpc_slot_event_notify(ste->hpc3130_slot_handle,
+ HPC_EVENT_SLOT_POWER_OFF, 0);
+
+ if (hpc3130_p->present[offset] == B_TRUE) {
+ /*
+ * Illuminate the "OK-to-remove" indicator
+ * if there is a card in the slot.
+ */
+
+ (void) hpc3130_set_led(hpc3130_p, offset, HPC3130_LED_OK2REM,
+ HPC3130_ATTN_ON);
+
+ /*
+ * Turn off the "fault" indicator
+ */
+ (void) hpc3130_set_led(hpc3130_p, offset, HPC3130_LED_FAULT,
+ HPC3130_ATTN_OFF);
+ } else {
+ /*
+ * If the slot is being powered off with
+ * no cards in there, its at "boot time",
+ * put the LEDs in a sane state
+ */
+ if (slot_type == HPC3130_SLOT_TYPE_PCI) {
+ (void) hpc3130_set_led(hpc3130_p, offset,
+ HPC3130_LED_FAULT, HPC3130_ATTN_OFF);
+ (void) hpc3130_set_led(hpc3130_p, offset,
+ HPC3130_LED_OK2REM, HPC3130_ATTN_OFF);
+ }
+ }
+
+ result = HPC_SUCCESS;
+out:
+ mutex_exit(&hpc3130_p->hpc3130_mutex);
+
+ return (result);
+}
+
+static int
+hpc3130_verify_slot_power(hpc3130_unit_t *hpc3130_p, i2c_client_hdl_t handle,
+ uint8_t offset, char *phys_slot, boolean_t slot_target_state)
+{
+ uint8_t tries = 0;
+ uint8_t status;
+ int result = HPC_SUCCESS;
+ clock_t tm, timeleft;
+ boolean_t slot_actual_state;
+ boolean_t failure = B_FALSE;
+ hpc3130_slot_table_entry_t *ste;
+
+ /* This function is called while holding the hpc3130 mutex. */
+
+ /*
+ * For slot_target_state and slot_actual_state:
+ * B_TRUE == the slot is powered on
+ * B_FALSE == the slot is powered off
+ */
+
+ ste = &hpc3130_p->hpc3130_slot_table[offset];
+ slot_actual_state = hpc3130_p->power[offset];
+
+ while ((slot_actual_state != slot_target_state) &&
+ (failure != B_TRUE)) {
+ tm = ddi_get_lbolt();
+ tm += drv_usectohz(300000);
+ timeleft = cv_timedwait(&hpc3130_p->hpc3130_cond,
+ &hpc3130_p->hpc3130_mutex, tm);
+ if (timeleft == -1) {
+ if (tries++ < HPC3130_POWER_TRIES) {
+ /*
+ * The interrupt was missed - explicitly
+ * check the status.
+ */
+ if (hpc3130_read(handle,
+ HPC3130_STATUS, offset, &status)) {
+ failure = B_TRUE;
+ continue;
+ }
+ if (status & HPC3130_PWRGOOD) {
+ slot_actual_state = B_FALSE;
+ } else {
+ slot_actual_state = B_TRUE;
+ }
+ hpc3130_p->power[offset] = slot_actual_state;
+ } else {
+ /* Too many tries. We failed. */
+ failure = B_TRUE;
+ }
+ }
+ }
+
+ if (failure == B_TRUE) {
+ result = HPC_ERR_FAILED;
+ if (slot_target_state == B_TRUE) {
+ cmn_err(CE_WARN,
+ "Could not power on slot %s", phys_slot);
+ } else {
+ cmn_err(CE_WARN,
+ "Could not power off slot %s", phys_slot);
+ }
+ (void) hpc3130_set_led(hpc3130_p, offset, HPC3130_LED_FAULT,
+ HPC3130_ATTN_ON);
+ hpc_slot_event_notify(ste->hpc3130_slot_handle,
+ HPC_EVENT_SLOT_NOT_HEALTHY, 0);
+ }
+
+ return (result);
+}
+
+static int
+hpc3130_slot_insert(caddr_t ops_arg, hpc_slot_t slot_hdl,
+ void *data, uint_t flags)
+{
+ _NOTE(ARGUNUSED(ops_arg, slot_hdl, data, flags))
+ return (HPC_ERR_NOTSUPPORTED);
+}
+
+static int
+hpc3130_slot_remove(caddr_t ops_arg, hpc_slot_t slot_hdl,
+ void *data, uint_t flags)
+{
+ _NOTE(ARGUNUSED(ops_arg, slot_hdl, data, flags))
+ return (HPC_ERR_NOTSUPPORTED);
+}
+
+static int
+hpc3130_slot_control(caddr_t ops_arg, hpc_slot_t slot_hdl,
+ int request, caddr_t arg)
+{
+ _NOTE(ARGUNUSED(slot_hdl))
+ i2c_client_hdl_t handle;
+ uint8_t offset;
+ uint8_t state;
+ hpc_led_info_t *led_info;
+ hpc3130_unit_t *hpc3130_p;
+ hpc3130_slot_type_t slot_type;
+
+ hpc3130_callback_arg_t *info_p = (hpc3130_callback_arg_t *)ops_arg;
+
+ /*
+ * Callback parameter has specific device handle and offset
+ * information in it.
+ */
+
+ hpc3130_p = (hpc3130_unit_t *)info_p->statep;
+ ASSERT(hpc3130_p);
+
+ mutex_enter(&hpc3130_p->hpc3130_mutex);
+
+ handle = (i2c_client_hdl_t)info_p->handle;
+ offset = info_p->offset;
+
+ ASSERT(handle == hpc3130_p->hpc3130_hdl);
+
+ slot_type = hpc3130_p->slots_are;
+
+ switch (request) {
+ case HPC_CTRL_GET_LED_STATE: {
+ int led;
+
+ led_info = (hpc_led_info_t *)arg;
+ if (led_info->led != HPC_FAULT_LED &&
+ led_info->led != HPC_ATTN_LED) {
+ D1CMN_ERR((CE_WARN,
+ "Only FAULT and ATTN leds allowed"));
+ mutex_exit(&hpc3130_p->hpc3130_mutex);
+ return (HPC_ERR_INVALID);
+ }
+
+ if (led_info->led == HPC_FAULT_LED)
+ led = HPC3130_LED_FAULT;
+ else
+ led = HPC3130_LED_OK2REM;
+
+ if (hpc3130_get_led(handle, offset, led, &state) !=
+ DDI_SUCCESS) {
+ mutex_exit(&hpc3130_p->hpc3130_mutex);
+ return (HPC_ERR_FAILED);
+ }
+
+ /* Make sure that no one broke the conversion macros */
+ ASSERT(state < sizeof (hpc3130_to_hpc_led_map));
+ ASSERT(state ==
+ HPC3130_FROM_HPC_LED(HPC3130_TO_HPC_LED(state)));
+
+ led_info->state = HPC3130_TO_HPC_LED(state);
+ }
+ break;
+ case HPC_CTRL_SET_LED_STATE: {
+ int led;
+
+ /*
+ * The HPC3130 support modifications to the Fault and
+ * Ok-to-remove LEDs.
+ */
+ led_info = (hpc_led_info_t *)arg;
+ if (led_info->led != HPC_FAULT_LED &&
+ led_info->led != HPC_ATTN_LED) {
+ D1CMN_ERR((CE_WARN,
+ "Only FAULT and ATTN leds allowed"));
+ mutex_exit(&hpc3130_p->hpc3130_mutex);
+ return (HPC_ERR_INVALID);
+ }
+
+ if (led_info->led == HPC_FAULT_LED)
+ led = HPC3130_LED_FAULT;
+ else
+ led = HPC3130_LED_OK2REM;
+
+ state = led_info->state;
+ if (state >= sizeof (hpc3130_from_hpc_led_map) ||
+ (state != HPC3130_TO_HPC_LED(
+ HPC3130_FROM_HPC_LED(state)))) {
+ D1CMN_ERR((CE_WARN,
+ "Improper LED value: %d %d", state,
+ HPC3130_TO_HPC_LED(
+ HPC3130_FROM_HPC_LED(state))));
+ mutex_exit(&hpc3130_p->hpc3130_mutex);
+ return (HPC_ERR_INVALID);
+ }
+
+ (void) hpc3130_set_led(hpc3130_p, offset, led,
+ HPC3130_FROM_HPC_LED(state));
+ }
+ break;
+ case HPC_CTRL_GET_SLOT_STATE: {
+ if (hpc3130_p->power[offset] == B_FALSE) {
+ if (hpc3130_p->present[offset] == B_FALSE) {
+ *(ap_rstate_t *)arg =
+ AP_RSTATE_EMPTY;
+ } else {
+ *(ap_rstate_t *)arg =
+ AP_RSTATE_DISCONNECTED;
+ }
+ } else {
+ *(ap_rstate_t *)arg =
+ AP_RSTATE_CONNECTED;
+ }
+ }
+ break;
+ case HPC_CTRL_GET_BOARD_TYPE: {
+ *(hpc_board_type_t *)arg =
+ (slot_type == HPC3130_SLOT_TYPE_SBD ?
+ HPC_BOARD_UNKNOWN : HPC_BOARD_PCI_HOTPLUG);
+ }
+ break;
+ case HPC_CTRL_DEV_CONFIG_START:
+ case HPC_CTRL_DEV_UNCONFIG_START:
+ hpc3130_set_led(hpc3130_p, offset, HPC3130_LED_FAULT,
+ HPC3130_ATTN_SLO);
+ break;
+ case HPC_CTRL_DEV_CONFIG_FAILURE:
+ hpc3130_set_led(hpc3130_p, offset, HPC3130_LED_FAULT,
+ HPC3130_ATTN_ON);
+ break;
+ case HPC_CTRL_DEV_CONFIGURED:
+ hpc3130_set_led(hpc3130_p, offset, HPC3130_LED_FAULT,
+ HPC3130_ATTN_OFF);
+ hpc3130_p->present[offset] = B_TRUE;
+ break;
+ case HPC_CTRL_DEV_UNCONFIGURED:
+ if (hpc3130_p->power[offset] == B_TRUE) {
+ hpc3130_set_led(hpc3130_p, offset,
+ HPC3130_LED_FAULT, HPC3130_ATTN_SLO);
+ } else {
+ hpc3130_set_led(hpc3130_p, offset,
+ HPC3130_LED_FAULT, HPC3130_ATTN_OFF);
+ }
+ break;
+ case HPC_CTRL_DISABLE_SLOT: {
+ hpc3130_p->enabled[offset] = B_FALSE;
+ }
+ break;
+ case HPC_CTRL_ENABLE_SLOT: {
+ hpc3130_p->enabled[offset] = B_TRUE;
+ }
+ break;
+ default:
+ mutex_exit(&hpc3130_p->hpc3130_mutex);
+ return (HPC_ERR_FAILED);
+ }
+ mutex_exit(&hpc3130_p->hpc3130_mutex);
+ return (HPC_SUCCESS);
+}
+
+int
+hpc3130_lookup_slot(char *nexus, int pcidev)
+{
+ int i = 0;
+
+ while ((slot_translate[i].pcidev != pcidev ||
+ strcmp(nexus, slot_translate[i].nexus) != 0) &&
+ i < HPC3130_LOOKUP_SLOTS) i++;
+ ASSERT(i != HPC3130_LOOKUP_SLOTS);
+ return (i);
+}
+
+/*
+ * 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
+hpc3130_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);
+}