#include <sys/types.h>
#include <sys/note.h>
#include <sys/conf.h>
#include <sys/kmem.h>
#include <sys/debug.h>
#include <sys/vtrace.h>
#include <sys/autoconf.h>
#include <sys/varargs.h>
#include <sys/ddi_impldefs.h>
#include <sys/time.h>
#include <sys/callb.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/sysmacros.h>
#include <sys/sysevent/dr.h>
#include <sys/pci_impl.h>
#include <sys/kstat.h>
#include <sys/zone.h>
#include <sys/hotplug/pci/pcie_hp.h>
#include <sys/hotplug/pci/pciehpc.h>
int pcie_auto_online = 1;
typedef enum pciehpc_slot_power {
PSP_NO_CONTROLLER = 0,
PSP_HAS_CONTROLLER = (1U << 0),
PSP_OFF = (1U << 1),
PSP_FAULT = (1U << 2)
} pciehpc_slot_power_t;
typedef struct {
pcie_hp_ctrl_t *pst_ctrl;
ddi_hp_cn_state_t pst_targ;
ddi_hp_cn_state_t pst_cur;
} pciehpc_sync_task_t;
static const pciehpc_led_plat_state_t pciehpc_std_leds[PCIEHPC_LED_NSTATES] = {
[PCIE_LL_BASE] = { PCIE_HLA_OFF, PCIE_HLA_OFF },
[PCIE_LL_POWER_TRANSITION] = { PCIE_HLA_BLINK, PCIE_HLA_PASS },
[PCIE_LL_POWERED] = { PCIE_HLA_ON, PCIE_HLA_OFF },
[PCIE_LL_PROBE_FAILED] = { PCIE_HLA_PASS, PCIE_HLA_ON },
[PCIE_LL_POWER_FAULT] = { PCIE_HLA_BLINK, PCIE_HLA_PASS },
[PCIE_LL_ATTENTION_BUTTON] = { PCIE_HLA_BLINK, PCIE_HLA_PASS }
};
static const pciehpc_led_plat_state_t pciehpc_gimlet_leds[PCIEHPC_LED_NSTATES] =
{
[PCIE_LL_BASE] = { PCIE_HLA_OFF, PCIE_HLA_OFF },
[PCIE_LL_POWER_TRANSITION] = { PCIE_HLA_PASS, PCIE_HLA_PASS },
[PCIE_LL_POWERED] = { PCIE_HLA_PASS, PCIE_HLA_ON },
[PCIE_LL_PROBE_FAILED] = { PCIE_HLA_PASS, PCIE_HLA_BLINK },
[PCIE_LL_POWER_FAULT] = { PCIE_HLA_PASS, PCIE_HLA_BLINK },
[PCIE_LL_ATTENTION_BUTTON] = { PCIE_HLA_PASS, PCIE_HLA_PASS }
};
typedef struct pciehpc_led_plat_map {
uint16_t plpm_vid;
uint16_t plpm_did;
uint16_t plpm_svid;
uint16_t plpm_ssys;
const pciehpc_led_plat_state_t *plpm_map;
} pciehpc_led_plat_map_t;
static const pciehpc_led_plat_map_t pciehpc_led_plat_map[] = {
{ .plpm_vid = 0x1022, .plpm_did = 0x1483, .plpm_svid = 0x1de,
.plpm_ssys = 0xfff9, .plpm_map = pciehpc_gimlet_leds }
};
static int pciehpc_hpc_init(pcie_hp_ctrl_t *ctrl_p);
static int pciehpc_hpc_uninit(pcie_hp_ctrl_t *ctrl_p);
static int pciehpc_slotinfo_init(pcie_hp_ctrl_t *ctrl_p);
static int pciehpc_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p);
static int pciehpc_enable_intr(pcie_hp_ctrl_t *ctrl_p);
static int pciehpc_disable_intr(pcie_hp_ctrl_t *ctrl_p);
static pcie_hp_ctrl_t *pciehpc_create_controller(dev_info_t *dip);
static void pciehpc_destroy_controller(dev_info_t *dip);
static int pciehpc_register_slot(pcie_hp_ctrl_t *ctrl_p);
static int pciehpc_unregister_slot(pcie_hp_ctrl_t *ctrl_p);
static int pciehpc_slot_get_property(pcie_hp_slot_t *slot_p,
uintptr_t arg, uintptr_t rval);
static int pciehpc_slot_set_property(pcie_hp_slot_t *slot_p,
uintptr_t arg, uintptr_t rval);
static void pciehpc_issue_hpc_command(pcie_hp_ctrl_t *ctrl_p, uint16_t control);
static void pciehpc_attn_btn_handler(pcie_hp_ctrl_t *ctrl_p);
static pcie_hp_led_state_t pciehpc_led_state_to_hpc(uint16_t state);
static int pciehpc_upgrade_slot_state(pcie_hp_slot_t *slot_p,
ddi_hp_cn_state_t target_state);
static int pciehpc_downgrade_slot_state(pcie_hp_slot_t *slot_p,
ddi_hp_cn_state_t target_state);
static int pciehpc_change_slot_state(pcie_hp_slot_t *slot_p,
ddi_hp_cn_state_t target_state);
static int
pciehpc_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result);
static int
pciehpc_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result);
static int pciehpc_slot_probe(pcie_hp_slot_t *slot_p);
static int pciehpc_slot_unprobe(pcie_hp_slot_t *slot_p);
static void pciehpc_handle_power_fault(dev_info_t *dip);
static void pciehpc_power_fault_handler(void *arg);
#ifdef DEBUG
static void pciehpc_dump_hpregs(pcie_hp_ctrl_t *ctrl_p);
#endif
typedef struct pciehpc_stat_data {
kstat_named_t psd_attnsw_count;
kstat_named_t psd_attnsw_ts;
kstat_named_t psd_pwrflt_count;
kstat_named_t psd_pwrflt_ts;
kstat_named_t psd_mrl_chg_count;
kstat_named_t psd_mrl_chg_ts;
kstat_named_t psd_pres_chg_count;
kstat_named_t psd_pres_chg_ts;
kstat_named_t psd_cmd_cpl_count;
kstat_named_t psd_cmd_cpl_ts;
kstat_named_t psd_dll_chg_count;
kstat_named_t psd_dll_chg_ts;
kstat_named_t psd_intr_count;
kstat_named_t psd_intr_ts;
} pciehpc_stat_data_t;
static inline void
pciehpc_kstat_event(kstat_named_t *countp, kstat_named_t *tsp, hrtime_t now)
{
atomic_inc_64(&countp->value.ui64);
tsp->value.ui64 = now;
}
#define KSTAT_EVENT(_slotp, _evname, _now) \
pciehpc_kstat_event((&(_slotp)->hs_stat_data->psd_ ## _evname ## _count), \
(&(_slotp)->hs_stat_data->psd_ ## _evname ## _ts), (_now))
int
pciehpc_init(dev_info_t *dip, caddr_t arg)
{
pcie_hp_regops_t *regops = (pcie_hp_regops_t *)(void *)arg;
pcie_hp_ctrl_t *ctrl_p;
PCIE_DBG("pciehpc_init() called (dip=%p)\n", (void *)dip);
if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) != NULL) {
PCIE_DBG("%s%d: pciehpc instance already initialized!\n",
ddi_driver_name(dip), ddi_get_instance(dip));
return (DDI_SUCCESS);
}
ctrl_p = pciehpc_create_controller(dip);
if (regops != NULL) {
ctrl_p->hc_regops = *regops;
}
(void) pci_resource_setup(dip);
PCIE_DISABLE_ERRORS(dip);
ctrl_p->hc_ops.init_hpc_hw = pciehpc_hpc_init;
ctrl_p->hc_ops.uninit_hpc_hw = pciehpc_hpc_uninit;
ctrl_p->hc_ops.init_hpc_slotinfo = pciehpc_slotinfo_init;
ctrl_p->hc_ops.uninit_hpc_slotinfo = pciehpc_slotinfo_uninit;
ctrl_p->hc_ops.poweron_hpc_slot = pciehpc_slot_poweron;
ctrl_p->hc_ops.poweroff_hpc_slot = pciehpc_slot_poweroff;
ctrl_p->hc_ops.enable_hpc_intr = pciehpc_enable_intr;
ctrl_p->hc_ops.disable_hpc_intr = pciehpc_disable_intr;
#if defined(__x86)
pciehpc_update_ops(ctrl_p);
#endif
if ((ctrl_p->hc_ops.init_hpc_hw)(ctrl_p) != DDI_SUCCESS)
goto cleanup1;
if ((ctrl_p->hc_ops.init_hpc_slotinfo)(ctrl_p) != DDI_SUCCESS)
goto cleanup2;
if (pciehpc_register_slot(ctrl_p) != DDI_SUCCESS)
goto cleanup3;
if (pcie_create_minor_node(ctrl_p, 0) != DDI_SUCCESS)
goto cleanup4;
if (ctrl_p->hc_slots[0]->hs_info.cn_state == DDI_HP_CN_STATE_ENABLED) {
PCIE_ENABLE_ERRORS(dip);
}
ctrl_p->hc_flags |= PCIE_HP_INITIALIZED_FLAG;
#ifdef DEBUG
pciehpc_dump_hpregs(ctrl_p);
#endif
return (DDI_SUCCESS);
cleanup4:
(void) pciehpc_unregister_slot(ctrl_p);
cleanup3:
(void) (ctrl_p->hc_ops.uninit_hpc_slotinfo)(ctrl_p);
cleanup2:
(void) (ctrl_p->hc_ops.uninit_hpc_hw)(ctrl_p);
cleanup1:
PCIE_ENABLE_ERRORS(dip);
(void) pci_resource_destroy(dip);
pciehpc_destroy_controller(dip);
return (DDI_FAILURE);
}
int
pciehpc_uninit(dev_info_t *dip)
{
pcie_hp_ctrl_t *ctrl_p;
taskqid_t id;
PCIE_DBG("pciehpc_uninit() called (dip=%p)\n", (void *)dip);
if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL) {
return (DDI_FAILURE);
}
mutex_enter(&ctrl_p->hc_mutex);
ctrl_p->hc_flags &= ~PCIE_HP_INITIALIZED_FLAG;
id = ctrl_p->hc_startup_sync;
ctrl_p->hc_startup_sync = TASKQID_INVALID;
mutex_exit(&ctrl_p->hc_mutex);
if (id != TASKQID_INVALID)
taskq_wait_id(system_taskq, id);
pcie_remove_minor_node(ctrl_p, 0);
(void) pciehpc_unregister_slot(ctrl_p);
(void) (ctrl_p->hc_ops.uninit_hpc_slotinfo)(ctrl_p);
(void) (ctrl_p->hc_ops.uninit_hpc_hw)(ctrl_p);
PCIE_ENABLE_ERRORS(dip);
(void) pci_resource_destroy(dip);
pciehpc_destroy_controller(dip);
return (DDI_SUCCESS);
}
int
pciehpc_intr(dev_info_t *dip)
{
pcie_hp_ctrl_t *ctrl_p;
pcie_hp_slot_t *slot_p;
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
uint16_t status, control;
boolean_t clear_pend = B_FALSE;
hrtime_t now;
if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
return (DDI_INTR_UNCLAIMED);
now = gethrtime();
mutex_enter(&ctrl_p->hc_mutex);
if (!(ctrl_p->hc_flags & PCIE_HP_INITIALIZED_FLAG)) {
mutex_exit(&ctrl_p->hc_mutex);
return (DDI_INTR_UNCLAIMED);
}
if (bus_p->bus_hp_curr_mode != PCIE_NATIVE_HP_MODE) {
mutex_exit(&ctrl_p->hc_mutex);
return (DDI_INTR_UNCLAIMED);
}
slot_p = ctrl_p->hc_slots[0];
status = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTSTS);
if (!(status & PCIE_SLOTSTS_STATUS_EVENTS)) {
mutex_exit(&ctrl_p->hc_mutex);
return (DDI_INTR_UNCLAIMED);
}
KSTAT_EVENT(slot_p, intr, now);
pciehpc_reg_put16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTSTS, status);
if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
KSTAT_EVENT(slot_p, cmd_cpl, now);
PCIE_DBG("pciehpc_intr(): CMD COMPLETED interrupt received\n");
cv_signal(&ctrl_p->hc_cmd_comp_cv);
}
if (status & PCIE_SLOTSTS_ATTN_BTN_PRESSED) {
KSTAT_EVENT(slot_p, attnsw, now);
PCIE_DBG("pciehpc_intr(): ATTN BUTTON interrupt received\n");
if (slot_p->hs_attn_btn_pending == B_TRUE)
slot_p->hs_attn_btn_pending = B_FALSE;
else
slot_p->hs_attn_btn_pending = B_TRUE;
cv_signal(&slot_p->hs_attn_btn_cv);
}
if (status & PCIE_SLOTSTS_PWR_FAULT_DETECTED) {
KSTAT_EVENT(slot_p, pwrflt, now);
PCIE_DBG("pciehpc_intr(): POWER FAULT interrupt received"
" on slot %d\n", slot_p->hs_phy_slot_num);
control = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL);
if (control & PCIE_SLOTCTL_PWR_FAULT_EN) {
slot_p->hs_condition = AP_COND_FAILED;
pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
PCIE_SLOTCTL, control & ~PCIE_SLOTCTL_PWR_FAULT_EN);
pciehpc_handle_power_fault(dip);
clear_pend = B_TRUE;
}
}
if (status & PCIE_SLOTSTS_MRL_SENSOR_CHANGED) {
KSTAT_EVENT(slot_p, mrl_chg, now);
PCIE_DBG("pciehpc_intr(): MRL SENSOR CHANGED interrupt received"
" on slot %d\n", slot_p->hs_phy_slot_num);
}
if (status & PCIE_SLOTSTS_PRESENCE_CHANGED) {
KSTAT_EVENT(slot_p, pres_chg, now);
PCIE_DBG("pciehpc_intr(): PRESENCE CHANGED interrupt received"
" on slot %d\n", slot_p->hs_phy_slot_num);
if (status & PCIE_SLOTSTS_PRESENCE_DETECTED) {
ddi_hp_cn_state_t tgt_state = (pcie_auto_online != 0) ?
DDI_HP_CN_STATE_ENABLED : DDI_HP_CN_STATE_PRESENT;
cmn_err(CE_NOTE, "pciehpc (%s%d): card is inserted"
" in the slot %s",
ddi_driver_name(dip),
ddi_get_instance(dip),
slot_p->hs_info.cn_name);
(void) ndi_hp_state_change_req(dip,
slot_p->hs_info.cn_name,
tgt_state, DDI_HP_REQ_ASYNC);
} else {
cmn_err(CE_NOTE, "pciehpc (%s%d): card is removed"
" from the slot %s",
ddi_driver_name(dip),
ddi_get_instance(dip),
slot_p->hs_info.cn_name);
if (slot_p->hs_info.cn_state ==
DDI_HP_CN_STATE_ENABLED) {
slot_p->hs_condition = AP_COND_FAILED;
} else {
slot_p->hs_condition = AP_COND_UNKNOWN;
}
control = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL);
if (control & PCIE_SLOTCTL_PWR_FAULT_EN)
pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
PCIE_SLOTCTL,
control & ~PCIE_SLOTCTL_PWR_FAULT_EN);
dev_info_t *cdip = ddi_get_child(dip);
if (cdip != NULL) {
ddi_eventcookie_t rm_cookie;
if (ddi_get_eventcookie(cdip,
DDI_DEVI_REMOVE_EVENT,
&rm_cookie) == DDI_SUCCESS) {
ndi_post_event(dip, cdip, rm_cookie,
NULL);
}
}
(void) ndi_hp_state_change_req(dip,
slot_p->hs_info.cn_name,
DDI_HP_CN_STATE_EMPTY,
DDI_HP_REQ_ASYNC);
}
clear_pend = B_TRUE;
}
if (ctrl_p->hc_dll_active_rep &&
(status & PCIE_SLOTSTS_DLL_STATE_CHANGED)) {
KSTAT_EVENT(slot_p, dll_chg, now);
PCIE_DBG("pciehpc_intr(): DLL STATE CHANGED interrupt received"
" on slot %d\n", slot_p->hs_phy_slot_num);
cv_signal(&slot_p->hs_dll_active_cv);
}
if (clear_pend) {
ctrl_p->hc_flags &= ~PCIE_HP_SYNC_PENDING;
}
mutex_exit(&ctrl_p->hc_mutex);
return (DDI_INTR_CLAIMED);
}
int
pciehpc_hp_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op,
void *arg, void *result)
{
pcie_hp_ctrl_t *ctrl_p;
pcie_hp_slot_t *slot_p;
int ret = DDI_SUCCESS;
PCIE_DBG("pciehpc_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);
slot_p = ctrl_p->hc_slots[0];
if (strcmp(cn_name, slot_p->hs_info.cn_name) != 0)
return (DDI_EINVAL);
switch (op) {
case DDI_HPOP_CN_GET_STATE:
{
mutex_enter(&slot_p->hs_ctrl->hc_mutex);
pciehpc_get_slot_state(slot_p);
*((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_CHANGE_STATE:
{
ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
mutex_enter(&slot_p->hs_ctrl->hc_mutex);
ret = pciehpc_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 = pciehpc_slot_probe(slot_p);
break;
case DDI_HPOP_CN_UNPROBE:
ret = pciehpc_slot_unprobe(slot_p);
break;
case DDI_HPOP_CN_GET_PROPERTY:
ret = pciehpc_slot_get_property(slot_p, (uintptr_t)arg,
(uintptr_t)result);
break;
case DDI_HPOP_CN_SET_PROPERTY:
ret = pciehpc_slot_set_property(slot_p, (uintptr_t)arg,
(uintptr_t)result);
break;
default:
ret = DDI_ENOTSUP;
break;
}
return (ret);
}
void
pciehpc_get_slot_state(pcie_hp_slot_t *slot_p)
{
pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
uint16_t control, status;
ddi_hp_cn_state_t curr_state = slot_p->hs_info.cn_state;
ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
control = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL);
slot_p->hs_fault_led_state = PCIE_HP_LED_OFF;
slot_p->hs_active_led_state = PCIE_HP_LED_OFF;
status = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTSTS);
slot_p->hs_power_led_state =
pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control));
slot_p->hs_attn_led_state =
pciehpc_led_state_to_hpc(pcie_slotctl_attn_indicator_get(control));
if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
slot_p->hs_info.cn_state = DDI_HP_CN_STATE_EMPTY;
return;
}
slot_p->hs_info.cn_state = DDI_HP_CN_STATE_PRESENT;
if (ctrl_p->hc_has_pwr && (control & PCIE_SLOTCTL_PWR_CONTROL) != 0) {
return;
}
if (curr_state == DDI_HP_CN_STATE_ENABLED) {
slot_p->hs_info.cn_state = curr_state;
} else {
slot_p->hs_info.cn_state = DDI_HP_CN_STATE_POWERED;
}
}
void
pciehpc_set_slot_name(pcie_hp_ctrl_t *ctrl_p)
{
pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
uchar_t *slotname_data;
int *slotnum;
uint_t count;
int len;
int invalid_slotnum = 0;
uint32_t slot_capabilities;
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 {
slot_capabilities = pciehpc_reg_get32(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCAP);
slot_p->hs_phy_slot_num =
PCIE_SLOTCAP_PHY_SLOT_NUM(slot_capabilities);
}
if (!slot_p->hs_phy_slot_num) {
PCIE_DBG("%s#%d: Invalid slot number!\n",
ddi_driver_name(ctrl_p->hc_dip),
ddi_get_instance(ctrl_p->hc_dip));
slot_p->hs_phy_slot_num = pciehpc_reg_get8(ctrl_p,
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;
if (ddi_getlongprop(DDI_DEV_T_ANY, ctrl_p->hc_dip, DDI_PROP_DONTPASS,
"slot-names", (caddr_t)&slotname_data, &len) == DDI_PROP_SUCCESS) {
char tmp_name[256];
(void) snprintf(tmp_name, sizeof (tmp_name),
(char *)slotname_data + 4);
slot_p->hs_info.cn_name = ddi_strdup(tmp_name, KM_SLEEP);
kmem_free(slotname_data, len);
} else {
if (invalid_slotnum) {
slot_p->hs_info.cn_name = ddi_strdup("pcie0",
KM_SLEEP);
} else {
char tmp_name[256];
(void) snprintf(tmp_name, sizeof (tmp_name), "pcie%d",
slot_p->hs_phy_slot_num);
slot_p->hs_info.cn_name = ddi_strdup(tmp_name,
KM_SLEEP);
}
}
}
uint8_t
pciehpc_reg_get8(pcie_hp_ctrl_t *ctrl_p, uint_t off)
{
if (ctrl_p->hc_regops.get != NULL) {
return ((uint8_t)ctrl_p->hc_regops.get(
ctrl_p->hc_regops.cookie, (off_t)off));
} else {
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
return (pci_config_get8(bus_p->bus_cfg_hdl, off));
}
}
uint16_t
pciehpc_reg_get16(pcie_hp_ctrl_t *ctrl_p, uint_t off)
{
if (ctrl_p->hc_regops.get != NULL) {
return ((uint16_t)ctrl_p->hc_regops.get(
ctrl_p->hc_regops.cookie, (off_t)off));
} else {
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
return (pci_config_get16(bus_p->bus_cfg_hdl, off));
}
}
uint32_t
pciehpc_reg_get32(pcie_hp_ctrl_t *ctrl_p, uint_t off)
{
if (ctrl_p->hc_regops.get != NULL) {
return ((uint32_t)ctrl_p->hc_regops.get(
ctrl_p->hc_regops.cookie, (off_t)off));
} else {
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
return (pci_config_get32(bus_p->bus_cfg_hdl, off));
}
}
void
pciehpc_reg_put8(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint8_t val)
{
if (ctrl_p->hc_regops.put != NULL) {
ctrl_p->hc_regops.put(ctrl_p->hc_regops.cookie,
(off_t)off, (uint_t)val);
} else {
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
pci_config_put8(bus_p->bus_cfg_hdl, off, val);
}
}
void
pciehpc_reg_put16(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint16_t val)
{
if (ctrl_p->hc_regops.put != NULL) {
ctrl_p->hc_regops.put(ctrl_p->hc_regops.cookie,
(off_t)off, (uint_t)val);
} else {
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
pci_config_put16(bus_p->bus_cfg_hdl, off, val);
}
}
void
pciehpc_reg_put32(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint32_t val)
{
if (ctrl_p->hc_regops.put != NULL) {
ctrl_p->hc_regops.put(ctrl_p->hc_regops.cookie,
(off_t)off, (uint_t)val);
} else {
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
pci_config_put32(bus_p->bus_cfg_hdl, off, val);
}
}
static uint8_t
pciehpc_led_state_to_pcie(pcie_hp_led_state_t state)
{
switch (state) {
case PCIE_HP_LED_ON:
return (PCIE_SLOTCTL_INDICATOR_STATE_ON);
case PCIE_HP_LED_BLINK:
return (PCIE_SLOTCTL_INDICATOR_STATE_BLINK);
case PCIE_HP_LED_OFF:
default:
return (PCIE_SLOTCTL_INDICATOR_STATE_OFF);
}
}
static pcie_hp_led_state_t
pciehpc_calc_logical_led(pcie_hp_slot_t *slot_p, pciehpc_led_plat_id_t led)
{
pcie_hp_led_state_t ret = PCIE_HP_LED_OFF;
for (uint_t i = PCIEHPC_LED_NSTATES; i > 0; i--) {
const uint_t idx = i - 1;
if (!slot_p->hs_led_plat_en[idx])
continue;
switch (slot_p->hs_led_plat_conf[idx].plps_acts[led]) {
case PCIE_HLA_PASS:
continue;
case PCIE_HLA_OFF:
return (PCIE_HP_LED_OFF);
case PCIE_HLA_ON:
return (PCIE_HP_LED_ON);
case PCIE_HLA_BLINK:
return (PCIE_HP_LED_BLINK);
}
}
return (ret);
}
static void
pciehpc_sync_leds_to_hw(pcie_hp_slot_t *slot_p)
{
pcie_hp_led_state_t power, attn;
uint16_t orig_ctrl, ctrl;
pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
if (slot_p->hs_power_usr_ovr) {
power = slot_p->hs_power_usr_ovr_state;
} else {
power = pciehpc_calc_logical_led(slot_p, PCIEHPC_PLAT_ID_POWER);
}
if (slot_p->hs_attn_usr_ovr) {
attn = slot_p->hs_attn_usr_ovr_state;
} else {
attn = pciehpc_calc_logical_led(slot_p, PCIEHPC_PLAT_ID_ATTN);
}
slot_p->hs_power_led_state = power;
slot_p->hs_attn_led_state = attn;
orig_ctrl = ctrl = pciehpc_reg_get16(ctrl_p, bus_p->bus_pcie_off +
PCIE_SLOTCTL);
ctrl = pcie_slotctl_attn_indicator_set(ctrl,
pciehpc_led_state_to_pcie(attn));
ctrl = pcie_slotctl_pwr_indicator_set(ctrl,
pciehpc_led_state_to_pcie(power));
if (orig_ctrl != ctrl) {
pciehpc_issue_hpc_command(ctrl_p, ctrl);
}
}
static int
pciehpc_hpc_init(pcie_hp_ctrl_t *ctrl_p)
{
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
uint16_t reg;
reg = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL);
reg &= ~(PCIE_SLOTCTL_INTR_MASK);
pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
PCIE_SLOTCTL, reg);
reg = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTSTS);
pciehpc_reg_put16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTSTS, reg);
return (DDI_SUCCESS);
}
static int
pciehpc_hpc_uninit(pcie_hp_ctrl_t *ctrl_p)
{
(void) pciehpc_disable_intr(ctrl_p);
return (DDI_SUCCESS);
}
void
pciehpc_led_init(pcie_hp_slot_t *slot_p)
{
int vid, did, subvid, subsys;
pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
slot_p->hs_power_usr_ovr = false;
slot_p->hs_attn_usr_ovr = false;
slot_p->hs_power_usr_ovr_state = PCIE_HP_LED_OFF;
slot_p->hs_attn_usr_ovr_state = PCIE_HP_LED_OFF;
bzero(slot_p->hs_led_plat_en, sizeof (slot_p->hs_led_plat_en));
slot_p->hs_led_plat_conf = pciehpc_std_leds;
vid = ddi_prop_get_int(DDI_DEV_T_ANY, ctrl_p->hc_dip, DDI_PROP_DONTPASS,
"vendor-id", PCI_EINVAL16);
did = ddi_prop_get_int(DDI_DEV_T_ANY, ctrl_p->hc_dip, DDI_PROP_DONTPASS,
"device-id", PCI_EINVAL16);
subvid = ddi_prop_get_int(DDI_DEV_T_ANY, ctrl_p->hc_dip,
DDI_PROP_DONTPASS, "subsystem-vendor-id", PCI_EINVAL16);
subsys = ddi_prop_get_int(DDI_DEV_T_ANY, ctrl_p->hc_dip,
DDI_PROP_DONTPASS, "subsystem-id", PCI_EINVAL16);
for (size_t i = 0; i < ARRAY_SIZE(pciehpc_led_plat_map); i++) {
if (vid == pciehpc_led_plat_map[i].plpm_vid &&
did == pciehpc_led_plat_map[i].plpm_did &&
subvid == pciehpc_led_plat_map[i].plpm_svid &&
subsys == pciehpc_led_plat_map[i].plpm_ssys) {
slot_p->hs_led_plat_conf =
pciehpc_led_plat_map[i].plpm_map;
break;
}
}
slot_p->hs_led_plat_en[PCIE_LL_BASE] = true;
}
bool
pciehpc_slot_kstat_init(pcie_hp_slot_t *slot_p)
{
int inst = ddi_get_instance(slot_p->hs_ctrl->hc_dip);
slot_p->hs_kstat = kstat_create_zone("pcie", inst,
slot_p->hs_info.cn_name, "controller", KSTAT_TYPE_NAMED,
sizeof (pciehpc_stat_data_t) / sizeof (kstat_named_t),
KSTAT_FLAG_PERSISTENT, GLOBAL_ZONEID);
if (slot_p->hs_kstat == NULL) {
PCIE_DBG("pciehpc_slot_kstat_init: %d:%s kstat_create failed",
inst, slot_p->hs_info.cn_name);
return (false);
}
slot_p->hs_stat_data = (pciehpc_stat_data_t *)slot_p->hs_kstat->ks_data;
kstat_named_init(&slot_p->hs_stat_data->psd_attnsw_count,
"attention_switch_count", KSTAT_DATA_UINT64);
kstat_named_init(&slot_p->hs_stat_data->psd_attnsw_ts,
"attention_switch_ts", KSTAT_DATA_UINT64);
kstat_named_init(&slot_p->hs_stat_data->psd_pwrflt_count,
"power_fault_count", KSTAT_DATA_UINT64);
kstat_named_init(&slot_p->hs_stat_data->psd_pwrflt_ts,
"power_fault_ts", KSTAT_DATA_UINT64);
kstat_named_init(&slot_p->hs_stat_data->psd_mrl_chg_count,
"mrl_change_count", KSTAT_DATA_UINT64);
kstat_named_init(&slot_p->hs_stat_data->psd_mrl_chg_ts,
"mrl_change_ts", KSTAT_DATA_UINT64);
kstat_named_init(&slot_p->hs_stat_data->psd_pres_chg_count,
"presence_change_count", KSTAT_DATA_UINT64);
kstat_named_init(&slot_p->hs_stat_data->psd_pres_chg_ts,
"presence_change_ts", KSTAT_DATA_UINT64);
kstat_named_init(&slot_p->hs_stat_data->psd_cmd_cpl_count,
"command_completion_count", KSTAT_DATA_UINT64);
kstat_named_init(&slot_p->hs_stat_data->psd_cmd_cpl_ts,
"command_completion_ts", KSTAT_DATA_UINT64);
kstat_named_init(&slot_p->hs_stat_data->psd_dll_chg_count,
"dll_active_change_count", KSTAT_DATA_UINT64);
kstat_named_init(&slot_p->hs_stat_data->psd_dll_chg_ts,
"dll_active_change_ts", KSTAT_DATA_UINT64);
kstat_named_init(&slot_p->hs_stat_data->psd_intr_count,
"interrupt_count", KSTAT_DATA_UINT64);
kstat_named_init(&slot_p->hs_stat_data->psd_intr_ts,
"interrupt_ts", KSTAT_DATA_UINT64);
kstat_install(slot_p->hs_kstat);
return (true);
}
static int
pciehpc_slotinfo_init(pcie_hp_ctrl_t *ctrl_p)
{
uint32_t slot_capabilities, link_capabilities;
pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
boolean_t have_child;
ndi_devi_enter(ctrl_p->hc_dip);
have_child = ddi_get_child(ctrl_p->hc_dip) != NULL;
ndi_devi_exit(ctrl_p->hc_dip);
mutex_enter(&ctrl_p->hc_mutex);
slot_p->hs_device_num = 0;
slot_p->hs_info.cn_type = DDI_HP_CN_TYPE_PCIE;
slot_p->hs_info.cn_type_str = (ctrl_p->hc_regops.get == NULL) ?
PCIE_NATIVE_HP_TYPE : PCIE_PROP_HP_TYPE;
slot_p->hs_info.cn_child = NULL;
slot_p->hs_minor =
PCI_MINOR_NUM(ddi_get_instance(ctrl_p->hc_dip),
slot_p->hs_device_num);
slot_p->hs_condition = AP_COND_UNKNOWN;
slot_capabilities = pciehpc_reg_get32(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCAP);
pciehpc_set_slot_name(ctrl_p);
ctrl_p->hc_has_attn = (slot_capabilities & PCIE_SLOTCAP_ATTN_BUTTON) ?
B_TRUE : B_FALSE;
ctrl_p->hc_has_mrl = (slot_capabilities & PCIE_SLOTCAP_MRL_SENSOR) ?
B_TRUE : B_FALSE;
ctrl_p->hc_has_pwr = (slot_capabilities &
PCIE_SLOTCAP_POWER_CONTROLLER) ? B_TRUE: B_FALSE;
ctrl_p->hc_has_emi_lock = (slot_capabilities &
PCIE_SLOTCAP_EMI_LOCK_PRESENT) ? B_TRUE : B_FALSE;
link_capabilities = pciehpc_reg_get32(ctrl_p,
bus_p->bus_pcie_off + PCIE_LINKCAP);
ctrl_p->hc_dll_active_rep = (link_capabilities &
PCIE_LINKCAP_DLL_ACTIVE_REP_CAPABLE) ? B_TRUE : B_FALSE;
if (ctrl_p->hc_dll_active_rep)
cv_init(&slot_p->hs_dll_active_cv, NULL, CV_DRIVER, NULL);
if (ctrl_p->hc_has_attn) {
PCIE_DBG("pciehpc_slotinfo_init: setting up ATTN button event "
"handler thread for slot %d\n", slot_p->hs_phy_slot_num);
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,
pciehpc_attn_btn_handler,
(void *)ctrl_p, 0, &p0, TS_RUN, minclsyspri);
slot_p->hs_attn_btn_thread_exit = B_FALSE;
}
pciehpc_led_init(slot_p);
slot_p->hs_info.cn_state = DDI_HP_CN_STATE_EMPTY;
pciehpc_get_slot_state(slot_p);
if (slot_p->hs_info.cn_state == DDI_HP_CN_STATE_POWERED &&
have_child) {
slot_p->hs_info.cn_state = DDI_HP_CN_STATE_ENABLED;
}
if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED)
slot_p->hs_led_plat_en[PCIE_LL_POWERED] = true;
if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_ENABLED)
slot_p->hs_condition = AP_COND_OK;
mutex_exit(&ctrl_p->hc_mutex);
if (!pciehpc_slot_kstat_init(slot_p)) {
(void) pciehpc_slotinfo_uninit(ctrl_p);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
void
pciehpc_slot_kstat_fini(pcie_hp_slot_t *slot_p)
{
if (slot_p->hs_kstat != NULL) {
kstat_delete(slot_p->hs_kstat);
slot_p->hs_kstat = NULL;
slot_p->hs_stat_data = NULL;
}
}
static int
pciehpc_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p)
{
pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
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("pciehpc_slotinfo_uninit: "
"waiting for ATTN thread exit\n");
cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
PCIE_DBG("pciehpc_slotinfo_uninit: 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);
}
if (ctrl_p->hc_dll_active_rep)
cv_destroy(&slot_p->hs_dll_active_cv);
if (slot_p->hs_info.cn_name)
kmem_free(slot_p->hs_info.cn_name,
strlen(slot_p->hs_info.cn_name) + 1);
pciehpc_slot_kstat_fini(slot_p);
return (DDI_SUCCESS);
}
static void
pciehpc_state_sync(void *arg)
{
pciehpc_sync_task_t *sync = arg;
pcie_hp_ctrl_t *ctrl_p = sync->pst_ctrl;
dev_info_t *dip = ctrl_p->hc_dip;
pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
mutex_enter(&ctrl_p->hc_mutex);
if (ctrl_p->hc_startup_sync == TASKQID_INVALID) {
mutex_exit(&ctrl_p->hc_mutex);
kmem_free(sync, sizeof (pciehpc_sync_task_t));
return;
}
if ((ctrl_p->hc_flags & PCIE_HP_SYNC_PENDING) == 0) {
goto done;
}
cmn_err(CE_NOTE, "pciehpc (%s%d): synchronizing state in slot %s to "
"0x%x", ddi_driver_name(dip), ddi_get_instance(dip),
slot_p->hs_info.cn_name, sync->pst_targ);
ASSERT3U(slot_p->hs_info.cn_state, ==, sync->pst_cur);
ctrl_p->hc_flags &= ~PCIE_HP_SYNC_PENDING;
ctrl_p->hc_flags |= PCIE_HP_SYNC_RUNNING;
mutex_exit(&ctrl_p->hc_mutex);
(void) ndi_hp_state_change_req(dip, slot_p->hs_info.cn_name,
sync->pst_targ, DDI_HP_REQ_SYNC);
mutex_enter(&ctrl_p->hc_mutex);
done:
ctrl_p->hc_flags &= ~PCIE_HP_SYNC_RUNNING;
ctrl_p->hc_startup_sync = TASKQID_INVALID;
mutex_exit(&ctrl_p->hc_mutex);
kmem_free(sync, sizeof (pciehpc_sync_task_t));
}
static void
pciehpc_dispatch_state_sync(pcie_hp_ctrl_t *ctrl_p, ddi_hp_cn_state_t targ)
{
pciehpc_sync_task_t *sync;
pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
sync = kmem_alloc(sizeof (pciehpc_sync_task_t), KM_SLEEP);
sync->pst_ctrl = ctrl_p;
sync->pst_targ = targ;
sync->pst_cur = slot_p->hs_info.cn_state;
ctrl_p->hc_flags |= PCIE_HP_SYNC_PENDING;
ctrl_p->hc_startup_sync = taskq_dispatch(system_taskq,
pciehpc_state_sync, sync, TQ_SLEEP);
}
static void
pciehpc_enable_state_sync_leds(pcie_hp_ctrl_t *ctrl_p)
{
pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
bzero(slot_p->hs_led_plat_en, sizeof (slot_p->hs_led_plat_en));
switch (slot_p->hs_info.cn_state) {
case DDI_HP_CN_STATE_ENABLED:
case DDI_HP_CN_STATE_POWERED:
slot_p->hs_led_plat_en[PCIE_LL_POWERED] = true;
slot_p->hs_led_plat_en[PCIE_LL_BASE] = true;
break;
case DDI_HP_CN_STATE_PRESENT:
case DDI_HP_CN_STATE_EMPTY:
slot_p->hs_led_plat_en[PCIE_LL_BASE] = true;
break;
default:
dev_err(ctrl_p->hc_dip, CE_PANIC, "encountered invalid "
"connector state: 0x%x", slot_p->hs_info.cn_state);
break;
}
pciehpc_sync_leds_to_hw(slot_p);
}
static void
pciehpc_enable_state_sync(pcie_hp_ctrl_t *ctrl_p)
{
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
uint16_t control, status;
ddi_hp_cn_state_t curr_state, online_targ;
online_targ = (pcie_auto_online != 0) ? DDI_HP_CN_STATE_ENABLED :
DDI_HP_CN_STATE_PRESENT;
ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
curr_state = slot_p->hs_info.cn_state;
control = pciehpc_reg_get16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTCTL);
status = pciehpc_reg_get16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTSTS);
if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
switch (curr_state) {
case DDI_HP_CN_STATE_ENABLED:
pciehpc_dispatch_state_sync(ctrl_p,
DDI_HP_CN_STATE_EMPTY);
break;
case DDI_HP_CN_STATE_EMPTY:
case DDI_HP_CN_STATE_PRESENT:
case DDI_HP_CN_STATE_POWERED:
if (ctrl_p->hc_has_pwr &&
(control & PCIE_SLOTCTL_PWR_CONTROL) == 0) {
slot_p->hs_info.cn_state =
DDI_HP_CN_STATE_POWERED;
pciehpc_dispatch_state_sync(ctrl_p,
DDI_HP_CN_STATE_EMPTY);
} else {
slot_p->hs_info.cn_state =
DDI_HP_CN_STATE_EMPTY;
pciehpc_enable_state_sync_leds(ctrl_p);
}
break;
default:
dev_err(ctrl_p->hc_dip, CE_PANIC, "encountered invalid "
"connector state: 0x%x", curr_state);
break;
}
return;
}
if (ctrl_p->hc_has_pwr && (control & PCIE_SLOTCTL_PWR_CONTROL) != 0) {
switch (curr_state) {
case DDI_HP_CN_STATE_EMPTY:
pciehpc_dispatch_state_sync(ctrl_p, online_targ);
break;
case DDI_HP_CN_STATE_PRESENT:
if (curr_state == online_targ) {
pciehpc_enable_state_sync_leds(ctrl_p);
break;
}
pciehpc_dispatch_state_sync(ctrl_p, online_targ);
break;
case DDI_HP_CN_STATE_POWERED:
dev_err(ctrl_p->hc_dip, CE_WARN, "device powered off "
"somehow from prior powered state, attempting "
"recovery");
slot_p->hs_info.cn_state = DDI_HP_CN_STATE_PRESENT;
if (online_targ > DDI_HP_CN_STATE_PRESENT) {
pciehpc_dispatch_state_sync(ctrl_p,
online_targ);
} else {
pciehpc_enable_state_sync_leds(ctrl_p);
}
break;
case DDI_HP_CN_STATE_ENABLED:
dev_err(ctrl_p->hc_dip, CE_PANIC, "device powered off "
"somehow from prior enabled state unable to "
"recover");
break;
default:
dev_err(ctrl_p->hc_dip, CE_PANIC, "encountered invalid "
"connector state: 0x%x", curr_state);
}
return;
}
switch (curr_state) {
case DDI_HP_CN_STATE_ENABLED:
pciehpc_enable_state_sync_leds(ctrl_p);
break;
case DDI_HP_CN_STATE_POWERED:
case DDI_HP_CN_STATE_EMPTY:
case DDI_HP_CN_STATE_PRESENT:
if (curr_state == online_targ) {
pciehpc_enable_state_sync_leds(ctrl_p);
} else {
pciehpc_dispatch_state_sync(ctrl_p, online_targ);
}
break;
default:
dev_err(ctrl_p->hc_dip, CE_PANIC, "encountered invalid "
"connector state: 0x%x", curr_state);
}
}
static int
pciehpc_enable_intr(pcie_hp_ctrl_t *ctrl_p)
{
pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
uint16_t reg;
uint16_t intr_mask = PCIE_SLOTCTL_INTR_MASK;
mutex_enter(&ctrl_p->hc_mutex);
if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED)
intr_mask &= ~PCIE_SLOTCTL_PWR_FAULT_EN;
reg = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL);
pciehpc_reg_put16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL,
reg | (intr_mask & ~PCIE_SLOTCTL_HP_INTR_EN));
reg = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTSTS);
pciehpc_reg_put16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTSTS, reg);
reg = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL);
pciehpc_reg_put16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL,
reg | intr_mask);
pciehpc_enable_state_sync(ctrl_p);
mutex_exit(&ctrl_p->hc_mutex);
return (DDI_SUCCESS);
}
static int
pciehpc_disable_intr(pcie_hp_ctrl_t *ctrl_p)
{
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
uint16_t reg;
reg = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL);
reg &= ~(PCIE_SLOTCTL_INTR_MASK);
pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTCTL, reg);
reg = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTSTS);
pciehpc_reg_put16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTSTS, reg);
return (DDI_SUCCESS);
}
static pcie_hp_ctrl_t *
pciehpc_create_controller(dev_info_t *dip)
{
pcie_hp_ctrl_t *ctrl_p;
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
ctrl_p = kmem_zalloc(sizeof (pcie_hp_ctrl_t), KM_SLEEP);
ctrl_p->hc_dip = dip;
ctrl_p->hc_slots[0] = kmem_zalloc(sizeof (pcie_hp_slot_t), KM_SLEEP);
ctrl_p->hc_slots[0]->hs_num = 0;
ctrl_p->hc_slots[0]->hs_ctrl = ctrl_p;
mutex_init(&ctrl_p->hc_mutex, NULL, MUTEX_DRIVER,
(void *)PCIE_INTR_PRI);
cv_init(&ctrl_p->hc_cmd_comp_cv, NULL, CV_DRIVER, NULL);
ctrl_p->hc_cmd_pending = B_FALSE;
bus_p->bus_hp_curr_mode = PCIE_NATIVE_HP_MODE;
PCIE_SET_HP_CTRL(dip, ctrl_p);
return (ctrl_p);
}
static void
pciehpc_destroy_controller(dev_info_t *dip)
{
pcie_hp_ctrl_t *ctrl_p;
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
return;
PCIE_SET_HP_CTRL(dip, NULL);
bus_p->bus_hp_curr_mode = PCIE_NONE_HP_MODE;
mutex_destroy(&ctrl_p->hc_mutex);
cv_destroy(&ctrl_p->hc_cmd_comp_cv);
kmem_free(ctrl_p->hc_slots[0], sizeof (pcie_hp_slot_t));
kmem_free(ctrl_p, sizeof (pcie_hp_ctrl_t));
}
static int
pciehpc_register_slot(pcie_hp_ctrl_t *ctrl_p)
{
pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
dev_info_t *dip = ctrl_p->hc_dip;
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("pciehpc_register_slot(): registered slot %d\n",
slot_p->hs_phy_slot_num);
return (DDI_SUCCESS);
}
static int
pciehpc_unregister_slot(pcie_hp_ctrl_t *ctrl_p)
{
pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
dev_info_t *dip = ctrl_p->hc_dip;
pcie_hp_delete_occupant_props(dip, makedevice(ddi_driver_major(dip),
slot_p->hs_minor));
if (ndi_hp_unregister(dip, slot_p->hs_info.cn_name) != NDI_SUCCESS) {
PCIE_DBG("pciehpc_unregister_slot() "
"failed to unregister slot %d\n", slot_p->hs_phy_slot_num);
return (DDI_FAILURE);
}
PCIE_DBG("pciehpc_unregister_slot(): unregistered slot %d\n",
slot_p->hs_phy_slot_num);
return (DDI_SUCCESS);
}
static pciehpc_slot_power_t
pciehpc_slot_power_state(pcie_hp_slot_t *slot_p)
{
pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
uint16_t control, status;
pciehpc_slot_power_t state = 0;
ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
if (!ctrl_p->hc_has_pwr) {
return (PSP_NO_CONTROLLER);
} else {
state |= PSP_HAS_CONTROLLER;
}
control = pciehpc_reg_get16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTCTL);
status = pciehpc_reg_get16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTSTS);
if ((control & PCIE_SLOTCTL_PWR_CONTROL) != 0)
state |= PSP_OFF;
if ((status & PCIE_SLOTSTS_PWR_FAULT_DETECTED) != 0)
state |= PSP_FAULT;
return (state);
}
static boolean_t
pciehpc_slot_wait_for_active(pcie_hp_slot_t *slot_p)
{
pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
if (ctrl_p->hc_dll_active_rep) {
clock_t deadline;
uint16_t status;
status = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_LINKSTS);
deadline = ddi_get_lbolt() +
SEC_TO_TICK(PCIE_HP_DLL_STATE_CHANGE_TIMEOUT);
while ((status & PCIE_LINKSTS_DLL_LINK_ACTIVE) == 0 &&
ddi_get_lbolt() < deadline) {
(void) cv_timedwait(&slot_p->hs_dll_active_cv,
&ctrl_p->hc_mutex, deadline);
status = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off +
PCIE_LINKSTS);
}
if ((status & PCIE_LINKSTS_DLL_LINK_ACTIVE) == 0) {
return (B_FALSE);
}
} else {
delay(drv_usectohz(1000000));
}
return (B_TRUE);
}
static int
pciehpc_slot_noctrl_active(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result)
{
pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
VERIFY3U(ctrl_p->hc_has_pwr, ==, B_FALSE);
ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
pciehpc_get_slot_state(slot_p);
if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
return (DDI_FAILURE);
}
if (!pciehpc_slot_wait_for_active(slot_p)) {
cmn_err(CE_WARN, "pciehpc_slot_poweron_noctrl (slot %d): "
"device failed to become active", slot_p->hs_phy_slot_num);
return (DDI_FAILURE);
}
pciehpc_get_slot_state(slot_p);
*result = slot_p->hs_info.cn_state;
if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) {
slot_p->hs_led_plat_en[PCIE_LL_POWERED] = true;
slot_p->hs_led_plat_en[PCIE_LL_POWER_FAULT] = false;
slot_p->hs_led_plat_en[PCIE_LL_PROBE_FAILED] = false;
pciehpc_sync_leds_to_hw(slot_p);
return (DDI_SUCCESS);
} else {
return (DDI_FAILURE);
}
}
static int
pciehpc_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result)
{
pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
uint16_t status, control;
ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
if (!ctrl_p->hc_has_pwr) {
PCIE_DBG("pciehpc_slot_poweron (slot %d): no power control "
"capability, but was asked to power on\n",
slot_p->hs_phy_slot_num);
return (DDI_FAILURE);
}
control = pciehpc_reg_get16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTCTL);
status = pciehpc_reg_get16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTSTS);
if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
PCIE_DBG("pciehpc_slot_poweron (slot %d): slot is empty\n",
slot_p->hs_phy_slot_num);
goto cleanup;
}
if ((ctrl_p->hc_has_mrl) && (status & PCIE_SLOTSTS_MRL_SENSOR_OPEN)) {
cmn_err(CE_WARN, "pciehpc_slot_poweron (slot %d): MRL switch "
"is open", slot_p->hs_phy_slot_num);
goto cleanup;
}
if ((control & PCIE_SLOTCTL_PWR_CONTROL) == 0 &&
slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
PCIE_DBG("pciehpc_slot_poweron (slot %d): controller is "
"already enabled in SW state %d; continuing\n",
slot_p->hs_phy_slot_num, slot_p->hs_info.cn_state);
goto alreadyon;
}
if ((control & PCIE_SLOTCTL_PWR_CONTROL) != 0 &&
slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) {
cmn_err(CE_WARN, "pciehpc_slot_poweron (slot %d): SW state is "
"already %d but power controller is disabled; continuing",
slot_p->hs_phy_slot_num, slot_p->hs_info.cn_state);
}
slot_p->hs_led_plat_en[PCIE_LL_POWER_TRANSITION] = true;
alreadyon:
slot_p->hs_led_plat_en[PCIE_LL_PROBE_FAILED] = false;
slot_p->hs_led_plat_en[PCIE_LL_POWER_FAULT] = false;
pciehpc_sync_leds_to_hw(slot_p);
control = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL);
control &= ~PCIE_SLOTCTL_PWR_CONTROL;
pciehpc_issue_hpc_command(ctrl_p, control);
if (!pciehpc_slot_wait_for_active(slot_p))
goto cleanup;
control = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL);
if (control & PCIE_SLOTCTL_PWR_CONTROL) {
PCIE_DBG("pciehpc_slot_poweron (slot %d): power controller "
"enable was disabled autonomously after SW enable",
slot_p->hs_phy_slot_num);
goto cleanup;
}
status = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTSTS);
status |= PCIE_SLOTSTS_PWR_FAULT_DETECTED;
pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTSTS,
status);
control |= PCIE_SLOTCTL_PWR_FAULT_EN;
pciehpc_issue_hpc_command(ctrl_p, control);
slot_p->hs_led_plat_en[PCIE_LL_POWER_TRANSITION] = false;
slot_p->hs_led_plat_en[PCIE_LL_POWERED] = true;
pciehpc_sync_leds_to_hw(slot_p);
if (ctrl_p->hc_has_emi_lock) {
status = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTSTS);
if (!(status & PCIE_SLOTSTS_EMI_LOCK_SET)) {
control = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL);
control |= PCIE_SLOTCTL_EMI_LOCK_CONTROL;
pciehpc_issue_hpc_command(ctrl_p, control);
delay(drv_usectohz(1000000));
}
}
*result = slot_p->hs_info.cn_state = DDI_HP_CN_STATE_POWERED;
return (DDI_SUCCESS);
cleanup:
control = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL);
if ((control & PCIE_SLOTCTL_PWR_CONTROL) == 0) {
control |= PCIE_SLOTCTL_PWR_CONTROL;
pciehpc_issue_hpc_command(ctrl_p, control);
}
slot_p->hs_led_plat_en[PCIE_LL_POWER_TRANSITION] = false;
slot_p->hs_led_plat_en[PCIE_LL_POWERED] = false;
pciehpc_sync_leds_to_hw(slot_p);
return (DDI_FAILURE);
}
static int
pciehpc_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result)
{
pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
uint16_t status, control;
ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
if (!ctrl_p->hc_has_pwr) {
PCIE_DBG("pciehpc_slot_poweroff (slot %d): no power control "
"capability, but was asked to power off\n",
slot_p->hs_phy_slot_num);
return (DDI_ENOTSUP);
}
if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
PCIE_DBG("pciehpc_slot_poweroff (slot %d): SW state is "
"already %d; continuing\n",
slot_p->hs_phy_slot_num, slot_p->hs_info.cn_state);
}
control = pciehpc_reg_get16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTCTL);
status = pciehpc_reg_get16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTSTS);
if ((control & PCIE_SLOTCTL_PWR_CONTROL) != 0 &&
slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) {
cmn_err(CE_WARN, "pciehpc_slot_poweroff (slot %d): SW state is "
"%d but power controller is already disabled; continuing",
slot_p->hs_phy_slot_num, slot_p->hs_info.cn_state);
goto alreadyoff;
}
if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
PCIE_DBG("pciehpc_slot_poweroff (slot %d): powering off "
"empty slot\n", slot_p->hs_phy_slot_num);
}
slot_p->hs_led_plat_en[PCIE_LL_POWER_TRANSITION] = true;
alreadyoff:
slot_p->hs_led_plat_en[PCIE_LL_PROBE_FAILED] = false;
pciehpc_sync_leds_to_hw(slot_p);
control = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL);
control &= ~PCIE_SLOTCTL_PWR_FAULT_EN;
pciehpc_issue_hpc_command(ctrl_p, control);
control = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL);
control |= PCIE_SLOTCTL_PWR_CONTROL;
pciehpc_issue_hpc_command(ctrl_p, control);
control = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL);
if ((control & PCIE_SLOTCTL_PWR_CONTROL) == 0) {
cmn_err(CE_WARN, "pciehpc_slot_poweroff (slot %d): power "
"controller completed our disable command but is still "
"enabled", slot_p->hs_phy_slot_num);
slot_p->hs_led_plat_en[PCIE_LL_POWER_TRANSITION] = false;
slot_p->hs_led_plat_en[PCIE_LL_POWERED] = true;
pciehpc_sync_leds_to_hw(slot_p);
return (DDI_FAILURE);
}
slot_p->hs_led_plat_en[PCIE_LL_POWER_TRANSITION] = false;
slot_p->hs_led_plat_en[PCIE_LL_POWERED] = false;
pciehpc_sync_leds_to_hw(slot_p);
if (ctrl_p->hc_has_emi_lock) {
status = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTSTS);
if (status & PCIE_SLOTSTS_EMI_LOCK_SET) {
control = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL);
control |= PCIE_SLOTCTL_EMI_LOCK_CONTROL;
pciehpc_issue_hpc_command(ctrl_p, control);
delay(drv_usectohz(1000000));
}
}
pciehpc_get_slot_state(slot_p);
*result = slot_p->hs_info.cn_state;
return (DDI_SUCCESS);
}
static int
pciehpc_slot_probe(pcie_hp_slot_t *slot_p)
{
pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
int ret = DDI_SUCCESS;
mutex_enter(&ctrl_p->hc_mutex);
slot_p->hs_led_plat_en[PCIE_LL_PROBE_FAILED] = false;
pciehpc_sync_leds_to_hw(slot_p);
pciehpc_get_slot_state(slot_p);
PCIE_DISABLE_ERRORS(ctrl_p->hc_dip);
ret = pcie_hp_probe(slot_p);
if (ret != DDI_SUCCESS) {
PCIE_DBG("pciehpc_slot_probe() failed\n");
slot_p->hs_led_plat_en[PCIE_LL_PROBE_FAILED] = true;
pciehpc_sync_leds_to_hw(slot_p);
mutex_exit(&ctrl_p->hc_mutex);
return (DDI_FAILURE);
}
PCIE_ENABLE_ERRORS(ctrl_p->hc_dip);
pciehpc_get_slot_state(slot_p);
mutex_exit(&ctrl_p->hc_mutex);
return (DDI_SUCCESS);
}
static int
pciehpc_slot_unprobe(pcie_hp_slot_t *slot_p)
{
pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
int ret;
mutex_enter(&ctrl_p->hc_mutex);
pciehpc_get_slot_state(slot_p);
PCIE_DISABLE_ERRORS(ctrl_p->hc_dip);
ret = pcie_hp_unprobe(slot_p);
if (ret != DDI_SUCCESS) {
PCIE_DBG("pciehpc_slot_unprobe() failed\n");
PCIE_ENABLE_ERRORS(ctrl_p->hc_dip);
mutex_exit(&ctrl_p->hc_mutex);
return (DDI_FAILURE);
}
pciehpc_get_slot_state(slot_p);
mutex_exit(&ctrl_p->hc_mutex);
return (DDI_SUCCESS);
}
static int
pciehpc_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;
pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
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:
pciehpc_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:
if (!ctrl_p->hc_has_pwr) {
pciehpc_get_slot_state(slot_p);
curr_state = slot_p->hs_info.cn_state;
if (curr_state < DDI_HP_CN_STATE_POWERED)
rv = DDI_FAILURE;
break;
}
rv = (ctrl_p->hc_ops.poweron_hpc_slot)(slot_p,
&curr_state);
break;
case DDI_HP_CN_STATE_POWERED:
if ((ctrl_p->hc_flags & PCIE_HP_SYNC_RUNNING) != 0 &&
ctrl_p->hc_has_pwr) {
rv = (ctrl_p->hc_ops.poweron_hpc_slot)(slot_p,
&curr_state);
if (rv != DDI_SUCCESS) {
slot_p->hs_info.cn_state =
DDI_HP_CN_STATE_PRESENT;
break;
}
} else if (!ctrl_p->hc_has_pwr) {
rv = pciehpc_slot_noctrl_active(slot_p,
&curr_state);
if (rv != DDI_SUCCESS)
break;
}
curr_state = slot_p->hs_info.cn_state =
DDI_HP_CN_STATE_ENABLED;
break;
default:
ASSERT("unknown devinfo state");
}
}
return (rv);
}
static void
pciehpc_downgrade_slot_leds(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t state)
{
switch (state) {
case DDI_HP_CN_STATE_EMPTY:
slot_p->hs_led_plat_en[PCIE_LL_POWERED] = false;
slot_p->hs_led_plat_en[PCIE_LL_PROBE_FAILED] = false;
slot_p->hs_led_plat_en[PCIE_LL_POWER_FAULT] = false;
break;
case DDI_HP_CN_STATE_PRESENT:
slot_p->hs_led_plat_en[PCIE_LL_POWERED] = false;
slot_p->hs_led_plat_en[PCIE_LL_PROBE_FAILED] = false;
break;
default:
break;
}
pciehpc_sync_leds_to_hw(slot_p);
}
static int
pciehpc_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:
pciehpc_get_slot_state(slot_p);
curr_state = slot_p->hs_info.cn_state;
if (curr_state >= DDI_HP_CN_STATE_PRESENT) {
rv = DDI_FAILURE;
} else {
pciehpc_downgrade_slot_leds(slot_p, curr_state);
}
break;
case DDI_HP_CN_STATE_POWERED:
if (!slot_p->hs_ctrl->hc_has_pwr) {
pciehpc_get_slot_state(slot_p);
curr_state = slot_p->hs_info.cn_state;
if (curr_state >= DDI_HP_CN_STATE_POWERED) {
rv = DDI_FAILURE;
} else {
pciehpc_downgrade_slot_leds(slot_p,
curr_state);
}
break;
}
rv = (slot_p->hs_ctrl->hc_ops.poweroff_hpc_slot)(
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:
ASSERT("unknown devinfo state");
}
}
return (rv);
}
static int
pciehpc_change_slot_state(pcie_hp_slot_t *slot_p,
ddi_hp_cn_state_t target_state)
{
ddi_hp_cn_state_t curr_state;
pciehpc_slot_power_t pwr_state;
boolean_t sync = B_FALSE;
int rv = 0;
ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));
pciehpc_get_slot_state(slot_p);
curr_state = slot_p->hs_info.cn_state;
pwr_state = pciehpc_slot_power_state(slot_p);
if ((slot_p->hs_ctrl->hc_flags & PCIE_HP_SYNC_PENDING) != 0 &&
slot_p->hs_info.cn_state == DDI_HP_CN_STATE_POWERED) {
sync = B_TRUE;
slot_p->hs_ctrl->hc_flags |= PCIE_HP_SYNC_RUNNING;
}
slot_p->hs_ctrl->hc_flags &= ~PCIE_HP_SYNC_PENDING;
if (pwr_state == PSP_NO_CONTROLLER)
goto skip_sync;
switch (target_state) {
case DDI_HP_CN_STATE_EMPTY:
case DDI_HP_CN_STATE_PRESENT:
if ((pwr_state & PSP_OFF) == 0 &&
curr_state < DDI_HP_CN_STATE_POWERED) {
curr_state = DDI_HP_CN_STATE_POWERED;
}
break;
case DDI_HP_CN_STATE_POWERED:
case DDI_HP_CN_STATE_ENABLED:
if ((pwr_state & PSP_OFF) != 0 &&
curr_state >= DDI_HP_CN_STATE_POWERED) {
curr_state = DDI_HP_CN_STATE_PRESENT;
}
break;
default:
break;
}
slot_p->hs_info.cn_state = curr_state;
skip_sync:
if (curr_state == target_state) {
return (DDI_SUCCESS);
}
if (curr_state < target_state) {
rv = pciehpc_upgrade_slot_state(slot_p, target_state);
} else {
rv = pciehpc_downgrade_slot_state(slot_p, target_state);
}
if (sync) {
slot_p->hs_ctrl->hc_flags &= ~PCIE_HP_SYNC_RUNNING;
}
return (rv);
}
typedef struct pciehpc_prop {
const char *prop_name;
const char *prop_value;
bool (*prop_valid)(const char *);
int (*prop_get)(pcie_hp_slot_t *, const char **);
void (*prop_set)(pcie_hp_slot_t *, const char *);
} pciehpc_prop_t;
static bool
pciehpc_prop_led_valid(const char *value)
{
return (strcmp(value, PCIEHPC_PROP_VALUE_ON) == 0 ||
strcmp(value, PCIEHPC_PROP_VALUE_OFF) == 0 ||
strcmp(value, PCIEHPC_PROP_VALUE_BLINK) == 0 ||
strcmp(value, PCIEHPC_PROP_VALUE_DEFAULT) == 0);
}
static int
pciehpc_prop_card_get(pcie_hp_slot_t *slot_p, const char **value_p)
{
pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
ddi_acc_handle_t handle;
dev_info_t *cdip;
uint8_t prog_class, base_class, sub_class;
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) {
return (DDI_ENOTSUP);
}
if (pci_config_setup(cdip, &handle) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
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);
*value_p = PCIEHPC_PROP_VALUE_UNKNOWN;
for (size_t 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_p = class_pci[i].short_desc;
break;
}
}
return (DDI_SUCCESS);
}
static int
pciehpc_prop_board_get(pcie_hp_slot_t *slot_p, const char **value_p)
{
if (slot_p->hs_info.cn_state <= DDI_HP_CN_STATE_EMPTY) {
*value_p = PCIEHPC_PROP_VALUE_UNKNOWN;
} else {
*value_p = PCIEHPC_PROP_VALUE_PCIHOTPLUG;
}
return (DDI_SUCCESS);
}
static int
pciehpc_prop_slot_get(pcie_hp_slot_t *slot_p, const char **value_p)
{
*value_p = pcie_slot_condition_text(slot_p->hs_condition);
return (DDI_SUCCESS);
}
static int
pciehpc_prop_led_get_power(pcie_hp_slot_t *slot_p, const char **value_p)
{
if (slot_p->hs_power_usr_ovr) {
*value_p = pcie_led_state_text(slot_p->hs_power_usr_ovr_state);
} else {
*value_p = PCIEHPC_PROP_VALUE_DEFAULT;
}
return (DDI_SUCCESS);
}
static int
pciehpc_prop_led_get_attn(pcie_hp_slot_t *slot_p, const char **value_p)
{
if (slot_p->hs_attn_usr_ovr) {
*value_p = pcie_led_state_text(slot_p->hs_attn_usr_ovr_state);
} else {
*value_p = PCIEHPC_PROP_VALUE_DEFAULT;
}
return (DDI_SUCCESS);
}
static void
pciehpc_prop_led_set_common(bool *ovr, pcie_hp_led_state_t *state,
const char *val)
{
if (strcmp(val, PCIEHPC_PROP_VALUE_DEFAULT) == 0) {
*ovr = false;
return;
}
*ovr = true;
if (strcmp(val, PCIEHPC_PROP_VALUE_ON) == 0) {
*state = PCIE_HP_LED_ON;
} else if (strcmp(val, PCIEHPC_PROP_VALUE_OFF) == 0) {
*state = PCIE_HP_LED_OFF;
} else if (strcmp(val, PCIEHPC_PROP_VALUE_BLINK) == 0) {
*state = PCIE_HP_LED_BLINK;
}
}
static void
pciehpc_prop_led_set_attn(pcie_hp_slot_t *slot_p, const char *value)
{
pciehpc_prop_led_set_common(&slot_p->hs_attn_usr_ovr,
&slot_p->hs_attn_usr_ovr_state, value);
}
static void
pciehpc_prop_led_set_power(pcie_hp_slot_t *slot_p, const char *value)
{
pciehpc_prop_led_set_common(&slot_p->hs_power_usr_ovr,
&slot_p->hs_power_usr_ovr_state, value);
}
static pciehpc_prop_t pciehpc_props[] = {
{ PCIEHPC_PROP_LED_POWER, PCIEHPC_PROP_VALUE_LED_DEF,
pciehpc_prop_led_valid, pciehpc_prop_led_get_power,
pciehpc_prop_led_set_power },
{ PCIEHPC_PROP_LED_ATTN, PCIEHPC_PROP_VALUE_LED_DEF,
pciehpc_prop_led_valid, pciehpc_prop_led_get_attn,
pciehpc_prop_led_set_attn },
{ PCIEHPC_PROP_CARD_TYPE, PCIEHPC_PROP_VALUE_TYPE, NULL,
pciehpc_prop_card_get, NULL },
{ PCIEHPC_PROP_BOARD_TYPE, PCIEHPC_PROP_VALUE_TYPE, NULL,
pciehpc_prop_board_get, NULL },
{ PCIEHPC_PROP_SLOT_CONDITION, PCIEHPC_PROP_VALUE_TYPE, NULL,
pciehpc_prop_slot_get, NULL },
};
static bool
pciehpc_slot_prop_copyin(uintptr_t arg, ddi_hp_property_t *prop)
{
switch (ddi_model_convert_from(get_udatamodel())) {
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32: {
ddi_hp_property32_t prop32;
if (ddi_copyin((void *)arg, &prop32, sizeof (prop32), 0) != 0) {
return (false);
}
bzero(prop, sizeof (prop));
prop->nvlist_buf = (void *)(uintptr_t)prop32.nvlist_buf;
prop->buf_size = prop32.buf_size;
break;
}
#endif
case DDI_MODEL_NONE:
if (ddi_copyin((void *)arg, prop, sizeof (*prop), 0) != 0) {
return (false);
}
break;
default:
return (false);
}
return (true);
}
static bool
pciehpc_slot_prop_copyout(uintptr_t dest, const ddi_hp_property_t *prop)
{
switch (ddi_model_convert_from(get_udatamodel())) {
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32: {
ddi_hp_property32_t prop32;
if ((uintptr_t)prop->nvlist_buf > UINT32_MAX ||
prop->buf_size > UINT32_MAX) {
return (false);
}
bzero(&prop32, sizeof (prop32));
prop32.nvlist_buf = (caddr32_t)(uintptr_t)prop->nvlist_buf;
prop32.buf_size = (uint32_t)prop->buf_size;
if (ddi_copyout(&prop32, (void *)dest, sizeof (prop32), 0) !=
0) {
return (false);
}
break;
}
#endif
case DDI_MODEL_NONE:
if (ddi_copyout(prop, (void *)dest, sizeof (ddi_hp_property_t),
0) != 0) {
return (false);
}
break;
default:
return (false);
}
return (true);
}
int
pciehpc_slot_get_property(pcie_hp_slot_t *slot_p, uintptr_t arg, uintptr_t rval)
{
ddi_hp_property_t request, result;
pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
nvlist_t *prop_list;
nvlist_t *prop_rlist;
nvpair_t *prop_pair;
int ret = DDI_SUCCESS;
boolean_t get_all_prop = B_FALSE;
if (!pciehpc_slot_prop_copyin(arg, &request) ||
!pciehpc_slot_prop_copyin(rval, &result))
return (false);
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;
}
prop_pair = nvlist_next_nvpair(prop_list, NULL);
if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair)) {
const char *name = nvpair_name(prop_pair);
if (strcmp(name, PCIEHPC_PROP_ALL) == 0) {
(void) nvlist_remove_all(prop_list, PCIEHPC_PROP_ALL);
for (size_t i = 0; i < ARRAY_SIZE(pciehpc_props); i++) {
if (nvlist_add_string(prop_list,
pciehpc_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) {
(void) nvlist_remove_all(prop_list, PCIEHPC_PROP_HELP);
for (size_t i = 0; i < ARRAY_SIZE(pciehpc_props); i++) {
if (nvlist_add_string(prop_rlist,
pciehpc_props[i].prop_name,
pciehpc_props[i].prop_value) != 0) {
ret = DDI_FAILURE;
goto get_prop_cleanup1;
}
}
}
}
mutex_enter(&ctrl_p->hc_mutex);
pciehpc_get_slot_state(slot_p);
prop_pair = NULL;
while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) {
const char *name = nvpair_name(prop_pair);
const pciehpc_prop_t *prop = NULL;
const char *value = NULL;
for (size_t i = 0; i < ARRAY_SIZE(pciehpc_props); i++) {
if (strcmp(pciehpc_props[i].prop_name, name) == 0) {
prop = &pciehpc_props[i];
break;
}
}
if (prop == NULL) {
PCIE_DBG("Unsupported property: %s\n", name);
ret = DDI_EINVAL;
goto get_prop_cleanup2;
}
ret = prop->prop_get(slot_p, &value);
if (ret != DDI_SUCCESS &&
(get_all_prop && ret != DDI_ENOTSUP)) {
goto get_prop_cleanup2;
}
if (nvlist_add_string(prop_rlist, name, value) != 0) {
ret = DDI_FAILURE;
goto get_prop_cleanup2;
}
}
if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf,
&result.buf_size)) != DDI_SUCCESS) {
goto get_prop_cleanup2;
}
if (!pciehpc_slot_prop_copyout(rval, &result)) {
ret = DDI_FAILURE;
}
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
pciehpc_slot_set_property(pcie_hp_slot_t *slot_p, uintptr_t arg,
uintptr_t rval)
{
ddi_hp_property_t request, result;
pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
nvlist_t *prop_list;
nvlist_t *prop_rlist;
nvpair_t *prop_pair;
int ret = DDI_SUCCESS;
if (!pciehpc_slot_prop_copyin(arg, &request) ||
!pciehpc_slot_prop_copyin(rval, &result))
return (false);
if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size,
&prop_list)) != DDI_SUCCESS)
return (ret);
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;
}
for (size_t i = 0; i < ARRAY_SIZE(pciehpc_props); i++) {
if (pciehpc_props[i].prop_valid == NULL)
continue;
if (nvlist_add_string(prop_rlist,
pciehpc_props[i].prop_name,
pciehpc_props[i].prop_value) != 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 (!pciehpc_slot_prop_copyout(rval, &result)) {
ret = DDI_FAILURE;
}
set_prop_cleanup1:
nvlist_free(prop_rlist);
nvlist_free(prop_list);
return (ret);
}
prop_pair = NULL;
while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) {
const pciehpc_prop_t *prop;
char *value;
const char *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;
}
prop = NULL;
for (size_t i = 0; i < ARRAY_SIZE(pciehpc_props); i++) {
if (strcmp(pciehpc_props[i].prop_name, name) == 0) {
prop = &pciehpc_props[i];
break;
}
}
if (prop == NULL) {
PCIE_DBG("Unsupported property: %s\n", name);
ret = DDI_EINVAL;
goto set_prop_cleanup;
}
if (prop->prop_valid == NULL) {
PCIE_DBG("Read only property: %s\n", name);
ret = DDI_ENOTSUP;
goto set_prop_cleanup;
} else if (!prop->prop_valid(value)) {
PCIE_DBG("Unsupported value ('%s') for property: %s\n",
value, name);
ret = DDI_EINVAL;
goto set_prop_cleanup;
}
}
mutex_enter(&ctrl_p->hc_mutex);
pciehpc_get_slot_state(slot_p);
prop_pair = NULL;
while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) {
const char *name = nvpair_name(prop_pair);
const pciehpc_prop_t *prop = NULL;
char *value;
(void) nvpair_value_string(prop_pair, &value);
for (size_t i = 0; i < ARRAY_SIZE(pciehpc_props); i++) {
if (strcmp(pciehpc_props[i].prop_name, name) == 0) {
prop = &pciehpc_props[i];
break;
}
}
ASSERT3P(prop, !=, NULL);
prop->prop_set(slot_p, value);
}
pciehpc_sync_leds_to_hw(slot_p);
if (rval != 0) {
result.buf_size = 0;
if (!pciehpc_slot_prop_copyout(rval, &result)) {
ret = DDI_FAILURE;
}
}
mutex_exit(&ctrl_p->hc_mutex);
set_prop_cleanup:
nvlist_free(prop_list);
return (ret);
}
static void
pciehpc_issue_hpc_command(pcie_hp_ctrl_t *ctrl_p, uint16_t control)
{
pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
uint16_t status;
uint32_t slot_cap;
slot_cap = pciehpc_reg_get32(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCAP);
if ((slot_cap & PCIE_SLOTCAP_NO_CMD_COMP_SUPP) ||
(bus_p->bus_hp_curr_mode == PCIE_ACPI_HP_MODE)) {
pciehpc_reg_put16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL, control);
return;
}
if (!(ctrl_p->hc_flags & PCIE_HP_INITIALIZED_FLAG)) {
int retry = PCIE_HP_CMD_WAIT_RETRY;
pciehpc_reg_put16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL, control);
while (retry--) {
delay(drv_usectohz(PCIE_HP_CMD_WAIT_TIME));
status = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTSTS);
if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
pciehpc_reg_put16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTSTS, status);
break;
}
}
return;
}
ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
while (ctrl_p->hc_cmd_pending == B_TRUE)
cv_wait(&ctrl_p->hc_cmd_comp_cv, &ctrl_p->hc_mutex);
pciehpc_reg_put16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL, control);
ctrl_p->hc_cmd_pending = B_TRUE;
if (cv_timedwait(&ctrl_p->hc_cmd_comp_cv, &ctrl_p->hc_mutex,
ddi_get_lbolt() + SEC_TO_TICK(1)) == -1) {
PCIE_DBG("pciehpc_issue_hpc_command: Command Complete"
" interrupt is not received for slot %d\n",
slot_p->hs_phy_slot_num);
status = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTSTS);
if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
pciehpc_reg_put16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTSTS, status);
}
}
ctrl_p->hc_cmd_pending = B_FALSE;
cv_signal(&ctrl_p->hc_cmd_comp_cv);
}
static void
pciehpc_attn_btn_handler(pcie_hp_ctrl_t *ctrl_p)
{
pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
callb_cpr_t cprinfo;
PCIE_DBG("pciehpc_attn_btn_handler: thread started\n");
CALLB_CPR_INIT(&cprinfo, &ctrl_p->hc_mutex, callb_generic_cpr,
"pciehpc_attn_btn_handler");
mutex_enter(&ctrl_p->hc_mutex);
cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
while (slot_p->hs_attn_btn_thread_exit == B_FALSE) {
if (slot_p->hs_attn_btn_pending == B_TRUE) {
slot_p->hs_led_plat_en[PCIE_LL_ATTENTION_BUTTON] = true;
pciehpc_sync_leds_to_hw(slot_p);
if (cv_timedwait(&slot_p->hs_attn_btn_cv,
&ctrl_p->hc_mutex,
ddi_get_lbolt() + SEC_TO_TICK(5)) == -1) {
if (slot_p->hs_attn_btn_pending == B_TRUE) {
int hint;
slot_p->hs_attn_btn_pending = B_FALSE;
pciehpc_get_slot_state(slot_p);
if (slot_p->hs_info.cn_state <=
DDI_HP_CN_STATE_PRESENT) {
hint = SE_INCOMING_RES;
} else {
hint = SE_OUTGOING_RES;
}
pcie_hp_gen_sysevent_req(
slot_p->hs_info.cn_name,
hint,
ctrl_p->hc_dip,
KM_SLEEP);
}
}
slot_p->hs_led_plat_en[PCIE_LL_ATTENTION_BUTTON] =
false;
pciehpc_sync_leds_to_hw(slot_p);
continue;
}
cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
}
PCIE_DBG("pciehpc_attn_btn_handler: thread exit\n");
cv_signal(&slot_p->hs_attn_btn_cv);
CALLB_CPR_EXIT(&cprinfo);
thread_exit();
}
static pcie_hp_led_state_t
pciehpc_led_state_to_hpc(uint16_t state)
{
switch (state) {
case PCIE_SLOTCTL_INDICATOR_STATE_ON:
return (PCIE_HP_LED_ON);
case PCIE_SLOTCTL_INDICATOR_STATE_BLINK:
return (PCIE_HP_LED_BLINK);
case PCIE_SLOTCTL_INDICATOR_STATE_OFF:
default:
return (PCIE_HP_LED_OFF);
}
}
static void
pciehpc_handle_power_fault(dev_info_t *dip)
{
ndi_hold_devi(dip);
if (taskq_dispatch(system_taskq, pciehpc_power_fault_handler, dip,
TQ_NOSLEEP) == TASKQID_INVALID) {
ndi_rele_devi(dip);
PCIE_DBG("pciehpc_intr(): "
"Failed to dispatch power fault handler, dip %p\n", dip);
}
}
static void
pciehpc_power_fault_handler(void *arg)
{
dev_info_t *dip = (dev_info_t *)arg;
pcie_hp_ctrl_t *ctrl_p;
pcie_hp_slot_t *slot_p;
if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL) {
ndi_rele_devi(dip);
return;
}
slot_p = ctrl_p->hc_slots[0];
(void) ndi_hp_state_change_req(dip,
slot_p->hs_info.cn_name,
DDI_HP_CN_STATE_PRESENT, DDI_HP_REQ_SYNC);
mutex_enter(&ctrl_p->hc_mutex);
slot_p->hs_led_plat_en[PCIE_LL_POWER_FAULT] = true;
pciehpc_sync_leds_to_hw(slot_p);
mutex_exit(&ctrl_p->hc_mutex);
ndi_rele_devi(dip);
}
#ifdef DEBUG
static void
pciehpc_dump_hpregs(pcie_hp_ctrl_t *ctrl_p)
{
pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
uint16_t control;
uint32_t capabilities;
if (!pcie_debug_flags)
return;
capabilities = pciehpc_reg_get32(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCAP);
control = pciehpc_reg_get16(ctrl_p,
bus_p->bus_pcie_off + PCIE_SLOTCTL);
PCIE_DBG("pciehpc_dump_hpregs: Found PCI-E hot plug slot %d\n",
slot_p->hs_phy_slot_num);
PCIE_DBG("Attention Button Present = %s\n",
capabilities & PCIE_SLOTCAP_ATTN_BUTTON ? "Yes":"No");
PCIE_DBG("Power controller Present = %s\n",
capabilities & PCIE_SLOTCAP_POWER_CONTROLLER ? "Yes":"No");
PCIE_DBG("MRL Sensor Present = %s\n",
capabilities & PCIE_SLOTCAP_MRL_SENSOR ? "Yes":"No");
PCIE_DBG("Attn Indicator Present = %s\n",
capabilities & PCIE_SLOTCAP_ATTN_INDICATOR ? "Yes":"No");
PCIE_DBG("Power Indicator Present = %s\n",
capabilities & PCIE_SLOTCAP_PWR_INDICATOR ? "Yes":"No");
PCIE_DBG("HotPlug Surprise = %s\n",
capabilities & PCIE_SLOTCAP_HP_SURPRISE ? "Yes":"No");
PCIE_DBG("HotPlug Capable = %s\n",
capabilities & PCIE_SLOTCAP_HP_CAPABLE ? "Yes":"No");
PCIE_DBG("Physical Slot Number = %d\n",
PCIE_SLOTCAP_PHY_SLOT_NUM(capabilities));
PCIE_DBG("Attn Button interrupt Enabled = %s\n",
control & PCIE_SLOTCTL_ATTN_BTN_EN ? "Yes":"No");
PCIE_DBG("Power Fault interrupt Enabled = %s\n",
control & PCIE_SLOTCTL_PWR_FAULT_EN ? "Yes":"No");
PCIE_DBG("MRL Sensor INTR Enabled = %s\n",
control & PCIE_SLOTCTL_MRL_SENSOR_EN ? "Yes":"No");
PCIE_DBG("Presence interrupt Enabled = %s\n",
control & PCIE_SLOTCTL_PRESENCE_CHANGE_EN ? "Yes":"No");
PCIE_DBG("Cmd Complete interrupt Enabled = %s\n",
control & PCIE_SLOTCTL_CMD_INTR_EN ? "Yes":"No");
PCIE_DBG("HotPlug interrupt Enabled = %s\n",
control & PCIE_SLOTCTL_HP_INTR_EN ? "Yes":"No");
PCIE_DBG("Power Indicator LED = %s", pcie_led_state_text(
pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control))));
PCIE_DBG("Attn Indicator LED = %s\n",
pcie_led_state_text(pciehpc_led_state_to_hpc(
pcie_slotctl_attn_indicator_get(control))));
}
#endif