root/usr/src/uts/common/io/pciex/hotplug/pcishpc.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 * Copyright 2019 Joyent, Inc.
 */

/*
 * This file contains PCI HotPlug functionality that is compatible with the
 * PCI SHPC specification 1.x.
 *
 * NOTE: This file is compiled and delivered through misc/pcie module.
 */

#include <sys/note.h>
#include <sys/conf.h>
#include <sys/kmem.h>
#include <sys/kstat.h>
#include <sys/debug.h>
#include <sys/vtrace.h>
#include <sys/autoconf.h>
#include <sys/varargs.h>
#include <sys/hwconf.h>
#include <sys/ddi_impldefs.h>
#include <sys/callb.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/sysevent/dr.h>
#include <sys/ndi_impldefs.h>
#include <sys/pci_impl.h>
#include <sys/hotplug/pci/pcie_hp.h>
#include <sys/hotplug/pci/pcishpc.h>

typedef struct pcishpc_prop {
        char    *prop_name;
        char    *prop_value;
} pcishpc_prop_t;

static pcishpc_prop_t   pcishpc_props[] = {
        { PCIEHPC_PROP_LED_FAULT,       PCIEHPC_PROP_VALUE_LED },
        { PCIEHPC_PROP_LED_POWER,       PCIEHPC_PROP_VALUE_LED },
        { PCIEHPC_PROP_LED_ATTN,        PCIEHPC_PROP_VALUE_LED },
        { PCIEHPC_PROP_LED_ACTIVE,      PCIEHPC_PROP_VALUE_LED },
        { PCIEHPC_PROP_CARD_TYPE,       PCIEHPC_PROP_VALUE_TYPE },
        { PCIEHPC_PROP_BOARD_TYPE,      PCIEHPC_PROP_VALUE_TYPE },
        { PCIEHPC_PROP_SLOT_CONDITION,  PCIEHPC_PROP_VALUE_TYPE }
};

/* reset delay to 1 sec. */
static int pcishpc_reset_delay = 1000000;

/* Local function prototype */
static pcie_hp_ctrl_t *pcishpc_create_controller(dev_info_t *dip);
static int      pcishpc_setup_controller(pcie_hp_ctrl_t *ctrl_p);
static int      pcishpc_destroy_controller(dev_info_t *dip);
static pcie_hp_slot_t   *pcishpc_create_slot(pcie_hp_ctrl_t *ctrl_p);
static int      pcishpc_register_slot(pcie_hp_ctrl_t *ctrl_p, int slot);
static int      pcishpc_destroy_slots(pcie_hp_ctrl_t *ctrl_p);
static int      pcishpc_slot_get_property(pcie_hp_slot_t *slot_p,
                    ddi_hp_property_t *arg, ddi_hp_property_t *rval);
static int      pcishpc_slot_set_property(pcie_hp_slot_t *slot_p,
                    ddi_hp_property_t *arg, ddi_hp_property_t *rval);
static int      pcishpc_issue_command(pcie_hp_ctrl_t *ctrl_p,
                    uint32_t cmd_code);
static int      pcishpc_wait_busy(pcie_hp_ctrl_t *ctrl_p);
static void     pcishpc_attn_btn_handler(pcie_hp_slot_t *slot_p);
static void     pcishpc_get_slot_state(pcie_hp_slot_t *slot_p);
static int      pcishpc_set_slot_state(pcie_hp_slot_t *slot_p,
                    ddi_hp_cn_state_t new_slot_state);
static void     pcishpc_set_slot_name(pcie_hp_ctrl_t *ctrl_p, int slot);
static int      pcishpc_set_bus_speed(pcie_hp_slot_t *slot_p);
static int      pcishpc_setled(pcie_hp_slot_t *slot_p, pcie_hp_led_t led,
                    pcie_hp_led_state_t state);
static int      pcishpc_led_shpc_to_hpc(int state);
static int      pcishpc_led_hpc_to_shpc(int state);
static int      pcishpc_slot_shpc_to_hpc(int shpc_state);
static int      pcishpc_slot_hpc_to_shpc(int state);
static char     *pcishpc_slot_textslotstate(ddi_hp_cn_state_t state);
static char     *pcishpc_slot_textledstate(pcie_hp_led_state_t state);

static uint32_t pcishpc_read_reg(pcie_hp_ctrl_t *ctrl_p, int reg);
static void     pcishpc_write_reg(pcie_hp_ctrl_t *ctrl_p, int reg,
                    uint32_t data);

static int      pcishpc_upgrade_slot_state(pcie_hp_slot_t *slot_p,
                    ddi_hp_cn_state_t target_state);
static int      pcishpc_downgrade_slot_state(pcie_hp_slot_t *slot_p,
                    ddi_hp_cn_state_t target_state);
static int      pcishpc_change_slot_state(pcie_hp_slot_t *slot_p,
                    ddi_hp_cn_state_t target_state);

static int      pcishpc_slot_poweron(pcie_hp_slot_t *slot_p,
                    ddi_hp_cn_state_t *result_state);
static int      pcishpc_slot_poweroff(pcie_hp_slot_t *slot_p,
                    ddi_hp_cn_state_t *result_state);
static int      pcishpc_slot_probe(pcie_hp_slot_t *slot_p);
static int      pcishpc_slot_unprobe(pcie_hp_slot_t *slot_p);
#ifdef  DEBUG
static void     pcishpc_dump_regs(pcie_hp_ctrl_t *ctrl_p);
#endif  /* DEBUG */


/*
 * Global functions (called by other drivers/modules)
 */

/*
 * pcishpc_init()
 *
 * Install and configure an SHPC controller and register the HotPlug slots
 * with the Solaris HotPlug framework. This function is usually called by
 * a PCI bridge Nexus driver that has a built in SHPC controller.
 */
int
pcishpc_init(dev_info_t *dip)
{
        pcie_bus_t      *bus_p = PCIE_DIP2BUS(dip);
        pcie_hp_ctrl_t  *ctrl_p;
        int             i;

        PCIE_DBG("pcishpc_init() called from %s#%d\n",
            ddi_driver_name(dip), ddi_get_instance(dip));

        if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) != NULL) {
                PCIE_DBG("pcishpc_init() shpc instance already "
                    "initialized!\n");
                return (DDI_SUCCESS);
        }

        /* Initialize soft state structure for the SHPC instance. */
        ctrl_p = pcishpc_create_controller(dip);

        if (ctrl_p == NULL) {
                PCIE_DBG("pcishpc_init() failed to create shpc softstate\n");
                return (DDI_FAILURE);
        }

        if (pcishpc_setup_controller(ctrl_p) != DDI_SUCCESS) {
                PCIE_DBG("pcishpc_init() failed to setup controller\n");
                goto cleanup;
        }

        /*
         * Setup resource maps for this bus node.
         */
        (void) pci_resource_setup(dip);

#ifdef  DEBUG
        PCIE_DBG("%s%d: P2P bridge register dump:\n",
            ddi_driver_name(dip), ddi_get_instance(dip));

        for (i = 0; i < 0x100; i += 4) {
                PCIE_DBG("SHPC Cfg reg 0x%02x: %08x\n", i,
                    pci_config_get32(bus_p->bus_cfg_hdl, i));
        }
#endif  /* DEBUG */

        /* Setup each HotPlug slot on this SHPC controller. */
        for (i = 0; i < ctrl_p->hc_num_slots_impl; i++) {
                if (pcishpc_register_slot(ctrl_p, i) != DDI_SUCCESS) {
                        PCIE_DBG("pcishpc_init() failed to register "
                            "slot %d\n", i);
                        goto cleanup1;
                }
                if (pcie_create_minor_node(ctrl_p, i) != DDI_SUCCESS) {
                        PCIE_DBG("pcishpc_init() failed to create "
                            "minor node for slot %d\n", i);
                        goto cleanup1;
                }
        }

#ifdef  DEBUG
        /* Dump out the SHPC registers. */
        pcishpc_dump_regs(ctrl_p);
#endif  /* DEBUG */

        PCIE_DBG("pcishpc_init() success(dip=%p)\n", dip);
        return (DDI_SUCCESS);

cleanup1:
        for (i = 0; i < ctrl_p->hc_num_slots_impl; i++) {
                if (ctrl_p->hc_slots[i] == NULL)
                        continue;

                pcie_remove_minor_node(ctrl_p, i);
        }
        (void) pci_resource_destroy(dip);
cleanup:
        (void) pcishpc_destroy_controller(dip);
        return (DDI_FAILURE);
}

/*
 * pcishpc_uninit()
 * Unload the HogPlug controller driver and deallocate all resources.
 */
int
pcishpc_uninit(dev_info_t *dip)
{
        pcie_hp_ctrl_t *ctrl_p;
        int i;

        PCIE_DBG("pcishpc_uninit() called(dip=%p)\n", dip);

        ctrl_p = PCIE_GET_HP_CTRL(dip);

        if (!ctrl_p) {
                PCIE_DBG("pcishpc_uninit() Unable to find softstate\n");
                return (DDI_FAILURE);
        }

        for (i = 0; i < PCIE_HP_MAX_SLOTS; i++) {
                if (ctrl_p->hc_slots[i] == NULL)
                        continue;

                pcie_remove_minor_node(ctrl_p, i);
        }

        ctrl_p->hc_flags = 0;

        /*
         * Destroy resource maps for this bus node.
         */
        (void) pci_resource_destroy(dip);

        (void) pcishpc_destroy_controller(dip);

        PCIE_DBG("pcishpc_uninit() success(dip=%p)\n", dip);

        return (DDI_SUCCESS);
}

/*
 * pcishpc_intr()
 *
 * This is the SHPC controller interrupt handler.
 */
int
pcishpc_intr(dev_info_t *dip)
{
        pcie_hp_ctrl_t  *ctrl_p;
        uint32_t        irq_locator, irq_serr_locator, reg;
        int             slot;

        PCIE_DBG("pcishpc_intr() called\n");

        /* get the soft state structure for this dip */
        if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
                return (DDI_INTR_UNCLAIMED);

        mutex_enter(&ctrl_p->hc_mutex);

        if (!(ctrl_p->hc_flags & PCIE_HP_INITIALIZED_FLAG)) {
                PCIE_DBG("pcishpc_intr() unclaimed\n");
                mutex_exit(&ctrl_p->hc_mutex);
                return (DDI_INTR_UNCLAIMED);
        }

        PCIE_DBG("pcishpc_intr() interrupt received\n");

        reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG);

        if (reg & PCI_HP_SERR_INT_CMD_COMPLETE_IRQ) {
                PCIE_DBG("pcishpc_intr() "
                    "PCI_HP_SERR_INT_CMD_COMPLETE_IRQ detected\n");
                ctrl_p->hc_cmd_pending = B_FALSE;
                cv_signal(&ctrl_p->hc_cmd_comp_cv);
        }

        if (reg & PCI_HP_SERR_INT_ARBITER_IRQ) {
                PCIE_DBG("pcishpc_intr() PCI_HP_SERR_INT_ARBITER_IRQ "
                    "detected\n");
                ctrl_p->hc_arbiter_timeout = B_TRUE;
        }

        /* Write back the SERR INT register to acknowledge the IRQs. */
        pcishpc_write_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG, reg);

        irq_locator = pcishpc_read_reg(ctrl_p, PCI_HP_IRQ_LOCATOR_REG);
        irq_serr_locator = pcishpc_read_reg(ctrl_p, PCI_HP_SERR_LOCATOR_REG);

        /* Check for slot events that might have occured. */
        for (slot = 0; slot < ctrl_p->hc_num_slots_impl; slot++) {
                if ((irq_locator & (PCI_HP_IRQ_SLOT_N_PENDING<<slot)) ||
                    (irq_serr_locator &
                    (PCI_HP_IRQ_SERR_SLOT_N_PENDING<<slot))) {
                        PCIE_DBG("pcishpc_intr() slot %d and "
                            "pending IRQ\n", slot+1);

                        reg = pcishpc_read_reg(ctrl_p,
                            PCI_HP_LOGICAL_SLOT_REGS+slot);

                        if (reg & PCI_HP_SLOT_PRESENCE_DETECTED)
                                PCIE_DBG("slot %d: "
                                    "PCI_HP_SLOT_PRESENCE_DETECTED\n",
                                    slot+1);

                        if (reg & PCI_HP_SLOT_ISO_PWR_DETECTED)
                                PCIE_DBG("slot %d: "
                                    "PCI_HP_SLOT_ISO_PWR_DETECTED\n",
                                    slot+1);

                        if (reg & PCI_HP_SLOT_ATTN_DETECTED) {
                                PCIE_DBG("slot %d: "
                                    "PCI_HP_SLOT_ATTN_DETECTED\n", slot+1);

                                /*
                                 * if ATTN button event is still pending
                                 * then cancel it
                                 */
                                if (ctrl_p->hc_slots[slot]->
                                    hs_attn_btn_pending == B_TRUE)
                                        ctrl_p->hc_slots[slot]->
                                            hs_attn_btn_pending = B_FALSE;

                                /* wake up the ATTN event handler */
                                cv_signal(&ctrl_p->hc_slots[slot]->
                                    hs_attn_btn_cv);
                        }

                        if (reg & PCI_HP_SLOT_MRL_DETECTED)
                                PCIE_DBG("slot %d: "
                                    "PCI_HP_SLOT_MRL_DETECTED\n", slot+1);

                        if (reg & PCI_HP_SLOT_POWER_DETECTED)
                                PCIE_DBG("slot %d: "
                                    "PCI_HP_SLOT_POWER_DETECTED\n", slot+1);

                        /* Acknoledge any slot interrupts */
                        pcishpc_write_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot,
                            reg);
                }
        }

        mutex_exit(&ctrl_p->hc_mutex);

        PCIE_DBG("pcishpc_intr() claimed\n");

        return (DDI_INTR_CLAIMED);
}

int
pcishpc_slot_get_property(pcie_hp_slot_t *slot_p, ddi_hp_property_t *arg,
    ddi_hp_property_t *rval)
{
        ddi_hp_property_t request, result;
#ifdef _SYSCALL32_IMPL
        ddi_hp_property32_t request32, result32;
#endif
        pcie_hp_ctrl_t  *ctrl_p = slot_p->hs_ctrl;
        nvlist_t        *prop_list;
        nvlist_t        *prop_rlist; /* nvlist for return values */
        nvpair_t        *prop_pair;
        char            *name, *value;
        int             ret = DDI_SUCCESS;
        int             i, n;
        boolean_t       get_all_prop = B_FALSE;

        if (get_udatamodel() == DATAMODEL_NATIVE) {
                if (copyin(arg, &request, sizeof (ddi_hp_property_t)) ||
                    copyin(rval, &result, sizeof (ddi_hp_property_t)))
                        return (DDI_FAILURE);
        }
#ifdef _SYSCALL32_IMPL
        else {
                bzero(&request, sizeof (request));
                bzero(&result, sizeof (result));
                if (copyin(arg, &request32, sizeof (ddi_hp_property32_t)) ||
                    copyin(rval, &result32, sizeof (ddi_hp_property32_t)))
                        return (DDI_FAILURE);
                request.nvlist_buf = (char *)(uintptr_t)request32.nvlist_buf;
                request.buf_size = request32.buf_size;
                result.nvlist_buf = (char *)(uintptr_t)result32.nvlist_buf;
                result.buf_size = result32.buf_size;
        }
#endif

        if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size,
            &prop_list)) != DDI_SUCCESS)
                return (ret);

        if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) {
                ret = DDI_ENOMEM;
                goto get_prop_cleanup;
        }

        /* check whether the requested property is "all" or "help" */
        prop_pair = nvlist_next_nvpair(prop_list, NULL);
        if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair)) {
                name = nvpair_name(prop_pair);
                n = sizeof (pcishpc_props) / sizeof (pcishpc_prop_t);

                if (strcmp(name, PCIEHPC_PROP_ALL) == 0) {
                        (void) nvlist_remove_all(prop_list, PCIEHPC_PROP_ALL);

                        /*
                         * Add all properties into the request list, so that we
                         * will get the values in the following for loop.
                         */
                        for (i = 0; i < n; i++) {
                                if (nvlist_add_string(prop_list,
                                    pcishpc_props[i].prop_name, "") != 0) {
                                        ret = DDI_FAILURE;
                                        goto get_prop_cleanup1;
                                }
                        }
                        get_all_prop = B_TRUE;
                } else if (strcmp(name, PCIEHPC_PROP_HELP) == 0) {
                        /*
                         * Empty the request list, and add help strings into the
                         * return list. We will pass the following for loop.
                         */
                        (void) nvlist_remove_all(prop_list, PCIEHPC_PROP_HELP);

                        for (i = 0; i < n; i++) {
                                if (nvlist_add_string(prop_rlist,
                                    pcishpc_props[i].prop_name,
                                    pcishpc_props[i].prop_value) != 0) {
                                        ret = DDI_FAILURE;
                                        goto get_prop_cleanup1;
                                }
                        }
                }
        }

        mutex_enter(&ctrl_p->hc_mutex);

        /* get the current slot state */
        pcishpc_get_slot_state(slot_p);

        /* for each requested property, get the value and add it to nvlist */
        prop_pair = NULL;
        while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) {
                name = nvpair_name(prop_pair);
                value = NULL;

                if (strcmp(name, PCIEHPC_PROP_LED_FAULT) == 0) {
                        value = pcie_led_state_text(
                            slot_p->hs_fault_led_state);
                } else if (strcmp(name, PCIEHPC_PROP_LED_POWER) == 0) {
                        value = pcie_led_state_text(
                            slot_p->hs_power_led_state);
                } else if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
                        value = pcie_led_state_text(
                            slot_p->hs_attn_led_state);
                } else if (strcmp(name, PCIEHPC_PROP_LED_ACTIVE) == 0) {
                        value = pcie_led_state_text(
                            slot_p->hs_active_led_state);
                } else if (strcmp(name, PCIEHPC_PROP_CARD_TYPE) == 0) {
                        ddi_acc_handle_t        handle;
                        dev_info_t      *cdip;
                        uint8_t         prog_class, base_class, sub_class;
                        size_t          i;

                        mutex_exit(&ctrl_p->hc_mutex);
                        cdip = pcie_hp_devi_find(
                            ctrl_p->hc_dip, slot_p->hs_device_num, 0);
                        mutex_enter(&ctrl_p->hc_mutex);

                        if ((slot_p->hs_info.cn_state !=
                            DDI_HP_CN_STATE_ENABLED) || (cdip == NULL)) {
                                /*
                                 * When getting all properties, just ignore the
                                 * one that's not available under certain state.
                                 */
                                if (get_all_prop)
                                        continue;

                                ret = DDI_ENOTSUP;
                                goto get_prop_cleanup2;
                        }

                        if (pci_config_setup(cdip, &handle) != DDI_SUCCESS) {
                                ret = DDI_FAILURE;
                                goto get_prop_cleanup2;
                        }

                        prog_class = pci_config_get8(handle,
                            PCI_CONF_PROGCLASS);
                        base_class = pci_config_get8(handle, PCI_CONF_BASCLASS);
                        sub_class = pci_config_get8(handle, PCI_CONF_SUBCLASS);
                        pci_config_teardown(&handle);

                        for (i = 0; i < class_pci_items; i++) {
                                if ((base_class == class_pci[i].base_class) &&
                                    (sub_class == class_pci[i].sub_class) &&
                                    (prog_class == class_pci[i].prog_class)) {
                                        value = class_pci[i].short_desc;
                                        break;
                                }
                        }
                        if (i == class_pci_items)
                                value = PCIEHPC_PROP_VALUE_UNKNOWN;
                } else if (strcmp(name, PCIEHPC_PROP_BOARD_TYPE) == 0) {
                        if (slot_p->hs_info.cn_state <= DDI_HP_CN_STATE_EMPTY)
                                value = PCIEHPC_PROP_VALUE_UNKNOWN;
                        else
                                value = PCIEHPC_PROP_VALUE_PCIHOTPLUG;
                } else if (strcmp(name, PCIEHPC_PROP_SLOT_CONDITION) == 0) {
                        value = pcie_slot_condition_text(slot_p->hs_condition);
                } else {
                        /* unsupported property */
                        PCIE_DBG("Unsupported property: %s\n", name);

                        ret = DDI_ENOTSUP;
                        goto get_prop_cleanup2;
                }
                if (nvlist_add_string(prop_rlist, name, value) != 0) {
                        ret = DDI_FAILURE;
                        goto get_prop_cleanup2;
                }
        }

        // pack nvlist and copyout
        if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf,
            &result.buf_size)) != DDI_SUCCESS) {
                goto get_prop_cleanup2;
        }
        if (get_udatamodel() == DATAMODEL_NATIVE) {
                if (copyout(&result, rval, sizeof (ddi_hp_property_t))) {
                        ret = DDI_FAILURE;
                        goto get_prop_cleanup2;
                }
        }
#ifdef _SYSCALL32_IMPL
        else {
                if (result.buf_size > UINT32_MAX) {
                        ret = DDI_FAILURE;
                } else {
                        result32.buf_size = (uint32_t)result.buf_size;
                        if (copyout(&result32, rval,
                            sizeof (ddi_hp_property32_t)))
                                ret = DDI_FAILURE;
                }
        }
#endif

get_prop_cleanup2:
        mutex_exit(&ctrl_p->hc_mutex);
get_prop_cleanup1:
        nvlist_free(prop_rlist);
get_prop_cleanup:
        nvlist_free(prop_list);
        return (ret);
}

int
pcishpc_slot_set_property(pcie_hp_slot_t *slot_p, ddi_hp_property_t *arg,
    ddi_hp_property_t *rval)
{
        ddi_hp_property_t request, result;
#ifdef _SYSCALL32_IMPL
        ddi_hp_property32_t request32, result32;
#endif
        pcie_hp_ctrl_t          *ctrl_p = slot_p->hs_ctrl;
        nvlist_t                *prop_list;
        nvlist_t                *prop_rlist;
        nvpair_t                *prop_pair;
        char                    *name, *value;
        pcie_hp_led_state_t     led_state;
        int                     ret = DDI_SUCCESS;

        if (get_udatamodel() == DATAMODEL_NATIVE) {
                if (copyin(arg, &request, sizeof (ddi_hp_property_t)))
                        return (DDI_FAILURE);
                if (rval &&
                    copyin(rval, &result, sizeof (ddi_hp_property_t)))
                        return (DDI_FAILURE);
        }
#ifdef _SYSCALL32_IMPL
        else {
                bzero(&request, sizeof (request));
                bzero(&result, sizeof (result));
                if (copyin(arg, &request32, sizeof (ddi_hp_property32_t)))
                        return (DDI_FAILURE);
                if (rval &&
                    copyin(rval, &result32, sizeof (ddi_hp_property32_t)))
                        return (DDI_FAILURE);
                request.nvlist_buf = (char *)(uintptr_t)request32.nvlist_buf;
                request.buf_size = request32.buf_size;
                if (rval) {
                        result.nvlist_buf =
                            (char *)(uintptr_t)result32.nvlist_buf;
                        result.buf_size = result32.buf_size;
                }
        }
#endif

        if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size,
            &prop_list)) != DDI_SUCCESS)
                return (ret);

        /* check whether the requested property is "help" */
        prop_pair = nvlist_next_nvpair(prop_list, NULL);
        if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair) &&
            (strcmp(nvpair_name(prop_pair), PCIEHPC_PROP_HELP) == 0)) {
                if (!rval) {
                        ret = DDI_ENOTSUP;
                        goto set_prop_cleanup;
                }

                if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) {
                        ret = DDI_ENOMEM;
                        goto set_prop_cleanup;
                }
                if (nvlist_add_string(prop_rlist, PCIEHPC_PROP_LED_ATTN,
                    PCIEHPC_PROP_VALUE_LED) != 0) {
                        ret = DDI_FAILURE;
                        goto set_prop_cleanup1;
                }

                if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf,
                    &result.buf_size)) != DDI_SUCCESS) {
                        goto set_prop_cleanup1;
                }
                if (get_udatamodel() == DATAMODEL_NATIVE) {
                        if (copyout(&result, rval,
                            sizeof (ddi_hp_property_t))) {
                                ret =  DDI_FAILURE;
                                goto set_prop_cleanup1;
                        }
                }
#ifdef _SYSCALL32_IMPL
                else {
                        if (result.buf_size > UINT32_MAX) {
                                ret =  DDI_FAILURE;
                                goto set_prop_cleanup1;
                        } else {
                                result32.buf_size = (uint32_t)result.buf_size;
                                if (copyout(&result32, rval,
                                    sizeof (ddi_hp_property32_t))) {
                                        ret =  DDI_FAILURE;
                                        goto set_prop_cleanup1;
                                }
                        }
                }
#endif
set_prop_cleanup1:
                nvlist_free(prop_rlist);
                nvlist_free(prop_list);
                return (ret);
        }

        /* Validate the request */
        prop_pair = NULL;
        while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) {
                name = nvpair_name(prop_pair);
                if (nvpair_type(prop_pair) != DATA_TYPE_STRING) {
                        PCIE_DBG("Unexpected data type of setting "
                            "property %s.\n", name);
                        ret = DDI_EINVAL;
                        goto set_prop_cleanup;
                }
                if (nvpair_value_string(prop_pair, &value)) {
                        PCIE_DBG("Get string value failed for property %s.\n",
                            name);
                        ret = DDI_FAILURE;
                        goto set_prop_cleanup;
                }

                if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
                        if ((strcmp(value, PCIEHPC_PROP_VALUE_ON) != 0) &&
                            (strcmp(value, PCIEHPC_PROP_VALUE_OFF) != 0) &&
                            (strcmp(value, PCIEHPC_PROP_VALUE_BLINK) != 0)) {
                                PCIE_DBG("Unsupported value of setting "
                                    "property %s\n", name);
                                ret = DDI_ENOTSUP;
                                goto set_prop_cleanup;
                        }
                } else {
                        PCIE_DBG("Unsupported property: %s\n", name);
                        ret = DDI_ENOTSUP;
                        goto set_prop_cleanup;
                }
        }

        mutex_enter(&ctrl_p->hc_mutex);

        /* get the current slot state */
        pcishpc_get_slot_state(slot_p);

        // set each property
        prop_pair = NULL;
        while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) {
                name = nvpair_name(prop_pair);

                if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
                        if (strcmp(value, PCIEHPC_PROP_VALUE_ON) == 0)
                                led_state = PCIE_HP_LED_ON;
                        else if (strcmp(value, PCIEHPC_PROP_VALUE_OFF) == 0)
                                led_state = PCIE_HP_LED_OFF;
                        else if (strcmp(value, PCIEHPC_PROP_VALUE_BLINK) == 0)
                                led_state = PCIE_HP_LED_BLINK;
                        else
                                continue;

                        (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED,
                            led_state);
                }
        }
        if (rval) {
                if (get_udatamodel() == DATAMODEL_NATIVE) {
                        result.buf_size = 0;
                        if (copyout(&result, rval, sizeof (ddi_hp_property_t)))
                                ret =  DDI_FAILURE;
                }
#ifdef _SYSCALL32_IMPL
                else {
                        result32.buf_size = 0;
                        if (copyout(&result32, rval,
                            sizeof (ddi_hp_property32_t)))
                                ret =  DDI_FAILURE;
                }
#endif
        }

        mutex_exit(&ctrl_p->hc_mutex);
set_prop_cleanup:
        nvlist_free(prop_list);
        return (ret);
}

/*
 * pcishpc_hp_ops()
 *
 * Handle hotplug commands
 *
 * Note: This function is called by DDI HP framework at kernel context only
 */
/* ARGSUSED */
int
pcishpc_hp_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op,
    void *arg, void *result)
{
        pcie_hp_slot_t  *slot_p = NULL;
        pcie_hp_ctrl_t  *ctrl_p;
        int             ret = DDI_SUCCESS, i;

        PCIE_DBG("pcishpc_hp_ops: dip=%p cn_name=%s op=%x arg=%p\n",
            dip, cn_name, op, arg);

        if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
                return (DDI_FAILURE);

        for (i = 0; i < PCIE_HP_MAX_SLOTS && ctrl_p->hc_slots[i]; i++) {
                if (strcmp(ctrl_p->hc_slots[i]->hs_info.cn_name, cn_name)
                    == 0) {
                        /* Match with a physical slot, found */
                        slot_p = ctrl_p->hc_slots[i];
                        break;
                }
        }
        if (!slot_p) {
                PCIE_DBG("pcishpc_hp_ops: Failed to find the slot under"
                    "dip %p with name: %s; op=%x arg=%p\n",
                    dip, cn_name, op, arg);
                return (DDI_EINVAL);
        }
        switch (op) {
        case DDI_HPOP_CN_GET_STATE:
        {
                mutex_enter(&ctrl_p->hc_mutex);

                /* get the current slot state */
                pcishpc_get_slot_state(slot_p);

                *((ddi_hp_cn_state_t *)result) = slot_p->hs_info.cn_state;

                mutex_exit(&ctrl_p->hc_mutex);
                break;
        }
        case DDI_HPOP_CN_CHANGE_STATE:
        {
                ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;

                mutex_enter(&slot_p->hs_ctrl->hc_mutex);

                ret = pcishpc_change_slot_state(slot_p, target_state);
                *((ddi_hp_cn_state_t *)result) = slot_p->hs_info.cn_state;

                mutex_exit(&slot_p->hs_ctrl->hc_mutex);
                break;
        }
        case DDI_HPOP_CN_PROBE:
                ret = pcishpc_slot_probe(slot_p);

                break;
        case DDI_HPOP_CN_UNPROBE:
                ret = pcishpc_slot_unprobe(slot_p);

                break;
        case DDI_HPOP_CN_GET_PROPERTY:
                ret = pcishpc_slot_get_property(slot_p,
                    (ddi_hp_property_t *)arg, (ddi_hp_property_t *)result);
                break;
        case DDI_HPOP_CN_SET_PROPERTY:
                ret = pcishpc_slot_set_property(slot_p,
                    (ddi_hp_property_t *)arg, (ddi_hp_property_t *)result);
                break;
        default:
                ret = DDI_ENOTSUP;
                break;
        }

        return (ret);
}

/*
 * Local functions (called within this file)
 */

/*
 * pcishpc_create_controller()
 *
 * This function allocates and creates an SHPC controller state structure
 * and adds it to the linked list of controllers.
 */
static pcie_hp_ctrl_t *
pcishpc_create_controller(dev_info_t *dip)
{
        pcie_bus_t      *bus_p = PCIE_DIP2BUS(dip);
        pcie_hp_ctrl_t  *ctrl_p;

        PCIE_DBG("pcishpc: create controller for %s#%d\n",
            ddi_driver_name(dip), ddi_get_instance(dip));

        ctrl_p = kmem_zalloc(sizeof (pcie_hp_ctrl_t), KM_SLEEP);
        ctrl_p->hc_dip = dip;

        cv_init(&ctrl_p->hc_cmd_comp_cv, NULL, CV_DRIVER, NULL);

        /* Init the shpc controller's mutex. */
        mutex_init(&ctrl_p->hc_mutex, NULL, MUTEX_DRIVER, NULL);

        /* HPC initialization is complete now */
        ctrl_p->hc_flags = PCIE_HP_INITIALIZED_FLAG;
        bus_p->bus_hp_curr_mode = PCIE_PCI_HP_MODE;

        PCIE_SET_HP_CTRL(dip, ctrl_p);

        PCIE_DBG("pcishpc_create_controller() success\n");

        return (ctrl_p);
}


/*
 * pcishpc_setup_controller()
 *
 * Get the number of HotPlug Slots, and the PCI device information
 * for this HotPlug controller.
 */
static int
pcishpc_setup_controller(pcie_hp_ctrl_t *ctrl_p)
{
        uint32_t config;
        dev_info_t *ppdip;

        config = pcishpc_read_reg(ctrl_p, PCI_HP_SLOT_CONFIGURATION_REG);

        /* Get the number of HotPlug slots implemented */
        ctrl_p->hc_num_slots_impl = ((config)&31);

        /*
         * Initilize the current bus speed and number of hotplug slots
         * currently connected.
         */
        ctrl_p->hc_curr_bus_speed = -1;
        ctrl_p->hc_num_slots_connected = 0;

        /*
         * Get the first PCI device Number used.
         *
         * PCI-X I/O boat workaround.
         * The register doesn't set up the correct value.
         */
        ppdip = ddi_get_parent(ddi_get_parent(ctrl_p->hc_dip));
        if ((ddi_prop_get_int(DDI_DEV_T_ANY, ppdip, DDI_PROP_DONTPASS,
            "vendor-id", -1) == 0x108e) &&
            (ddi_prop_get_int(DDI_DEV_T_ANY, ppdip, DDI_PROP_DONTPASS,
            "device-id", -1) == 0x9010))
                ctrl_p->hc_device_start = 4;
        else
                ctrl_p->hc_device_start = ((config>>8)&31);

        /* Get the first Physical device number. */
        ctrl_p->hc_phys_start = ((config>>16)&0x7ff);

        /* Check if the device numbers increase or decrease. */
        ctrl_p->hc_device_increases = ((config>>29)&0x1);

        ctrl_p->hc_has_attn =
            (config & PCI_HP_SLOT_CONFIG_ATTN_BUTTON) ? B_TRUE : B_FALSE;
        ctrl_p->hc_has_mrl =
            (config & PCI_HP_SLOT_CONFIG_MRL_SENSOR) ? B_TRUE : B_FALSE;

        ctrl_p->hc_cmd_pending = B_FALSE;
        ctrl_p->hc_arbiter_timeout = B_FALSE;

        if (ctrl_p->hc_num_slots_impl > PCIE_HP_MAX_SLOTS) {
                PCIE_DBG("pcishpc_setup_controller() too many SHPC "
                    "slots error\n");
                return (DDI_FAILURE);
        }

        return (DDI_SUCCESS);
}


/*
 * pcishpc_destroy_controller()
 *
 * This function deallocates all of the SHPC controller resources.
 */
static int
pcishpc_destroy_controller(dev_info_t *dip)
{
        pcie_hp_ctrl_t  *ctrl_p;
        pcie_bus_t      *bus_p = PCIE_DIP2BUS(dip);

        PCIE_DBG("pcishpc_destroy_controller() called(dip=%p)\n", dip);

        /* get the soft state structure for this dip */
        if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL) {
                PCIE_DBG("pcishpc_destroy_controller() not found\n");
                return (DDI_FAILURE);
        }

        /*
         * Deallocate the slot state structures for this controller.
         */
        PCIE_SET_HP_CTRL(dip, NULL);
        bus_p->bus_hp_curr_mode = PCIE_NONE_HP_MODE;

        (void) pcishpc_destroy_slots(ctrl_p);
        cv_destroy(&ctrl_p->hc_cmd_comp_cv);
        mutex_destroy(&ctrl_p->hc_mutex);
        kmem_free(ctrl_p, sizeof (pcie_hp_ctrl_t));

        PCIE_DBG("pcishpc_destroy_controller() success\n");
        return (DDI_SUCCESS);
}

/*
 * pcishpc_create_slot()
 *
 * Allocate and add a new HotPlug slot state structure to the linked list.
 */
static pcie_hp_slot_t *
pcishpc_create_slot(pcie_hp_ctrl_t *ctrl_p)
{
        pcie_hp_slot_t *slot_p;

        PCIE_DBG("pcishpc_create_slot() called(ctrl_p=%x)\n", ctrl_p);

        /* Allocate a new slot structure. */
        slot_p = kmem_zalloc(sizeof (pcie_hp_slot_t), KM_SLEEP);
        slot_p->hs_ctrl = ctrl_p;

        /* Assign an initial value */
        slot_p->hs_info.cn_state = DDI_HP_CN_STATE_EMPTY;

        PCIE_DBG("pcishpc_create_slot() success\n");
        return (slot_p);
}

/*
 * pcishpc_register_slot()
 *
 * Create and register a slot with the Solaris HotPlug framework.
 */
static int
pcishpc_register_slot(pcie_hp_ctrl_t *ctrl_p, int slot)
{
        dev_info_t      *dip = ctrl_p->hc_dip;
        pcie_hp_slot_t  *slot_p;

        slot_p = pcishpc_create_slot(ctrl_p);
        ctrl_p->hc_slots[slot] = slot_p;
        slot_p->hs_num = slot;

        /* Setup the PCI device # for this SHPC slot. */
        if (ctrl_p->hc_device_increases)
                slot_p->hs_device_num = ctrl_p->hc_device_start +
                    slot_p->hs_num;
        else
                slot_p->hs_device_num = ctrl_p->hc_device_start -
                    slot_p->hs_num;

        /* Setup the DDI HP framework slot information. */
        slot_p->hs_info.cn_type = DDI_HP_CN_TYPE_PCI;
        slot_p->hs_info.cn_type_str = PCIE_PCI_HP_TYPE;
        slot_p->hs_info.cn_child = NULL;

        slot_p->hs_minor = PCI_MINOR_NUM(
            ddi_get_instance(dip), slot_p->hs_device_num);
        slot_p->hs_condition = AP_COND_UNKNOWN;

        /* setup thread for handling ATTN button events */
        if (ctrl_p->hc_has_attn) {
                PCIE_DBG("pcishpc_register_slot: "
                    "setting up ATTN button event "
                    "handler thread for slot %d\n", slot);

                cv_init(&slot_p->hs_attn_btn_cv, NULL, CV_DRIVER, NULL);
                slot_p->hs_attn_btn_pending = B_FALSE;
                slot_p->hs_attn_btn_threadp = thread_create(NULL, 0,
                    pcishpc_attn_btn_handler,
                    (void *)slot_p, 0, &p0, TS_RUN, minclsyspri);
                slot_p->hs_attn_btn_thread_exit = B_FALSE;
        }

        /* setup the slot name (used for ap-id) */
        pcishpc_set_slot_name(ctrl_p, slot);

        pcishpc_get_slot_state(slot_p);
        if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_ENABLED)
                slot_p->hs_condition = AP_COND_OK;

        /* register the slot with DDI HP framework */
        if (ndi_hp_register(dip, &slot_p->hs_info) != NDI_SUCCESS) {
                PCIE_DBG("pciehpc_register_slot() failed to register slot %d\n",
                    slot_p->hs_phy_slot_num);
                return (DDI_FAILURE);
        }

        pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip),
            slot_p->hs_minor), slot_p->hs_device_num);

        PCIE_DBG("pcishpc_register_slot() success for slot %d\n", slot);

        return (DDI_SUCCESS);
}

/*
 * pcishpc_destroy_slots()
 *
 * Free up all of the slot resources for this controller.
 */
static int
pcishpc_destroy_slots(pcie_hp_ctrl_t *ctrl_p)
{
        dev_info_t      *dip = ctrl_p->hc_dip;
        pcie_hp_slot_t  *slot_p;
        int             i;

        PCIE_DBG("pcishpc_destroy_slots() called(ctrl_p=%p)\n", ctrl_p);

        for (i = 0; i < PCIE_HP_MAX_SLOTS; i++) {
                if ((slot_p = ctrl_p->hc_slots[i]) == NULL)
                        continue;

                if (slot_p->hs_attn_btn_threadp != NULL) {
                        mutex_enter(&ctrl_p->hc_mutex);
                        slot_p->hs_attn_btn_thread_exit = B_TRUE;
                        cv_signal(&slot_p->hs_attn_btn_cv);
                        PCIE_DBG("pcishpc_destroy_slots: "
                            "waiting for ATTN thread exit\n");
                        cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
                        PCIE_DBG("pcishpc_destroy_slots: "
                            "ATTN thread exit\n");
                        cv_destroy(&slot_p->hs_attn_btn_cv);
                        slot_p->hs_attn_btn_threadp = NULL;
                        mutex_exit(&ctrl_p->hc_mutex);
                }

                PCIE_DBG("pcishpc_destroy_slots() (shpc_p=%p)\n"
                    "destroyed", slot_p);

                pcie_hp_delete_occupant_props(dip,
                    makedevice(ddi_driver_major(dip),
                    slot_p->hs_minor));

                /* unregister the slot with DDI HP framework */
                if (ndi_hp_unregister(dip, slot_p->hs_info.cn_name) !=
                    NDI_SUCCESS) {
                        PCIE_DBG("pcishpc_destroy_slots() "
                            "failed to unregister slot %d\n",
                            slot_p->hs_phy_slot_num);
                        return (DDI_FAILURE);
                }
                kmem_free(slot_p->hs_info.cn_name,
                    strlen(slot_p->hs_info.cn_name) + 1);
                kmem_free(slot_p, sizeof (pcie_hp_slot_t));
        }

        return (DDI_SUCCESS);
}

/*
 * pcishpc_enable_irqs()
 *
 * Enable/unmask the different IRQ's we support from the SHPC controller.
 */
int
pcishpc_enable_irqs(pcie_hp_ctrl_t *ctrl_p)
{
        uint32_t reg;
        int slot;

        reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG);

        /* Enable all interrupts. */
        reg &= ~PCI_HP_SERR_INT_MASK_ALL;

        pcishpc_write_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG, reg);

        /* Unmask the interrupts for each slot. */
        for (slot = 0; slot < ctrl_p->hc_num_slots_impl; slot++) {
                reg = pcishpc_read_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot);
                if ((reg & PCI_HP_SLOT_STATE_MASK) == PCI_HP_SLOT_ENABLED) {
                        reg &= ~(PCI_HP_SLOT_MASK_ALL |
                            PCI_HP_SLOT_MRL_SERR_MASK);
                        ctrl_p->hc_num_slots_connected++;
                        if (ctrl_p->hc_curr_bus_speed == -1)
                                ctrl_p->hc_curr_bus_speed =
                                    pcishpc_read_reg(ctrl_p,
                                    PCI_HP_PROF_IF_SBCR_REG) &
                                    PCI_HP_SBCR_SPEED_MASK;
                } else {
                        reg &= ~(PCI_HP_SLOT_MASK_ALL);
                }

                /* Enable/Unmask all slot interrupts. */
                pcishpc_write_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot, reg);
        }

        PCIE_DBG("pcishpc_enable_irqs: ctrl_p 0x%p, "
            "current bus speed 0x%x, slots connected 0x%x\n", ctrl_p,
            ctrl_p->hc_curr_bus_speed, ctrl_p->hc_num_slots_connected);

        return (DDI_SUCCESS);
}


/*
 * pcishpc_disable_irqs()
 *
 * Disable/Mask the different IRQ's we support from the SHPC controller.
 */
int
pcishpc_disable_irqs(pcie_hp_ctrl_t *ctrl_p)
{
        uint32_t reg;
        int slot;

        reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG);

        /* Mask all interrupts. */
        reg |= PCI_HP_SERR_INT_MASK_ALL;

        pcishpc_write_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG, reg);

        /* Unmask the interrupts for each slot. */
        for (slot = 0; slot < ctrl_p->hc_num_slots_impl; slot++) {
                reg = pcishpc_read_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot);

                /* Disable/Mask all slot interrupts. */
                reg |= PCI_HP_SLOT_MASK_ALL;

                pcishpc_write_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot, reg);
        }

        PCIE_DBG("pcishpc_disable_irqs: ctrl_p 0x%p, "
            "current bus speed 0x%x, slots connected 0x%x\n", ctrl_p,
            ctrl_p->hc_curr_bus_speed, ctrl_p->hc_num_slots_connected);

        return (DDI_SUCCESS);
}

/*
 * pcishpc_slot_poweron()
 *
 * Poweron/Enable the slot.
 *
 * Note: This function is called by DDI HP framework at kernel context only
 */
/*ARGSUSED*/
static int
pcishpc_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result_state)
{
        uint32_t        status;

        PCIE_DBG("pcishpc_slot_poweron called()\n");

        ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));

        /* get the current slot state */
        pcishpc_get_slot_state(slot_p);

        /* check if the slot is already in the 'enabled' state */
        if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) {
                /* slot is already in the 'enabled' state */
                PCIE_DBG("pcishpc_slot_poweron() slot %d already enabled\n",
                    slot_p->hs_phy_slot_num);

                *result_state = slot_p->hs_info.cn_state;
                return (DDI_SUCCESS);
        }

        if (slot_p->hs_info.cn_state == DDI_HP_CN_STATE_EMPTY) {
                PCIE_DBG("pcishpc_slot_poweron() slot in empty state\n");
                goto cleanup;
        }

        /* make sure the MRL sensor is closed */
        status = pcishpc_read_reg(slot_p->hs_ctrl,
            PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);

        if (status & PCI_HP_SLOT_MRL_STATE_MASK) {
                PCIE_DBG("pcishpc_slot_poweron() failed: MRL open\n");
                goto cleanup;
        }

        /* Set the Power LED to blink */
        (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_BLINK);

        /* Turn all other LEDS off */
        (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF);
        (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
        (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF);

        /* Set the bus speed only if the bus segment is not running */
        if (pcishpc_set_bus_speed(slot_p) != DDI_SUCCESS) {
                PCIE_DBG("pcishpc_slot_poweron() setting speed failed\n");
                goto cleanup;
        }

        slot_p->hs_ctrl->hc_num_slots_connected++;

        PCIE_DBG("pcishpc_slot_poweron(): slot_p 0x%p, slot state 0x%x, "
            "current bus speed 0x%x, slots connected 0x%x\n", slot_p,
            slot_p->hs_info.cn_state, slot_p->hs_ctrl->hc_curr_bus_speed,
            slot_p->hs_ctrl->hc_num_slots_connected);

        /* Mask or Unmask MRL Sensor SEER bit based on new slot state */
        if (slot_p->hs_ctrl->hc_has_mrl == B_TRUE) {
                uint32_t reg;

                reg = pcishpc_read_reg(slot_p->hs_ctrl,
                    PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);

                pcishpc_write_reg(slot_p->hs_ctrl,
                    PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num,
                    reg & ~PCI_HP_SLOT_MRL_SERR_MASK);
        }

        /* Update the hardware slot state. */
        if (pcishpc_set_slot_state(slot_p,
            DDI_HP_CN_STATE_ENABLED) != DDI_SUCCESS) {
                PCIE_DBG("pcishpc_slot_poweron() failed\n");

                pcishpc_get_slot_state(slot_p);
                goto cleanup;
        }
        /* Update the current state. It will be used in pcishpc_setled() */
        slot_p->hs_info.cn_state = DDI_HP_CN_STATE_ENABLED;

        /* Turn the Power LED ON for a enabled slot. */
        (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_ON);

        /* Turn all other LEDS off. */
        (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF);
        (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
        (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF);

        /* delay after powerON to let the device initialize itself */
        delay(drv_usectohz(pcishpc_reset_delay));

        PCIE_DBG("pcishpc_slot_poweron() success!\n");

        /*
         * Want to show up as POWERED state for now. It will be updated to
         * ENABLED state when user explicitly enable the slot.
         */
        slot_p->hs_info.cn_state = DDI_HP_CN_STATE_POWERED;

        /* get the current slot state */
        pcishpc_get_slot_state(slot_p);
        /*
         * It should be poweron'ed now. Have a check here in case any
         * hardware problems.
         */
        if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
                PCIE_DBG("pcishpc_slot_poweron() failed after hardware"
                    " registers all programmed.\n");

                goto cleanup;
        }

        *result_state = slot_p->hs_info.cn_state;

        return (DDI_SUCCESS);

cleanup:
        (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);
        return (DDI_FAILURE);
}

/*ARGSUSED*/
static int
pcishpc_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result_state)
{
        PCIE_DBG("pcishpc_slot_poweroff called()\n");

        ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));

        /* get the current slot state */
        pcishpc_get_slot_state(slot_p);

        /* check if the slot is not in the "enabled" or "powered" state */
        if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
                /* slot is in the 'disabled' state */
                PCIE_DBG("pcishpc_slot_poweroff(): "
                    "slot %d already disabled\n", slot_p->hs_phy_slot_num);

                *result_state = slot_p->hs_info.cn_state;
                return (DDI_SUCCESS);
        }

        /* Set the Power LED to blink */
        (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_BLINK);

        /* Turn all other LEDS off */
        (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF);
        (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
        (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF);

        if (--slot_p->hs_ctrl->hc_num_slots_connected == 0)
                slot_p->hs_ctrl->hc_curr_bus_speed = -1;

        PCIE_DBG("pcishpc_slot_poweroff(): slot_p 0x%p, slot state 0x%x, "
            "current bus speed 0x%x, slots connected 0x%x\n", slot_p,
            slot_p->hs_info.cn_state, slot_p->hs_ctrl->hc_curr_bus_speed,
            slot_p->hs_ctrl->hc_num_slots_connected);

        /* Mask or Unmask MRL Sensor SEER bit based on new slot state */
        if (slot_p->hs_ctrl->hc_has_mrl == B_TRUE) {
                uint32_t reg;

                reg = pcishpc_read_reg(slot_p->hs_ctrl,
                    PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);

                pcishpc_write_reg(slot_p->hs_ctrl,
                    PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num,
                    reg | PCI_HP_SLOT_MRL_SERR_MASK);
        }

        /* Update the hardware slot state. */
        if (pcishpc_set_slot_state(slot_p, DDI_HP_CN_STATE_PRESENT) !=
            DDI_SUCCESS) {
                PCIE_DBG("pcishpc_slot_poweroff() failed\n");

                pcishpc_get_slot_state(slot_p);
                goto cleanup;
        }

        /* Update the current state. It will be used in pcishpc_setled() */
        slot_p->hs_info.cn_state = DDI_HP_CN_STATE_PRESENT;

        /* Turn the Power LED OFF for a disabled slot. */
        (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);

        /* Turn all other LEDS off. */
        (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF);
        (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
        (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF);

        /* delay after powerON to let the device initialize itself */
        delay(drv_usectohz(pcishpc_reset_delay));

        pcishpc_get_slot_state(slot_p);
        /*
         * It should be poweroff'ed now. Have a check here in case any
         * hardware problems.
         */
        if (slot_p->hs_info.cn_state > DDI_HP_CN_STATE_PRESENT) {
                PCIE_DBG("pcishpc_slot_poweroff() failed after hardware"
                    " registers all programmed.\n");

                goto cleanup;
        }

        PCIE_DBG("pcishpc_slot_poweroff() success!\n");

        *result_state = slot_p->hs_info.cn_state;
        return (DDI_SUCCESS);

cleanup:
        (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);
        return (DDI_FAILURE);
}

/*
 * pcishpc_slot_probe()
 *
 * Probe the slot.
 *
 * Note: This function is called by DDI HP framework at kernel context only
 */
/*ARGSUSED*/
static int
pcishpc_slot_probe(pcie_hp_slot_t *slot_p)
{
        mutex_enter(&slot_p->hs_ctrl->hc_mutex);

        PCIE_DBG("pcishpc_slot_probe called()\n");

        /* get the current slot state */
        pcishpc_get_slot_state(slot_p);

        /*
         * Probe a given PCI Hotplug Connection (CN).
         */
        if (pcie_hp_probe(slot_p) != DDI_SUCCESS) {
                (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED,
                    PCIE_HP_LED_BLINK);

                PCIE_DBG("pcishpc_slot_probe() failed\n");

                mutex_exit(&slot_p->hs_ctrl->hc_mutex);
                return (DDI_FAILURE);
        }

        PCIE_DBG("pcishpc_slot_probe() success!\n");

        /* get the current slot state */
        pcishpc_get_slot_state(slot_p);

        mutex_exit(&slot_p->hs_ctrl->hc_mutex);
        return (DDI_SUCCESS);
}

/*
 * pcishpc_slot_unprobe()
 *
 * Unprobe the slot.
 *
 * Note: This function is called by DDI HP framework at kernel context only
 */
/*ARGSUSED*/
static int
pcishpc_slot_unprobe(pcie_hp_slot_t *slot_p)
{
        mutex_enter(&slot_p->hs_ctrl->hc_mutex);

        PCIE_DBG("pcishpc_slot_unprobe called()\n");

        /* get the current slot state */
        pcishpc_get_slot_state(slot_p);

        /*
         * Unprobe a given PCI Hotplug Connection (CN).
         */
        if (pcie_hp_unprobe(slot_p) != DDI_SUCCESS) {
                (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED,
                    PCIE_HP_LED_BLINK);

                PCIE_DBG("pcishpc_slot_unprobe() failed\n");

                mutex_exit(&slot_p->hs_ctrl->hc_mutex);
                return (DDI_FAILURE);
        }

        PCIE_DBG("pcishpc_slot_unprobe() success!\n");

        /* get the current slot state */
        pcishpc_get_slot_state(slot_p);

        mutex_exit(&slot_p->hs_ctrl->hc_mutex);
        return (DDI_SUCCESS);
}

static int
pcishpc_upgrade_slot_state(pcie_hp_slot_t *slot_p,
    ddi_hp_cn_state_t target_state)
{
        ddi_hp_cn_state_t curr_state;
        int rv = DDI_SUCCESS;

        if (target_state > DDI_HP_CN_STATE_ENABLED) {
                return (DDI_EINVAL);
        }

        curr_state = slot_p->hs_info.cn_state;
        while ((curr_state < target_state) && (rv == DDI_SUCCESS)) {

                switch (curr_state) {
                case DDI_HP_CN_STATE_EMPTY:
                        /*
                         * From EMPTY to PRESENT, just check the hardware
                         * slot state.
                         */
                        pcishpc_get_slot_state(slot_p);
                        curr_state = slot_p->hs_info.cn_state;
                        if (curr_state < DDI_HP_CN_STATE_PRESENT)
                                rv = DDI_FAILURE;
                        break;
                case DDI_HP_CN_STATE_PRESENT:
                        rv = pcishpc_slot_poweron(slot_p, &curr_state);
                        break;
                case DDI_HP_CN_STATE_POWERED:
                        curr_state = slot_p->hs_info.cn_state =
                            DDI_HP_CN_STATE_ENABLED;
                        break;
                default:
                        /* should never reach here */
                        ASSERT("unknown devinfo state");
                }
        }

        return (rv);
}

static int
pcishpc_downgrade_slot_state(pcie_hp_slot_t *slot_p,
    ddi_hp_cn_state_t target_state)
{
        ddi_hp_cn_state_t curr_state;
        int rv = DDI_SUCCESS;


        curr_state = slot_p->hs_info.cn_state;
        while ((curr_state > target_state) && (rv == DDI_SUCCESS)) {

                switch (curr_state) {
                case DDI_HP_CN_STATE_PRESENT:
                        /*
                         * From PRESENT to EMPTY, just check hardware
                         * slot state.
                         */
                        pcishpc_get_slot_state(slot_p);
                        curr_state = slot_p->hs_info.cn_state;
                        if (curr_state >= DDI_HP_CN_STATE_PRESENT)
                                rv = DDI_FAILURE;
                        break;
                case DDI_HP_CN_STATE_POWERED:
                        rv = pcishpc_slot_poweroff(slot_p, &curr_state);

                        break;
                case DDI_HP_CN_STATE_ENABLED:
                        curr_state = slot_p->hs_info.cn_state =
                            DDI_HP_CN_STATE_POWERED;

                        break;
                default:
                        /* should never reach here */
                        ASSERT("unknown devinfo state");
                }
        }

        return (rv);
}

/* Change slot state to a target state */
static int
pcishpc_change_slot_state(pcie_hp_slot_t *slot_p,
    ddi_hp_cn_state_t target_state)
{
        ddi_hp_cn_state_t curr_state;
        int rv;

        pcishpc_get_slot_state(slot_p);
        curr_state = slot_p->hs_info.cn_state;

        if (curr_state == target_state) {
                return (DDI_SUCCESS);
        }
        if (curr_state < target_state) {

                rv = pcishpc_upgrade_slot_state(slot_p, target_state);
        } else {
                rv = pcishpc_downgrade_slot_state(slot_p, target_state);
        }

        return (rv);
}

/*
 * pcishpc_issue_command()
 *
 * Sends a command to the SHPC controller.
 */
static int
pcishpc_issue_command(pcie_hp_ctrl_t *ctrl_p, uint32_t cmd_code)
{
        int     retCode;

        ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));

        PCIE_DBG("pcishpc_issue_command() cmd_code=%02x\n", cmd_code);

        ctrl_p->hc_cmd_pending = B_TRUE;

        /* Write the command to the SHPC controller. */
        pcishpc_write_reg(ctrl_p, PCI_HP_COMMAND_STATUS_REG, cmd_code);

        while (ctrl_p->hc_cmd_pending == B_TRUE)
                cv_wait(&ctrl_p->hc_cmd_comp_cv, &ctrl_p->hc_mutex);

        /* Wait until the SHPC controller processes the command. */
        retCode = pcishpc_wait_busy(ctrl_p);

        /* Make sure the command completed. */
        if (retCode == DDI_SUCCESS) {
                /* Did the command fail to generate the command complete IRQ? */
                if (ctrl_p->hc_cmd_pending != B_FALSE) {
                        PCIE_DBG("pcishpc_issue_command() Failed on "
                            "generate cmd complete IRQ\n");
                        retCode = DDI_FAILURE;
                }
        }

        if (retCode == DDI_FAILURE)
                PCIE_DBG("pcishpc_issue_command() Failed on cmd_code=%02x\n",
                    cmd_code);
        else
                PCIE_DBG("pcishpc_issue_command() Success on "
                    "cmd_code=%02x\n", cmd_code);

        return (retCode);
}

/*
 * pcishpc_wait_busy()
 *
 * Wait until the SHPC controller is not busy.
 */
static int
pcishpc_wait_busy(pcie_hp_ctrl_t *ctrl_p)
{
        uint32_t        status;

        /* Wait until SHPC controller is NOT busy */
        for (;;) {
                status = pcishpc_read_reg(ctrl_p, PCI_HP_COMMAND_STATUS_REG);

                /* Is there an MRL Sensor error? */
                if ((status & PCI_HP_COMM_STS_ERR_MASK) ==
                    PCI_HP_COMM_STS_ERR_MRL_OPEN) {
                        PCIE_DBG("pcishpc_wait_busy() ERROR: "
                            "MRL Sensor error\n");
                        break;
                }

                /* Is there an Invalid command error? */
                if ((status & PCI_HP_COMM_STS_ERR_MASK) ==
                    PCI_HP_COMM_STS_ERR_INVALID_COMMAND) {
                        PCIE_DBG("pcishpc_wait_busy() ERROR: Invalid "
                            "command error\n");
                        break;
                }

                /* Is there an Invalid Speed/Mode error? */
                if ((status & PCI_HP_COMM_STS_ERR_MASK) ==
                    PCI_HP_COMM_STS_ERR_INVALID_SPEED) {
                        PCIE_DBG("pcishpc_wait_busy() ERROR: Invalid "
                            "Speed/Mode error\n");
                        break;
                }

                /* Is the SHPC controller not BUSY? */
                if (!(status & PCI_HP_COMM_STS_CTRL_BUSY)) {
                        /* Return Success. */
                        return (DDI_SUCCESS);
                }

                PCIE_DBG("pcishpc_wait_busy() SHPC controller busy. Waiting\n");

                /* Wait before polling the status register again. */
                delay(drv_usectohz(PCIE_HP_CMD_WAIT_TIME));
        }

        return (DDI_FAILURE);
}

static void
pcishpc_attn_btn_handler(pcie_hp_slot_t *slot_p)
{
        pcie_hp_led_state_t hs_power_led_state;
        callb_cpr_t cprinfo;

        PCIE_DBG("pcishpc_attn_btn_handler: thread started\n");

        CALLB_CPR_INIT(&cprinfo, &slot_p->hs_ctrl->hc_mutex,
            callb_generic_cpr, "pcishpc_attn_btn_handler");

        mutex_enter(&slot_p->hs_ctrl->hc_mutex);

        /* wait for ATTN button event */
        cv_wait(&slot_p->hs_attn_btn_cv, &slot_p->hs_ctrl->hc_mutex);

        while (slot_p->hs_attn_btn_thread_exit == B_FALSE) {
                if (slot_p->hs_attn_btn_pending == B_TRUE) {
                        /* get the current state of power LED */
                        hs_power_led_state = slot_p->hs_power_led_state;

                        /* Blink the Power LED while we wait for 5 seconds */
                        (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED,
                            PCIE_HP_LED_BLINK);

                        /* wait for 5 seconds before taking any action */
                        if (cv_reltimedwait(&slot_p->hs_attn_btn_cv,
                            &slot_p->hs_ctrl->hc_mutex,
                            SEC_TO_TICK(5), TR_CLOCK_TICK) == -1) {
                                /*
                                 * It is a time out;
                                 * make sure the ATTN pending flag is
                                 * still ON before sending the event
                                 * to DDI HP framework.
                                 */
                                if (slot_p->hs_attn_btn_pending == B_TRUE) {
                                        int hint;

                                        /* restore the power LED state */
                                        (void) pcishpc_setled(slot_p,
                                            PCIE_HP_POWER_LED,
                                            hs_power_led_state);
                                        /*
                                         * send the ATTN button event
                                         * to DDI HP framework
                                         */
                                        slot_p->hs_attn_btn_pending = B_FALSE;

                                        pcishpc_get_slot_state(slot_p);

                                        if (slot_p->hs_info.cn_state <=
                                            DDI_HP_CN_STATE_PRESENT) {
                                                /*
                                                 * Insertion.
                                                 */
                                                hint = SE_INCOMING_RES;
                                        } else {
                                                /*
                                                 * Want to remove;
                                                 */
                                                hint = SE_OUTGOING_RES;
                                        }
                                        pcie_hp_gen_sysevent_req(
                                            slot_p->hs_info.cn_name,
                                            hint,
                                            slot_p->hs_ctrl->hc_dip,
                                            KM_SLEEP);

                                        continue;
                                }
                        }

                        /* restore the power LED state */
                        (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED,
                            hs_power_led_state);
                        continue;
                }

                /* wait for another ATTN button event */
                cv_wait(&slot_p->hs_attn_btn_cv, &slot_p->hs_ctrl->hc_mutex);
        }

        PCIE_DBG("pcishpc_attn_btn_handler: thread exit\n");
        cv_signal(&slot_p->hs_attn_btn_cv);
        CALLB_CPR_EXIT(&cprinfo);
        thread_exit();
}

/*
 * pcishpc_get_slot_state()
 *
 * Get the state of the slot.
 * The slot state should have been initialized before this function gets called.
 */
static void
pcishpc_get_slot_state(pcie_hp_slot_t *slot_p)
{
        uint32_t reg;
        ddi_hp_cn_state_t curr_state = slot_p->hs_info.cn_state;

        /* Read the logical slot register for this Slot. */
        reg = pcishpc_read_reg(slot_p->hs_ctrl,
            PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);

        /* Convert from the SHPC slot state to the HPC slot state. */
        slot_p->hs_info.cn_state = pcishpc_slot_shpc_to_hpc(reg);
        if (curr_state == DDI_HP_CN_STATE_POWERED &&
            slot_p->hs_info.cn_state > DDI_HP_CN_STATE_POWERED) {
                /*
                 * Keep POWERED state if it is currently POWERED state because
                 * this driver does not really implement enable/disable
                 * slot operations. That is, when poweron, it actually enables
                 * the slot also.
                 * So, from hardware view, POWERED == ENABLED.
                 * But, when user explicitly change to POWERED state, it should
                 * be kept until user explicitly change to other states later.
                 */
                slot_p->hs_info.cn_state = DDI_HP_CN_STATE_POWERED;
        }

        /* Convert from the SHPC Power LED state to the HPC Power LED state. */
        slot_p->hs_power_led_state = pcishpc_led_shpc_to_hpc((reg>>2)&3);

        /* Convert from the SHPC Attn LED state to the HPC Attn LED state. */
        slot_p->hs_attn_led_state = pcishpc_led_shpc_to_hpc((reg>>4)&3);

        /* We don't have a fault LED so just default it to OFF. */
        slot_p->hs_fault_led_state = PCIE_HP_LED_OFF;

        /* We don't have an active LED so just default it to OFF. */
        slot_p->hs_active_led_state = PCIE_HP_LED_OFF;
}

/*
 * pcishpc_set_slot_state()
 *
 * Updates the slot's state and leds.
 */
static int
pcishpc_set_slot_state(pcie_hp_slot_t *slot_p,
    ddi_hp_cn_state_t new_slot_state)
{
        uint32_t                reg, cmd_code;
        ddi_hp_cn_state_t       curr_state;

        ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));

        reg = pcishpc_read_reg(slot_p->hs_ctrl,
            PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);

        /* Default all states to unchanged. */
        cmd_code = ((1 + slot_p->hs_num) << 8);

        /* Has the slot state changed? */
        curr_state = pcishpc_slot_shpc_to_hpc(reg);
        if (curr_state != new_slot_state) {
                PCIE_DBG("pcishpc_set_slot_state() Slot State changed");

                /* Set the new slot state in the Slot operation command. */
                cmd_code |= pcishpc_slot_hpc_to_shpc(new_slot_state);
        }

        /* Has the Power LED state changed? */
        if (slot_p->hs_power_led_state != pcishpc_led_shpc_to_hpc((reg>>2)&3)) {
                PCIE_DBG("pcishpc_set_slot_state() Power LED State changed\n");

                /* Set the new power led state in the Slot operation command. */
                cmd_code |=
                    (pcishpc_led_hpc_to_shpc(slot_p->hs_power_led_state) << 2);
        }

        /* Has the Attn LED state changed? */
        if (slot_p->hs_attn_led_state != pcishpc_led_shpc_to_hpc((reg>>4)&3)) {
                PCIE_DBG("pcishpc_set_slot_state() Attn LED State changed\n");

                /* Set the new attn led state in the Slot operation command. */
                cmd_code |=
                    (pcishpc_led_hpc_to_shpc(slot_p->hs_attn_led_state) << 4);
        }

        return (pcishpc_issue_command(slot_p->hs_ctrl, cmd_code));
}

/*
 * setup slot name/slot-number info.
 */
static void
pcishpc_set_slot_name(pcie_hp_ctrl_t *ctrl_p, int slot)
{
        pcie_hp_slot_t  *slot_p = ctrl_p->hc_slots[slot];
        pcie_bus_t      *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
        uchar_t *slotname_data;
        int *slotnum;
        uint_t count;
        int len;
        uchar_t *s;
        uint32_t bit_mask;
        int pci_id_cnt, pci_id_bit;
        int slots_before, found;
        int invalid_slotnum = 0;

        if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ctrl_p->hc_dip,
            DDI_PROP_DONTPASS, "physical-slot#", &slotnum, &count) ==
            DDI_PROP_SUCCESS) {
                slot_p->hs_phy_slot_num = slotnum[0];
                ddi_prop_free(slotnum);
        } else {
                if (ctrl_p->hc_device_increases)
                        slot_p->hs_phy_slot_num = ctrl_p->hc_phys_start + slot;
                else
                        slot_p->hs_phy_slot_num = ctrl_p->hc_phys_start - slot;

                if ((ndi_prop_update_int(DDI_DEV_T_NONE, ctrl_p->hc_dip,
                    "physical-slot#", slot_p->hs_phy_slot_num)) != DDI_SUCCESS)
                        PCIE_DBG("pcishpc_set_slot_name(): failed to "
                            "create phyical-slot#%d\n",
                            slot_p->hs_phy_slot_num);
        }

        /* Platform may not have initialized it */
        if (!slot_p->hs_phy_slot_num) {
                slot_p->hs_phy_slot_num = pci_config_get8(bus_p->bus_cfg_hdl,
                    PCI_BCNF_SECBUS);
                invalid_slotnum = 1;
        }
        slot_p->hs_info.cn_num = slot_p->hs_phy_slot_num;
        slot_p->hs_info.cn_num_dpd_on = DDI_HP_CN_NUM_NONE;

        /*
         * construct the slot_name:
         *      if "slot-names" property exists then use that name
         *      else if valid slot number exists then it is "pci<slot-num>".
         *      else it will be "pci<sec-bus-number>dev<dev-number>"
         */
        if (ddi_getlongprop(DDI_DEV_T_ANY, ctrl_p->hc_dip, DDI_PROP_DONTPASS,
            "slot-names", (caddr_t)&slotname_data, &len) == DDI_PROP_SUCCESS) {
                bit_mask = slotname_data[3] | (slotname_data[2] << 8) |
                    (slotname_data[1] << 16) | (slotname_data[0] << 24);

                pci_id_bit = 1;
                pci_id_cnt = slots_before = found = 0;

                /*
                 * Walk the bit mask until we find the bit that corresponds
                 * to our slots device number.  We count how many bits
                 * we find before we find our slot's bit.
                 */
                while (!found && (pci_id_cnt < 32)) {
                        while (slot_p->hs_device_num != pci_id_cnt) {

                                /*
                                 * Find the next bit set.
                                 */
                                while (!(bit_mask & pci_id_bit) &&
                                    (pci_id_cnt < 32)) {
                                        pci_id_bit = pci_id_bit << 1;
                                        pci_id_cnt++;
                                }

                                if (slot_p->hs_device_num != pci_id_cnt)
                                        slots_before++;
                                else
                                        found = 1;
                        }
                }

                if (pci_id_cnt < 32) {

                        /*
                         * Set ptr to first string.
                         */
                        s = slotname_data + 4;

                        /*
                         * Increment past all the strings for the slots
                         * before ours.
                         */
                        while (slots_before) {
                                while (*s != '\0')
                                        s++;
                                s++;
                                slots_before--;
                        }

                        slot_p->hs_info.cn_name = i_ddi_strdup((char *)s,
                            KM_SLEEP);
                        kmem_free(slotname_data, len);
                        return;
                }

                /* slot-names entry not found */
                PCIE_DBG("pcishpc_set_slot_name(): "
                    "No slot-names entry found for slot #%d\n",
                    slot_p->hs_phy_slot_num);
                kmem_free(slotname_data, len);
        }

        if (invalid_slotnum) {
                char tmp_name[256];

                (void) snprintf(tmp_name, sizeof (tmp_name), "pci%d",
                    slot_p->hs_device_num);
                slot_p->hs_info.cn_name = i_ddi_strdup(tmp_name, KM_SLEEP);
        } else {
                char tmp_name[256];

                (void) snprintf(tmp_name, sizeof (tmp_name), "pci%d",
                    slot_p->hs_phy_slot_num);
                slot_p->hs_info.cn_name = i_ddi_strdup(tmp_name, KM_SLEEP);
        }
}

/*
 * pcishpc_set_bus_speed()
 *
 * Set the bus speed and mode.
 */
static int
pcishpc_set_bus_speed(pcie_hp_slot_t *slot_p)
{
        pcie_hp_ctrl_t  *ctrl_p = slot_p->hs_ctrl;
        int             curr_speed = ctrl_p->hc_curr_bus_speed;
        int             speed = -1;
        int             avail_slots;
        uint32_t        status, slots_avail1_reg, slots_avail2_reg;

        ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));

        /* Make sure that the slot is in a correct state */
        status = pcishpc_read_reg(ctrl_p,
            PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);

        /* Return failure if the slot is empty */
        if ((status & PCI_HP_SLOT_CARD_EMPTY_MASK) ==
            PCI_HP_SLOT_CARD_EMPTY_MASK) {
                PCIE_DBG("pcishpc_set_bus_speed() failed: "
                    "the slot is empty\n");
                return (DDI_FAILURE);
        }

        /* Return failure if the slot is not in disabled state */
        if ((status & PCI_HP_SLOT_STATE_MASK) != PCI_HP_SLOT_DISABLED) {
                PCIE_DBG("pcishpc_set_bus_speed() failed: "
                    "incorrect slot state\n");
                return (DDI_FAILURE);
        }

        /* Set the "power-only" mode for the slot */
        if (pcishpc_issue_command(ctrl_p, ((1+slot_p->hs_num)<<8) |
            PCI_HP_SLOT_POWER_ONLY) != DDI_SUCCESS) {
                PCIE_DBG("pcishpc_set_bus_speed() failed to set "
                    "the slot %d in the power-only mode\n", slot_p->hs_num);
                return (DDI_FAILURE);
        }

        /* Wait for power good */
        delay(drv_usectohz(PCIE_HP_POWER_GOOD_WAIT_TIME));

        /* Make sure that the slot is in "power-only" state */
        status = pcishpc_read_reg(ctrl_p,
            PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);

        if ((status & PCI_HP_SLOT_STATE_MASK) != PCI_HP_SLOT_POWER_ONLY) {
                PCIE_DBG("pcishpc_set_bus_speed() "
                    "power-only failed: incorrect slot state\n");
                return (DDI_FAILURE);
        }

        slots_avail1_reg = pcishpc_read_reg(ctrl_p,
            PCI_HP_SLOTS_AVAIL_I_REG);
        slots_avail2_reg = pcishpc_read_reg(ctrl_p,
            PCI_HP_SLOTS_AVAIL_II_REG);

        /*
         * Check if SHPC has available slots and select the highest
         * available bus speed for the slot.
         *
         * The bus speed codes are:
         * 100 - 133Mhz; <--+
         * 011 - 100Mhz; <--+   PCI-X
         * 010 - 66Mhz;  <--+
         *
         * 001 - 66Mhz;  <--+
         * 000 - 33Mhz   <--+   Conv PCI
         */
        switch (status & PCI_HP_SLOT_PCIX_CAPABLE_MASK) {
        case PCI_HP_SLOT_133MHZ_PCIX_CAPABLE:
                avail_slots = (slots_avail1_reg >>
                    PCI_HP_AVAIL_133MHZ_PCIX_SPEED_SHIFT) &
                    PCI_HP_AVAIL_SPEED_MASK;

                if (((curr_speed == -1) && avail_slots) ||
                    (curr_speed == PCI_HP_SBCR_133MHZ_PCIX_SPEED)) {
                        speed = PCI_HP_SBCR_133MHZ_PCIX_SPEED;
                        break;
                }
                /* FALLTHROUGH */
        case PCI_HP_SLOT_100MHZ_PCIX_CAPABLE:
                avail_slots = (slots_avail1_reg >>
                    PCI_HP_AVAIL_100MHZ_PCIX_SPEED_SHIFT) &
                    PCI_HP_AVAIL_SPEED_MASK;

                if (((curr_speed == -1) && avail_slots) ||
                    (curr_speed == PCI_HP_SBCR_100MHZ_PCIX_SPEED)) {
                        speed = PCI_HP_SBCR_100MHZ_PCIX_SPEED;
                        break;
                }
                /* FALLTHROUGH */
        case PCI_HP_SLOT_66MHZ_PCIX_CAPABLE:
                avail_slots = (slots_avail1_reg >>
                    PCI_HP_AVAIL_66MHZ_PCIX_SPEED_SHIFT) &
                    PCI_HP_AVAIL_SPEED_MASK;

                if (((curr_speed == -1) && avail_slots) ||
                    (curr_speed == PCI_HP_SBCR_66MHZ_PCIX_SPEED)) {
                        speed = PCI_HP_SBCR_66MHZ_PCIX_SPEED;
                        break;
                }
                /* FALLTHROUGH */
        default:
                avail_slots = (slots_avail2_reg >>
                    PCI_HP_AVAIL_66MHZ_CONV_SPEED_SHIFT) &
                    PCI_HP_AVAIL_SPEED_MASK;

                if ((status & PCI_HP_SLOT_66MHZ_CONV_CAPABLE) &&
                    (((curr_speed == -1) && avail_slots) ||
                    (curr_speed == PCI_HP_SBCR_66MHZ_CONV_SPEED))) {
                        speed = PCI_HP_SBCR_66MHZ_CONV_SPEED;
                } else {
                        avail_slots = (slots_avail1_reg >>
                            PCI_HP_AVAIL_33MHZ_CONV_SPEED_SHIFT) &
                            PCI_HP_AVAIL_SPEED_MASK;

                        if (((curr_speed == -1) && (avail_slots)) ||
                            (curr_speed == PCI_HP_SBCR_33MHZ_CONV_SPEED)) {
                                speed = PCI_HP_SBCR_33MHZ_CONV_SPEED;
                        } else {
                                PCIE_DBG("pcishpc_set_bus_speed() "
                                    " failed to set the bus speed, slot# %d\n",
                                    slot_p->hs_num);
                                return (DDI_FAILURE);
                        }
                }
                break;
        }

        /*
         * If the bus segment is already running, check to see the card
         * in the slot can support the current bus speed.
         */
        if (curr_speed == speed) {
                /*
                 * Check to see there is any slot available for the current
                 * bus speed. Otherwise, we need fail the current slot connect
                 * request.
                 */
                return ((avail_slots <= ctrl_p->hc_num_slots_connected) ?
                    DDI_FAILURE : DDI_SUCCESS);
        }

        /* Set the bus speed */
        if (pcishpc_issue_command(ctrl_p, PCI_HP_COMM_STS_SET_SPEED |
            speed) == DDI_FAILURE) {
                PCIE_DBG("pcishpc_set_bus_speed() failed "
                    "to set bus %d speed\n", slot_p->hs_num);
                return (DDI_FAILURE);
        }

        /* Check the current bus speed */
        status = pcishpc_read_reg(ctrl_p, PCI_HP_PROF_IF_SBCR_REG) &
            PCI_HP_SBCR_SPEED_MASK;
        if ((status & PCI_HP_SBCR_SPEED_MASK) != speed) {
                PCIE_DBG("pcishpc_set_bus_speed() an incorrect "
                    "bus speed, slot = 0x%x, speed = 0x%x\n",
                    slot_p->hs_num, status & PCI_HP_SBCR_SPEED_MASK);
                return (DDI_FAILURE);
        }


        /* Save the current bus speed */
        ctrl_p->hc_curr_bus_speed = speed;

        return (DDI_SUCCESS);
}

/*
 * pcishpc_setled()
 *
 * Change the state of a slot's LED.
 */
static int
pcishpc_setled(pcie_hp_slot_t *slot_p, pcie_hp_led_t led,
    pcie_hp_led_state_t state)
{
        ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));

        switch (led) {
                case PCIE_HP_FAULT_LED:
                        PCIE_DBG("pcishpc_setled() - PCIE_HP_FAULT_LED "
                            "(set %s)\n", pcishpc_slot_textledstate(state));
                        slot_p->hs_fault_led_state = state;
                        break;

                case PCIE_HP_POWER_LED:
                        PCIE_DBG("pcishpc_setled() - PCIE_HP_POWER_LED "
                            "(set %s)\n", pcishpc_slot_textledstate(state));
                        slot_p->hs_power_led_state = state;
                        break;

                case PCIE_HP_ATTN_LED:
                        PCIE_DBG("pcishpc_setled() - PCIE_HP_ATTN_LED "
                            "(set %s)\n", pcishpc_slot_textledstate(state));
                        slot_p->hs_attn_led_state = state;
                        break;

                case PCIE_HP_ACTIVE_LED:
                        PCIE_DBG("pcishpc_setled() - PCIE_HP_ACTIVE_LED "
                            "(set %s)\n", pcishpc_slot_textledstate(state));
                        slot_p->hs_active_led_state = state;
                        break;
        }

        return (pcishpc_set_slot_state(slot_p, slot_p->hs_info.cn_state));
}

/*
 * pcishpc_led_shpc_to_hpc()
 *
 * Convert from SHPC indicator status to HPC indicator status.
 */
static int
pcishpc_led_shpc_to_hpc(int state)
{
        switch (state) {
                case 1: /* SHPC On bits b01 */
                        return (PCIE_HP_LED_ON);
                case 2: /* SHPC Blink bits b10 */
                        return (PCIE_HP_LED_BLINK);
                case 3: /* SHPC Off bits b11 */
                        return (PCIE_HP_LED_OFF);
        }

        return (PCIE_HP_LED_OFF);
}


/*
 * pcishpc_led_hpc_to_shpc()
 *
 * Convert from HPC indicator status to SHPC indicator status.
 */
static int
pcishpc_led_hpc_to_shpc(int state)
{
        switch (state) {
                case PCIE_HP_LED_ON:
                        return (1); /* SHPC On bits b01 */
                case PCIE_HP_LED_BLINK:
                        return (2); /* SHPC Blink bits b10 */
                case PCIE_HP_LED_OFF:
                        return (3); /* SHPC Off bits b11 */
        }

        return (3); /* SHPC Off bits b11 */
}

/*
 * pcishpc_slot_shpc_to_hpc()
 *
 * Convert from SHPC slot state to HPC slot state.
 * The argument shpc_state is expected to be read from the slot register.
 */
static int
pcishpc_slot_shpc_to_hpc(int shpc_state)
{
        if ((shpc_state & PCI_HP_SLOT_CARD_EMPTY_MASK) ==
            PCI_HP_SLOT_CARD_EMPTY_MASK)
                return (DDI_HP_CN_STATE_EMPTY);

        switch (shpc_state & PCI_HP_SLOT_STATE_MASK) {
                case PCI_HP_SLOT_POWER_ONLY: /* SHPC Powered Only */
                        return (DDI_HP_CN_STATE_POWERED);

                case PCI_HP_SLOT_ENABLED: /* SHPC Enabled */
                        return (DDI_HP_CN_STATE_ENABLED);

                case PCI_HP_SLOT_DISABLED:      /* SHPC Disabled */
                default :                       /* SHPC Reserved */
                        return (DDI_HP_CN_STATE_PRESENT);
        }
}

/*
 * pcishpc_slot_hpc_to_shpc()
 *
 * Convert from HPC slot state to SHPC slot state.
 */
static int
pcishpc_slot_hpc_to_shpc(int state)
{
        switch (state) {
                case DDI_HP_CN_STATE_EMPTY:
                        return (0);

                case DDI_HP_CN_STATE_POWERED:
                        return (PCI_HP_SLOT_POWER_ONLY);

                case DDI_HP_CN_STATE_ENABLED:
                        return (PCI_HP_SLOT_ENABLED);

                default:
                        return (PCI_HP_SLOT_DISABLED);
        }
}

/*
 * pcishpc_slot_textslotstate()
 *
 * Convert the request into a text message.
 */
static char *
pcishpc_slot_textslotstate(ddi_hp_cn_state_t state)
{
        /* Convert an HPC slot state into a textual string. */
        if (state == DDI_HP_CN_STATE_EMPTY)
                return ("HPC_SLOT_EMPTY");
        else if (state == DDI_HP_CN_STATE_ENABLED)
                return ("HPC_SLOT_ENABLED");
        else if (state == DDI_HP_CN_STATE_POWERED)
                return ("HPC_SLOT_POWERED_ONLY");
        else
                return ("HPC_SLOT_DISABLED");
}


/*
 * pcishpc_slot_textledstate()
 *
 * Convert the led state into a text message.
 */
static char *
pcishpc_slot_textledstate(pcie_hp_led_state_t state)
{
        /* Convert an HPC led state into a textual string. */
        switch (state) {
                case PCIE_HP_LED_OFF:
                        return ("off");

                case PCIE_HP_LED_ON:
                        return ("on");

                case PCIE_HP_LED_BLINK:
                        return ("blink");
        }
        return ("unknown");
}


/*
 * pcishpc_read_reg()
 *
 * Read from a SHPC controller register.
 */
static uint32_t
pcishpc_read_reg(pcie_hp_ctrl_t *ctrl_p, int reg)
{
        pcie_bus_t      *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);

        /* Setup the SHPC dword select register. */
        pci_config_put8(bus_p->bus_cfg_hdl,
            bus_p->bus_pci_hp_off + PCI_HP_DWORD_SELECT_OFF, (uint8_t)reg);

        /* Read back the SHPC dword select register and verify. */
        if (pci_config_get8(bus_p->bus_cfg_hdl, bus_p->bus_pci_hp_off +
            PCI_HP_DWORD_SELECT_OFF) != (uint8_t)reg) {
                PCIE_DBG("pcishpc_read_reg() - Failed writing DWORD "
                    "select reg\n");
                return (0xFFFFFFFF);
        }

        /* Read from the SHPC dword data register. */
        return (pci_config_get32(bus_p->bus_cfg_hdl,
            bus_p->bus_pci_hp_off + PCI_HP_DWORD_DATA_OFF));
}


/*
 * pcishpc_write_reg()
 *
 * Write to a SHPC controller register.
 */
static void
pcishpc_write_reg(pcie_hp_ctrl_t *ctrl_p, int reg, uint32_t data)
{
        pcie_bus_t      *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);

        /* Setup the SHPC dword select register. */
        pci_config_put8(bus_p->bus_cfg_hdl,
            bus_p->bus_pci_hp_off + PCI_HP_DWORD_SELECT_OFF, (uint8_t)reg);

        /* Read back the SHPC dword select register and verify. */
        if (pci_config_get8(bus_p->bus_cfg_hdl, bus_p->bus_pci_hp_off +
            PCI_HP_DWORD_SELECT_OFF) != (uint8_t)reg) {
                PCIE_DBG("pcishpc_write_reg() - Failed writing "
                    "DWORD select reg\n");
                return;
        }

        /* Write to the SHPC dword data register. */
        pci_config_put32(bus_p->bus_cfg_hdl,
            bus_p->bus_pci_hp_off + PCI_HP_DWORD_DATA_OFF, data);

        /*
         * Issue a read of the VendorID/DeviceID just to force the previous
         * write to complete. This is probably not necessary, but it does
         * help enforce ordering if there is an issue.
         */
        (void) pci_config_get16(bus_p->bus_cfg_hdl, PCI_CONF_VENID);
}


#ifdef  DEBUG
/*
 * pcishpc_dump_regs()
 *
 * Dumps all of the SHPC controller registers.
 */
static void
pcishpc_dump_regs(pcie_hp_ctrl_t *ctrl_p)
{
        int slot, numSlots;
        uint32_t reg;
        char *state;

        if (!pcie_debug_flags)
                return;

        PCIE_DBG("pcishpc_dump_regs() called:\n");
        PCIE_DBG("==========================================================");

        PCIE_DBG("SHPC Base Offset                              "
            ": 0x%08x\n", pcishpc_read_reg(ctrl_p, PCI_HP_BASE_OFFSET_REG));

        reg = pcishpc_read_reg(ctrl_p, PCI_HP_SLOTS_AVAIL_I_REG);

        PCIE_DBG("Number of PCIX slots avail (33 Mhz)            : %d\n",
            (reg & 31));

        PCIE_DBG("Number of PCIX slots avail (66 Mhz)            : %d\n",
            ((reg>>8) & 31));

        PCIE_DBG("Number of PCIX slots avail (100 Mhz)          : %d\n",
            ((reg>>16) & 31));

        PCIE_DBG("Number of PCIX slots avail (133 Mhz)          : %d\n",
            ((reg>>24) & 31));

        reg = pcishpc_read_reg(ctrl_p, PCI_HP_SLOTS_AVAIL_II_REG);

        PCIE_DBG("Number of conventional PCI slots (66 Mhz) : %d\n",
            (reg & 31));

        reg = pcishpc_read_reg(ctrl_p, PCI_HP_SLOT_CONFIGURATION_REG);

        numSlots = (reg & 31);

        PCIE_DBG("Number of Slots connected to this port         : %d\n",
            numSlots);

        PCIE_DBG("PCI Device # for First HotPlug Slot            : %d\n",
            ((reg>>8) & 31));

        PCIE_DBG("Physical Slot # for First PCI Device #         : %d\n",
            ((reg>>16) & 0x7ff));

        PCIE_DBG("Physical Slot Number Up/Down                   : %d\n",
            ((reg>>29) & 0x1));

        PCIE_DBG("MRL Sensor Implemented                         : %s\n",
            (reg & PCI_HP_SLOT_CONFIG_MRL_SENSOR) ? "Yes" : "No");

        PCIE_DBG("Attention Button Implemented                   : %s\n",
            (reg & PCI_HP_SLOT_CONFIG_ATTN_BUTTON) ? "Yes" : "No");

        reg = pcishpc_read_reg(ctrl_p, PCI_HP_PROF_IF_SBCR_REG);

        switch (reg & 7) {
                case 0:
                        state = "33Mhz Conventional PCI";
                        break;
                case 1:
                        state = "66Mhz Conventional PCI";
                        break;
                case 2:
                        state = "66Mhz PCI-X";
                        break;
                case 3:
                        state = "100Mhz PCI-X";
                        break;
                case 4:
                        state = "133Mhz PCI-X";
                        break;
                default:
                        state = "Reserved (Error)";
                        break;
        }

        PCIE_DBG("Current Port Operation Mode           : %s\n", state);

        PCIE_DBG("SHPC Interrupt Message Number         : %d\n",
            ((reg>>16) &31));

        PCIE_DBG("SHPC Programming Interface            : %d\n",
            ((reg>>24) & 0xff));

        reg = pcishpc_read_reg(ctrl_p, PCI_HP_COMMAND_STATUS_REG);

        PCIE_DBG("SHPC Command Code                     : %d\n",
            (reg & 0xff));

        PCIE_DBG("SHPC Target Slot                      : %d\n",
            ((reg>>8) & 31));

        PCIE_DBG("SHPC Controller Busy                  : %s\n",
            ((reg>>16) & 1) ? "Yes" : "No");

        PCIE_DBG("SHPC Controller Err: MRL Sensor       : %s\n",
            ((reg>>17) & 1) ? "Yes" : "No");

        PCIE_DBG("SHPC Controller Err: Invalid Command  : %s\n",
            ((reg>>18) & 1) ? "Yes" : "No");

        PCIE_DBG("SHPC Controller Err: Invalid Speed/Mode : %s\n",
            ((reg>>19) & 1) ? "Yes" : "No");

        reg = pcishpc_read_reg(ctrl_p, PCI_HP_IRQ_LOCATOR_REG);

        PCIE_DBG("Command Completion Interrupt Pending  : %s\n",
            (reg & PCI_HP_IRQ_CMD_COMPLETE) ? "Yes" : "No");

        for (slot = 0; slot < numSlots; slot++) {
                PCIE_DBG("Slot %d Interrupt Pending     : %s\n", slot+1,
                    (reg & (PCI_HP_IRQ_SLOT_N_PENDING<<slot)) ? "Yes" : "No");
        }

        reg = pcishpc_read_reg(ctrl_p, PCI_HP_SERR_LOCATOR_REG);

        PCIE_DBG("Arbiter SERR Pending                  : %s\n",
            (reg & PCI_HP_IRQ_SERR_ARBITER_PENDING) ? "Yes" : "No");

        for (slot = 0; slot < numSlots; slot++) {
                PCIE_DBG("Slot %d SERR Pending          : %s\n",
                    slot+1, (reg &
                    (PCI_HP_IRQ_SERR_SLOT_N_PENDING<<slot)) ? "Yes" : "No");
        }

        reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG);

        PCIE_DBG("Global Interrupt Mask                 : %s\n",
            (reg & PCI_HP_SERR_INT_GLOBAL_IRQ_MASK) ? "Yes" : "No");

        PCIE_DBG("Global SERR Mask                      : %s\n",
            (reg & PCI_HP_SERR_INT_GLOBAL_SERR_MASK) ? "Yes" : "No");

        PCIE_DBG("Command Completion Interrupt Mask     : %s\n",
            (reg & PCI_HP_SERR_INT_CMD_COMPLETE_MASK) ? "Yes" : "No");

        PCIE_DBG("Arbiter SERR Mask                     : %s\n",
            (reg & PCI_HP_SERR_INT_ARBITER_SERR_MASK) ? "Yes" : "No");

        PCIE_DBG("Command Completion Detected           : %s\n",
            (reg & PCI_HP_SERR_INT_CMD_COMPLETE_IRQ) ? "Yes" : "No");

        PCIE_DBG("Arbiter Timeout Detected              : %s\n",
            (reg & PCI_HP_SERR_INT_ARBITER_IRQ) ? "Yes" : "No");

        for (slot = 0; slot < numSlots; slot++) {
                PCIE_DBG("Logical Slot %d Registers:\n", slot+1);
                PCIE_DBG("------------------------------------\n");

                reg = pcishpc_read_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot);

                PCIE_DBG("Slot %d state                 : %s\n", slot+1,
                    pcishpc_slot_textslotstate(pcishpc_slot_shpc_to_hpc(reg)));

                PCIE_DBG("Slot %d Power Indicator State : %s\n", slot+1,
                    pcishpc_slot_textledstate(pcishpc_led_shpc_to_hpc(
                    (reg>>2) &3)));

                PCIE_DBG("Slot %d Attention Indicator State : %s\n", slot+1,
                    pcishpc_slot_textledstate(pcishpc_led_shpc_to_hpc(
                    (reg>>4)&3)));

                PCIE_DBG("Slot %d Power Fault           : %s\n", slot+1,
                    ((reg>>6)&1) ? "Fault Detected" : "No Fault");
                PCIE_DBG("Slot %d Attention Button      : %s\n", slot+1,
                    ((reg>>7)&1) ? "Depressed" : "Not Depressed");
                PCIE_DBG("Slot %d MRL Sensor            : %s\n", slot+1,
                    ((reg>>8)&1) ? "Not Closed" : "Closed");
                PCIE_DBG("Slot %d 66mhz Capable         : %s\n", slot+1,
                    ((reg>>9)&1) ? "66mhz" : "33mgz");

                switch ((reg>>10)&3) {
                        case 0:
                                state = "Card Present 7.5W";
                                break;
                        case 1:
                                state = "Card Present 15W";
                                break;
                        case 2:
                                state = "Card Present 25W";
                                break;
                        case 3:
                                state = "Slot Empty";
                                break;
                }

                PCIE_DBG("Slot %d PRSNT1#/PRSNT2#       : %s\n", slot+1,
                    state);

                switch ((reg>>12)&3) {
                        case 0:
                                state = "Non PCI-X";
                                break;
                        case 1:
                                state = "66mhz PCI-X";
                                break;
                        case 2:
                                state = "Reserved";
                                break;
                        case 3:
                                state = "133mhz PCI-X";
                                break;
                }

                PCIE_DBG("Slot %d Card Presence Change Detected   : %s\n",
                    slot+1, (reg & PCI_HP_SLOT_PRESENCE_DETECTED) ? "Yes" :
                    "No");
                PCIE_DBG("Slot %d Isolated Power Fault Detected   : %s\n",
                    slot+1, (reg & PCI_HP_SLOT_ISO_PWR_DETECTED) ? "Yes" :
                    "No");
                PCIE_DBG("Slot %d Attention Button Press Detected : %s\n",
                    slot+1, (reg & PCI_HP_SLOT_ATTN_DETECTED) ? "Yes" : "No");
                PCIE_DBG("Slot %d MRL Sensor Change Detected      : %s\n",
                    slot+1, (reg & PCI_HP_SLOT_MRL_DETECTED) ? "Yes" : "No");
                PCIE_DBG("Slot %d Connected Power Fault Detected  : %s\n",
                    slot+1, (reg & PCI_HP_SLOT_POWER_DETECTED) ? "Yes" : "No");

                PCIE_DBG("Slot %d Card Presence IRQ Masked        : %s\n",
                    slot+1, (reg & PCI_HP_SLOT_PRESENCE_MASK) ? "Yes" : "No");
                PCIE_DBG("Slot %d Isolated Power Fault IRQ Masked : %s\n",
                    slot+1, (reg & PCI_HP_SLOT_ISO_PWR_MASK) ? "Yes" : "No");
                PCIE_DBG("Slot %d Attention Button IRQ Masked     : %s\n",
                    slot+1, (reg & PCI_HP_SLOT_ATTN_MASK) ? "Yes" : "No");
                PCIE_DBG("Slot %d MRL Sensor IRQ Masked           : %s\n",
                    slot+1, (reg & PCI_HP_SLOT_MRL_MASK) ? "Yes" : "No");
                PCIE_DBG("Slot %d Connected Power Fault IRQ Masked : %s\n",
                    slot+1, (reg & PCI_HP_SLOT_POWER_MASK) ? "Yes" : "No");
                PCIE_DBG("Slot %d MRL Sensor SERR Masked          : %s\n",
                    slot+1, (reg & PCI_HP_SLOT_MRL_SERR_MASK) ? "Yes" : "No");
                PCIE_DBG("Slot %d Connected Power Fault SERR Masked : %s\n",
                    slot+1, (reg & PCI_HP_SLOT_POWER_SERR_MASK) ? "Yes" : "No");
        }
}
#endif  /* DEBUG */