schpc.c revision aac517695f87c6b55a5fefa5a14da8c270097ef4
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
*/
#define CPCI_ENUM
#include <sys/ddi_impldefs.h>
#include <sys/ndi_impldefs.h>
#include <sys/schpc_msg.h>
#include <post/scat_dcd.h>
#ifdef DEBUG
int schpc_dump_save_regs = 0;
static uint_t schpc_debug_flags = 0;
#define SCHPC_DEBUG0(f, s) if ((f)& schpc_debug_flags) \
#define SCHPC_DEBUG1(f, s, a) if ((f)& schpc_debug_flags) \
#define SCHPC_DEBUG2(f, s, a, b) if ((f)& schpc_debug_flags) \
#define SCHPC_DEBUG3(f, s, a, b, c) if ((f)& schpc_debug_flags) \
#define SCHPC_DEBUG4(f, s, a, b, c, d) if ((f)& schpc_debug_flags) \
#define SCHPC_DEBUG5(f, s, a, b, c, d, e) if ((f)& schpc_debug_flags) \
#else
#define SCHPC_DEBUG0(f, s)
#define SCHPC_DEBUG1(f, s, a)
#define SCHPC_DEBUG2(f, s, a, b)
#define SCHPC_DEBUG3(f, s, a, b, c)
#define SCHPC_DEBUG4(f, s, a, b, c, d)
#define SCHPC_DEBUG5(f, s, a, b, c, d, e)
#define SCHPC_DEBUG6(f, s, a, b, c, d, e, ff)
#endif
#define D_IDENTIFY 0x00000001
#define D_ATTACH 0x00000002
#define D_DETACH 0x00000004
#define D_OPEN 0x00000008
#define D_GETSLOTSTATUS 0x00000010
#define D_SETSLOTSTATUS 0x00000020
#define D_IOCTL 0x00010000
#define D_IOC_CONNECT 0x00020000
#define D_IOC_CONTROL 0x00040000
#define D_IOC_CONFIG 0x00080000
#define D_IOC_STATUS 0x00100000
#define D_IOC_MSG 0x00200000
#define D_IOC_TEST 0x00400000
#define D_IOC_LED 0x00800000
#define D_EVENT 0x01000000
#define D_THREAD 0x02000000
#define D_TRANSID 0x04000000
#define D_SLOTTABLE 0x08000000
#define D_FREQCHG 0x10000000
#define D_APID 0x20000000
/*
* driver global data:
*/
static void *per_schpc_state; /* soft state head */
int schpc_use_legacy_apid = 0;
/*
* replies to mboxsc_getmsg() are handled asynchronously by the
* schpc_msg_thread using a linked list of schpc_replylist_t
* elements
*/
typedef struct schpc_replylist {
typedef struct {
char *cname;
char *caddr;
char schizo;
char leaf;
} find_dev_t;
/*
* Function prototypes for local functions
*/
static int schpc_getexpander(dev_info_t *);
static int schpc_getboard(dev_info_t *);
static void schpc_event_handler(void *);
static void schpc_register_all_slots(schpc_t *);
static void schpc_setslotled(int, int, int, uint32_t);
static void schpc_init_setslot_message(pci_setslot_t *);
static int schpc_match_dip(dev_info_t *, void *);
static void schpc_buildapid(dev_info_t *, int, char *);
static void schpc_msg_thread(void);
static int schpc_slot_freq(pci_getslot_t *);
static int schpc_find_dip(dev_info_t *, void *);
static int schpc_save_leaf(int slot);
static void schpc_restore_leaf(int slot);
static int schpc_is_leaf_reset_required(int slot);
static int schpc_is_freq_switchable(int slot);
/*
* Function prototype for Hot Plug Services
*/
/*
* cb_ops and dev_ops:
*/
static struct cb_ops schpc_cb_ops = {
nodev, /* open */
nodev, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
nodev, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* prop_op */
0, /* streamtab */
};
/*
* Function prototype for dev_ops
*/
static struct dev_ops schpc_dev_ops = {
DEVO_REV, /* devo_rev, */
0, /* refcnt */
schpc_info, /* get_dev_info */
nulldev, /* identify */
nulldev, /* probe */
schpc_attach, /* attach */
schpc_detach, /* detach */
nodev, /* reset */
&schpc_cb_ops, /* driver operations */
(struct bus_ops *)0, /* no bus operations */
NULL, /* power */
ddi_quiesce_not_supported, /* devo_quiesce */
};
/*
* loadable module declarations:
*/
"PCI Hot Plug Controller Driver (schpc)",
};
static struct modlinkage modlinkage = {
(void *)&modldrv,
};
int
_init(void)
{
int ret;
int rv;
if (ret != 0) {
return (ret);
}
/*
* Initialize Outgoing Mailbox.
*/
if (ret != 0) {
return (ret);
}
(void *) &schpc_putmsg_timeout_range);
if (ret != 0) {
return (ret);
}
}
}
/*
* Create the schpc_event_taskq for MBOXSC_MSG_EVENT processing.
*/
/*
* Initialize Incoming Mailbox.
* NOTE: the callback is null because the schpc_msg_thread will
* handle all incoming MBOXSC_MSG_EVENT and MBOXSC_MSG_REPLY
* messages.
*/
if (ret != 0) {
"MBOXSC_MBOX_IN");
return (ret);
}
(void *) &schpc_getmsg_timeout_range);
if (ret != 0) {
return (ret);
}
}
}
}
}
if (ret != 0) {
"mboxsc_fini(KEY_PCSC) failed: 0x%x", rv);
}
"mboxsc_fini(KEY_SCPC) failed: 0x%x", rv);
}
return (ret);
}
/*
* Start the schpc_msg_thread to continuously monitor the
* MBOXSC_MBOX_IN mailbox for incoming MBOXSC_MSG_EVENTs and
* MBOXSC_MSG_REPLYs.
*/
return (ret);
}
int
_fini(void)
{
return (DDI_FAILURE);
}
int
{
}
static int
{
int rval;
switch (cmd) {
case DDI_ATTACH:
/*
* Allocate the soft state structure for this instance.
*/
if (rval != DDI_SUCCESS) {
"schpc_attach(%x) Can not allocate "
"soft state structure", instance);
return (DDI_FAILURE);
}
instance);
return (DDI_FAILURE);
}
/*
* Put schpc structure on global linked list.
*/
/*
* Initialize starting transaction ID.
*/
schpc_p->schpc_transid = 0;
"describes %d slots", instance,
/*
* What type of hot plug do these slots support? The only
* types of slots we support is the cPCI Hot Plug Model
* and Not Hot Pluggable.
*/
if (schpc_p->schpc_hotplugmodel !=
}
KM_SLEEP);
/*
* Start thread to search the device tree and register
* all found pci slots.
*/
break;
case DDI_PM_RESUME:
case DDI_RESUME:
return (DDI_SUCCESS);
default:
instance);
return (DDI_FAILURE);
}
"schpc_attach(%x) Attach - DDI_SUCCESS", instance);
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
return (DDI_FAILURE);
}
/*ARGSUSED*/
static int
void **result)
{
int error;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
*result = (void *)schpc_devi;
error = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
/*
* schpc_connect()
*
* Called by Hot Plug Services to connect a slot to the bus.
*/
/*ARGSUSED*/
static int
{
int rval;
int slot;
return (HPC_ERR_FAILED);
}
/*
* Check to see if the slot is already connected.
*/
return (0);
}
/*
* Block if another thread is executing a HPC command.
*/
}
"schpc_connect Expander=%x Board=%x Slot=%x",
"connection on Expander %d Board %d Slot %d - "
"Ap_Id=%s : Occupant is in failed state",
/* Fault LED should already be illuminated */
goto failed;
}
"connection on Expander %d Board %d Slot %d - "
"Ap_Id=%s : Receptacle is in failed state",
/* Fault LED should already be illuminated */
goto failed;
}
if (rval) {
/*
* System Controller/Mailbox failure.
*/
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : Unable to "
goto failed;
}
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : Unable to "
goto failed;
}
if (getslot.slot_empty) {
/*
* If the slot is empty - fail the connection request.
*/
goto failed;
}
if (!schpc_is_freq_switchable(slot) &&
"on Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
"Bus Speed Mismatch", expander,
goto failed;
}
if (schpc_is_leaf_reset_required(slot) &&
slot);
/*
* A prior disconnect had not saved off the leaf so lets
* save it now. This is probably due to the domain being
* booted with a slot with no cassette.
*/
if (schpc_save_leaf(slot) != 0) {
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : ",
goto failed;
}
}
/*
* Initialize Set Slot Command.
*/
if (rval != 0) {
/*
* System Controller/Mailbox failure.
*/
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : Unable to "
goto failed;
}
/*
* The Request was successfully completed.
*/
"succeeded");
/*
* Need to check HEALTHY# signal.
*/
if (rval) {
/*
* System Controller/Mailbox failure.
*/
"on Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
"Unable to Communicate with System Controller",
goto failed;
}
"on Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
goto failed;
}
"on Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
/*
* Initialize Set Slot Command.
*/
/*
* Turn slot power off.
*/
(SERVICE_LED_ON | FAULT_LED_ON));
goto failed;
}
if (!getslot.slot_HEALTHY) {
"on Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
/*
* Initialize Set Slot Command.
*/
/*
* Turn slot power off.
*/
&setslot);
(SERVICE_LED_ON | FAULT_LED_ON));
goto failed;
}
/*
* Initialize Set Slot Command.
*/
/*
* Start monitoring ENUM# and HEALTHY#
*/
if (rval != 0) {
/*
* System Controller/Mailbox failure.
*/
"on Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
"Unable to Communicate with System Controller",
goto failed;
}
int freq;
/*
* The Request was successfully completed.
*/
"schpc_connect() - setslotstatus succeeded");
KM_SLEEP);
/* root node doesn't have to be held */
&find_dev);
/*
* Update the clock-frequency property to
* reflect the new slot-frequency.
*/
"schpc_connect: updating dip=%p freq=%dHZ",
!= DDI_SUCCESS) {
"schpc: - failed to update "
"clock-frequency property for %s",
}
} else {
"schpc: couldn't find dip for %s ",
}
/*
* If leaf registers were saved off, then they
* need to be restored.
*/
/*
* Since the device saw a PCI Reset, we need to
* wait 2^25 clock cycles before the first
* Configuration access. The worst case is 33MHz,
* which is a 1 second wait.
*/
drv_usecwait(1000000);
return (0);
} else {
/*
* The System Controller Rejected the
* connection request.
*/
"on Expander %d Board %d PCI Slot %d - Ap_Id=%s :"
"System Controller failed connection request",
goto failed;
}
}
/*
* The System Controller Rejected the connection request.
*/
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : System Controller "
return (HPC_ERR_FAILED);
}
/*
* schpc_disconnect()
*
* Called by Hot Plug Services to disconnect a slot to the bus.
*/
/*ARGSUSED*/
static int
{
int rval;
"schpc_disconnect( ops_arg=%p slot_hdl=%p)", (void *)ops_arg,
slot_hdl);
"schpc_disconnect - HPC Not Inited");
return (HPC_ERR_FAILED);
}
/*
* Check to see if we are already disconnected.
*/
return (0);
}
/*
* Block if another thread is executing a HPC command.
*/
}
/*
* change, then the leaf registers of the XMITS bridge will need
* to be saved off prior to the connect.
*/
if (schpc_is_leaf_reset_required(slot)) {
if (schpc_save_leaf(slot) != 0) {
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : ",
goto failed;
}
}
/*
* Initialize Set Slot Command.
*/
"setslotstatus returned 0x%x", rval);
if (rval != 0) {
/*
* System Controller/Mailbox failure.
*/
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : Unable to "
goto failed;
}
/*
* The Request was successfully completed.
*/
"schpc_disconnect() - setslotstatus succeeded");
return (0);
}
/*
* System Controller/Mailbox failure.
*/
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : System Controller "
return (HPC_ERR_FAILED);
}
/*
* schpc_cpci_control
*
* Called by Hot Plug Services to perform a attachment point specific
* on a Hot Pluggable Compact PCI Slot.
*/
/*ARGSUSED*/
static int
{
int rval;
"schpc_cpci_control(op_args=%p slot_hdl=%p request=%x)",
"schpc_disconnect - HPC Not Inited");
return (HPC_ERR_FAILED);
}
/*
* Block if another thread is executing a HPC command.
*/
}
/*
* Initialize Set Slot Command.
*/
/*
* Initialize LED to last know state.
*/
case LED_ON:
break;
case LED_OFF:
break;
case LED_FLASH:
break;
}
case LED_ON:
break;
case LED_OFF:
break;
case LED_FLASH:
break;
}
case LED_ON:
break;
case LED_OFF:
break;
case LED_FLASH:
break;
}
switch (request) {
case HPC_CTRL_GET_LED_STATE:
"HPC_CTRL_GET_LED_STATE");
switch (hpc_led_info->led) {
case HPC_FAULT_LED:
case LED_OFF:
break;
case LED_ON:
break;
case LED_FLASH:
break;
}
break;
case HPC_POWER_LED:
case LED_OFF:
break;
case LED_ON:
break;
case LED_FLASH:
break;
}
break;
case HPC_ATTN_LED:
case LED_OFF:
break;
case LED_ON:
break;
case LED_FLASH:
break;
}
break;
case HPC_ACTIVE_LED:
case LED_OFF:
break;
case LED_ON:
break;
case LED_FLASH:
break;
}
break;
default:
return (HPC_ERR_FAILED);
}
return (0);
case HPC_CTRL_SET_LED_STATE:
"HPC_CTRL_SET_LED_STATE hpc_led_info=%p",
(void *)hpc_led_info);
switch (hpc_led_info->led) {
case HPC_FAULT_LED:
switch (hpc_led_info->state) {
case HPC_LED_OFF:
break;
case HPC_LED_ON:
break;
case HPC_LED_BLINK:
break;
}
break;
case HPC_POWER_LED:
switch (hpc_led_info->state) {
case HPC_LED_OFF:
break;
case HPC_LED_ON:
break;
case HPC_LED_BLINK:
break;
}
break;
case HPC_ATTN_LED:
switch (hpc_led_info->state) {
case HPC_LED_OFF:
break;
case HPC_LED_ON:
break;
case HPC_LED_BLINK:
break;
}
break;
case HPC_ACTIVE_LED:
switch (hpc_led_info->state) {
case HPC_LED_OFF:
break;
case HPC_LED_ON:
break;
case HPC_LED_BLINK:
break;
}
break;
default:
return (0);
}
return (0);
case HPC_CTRL_GET_SLOT_STATE: {
"HPC_CTRL_GET_SLOT_STATE hpc_slot_state=%p",
(void *)hpc_slot_state);
if (!rval) {
return (HPC_ERR_FAILED);
}
} else {
"Slot Disconnected");
}
} else {
return (HPC_ERR_FAILED);
}
return (0);
}
case HPC_CTRL_GET_BOARD_TYPE: {
"HPC_CTRL_GET_BOARD_TYPE");
/*
* The HPC driver does not know what board type
* is plugged in.
*/
return (0);
}
case HPC_CTRL_DEV_CONFIGURED:
"HPC_CTRL_DEV_CONFIGURED");
return (0);
"HPC_CTRL_DEV_UNCONFIGURED");
/*
* When the occupant is unconfigured, power
* down the slot.
*/
0, 0);
}
return (0);
case HPC_CTRL_ENABLE_AUTOCFG:
"HPC_CTRL_ENABLE_AUTOCFG");
return (0);
case HPC_CTRL_DISABLE_AUTOCFG:
"HPC_CTRL_DISABLE_AUTOCFG");
return (0);
case HPC_CTRL_DISABLE_ENUM:
"HPC_CTRL_DISABLE_ENUM");
if (rval)
return (rval);
case HPC_CTRL_ENABLE_ENUM:
"HPC_CTRL_ENABLE_ENUM");
if (rval)
return (rval);
default:
"****NOT SUPPORTED CONTROL CMD");
return (HPC_ERR_NOTSUPPORTED);
}
}
/*
* schpc_pci_control
*
* Called by Hot Plug Services to perform a attachment point specific
* on a Hot Pluggable Standard PCI Slot.
*/
/*ARGSUSED*/
static int
{
int rval;
"schpc_pci_control(op_args=%p slot_hdl=%p request=%x)",
"schpc_disconnect - HPC Not Inited");
return (HPC_ERR_FAILED);
}
/*
* Block if another thread is executing a HPC command.
*/
}
/*
* Initialize Set Slot Command.
*/
/*
* Initialize LED to last know state.
*/
case LED_ON:
break;
case LED_OFF:
break;
case LED_FLASH:
break;
}
case LED_ON:
break;
case LED_OFF:
break;
case LED_FLASH:
break;
}
case LED_ON:
break;
case LED_OFF:
break;
case LED_FLASH:
break;
}
switch (request) {
case HPC_CTRL_GET_SLOT_STATE: {
"HPC_CTRL_GET_SLOT_STATE hpc_slot_state=%p",
(void *)hpc_slot_state);
if (!rval) {
return (HPC_ERR_FAILED);
}
} else {
"Slot Disconnected");
}
} else {
return (HPC_ERR_FAILED);
}
return (0);
}
case HPC_CTRL_GET_BOARD_TYPE: {
"HPC_CTRL_GET_BOARD_TYPE");
/*
* The HPC driver does not know what board type
* is plugged in.
*/
return (0);
}
case HPC_CTRL_DEV_CONFIGURED:
return (0);
case HPC_CTRL_GET_LED_STATE:
"HPC_CTRL_GET_LED_STATE");
switch (hpc_led_info->led) {
case HPC_FAULT_LED:
case LED_OFF:
break;
case LED_ON:
break;
case LED_FLASH:
break;
}
break;
case HPC_POWER_LED:
case LED_OFF:
break;
case LED_ON:
break;
case LED_FLASH:
break;
}
break;
case HPC_ATTN_LED:
case LED_OFF:
break;
case LED_ON:
break;
case LED_FLASH:
break;
}
break;
case HPC_ACTIVE_LED:
case LED_OFF:
break;
case LED_ON:
break;
case LED_FLASH:
break;
}
break;
default:
return (HPC_ERR_FAILED);
}
return (0);
case HPC_CTRL_SET_LED_STATE:
"HPC_CTRL_SET_LED_STATE hpc_led_info=%p",
(void *)hpc_led_info);
switch (hpc_led_info->led) {
case HPC_FAULT_LED:
switch (hpc_led_info->state) {
case HPC_LED_OFF:
break;
case HPC_LED_ON:
break;
case HPC_LED_BLINK:
break;
}
break;
case HPC_POWER_LED:
switch (hpc_led_info->state) {
case HPC_LED_OFF:
break;
case HPC_LED_ON:
break;
case HPC_LED_BLINK:
break;
}
break;
case HPC_ATTN_LED:
switch (hpc_led_info->state) {
case HPC_LED_OFF:
break;
case HPC_LED_ON:
break;
case HPC_LED_BLINK:
break;
}
break;
case HPC_ACTIVE_LED:
switch (hpc_led_info->state) {
case HPC_LED_OFF:
break;
case HPC_LED_ON:
break;
case HPC_LED_BLINK:
break;
}
break;
default:
return (0);
}
return (0);
case HPC_CTRL_ENABLE_AUTOCFG:
"HPC_CTRL_ENABLE_AUTOCFG");
return (0);
case HPC_CTRL_DISABLE_AUTOCFG:
"HPC_CTRL_DISABLE_AUTOCFG");
return (0);
case HPC_CTRL_DISABLE_ENUM:
case HPC_CTRL_ENABLE_ENUM:
default:
return (HPC_ERR_NOTSUPPORTED);
}
}
/*
* schpc_test
*
* Tests the slot.
*/
/*ARGSUSED*/
static void
{
int rval;
int retry = 1;
" schpc_test() Expander=%d Board=%d Slot=%d",
/*
* Initial the slot with its occupant and receptacle in good condition.
*/
if (rval) {
/*
* System Controller/Mailbox failure.
*/
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : Unable to "
return;
}
return;
}
switch (slotstatus.slot_condition) {
case PCIMSG_SLOTCOND_OCC_FAIL:
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
"System Controller/Occupant Failed",
return;
case PCIMSG_SLOTCOND_REC_FAIL:
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
"System Controller/Receptacle Failed",
return;
}
if (slotstatus.slot_power_on) {
if (!slotstatus.slot_HEALTHY) {
/*
* cPCI Adapter is not asserting HEALTHY#.
*/
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
return;
}
if (!slotstatus.slot_powergood) {
/*
* PCI Power Input is not good.
*/
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
"System Controller PCI Power Input Not Good",
return;
}
if (slotstatus.slot_powerfault) {
/*
* PCI Power Fault.
*/
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
"System Controller PCI Power Fault",
return;
}
}
/*
* Is the slot empty?
*/
if (slotstatus.slot_empty) {
if (slotstatus.slot_power_on) {
"is powered ON");
/*
* Tests will be retried once after powering off
* an empty slot.
*/
if (retry) {
/*
* Turn off the slot and restart test.
*/
"Turning Empty Slot OFF");
(void) schpc_setslotstatus(
retry = 0;
goto restart_test;
}
}
} else {
if (!slotstatus.slot_power_on) {
if (retry) {
/*
* If there is a cassette present and the
* power is off, try turning the power on and
* restart the test. This allows access to
* the FRUID when an empty cassette is
* installed.
*/
"schpc_test() Power On Adapter");
(void) schpc_setslotstatus(
retry = 0;
goto restart_test;
}
}
}
/*
* Is the slot powered up?
*/
if (slotstatus.slot_power_on) {
} else {
}
/*
* Save LED State.
*/
switch (setslot.slot_led_power) {
case PCIMSG_LED_ON:
break;
case PCIMSG_LED_OFF:
break;
case PCIMSG_LED_FLASH:
break;
}
switch (setslot.slot_led_service) {
case PCIMSG_LED_ON:
break;
case PCIMSG_LED_OFF:
break;
case PCIMSG_LED_FLASH:
break;
}
switch (setslot.slot_led_fault) {
case PCIMSG_LED_ON:
break;
case PCIMSG_LED_OFF:
break;
case PCIMSG_LED_FLASH:
break;
}
}
/*
* schpc_event_handler
*
* Placed on the schpc_event_taskq by schpc_event_filter when an
* unsolicited MBOXSC_MSG_EVENT is received from the SC. It handles
*/
static void
schpc_event_handler(void *arg)
{
int rval;
/*
* OK, we got an event message. Since the event message only tells
* us something has changed and not changed to what, we need to get
* the current slot status to find how WHAT was change to WHAT.
*/
"schpc_event_handler() - exp=%d board=%d slot=%d",
/* create a slot table index */
"schpc_event_handler() - expanded slot %d", slot);
return;
}
return;
}
/*
* Block if another thread is executing a HPC command.
*/
}
if (rval) {
"for expander=%d board=%d slot=%d\n",
return;
}
"status for expander=%d board=%d slot=%d\n",
return;
}
"for unregistered slot for expander=%d board=%d slot=%d",
return;
}
/* Slot Power Event */
/*
* The SC may have changed to slot power status.
*/
if (slotstatus.slot_power_on) {
(void) hpc_slot_event_notify(
} else {
(void) hpc_slot_event_notify(
}
}
/* Adapter Removed */
/*
* If the adapter has been removed while
* there the slot is connected, it could be
* due to a ENUM handling.
*/
"powered on slot at "
"expander=%d board=%d slot=%d\n",
0, 0);
"schpc_event_handler - "
"Slot is busy");
&schpc_p->schpc_mutex);
}
}
(void) hpc_slot_event_notify(
} else {
/* Adapter Inserted */
/*
* If the adapter is already present
* throw the this event away.
*/
"Adapter is already present");
return;
}
(void) hpc_slot_event_notify(
0, 0);
if (rval) {
" Can not connect");
return;
}
"schpc_event_handler - "
"Slot is busy");
&schpc_p->schpc_mutex);
}
(void) hpc_slot_event_notify(
} else {
}
}
}
/* ENUM# signal change event */
/*
* ENUM should only be received to the adapter remove
* procedure.
*/
(void) hpc_slot_event_notify(
HPC_EVENT_SLOT_ENUM, 0);
}
/* HEALTHY# signal change event */
if (!slotstatus.slot_HEALTHY) {
(void) hpc_slot_event_notify(
} else {
(void) hpc_slot_event_notify(
}
}
/* Good Power change event */
"Event Type: Slot Power Good Detected");
(void) hpc_slot_event_notify(
} else {
"Detected");
"power failed");
(void) hpc_slot_event_notify(
}
}
}
/* Power Fault change event */
"Detected");
(void) hpc_slot_event_notify(
} else {
"Cleared");
(void) hpc_slot_event_notify(
}
}
}
/*
* schpc_event_filter
*
* The schpc_event_filter enqueues MBOXSC_MSG_EVENTs into the
* schpc_event_taskq for processing by the schpc_event_handler _if_
* hotpluggable pci slots have been registered; otherwise, the
* MBOXSC_MSG_EVENTs are discarded in order to keep the incoming mailbox
* open for future messages.
*/
static void
{
if (slots_registered == B_TRUE) {
/*
* If hotpluggable pci slots have been registered then enqueue
* the event onto the schpc_event_taskq for processing.
*/
"slots_registered = B_TRUE");
"event alloc'd");
"taskq_dispatch failed to enqueue event");
return;
}
"event was taskq_dispatch'ed to schpc_event_handler");
} else {
/*
* Oops, schpc received an event _before_ the slots have been
* registered. In that case there is no choice but to toss
* the event.
*/
"premature event");
}
}
/*
* schpc_msg_thread
* A stand-alone thread that monitors the incoming mailbox for
* MBOXSC_MSG_REPLYs and MBOXSC_MSG_EVENTs, and removes them from
* the mailbox for processing.
*
* MBOXSC_MSG_REPLYs are matched against outstanding REPLYs in the
* schpc_replylist, and the waiting thread is notified that its REPLY
* message has arrived; otherwise, if no REPLY match is found, then it is
* discarded.
*
* MBOXSC_MSG_EVENTs are enqueued into the schpc_event_taskq and processed
* by the schpc_event_handler.
*
* The schpc_msg_thread is started in _init().
*/
void
schpc_msg_thread(void)
{
int err;
/* CONSTCOND */
while (1) {
/* setup wildcard arguments */
type = 0;
cmd = 0;
transid = 0;
if (err) {
switch (err) {
/*FALLTHROUGH*/
case ETIMEDOUT:
case EAGAIN:
continue;
default:
/*
* unfortunately, we can't do very much here
* because we're wildcarding mboxsc_getmsg
* so if it encounters an error, we can't
* identify which transid it belongs to.
*/
"schpc - mboxsc_getmsg failed, err=0x%x", err);
continue;
}
}
/*
* This version of the schpc driver only understands
* version 1.0 of the PCI Hot Plug Message format.
*/
"discarding event w/ unknown message version %x",
continue;
}
switch (type) {
case MBOXSC_MSG_EVENT:
break;
case MBOXSC_MSG_REPLY:
break;
default:
"schpc - mboxsc_getmsg unknown msg"
" type=0x%x", type);
break;
}
}
/* this thread never exits */
}
void
{
break;
} else
}
if (entry) {
"schpc_reply_handler() - 0x%lx transid reply "
"received", transid);
"schpc_reply_handler() - 0x%lx transid"
" cv_signal waiting thread", transid);
/*
* emulate mboxsc_getmsg by copying the reply
*/
/* reply was received */
/*
* wake up thread waiting for reply with transid
*/
}
} else {
transid);
}
}
/*
* schpc_putrequest
*
* A wrapper around the synchronous call mboxsc_putmsg().
*/
int
{
int rval;
/* add the request to replylist to keep track of outstanding requests */
"0x%lx transid mboxsc_putmsg called", *transidp);
/* wait synchronously for request to be sent */
/* if problem is encountered then remove the request from replylist */
if (rval)
return (rval);
}
/*
* schpc_getreply
*
* Wait for the schpc_msg_thread to respond that a matching reply has
* arrived; otherwise, timeout and remove the entry from the schpc_replylist.
*/
/*ARGSUSED*/
int
{
int rc = 0;
"schpc_getreply() - 0x%lx transid waiting for reply",
*transidp);
/*
* wait here until schpc_msg_thread because it's always
* looking for reply messages
*/
/*
* wait for reply or timeout
*/
switch (rc) {
case -1: /* most likely a timeout, but check anyway */
/* message was received after all */
break;
/* no, it's really a timeout */
"schpc - 0x%lx transid reply timed out", *transidp);
return (ETIMEDOUT);
default:
break;
}
}
"schpc_getreply() - 0x%lx transid received", *transidp);
return (0);
}
/*
* schpc_replylist_unlink
*
* Deallocate a schpc_replylist_t element.
*/
void
{
#if DEBUG
#endif /* DEBUG */
"schpc_replylist_unlink() - 0x%lx transid deleted from replylist",
} else {
}
if (entry == schpc_replylist_last) {
}
#if DEBUG
"count = %d\n", schpc_replylist_count);
}
}
#endif /* DEBUG */
}
/*
* schpc_replylist_link
*
* Allocate and initialize a schpc_replylist_t element.
*/
{
#if DEBUG
#endif /* DEBUG */
"schpc_replylist_link() - 0x%lx transid inserting into replylist",
transid);
if (schpc_replylist_last) {
} else {
}
#if DEBUG
"count = %d\n", schpc_replylist_count);
}
}
#endif /* DEBUG */
return (entry);
}
/*
* schpc_getslotstatus
*
* Issues a Get Slot Status command to the System Controller
* for a specific slot.
*/
static int
{
int rval;
"schpc_getslotstatus(expander=%d board=%d "
return (1);
}
"0x%lx transid schpc_putrequest called", transid);
if (rval) {
return (rval);
}
"0x%lx transid schpc_getreply called", transid);
if (rval == 0) {
}
return (rval);
}
/*
* schpc_setslotstatus
*
* Issues a Set Slot Status command to the System Controller
* for a specific slot.
*/
static int
{
int rval;
"schpc_setslotstatus(expander=%d board=%d "
return (1);
}
"0x%lx transid schpc_putrequest called", transid);
if (rval) {
return (rval);
}
"0x%lx transid schpc_getreply called", transid);
if (rval == 0) {
}
return (rval);
}
/*
* schpc_setslotled
*
* Changes the attention indicators for a given slot.
*/
static void
{
return;
}
if (led_state & POWER_LED_ON) {
}
if (led_state & POWER_LED_OFF) {
}
if (led_state & POWER_LED_FLASH) {
}
if (led_state & SERVICE_LED_ON) {
}
if (led_state & SERVICE_LED_OFF) {
}
if (led_state & SERVICE_LED_FLASH) {
}
if (led_state & FAULT_LED_ON) {
}
if (led_state & FAULT_LED_OFF) {
}
if (led_state & FAULT_LED_FLASH) {
}
case PCIMSG_LED_ON:
break;
case PCIMSG_LED_OFF:
break;
case PCIMSG_LED_FLASH:
break;
}
case PCIMSG_LED_ON:
break;
case PCIMSG_LED_OFF:
break;
case PCIMSG_LED_FLASH:
break;
}
case PCIMSG_LED_ON:
break;
case PCIMSG_LED_OFF:
break;
case PCIMSG_LED_FLASH:
break;
}
}
/*
* schpc_init_setslot_message
*
* Initialize Set Slot Message before using it.
*/
static void
{
/*
* Initialize Set Slot Command.
*/
}
/*
* schpc_gettransid
*
* Builds a unique transaction ID.
*/
static uint64_t
{
if (++schpc_p->schpc_transid == 0)
trans_id);
return (trans_id);
}
/*
* schpc_slot_get_index
*
* get slot table index from the slot handle
*/
static int
{
int i;
int rval = -1;
for (i = 0; i < schpc_p->schpc_number_of_slots; i++) {
return (i);
}
return (rval);
}
/*
* schpc_register_all_slots
*
* Search device tree for pci nodes and register attachment points
* for all hot pluggable slots.
*/
/*ARGSUSED*/
static void
{
int slot = 0;
char caddr[64];
"schpc_register_all_slots(schpc_p=%p)", (void *)schpc_p);
/*
* Allow the event_handler to start processing unsolicited
* events now that slots are about to be registered.
*/
if (schizo == 0)
portid = 0x1c;
else
portid = 0x1d;
if (leaf == 0)
offset = 0x600000;
else
offset = 0x700000;
"schpc_register_all_slots: searching for pci@%s"
/* root node doesn't have to be held */
&find_dev);
"schpc_register_all_slots: pci@%s NOT FOUND",
caddr);
continue;
}
"schpc_register_all_slots: pci@%s FOUND dip=0x%p",
(void) schpc_add_pci(pci_dip);
/*
* Release hold acquired in schpc_match_dip()
*/
}
thread_exit();
}
/*
* schpc_add_pci
*
* Routine to add attachments points associated with a pci node.
* Can be call externally by DR when configuring a PCI I/O Board.
*/
int
{
int portid;
char ap_id[MAXNAMELEN];
char caddr[64];
char *naddr;
/*
* The schpc driver has not been attached yet.
*/
return (DDI_SUCCESS);
}
(void *)sdip);
return (DDI_FAILURE);
}
switch (portid & 0x1f) {
case 0x1c:
schizo = 0;
break;
case 0x1d:
schizo = 1;
break;
default:
return (DDI_FAILURE);
}
"(0x%p) returns null", (void *)sdip);
return (DDI_FAILURE);
}
leaf = 0;
} else {
char *name;
leaf = 1;
(schizo == 0)) {
int circ;
/*
* XMITS 0 Leaf B will have its hot
* pluggable slot off a PCI-PCI bridge,
* which is the only child.
*/
"schpc_add_pci(dip=0x%p) - "
"Invalid pci name addr %s\n",
return (DDI_FAILURE);
}
}
} else {
return (DDI_FAILURE);
}
}
/* create a slot table index */
"pci node already registered\n", (void *)sdip);
return (DDI_FAILURE);
}
/*
* There is no need to hold the dip while saving it in
* the devi field below. The dip is never dereferenced.
* (If that changes, this code should be modified).
* We want to avoid holding the dip here because it
* prevents DR.
*
* NOTE: Even though the slot on XMITS0 Leaf-B
* is connected to a pci_pci bridge, we will be saving
* the busdip in this datastructure. This will make
* it easier to identify the dip being removed in
* schpc_remove_pci().
*/
/*
* Starcat PCI slots are always PCI device 1.
*/
/* safe to call ddi_pathname(): bdip is held */
switch (status) {
case RSV_UNKNOWN:
case RSV_PRESENT:
case RSV_MISS:
case RSV_PASS:
case RSV_EMPTY_CASSETTE:
/*
* Test the condition of the slot.
*/
break;
case RSV_BLACK:
"expander=%d board=%d slot=%d\n", expander,
break;
default:
"expander=%d board=%d slot=%d failure=0x%x\n",
break;
}
/* allocate slot ops */
/*
* Default to Autoconfiguration disabled.
*/
/*
* Fill in the slot information structure that
* describes the slot.
*/
if (schpc_p->schpc_hotplugmodel ==
else
else
/*
* Fill in the slot ops structure that tells
* the Hot Plug Services what function we
* support.
*/
if (schpc_p->schpc_hotplugmodel ==
} else {
}
"- nexus =%s schpc_p=%p slot=%d pci number=%d ap_id=%s",
/*
* If the slot can not be registered,
* then the slot_ops need to be freed.
*/
return (DDI_FAILURE);
}
/*
* We are ready to take commands from the HPC Services.
*/
}
return (DDI_SUCCESS);
}
/*
* schpc_remove_pci
*
* Routine to remove attachments points associated with a pci node.
* Can be call externally by DR when unconfiguring a PCI I/O Board.
*/
int
{
int slot;
/*
* The schpc driver has not been attached yet.
*/
return (DDI_SUCCESS);
}
if (hpc_slot_unregister(
"schpc_remove_pci(dip=0x%p) - "
"unable to unregister pci slots\n",
(void *)dip);
return (DDI_FAILURE);
} else {
NULL;
return (DDI_SUCCESS);
}
} else {
return (DDI_SUCCESS);
}
}
}
"dip not found\n", (void *)dip);
return (DDI_SUCCESS);
}
/*
* schpc_match_dip
*
* Used by ddi_walk_devs to find PCI Nexus nodes associated with
* Hot Plug Controllers.
*/
static int
{
char *naddr;
/*
* While ddi_walk_devs() holds dips when invoking this
* callback, this dip is being saved and will be accessible
* to the caller outside ddi_walk_devs(). Therefore it must be
* held.
*/
"schpc_match_dip: pci@%s FOUND dip=0x%p",
return (DDI_WALK_TERMINATE);
}
return (DDI_WALK_CONTINUE);
}
/*
* schpc_buildapid
*
* Takes a component address and translates it into a ap_id prefix.
*/
static void
{
int r, pci_id_cnt, pci_id_bit;
int slots_before, found;
unsigned char *slot_names_data, *s;
int slot_names_size;
int slot_num;
unsigned int bit_mask;
if (schpc_use_legacy_apid) {
return;
}
if (r == DDI_PROP_SUCCESS) {
/*
* We can try to use the slot-names property to
* build our ap-id.
*/
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.
*/
!= pci_id_cnt) {
/*
* 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 = slot_names_data + 4;
/*
* Increment past all the strings for the slots
* before ours.
*/
while (slots_before) {
while (*s != NULL)
s++;
s++;
slots_before--;
}
/*
* We should be at our string.
*/
schpc_getexpander(dip), s);
return;
}
slot);
} else
slot);
/*
* Build the ap-id using the legacy naming scheme.
*/
}
/*
* schpc_getexpander
*
* Returns the Expander Number (0-17) for the dip passed in. The Expander
* Number is extracted from the portid property of the pci node. Portid
* consists of <Expbrd#><1110x>, where x is the schizo number.
*/
static int
{
int id;
if (id != -1)
return (id >> 5);
else {
return (id);
}
}
/*
* schpc_getboard
*
* Returns the board number (0 or 1) for the dip passed in.
*/
static int
{
/*
* Board 1 (half-bandwidth slot).
*/
return (1);
}
/*ARGSUSED*/
static int
{
return (RSV_UNDEFINED);
}
/*
* Get the Starcat Specific Global DCD Structure from the golden
* IOSRAM.
*/
"From IOSRAM\n");
return (RSV_UNDEFINED);
}
return (RSV_UNDEFINED);
}
"GDCD Version 0x%x Expecting 0x%x\n",
return (RSV_UNDEFINED);
}
if (slot < 2)
prd_slot = 4;
else
prd_slot = 5;
return (status);
}
#define LEAF_SAVE_END 0xff
typedef struct {
int reg;
int offset;
int access_size;
int number;
/*
* Save List Array. Describes the leaf registers that need to
* be restored after a leaf reset.
*
* Entry 1 - Reg Entry: 0=PCI Leaf CSRs, 2=PCI Config Space
* Entry 2 - Offset Start
* Entry 3 - Access Size: 8=64 bit, 4=32 bit, 2=16 bit, 1=8 bit
* Entry 4 - # of registers to be saved starting at offset,
*/
0, 0x200, 8, 2,
0, 0x1000, 8, 0x18,
0, 0x1a00, 8, 1,
0, 0x2000, 8, 1,
0, 0x2020, 8, 1,
0, 0x2040, 8, 1,
0, 0x2308, 8, 2,
0, 0x2800, 8, 1,
2, 0x04, 2, 1, /* Command */
2, 0x0d, 1, 1, /* Latency */
2, 0x40, 1, 1, /* Bus # */
2, 0x41, 1, 1, /* Sub. Bus # */
LEAF_SAVE_END, 0, 0, 0};
static int
schpc_save_leaf(int slot)
{
/*
* Map in the 3 addresses spaces defined for XMITS.
*/
return (1);
}
}
/*
* Determine how many entries are in the list so we can
* allocate the save space.
*/
list_entry = 0;
save_entry = 0;
list_entry++;
}
return (0);
KM_SLEEP);
/*
* Walk through the register list and save contents.
*/
list_entry = 0;
save_entry = 0;
list_entry ++;
}
return (0);
}
static void
schpc_restore_leaf(int slot)
{
return;
/*
* Walk through the register list and restore contents.
*/
list_entry = 0;
save_entry = 0;
list_entry ++;
}
/*
* Free the mapped in registers.
*/
}
}
}
static void
{
case 8:
#ifdef DEBUG
if (schpc_dump_save_regs)
#endif
break;
case 4:
#ifdef DEBUG
if (schpc_dump_save_regs)
#endif
break;
case 2:
#ifdef DEBUG
if (schpc_dump_save_regs)
#endif
break;
case 1:
#ifdef DEBUG
if (schpc_dump_save_regs)
#endif
break;
default:
"schpc: Illegal List Entry\n");
}
reads++;
save_entry++;
}
}
static void
{
case 8:
#ifdef DEBUG
if (schpc_dump_save_regs)
#endif
break;
case 4:
#ifdef DEBUG
if (schpc_dump_save_regs)
#endif
break;
case 2:
#ifdef DEBUG
if (schpc_dump_save_regs)
#endif
break;
case 1:
#ifdef DEBUG
if (schpc_dump_save_regs)
#endif
break;
default:
"schpc: Illegal List Entry\n");
}
writes++;
save_entry++;
}
}
/*
* Returns TRUE if a leaf reset is required to change frequencies/mode.
*/
static int
{
char *name;
/*
* Only XMITS 3.0 and greater connected slots will require a
*/
DDI_PROP_DONTPASS, "module-revision#", 0);
/*
* Check for XMITS 3.0 or greater.
*/
/*
* The leaf attached to C5V0 (slot 1) should
* not be reset.
*/
"Not Required - C5V0", slot);
return (0);
}
"Required", slot);
return (1);
}
}
return (0);
}
/*
* Returns TRUE if the bus can change frequencies.
*/
static int
{
char *name;
DDI_PROP_DONTPASS, "module-revision#", 0);
/*
* We will only report back that XMITS 2.0 (mod_rev = 2)
* or greater will have the ability to switch frequencies.
*/
"Frequency is switchable", slot);
return (1);
}
}
return (0);
}
/*
* schpc_slot_freq
*
* Convert the slot frequency setting to integer value.
*/
static int
{
switch (getslotp->slot_freq_setting) {
case PCIMSG_FREQ_33MHZ:
return (SCHPC_33MHZ);
case PCIMSG_FREQ_66MHZ:
return (SCHPC_66MHZ);
case PCIMSG_FREQ_90MHZ:
return (SCHPC_90MHZ);
case PCIMSG_FREQ_133MHZ:
return (SCHPC_133MHZ);
default:
return (0);
}
}
/*
* schpc_find_dip
*
* Used by ddi_walk_devs to find the dip which belongs
* to a certain slot.
*
* When this function returns, the dip is held. It is the
* responsibility of the caller to release the dip.
*/
static int
{
return (DDI_WALK_TERMINATE);
}
return (DDI_WALK_CONTINUE);
}