pcishpc.c revision 92e1ac0d7d0f8596dfc8b9e1302e1100e5b35efa
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* PCISHPC - The Standard PCI HotPlug Controller driver module. This driver
* can be used with PCI HotPlug controllers that are compatible
* with the PCI SHPC specification 1.x.
*/
#include <sys/autoconf.h>
#include <sys/ddi_impldefs.h>
#include <sys/ndi_impldefs.h>
/*
* SHPC controller registers accessed via the SHPC DWORD select and DATA
* registers in PCI configuration space relative to the SHPC capibility
* pointer.
*/
#define SHPC_BASE_OFFSET_REG 0x00
#define SHPC_SLOTS_AVAIL_I_REG 0x01
#define SHPC_SLOTS_AVAIL_II_REG 0x02
#define SHPC_SLOT_CONFIGURATION_REG 0x03
#define SHPC_PROF_IF_SBCR_REG 0x04
#define SHPC_COMMAND_STATUS_REG 0x05
#define SHPC_IRQ_LOCATOR_REG 0x06
#define SHPC_SERR_LOCATOR_REG 0x07
#define SHPC_CTRL_SERR_INT_REG 0x08
#define SHPC_LOGICAL_SLOT_REGS 0x09
#define SHPC_VENDOR_SPECIFIC 0x28
/* General Register bit weights for the 32-bit SHPC registers */
#define REG_BIT0 0x00000001
#define REG_BIT1 0x00000002
#define REG_BIT2 0x00000004
#define REG_BIT3 0x00000008
#define REG_BIT4 0x00000010
#define REG_BIT5 0x00000020
#define REG_BIT6 0x00000040
#define REG_BIT7 0x00000080
#define REG_BIT8 0x00000100
#define REG_BIT9 0x00000200
#define REG_BIT10 0x00000400
#define REG_BIT11 0x00000800
#define REG_BIT12 0x00001000
#define REG_BIT13 0x00002000
#define REG_BIT14 0x00004000
#define REG_BIT15 0x00008000
#define REG_BIT16 0x00010000
#define REG_BIT17 0x00020000
#define REG_BIT18 0x00040000
#define REG_BIT19 0x00080000
#define REG_BIT20 0x00100000
#define REG_BIT21 0x00200000
#define REG_BIT22 0x00400000
#define REG_BIT23 0x00800000
#define REG_BIT24 0x01000000
#define REG_BIT25 0x02000000
#define REG_BIT26 0x04000000
#define REG_BIT27 0x08000000
#define REG_BIT28 0x10000000
#define REG_BIT29 0x20000000
#define REG_BIT30 0x40000000
#define REG_BIT31 0x80000000
/* Register bits used with the SHPC SHPC_CTRL_SERR_INT_REG register */
#define SHPC_SERR_INT_ARBITER_IRQ REG_BIT17
/* Register bits used with the SHPC SHPC_LOGICAL_SLOT_REGS register */
#define SHPC_SLOT_MRL_STATE_MASK REG_BIT8
#define SHPC_SLOT_PRESENCE_DETECTED REG_BIT16
#define SHPC_SLOT_ISO_PWR_DETECTED REG_BIT17
#define SHPC_SLOT_ATTN_DETECTED REG_BIT18
#define SHPC_SLOT_MRL_DETECTED REG_BIT19
#define SHPC_SLOT_POWER_DETECTED REG_BIT20
#define SHPC_SLOT_PRESENCE_MASK REG_BIT24
#define SHPC_SLOT_ISO_PWR_MASK REG_BIT25
#define SHPC_SLOT_ATTN_MASK REG_BIT26
#define SHPC_SLOT_MRL_MASK REG_BIT27
#define SHPC_SLOT_POWER_MASK REG_BIT28
#define SHPC_SLOT_MRL_SERR_MASK REG_BIT29
#define SHPC_SLOT_POWER_SERR_MASK REG_BIT30
/* Register bits used with the SHPC SHPC_IRQ_LOCATOR_REG register. */
#define SHPC_IRQ_CMD_COMPLETE REG_BIT0
#define SHPC_IRQ_SLOT_N_PENDING REG_BIT1
/* Register bits used with the SHPC SHPC_SERR_LOCATOR_REG register. */
#define SHPC_IRQ_SERR_SLOT_N_PENDING REG_BIT1
/* Register bits used with the SHPC SHPC_SLOT_CONFIGURATION_REG register */
#define SHPC_SLOT_CONFIG_MRL_SENSOR REG_BIT30
#define SHPC_SLOT_CONFIG_PHY_SLOT_NUM_SHIFT 16
#define SHPC_SLOT_CONFIG_PHY_SLOT_NUM_MASK 0x3FF
/* Max PCISHPC controller slots */
#define MAX_SHPC_SLOTS 31
/* PCISHPC controller command complete delay in microseconds. */
#define SHPC_COMMAND_WAIT_TIME 10000
/* reset delay is 2^25 clock cycles as per PCI spec @33MHz. */
static int pcishpc_reset_delay = 0x1000000;
/* PCISHPC controller softstate structure */
typedef struct pcishpc_ctrl {
/* PCISHPC slot softstate structure */
typedef struct pcishpc {
} pcishpc_t;
/* mutex to protect the shpc_head and shpc_ctrl_head linked lists */
static kmutex_t pcishpc_list_mutex;
/* Pointer to a linked list of shpc slot softstate structures */
/* Pointer to a linked list of shpc controller softstate structures */
/* mutex to protect access to the controller */
static kmutex_t pcishpc_control_mutex;
/* SHPC static function prototypes */
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 state);
static int pcishpc_slot_hpc_to_shpc(int state);
static char *pcishpc_textrequest(int request);
static void pcishpc_debug(char *fmt, ...);
static int pcishpc_debug_enabled = 0;
/* Module operations information for the kernel */
extern struct mod_ops mod_miscops;
"PCI SHPC hotplug module v1.12",
};
/* Module linkage information for the kernel */
static struct modlinkage modlinkage = {
&modlmisc,
};
int
_init(void)
{
int rc;
return (rc);
}
/* Init the shpc driver list mutex. */
/* Init the shpc control mutex. */
pcishpc_debug("pcishpc: installed");
return (rc);
}
int
_fini(void)
{
pcishpc_debug("pcishpc: _fini called()");
/* XXX - to be fixed later */
return (EBUSY);
}
int
{
pcishpc_debug("pcishpc: _info called()");
}
/*
* pcishpc_create_controller()
*
* This function allocates and creates an SHPC controller state structure
* and adds it to the linked list of controllers.
*/
static pcishpc_ctrl_t *
{
pcishpc_debug("pcishpc: create controller for %s#%d",
/* Get the PCI BUS,DEVICE,FUNCTION for this SHPC controller. */
pcishpc_debug("pcishpc_create_controller() "
"Error: pcishpc_get_pci_info() failed");
return (NULL);
}
pcishpc_debug("pcishpc_create_controller() "
"Error: Unable to map SHPC PCI Config registers");
return (NULL);
}
/* Make sure the SHPC is listed in the PCI capibilities list. */
pcishpc_debug("pcishpc_create_controller() "
"Error: Unable to find SHPC controller");
return (NULL);
}
/* Init the interrupt mutex */
(void *)PCISHPC_INTR_PRI);
/* Interrupts are now enabled. */
/* Init the shpc controller's mutex. */
/* Insert new softstate into linked list of current soft states. */
pcishpc_debug("pcishpc_create_controller() success");
return (ctrl_p);
}
/*
* pcishpc_probe_controller()
*
* This function probes to make sure there is indeed an SHPC controller.
*/
static int
{
if (!(status & PCI_STAT_CAP)) {
return (DDI_FAILURE);
}
/* Get a pointer to the PCI capabilities list. */
cap_ptr &= 0xFC;
/* Walk PCI capabilities list searching for the SHPC capability. */
while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) {
pcishpc_debug("pcishpc_probe_controller() capability @ "
if (cap_id == PCI_CAP_ID_PCI_HOTPLUG) {
/* Save the SHPC register offset. */
/* Save the SHPC data register. */
break;
}
/* Get the pointer to the next capability. */
cap_ptr+1);
cap_ptr &= 0xFC;
}
if (cap_ptr == PCI_CAP_NEXT_PTR_NULL) {
return (DDI_FAILURE);
}
pcishpc_debug("pcishpc_probe_controller() Found SHPC capibility");
return (DDI_SUCCESS);
}
/*
* pcishpc_destroy_controller()
*
* This function deallocates all of the SHPC controller resources.
*/
static int
{
/* Walk the linked list of softstates. */
/*
* Deallocate the slot state structures for
* this controller.
*/
(void) pcishpc_destroy_slots(ctrl_p);
pcishpc_debug("pcishpc_destroy_controller() success");
return (DDI_SUCCESS);
}
}
pcishpc_debug("pcishpc_destroy_controller() not found");
return (DDI_FAILURE);
}
/*
* pcishpc_intr()
*
* This is the SHPC controller interrupt handler.
*/
int
{
int slot;
pcishpc_debug("pcishpc_intr() called");
pcishpc_debug("pcishpc_intr() interrupt received");
if (reg & SHPC_SERR_INT_CMD_COMPLETE_IRQ) {
pcishpc_debug("pcishpc_intr() "
"SHPC_SERR_INT_CMD_COMPLETE_IRQ detected");
}
if (reg & SHPC_SERR_INT_ARBITER_IRQ) {
pcishpc_debug("pcishpc_intr() SHPC_SERR_INT_ARBITER_IRQ"
" detected");
}
/* Write back the SERR INT register to acknowledge the IRQs. */
/* Check for slot events that might have occured. */
(SHPC_IRQ_SERR_SLOT_N_PENDING<<slot))) {
pcishpc_debug("pcishpc_intr() slot %d and "
/*
* Note that we will need to generate a
* slot event interrupt.
*/
slot_event = B_TRUE;
/* Record any pending slot interrupts/events. */
/* Acknoledge any slot interrupts */
}
}
if (slot_event == B_TRUE) {
pcishpc_debug("pcishpc_intr() slot(s) have event(s)");
(void) pcishpc_process_intr(ctrl_p);
} else {
pcishpc_debug("pcishpc_intr() No slot event(s)");
}
pcishpc_debug("pcishpc_intr() claimed");
return (DDI_INTR_CLAIMED);
}
pcishpc_debug("pcishpc_intr() unclaimed");
return (DDI_INTR_UNCLAIMED);
}
/*
* pcishpc_process_intr()
*
* This is the SHPC soft interrupt handler.
*/
static int
{
int slot;
pcishpc_debug("pcishpc_process_intr() called");
/* XXX - add event handling code here */
pcishpc_debug("slot %d: SHPC_SLOT_PRESENCE_DETECTED",
slot+1);
pcishpc_debug("slot %d: SHPC_SLOT_ISO_PWR_DETECTED",
slot+1);
pcishpc_debug("slot %d: SHPC_SLOT_ATTN_DETECTED",
slot+1);
/*
* if ATTN button event is still pending
* then cancel it
*/
/* wake up the ATTN event handler */
}
pcishpc_debug("slot %d: SHPC_SLOT_MRL_DETECTED",
slot+1);
pcishpc_debug("slot %d: SHPC_SLOT_POWER_DETECTED",
slot+1);
/* Clear the events now that we've processed all of them. */
}
return (DDI_INTR_CLAIMED);
}
/*
* pcishpc_get_controller()
*
* This function retrieves the hot plug SHPC controller soft state.
*/
static pcishpc_ctrl_t *
{
while (ctrl_p) {
break;
}
return (ctrl_p);
}
/*
* pcishpc_hpc_get_slot_state()
*
* This function retrieves the hot plug SHPC soft state from the
* the HPS framework slot handle.
*/
static pcishpc_t *
{
pcishpc_debug("pcishpc_hpc_get_slot_state() called (hpc_slot=%x)",
slot);
while (pcishpc_p) {
pcishpc_debug("pcishpc_hpc_get_slot_state() found "
"(pcishpc=%x)", pcishpc_p);
return (pcishpc_p);
}
}
return (NULL);
}
/*
* pcishpc_get_pci_info()
*
* Read the PCI Bus, PCI Device, and PCI function for the SHPC controller.
*/
static int
{
int reglen;
pcishpc_debug("pcishpc_get_pci_info() called");
!= DDI_SUCCESS) {
pcishpc_debug("pcishpc_get_pci_info() failed to get regspec.");
return (DDI_FAILURE);
}
pcishpc_debug("pcishpc_get_pci_info() %s%d: bus=%d, dev=%d, func=%d",
return (DDI_SUCCESS);
}
/*
* 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;
pcishpc_debug("pcishpc_init() called from %s#%d",
pcishpc_debug("pcishpc_init() shpc instance already "
"initialized!");
return (DDI_SUCCESS);
}
/* Initialize soft state structure for the SHPC instance. */
pcishpc_debug("pcishpc_init() failed to create shpc softstate");
return (DDI_FAILURE);
}
pcishpc_debug("pcishpc_init() failed to setup controller");
(void) pcishpc_destroy_controller(dip);
return (DDI_FAILURE);
}
#if 0
pcishpc_debug("%s%d: P2P bridge register dump:",
for (i = 0; i < 0x100; i += 4) {
pcishpc_debug("SHPC Cfg reg 0x%02x: %08x", i,
}
#endif
/* Setup each HotPlug slot on this SHPC controller. */
pcishpc_debug("pcishpc_init() failed to register "
"slot %d", i);
(void) pcishpc_destroy_controller(dip);
return (DDI_FAILURE);
}
}
(void) pcishpc_enable_irqs(ctrl_p);
if (pcishpc_debug_enabled) {
/* Dump out the SHPC registers. */
}
return (DDI_SUCCESS);
}
/*
* pcishpc_enable_irqs()
*
*/
static int
{
int slot;
/* Enable all interrupts. */
/* Unmask the interrupts for each slot. */
reg &= (~SHPC_SLOT_MASK_ALL);
}
return (DDI_SUCCESS);
}
/*
* pcishpc_disable_irqs()
*
*/
static int
{
int slot;
/* Mask all interrupts. */
/* Unmask the interrupts for each slot. */
}
return (DDI_SUCCESS);
}
/*
* 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->deviceIncreases)
else
/* Setup the HPS framework slot ops callbacks for the SHPC driver. */
/* Setup the HPS framework slot information. */
/* Do not auto enable the deivce in this slot. */
/* setup thread for handling ATTN button events */
pcishpc_debug("pcishpc_register_slot: "
"setting up ATTN button event "
"handler thread for slot %d\n", slot);
}
/* setup the slot name (used for ap-id) */
/* Register this SHPC slot with the HPS framework. */
pcishpc_debug("pcishpc_register_slot() faled to Register "
"slot");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* pcishpc_create_slot()
*
* Allocate and add a new HotPlug slot state structure to the linked list.
*/
static pcishpc_t *
{
/* Allocate a new slot structure. */
/* Insert new slot into linked list of current slots. */
pcishpc_debug("pcishpc_create_slot() success");
return (pcishpc_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. */
/* 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. */
pcishpc_debug("pcishpc_setup_controller() too many SHPC "
"slots error");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* pcishpc_uninit()
* Unload the HogPlug controller driver and deallocate all resources.
*/
int
{
if (!ctrl_p) {
pcishpc_debug("pcishpc_uninit() Unable to find softstate");
return (DDI_FAILURE);
}
(void) pcishpc_destroy_controller(dip);
return (DDI_SUCCESS);
}
/*
* pcishpc_destroy_slots()
*
* Free up all of the slot resources for this controller.
*/
static int
{
pcishpc_debug("pcishpc_destroy_slots: "
"waiting for ATTN thread exit\n");
&ctrl_p->shpc_mutex);
pcishpc_debug("pcishpc_destroy_slots: "
"ATTN thread exit\n");
}
if (prev_pcishpc_p == NULL)
else
pcishpc_debug("pcishpc_destroy_slots() (shpc_p=%p) "
"destroyed", pcishpc_p);
} else
}
return (DDI_SUCCESS);
}
/*
* pcishpc_connect()
*
* Called by Hot Plug Services to connect a slot on the bus.
*/
/*ARGSUSED*/
static int
{
pcishpc_debug("pcishpc_connect called()");
if (!pcishpc_p) {
pcishpc_debug("pcishpc_connect() "
"Failed to find soft state for slot_hdl %x", slot_hdl);
return (HPC_ERR_FAILED);
}
/* make sure the MRL sensor is closed */
if (status & SHPC_SLOT_MRL_STATE_MASK) {
pcishpc_debug("pcishpc_connect() failed: MRL open");
goto cleanup;
}
DDI_SUCCESS) {
pcishpc_debug("pcishpc_connect() failed: set power state");
goto cleanup;
}
pcishpc_debug("pcishpc_connect() success!");
return (HPC_SUCCESS);
return (HPC_ERR_FAILED);
}
/*
* pcishpc_set_power_state()
*
* Changed a slot's power state.
*/
static int
{
/* Check to see if the slot is already in this state. */
pcishpc_debug("pcishpc_set_power_state() slot already in "
"this state");
return (DDI_SUCCESS);
}
((state == HPC_SLOT_CONNECTED) ||
(state == HPC_SLOT_DISCONNECTED))) {
pcishpc_debug("pcishpc_set_power_state() slot in "
"empty state");
return (DDI_FAILURE);
}
/* Set the Power LED to blink. */
/* Turn all other LEDS off. */
/* Set the slot state to the new slot state. */
/* Update the hardweare slot state. */
pcishpc_debug("pcishpc_set_power_state() failed");
return (DDI_FAILURE);
}
/* Turn the Power LED ON for a connected slot. */
if (state == HPC_SLOT_CONNECTED) {
}
/* Turn the Power LED OFF for a disconnected slot. */
if (state == HPC_SLOT_DISCONNECTED) {
}
/* Turn all other LEDS off. */
pcishpc_debug("pcishpc_set_power_state() success!");
/* delay after powerON to let the device initialize itself */
return (DDI_SUCCESS);
}
/*
* pcishpc_disconnect()
*
* Called by Hot Plug Services to disconnect a slot on the bus.
*/
/*ARGSUSED*/
static int
{
pcishpc_debug("pcishpc_disconnect called()");
if (!pcishpc_p) {
pcishpc_debug("pcishpc_disconnect() "
"Failed to find soft state for slot_hdl %x", slot_hdl);
return (HPC_ERR_FAILED);
}
!= DDI_SUCCESS) {
pcishpc_debug("pcishpc_disconnect() failed");
goto cleanup;
}
pcishpc_debug("pcishpc_disconnect() success!");
return (HPC_SUCCESS);
return (HPC_ERR_FAILED);
}
/*
* pcishpc_pci_control()
*
* Called by Hot Plug Services to perform a attachment point specific
* operation on a Hot Pluggable Standard PCI Slot.
*/
/*ARGSUSED*/
static int
{
int ret = HPC_SUCCESS;
pcishpc_debug("pcishpc_pci_control called(Request %s)",
if (!pcishpc_p) {
pcishpc_debug("pcishpc_pci_control() Error: "
"Failed to find soft state for slot_hdl %x", slot_hdl);
return (HPC_ERR_FAILED);
}
switch (request) {
case HPC_CTRL_GET_SLOT_STATE:
pcishpc_debug("pcishpc_pci_control() - "
"HPC_CTRL_GET_SLOT_STATE (state=%s)",
break;
case HPC_CTRL_GET_BOARD_TYPE:
pcishpc_debug("pcishpc_pci_control() - "
"HPC_CTRL_GET_BOARD_TYPE");
/*
* The HPS framework does not know what board
* type is plugged in.
*/
else
break;
case HPC_CTRL_GET_LED_STATE:
switch (hpc_led_info->led) {
case HPC_FAULT_LED:
pcishpc_debug("pcishpc_pci_control() - "
"GET_LED FAULT (state=%s)",
hpc_led_info->state));
break;
case HPC_POWER_LED:
pcishpc_debug("pcishpc_pci_control() - "
"GET_LED POWER (state=%s)",
hpc_led_info->state));
break;
case HPC_ATTN_LED:
pcishpc_debug("pcishpc_pci_control() - "
"GET_LED ATTN(state = %s)",
hpc_led_info->state));
break;
case HPC_ACTIVE_LED:
pcishpc_debug("pcishpc_pci_control() - "
"GET_LED ACTIVE(state = %s)",
hpc_led_info->state));
break;
default:
pcishpc_debug("pcishpc_pci_control() "
"Error: GET_LED - "
"Invalid LED %x",
hpc_led_info->led);
break;
}
break;
case HPC_CTRL_SET_LED_STATE:
switch (hpc_led_info->led) {
case HPC_ATTN_LED:
(void) pcishpc_setled(pcishpc_p,
break;
case HPC_POWER_LED:
pcishpc_debug("pcishpc_pci_control() "
"Error: SET_LED - power LED");
break;
case HPC_FAULT_LED:
case HPC_ACTIVE_LED:
break;
default:
pcishpc_debug("pcishpc_pci_control() "
"Error: SET_LED - Unknown LED %x",
hpc_led_info->led);
break;
}
break;
pcishpc_debug("pcishpc_pci_control() Config/Unconfig "
"failed.");
break;
case HPC_CTRL_ENABLE_AUTOCFG:
case HPC_CTRL_DISABLE_AUTOCFG:
case HPC_CTRL_DISABLE_SLOT:
case HPC_CTRL_ENABLE_SLOT:
case HPC_CTRL_DISABLE_ENUM:
case HPC_CTRL_DEV_CONFIGURED:
pcishpc_debug("pcishpc_pci_control() - %s",
break;
case HPC_CTRL_ENABLE_ENUM:
default:
pcishpc_debug("pcishpc_pci_control() - Error: "
"request (%d) NOT SUPPORTED", request);
break;
}
return (ret);
}
/*
* pcishpc_setled()
*
* Change the state of a slot's LED.
*/
static int
{
switch (led) {
case HPC_FAULT_LED:
pcishpc_debug("pcishpc_setled() - HPC_FAULT_LED "
break;
case HPC_POWER_LED:
pcishpc_debug("pcishpc_setled() - HPC_POWER_LED "
break;
case HPC_ATTN_LED:
pcishpc_debug("pcishpc_setled() - HPC_ATTN_LED "
break;
case HPC_ACTIVE_LED:
pcishpc_debug("pcishpc_setled() - HPC_ACTIVE_LED "
break;
}
return (pcishpc_set_slot_state(pcishpc_p));
}
/*
* pcishpc_set_slot_state()
*
* Updates the slot's state and leds.
*/
static int
{
/* Default all states to unchanged. */
cmd_code = 0;
/* Has the slot state changed? */
else
pcishpc_debug("pcishpc_set_slot_state() Slot State changed");
/* Set the new slot state in the Slot operation command. */
}
/* Has the Power LED state changed? */
pcishpc_debug("pcishpc_set_slot_state() Power LED State "
"changed");
/* Set the new power led state in the Slot operation command. */
cmd_code |=
<< 2);
}
/* Has the Attn LED state changed? */
pcishpc_debug("pcishpc_set_slot_state() Attn LED State "
"changed");
/* Set the new attn led state in the Slot operation command. */
<< 4);
}
}
/*
* pcishpc_wait_busy()
*
* Wait until the SHPC controller is not busy.
*/
static int
{
/* Wait until SHPC controller is NOT busy. */
/*CONSTCOND*/
while (1) {
/* Is there an MRL Sensor error? */
pcishpc_debug("pcishpc_wait_busy() ERROR: MRL Sensor "
"error");
break;
}
/* Is there an Invalid command error? */
pcishpc_debug("pcishpc_wait_busy() ERROR: Invalid "
"command error");
break;
}
pcishpc_debug("pcishpc_wait_busy() ERROR: Invalid "
break;
}
/* Is the SHPC controller not BUSY? */
/* Return Success. */
return (DDI_SUCCESS);
}
pcishpc_debug("pcishpc_wait_busy() SHPC controller busy. "
"Waiting");
/* Wait before polling the status register again. */
}
return (DDI_FAILURE);
}
/*
* pcishpc_issue_command()
*
* Sends a command to the SHPC controller.
*/
static int
{
int retCode;
/* Setup the slot for this command. */
/* 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? */
pcishpc_debug("pcishpc_issue_command() Failed on "
"generate cmd complete IRQ");
}
}
if (retCode == DDI_FAILURE)
pcishpc_debug("pcishpc_issue_command() Failed on cmd_code=%02x",
cmd_code);
else
pcishpc_debug("pcishpc_issue_command() Success on "
"cmd_code=%02x", cmd_code);
return (retCode);
}
/*
* 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 (HPC_LED_ON);
case 2: /* SHPC Blink bits b10 */
return (HPC_LED_BLINK);
case 3: /* SHPC Off bits b11 */
return (HPC_LED_OFF);
}
return (HPC_LED_OFF);
}
/*
* pcishpc_led_hpc_to_shpc()
*
* Convert from HPC indicator status to SHPC indicator status.
*/
static int
{
switch (state) {
case HPC_LED_ON:
return (1); /* SHPC On bits b01 */
case HPC_LED_BLINK:
return (2); /* SHPC Blink bits b10 */
case HPC_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.
*/
static int
{
switch (state) {
case 0: /* SHPC Reserved */
return (HPC_SLOT_EMPTY);
case 1: /* SHPC Powered Only */
return (HPC_SLOT_UNKNOWN);
case 2: /* SHPC Enabled */
return (HPC_SLOT_CONNECTED);
case 3: /* SHPC Disabled */
return (HPC_SLOT_DISCONNECTED);
}
/* Unknown slot state. */
return (HPC_SLOT_UNKNOWN);
}
/*
* pcishpc_slot_hpc_to_shpc()
*
* Convert from HPC slot state to SHPC slot state.
*/
static int
{
switch (state) {
case HPC_SLOT_EMPTY:
return (0); /* SHPC Reserved */
case HPC_SLOT_UNKNOWN:
return (1); /* SHPC Powered Only */
case HPC_SLOT_CONNECTED:
return (2); /* SHPC Enabled */
case HPC_SLOT_DISCONNECTED:
return (3); /* SHPC Disabled */
}
/* Known slot state is reserved. */
return (0);
}
/*
* pcishpc_get_slot_state()
*
* Get the state of the slot.
*/
static void
{
/* Read the logical slot register for this Slot. */
/* Convert from the SHPC slot state to the HPC slot state. */
else
/* 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_textledstate()
*
* Convert the led state into a text message.
*/
static char *
{
/* Convert an HPC led state into a textual string. */
switch (state) {
case HPC_LED_OFF:
return ("off");
case HPC_LED_ON:
return ("on");
case HPC_LED_BLINK:
return ("blink");
}
return ("unknown");
}
/*
* pcishpc_textrequest()
*
* Convert the request into a text message.
*/
static char *
{
/* Convert an HPC request into a textual string. */
switch (request) {
case HPC_CTRL_GET_LED_STATE:
return ("HPC_CTRL_GET_LED_STATE");
case HPC_CTRL_SET_LED_STATE:
return ("HPC_CTRL_SET_LED_STATE");
case HPC_CTRL_GET_SLOT_STATE:
return ("HPC_CTRL_GET_SLOT_STATE");
case HPC_CTRL_DEV_CONFIGURED:
return ("HPC_CTRL_DEV_CONFIGURED");
return ("HPC_CTRL_DEV_UNCONFIGURED");
case HPC_CTRL_GET_BOARD_TYPE:
return ("HPC_CTRL_GET_BOARD_TYPE");
case HPC_CTRL_DISABLE_AUTOCFG:
return ("HPC_CTRL_DISABLE_AUTOCFG");
case HPC_CTRL_ENABLE_AUTOCFG:
return ("HPC_CTRL_ENABLE_AUTOCFG");
case HPC_CTRL_DISABLE_SLOT:
return ("HPC_CTRL_DISABLE_SLOT");
case HPC_CTRL_ENABLE_SLOT:
return ("HPC_CTRL_ENABLE_SLOT");
case HPC_CTRL_DISABLE_ENUM:
return ("HPC_CTRL_DISABLE_ENUM");
case HPC_CTRL_ENABLE_ENUM:
return ("HPC_CTRL_ENABLE_ENUM");
return ("HPC_CTRL_DEV_CONFIG_FAILURE");
return ("HPC_CTRL_DEV_UNCONFIG_FAILURE");
return ("HPC_CTRL_DEV_CONFIG_START");
return ("HPC_CTRL_DEV_UNCONFIG_START");
}
return ("Unknown");
}
/*
* pcishpc_textslotstate()
*
* Convert the request into a text message.
*/
static char *
{
/* Convert an HPC slot state into a textual string. */
switch (state) {
case HPC_SLOT_EMPTY:
return ("HPC_SLOT_EMPTY");
case HPC_SLOT_DISCONNECTED:
return ("HPC_SLOT_DISCONNECTED");
case HPC_SLOT_CONNECTED:
return ("HPC_SLOT_CONNECTED");
case HPC_SLOT_UNKNOWN:
return ("HPC_SLOT_UNKNOWN");
}
return ("Unknown");
}
/*
* 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. */
pcishpc_debug("pcishpc_write_reg() - Failed writing "
"DWORD select reg");
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.
*/
}
/*
* 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. */
pcishpc_debug("pcishpc_read_reg() - Failed writing DWORD "
"select reg");
return (0xFFFFFFFF);
}
/* Read from the SHPC dword data register. */
}
/*
* pcishpc_debug()
*
* Controls debug output if enabled.
*/
static void
pcishpc_debug(char *fmt, ...)
{
if (pcishpc_debug_enabled) {
}
}
/*
* pcishpc_dump_regs()
*
* Dumps all of the SHPC controller registers.
*/
static void
{
char *state;
"==========");
(reg & 31));
(reg & 31));
numSlots);
"No");
"No");
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;
}
": %s", state);
}
"Yes" : "No");
"Yes" : "No");
}
"Yes" : "No");
"Yes" : "No");
"Yes" : "No");
"Yes" : "No");
"Yes" : "No");
"Yes" : "No");
(reg & 3))));
"No Fault");
"Not Depressed");
"Closed");
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;
}
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;
}
"No");
"No");
"No");
}
}
static void
{
pcishpc_debug("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 HPS framework.
*/
/*
* send the ATTN button event
* to HPS framework
*/
(void) hpc_slot_event_notify(
}
}
/* restore the power LED state ??? XXX */
continue;
}
/* wait for another ATTN button event */
}
pcishpc_debug("pcishpc_attn_btn_handler: thread exit\n");
thread_exit();
}
/*
* setup slot name/slot-number info.
*/
static void
{
int *slotnum;
int len;
uchar_t *s;
int pci_id_cnt, pci_id_bit;
int slots_before, found;
int invalid_slotnum = 0;
p->phy_slot_num = slotnum[0];
} else {
}
if (!p->phy_slot_num) { /* platform may not have initialized it */
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>"
*/
&len) == DDI_PROP_SUCCESS) {
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.
*/
while (p->deviceNum != pci_id_cnt) {
/*
* Find the next bit set.
*/
while (!(bit_mask & pci_id_bit) &&
(pci_id_cnt < 32)) {
pci_id_cnt++;
}
if (p->deviceNum != 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--;
}
return;
}
/* slot-names entry not found */
pcishpc_debug("pcishpc_set_slot_name(): "
"No slot-names entry found for slot #%d",
p->phy_slot_num);
}
if (invalid_slotnum)
p->phy_slot_num, p->deviceNum);
else
p->phy_slot_num);
}