/*
* 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
* 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.
*/
/*
* This file contains PCI HotPlug functionality that is compatible with the
* PCI SHPC specification 1.x.
*
*/
#include <sys/autoconf.h>
#include <sys/ddi_impldefs.h>
#include <sys/ndi_impldefs.h>
#include <sys/pci_impl.h>
typedef struct pcishpc_prop {
char *prop_name;
char *prop_value;
};
/* reset delay to 1 sec. */
/* Local function prototype */
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);
#ifdef DEBUG
#endif /* DEBUG */
/*
*/
/*
* 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
{
int i;
PCIE_DBG("pcishpc_init() called from %s#%d\n",
PCIE_DBG("pcishpc_init() shpc instance already "
"initialized!\n");
return (DDI_SUCCESS);
}
/* Initialize soft state structure for the SHPC instance. */
PCIE_DBG("pcishpc_init() failed to create shpc softstate\n");
return (DDI_FAILURE);
}
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",
for (i = 0; i < 0x100; i += 4) {
PCIE_DBG("SHPC Cfg reg 0x%02x: %08x\n", i,
}
#endif /* DEBUG */
/* Setup each HotPlug slot on this SHPC controller. */
for (i = 0; i < ctrl_p->hc_num_slots_impl; i++) {
PCIE_DBG("pcishpc_init() failed to register "
"slot %d\n", i);
goto cleanup1;
}
PCIE_DBG("pcishpc_init() failed to create "
"minor node for slot %d\n", i);
goto cleanup1;
}
}
#ifdef DEBUG
/* Dump out the SHPC registers. */
#endif /* DEBUG */
return (DDI_SUCCESS);
for (i = 0; i < ctrl_p->hc_num_slots_impl; i++) {
continue;
}
(void) pci_resource_destroy(dip);
(void) pcishpc_destroy_controller(dip);
return (DDI_FAILURE);
}
/*
* pcishpc_uninit()
* Unload the HogPlug controller driver and deallocate all resources.
*/
int
{
int i;
if (!ctrl_p) {
PCIE_DBG("pcishpc_uninit() Unable to find softstate\n");
return (DDI_FAILURE);
}
for (i = 0; i < PCIE_HP_MAX_SLOTS; i++) {
continue;
}
/*
* Destroy resource maps for this bus node.
*/
(void) pci_resource_destroy(dip);
(void) pcishpc_destroy_controller(dip);
return (DDI_SUCCESS);
}
/*
* pcishpc_intr()
*
* This is the SHPC controller interrupt handler.
*/
int
{
int slot;
PCIE_DBG("pcishpc_intr() called\n");
/* get the soft state structure for this dip */
return (DDI_INTR_UNCLAIMED);
PCIE_DBG("pcishpc_intr() unclaimed\n");
return (DDI_INTR_UNCLAIMED);
}
PCIE_DBG("pcishpc_intr() interrupt received\n");
if (reg & PCI_HP_SERR_INT_CMD_COMPLETE_IRQ) {
PCIE_DBG("pcishpc_intr() "
"PCI_HP_SERR_INT_CMD_COMPLETE_IRQ detected\n");
}
if (reg & PCI_HP_SERR_INT_ARBITER_IRQ) {
PCIE_DBG("pcishpc_intr() PCI_HP_SERR_INT_ARBITER_IRQ "
"detected\n");
}
/* Write back the SERR INT register to acknowledge the IRQs. */
/* Check for slot events that might have occured. */
PCIE_DBG("pcishpc_intr() slot %d and "
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: "
/*
* if ATTN button event is still pending
* then cancel it
*/
/* wake up the ATTN event handler */
}
if (reg & PCI_HP_SLOT_MRL_DETECTED)
PCIE_DBG("slot %d: "
if (reg & PCI_HP_SLOT_POWER_DETECTED)
PCIE_DBG("slot %d: "
/* Acknoledge any slot interrupts */
reg);
}
}
PCIE_DBG("pcishpc_intr() claimed\n");
return (DDI_INTR_CLAIMED);
}
int
{
#ifdef _SYSCALL32_IMPL
#endif
int i, n;
if (get_udatamodel() == DATAMODEL_NATIVE) {
return (DDI_FAILURE);
}
#ifdef _SYSCALL32_IMPL
else {
return (DDI_FAILURE);
}
#endif
&prop_list)) != DDI_SUCCESS)
return (ret);
ret = DDI_ENOMEM;
goto get_prop_cleanup;
}
/* check whether the requested property is "all" or "help" */
n = sizeof (pcishpc_props) / sizeof (pcishpc_prop_t);
/*
* 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++) {
ret = DDI_FAILURE;
goto get_prop_cleanup1;
}
}
/*
* Empty the request list, and add help strings into the
* return list. We will pass the following for loop.
*/
for (i = 0; i < n; i++) {
pcishpc_props[i].prop_value) != 0) {
ret = DDI_FAILURE;
goto get_prop_cleanup1;
}
}
}
}
/* get the current slot state */
/* for each requested property, get the value and add it to nvlist */
int i;
/*
* 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;
}
ret = DDI_FAILURE;
goto get_prop_cleanup2;
}
for (i = 0; i < class_pci_items; i++) {
break;
}
}
if (i == class_pci_items)
else
} else {
/* unsupported property */
ret = DDI_ENOTSUP;
goto get_prop_cleanup2;
}
ret = DDI_FAILURE;
goto get_prop_cleanup2;
}
}
// pack nvlist and copyout
goto get_prop_cleanup2;
}
if (get_udatamodel() == DATAMODEL_NATIVE) {
ret = DDI_FAILURE;
goto get_prop_cleanup2;
}
}
#ifdef _SYSCALL32_IMPL
else {
ret = DDI_FAILURE;
} else {
sizeof (ddi_hp_property32_t)))
ret = DDI_FAILURE;
}
}
#endif
return (ret);
}
int
{
#ifdef _SYSCALL32_IMPL
#endif
if (get_udatamodel() == DATAMODEL_NATIVE) {
return (DDI_FAILURE);
if (rval &&
return (DDI_FAILURE);
}
#ifdef _SYSCALL32_IMPL
else {
return (DDI_FAILURE);
if (rval &&
return (DDI_FAILURE);
if (rval) {
}
}
#endif
&prop_list)) != DDI_SUCCESS)
return (ret);
/* check whether the requested property is "help" */
if (!rval) {
ret = DDI_ENOTSUP;
goto set_prop_cleanup;
}
ret = DDI_ENOMEM;
goto set_prop_cleanup;
}
PCIEHPC_PROP_VALUE_LED) != 0) {
ret = DDI_FAILURE;
goto set_prop_cleanup1;
}
goto set_prop_cleanup1;
}
if (get_udatamodel() == DATAMODEL_NATIVE) {
sizeof (ddi_hp_property_t))) {
ret = DDI_FAILURE;
goto set_prop_cleanup1;
}
}
#ifdef _SYSCALL32_IMPL
else {
ret = DDI_FAILURE;
goto set_prop_cleanup1;
} else {
sizeof (ddi_hp_property32_t))) {
ret = DDI_FAILURE;
goto set_prop_cleanup1;
}
}
}
#endif
return (ret);
}
/* Validate the request */
PCIE_DBG("Unexpected data type of setting "
"property %s.\n", name);
ret = DDI_EINVAL;
goto set_prop_cleanup;
}
PCIE_DBG("Get string value failed for property %s.\n",
name);
ret = DDI_FAILURE;
goto set_prop_cleanup;
}
PCIE_DBG("Unsupported value of setting "
"property %s\n", name);
ret = DDI_ENOTSUP;
goto set_prop_cleanup;
}
} else {
ret = DDI_ENOTSUP;
goto set_prop_cleanup;
}
}
/* get the current slot state */
// set each property
}
}
if (rval) {
if (get_udatamodel() == DATAMODEL_NATIVE) {
ret = DDI_FAILURE;
}
#ifdef _SYSCALL32_IMPL
else {
sizeof (ddi_hp_property32_t)))
ret = DDI_FAILURE;
}
#endif
}
return (ret);
}
/*
* pcishpc_hp_ops()
*
* Handle hotplug commands
*
* Note: This function is called by DDI HP framework at kernel context only
*/
/* ARGSUSED */
int
{
PCIE_DBG("pcishpc_hp_ops: dip=%p cn_name=%s op=%x arg=%p\n",
return (DDI_FAILURE);
== 0) {
/* Match with a physical slot, found */
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",
return (DDI_EINVAL);
}
switch (op) {
case DDI_HPOP_CN_GET_STATE:
{
/* get the current slot state */
break;
}
case DDI_HPOP_CN_CHANGE_STATE:
{
break;
}
case DDI_HPOP_CN_PROBE:
break;
case DDI_HPOP_CN_UNPROBE:
break;
case DDI_HPOP_CN_GET_PROPERTY:
break;
case DDI_HPOP_CN_SET_PROPERTY:
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 *
{
PCIE_DBG("pcishpc: create controller for %s#%d\n",
/* Init the shpc controller's mutex. */
/* HPC initialization is complete now */
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
{
/* Get the number of HotPlug slots implemented */
/*
* Initilize the current bus speed and number of hotplug slots
* currently connected.
*/
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.
*/
"vendor-id", -1) == 0x108e) &&
"device-id", -1) == 0x9010))
else
/* Get the first Physical device number. */
/* Check if the device numbers increase or decrease. */
ctrl_p->hc_has_mrl =
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
{
/* get the soft state structure for this dip */
PCIE_DBG("pcishpc_destroy_controller() not found\n");
return (DDI_FAILURE);
}
/*
* Deallocate the slot state structures for this controller.
*/
(void) pcishpc_destroy_slots(ctrl_p);
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 *
{
/* Allocate a new slot structure. */
/* Assign an initial value */
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
{
/* Setup the PCI device # for this SHPC slot. */
if (ctrl_p->hc_device_increases)
else
/* Setup the DDI HP framework slot information. */
/* 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);
}
/* setup the slot name (used for ap-id) */
/* register the slot with DDI HP framework */
PCIE_DBG("pciehpc_register_slot() failed to register slot %d\n",
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* pcishpc_destroy_slots()
*
* Free up all of the slot resources for this controller.
*/
static int
{
int i;
for (i = 0; i < PCIE_HP_MAX_SLOTS; i++) {
continue;
PCIE_DBG("pcishpc_destroy_slots: "
"waiting for ATTN thread exit\n");
PCIE_DBG("pcishpc_destroy_slots: "
"ATTN thread exit\n");
}
PCIE_DBG("pcishpc_destroy_slots() (shpc_p=%p)\n"
"destroyed", slot_p);
/* unregister the slot with DDI HP framework */
NDI_SUCCESS) {
PCIE_DBG("pcishpc_destroy_slots() "
"failed to unregister slot %d\n",
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
/*
* pcishpc_enable_irqs()
*
*/
int
{
int slot;
/* Enable all interrupts. */
/* Unmask the interrupts for each slot. */
reg &= ~(PCI_HP_SLOT_MASK_ALL |
} else {
reg &= ~(PCI_HP_SLOT_MASK_ALL);
}
}
PCIE_DBG("pcishpc_enable_irqs: ctrl_p 0x%p, "
"current bus speed 0x%x, slots connected 0x%x\n", ctrl_p,
return (DDI_SUCCESS);
}
/*
* pcishpc_disable_irqs()
*
*/
int
{
int slot;
/* Mask all interrupts. */
/* Unmask the interrupts for each slot. */
}
PCIE_DBG("pcishpc_disable_irqs: ctrl_p 0x%p, "
"current bus speed 0x%x, slots connected 0x%x\n", ctrl_p,
return (DDI_SUCCESS);
}
/*
* pcishpc_slot_poweron()
*
*
* Note: This function is called by DDI HP framework at kernel context only
*/
/*ARGSUSED*/
static int
{
PCIE_DBG("pcishpc_slot_poweron called()\n");
/* get the current slot state */
/* check if the slot is already in the 'enabled' state */
/* slot is already in the 'enabled' state */
PCIE_DBG("pcishpc_slot_poweron() slot %d already enabled\n",
return (DDI_SUCCESS);
}
PCIE_DBG("pcishpc_slot_poweron() slot in empty state\n");
goto cleanup;
}
/* make sure the MRL sensor is closed */
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 */
/* Turn all other LEDS off */
/* Set the bus speed only if the bus segment is not running */
PCIE_DBG("pcishpc_slot_poweron() setting speed failed\n");
goto cleanup;
}
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,
/* Mask or Unmask MRL Sensor SEER bit based on new slot state */
}
/* Update the hardware slot state. */
PCIE_DBG("pcishpc_slot_poweron() failed\n");
goto cleanup;
}
/* Update the current state. It will be used in pcishpc_setled() */
/* Turn the Power LED ON for a enabled slot. */
/* Turn all other LEDS off. */
/* delay after powerON to let the device initialize itself */
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.
*/
/* get the current slot state */
/*
* It should be poweron'ed now. Have a check here in case any
* hardware problems.
*/
PCIE_DBG("pcishpc_slot_poweron() failed after hardware"
" registers all programmed.\n");
goto cleanup;
}
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/*ARGSUSED*/
static int
{
PCIE_DBG("pcishpc_slot_poweroff called()\n");
/* get the current slot state */
/* check if the slot is not in the "enabled" or "powered" state */
/* slot is in the 'disabled' state */
PCIE_DBG("pcishpc_slot_poweroff(): "
return (DDI_SUCCESS);
}
/* Set the Power LED to blink */
/* Turn all other LEDS off */
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,
/* Mask or Unmask MRL Sensor SEER bit based on new slot state */
}
/* Update the hardware slot state. */
DDI_SUCCESS) {
PCIE_DBG("pcishpc_slot_poweroff() failed\n");
goto cleanup;
}
/* Update the current state. It will be used in pcishpc_setled() */
/* Turn the Power LED OFF for a disabled slot. */
/* Turn all other LEDS off. */
/* delay after powerON to let the device initialize itself */
/*
* It should be poweroff'ed now. Have a check here in case any
* hardware problems.
*/
PCIE_DBG("pcishpc_slot_poweroff() failed after hardware"
" registers all programmed.\n");
goto cleanup;
}
PCIE_DBG("pcishpc_slot_poweroff() success!\n");
return (DDI_SUCCESS);
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
{
PCIE_DBG("pcishpc_slot_probe called()\n");
/* get the current slot state */
/*
* Probe a given PCI Hotplug Connection (CN).
*/
PCIE_DBG("pcishpc_slot_probe() failed\n");
return (DDI_FAILURE);
}
PCIE_DBG("pcishpc_slot_probe() success!\n");
/* get the current slot state */
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
{
PCIE_DBG("pcishpc_slot_unprobe called()\n");
/* get the current slot state */
/*
* Unprobe a given PCI Hotplug Connection (CN).
*/
PCIE_DBG("pcishpc_slot_unprobe() failed\n");
return (DDI_FAILURE);
}
PCIE_DBG("pcishpc_slot_unprobe() success!\n");
/* get the current slot state */
return (DDI_SUCCESS);
}
static int
{
if (target_state > DDI_HP_CN_STATE_ENABLED) {
return (DDI_EINVAL);
}
switch (curr_state) {
case DDI_HP_CN_STATE_EMPTY:
/*
* From EMPTY to PRESENT, just check the hardware
* slot state.
*/
rv = DDI_FAILURE;
break;
case DDI_HP_CN_STATE_PRESENT:
break;
case DDI_HP_CN_STATE_POWERED:
break;
default:
/* should never reach here */
ASSERT("unknown devinfo state");
}
}
return (rv);
}
static int
{
switch (curr_state) {
case DDI_HP_CN_STATE_PRESENT:
/*
* From PRESENT to EMPTY, just check hardware
* slot state.
*/
if (curr_state >= DDI_HP_CN_STATE_PRESENT)
rv = DDI_FAILURE;
break;
case DDI_HP_CN_STATE_POWERED:
break;
case DDI_HP_CN_STATE_ENABLED:
break;
default:
/* should never reach here */
ASSERT("unknown devinfo state");
}
}
return (rv);
}
/* Change slot state to a target state */
static int
{
int rv;
if (curr_state == target_state) {
return (DDI_SUCCESS);
}
if (curr_state < target_state) {
} else {
}
return (rv);
}
/*
* pcishpc_issue_command()
*
* Sends a command to the SHPC controller.
*/
static int
{
int retCode;
/* Write the command to the SHPC controller. */
/* Wait until the SHPC controller processes the command. */
/* Make sure the command completed. */
if (retCode == DDI_SUCCESS) {
/* Did the command fail to generate the command complete IRQ? */
PCIE_DBG("pcishpc_issue_command() Failed on "
"generate cmd complete IRQ\n");
}
}
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
{
/* Wait until SHPC controller is NOT busy */
for (;;) {
/* Is there an MRL Sensor error? */
if ((status & PCI_HP_COMM_STS_ERR_MASK) ==
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) ==
PCIE_DBG("pcishpc_wait_busy() ERROR: Invalid "
"command error\n");
break;
}
if ((status & PCI_HP_COMM_STS_ERR_MASK) ==
PCIE_DBG("pcishpc_wait_busy() ERROR: Invalid "
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. */
}
return (DDI_FAILURE);
}
static void
{
PCIE_DBG("pcishpc_attn_btn_handler: thread started\n");
callb_generic_cpr, "pcishpc_attn_btn_handler");
/* wait for ATTN button event */
/* get the current state of power LED */
/* Blink the Power LED while we wait for 5 seconds */
/* wait for 5 seconds before taking any action */
/*
* It is a time out;
* make sure the ATTN pending flag is
* still ON before sending the event
* to DDI HP framework.
*/
int hint;
/* restore the power LED state */
(void) pcishpc_setled(slot_p,
/*
* send the ATTN button event
* to DDI HP framework
*/
/*
* Insertion.
*/
} else {
/*
* Want to remove;
*/
}
hint,
KM_SLEEP);
continue;
}
}
/* restore the power LED state */
continue;
}
/* wait for another ATTN button event */
}
PCIE_DBG("pcishpc_attn_btn_handler: thread exit\n");
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
{
/* Read the logical slot register for this Slot. */
/* Convert from the SHPC slot state to the HPC slot state. */
if (curr_state == DDI_HP_CN_STATE_POWERED &&
/*
* Keep POWERED state if it is currently POWERED state because
* 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.
*/
}
/* Convert from the SHPC Power LED state to the HPC Power LED state. */
/* Convert from the SHPC Attn LED state to the HPC Attn LED state. */
/* We don't have a fault LED so just default it to OFF. */
/* We don't have an active LED so just default it to OFF. */
}
/*
* pcishpc_set_slot_state()
*
* Updates the slot's state and leds.
*/
static int
{
/* Default all states to unchanged. */
/* Has the slot state changed? */
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. */
}
/* Has the Power LED state changed? */
PCIE_DBG("pcishpc_set_slot_state() Power LED State changed\n");
/* Set the new power led state in the Slot operation command. */
cmd_code |=
}
/* Has the Attn LED state changed? */
PCIE_DBG("pcishpc_set_slot_state() Attn LED State changed\n");
/* Set the new attn led state in the Slot operation command. */
cmd_code |=
}
}
/*
* setup slot name/slot-number info.
*/
static void
{
int *slotnum;
int len;
uchar_t *s;
int invalid_slotnum = 0;
} else {
if (ctrl_p->hc_device_increases)
else
PCIE_DBG("pcishpc_set_slot_name(): failed to "
"create phyical-slot#%d\n",
}
/* Platform may not have initialized it */
if (!slot_p->hs_phy_slot_num) {
invalid_slotnum = 1;
}
/*
* 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>"
*/
pci_id_bit = 1;
/*
* 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.
*/
/*
* Find the next bit set.
*/
while (!(bit_mask & pci_id_bit) &&
(pci_id_cnt < 32)) {
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 != NULL)
s++;
s++;
slots_before--;
}
KM_SLEEP);
return;
}
/* slot-names entry not found */
PCIE_DBG("pcishpc_set_slot_name(): "
"No slot-names entry found for slot #%d\n",
}
if (invalid_slotnum) {
} else {
}
}
/*
* pcishpc_set_bus_speed()
*
* Set the bus speed and mode.
*/
static int
{
int avail_slots;
/* Make sure that the slot is in a correct state */
/* Return failure if the slot is empty */
if ((status & 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 */
PCIE_DBG("pcishpc_set_bus_speed() failed: "
"incorrect slot state\n");
return (DDI_FAILURE);
}
/* Set the "power-only" mode for the slot */
PCIE_DBG("pcishpc_set_bus_speed() failed to set "
return (DDI_FAILURE);
}
/* Wait for power good */
/* Make sure that the slot is in "power-only" state */
PCIE_DBG("pcishpc_set_bus_speed() "
"power-only failed: incorrect slot state\n");
return (DDI_FAILURE);
}
/*
* 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) {
avail_slots = (slots_avail1_reg >>
break;
}
/* FALLTHROUGH */
avail_slots = (slots_avail1_reg >>
break;
}
/* FALLTHROUGH */
avail_slots = (slots_avail1_reg >>
break;
}
/* FALLTHROUGH */
default:
avail_slots = (slots_avail2_reg >>
if ((status & PCI_HP_SLOT_66MHZ_CONV_CAPABLE) &&
(curr_speed == PCI_HP_SBCR_66MHZ_CONV_SPEED))) {
} else {
avail_slots = (slots_avail1_reg >>
} else {
PCIE_DBG("pcishpc_set_bus_speed() "
" failed to set the bus speed, slot# %d\n",
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.
*/
}
/* Set the bus speed */
speed) == DDI_FAILURE) {
PCIE_DBG("pcishpc_set_bus_speed() failed "
return (DDI_FAILURE);
}
/* Check the current bus speed */
PCIE_DBG("pcishpc_set_bus_speed() an incorrect "
"bus speed, slot = 0x%x, speed = 0x%x\n",
return (DDI_FAILURE);
}
/* Save the current bus speed */
return (DDI_SUCCESS);
}
/*
* pcishpc_setled()
*
* Change the state of a slot's LED.
*/
static int
{
switch (led) {
case PCIE_HP_FAULT_LED:
PCIE_DBG("pcishpc_setled() - PCIE_HP_FAULT_LED "
break;
case PCIE_HP_POWER_LED:
PCIE_DBG("pcishpc_setled() - PCIE_HP_POWER_LED "
break;
case PCIE_HP_ATTN_LED:
PCIE_DBG("pcishpc_setled() - PCIE_HP_ATTN_LED "
break;
case PCIE_HP_ACTIVE_LED:
PCIE_DBG("pcishpc_setled() - PCIE_HP_ACTIVE_LED "
break;
}
}
/*
* pcishpc_led_shpc_to_hpc()
*
* Convert from SHPC indicator status to HPC indicator status.
*/
static int
{
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
{
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
{
if ((shpc_state & 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
{
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 *
{
/* 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 *
{
/* 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
{
/* Setup the SHPC dword select register. */
/* Read back the SHPC dword select register and verify. */
PCIE_DBG("pcishpc_read_reg() - Failed writing DWORD "
"select reg\n");
return (0xFFFFFFFF);
}
/* Read from the SHPC dword data register. */
}
/*
* pcishpc_write_reg()
*
* Write to a SHPC controller register.
*/
static void
{
/* Setup the SHPC dword select register. */
/* Read back the SHPC dword select register and verify. */
PCIE_DBG("pcishpc_write_reg() - Failed writing "
"DWORD select reg\n");
return;
}
/* Write to the SHPC dword data register. */
/*
* write to complete. This is probably not necessary, but it does
* help enforce ordering if there is an issue.
*/
}
#ifdef DEBUG
/*
* pcishpc_dump_regs()
*
* Dumps all of the SHPC controller registers.
*/
static void
{
char *state;
if (!pcie_debug_flags)
return;
PCIE_DBG("pcishpc_dump_regs() called:\n");
PCIE_DBG("==========================================================");
PCIE_DBG("SHPC Base Offset "
PCIE_DBG("Number of PCIX slots avail (33 Mhz) : %d\n",
(reg & 31));
PCIE_DBG("Number of PCIX slots avail (66 Mhz) : %d\n",
PCIE_DBG("Number of PCIX slots avail (100 Mhz) : %d\n",
PCIE_DBG("Number of PCIX slots avail (133 Mhz) : %d\n",
PCIE_DBG("Number of conventional PCI slots (66 Mhz) : %d\n",
(reg & 31));
PCIE_DBG("Number of Slots connected to this port : %d\n",
numSlots);
PCIE_DBG("PCI Device # for First HotPlug Slot : %d\n",
PCIE_DBG("Physical Slot # for First PCI Device # : %d\n",
PCIE_DBG("MRL Sensor Implemented : %s\n",
PCIE_DBG("Attention Button Implemented : %s\n",
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("SHPC Interrupt Message Number : %d\n",
PCIE_DBG("SHPC Programming Interface : %d\n",
PCIE_DBG("SHPC Command Code : %d\n",
(reg & 0xff));
PCIE_DBG("SHPC Target Slot : %d\n",
PCIE_DBG("SHPC Controller Busy : %s\n",
PCIE_DBG("SHPC Controller Err: MRL Sensor : %s\n",
PCIE_DBG("SHPC Controller Err: Invalid Command : %s\n",
PCIE_DBG("Command Completion Interrupt Pending : %s\n",
}
PCIE_DBG("Arbiter SERR Pending : %s\n",
PCIE_DBG("Slot %d SERR Pending : %s\n",
}
PCIE_DBG("Global Interrupt Mask : %s\n",
PCIE_DBG("Global SERR Mask : %s\n",
PCIE_DBG("Command Completion Interrupt Mask : %s\n",
PCIE_DBG("Arbiter SERR Mask : %s\n",
PCIE_DBG("Command Completion Detected : %s\n",
PCIE_DBG("Arbiter Timeout Detected : %s\n",
PCIE_DBG("------------------------------------\n");
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;
}
state);
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",
"No");
PCIE_DBG("Slot %d Isolated Power Fault Detected : %s\n",
"No");
PCIE_DBG("Slot %d Attention Button Press Detected : %s\n",
PCIE_DBG("Slot %d MRL Sensor Change Detected : %s\n",
PCIE_DBG("Slot %d Connected Power Fault Detected : %s\n",
PCIE_DBG("Slot %d Card Presence IRQ Masked : %s\n",
PCIE_DBG("Slot %d Isolated Power Fault IRQ Masked : %s\n",
PCIE_DBG("Slot %d Attention Button IRQ Masked : %s\n",
PCIE_DBG("Slot %d MRL Sensor IRQ Masked : %s\n",
PCIE_DBG("Slot %d Connected Power Fault IRQ Masked : %s\n",
PCIE_DBG("Slot %d MRL Sensor SERR Masked : %s\n",
PCIE_DBG("Slot %d Connected Power Fault SERR Masked : %s\n",
}
}
#endif /* DEBUG */