schpc.c revision 03831d35f7499c87d51205817c93e9a8d42c4bae
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Starcat IOSRAM/Tunnel PCI Hot Plug Controller Driver
*/
#define CPCI_ENUM
#include <sys/note.h>
#include <sys/types.h>
#include <sys/cmn_err.h>
#include <sys/kmem.h>
#include <sys/errno.h>
#include <sys/open.h>
#include <sys/stat.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/cmn_err.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/ddi_impldefs.h>
#include <sys/ndi_impldefs.h>
#include <sys/modctl.h>
#include <sys/disp.h>
#include <sys/async.h>
#include <sys/hotplug/hpcsvc.h>
#include <sys/mboxsc.h>
#include <sys/schpc_msg.h>
#include <sys/schpc.h>
#include <post/scat_dcd.h>
#include <sys/taskq.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) \
cmn_err(CE_CONT, "schpc: " s "\n")
#define SCHPC_DEBUG1(f, s, a) if ((f)& schpc_debug_flags) \
cmn_err(CE_CONT, "schpc: " s "\n", a)
#define SCHPC_DEBUG2(f, s, a, b) if ((f)& schpc_debug_flags) \
cmn_err(CE_CONT, "schpc: " s "\n", a, b)
#define SCHPC_DEBUG3(f, s, a, b, c) if ((f)& schpc_debug_flags) \
cmn_err(CE_CONT, "schpc: " s "\n", a, b, c)
#define SCHPC_DEBUG4(f, s, a, b, c, d) if ((f)& schpc_debug_flags) \
cmn_err(CE_CONT, "schpc: " s "\n", a, b, c, d)
#define SCHPC_DEBUG5(f, s, a, b, c, d, e) if ((f)& schpc_debug_flags) \
cmn_err(CE_CONT, "schpc: " s "\n", a, b, c, d, e)
#define SCHPC_DEBUG6(f, s, a, b, c, d, e, ff) if ((f)& schpc_debug_flags) \
cmn_err(CE_CONT, "schpc: " s "\n", a, b, c, d, e, ff)
#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 */
dev_info_t *schpc_devi;
static schpc_t *schpc_p;
clock_t schpc_timeout_putmsg = 60 * 1000; /* 60 seconds */
clock_t schpc_timeout_getmsg = 60 * 1000; /* 60 seconds */
clock_t schpc_timeout_event = 60 * 5 * 1000; /* 5 minutes */
int schpc_use_legacy_apid = 0;
static mboxsc_timeout_range_t schpc_putmsg_timeout_range;
static mboxsc_timeout_range_t schpc_getmsg_timeout_range;
static taskq_t *schpc_event_taskq = NULL;
/*
* 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 {
struct schpc_replylist *prev; /* link to previous entry */
struct schpc_replylist *next; /* link to next entry */
kcondvar_t reply_cv; /* condvar for getting reply */
kmutex_t reply_lock; /* mutex for getting reply */
uint32_t type; /* mboxsc_xxxmsg() msg type */
uint32_t cmd; /* mboxsc_xxxmsg() cmd */
uint64_t transid; /* mboxsc_xxxmsg() trans id */
uint32_t length; /* mboxsc_xxxmsg() length */
pcimsg_t reply; /* mboxsc_xxxmsg() reply msg */
boolean_t reply_recvd; /* msg reply received */
boolean_t reply_cexit; /* client early exit */
} schpc_replylist_t;
static kmutex_t schpc_replylist_mutex; /* replylist mutex */
static uint32_t schpc_replylist_count; /* replylist size */
static schpc_replylist_t *schpc_replylist_first; /* replylist 1st elem */
static schpc_replylist_t *schpc_replylist_last; /* replylist last elem */
static boolean_t slots_registered = B_FALSE; /* slots registered? */
typedef struct {
char *cname;
char *caddr;
char schizo;
char leaf;
dev_info_t *dip;
} 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_event_filter(pcimsg_t *msg);
static void schpc_reply_handler(pcimsg_t *pmsg, uint32_t type, uint32_t cmd,
uint64_t transid, uint32_t length);
static uint64_t schpc_gettransid(schpc_t *, int);
static int schpc_slot_get_index(schpc_t *, hpc_slot_t);
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 void schpc_test(caddr_t, int, void *, uint_t);
static int schpc_getslotstatus(uint32_t, uint32_t, uint32_t, pci_getslot_t *);
static int schpc_setslotstatus(uint32_t, uint32_t, uint32_t, pci_setslot_t *);
static int schpc_match_dip(dev_info_t *, void *);
static void schpc_buildapid(dev_info_t *, int, char *);
static int schpc_get_slot_status(uint_t, uint_t, uint_t);
static void schpc_replylist_unlink(schpc_replylist_t *entry);
static schpc_replylist_t *schpc_replylist_link(uint32_t cmd, uint64_t transid,
uint32_t length);
static void schpc_msg_thread(void);
static int schpc_putrequest(uint32_t key, uint32_t type, uint32_t cmd,
uint64_t *transidp, uint32_t length,
void *datap, clock_t timeout,
schpc_replylist_t **entryp);
static int schpc_getreply(uint32_t key, uint32_t *typep, uint32_t *cmdp,
uint64_t *transidp, uint32_t *lengthp, void *datap,
clock_t timeout, schpc_replylist_t *listp);
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);
static void schpc_save_entry(int slot, int list_entry, int save_entry);
static void schpc_restore_entry(int slot, int list_entry, int save_entry);
/*
* Function prototype for Hot Plug Services
*/
static int schpc_connect(caddr_t, hpc_slot_t, void *, uint_t);
static int schpc_disconnect(caddr_t, hpc_slot_t, void *, uint_t);
static int schpc_cpci_control(caddr_t, hpc_slot_t, int, caddr_t);
static int schpc_pci_control(caddr_t, hpc_slot_t, int, caddr_t);
extern int iosram_rd(uint32_t, uint32_t, uint32_t, caddr_t);
/*
* 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 */
D_NEW | D_MP | D_HOTPLUG /* Driver compatibility flag */
};
/*
* Function prototype for dev_ops
*/
static int schpc_attach(dev_info_t *, ddi_attach_cmd_t);
static int schpc_detach(dev_info_t *, ddi_detach_cmd_t);
static int schpc_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
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 */
};
/*
* loadable module declarations:
*/
static struct modldrv modldrv = {
&mod_driverops,
"PCI Hot Plug Controller Driver (schpc) %I%",
&schpc_dev_ops,
};
static struct modlinkage modlinkage = {
MODREV_1,
(void *)&modldrv,
NULL
};
int
_init(void)
{
int ret;
int rv;
SCHPC_DEBUG0(D_ATTACH, "_init() installing module");
ret = ddi_soft_state_init(&per_schpc_state, sizeof (schpc_t), 1);
if (ret != 0) {
return (ret);
}
/*
* Initialize Outgoing Mailbox.
*/
ret = mboxsc_init(KEY_PCSC, MBOXSC_MBOX_OUT, NULL);
if (ret != 0) {
ddi_soft_state_fini(&per_schpc_state);
return (ret);
}
ret = mboxsc_ctrl(KEY_PCSC, MBOXSC_CMD_PUTMSG_TIMEOUT_RANGE,
(void *) &schpc_putmsg_timeout_range);
if (ret != 0) {
ddi_soft_state_fini(&per_schpc_state);
return (ret);
}
if (schpc_timeout_putmsg < schpc_putmsg_timeout_range.min_timeout) {
schpc_timeout_putmsg = schpc_putmsg_timeout_range.min_timeout;
cmn_err(CE_WARN, " schpc: resetting putmsg timeout to %ld\n",
schpc_timeout_putmsg);
}
if (schpc_timeout_putmsg > schpc_putmsg_timeout_range.max_timeout) {
schpc_timeout_putmsg = schpc_putmsg_timeout_range.max_timeout;
cmn_err(CE_WARN, " schpc: resetting putmsg timeout to %ld\n",
schpc_timeout_putmsg);
}
/*
* Create the schpc_event_taskq for MBOXSC_MSG_EVENT processing.
*/
schpc_event_taskq = taskq_create("schpc_event_taskq", 2,
minclsyspri, 4, 4, TASKQ_PREPOPULATE);
/*
* 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.
*/
ret = mboxsc_init(KEY_SCPC, MBOXSC_MBOX_IN, NULL);
if (ret != 0) {
cmn_err(CE_WARN, "schpc: can not initialize KEY_SCPC as "
"MBOXSC_MBOX_IN");
ddi_soft_state_fini(&per_schpc_state);
return (ret);
}
ret = mboxsc_ctrl(KEY_SCPC, MBOXSC_CMD_GETMSG_TIMEOUT_RANGE,
(void *) &schpc_getmsg_timeout_range);
if (ret != 0) {
ddi_soft_state_fini(&per_schpc_state);
return (ret);
}
if (schpc_timeout_getmsg < schpc_getmsg_timeout_range.min_timeout) {
schpc_timeout_getmsg = schpc_getmsg_timeout_range.min_timeout;
cmn_err(CE_WARN, " schpc: resetting getmsg timeout to %ld\n",
schpc_timeout_getmsg);
}
if (schpc_timeout_getmsg > schpc_getmsg_timeout_range.max_timeout) {
schpc_timeout_getmsg = schpc_getmsg_timeout_range.max_timeout;
cmn_err(CE_WARN, " schpc: resetting putmsg timeout to %ld\n",
schpc_timeout_putmsg);
}
if (schpc_timeout_event < schpc_getmsg_timeout_range.min_timeout) {
schpc_timeout_event = schpc_getmsg_timeout_range.min_timeout;
cmn_err(CE_WARN, " schpc: resetting event timeout to %ld\n",
schpc_timeout_event);
}
if (schpc_timeout_event > schpc_getmsg_timeout_range.max_timeout) {
schpc_timeout_event = schpc_getmsg_timeout_range.max_timeout;
cmn_err(CE_WARN, " schpc: resetting event timeout to %ld\n",
schpc_timeout_event);
}
ret = mod_install(&modlinkage);
if (ret != 0) {
if ((rv = mboxsc_fini(KEY_PCSC)) != 0) {
cmn_err(CE_WARN, "schpc: _init() - "
"mboxsc_fini(KEY_PCSC) failed: 0x%x", rv);
}
if ((rv = mboxsc_fini(KEY_SCPC)) != 0) {
cmn_err(CE_WARN, "schpc: _init() - "
"mboxsc_fini(KEY_SCPC) failed: 0x%x", rv);
}
taskq_destroy(schpc_event_taskq);
ddi_soft_state_fini(&per_schpc_state);
return (ret);
}
SCHPC_DEBUG0(D_ATTACH, "_init() module installed");
/*
* Start the schpc_msg_thread to continuously monitor the
* MBOXSC_MBOX_IN mailbox for incoming MBOXSC_MSG_EVENTs and
* MBOXSC_MSG_REPLYs.
*/
mutex_init(&schpc_replylist_mutex, NULL, MUTEX_DRIVER, NULL);
(void) thread_create(NULL, 0, schpc_msg_thread,
NULL, 0, &p0, TS_RUN, minclsyspri);
SCHPC_DEBUG0(D_ATTACH, "_init() started schpc_msg_thread");
return (ret);
}
int
_fini(void)
{
SCHPC_DEBUG0(D_ATTACH, "_fini()");
return (DDI_FAILURE);
}
int
_info(struct modinfo *modinfop)
{
SCHPC_DEBUG0(D_ATTACH, "_info() called.");
return (mod_info(&modlinkage, modinfop));
}
static int
schpc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
int instance = ddi_get_instance(devi);
int rval;
SCHPC_DEBUG1(D_ATTACH, "attach(%x) ATTACH", instance);
switch (cmd) {
case DDI_ATTACH:
/*
* Allocate the soft state structure for this instance.
*/
rval = ddi_soft_state_zalloc(per_schpc_state, instance);
if (rval != DDI_SUCCESS) {
SCHPC_DEBUG1(D_ATTACH,
"schpc_attach(%x) Can not allocate "
"soft state structure", instance);
return (DDI_FAILURE);
}
schpc_p = (schpc_t *)ddi_get_soft_state(per_schpc_state,
instance);
if (schpc_p == NULL) {
return (DDI_FAILURE);
}
mutex_init(&schpc_p->schpc_mutex, NULL, MUTEX_DRIVER, NULL);
cv_init(&schpc_p->schpc_cv, NULL, CV_DRIVER, NULL);
/*
* Put schpc structure on global linked list.
*/
/*
* Initialize starting transaction ID.
*/
schpc_p->schpc_transid = 0;
schpc_p->schpc_number_of_slots = STARCAT_MAX_SLOTS;
SCHPC_DEBUG2(D_ATTACH, "schpc_attach(%x) slot-table property "
"describes %d slots", instance,
schpc_p->schpc_number_of_slots);
schpc_p->schpc_hotplugmodel = ddi_getprop(DDI_DEV_T_ANY,
devi, 0, "hot-plug-model", SCHPC_HOTPLUGTYPE_CPCIHOTPLUG);
SCHPC_DEBUG2(D_ATTACH, "attach(%x) ATTACH - Hot Plug Model=%x",
instance, schpc_p->schpc_hotplugmodel);
/*
* 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 !=
SCHPC_HOTPLUGTYPE_CPCIHOTPLUG) {
schpc_p->schpc_hotplugmodel =
SCHPC_HOTPLUGTYPE_NOTHOTPLUGGABLE;
}
schpc_p->schpc_slot = (schpc_slot_t *)kmem_zalloc((size_t)
(schpc_p->schpc_number_of_slots * sizeof (schpc_slot_t)),
KM_SLEEP);
schpc_p->schpc_devi = devi;
schpc_p->schpc_instance = instance;
/*
* Start thread to search the device tree and register
* all found pci slots.
*/
(void) thread_create(NULL, 0, schpc_register_all_slots,
(void *)schpc_p, 0, &p0, TS_RUN, minclsyspri);
break;
case DDI_PM_RESUME:
case DDI_RESUME:
return (DDI_SUCCESS);
default:
cmn_err(CE_WARN, "schpc%d: Cmd != DDI_ATTACH/DDI_RESUME",
instance);
return (DDI_FAILURE);
}
SCHPC_DEBUG1(D_ATTACH,
"schpc_attach(%x) Attach - DDI_SUCCESS", instance);
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
schpc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
{
int instance = ddi_get_instance(devi);
SCHPC_DEBUG1(D_DETACH, "detach(%x) DETACH", instance);
return (DDI_FAILURE);
}
/*ARGSUSED*/
static int
schpc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
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
schpc_connect(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags)
{
int rval;
int expander, board;
pci_setslot_t setslot;
pci_getslot_t getslot;
int slot;
SCHPC_DEBUG2(D_IOC_CONNECT, "schpc_connect( ops_arg=%p slot_hdl=%p)",
ops_arg, slot_hdl);
mutex_enter(&schpc_p->schpc_mutex);
slot = schpc_slot_get_index(schpc_p, slot_hdl);
if (!(schpc_p->schpc_slot[slot].state & SCHPC_SLOTSTATE_HPCINITED)) {
SCHPC_DEBUG0(D_IOC_CONNECT, "schpc_connect - HPC Not Inited");
mutex_exit(&schpc_p->schpc_mutex);
return (HPC_ERR_FAILED);
}
/*
* Check to see if the slot is already connected.
*/
if (schpc_p->schpc_slot[slot].state & SCHPC_SLOTSTATE_CONNECTED) {
mutex_exit(&schpc_p->schpc_mutex);
return (0);
}
/*
* Block if another thread is executing a HPC command.
*/
while (schpc_p->schpc_slot[slot].state & SCHPC_SLOTSTATE_EXECUTING) {
cv_wait(&schpc_p->schpc_cv, &schpc_p->schpc_mutex);
}
schpc_p->schpc_slot[slot].state |= SCHPC_SLOTSTATE_EXECUTING;
mutex_exit(&schpc_p->schpc_mutex);
expander = schpc_p->schpc_slot[slot].expander; /* get expander */
board = schpc_p->schpc_slot[slot].board; /* get board */
SCHPC_DEBUG3(D_IOC_CONNECT,
"schpc_connect Expander=%x Board=%x Slot=%x",
expander, board, SCHPC_SLOT_NUM(slot));
if (!(schpc_p->schpc_slot[slot].state & SCHPC_SLOTSTATE_OCC_GOOD)) {
cmn_err(CE_WARN, "schpc: Hot Plug - Unable to complete "
"connection on Expander %d Board %d Slot %d - "
"Ap_Id=%s : Occupant is in failed state",
expander, board, SCHPC_SLOT_NUM(slot),
schpc_p->schpc_slot[slot].ap_id);
/* Fault LED should already be illuminated */
goto failed;
}
if (!(schpc_p->schpc_slot[slot].state & SCHPC_SLOTSTATE_REC_GOOD)) {
cmn_err(CE_WARN, "schpc: Hot Plug - Unable to complete "
"connection on Expander %d Board %d Slot %d - "
"Ap_Id=%s : Receptacle is in failed state",
expander, board, SCHPC_SLOT_NUM(slot),
schpc_p->schpc_slot[slot].ap_id);
/* Fault LED should already be illuminated */
goto failed;
}
rval = schpc_getslotstatus(expander, board, slot, &getslot);
if (rval) {
/*
* System Controller/Mailbox failure.
*/
cmn_err(CE_WARN, "schpc - Hot Plug Connection Failed on "
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : Unable to "
"Communicate with System Controller", expander, board,
SCHPC_SLOT_NUM(slot), schpc_p->schpc_slot[slot].ap_id);
schpc_setslotled(expander, board, slot, FAULT_LED_ON);
goto failed;
}
if (getslot.slot_replystatus != PCIMSG_REPLY_GOOD) {
cmn_err(CE_WARN, "schpc - Hot Plug Connection Failed on "
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : Unable to "
"Read Slot Status", expander, board,
SCHPC_SLOT_NUM(slot), schpc_p->schpc_slot[slot].ap_id);
schpc_setslotled(expander, board, slot, FAULT_LED_ON);
goto failed;
}
if (getslot.slot_empty) {
/*
* If the slot is empty - fail the connection request.
*/
goto failed;
}
SCHPC_DEBUG3(D_FREQCHG, "Slot %d - slot_freq_setting %d "
"slot_freq_cap %d", slot, getslot.slot_freq_setting,
getslot.slot_freq_cap);
if (!schpc_is_freq_switchable(slot) &&
(getslot.slot_freq_setting > getslot.slot_freq_cap)) {
cmn_err(CE_WARN, "schpc - Hot Plug Connection Failed "
"on Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
"Bus Speed Mismatch", expander,
board, SCHPC_SLOT_NUM(slot),
schpc_p->schpc_slot[slot].ap_id);
schpc_setslotled(expander, board, slot, FAULT_LED_ON);
goto failed;
}
if (schpc_is_leaf_reset_required(slot) &&
(schpc_p->schpc_slot[slot].saved_regs == NULL)) {
SCHPC_DEBUG1(D_FREQCHG, "Slot %d - Save Regs before connect",
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) {
cmn_err(CE_WARN, "schpc - Unable to save leaf regs on "
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : ",
expander, board, slot & 3,
schpc_p->schpc_slot[slot].ap_id);
schpc_setslotled(expander, board, slot, FAULT_LED_ON);
goto failed;
}
}
/*
* Initialize Set Slot Command.
*/
schpc_init_setslot_message(&setslot);
setslot.slot_power_on = PCIMSG_ON; /* Turn slot power on */
setslot.slot_led_fault = PCIMSG_LED_FLASH; /* Flash Fault LED */
rval = schpc_setslotstatus(expander, board, slot, &setslot);
if (rval != 0) {
/*
* System Controller/Mailbox failure.
*/
cmn_err(CE_WARN, "schpc - Hot Plug Connection Failed on "
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : Unable to "
"Communicate with System Controller", expander, board,
SCHPC_SLOT_NUM(slot), schpc_p->schpc_slot[slot].ap_id);
schpc_setslotled(expander, board, slot, FAULT_LED_ON);
goto failed;
}
if (setslot.slot_replystatus == PCIMSG_REPLY_GOOD) {
/*
* The Request was successfully completed.
*/
SCHPC_DEBUG0(D_IOC_CONNECT, "schpc_connect() - setslotstatus "
"succeeded");
/*
* Need to check HEALTHY# signal.
*/
rval = schpc_getslotstatus(expander, board, slot, &getslot);
if (rval) {
/*
* System Controller/Mailbox failure.
*/
cmn_err(CE_WARN, "schpc - Hot Plug Connection Failed "
"on Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
"Unable to Communicate with System Controller",
expander, board, SCHPC_SLOT_NUM(slot),
schpc_p->schpc_slot[slot].ap_id);
schpc_setslotled(expander, board, slot, FAULT_LED_ON);
goto failed;
}
if (getslot.slot_replystatus != PCIMSG_REPLY_GOOD) {
cmn_err(CE_WARN, "schpc - Hot Plug Connection Failed "
"on Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
"Unable to Read Slot Status", expander, board,
SCHPC_SLOT_NUM(slot),
schpc_p->schpc_slot[slot].ap_id);
schpc_setslotled(expander, board, slot, FAULT_LED_ON);
goto failed;
}
if ((getslot.slot_powergood != PCIMSG_ON) ||
(getslot.slot_powerfault == PCIMSG_ON)) {
cmn_err(CE_WARN, "schpc - Hot Plug Connection Failed "
"on Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
"Power failure detected", expander, board,
SCHPC_SLOT_NUM(slot),
schpc_p->schpc_slot[slot].ap_id);
/*
* Initialize Set Slot Command.
*/
schpc_init_setslot_message(&setslot);
/*
* Turn slot power off.
*/
setslot.slot_power_off = PCIMSG_ON;
(void) schpc_setslotstatus(expander, board,
slot, &setslot);
schpc_setslotled(expander, board, slot,
(SERVICE_LED_ON | FAULT_LED_ON));
goto failed;
}
if (!getslot.slot_HEALTHY) {
cmn_err(CE_WARN, "schpc - Hot Plug Connection Failed "
"on Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
"Adapter did not assert HEALTHY#", expander, board,
SCHPC_SLOT_NUM(slot),
schpc_p->schpc_slot[slot].ap_id);
/*
* Initialize Set Slot Command.
*/
schpc_init_setslot_message(&setslot);
/*
* Turn slot power off.
*/
setslot.slot_power_off = PCIMSG_ON;
(void) schpc_setslotstatus(expander, board, slot,
&setslot);
schpc_setslotled(expander, board, slot,
(SERVICE_LED_ON | FAULT_LED_ON));
goto failed;
}
/*
* Initialize Set Slot Command.
*/
schpc_init_setslot_message(&setslot);
/*
* Start monitoring ENUM# and HEALTHY#
*/
setslot.slot_enable_HEALTHY = PCIMSG_ON;
setslot.slot_enable_ENUM = PCIMSG_ON;
rval = schpc_setslotstatus(expander, board, slot, &setslot);
if (rval != 0) {
/*
* System Controller/Mailbox failure.
*/
cmn_err(CE_WARN, "schpc - Hot Plug Connection Failed "
"on Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
"Unable to Communicate with System Controller",
expander, board, SCHPC_SLOT_NUM(slot),
schpc_p->schpc_slot[slot].ap_id);
schpc_setslotled(expander, board, slot, FAULT_LED_ON);
goto failed;
}
if (setslot.slot_replystatus == PCIMSG_REPLY_GOOD) {
int freq;
find_dev_t find_dev;
/*
* The Request was successfully completed.
*/
SCHPC_DEBUG0(D_IOC_CONNECT,
"schpc_connect() - setslotstatus succeeded");
schpc_p->schpc_slot[slot].state |=
SCHPC_SLOTSTATE_CONNECTED;
schpc_setslotled(expander, board, slot,
(POWER_LED_ON | SERVICE_LED_OFF | FAULT_LED_OFF));
find_dev.cname = schpc_p->schpc_slot[slot].nexus_path;
find_dev.caddr = (char *)kmem_alloc(MAXPATHLEN,
KM_SLEEP);
find_dev.dip = NULL;
/* root node doesn't have to be held */
ddi_walk_devs(ddi_root_node(), schpc_find_dip,
&find_dev);
if (find_dev.dip != NULL) {
/*
* Update the clock-frequency property to
* reflect the new slot-frequency.
*/
freq = schpc_slot_freq(&getslot);
SCHPC_DEBUG2(D_FREQCHG,
"schpc_connect: updating dip=%p freq=%dHZ",
find_dev.dip, freq);
if (ndi_prop_update_int(DDI_DEV_T_NONE,
find_dev.dip, "clock-frequency", freq)
!= DDI_SUCCESS) {
cmn_err(CE_WARN,
"schpc: - failed to update "
"clock-frequency property for %s",
find_dev.cname);
}
ndi_rele_devi(find_dev.dip);
} else {
cmn_err(CE_WARN,
"schpc: couldn't find dip for %s ",
find_dev.cname);
}
kmem_free(find_dev.caddr, MAXPATHLEN);
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
/*
* If leaf registers were saved off, then they
* need to be restored.
*/
schpc_restore_leaf(slot);
/*
* 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);
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (0);
} else {
/*
* The System Controller Rejected the
* connection request.
*/
cmn_err(CE_WARN, "schpc - Hot Plug Connection Failed "
"on Expander %d Board %d PCI Slot %d - Ap_Id=%s :"
"System Controller failed connection request",
expander, board, SCHPC_SLOT_NUM(slot),
schpc_p->schpc_slot[slot].ap_id);
schpc_setslotled(expander, board, slot, FAULT_LED_ON);
goto failed;
}
}
/*
* The System Controller Rejected the connection request.
*/
cmn_err(CE_WARN, "schpc - Hot Plug Connection Failed on "
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : System Controller "
"failed connection request", expander, board, SCHPC_SLOT_NUM(slot),
schpc_p->schpc_slot[slot].ap_id);
schpc_setslotled(expander, board, slot, FAULT_LED_ON);
failed:
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (HPC_ERR_FAILED);
}
/*
* schpc_disconnect()
*
* Called by Hot Plug Services to disconnect a slot to the bus.
*/
/*ARGSUSED*/
static int
schpc_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data,
uint_t flags)
{
int rval;
int expander, board, slot;
pci_setslot_t setslot;
SCHPC_DEBUG2(D_IOC_CONNECT,
"schpc_disconnect( ops_arg=%p slot_hdl=%p)", ops_arg, slot_hdl);
mutex_enter(&schpc_p->schpc_mutex);
slot = schpc_slot_get_index(schpc_p, slot_hdl);
if (!(schpc_p->schpc_slot[slot].state & SCHPC_SLOTSTATE_HPCINITED)) {
SCHPC_DEBUG0(D_IOC_CONNECT,
"schpc_disconnect - HPC Not Inited");
mutex_exit(&schpc_p->schpc_mutex);
return (HPC_ERR_FAILED);
}
/*
* Check to see if we are already disconnected.
*/
if (!(schpc_p->schpc_slot[slot].state & SCHPC_SLOTSTATE_CONNECTED)) {
mutex_exit(&schpc_p->schpc_mutex);
return (0);
}
/*
* Block if another thread is executing a HPC command.
*/
while (schpc_p->schpc_slot[slot].state & SCHPC_SLOTSTATE_EXECUTING) {
cv_wait(&schpc_p->schpc_cv, &schpc_p->schpc_mutex);
}
schpc_p->schpc_slot[slot].state |= SCHPC_SLOTSTATE_EXECUTING;
mutex_exit(&schpc_p->schpc_mutex);
expander = schpc_p->schpc_slot[slot].expander; /* get expander */
board = schpc_p->schpc_slot[slot].board; /* get board */
/*
* If a leaf reset is going to be asserted due to a mode/freq.
* 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) {
cmn_err(CE_WARN, "schpc - Unable to save leaf regs on "
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : ",
expander, board, slot & 3,
schpc_p->schpc_slot[slot].ap_id);
schpc_setslotled(expander, board, slot, FAULT_LED_ON);
goto failed;
}
}
/*
* Initialize Set Slot Command.
*/
schpc_init_setslot_message(&setslot);
setslot.slot_power_off = PCIMSG_ON; /* Turn Power Off */
setslot.slot_led_fault = PCIMSG_LED_FLASH; /* Flash the Fault LED */
setslot.slot_disable_ENUM = PCIMSG_ON; /* Mask the ENUM# signal */
setslot.slot_disable_HEALTHY = PCIMSG_ON; /* Mask the HEALTHY# sig */
rval = schpc_setslotstatus(expander, board, slot, &setslot);
SCHPC_DEBUG1(D_IOC_CONNECT, "schpc_disconnect() - "
"setslotstatus returned 0x%x", rval);
if (rval != 0) {
/*
* System Controller/Mailbox failure.
*/
cmn_err(CE_WARN, "schpc - Hot Plug Disconnection Failed on "
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : Unable to "
"Communicate with System Controller", expander, board,
SCHPC_SLOT_NUM(slot), schpc_p->schpc_slot[slot].ap_id);
schpc_setslotled(expander, board, slot, FAULT_LED_ON);
goto failed;
}
SCHPC_DEBUG1(D_IOC_CONNECT, "schpc_disconnect() - "
"slot_replystatus returned 0x%x", setslot.slot_replystatus);
if (setslot.slot_replystatus == PCIMSG_REPLY_GOOD) {
/*
* The Request was successfully completed.
*/
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_CONNECTED;
schpc_setslotled(expander, board, slot,
(POWER_LED_OFF | SERVICE_LED_ON | FAULT_LED_OFF));
SCHPC_DEBUG0(D_IOC_CONNECT,
"schpc_disconnect() - setslotstatus succeeded");
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (0);
}
/*
* System Controller/Mailbox failure.
*/
cmn_err(CE_WARN, "schpc - Hot Plug Disconnection Failed on "
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : System Controller "
"failed disconnection request", expander, board,
SCHPC_SLOT_NUM(slot),
schpc_p->schpc_slot[slot].ap_id);
schpc_setslotled(expander, board, slot, FAULT_LED_ON);
failed:
schpc_restore_leaf(slot);
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
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
schpc_cpci_control(caddr_t ops_arg, hpc_slot_t slot_hdl, int request,
caddr_t arg)
{
int rval;
int expander, board, slot;
pci_setslot_t setslot;
pci_getslot_t slotstatus;
hpc_led_info_t *hpc_led_info;
SCHPC_DEBUG3(D_IOC_CONTROL,
"schpc_cpci_control(op_args=%p slot_hdl=%p request=%x)",
ops_arg, slot_hdl, request);
mutex_enter(&schpc_p->schpc_mutex);
slot = schpc_slot_get_index(schpc_p, slot_hdl);
if (!(schpc_p->schpc_slot[slot].state & SCHPC_SLOTSTATE_HPCINITED)) {
SCHPC_DEBUG0(D_IOC_CONNECT,
"schpc_disconnect - HPC Not Inited");
mutex_exit(&schpc_p->schpc_mutex);
return (HPC_ERR_FAILED);
}
/*
* Block if another thread is executing a HPC command.
*/
while (schpc_p->schpc_slot[slot].state & SCHPC_SLOTSTATE_EXECUTING) {
cv_wait(&schpc_p->schpc_cv, &schpc_p->schpc_mutex);
}
schpc_p->schpc_slot[slot].state |= SCHPC_SLOTSTATE_EXECUTING;
mutex_exit(&schpc_p->schpc_mutex);
expander = schpc_p->schpc_slot[slot].expander; /* get expander */
board = schpc_p->schpc_slot[slot].board; /* get board */
/*
* Initialize Set Slot Command.
*/
schpc_init_setslot_message(&setslot);
/*
* Initialize LED to last know state.
*/
switch (schpc_p->schpc_slot[slot].led.led_power) {
case LED_ON:
setslot.slot_led_power = PCIMSG_LED_ON;
break;
case LED_OFF:
setslot.slot_led_power = PCIMSG_LED_OFF;
break;
case LED_FLASH:
setslot.slot_led_power = PCIMSG_LED_FLASH;
break;
}
switch (schpc_p->schpc_slot[slot].led.led_service) {
case LED_ON:
setslot.slot_led_service = PCIMSG_LED_ON;
break;
case LED_OFF:
setslot.slot_led_service = PCIMSG_LED_OFF;
break;
case LED_FLASH:
setslot.slot_led_service = PCIMSG_LED_FLASH;
break;
}
switch (schpc_p->schpc_slot[slot].led.led_fault) {
case LED_ON:
setslot.slot_led_fault = PCIMSG_LED_ON;
break;
case LED_OFF:
setslot.slot_led_fault = PCIMSG_LED_OFF;
break;
case LED_FLASH:
setslot.slot_led_fault = PCIMSG_LED_FLASH;
break;
}
switch (request) {
case HPC_CTRL_GET_LED_STATE:
SCHPC_DEBUG0(D_IOC_CONTROL, "schpc_cpci_control() - "
"HPC_CTRL_GET_LED_STATE");
hpc_led_info = (hpc_led_info_t *)arg;
switch (hpc_led_info->led) {
case HPC_FAULT_LED:
switch (schpc_p->schpc_slot[slot].led.led_fault) {
case LED_OFF:
hpc_led_info->state = HPC_LED_OFF;
break;
case LED_ON:
hpc_led_info->state = HPC_LED_ON;
break;
case LED_FLASH:
hpc_led_info->state = HPC_LED_BLINK;
break;
}
break;
case HPC_POWER_LED:
switch (schpc_p->schpc_slot[slot].led.led_power) {
case LED_OFF:
hpc_led_info->state = HPC_LED_OFF;
break;
case LED_ON:
hpc_led_info->state = HPC_LED_ON;
break;
case LED_FLASH:
hpc_led_info->state = HPC_LED_BLINK;
break;
}
break;
case HPC_ATTN_LED:
switch (schpc_p->schpc_slot[slot].led.led_fault) {
case LED_OFF:
hpc_led_info->state = HPC_LED_OFF;
break;
case LED_ON:
hpc_led_info->state = HPC_LED_OFF;
break;
case LED_FLASH:
hpc_led_info->state = HPC_LED_ON;
break;
}
break;
case HPC_ACTIVE_LED:
switch (schpc_p->schpc_slot[slot].led.led_service) {
case LED_OFF:
hpc_led_info->state = HPC_LED_OFF;
break;
case LED_ON:
hpc_led_info->state = HPC_LED_ON;
break;
case LED_FLASH:
hpc_led_info->state = HPC_LED_BLINK;
break;
}
break;
default:
SCHPC_DEBUG1(D_IOC_CONTROL, "schpc_cpci_control() - "
"Invalid LED %x", hpc_led_info->led);
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (HPC_ERR_FAILED);
}
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (0);
case HPC_CTRL_SET_LED_STATE:
hpc_led_info = (hpc_led_info_t *)arg;
SCHPC_DEBUG1(D_IOC_CONTROL, "schpc_cpci_control() - "
"HPC_CTRL_SET_LED_STATE hpc_led_info=%p", hpc_led_info);
switch (hpc_led_info->led) {
case HPC_FAULT_LED:
switch (hpc_led_info->state) {
case HPC_LED_OFF:
schpc_p->schpc_slot[slot].led.led_fault =
LED_OFF;
setslot.slot_led_fault = PCIMSG_LED_OFF;
break;
case HPC_LED_ON:
schpc_p->schpc_slot[slot].led.led_fault =
LED_ON;
setslot.slot_led_fault = PCIMSG_LED_ON;
break;
case HPC_LED_BLINK:
schpc_p->schpc_slot[slot].led.led_fault =
LED_FLASH;
setslot.slot_led_fault = PCIMSG_LED_FLASH;
break;
}
break;
case HPC_POWER_LED:
switch (hpc_led_info->state) {
case HPC_LED_OFF:
schpc_p->schpc_slot[slot].led.led_power =
LED_OFF;
setslot.slot_led_power = PCIMSG_LED_OFF;
break;
case HPC_LED_ON:
schpc_p->schpc_slot[slot].led.led_power =
LED_ON;
setslot.slot_led_power = PCIMSG_LED_ON;
break;
case HPC_LED_BLINK:
schpc_p->schpc_slot[slot].led.led_power =
LED_FLASH;
setslot.slot_led_power = PCIMSG_LED_FLASH;
break;
}
break;
case HPC_ATTN_LED:
switch (hpc_led_info->state) {
case HPC_LED_OFF:
schpc_p->schpc_slot[slot].led.led_fault =
LED_OFF;
setslot.slot_led_fault = PCIMSG_LED_OFF;
break;
case HPC_LED_ON:
schpc_p->schpc_slot[slot].led.led_fault =
LED_FLASH;
setslot.slot_led_fault = PCIMSG_LED_FLASH;
break;
case HPC_LED_BLINK:
schpc_p->schpc_slot[slot].led.led_fault =
LED_FLASH;
setslot.slot_led_fault = PCIMSG_LED_FLASH;
break;
}
break;
case HPC_ACTIVE_LED:
switch (hpc_led_info->state) {
case HPC_LED_OFF:
schpc_p->schpc_slot[slot].led.led_service =
LED_OFF;
setslot.slot_led_service = PCIMSG_LED_OFF;
break;
case HPC_LED_ON:
schpc_p->schpc_slot[slot].led.led_service =
LED_ON;
setslot.slot_led_service = PCIMSG_LED_ON;
break;
case HPC_LED_BLINK:
schpc_p->schpc_slot[slot].led.led_service =
LED_FLASH;
setslot.slot_led_service = PCIMSG_LED_FLASH;
break;
}
break;
default:
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (0);
}
(void) schpc_setslotstatus(expander, board, slot, &setslot);
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (0);
case HPC_CTRL_GET_SLOT_STATE: {
hpc_slot_state_t *hpc_slot_state;
hpc_slot_state = (hpc_slot_state_t *)arg;
SCHPC_DEBUG1(D_IOC_CONTROL, "schpc_cpci_control() - "
"HPC_CTRL_GET_SLOT_STATE hpc_slot_state=%p",
hpc_slot_state);
rval = schpc_getslotstatus(expander, board, slot, &slotstatus);
if (!rval) {
if (slotstatus.slot_replystatus != PCIMSG_REPLY_GOOD) {
return (HPC_ERR_FAILED);
}
if (slotstatus.slot_empty == PCIMSG_ON) {
*hpc_slot_state = HPC_SLOT_EMPTY;
SCHPC_DEBUG0(D_IOC_CONTROL, "Slot Empty");
} else if (slotstatus.slot_power_on == PCIMSG_ON) {
*hpc_slot_state = HPC_SLOT_CONNECTED;
SCHPC_DEBUG0(D_IOC_CONTROL, "Slot Connected");
schpc_p->schpc_slot[slot].state |=
SCHPC_SLOTSTATE_CONNECTED;
} else {
*hpc_slot_state = HPC_SLOT_DISCONNECTED;
SCHPC_DEBUG0(D_IOC_CONTROL,
"Slot Disconnected");
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_CONNECTED;
}
} else {
SCHPC_DEBUG0(D_IOC_CONTROL, "Mailbox Command failed");
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (HPC_ERR_FAILED);
}
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (0);
}
case HPC_CTRL_GET_BOARD_TYPE: {
hpc_board_type_t *hpc_board_type;
hpc_board_type = (hpc_board_type_t *)arg;
SCHPC_DEBUG0(D_IOC_CONTROL, "schpc_cpci_control() - "
"HPC_CTRL_GET_BOARD_TYPE");
/*
* The HPC driver does not know what board type
* is plugged in.
*/
*hpc_board_type = HPC_BOARD_CPCI_HS;
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (0);
}
case HPC_CTRL_DEV_CONFIGURED:
SCHPC_DEBUG0(D_IOC_CONTROL, "schpc_cpci_control() - "
"HPC_CTRL_DEV_CONFIGURED");
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (0);
case HPC_CTRL_DEV_UNCONFIGURED:
SCHPC_DEBUG0(D_IOC_CONTROL, "schpc_cpci_control() - "
"HPC_CTRL_DEV_UNCONFIGURED");
if (schpc_p->schpc_slot[slot].state & SCHPC_SLOTSTATE_ENUM) {
/*
* When the occupant is unconfigured, power
* down the slot.
*/
rval = schpc_disconnect((caddr_t)schpc_p,
schpc_p->schpc_slot[slot].slot_handle,
0, 0);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_ENUM;
}
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (0);
case HPC_CTRL_ENABLE_AUTOCFG:
SCHPC_DEBUG0(D_IOC_CONTROL, "schpc_cpci_control() - "
"HPC_CTRL_ENABLE_AUTOCFG");
schpc_p->schpc_slot[slot].state |=
SCHPC_SLOTSTATE_AUTOCFG_ENABLE;
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (0);
case HPC_CTRL_DISABLE_AUTOCFG:
SCHPC_DEBUG0(D_IOC_CONTROL, "schpc_cpci_control() - "
"HPC_CTRL_DISABLE_AUTOCFG");
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_AUTOCFG_ENABLE;
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (0);
case HPC_CTRL_DISABLE_ENUM:
SCHPC_DEBUG0(D_IOC_CONTROL, "schpc_cpci_control() - "
"HPC_CTRL_DISABLE_ENUM");
setslot.slot_disable_ENUM = PCIMSG_ON;
rval = schpc_setslotstatus(expander, board, slot, &setslot);
if (rval)
rval = HPC_ERR_FAILED;
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (rval);
case HPC_CTRL_ENABLE_ENUM:
SCHPC_DEBUG0(D_IOC_CONTROL, "schpc_cpci_control() - "
"HPC_CTRL_ENABLE_ENUM");
setslot.slot_enable_ENUM = PCIMSG_ON;
rval = schpc_setslotstatus(expander, board, slot, &setslot);
if (rval)
rval = HPC_ERR_FAILED;
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (rval);
default:
SCHPC_DEBUG0(D_IOC_CONTROL, "schpc_cpci_control() - "
"****NOT SUPPORTED CONTROL CMD");
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
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
schpc_pci_control(caddr_t ops_arg, hpc_slot_t slot_hdl, int request,
caddr_t arg)
{
int rval;
int expander, board, slot;
pci_setslot_t setslot;
pci_getslot_t slotstatus;
hpc_led_info_t *hpc_led_info;
SCHPC_DEBUG3(D_IOC_CONTROL,
"schpc_pci_control(op_args=%p slot_hdl=%p request=%x)",
ops_arg, slot_hdl, request);
mutex_enter(&schpc_p->schpc_mutex);
slot = schpc_slot_get_index(schpc_p, slot_hdl);
if (!(schpc_p->schpc_slot[slot].state & SCHPC_SLOTSTATE_HPCINITED)) {
SCHPC_DEBUG0(D_IOC_CONNECT,
"schpc_disconnect - HPC Not Inited");
mutex_exit(&schpc_p->schpc_mutex);
return (HPC_ERR_FAILED);
}
/*
* Block if another thread is executing a HPC command.
*/
while (schpc_p->schpc_slot[slot].state & SCHPC_SLOTSTATE_EXECUTING) {
cv_wait(&schpc_p->schpc_cv, &schpc_p->schpc_mutex);
}
schpc_p->schpc_slot[slot].state |= SCHPC_SLOTSTATE_EXECUTING;
mutex_exit(&schpc_p->schpc_mutex);
expander = schpc_p->schpc_slot[slot].expander; /* get expander */
board = schpc_p->schpc_slot[slot].board; /* get board */
/*
* Initialize Set Slot Command.
*/
schpc_init_setslot_message(&setslot);
/*
* Initialize LED to last know state.
*/
switch (schpc_p->schpc_slot[slot].led.led_power) {
case LED_ON:
setslot.slot_led_power = PCIMSG_LED_ON;
break;
case LED_OFF:
setslot.slot_led_power = PCIMSG_LED_OFF;
break;
case LED_FLASH:
setslot.slot_led_power = PCIMSG_LED_FLASH;
break;
}
switch (schpc_p->schpc_slot[slot].led.led_service) {
case LED_ON:
setslot.slot_led_service = PCIMSG_LED_ON;
break;
case LED_OFF:
setslot.slot_led_service = PCIMSG_LED_OFF;
break;
case LED_FLASH:
setslot.slot_led_service = PCIMSG_LED_FLASH;
break;
}
switch (schpc_p->schpc_slot[slot].led.led_fault) {
case LED_ON:
setslot.slot_led_fault = PCIMSG_LED_ON;
break;
case LED_OFF:
setslot.slot_led_fault = PCIMSG_LED_OFF;
break;
case LED_FLASH:
setslot.slot_led_fault = PCIMSG_LED_FLASH;
break;
}
switch (request) {
case HPC_CTRL_GET_SLOT_STATE: {
hpc_slot_state_t *hpc_slot_state;
hpc_slot_state = (hpc_slot_state_t *)arg;
SCHPC_DEBUG1(D_IOC_CONTROL, "schpc_pci_control() - "
"HPC_CTRL_GET_SLOT_STATE hpc_slot_state=%p",
hpc_slot_state);
rval = schpc_getslotstatus(expander, board, slot, &slotstatus);
if (!rval) {
if (slotstatus.slot_replystatus != PCIMSG_REPLY_GOOD) {
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (HPC_ERR_FAILED);
}
if (slotstatus.slot_empty == PCIMSG_ON) {
*hpc_slot_state = HPC_SLOT_EMPTY;
SCHPC_DEBUG0(D_IOC_CONTROL, "Slot Empty");
} else if (slotstatus.slot_power_on == PCIMSG_ON) {
*hpc_slot_state = HPC_SLOT_CONNECTED;
SCHPC_DEBUG0(D_IOC_CONTROL, "Slot Connected");
schpc_p->schpc_slot[slot].state |=
SCHPC_SLOTSTATE_CONNECTED;
} else {
*hpc_slot_state = HPC_SLOT_DISCONNECTED;
SCHPC_DEBUG0(D_IOC_CONTROL,
"Slot Disconnected");
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_CONNECTED;
}
} else {
SCHPC_DEBUG0(D_IOC_CONTROL, "Mailbox Command failed");
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (HPC_ERR_FAILED);
}
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (0);
}
case HPC_CTRL_GET_BOARD_TYPE: {
hpc_board_type_t *hpc_board_type;
hpc_board_type = (hpc_board_type_t *)arg;
SCHPC_DEBUG0(D_IOC_CONTROL, "schpc_pci_control() - "
"HPC_CTRL_GET_BOARD_TYPE");
/*
* The HPC driver does not know what board type
* is plugged in.
*/
*hpc_board_type = HPC_BOARD_PCI_HOTPLUG;
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (0);
}
case HPC_CTRL_DEV_UNCONFIG_START:
case HPC_CTRL_DEV_CONFIG_START:
case HPC_CTRL_DEV_CONFIGURED:
case HPC_CTRL_DEV_UNCONFIGURED:
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (0);
case HPC_CTRL_GET_LED_STATE:
SCHPC_DEBUG0(D_IOC_CONTROL, "schpc_pci_control() - "
"HPC_CTRL_GET_LED_STATE");
hpc_led_info = (hpc_led_info_t *)arg;
switch (hpc_led_info->led) {
case HPC_FAULT_LED:
switch (schpc_p->schpc_slot[slot].led.led_fault) {
case LED_OFF:
hpc_led_info->state = HPC_LED_OFF;
break;
case LED_ON:
hpc_led_info->state = HPC_LED_ON;
break;
case LED_FLASH:
hpc_led_info->state = HPC_LED_BLINK;
break;
}
break;
case HPC_POWER_LED:
switch (schpc_p->schpc_slot[slot].led.led_power) {
case LED_OFF:
hpc_led_info->state = HPC_LED_OFF;
break;
case LED_ON:
hpc_led_info->state = HPC_LED_ON;
break;
case LED_FLASH:
hpc_led_info->state = HPC_LED_BLINK;
break;
}
break;
case HPC_ATTN_LED:
switch (schpc_p->schpc_slot[slot].led.led_fault) {
case LED_OFF:
hpc_led_info->state = HPC_LED_OFF;
break;
case LED_ON:
hpc_led_info->state = HPC_LED_OFF;
break;
case LED_FLASH:
hpc_led_info->state = HPC_LED_ON;
break;
}
break;
case HPC_ACTIVE_LED:
switch (schpc_p->schpc_slot[slot].led.led_service) {
case LED_OFF:
hpc_led_info->state = HPC_LED_OFF;
break;
case LED_ON:
hpc_led_info->state = HPC_LED_ON;
break;
case LED_FLASH:
hpc_led_info->state = HPC_LED_BLINK;
break;
}
break;
default:
SCHPC_DEBUG1(D_IOC_CONTROL, "schpc_pci_control() - "
"Invalid LED %x", hpc_led_info->led);
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (HPC_ERR_FAILED);
}
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (0);
case HPC_CTRL_SET_LED_STATE:
hpc_led_info = (hpc_led_info_t *)arg;
SCHPC_DEBUG1(D_IOC_CONTROL, "schpc_pci_control() - "
"HPC_CTRL_SET_LED_STATE hpc_led_info=%p", hpc_led_info);
switch (hpc_led_info->led) {
case HPC_FAULT_LED:
switch (hpc_led_info->state) {
case HPC_LED_OFF:
schpc_p->schpc_slot[slot].led.led_fault =
LED_OFF;
setslot.slot_led_fault = PCIMSG_LED_OFF;
break;
case HPC_LED_ON:
schpc_p->schpc_slot[slot].led.led_fault =
LED_ON;
setslot.slot_led_fault = PCIMSG_LED_ON;
break;
case HPC_LED_BLINK:
schpc_p->schpc_slot[slot].led.led_fault =
LED_FLASH;
setslot.slot_led_fault = PCIMSG_LED_FLASH;
break;
}
break;
case HPC_POWER_LED:
switch (hpc_led_info->state) {
case HPC_LED_OFF:
schpc_p->schpc_slot[slot].led.led_power =
LED_OFF;
setslot.slot_led_power = PCIMSG_LED_OFF;
break;
case HPC_LED_ON:
schpc_p->schpc_slot[slot].led.led_power =
LED_ON;
setslot.slot_led_power = PCIMSG_LED_ON;
break;
case HPC_LED_BLINK:
schpc_p->schpc_slot[slot].led.led_power =
LED_FLASH;
setslot.slot_led_power = PCIMSG_LED_FLASH;
break;
}
break;
case HPC_ATTN_LED:
switch (hpc_led_info->state) {
case HPC_LED_OFF:
schpc_p->schpc_slot[slot].led.led_fault =
LED_OFF;
setslot.slot_led_fault = PCIMSG_LED_OFF;
break;
case HPC_LED_ON:
schpc_p->schpc_slot[slot].led.led_fault =
LED_FLASH;
setslot.slot_led_fault = PCIMSG_LED_FLASH;
break;
case HPC_LED_BLINK:
schpc_p->schpc_slot[slot].led.led_fault =
LED_FLASH;
setslot.slot_led_fault = PCIMSG_LED_FLASH;
break;
}
break;
case HPC_ACTIVE_LED:
switch (hpc_led_info->state) {
case HPC_LED_OFF:
schpc_p->schpc_slot[slot].led.led_service =
LED_OFF;
setslot.slot_led_service = PCIMSG_LED_OFF;
break;
case HPC_LED_ON:
schpc_p->schpc_slot[slot].led.led_service =
LED_ON;
setslot.slot_led_service = PCIMSG_LED_ON;
break;
case HPC_LED_BLINK:
schpc_p->schpc_slot[slot].led.led_service =
LED_FLASH;
setslot.slot_led_service = PCIMSG_LED_FLASH;
break;
}
break;
default:
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (0);
}
(void) schpc_setslotstatus(expander, board, slot, &setslot);
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (0);
case HPC_CTRL_ENABLE_AUTOCFG:
SCHPC_DEBUG0(D_IOC_CONTROL, "schpc_pci_control() - "
"HPC_CTRL_ENABLE_AUTOCFG");
schpc_p->schpc_slot[slot].state |=
SCHPC_SLOTSTATE_AUTOCFG_ENABLE;
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (0);
case HPC_CTRL_DISABLE_AUTOCFG:
SCHPC_DEBUG0(D_IOC_CONTROL, "schpc_pci_control() - "
"HPC_CTRL_DISABLE_AUTOCFG");
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_AUTOCFG_ENABLE;
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (0);
case HPC_CTRL_DISABLE_ENUM:
case HPC_CTRL_ENABLE_ENUM:
default:
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
return (HPC_ERR_NOTSUPPORTED);
}
}
/*
* schpc_test
*
* Tests the slot.
*/
/*ARGSUSED*/
static void
schpc_test(caddr_t ops_arg, int slot, void *data, uint_t flags)
{
pci_getslot_t slotstatus;
pci_setslot_t setslot;
int expander, board;
int rval;
int retry = 1;
SCHPC_DEBUG2(D_IOC_TEST, "schpc_test(op_args=%p slot=%x)",
ops_arg, SCHPC_SLOT_NUM(slot));
SCHPC_DEBUG3(D_IOC_TEST,
" schpc_test() Expander=%d Board=%d Slot=%d",
schpc_p->schpc_slot[slot].expander,
schpc_p->schpc_slot[slot].board, SCHPC_SLOT_NUM(slot));
expander = schpc_p->schpc_slot[slot].expander;
board = schpc_p->schpc_slot[slot].board;
restart_test:
/*
* Initial the slot with its occupant and receptacle in good condition.
*/
schpc_p->schpc_slot[slot].state |= SCHPC_SLOTSTATE_REC_GOOD;
schpc_p->schpc_slot[slot].state |= SCHPC_SLOTSTATE_OCC_GOOD;
rval = schpc_getslotstatus(expander, board, slot, &slotstatus);
if (rval) {
/*
* System Controller/Mailbox failure.
*/
cmn_err(CE_WARN, "schpc - Hot Plug Slot Test Failed on "
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : Unable to "
"Communicate with System Controller", expander, board,
SCHPC_SLOT_NUM(slot), schpc_p->schpc_slot[slot].ap_id);
schpc_p->schpc_slot[slot].state &= ~SCHPC_SLOTSTATE_REC_GOOD;
return;
}
if (slotstatus.slot_replystatus != PCIMSG_REPLY_GOOD) {
cmn_err(CE_WARN, "schpc - Expander %d Board %d PCI Slot %d "
"is not hot pluggable\n", expander, board,
SCHPC_SLOT_NUM(slot));
schpc_p->schpc_slot[slot].state &= ~SCHPC_SLOTSTATE_REC_GOOD;
return;
}
switch (slotstatus.slot_condition) {
case PCIMSG_SLOTCOND_OCC_FAIL:
cmn_err(CE_WARN, "schpc - Hot Plug Slot Test Failed on "
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
"System Controller/Occupant Failed",
expander, board, SCHPC_SLOT_NUM(slot),
schpc_p->schpc_slot[slot].ap_id);
schpc_setslotled(expander, board, slot,
(POWER_LED_OFF | SERVICE_LED_ON | FAULT_LED_ON));
schpc_p->schpc_slot[slot].state &= ~SCHPC_SLOTSTATE_OCC_GOOD;
return;
case PCIMSG_SLOTCOND_REC_FAIL:
cmn_err(CE_WARN, "schpc - Hot Plug Slot Test Failed on "
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
"System Controller/Receptacle Failed",
expander, board, SCHPC_SLOT_NUM(slot),
schpc_p->schpc_slot[slot].ap_id);
schpc_setslotled(expander, board, slot,
(POWER_LED_OFF | SERVICE_LED_OFF | FAULT_LED_ON));
schpc_p->schpc_slot[slot].state &= ~SCHPC_SLOTSTATE_REC_GOOD;
return;
case PCIMSG_SLOTCOND_NOHOTPLUG:
cmn_err(CE_WARN, "schpc - Expander %d Board %d PCI Slot %d "
"is not hot pluggable\n", expander, board,
SCHPC_SLOT_NUM(slot));
schpc_p->schpc_slot[slot].state &= ~SCHPC_SLOTSTATE_REC_GOOD;
return;
}
if (slotstatus.slot_power_on) {
schpc_p->schpc_slot[slot].led.led_power = PCIMSG_LED_ON;
if (!slotstatus.slot_HEALTHY) {
/*
* cPCI Adapter is not asserting HEALTHY#.
*/
cmn_err(CE_WARN, "schpc - Hot Plug Slot Test Failed on "
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
"PCI adapter not HEALTHY", expander, board,
SCHPC_SLOT_NUM(slot),
schpc_p->schpc_slot[slot].ap_id);
schpc_setslotled(expander, board, slot,
(POWER_LED_ON | SERVICE_LED_OFF | FAULT_LED_ON));
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_OCC_GOOD;
return;
}
if (!slotstatus.slot_powergood) {
/*
* PCI Power Input is not good.
*/
cmn_err(CE_WARN, "schpc - Hot Plug Slot Test Failed on "
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
"System Controller PCI Power Input Not Good",
expander, board, SCHPC_SLOT_NUM(slot),
schpc_p->schpc_slot[slot].ap_id);
schpc_setslotled(expander, board, slot,
(POWER_LED_ON | SERVICE_LED_OFF | FAULT_LED_ON));
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_OCC_GOOD;
return;
}
if (slotstatus.slot_powerfault) {
/*
* PCI Power Fault.
*/
cmn_err(CE_WARN, "schpc - Hot Plug Slot Test Failed on "
"Expander %d Board %d PCI Slot %d - Ap_Id=%s : "
"System Controller PCI Power Fault",
expander, board, SCHPC_SLOT_NUM(slot),
schpc_p->schpc_slot[slot].ap_id);
schpc_setslotled(expander, board, slot,
(POWER_LED_ON | SERVICE_LED_OFF | FAULT_LED_ON));
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_OCC_GOOD;
return;
}
}
SCHPC_DEBUG0(D_IOC_TEST, "schpc_test() Test Successful - ret 0");
/*
* Is the slot empty?
*/
if (slotstatus.slot_empty) {
SCHPC_DEBUG0(D_IOC_TEST, "schpc_test() Slot Empty");
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_PRESENT;
if (slotstatus.slot_power_on) {
SCHPC_DEBUG0(D_IOC_TEST, "schpc_test() Empty Slot "
"is powered ON");
/*
* Tests will be retried once after powering off
* an empty slot.
*/
if (retry) {
/*
* Turn off the slot and restart test.
*/
SCHPC_DEBUG0(D_IOC_TEST, "schpc_test() "
"Turning Empty Slot OFF");
schpc_init_setslot_message(&setslot);
setslot.slot_power_off = PCIMSG_ON;
(void) schpc_setslotstatus(
expander, board, slot, &setslot);
retry = 0;
goto restart_test;
}
}
} else {
SCHPC_DEBUG0(D_IOC_TEST, "schpc_test() Adapter Present");
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_DEBUG0(D_IOC_TEST,
"schpc_test() Power On Adapter");
schpc_init_setslot_message(&setslot);
setslot.slot_power_on = PCIMSG_ON;
(void) schpc_setslotstatus(
expander, board, slot, &setslot);
retry = 0;
goto restart_test;
}
}
schpc_p->schpc_slot[slot].state |=
SCHPC_SLOTSTATE_PRESENT;
}
/*
* Is the slot powered up?
*/
schpc_init_setslot_message(&setslot);
if (slotstatus.slot_power_on) {
SCHPC_DEBUG0(D_IOC_TEST, "schpc_test() Slot Power On");
schpc_p->schpc_slot[slot].state |=
SCHPC_SLOTSTATE_CONNECTED;
setslot.slot_led_power = PCIMSG_LED_ON;
setslot.slot_led_service = PCIMSG_LED_OFF;
setslot.slot_enable_ENUM = PCIMSG_ON;
setslot.slot_enable_HEALTHY = PCIMSG_ON;
} else {
SCHPC_DEBUG0(D_IOC_TEST, "schpc_test() Slot Power Off");
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_CONNECTED;
setslot.slot_led_power = PCIMSG_LED_OFF;
setslot.slot_led_service = PCIMSG_LED_ON;
setslot.slot_disable_ENUM = PCIMSG_ON;
setslot.slot_disable_HEALTHY = PCIMSG_ON;
}
setslot.slot_led_fault = PCIMSG_LED_OFF;
(void) schpc_setslotstatus(expander, board, slot, &setslot);
/*
* Save LED State.
*/
switch (setslot.slot_led_power) {
case PCIMSG_LED_ON:
schpc_p->schpc_slot[slot].led.led_power = LED_ON;
break;
case PCIMSG_LED_OFF:
schpc_p->schpc_slot[slot].led.led_power = LED_OFF;
break;
case PCIMSG_LED_FLASH:
schpc_p->schpc_slot[slot].led.led_power = LED_FLASH;
break;
}
switch (setslot.slot_led_service) {
case PCIMSG_LED_ON:
schpc_p->schpc_slot[slot].led.led_service = LED_ON;
break;
case PCIMSG_LED_OFF:
schpc_p->schpc_slot[slot].led.led_service = LED_OFF;
break;
case PCIMSG_LED_FLASH:
schpc_p->schpc_slot[slot].led.led_service = LED_FLASH;
break;
}
switch (setslot.slot_led_fault) {
case PCIMSG_LED_ON:
schpc_p->schpc_slot[slot].led.led_fault = LED_ON;
break;
case PCIMSG_LED_OFF:
schpc_p->schpc_slot[slot].led.led_fault = LED_OFF;
break;
case PCIMSG_LED_FLASH:
schpc_p->schpc_slot[slot].led.led_fault = 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
* things like power insertion/removal, ENUM#, etc.
*/
static void
schpc_event_handler(void *arg)
{
pci_getslot_t slotstatus;
uint8_t expander, board, slot;
int rval;
pcimsg_t *event = (pcimsg_t *)arg;
/*
* 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.
*/
slot = event->pcimsg_slot;
expander = event->pcimsg_node; /* get expander */
board = event->pcimsg_board; /* get board */
SCHPC_DEBUG3(D_EVENT,
"schpc_event_handler() - exp=%d board=%d slot=%d",
expander, board, slot);
/* create a slot table index */
slot = SCHPC_MAKE_SLOT_INDEX2(expander, slot);
SCHPC_DEBUG1(D_EVENT,
"schpc_event_handler() - expanded slot %d", slot);
if (schpc_p == NULL) {
cmn_err(CE_WARN, "schpc/Event Handler - Can not find schpc");
kmem_free(event, sizeof (pcimsg_t));
return;
}
mutex_enter(&schpc_p->schpc_mutex);
if (!(schpc_p->schpc_slot[slot].state & SCHPC_SLOTSTATE_HPCINITED)) {
SCHPC_DEBUG0(D_EVENT, "schpc_event_handler - HPC Not Inited");
mutex_exit(&schpc_p->schpc_mutex);
kmem_free(event, sizeof (pcimsg_t));
return;
}
/*
* Block if another thread is executing a HPC command.
*/
while (schpc_p->schpc_slot[slot].state & SCHPC_SLOTSTATE_EXECUTING) {
SCHPC_DEBUG0(D_EVENT, "schpc_event_handler - Slot is busy");
cv_wait(&schpc_p->schpc_cv, &schpc_p->schpc_mutex);
}
schpc_p->schpc_slot[slot].state |= SCHPC_SLOTSTATE_EXECUTING;
mutex_exit(&schpc_p->schpc_mutex);
rval = schpc_getslotstatus(expander, board, slot, &slotstatus);
if (rval) {
cmn_err(CE_WARN, "schpc/Event Handler - Can not get status "
"for expander=%d board=%d slot=%d\n",
expander, board, SCHPC_SLOT_NUM(slot));
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
kmem_free(event, sizeof (pcimsg_t));
return;
}
if (slotstatus.slot_replystatus != PCIMSG_REPLY_GOOD) {
cmn_err(CE_WARN, "schpc/Event Handler - Can not get good "
"status for expander=%d board=%d slot=%d\n",
expander, board, SCHPC_SLOT_NUM(slot));
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
kmem_free(event, sizeof (pcimsg_t));
return;
}
SCHPC_DEBUG3(D_EVENT, "Event Received - Expander %d Board %d Slot %d",
expander, board, SCHPC_SLOT_NUM(slot));
if (schpc_p->schpc_slot[slot].slot_ops == NULL) {
SCHPC_DEBUG3(D_EVENT, "schpc/Event Handler - Received event "
"for unregistered slot for expander=%d board=%d slot=%d",
expander, board, SCHPC_SLOT_NUM(slot));
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
kmem_free(event, sizeof (pcimsg_t));
return;
}
/* Slot Power Event */
if (event->pcimsg_type.pcimsg_slotevent.slot_power) {
SCHPC_DEBUG0(D_EVENT, "Event Type: Slot Power Event");
/*
* The SC may have changed to slot power status.
*/
if (slotstatus.slot_power_on) {
schpc_p->schpc_slot[slot].state |=
SCHPC_SLOTSTATE_CONNECTED;
hpc_slot_event_notify(
schpc_p->schpc_slot[slot].slot_handle,
HPC_EVENT_SLOT_POWER_ON, 0);
} else {
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_CONNECTED;
hpc_slot_event_notify(
schpc_p->schpc_slot[slot].slot_handle,
HPC_EVENT_SLOT_POWER_OFF, 0);
}
}
/* Adapter Insertion/Removal Event */
if (event->pcimsg_type.pcimsg_slotevent.slot_presence) {
if (slotstatus.slot_empty == PCIMSG_ON) {
/* Adapter Removed */
SCHPC_DEBUG0(D_EVENT, "Event Type: Adapter Removed");
if (schpc_p->schpc_slot[slot].state &
SCHPC_SLOTSTATE_CONNECTED) {
/*
* If the adapter has been removed while
* there the slot is connected, it could be
* due to a ENUM handling.
*/
cmn_err(CE_WARN, "Card removed from "
"powered on slot at "
"expander=%d board=%d slot=%d\n",
expander, board, SCHPC_SLOT_NUM(slot));
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
rval = schpc_disconnect((caddr_t)schpc_p,
schpc_p->schpc_slot[slot].slot_handle,
0, 0);
mutex_enter(&schpc_p->schpc_mutex);
while (schpc_p->schpc_slot[slot].state &
SCHPC_SLOTSTATE_EXECUTING) {
SCHPC_DEBUG0(D_EVENT,
"schpc_event_handler - "
"Slot is busy");
cv_wait(&schpc_p->schpc_cv,
&schpc_p->schpc_mutex);
}
schpc_p->schpc_slot[slot].state |=
SCHPC_SLOTSTATE_EXECUTING;
mutex_exit(&schpc_p->schpc_mutex);
}
schpc_p->schpc_slot[slot].state |=
SCHPC_SLOTSTATE_OCC_GOOD;
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_PRESENT;
hpc_slot_event_notify(
schpc_p->schpc_slot[slot].slot_handle,
HPC_EVENT_SLOT_REMOVAL, 0);
} else {
/* Adapter Inserted */
SCHPC_DEBUG0(D_EVENT, "Event Type: Adapter Inserted");
if (schpc_p->schpc_slot[slot].state &
SCHPC_SLOTSTATE_PRESENT) {
/*
* If the adapter is already present
* throw the this event away.
*/
SCHPC_DEBUG0(D_EVENT,
"Adapter is already present");
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
kmem_free(event, sizeof (pcimsg_t));
return;
}
schpc_p->schpc_slot[slot].state |=
SCHPC_SLOTSTATE_PRESENT;
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_CONNECTED;
hpc_slot_event_notify(
schpc_p->schpc_slot[slot].slot_handle,
HPC_EVENT_SLOT_INSERTION, 0);
if (schpc_p->schpc_slot[slot].state &
SCHPC_SLOTSTATE_AUTOCFG_ENABLE) {
SCHPC_DEBUG0(D_EVENT, "Auto Configuration "
"(Connect/Configure) Started");
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
rval = schpc_connect((caddr_t)schpc_p,
schpc_p->schpc_slot[slot].slot_handle,
0, 0);
if (rval) {
cmn_err(CE_WARN, "schpc/Event Handler -"
" Can not connect");
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
kmem_free(event, sizeof (pcimsg_t));
return;
}
mutex_enter(&schpc_p->schpc_mutex);
while (schpc_p->schpc_slot[slot].state &
SCHPC_SLOTSTATE_EXECUTING) {
SCHPC_DEBUG0(D_EVENT,
"schpc_event_handler - "
"Slot is busy");
cv_wait(&schpc_p->schpc_cv,
&schpc_p->schpc_mutex);
}
schpc_p->schpc_slot[slot].state |=
SCHPC_SLOTSTATE_EXECUTING;
mutex_exit(&schpc_p->schpc_mutex);
hpc_slot_event_notify(
schpc_p->schpc_slot[slot].slot_handle,
HPC_EVENT_SLOT_CONFIGURE, 0);
} else {
schpc_setslotled(expander, board, slot,
SERVICE_LED_ON);
}
}
}
/* ENUM# signal change event */
if (event->pcimsg_type.pcimsg_slotevent.slot_ENUM) {
/*
* ENUM should only be received to the adapter remove
* procedure.
*/
SCHPC_DEBUG0(D_EVENT, "Event Type: ENUM Asserted");
schpc_setslotled(expander, board, slot, FAULT_LED_FLASH);
schpc_p->schpc_slot[slot].state |= SCHPC_SLOTSTATE_ENUM;
hpc_slot_event_notify(
schpc_p->schpc_slot[slot].slot_handle,
HPC_EVENT_SLOT_ENUM, 0);
}
/* HEALTHY# signal change event */
if (event->pcimsg_type.pcimsg_slotevent.slot_HEALTHY) {
if (!slotstatus.slot_HEALTHY) {
SCHPC_DEBUG0(D_EVENT, "Event Type: !HEALTHY ASSERTED");
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_OCC_GOOD;
hpc_slot_event_notify(
schpc_p->schpc_slot[slot].slot_handle,
HPC_EVENT_SLOT_NOT_HEALTHY, 0);
schpc_setslotled(expander, board, slot, FAULT_LED_ON);
} else {
SCHPC_DEBUG0(D_EVENT, "Event Type: HEALTHY OK");
schpc_p->schpc_slot[slot].state |=
SCHPC_SLOTSTATE_OCC_GOOD;
hpc_slot_event_notify(
schpc_p->schpc_slot[slot].slot_handle,
HPC_EVENT_SLOT_HEALTHY_OK, 0);
schpc_setslotled(expander, board, slot,
FAULT_LED_OFF);
}
}
/* Good Power change event */
if (event->pcimsg_type.pcimsg_slotevent.slot_powergood) {
if (slotstatus.slot_powergood == PCIMSG_ON) {
SCHPC_DEBUG0(D_EVENT,
"Event Type: Slot Power Good Detected");
schpc_p->schpc_slot[slot].state |=
SCHPC_SLOTSTATE_OCC_GOOD;
hpc_slot_event_notify(
schpc_p->schpc_slot[slot].slot_handle,
HPC_EVENT_SLOT_HEALTHY_OK, 0);
schpc_setslotled(expander, board, slot,
FAULT_LED_OFF);
} else {
SCHPC_DEBUG0(D_EVENT, "Event Type: Slot Power Not Good "
"Detected");
if (schpc_p->schpc_slot[slot].state &
SCHPC_SLOTSTATE_CONNECTED) {
SCHPC_DEBUG0(D_EVENT, "Slot Power Not Good: "
"power failed");
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_OCC_GOOD;
hpc_slot_event_notify(
schpc_p->schpc_slot[slot].slot_handle,
HPC_EVENT_SLOT_NOT_HEALTHY, 0);
schpc_setslotled(expander, board, slot,
FAULT_LED_ON);
}
}
}
/* Power Fault change event */
if (event->pcimsg_type.pcimsg_slotevent.slot_powerfault) {
if (slotstatus.slot_powerfault == PCIMSG_ON) {
SCHPC_DEBUG0(D_EVENT, "Event Type: Slot Power Fault "
"Detected");
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_OCC_GOOD;
hpc_slot_event_notify(
schpc_p->schpc_slot[slot].slot_handle,
HPC_EVENT_SLOT_NOT_HEALTHY, 0);
schpc_setslotled(expander, board, slot, FAULT_LED_ON);
} else {
SCHPC_DEBUG0(D_EVENT, "Event Type: Slot Power Fault "
"Cleared");
schpc_p->schpc_slot[slot].state |=
SCHPC_SLOTSTATE_OCC_GOOD;
hpc_slot_event_notify(
schpc_p->schpc_slot[slot].slot_handle,
HPC_EVENT_SLOT_HEALTHY_OK, 0);
schpc_setslotled(expander, board, slot,
FAULT_LED_OFF);
}
}
mutex_enter(&schpc_p->schpc_mutex);
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_EXECUTING;
cv_signal(&schpc_p->schpc_cv);
mutex_exit(&schpc_p->schpc_mutex);
kmem_free(event, sizeof (pcimsg_t));
}
/*
* 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
schpc_event_filter(pcimsg_t *pmsg)
{
if (slots_registered == B_TRUE) {
pcimsg_t *pevent;
/*
* If hotpluggable pci slots have been registered then enqueue
* the event onto the schpc_event_taskq for processing.
*/
SCHPC_DEBUG0(D_EVENT, "schpc_event_filter() - "
"slots_registered = B_TRUE");
pevent = (pcimsg_t *)kmem_zalloc(sizeof (pcimsg_t), KM_SLEEP);
bcopy(pmsg, pevent, sizeof (pcimsg_t));
SCHPC_DEBUG0(D_EVENT, "schpc_event_filter() - "
"event alloc'd");
if (taskq_dispatch(schpc_event_taskq, schpc_event_handler,
(void *)pevent, TQ_SLEEP) == NULL) {
cmn_err(CE_WARN, "schpc: schpc_event_filter - "
"taskq_dispatch failed to enqueue event");
kmem_free(pevent, sizeof (pcimsg_t));
return;
}
SCHPC_DEBUG0(D_EVENT, "schpc_event_filter() - "
"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.
*/
cmn_err(CE_WARN, "schpc: schpc_event_filter - discarding "
"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;
uint32_t type;
uint32_t cmd;
uint64_t transid;
uint32_t length;
pcimsg_t msg;
SCHPC_DEBUG0(D_THREAD, "schpc_msg_thread() running");
/* CONSTCOND */
while (1) {
/* setup wildcard arguments */
type = 0;
cmd = 0;
transid = 0;
length = sizeof (pcimsg_t);
bzero(&msg, sizeof (pcimsg_t));
err = mboxsc_getmsg(KEY_SCPC, &type, &cmd,
&transid, &length, (void *)&msg,
schpc_timeout_getmsg);
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.
*/
cmn_err(CE_WARN,
"schpc - mboxsc_getmsg failed, err=0x%x", err);
delay(drv_usectohz(100000));
continue;
}
}
if (msg.pcimsg_revision != PCIMSG_REVISION) {
/*
* This version of the schpc driver only understands
* version 1.0 of the PCI Hot Plug Message format.
*/
cmn_err(CE_WARN, " schpc: schpc_msg_thread - "
"discarding event w/ unknown message version %x",
msg.pcimsg_revision);
continue;
}
switch (type) {
case MBOXSC_MSG_EVENT:
schpc_event_filter(&msg);
break;
case MBOXSC_MSG_REPLY:
schpc_reply_handler(&msg, type, cmd, transid, length);
break;
default:
cmn_err(CE_WARN,
"schpc - mboxsc_getmsg unknown msg"
" type=0x%x", type);
break;
}
}
/* this thread never exits */
}
void
schpc_reply_handler(pcimsg_t *pmsg, uint32_t type, uint32_t cmd,
uint64_t transid, uint32_t length)
{
schpc_replylist_t *entry;
mutex_enter(&schpc_replylist_mutex);
entry = schpc_replylist_first;
while (entry != NULL) {
if (entry->transid == transid) {
break;
} else
entry = entry->next;
}
if (entry) {
SCHPC_DEBUG1(D_GETSLOTSTATUS|D_SETSLOTSTATUS,
"schpc_reply_handler() - 0x%lx transid reply "
"received", transid);
mutex_enter(&entry->reply_lock);
if (entry->reply_cexit == B_FALSE) {
SCHPC_DEBUG1(D_GETSLOTSTATUS|D_SETSLOTSTATUS,
"schpc_reply_handler() - 0x%lx transid"
" cv_signal waiting thread", transid);
/*
* emulate mboxsc_getmsg by copying the reply
*/
entry->type = type;
entry->cmd = cmd;
entry->transid = transid;
entry->length = length;
bcopy((caddr_t)pmsg, &entry->reply, length);
/* reply was received */
entry->reply_recvd = B_TRUE;
/*
* wake up thread waiting for reply with transid
*/
cv_signal(&entry->reply_cv);
}
mutex_exit(&entry->reply_lock);
} else {
cmn_err(CE_WARN, "schpc - no match for transid 0x%lx",
transid);
}
mutex_exit(&schpc_replylist_mutex);
}
/*
* schpc_putrequest
*
* A wrapper around the synchronous call mboxsc_putmsg().
*/
int
schpc_putrequest(uint32_t key, uint32_t type, uint32_t cmd, uint64_t *transidp,
uint32_t length, void *datap, clock_t timeout,
schpc_replylist_t **entryp)
{
int rval;
/* add the request to replylist to keep track of outstanding requests */
*entryp = schpc_replylist_link(cmd, *transidp, length);
SCHPC_DEBUG1(D_GETSLOTSTATUS|D_SETSLOTSTATUS, "schpc_putrequest() - "
"0x%lx transid mboxsc_putmsg called", *transidp);
/* wait synchronously for request to be sent */
rval = mboxsc_putmsg(key, type, cmd, transidp, length,
(void *)datap, timeout);
SCHPC_DEBUG2(D_GETSLOTSTATUS|D_SETSLOTSTATUS, "schpc_putrequest() - "
"0x%lx transid mboxsc_putmsg returned 0x%x", *transidp, rval);
/* if problem is encountered then remove the request from replylist */
if (rval)
schpc_replylist_unlink(*entryp);
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
schpc_getreply(uint32_t key, uint32_t *typep, uint32_t *cmdp,
uint64_t *transidp, uint32_t *lengthp, void *datap,
clock_t timeout, schpc_replylist_t *listp)
{
int rc = 0;
SCHPC_DEBUG1(D_GETSLOTSTATUS|D_SETSLOTSTATUS,
"schpc_getreply() - 0x%lx transid waiting for reply",
*transidp);
/*
* wait here until schpc_msg_thread because it's always
* looking for reply messages
*/
mutex_enter(&listp->reply_lock);
while (listp->reply_recvd == B_FALSE) {
/*
* wait for reply or timeout
*/
rc = cv_timedwait(&listp->reply_cv, &listp->reply_lock,
ddi_get_lbolt() + drv_usectohz(timeout * 1000));
switch (rc) {
case -1: /* most likely a timeout, but check anyway */
/* message was received after all */
if (listp->reply_recvd == B_TRUE)
break;
/* no, it's really a timeout */
listp->reply_cexit = B_TRUE;
mutex_exit(&listp->reply_lock);
cmn_err(CE_WARN,
"schpc - 0x%lx transid reply timed out", *transidp);
schpc_replylist_unlink(listp);
return (ETIMEDOUT);
default:
break;
}
}
*typep = listp->type;
*cmdp = listp->cmd;
*transidp = listp->transid;
*lengthp = listp->length;
bcopy((caddr_t)&listp->reply, datap, *lengthp);
mutex_exit(&listp->reply_lock);
SCHPC_DEBUG1(D_GETSLOTSTATUS|D_SETSLOTSTATUS,
"schpc_getreply() - 0x%lx transid received", *transidp);
schpc_replylist_unlink(listp);
return (0);
}
/*
* schpc_replylist_unlink
*
* Deallocate a schpc_replylist_t element.
*/
void
schpc_replylist_unlink(schpc_replylist_t *entry)
{
#if DEBUG
schpc_replylist_t *dbg_entry;
#endif /* DEBUG */
SCHPC_DEBUG1(D_GETSLOTSTATUS|D_SETSLOTSTATUS,
"schpc_replylist_unlink() - 0x%lx transid deleted from replylist",
entry->transid);
mutex_enter(&schpc_replylist_mutex);
if (entry->prev) {
entry->prev->next = entry->next;
if (entry->next)
entry->next->prev = entry->prev;
} else {
schpc_replylist_first = entry->next;
if (entry->next)
entry->next->prev = NULL;
}
if (entry == schpc_replylist_last) {
schpc_replylist_last = entry->prev;
}
kmem_free(entry, sizeof (schpc_replylist_t));
schpc_replylist_count--;
#if DEBUG
if (schpc_debug_flags & (D_GETSLOTSTATUS|D_SETSLOTSTATUS)) {
dbg_entry = schpc_replylist_first;
cmn_err(CE_CONT, "schpc: schpc_replylist_unlink() - replylist "
"count = %d\n", schpc_replylist_count);
while (dbg_entry != NULL) {
cmn_err(CE_CONT, "schpc: schpc_replylist_unlink() - "
"0x%lx transid\n", dbg_entry->transid);
dbg_entry = dbg_entry->next;
}
}
#endif /* DEBUG */
mutex_exit(&schpc_replylist_mutex);
}
/*
* schpc_replylist_link
*
* Allocate and initialize a schpc_replylist_t element.
*/
schpc_replylist_t *
schpc_replylist_link(uint32_t cmd, uint64_t transid, uint32_t length)
{
schpc_replylist_t *entry;
#if DEBUG
schpc_replylist_t *dbg_entry;
#endif /* DEBUG */
SCHPC_DEBUG1(D_GETSLOTSTATUS|D_SETSLOTSTATUS,
"schpc_replylist_link() - 0x%lx transid inserting into replylist",
transid);
entry = kmem_zalloc(sizeof (schpc_replylist_t), KM_SLEEP);
mutex_init(&entry->reply_lock, NULL, MUTEX_DRIVER, NULL);
cv_init(&entry->reply_cv, NULL, CV_DRIVER, NULL);
entry->type = MBOXSC_MSG_REPLY;
entry->cmd = cmd;
entry->transid = transid;
entry->length = length;
entry->reply_recvd = B_FALSE;
entry->reply_cexit = B_FALSE;
mutex_enter(&schpc_replylist_mutex);
if (schpc_replylist_last) {
entry->prev = schpc_replylist_last;
schpc_replylist_last->next = entry;
schpc_replylist_last = entry;
} else {
schpc_replylist_last = schpc_replylist_first = entry;
}
schpc_replylist_count++;
#if DEBUG
if (schpc_debug_flags & (D_GETSLOTSTATUS|D_SETSLOTSTATUS)) {
dbg_entry = schpc_replylist_first;
cmn_err(CE_CONT, "schpc: schpc_replylist_link() - replylist "
"count = %d\n", schpc_replylist_count);
while (dbg_entry != NULL) {
cmn_err(CE_CONT, "schpc: schpc_replylist_link() - "
"0x%lx transid\n", dbg_entry->transid);
dbg_entry = dbg_entry->next;
}
}
#endif /* DEBUG */
mutex_exit(&schpc_replylist_mutex);
return (entry);
}
/*
* schpc_getslotstatus
*
* Issues a Get Slot Status command to the System Controller
* for a specific slot.
*/
static int
schpc_getslotstatus(uint32_t expander, uint32_t board, uint32_t slot,
pci_getslot_t *slotstatus)
{
pcimsg_t request;
pcimsg_t reply;
int rval;
uint32_t type, cmd, length;
uint64_t transid;
schpc_replylist_t *entry;
SCHPC_DEBUG4(D_GETSLOTSTATUS,
"schpc_getslotstatus(expander=%d board=%d "
"slot=%d slotstatus=0x%p", expander, board,
SCHPC_SLOT_NUM(slot), slotstatus);
if (schpc_p == NULL) {
return (1);
}
bzero(&request, sizeof (pcimsg_t));
request.pcimsg_node = expander;
request.pcimsg_board = board;
request.pcimsg_slot = SCHPC_SLOT_NUM(slot);
request.pcimsg_revision = PCIMSG_REVISION;
request.pcimsg_command = PCIMSG_GETSLOTSTATUS;
type = MBOXSC_MSG_REQUEST;
cmd = PCIMSG_GETSLOTSTATUS;
transid = schpc_gettransid(schpc_p, slot);
length = sizeof (pcimsg_t);
SCHPC_DEBUG1(D_GETSLOTSTATUS, "schpc_getslotstatus() - "
"0x%lx transid schpc_putrequest called", transid);
rval = schpc_putrequest(KEY_PCSC, type, cmd, &transid, length,
(void *)&request, schpc_timeout_putmsg, &entry);
SCHPC_DEBUG2(D_GETSLOTSTATUS, "schpc_getslotstatus() - "
"0x%lx transid schpc_putrequest returned 0x%x", transid, rval);
if (rval) {
return (rval);
}
bzero(&reply, sizeof (pcimsg_t));
type = MBOXSC_MSG_REPLY;
SCHPC_DEBUG1(D_GETSLOTSTATUS, "schpc_getslotstatus() - "
"0x%lx transid schpc_getreply called", transid);
rval = schpc_getreply(KEY_SCPC, &type, &cmd, &transid, &length,
(void *)&reply, schpc_timeout_getmsg, entry);
SCHPC_DEBUG2(D_GETSLOTSTATUS, "schpc_getslotstatus() - "
"0x%lx transid schpc_getreply returned 0x%x", transid, rval);
if (rval == 0) {
*slotstatus = reply.pcimsg_type.pcimsg_getslot;
SCHPC_DEBUG0(D_GETSLOTSTATUS, "schpc_getslotstatus()");
SCHPC_DEBUG1(D_GETSLOTSTATUS, " slot_power_on %x",
reply.pcimsg_type.pcimsg_getslot.slot_power_on);
SCHPC_DEBUG1(D_GETSLOTSTATUS, " slot_powergood %x",
reply.pcimsg_type.pcimsg_getslot.slot_powergood);
SCHPC_DEBUG1(D_GETSLOTSTATUS, " slot_powerfault %x",
reply.pcimsg_type.pcimsg_getslot.slot_powerfault);
SCHPC_DEBUG1(D_GETSLOTSTATUS, " slot_empty %x",
reply.pcimsg_type.pcimsg_getslot.slot_empty);
SCHPC_DEBUG1(D_GETSLOTSTATUS, " slot_freq_cap %x",
reply.pcimsg_type.pcimsg_getslot.slot_freq_cap);
SCHPC_DEBUG1(D_GETSLOTSTATUS, " slot_freq_setting %x",
reply.pcimsg_type.pcimsg_getslot.slot_freq_setting);
SCHPC_DEBUG1(D_GETSLOTSTATUS, " slot_condition %x",
reply.pcimsg_type.pcimsg_getslot.slot_condition);
SCHPC_DEBUG1(D_GETSLOTSTATUS, " slot_HEALTHY %x",
reply.pcimsg_type.pcimsg_getslot.slot_HEALTHY);
SCHPC_DEBUG1(D_GETSLOTSTATUS, " slot_ENUM %x",
reply.pcimsg_type.pcimsg_getslot.slot_ENUM);
}
return (rval);
}
/*
* schpc_setslotstatus
*
* Issues a Set Slot Status command to the System Controller
* for a specific slot.
*/
static int
schpc_setslotstatus(uint32_t expander, uint32_t board, uint32_t slot,
pci_setslot_t *slotstatus)
{
pcimsg_t request;
pcimsg_t reply;
int rval;
uint32_t type, cmd, length;
uint64_t transid;
schpc_replylist_t *entry;
SCHPC_DEBUG4(D_SETSLOTSTATUS,
"schpc_setslotstatus(expander=%d board=%d "
"slot=%d slotstatus=0x%p", expander, board,
SCHPC_SLOT_NUM(slot), slotstatus);
bzero(&request, sizeof (pcimsg_t));
if (schpc_p == NULL) {
return (1);
}
request.pcimsg_node = expander;
request.pcimsg_board = board;
request.pcimsg_slot = SCHPC_SLOT_NUM(slot);
request.pcimsg_revision = PCIMSG_REVISION;
request.pcimsg_command = PCIMSG_SETSLOTSTATUS;
request.pcimsg_type.pcimsg_setslot = *slotstatus;
SCHPC_DEBUG0(D_IOC_LED, "schpc_setslotstatus() - LED state change");
SCHPC_DEBUG3(D_IOC_LED, "LED Power %d Service %d Fault %d",
slotstatus->slot_led_power,
slotstatus->slot_led_service,
slotstatus->slot_led_fault);
type = MBOXSC_MSG_REQUEST;
cmd = PCIMSG_SETSLOTSTATUS;
transid = schpc_gettransid(schpc_p, slot);
length = sizeof (pcimsg_t);
SCHPC_DEBUG1(D_SETSLOTSTATUS, "schpc_setslotstatus() - "
"0x%lx transid schpc_putrequest called", transid);
rval = schpc_putrequest(KEY_PCSC, type, cmd, &transid, length,
(void *)&request, schpc_timeout_putmsg, &entry);
SCHPC_DEBUG2(D_SETSLOTSTATUS, "schpc_setslotstatus() - "
"0x%lx transid schpc_putrequest returned 0x%x", transid, rval);
if (rval) {
return (rval);
}
bzero(&reply, sizeof (pcimsg_t));
type = MBOXSC_MSG_REPLY;
SCHPC_DEBUG1(D_SETSLOTSTATUS, "schpc_setslotstatus() - "
"0x%lx transid schpc_getreply called", transid);
rval = schpc_getreply(KEY_SCPC, &type, &cmd, &transid, &length,
(void *)&reply, schpc_timeout_getmsg, entry);
SCHPC_DEBUG2(D_SETSLOTSTATUS, "schpc_setslotstatus() - "
"0x%lx transid schpc_getreply returned 0x%x", transid, rval);
if (rval == 0) {
slotstatus->slot_replystatus =
reply.pcimsg_type.pcimsg_setslot.slot_replystatus;
}
return (rval);
}
/*
* schpc_setslotled
*
* Changes the attention indicators for a given slot.
*/
static void
schpc_setslotled(int expander, int board, int slot, uint32_t led_state)
{
pci_setslot_t setslot;
if (schpc_p == NULL) {
return;
}
schpc_init_setslot_message(&setslot);
if (led_state & POWER_LED_ON) {
schpc_p->schpc_slot[slot].led.led_power = PCIMSG_LED_ON;
}
if (led_state & POWER_LED_OFF) {
schpc_p->schpc_slot[slot].led.led_power = PCIMSG_LED_OFF;
}
if (led_state & POWER_LED_FLASH) {
schpc_p->schpc_slot[slot].led.led_power = PCIMSG_LED_FLASH;
}
if (led_state & SERVICE_LED_ON) {
schpc_p->schpc_slot[slot].led.led_service = PCIMSG_LED_ON;
}
if (led_state & SERVICE_LED_OFF) {
schpc_p->schpc_slot[slot].led.led_service = PCIMSG_LED_OFF;
}
if (led_state & SERVICE_LED_FLASH) {
schpc_p->schpc_slot[slot].led.led_service = PCIMSG_LED_FLASH;
}
if (led_state & FAULT_LED_ON) {
schpc_p->schpc_slot[slot].led.led_fault = PCIMSG_LED_ON;
}
if (led_state & FAULT_LED_OFF) {
schpc_p->schpc_slot[slot].led.led_fault = PCIMSG_LED_OFF;
}
if (led_state & FAULT_LED_FLASH) {
schpc_p->schpc_slot[slot].led.led_fault = PCIMSG_LED_FLASH;
}
switch (schpc_p->schpc_slot[slot].led.led_power) {
case PCIMSG_LED_ON:
setslot.slot_led_power = PCIMSG_LED_ON;
break;
case PCIMSG_LED_OFF:
setslot.slot_led_power = PCIMSG_LED_OFF;
break;
case PCIMSG_LED_FLASH:
setslot.slot_led_power = PCIMSG_LED_FLASH;
break;
}
switch (schpc_p->schpc_slot[slot].led.led_service) {
case PCIMSG_LED_ON:
setslot.slot_led_service = PCIMSG_LED_ON;
break;
case PCIMSG_LED_OFF:
setslot.slot_led_service = PCIMSG_LED_OFF;
break;
case PCIMSG_LED_FLASH:
setslot.slot_led_service = PCIMSG_LED_FLASH;
break;
}
switch (schpc_p->schpc_slot[slot].led.led_fault) {
case PCIMSG_LED_ON:
setslot.slot_led_fault = PCIMSG_LED_ON;
break;
case PCIMSG_LED_OFF:
setslot.slot_led_fault = PCIMSG_LED_OFF;
break;
case PCIMSG_LED_FLASH:
setslot.slot_led_fault = PCIMSG_LED_FLASH;
break;
}
(void) schpc_setslotstatus(expander, board, slot, &setslot);
}
/*
* schpc_init_setslot_message
*
* Initialize Set Slot Message before using it.
*/
static void
schpc_init_setslot_message(pci_setslot_t *setslot)
{
/*
* Initialize Set Slot Command.
*/
setslot->slot_power_on = PCIMSG_OFF;
setslot->slot_power_off = PCIMSG_OFF;
setslot->slot_led_power = PCIMSG_LED_OFF;
setslot->slot_led_service = PCIMSG_LED_OFF;
setslot->slot_led_fault = PCIMSG_LED_OFF;
setslot->slot_disable_ENUM = PCIMSG_OFF;
setslot->slot_enable_ENUM = PCIMSG_OFF;
setslot->slot_disable_HEALTHY = PCIMSG_OFF;
setslot->slot_enable_HEALTHY = PCIMSG_OFF;
}
/*
* schpc_gettransid
*
* Builds a unique transaction ID.
*/
static uint64_t
schpc_gettransid(schpc_t *schpc_p, int slot)
{
uint64_t trans_id;
mutex_enter(&schpc_p->schpc_mutex);
if (++schpc_p->schpc_transid == 0)
schpc_p->schpc_transid = 1;
trans_id = (schpc_p->schpc_slot[slot].expander<<24) |
(schpc_p->schpc_slot[slot].board << 16) | schpc_p->schpc_transid;
mutex_exit(&schpc_p->schpc_mutex);
SCHPC_DEBUG1(D_TRANSID, "schpc_gettransid() - 0x%lx transid returning",
trans_id);
return (trans_id);
}
/*
* schpc_slot_get_index
*
* get slot table index from the slot handle
*/
static int
schpc_slot_get_index(schpc_t *schpc_p, hpc_slot_t slot)
{
int i;
int rval = -1;
ASSERT(MUTEX_HELD(&schpc_p->schpc_mutex));
for (i = 0; i < schpc_p->schpc_number_of_slots; i++) {
if (schpc_p->schpc_slot[i].slot_handle == slot)
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
schpc_register_all_slots(schpc_t *schpc_p)
{
int slot = 0;
char caddr[64];
dev_info_t *pci_dip = NULL;
find_dev_t find_dev;
int leaf, schizo, expander, portid, offset;
SCHPC_DEBUG1(D_ATTACH,
"schpc_register_all_slots(schpc_p=%p)", schpc_p);
/*
* Allow the event_handler to start processing unsolicited
* events now that slots are about to be registered.
*/
slots_registered = B_TRUE;
for (slot = 0; slot < STARCAT_MAX_SLOTS; slot++) {
leaf = SCHPC_SLOT_LEAF(slot);
schizo = SCHPC_SLOT_SCHIZO(slot);
expander = SCHPC_SLOT_EXPANDER(slot);
if (schizo == 0)
portid = 0x1c;
else
portid = 0x1d;
if (leaf == 0)
offset = 0x600000;
else
offset = 0x700000;
portid = (expander << 5) | portid;
(void) sprintf(caddr, "%x,%x", portid, offset);
SCHPC_DEBUG3(D_ATTACH,
"schpc_register_all_slots: searching for pci@%s"
" schizo=%d, leaf=%d", caddr, schizo, leaf);
find_dev.cname = "pci";
find_dev.caddr = caddr;
find_dev.schizo = schizo;
find_dev.leaf = leaf;
find_dev.dip = NULL;
/* root node doesn't have to be held */
ddi_walk_devs(ddi_root_node(), schpc_match_dip,
&find_dev);
pci_dip = find_dev.dip;
if (pci_dip == NULL) {
SCHPC_DEBUG1(D_ATTACH,
"schpc_register_all_slots: pci@%s NOT FOUND",
caddr);
continue;
}
SCHPC_DEBUG2(D_ATTACH,
"schpc_register_all_slots: pci@%s FOUND dip=0x%p",
caddr, pci_dip);
(void) schpc_add_pci(pci_dip);
/*
* Release hold acquired in schpc_match_dip()
*/
ndi_rele_devi(pci_dip);
}
SCHPC_DEBUG0(D_ATTACH, "schpc_register_all_slots: Thread Exit");
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
schpc_add_pci(dev_info_t *bdip)
{
int portid;
int expander, board, schizo, leaf, slot, status;
char ap_id[MAXNAMELEN];
char caddr[64];
char *naddr;
hpc_slot_info_t slot_info;
hpc_slot_ops_t *slot_ops;
dev_info_t *sdip = bdip;
SCHPC_DEBUG1(D_ATTACH, "schpc_add_pci(dip=0x%p)", sdip);
if (schpc_p == NULL) {
/*
* The schpc driver has not been attached yet.
*/
return (DDI_SUCCESS);
}
if ((portid = ddi_getprop(DDI_DEV_T_ANY, sdip, 0, "portid", -1)) < 0) {
cmn_err(CE_WARN, "schpc_add_pci(dip=0x%p) - no portid\n", sdip);
return (DDI_FAILURE);
}
expander = schpc_getexpander(sdip);
board = schpc_getboard(sdip);
switch (portid & 0x1f) {
case 0x1c:
schizo = 0;
break;
case 0x1d:
schizo = 1;
break;
default:
cmn_err(CE_WARN, "schpc_add_pci(dip=0x%p) - "
"Invalid pci portid 0x%x\n", sdip, portid);
return (DDI_FAILURE);
}
naddr = ddi_get_name_addr(sdip);
if (naddr == NULL) {
SCHPC_DEBUG1(D_ATTACH, "schpc_add_pci: ddi_get_name_addr"
"(0x%p) returns null", sdip);
return (DDI_FAILURE);
}
(void) sprintf(caddr, "%x,600000", portid);
if (strcmp(caddr, naddr) == 0) {
leaf = 0;
} else {
(void) sprintf(caddr, "%x,700000", portid);
if (strcmp(caddr, naddr) == 0) {
char *name;
leaf = 1;
name = ddi_binding_name(sdip);
if ((strcmp(name, "pci108e,8002") == 0) &&
(schizo == 0)) {
int circ;
dev_info_t *cdip;
/*
* XMITS 0 Leaf B will have its hot
* pluggable slot off a PCI-PCI bridge,
* which is the only child.
*/
ndi_devi_enter(sdip, &circ);
cdip = ddi_get_child(sdip);
if (cdip == NULL) {
cmn_err(CE_WARN,
"schpc_add_pci(dip=0x%p) - "
"Invalid pci name addr %s\n",
sdip, naddr);
ndi_devi_exit(sdip, circ);
return (DDI_FAILURE);
}
ndi_devi_exit(sdip, circ);
sdip = cdip;
}
} else {
cmn_err(CE_WARN, "schpc_add_pci(dip=0x%p) - "
"Invalid pci name addr %s\n", sdip, naddr);
return (DDI_FAILURE);
}
}
/* create a slot table index */
slot = SCHPC_MAKE_SLOT_INDEX3(expander, schizo, leaf);
if (schpc_p->schpc_slot[slot].devi) {
cmn_err(CE_WARN, "schpc_add_pci(dip=0x%p) - "
"pci node already registered\n", 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().
*/
schpc_p->schpc_slot[slot].devi = bdip;
schpc_p->schpc_slot[slot].expander = expander;
schpc_p->schpc_slot[slot].board = board;
schpc_p->schpc_slot[slot].schizo = schizo;
schpc_p->schpc_slot[slot].leaf = leaf;
/*
* Starcat PCI slots are always PCI device 1.
*/
schpc_p->schpc_slot[slot].pci_id = 1;
schpc_buildapid(sdip, slot, (char *)&ap_id);
(void) strcpy(schpc_p->schpc_slot[slot].ap_id, (char *)&ap_id);
/* safe to call ddi_pathname(): bdip is held */
(void) ddi_pathname(sdip, schpc_p->schpc_slot[slot].nexus_path);
status = schpc_get_slot_status(expander, board, SCHPC_SLOT_NUM(slot));
switch (status) {
case RSV_UNKNOWN:
case RSV_PRESENT:
case RSV_MISS:
case RSV_PASS:
case RSV_EMPTY_CASSETTE:
/*
* Test the condition of the slot.
*/
schpc_test((caddr_t)schpc_p, slot, 0, 0);
break;
case RSV_BLACK:
schpc_p->schpc_slot[slot].state = 0;
cmn_err(CE_WARN, "schpc: PCI card blacklisted: "
"expander=%d board=%d slot=%d\n", expander,
board, SCHPC_SLOT_NUM(slot));
break;
default:
schpc_p->schpc_slot[slot].state = 0;
cmn_err(CE_WARN, "schpc: PCI card failed by POST: "
"expander=%d board=%d slot=%d failure=0x%x\n",
expander, board, SCHPC_SLOT_NUM(slot), status);
break;
}
if (schpc_p->schpc_slot[slot].state & SCHPC_SLOTSTATE_REC_GOOD) {
/* allocate slot ops */
slot_ops = hpc_alloc_slot_ops(KM_SLEEP);
schpc_p->schpc_slot[slot].slot_ops = slot_ops;
/*
* Default to Autoconfiguration disabled.
*/
schpc_p->schpc_slot[slot].state &=
~SCHPC_SLOTSTATE_AUTOCFG_ENABLE;
/*
* Fill in the slot information structure that
* describes the slot.
*/
slot_info.version = HPC_SLOT_OPS_VERSION;
if (schpc_p->schpc_hotplugmodel ==
SCHPC_HOTPLUGTYPE_CPCIHOTPLUG)
slot_info.slot_type = HPC_SLOT_TYPE_PCI;
else
slot_info.slot_type = HPC_SLOT_TYPE_CPCI;
slot_info.slot.pci.device_number =
schpc_p->schpc_slot[slot].pci_id;
slot_info.slot.pci.slot_capabilities = HPC_SLOT_64BITS;
if (schpc_use_legacy_apid)
slot_info.slot_flags = HPC_SLOT_NO_AUTO_ENABLE;
else
slot_info.slot_flags = HPC_SLOT_NO_AUTO_ENABLE |
HPC_SLOT_CREATE_DEVLINK;
strcpy(slot_info.slot.pci.slot_logical_name,
schpc_p->schpc_slot[slot].ap_id);
/*
* Fill in the slot ops structure that tells
* the Hot Plug Services what function we
* support.
*/
slot_ops->hpc_version = HPC_SLOT_OPS_VERSION;
if (schpc_p->schpc_hotplugmodel ==
SCHPC_HOTPLUGTYPE_CPCIHOTPLUG) {
slot_ops->hpc_op_connect = schpc_connect;
slot_ops->hpc_op_disconnect = schpc_disconnect;
slot_ops->hpc_op_insert = NULL;
slot_ops->hpc_op_remove = NULL;
slot_ops->hpc_op_control = schpc_pci_control;
} else {
slot_ops->hpc_op_connect = NULL;
slot_ops->hpc_op_disconnect = NULL;
slot_ops->hpc_op_insert = NULL;
slot_ops->hpc_op_remove = NULL;
slot_ops->hpc_op_control = schpc_cpci_control;
}
SCHPC_DEBUG5(D_ATTACH, "schpc_add_pci: Registering HPC "
"- nexus =%s schpc_p=%p slot=%d pci number=%d ap_id=%s",
schpc_p->schpc_slot[slot].nexus_path,
schpc_p, SCHPC_SLOT_NUM(slot),
slot_info.slot.pci.device_number,
slot_info.slot.pci.slot_logical_name);
if (hpc_slot_register(schpc_p->schpc_devi,
schpc_p->schpc_slot[slot].nexus_path, &slot_info,
&schpc_p->schpc_slot[slot].slot_handle,
slot_ops, (caddr_t)schpc_p, 0) != 0) {
/*
* If the slot can not be registered,
* then the slot_ops need to be freed.
*/
cmn_err(CE_WARN, "schpc%d Unable to Register "
"Slot %s", schpc_p->schpc_instance,
slot_info.slot.pci.slot_logical_name);
hpc_free_slot_ops(schpc_p->schpc_slot[slot].slot_ops);
schpc_p->schpc_slot[slot].slot_ops = NULL;
return (DDI_FAILURE);
}
/*
* We are ready to take commands from the HPC Services.
*/
schpc_p->schpc_slot[slot].state |= SCHPC_SLOTSTATE_HPCINITED;
}
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
schpc_remove_pci(dev_info_t *dip)
{
int slot;
SCHPC_DEBUG1(D_DETACH, "schpc_remove_pci(dip=0x%p)", dip);
if (schpc_p == NULL) {
/*
* The schpc driver has not been attached yet.
*/
return (DDI_SUCCESS);
}
for (slot = 0; slot < schpc_p->schpc_number_of_slots; slot++) {
if (schpc_p->schpc_slot[slot].devi == dip) {
if (schpc_p->schpc_slot[slot].slot_ops) {
if (hpc_slot_unregister(
&schpc_p->schpc_slot[slot].slot_handle)) {
cmn_err(CE_WARN,
"schpc_remove_pci(dip=0x%p) - "
"unable to unregister pci slots\n",
dip);
return (DDI_FAILURE);
} else {
hpc_free_slot_ops(
schpc_p->schpc_slot[slot].slot_ops);
schpc_p->schpc_slot[slot].slot_ops =
NULL;
schpc_p->schpc_slot[slot].devi = NULL;
return (DDI_SUCCESS);
}
} else {
schpc_p->schpc_slot[slot].devi = NULL;
return (DDI_SUCCESS);
}
}
}
cmn_err(CE_WARN, "schpc_remove_pci(dip=0x%p) "
"dip not found\n", dip);
return (DDI_SUCCESS);
}
/*
* schpc_match_dip
*
* Used by ddi_walk_devs to find PCI Nexus nodes associated with
* Hot Plug Controllers.
*/
static int
schpc_match_dip(dev_info_t *dip, void *arg)
{
char *naddr;
find_dev_t *find_dev = (find_dev_t *)arg;
if (strcmp(find_dev->cname, ddi_node_name(dip)) == 0 &&
((((naddr = ddi_get_name_addr(dip)) != NULL) &&
(strcmp(find_dev->caddr, naddr) == 0)) ||
((naddr == NULL) && (strlen(find_dev->caddr) == 0)))) {
/*
* 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.
*/
ndi_hold_devi(dip);
find_dev->dip = dip;
SCHPC_DEBUG2(D_ATTACH,
"schpc_match_dip: pci@%s FOUND dip=0x%p",
find_dev->caddr, find_dev->dip);
return (DDI_WALK_TERMINATE);
}
ASSERT(find_dev->dip == NULL);
return (DDI_WALK_CONTINUE);
}
/*
* schpc_buildapid
*
* Takes a component address and translates it into a ap_id prefix.
*/
static void
schpc_buildapid(dev_info_t *dip, int slot, char *ap_id)
{
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;
slot_num = SCHPC_SLOT_NUM(slot);
if (schpc_use_legacy_apid) {
SCHPC_DEBUG1(D_APID, "Slot %d - Using Legacy ap-id", slot);
sprintf(ap_id, "e%02db%dslot%d", schpc_getexpander(dip),
schpc_getboard(dip), slot_num);
SCHPC_DEBUG2(D_APID, "Slot %d - ap-id=%s", slot, ap_id);
return;
}
r = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"slot-names", (caddr_t)&slot_names_data,
&slot_names_size);
if (r == DDI_PROP_SUCCESS) {
/*
* We can try to use the slot-names property to
* build our ap-id.
*/
bit_mask = slot_names_data[3] | (slot_names_data[2] << 8) |
(slot_names_data[1] << 16) | (slot_names_data[0] << 24);
pci_id_bit = 1;
pci_id_cnt = slots_before = found = 0;
SCHPC_DEBUG2(D_APID, "Slot %d - slot-names bitmask=%x",
slot, bit_mask);
/*
* Walk the bit mask until we find the bit that corresponds
* to our slots device number. We count how many bits
* we find before we find our slot's bit.
*/
while (!found && (pci_id_cnt < 32)) {
while (schpc_p->schpc_slot[slot].pci_id
!= pci_id_cnt) {
/*
* Find the next bit set.
*/
while (!(bit_mask & pci_id_bit) &&
(pci_id_cnt < 32)) {
pci_id_bit = pci_id_bit << 1;
pci_id_cnt++;
}
if (schpc_p->schpc_slot[slot].pci_id !=
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.
*/
sprintf(ap_id, "IO%d_%s", schpc_getexpander(dip), s);
SCHPC_DEBUG2(D_APID, "Slot %d - ap-id=%s",
slot, ap_id);
kmem_free(slot_names_data, slot_names_size);
return;
}
SCHPC_DEBUG1(D_APID, "Slot %d - slot-names entry not found",
slot);
kmem_free(slot_names_data, slot_names_size);
} else
SCHPC_DEBUG1(D_APID, "Slot %d - No slot-names prop found",
slot);
/*
* Build the ap-id using the legacy naming scheme.
*/
sprintf(ap_id, "e%02db%dslot%d", schpc_getexpander(dip),
schpc_getboard(dip), slot_num);
SCHPC_DEBUG2(D_APID, "Slot %d - ap-id=%s", slot, ap_id);
}
/*
* 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
schpc_getexpander(dev_info_t *dip)
{
int id;
id = ddi_getprop(DDI_DEV_T_ANY, dip, 0, "portid", -1);
if (id != -1)
return (id >> 5);
else {
id = ddi_getprop(DDI_DEV_T_ANY, dip, 0, "expander", -1);
return (id);
}
}
/*
* schpc_getboard
*
* Returns the board number (0 or 1) for the dip passed in.
*/
static int
schpc_getboard(dev_info_t *dip)
{
_NOTE(ARGUNUSED(dip))
/*
* Hot Pluggable PCI/cPCI slots are only available on
* Board 1 (half-bandwidth slot).
*/
return (1);
}
/*ARGSUSED*/
static int
schpc_get_slot_status(uint_t expander, uint_t board, uint_t slot)
{
gdcd_t *gdcd;
int prd_slot, status, bus;
SCHPC_DEBUG3(D_ATTACH, "schpc_get_slot_status() "
"exp=%d board=%d slot=%d", expander, board, slot);
if ((gdcd = (gdcd_t *)kmem_zalloc(sizeof (gdcd_t),
KM_SLEEP)) == NULL) {
return (RSV_UNDEFINED);
}
/*
* Get the Starcat Specific Global DCD Structure from the golden
* IOSRAM.
*/
if (iosram_rd(GDCD_MAGIC, 0, sizeof (gdcd_t), (caddr_t)gdcd)) {
cmn_err(CE_WARN, "sc_gptwocfg: Unable To Read GDCD "
"From IOSRAM\n");
kmem_free(gdcd, sizeof (gdcd_t));
return (RSV_UNDEFINED);
}
if (gdcd->h.dcd_magic != GDCD_MAGIC) {
cmn_err(CE_WARN, "schpc: GDCD Bad Magic 0x%x\n",
gdcd->h.dcd_magic);
kmem_free(gdcd, sizeof (gdcd_t));
return (RSV_UNDEFINED);
}
if (gdcd->h.dcd_version != DCD_VERSION) {
cmn_err(CE_WARN, "schpc: GDCD Bad Version: "
"GDCD Version 0x%x Expecting 0x%x\n",
gdcd->h.dcd_version, DCD_VERSION);
kmem_free(gdcd, sizeof (gdcd_t));
return (RSV_UNDEFINED);
}
if (slot < 2)
prd_slot = 4;
else
prd_slot = 5;
bus = slot & 0x1;
status = gdcd->dcd_prd[expander][prd_slot].prd_iocard_rsv[bus][0];
kmem_free(gdcd, sizeof (gdcd_t));
SCHPC_DEBUG3(D_ATTACH, "schpc_get_slot_status() "
"prd_slot=%d bus=%d status=%d", prd_slot, bus, status);
return (status);
}
#define LEAF_SAVE_END 0xff
typedef struct {
int reg;
int offset;
int access_size;
int number;
} save_reg_list_t;
/*
* 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,
*/
save_reg_list_t save_reg_list[] = { 0, 0x110, 8, 1,
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)
{
int save_entry, list_entry, reg;
caddr_t leaf_regs;
ddi_device_acc_attr_t attr;
SCHPC_DEBUG1(D_FREQCHG, "Slot %d - Leaf Registers Saved", slot);
attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
/*
* Map in the 3 addresses spaces defined for XMITS.
*/
for (reg = 0; reg < 3; reg++) {
if (ddi_regs_map_setup(schpc_p->schpc_slot[slot].devi, reg,
&leaf_regs, 0, 0, &attr, &schpc_p->schpc_slot[slot].
saved_handle[reg]) != DDI_SUCCESS) {
cmn_err(CE_WARN, "Mapin failed\n");
schpc_p->schpc_slot[slot].saved_regs_va[reg] = NULL;
return (1);
}
schpc_p->schpc_slot[slot].saved_regs_va[reg] = leaf_regs;
}
/*
* Determine how many entries are in the list so we can
* allocate the save space.
*/
list_entry = 0;
save_entry = 0;
while (save_reg_list[list_entry].reg != LEAF_SAVE_END) {
save_entry += save_reg_list[list_entry].number;
list_entry++;
}
schpc_p->schpc_slot[slot].saved_size = (save_entry * sizeof (uint64_t));
if (schpc_p->schpc_slot[slot].saved_size == 0)
return (0);
schpc_p->schpc_slot[slot].saved_regs =
(uint64_t *)kmem_zalloc(schpc_p->schpc_slot[slot].saved_size,
KM_SLEEP);
/*
* Walk through the register list and save contents.
*/
list_entry = 0;
save_entry = 0;
while (save_reg_list[list_entry].reg != LEAF_SAVE_END) {
schpc_save_entry(slot, list_entry, save_entry);
save_entry += save_reg_list[list_entry].number;
list_entry ++;
}
SCHPC_DEBUG1(D_FREQCHG, "Slot %d - Leaf Registers Saved", slot);
return (0);
}
static void
schpc_restore_leaf(int slot)
{
int save_entry, list_entry, reg;
if (schpc_p->schpc_slot[slot].saved_regs == NULL)
return;
/*
* Walk through the register list and restore contents.
*/
list_entry = 0;
save_entry = 0;
while (save_reg_list[list_entry].reg != LEAF_SAVE_END) {
schpc_restore_entry(slot, list_entry, save_entry);
save_entry += save_reg_list[list_entry].number;
list_entry ++;
}
/*
* Free the mapped in registers.
*/
for (reg = 0; reg < 3; reg++) {
if (schpc_p->schpc_slot[slot].saved_regs_va[reg]) {
ddi_regs_map_free(
&schpc_p->schpc_slot[slot].saved_handle[reg]);
schpc_p->schpc_slot[slot].saved_regs_va[reg] = NULL;
}
}
kmem_free(schpc_p->schpc_slot[slot].saved_regs,
schpc_p->schpc_slot[slot].saved_size);
schpc_p->schpc_slot[slot].saved_size = 0;
schpc_p->schpc_slot[slot].saved_regs = NULL;
SCHPC_DEBUG1(D_FREQCHG, "Slot %d - Leaf Registers Restored", slot);
}
static void
schpc_save_entry(int slot, int list_entry, int save_entry)
{
int reg, reads = 0;
reg = save_reg_list[list_entry].reg;
while (reads < save_reg_list[list_entry].number) {
switch (save_reg_list[list_entry].access_size) {
case 8:
schpc_p->schpc_slot[slot].saved_regs[save_entry] =
ddi_get64(
schpc_p->schpc_slot[slot].saved_handle[reg],
(uint64_t *)(schpc_p->schpc_slot[slot].
saved_regs_va[reg]
+ save_reg_list[list_entry].offset +
(reads * sizeof (uint64_t))));
#ifdef DEBUG
if (schpc_dump_save_regs)
cmn_err(CE_WARN, "Save 64 %x %lx %lx\n", reg,
save_reg_list[list_entry].offset +
(reads * sizeof (uint64_t)),
schpc_p->schpc_slot[slot].
saved_regs[save_entry]);
#endif
break;
case 4:
schpc_p->schpc_slot[slot].saved_regs[save_entry] =
ddi_get32(
schpc_p->schpc_slot[slot].saved_handle[reg],
(uint32_t *)(schpc_p->schpc_slot[slot].
saved_regs_va[reg]
+ save_reg_list[list_entry].offset +
(reads * sizeof (uint32_t))));
#ifdef DEBUG
if (schpc_dump_save_regs)
cmn_err(CE_WARN, "Save 32 %x %lx %lx\n", reg,
save_reg_list[list_entry].offset +
(reads * sizeof (uint32_t)),
schpc_p->schpc_slot[slot].
saved_regs[save_entry]);
#endif
break;
case 2:
schpc_p->schpc_slot[slot].saved_regs[save_entry] =
ddi_get16(
schpc_p->schpc_slot[slot].saved_handle[reg],
(uint16_t *)(schpc_p->schpc_slot[slot].
saved_regs_va[reg]
+ save_reg_list[list_entry].offset +
(reads * sizeof (uint16_t))));
#ifdef DEBUG
if (schpc_dump_save_regs)
cmn_err(CE_WARN, "Save 16 %x %lx %lx\n", reg,
save_reg_list[list_entry].offset +
(reads * sizeof (uint16_t)),
schpc_p->schpc_slot[slot].
saved_regs[save_entry]);
#endif
break;
case 1:
schpc_p->schpc_slot[slot].saved_regs[save_entry] =
ddi_get8(
schpc_p->schpc_slot[slot].saved_handle[reg],
(uint8_t *)(schpc_p->schpc_slot[slot].
saved_regs_va[reg]
+ save_reg_list[list_entry].offset +
(reads * sizeof (uint8_t))));
#ifdef DEBUG
if (schpc_dump_save_regs)
cmn_err(CE_WARN, "Save 8 %x %lx %lx\n", reg,
save_reg_list[list_entry].offset +
(reads * sizeof (uint8_t)),
schpc_p->schpc_slot[slot].
saved_regs[save_entry]);
#endif
break;
default:
cmn_err(CE_WARN,
"schpc: Illegal List Entry\n");
}
reads++;
save_entry++;
}
}
static void
schpc_restore_entry(int slot, int list_entry, int save_entry)
{
int reg, writes = 0;
reg = save_reg_list[list_entry].reg;
while (writes < save_reg_list[list_entry].number) {
switch (save_reg_list[list_entry].access_size) {
case 8:
#ifdef DEBUG
if (schpc_dump_save_regs)
cmn_err(CE_WARN, "Restore 64 %x %lx %lx\n", reg,
save_reg_list[list_entry].offset +
(writes * sizeof (uint64_t)),
schpc_p->schpc_slot[slot].
saved_regs[save_entry]);
#endif
ddi_put64(schpc_p->schpc_slot[slot].saved_handle[reg],
(uint64_t *)(schpc_p->schpc_slot[slot].
saved_regs_va[reg]
+ save_reg_list[list_entry].offset +
(writes * sizeof (uint64_t))),
schpc_p->schpc_slot[slot].saved_regs[save_entry]);
break;
case 4:
#ifdef DEBUG
if (schpc_dump_save_regs)
cmn_err(CE_WARN, "Restore 32 %x %lx %lx\n", reg,
save_reg_list[list_entry].offset +
(writes * sizeof (uint32_t)),
schpc_p->schpc_slot[slot].
saved_regs[save_entry]);
#endif
ddi_put32(schpc_p->schpc_slot[slot].saved_handle[reg],
(uint32_t *)(schpc_p->schpc_slot[slot].
saved_regs_va[reg]
+ save_reg_list[list_entry].offset +
(writes * sizeof (uint32_t))),
schpc_p->schpc_slot[slot].saved_regs[save_entry]);
break;
case 2:
#ifdef DEBUG
if (schpc_dump_save_regs)
cmn_err(CE_WARN, "Restore 16 %x %lx %lx\n", reg,
save_reg_list[list_entry].offset +
(writes * sizeof (uint16_t)),
schpc_p->schpc_slot[slot].
saved_regs[save_entry]);
#endif
ddi_put16(schpc_p->schpc_slot[slot].saved_handle[reg],
(uint16_t *)(schpc_p->schpc_slot[slot].
saved_regs_va[reg]
+ save_reg_list[list_entry].offset +
(writes * sizeof (uint16_t))),
schpc_p->schpc_slot[slot].saved_regs[save_entry]);
break;
case 1:
#ifdef DEBUG
if (schpc_dump_save_regs)
cmn_err(CE_WARN, "Restore 8 %x %lx %lx\n", reg,
save_reg_list[list_entry].offset +
(writes * sizeof (uint8_t)),
schpc_p->schpc_slot[slot].
saved_regs[save_entry]);
#endif
ddi_put8(schpc_p->schpc_slot[slot].saved_handle[reg],
(uint8_t *)(schpc_p->schpc_slot[slot].
saved_regs_va[reg]
+ save_reg_list[list_entry].offset +
(writes * sizeof (uint8_t))),
schpc_p->schpc_slot[slot].saved_regs[save_entry]);
break;
default:
cmn_err(CE_WARN,
"schpc: Illegal List Entry\n");
}
writes++;
save_entry++;
}
}
/*
* Returns TRUE if a leaf reset is required to change frequencies/mode.
*/
static int
schpc_is_leaf_reset_required(int slot)
{
char *name;
int32_t mod_rev;
/*
* Only XMITS 3.0 and greater connected slots will require a
* reset to switch frequency and/or mode.
*/
name = ddi_binding_name(schpc_p->schpc_slot[slot].devi);
if (strcmp(name, "pci108e,8002") == 0) {
mod_rev = ddi_prop_get_int(DDI_DEV_T_ANY,
schpc_p->schpc_slot[slot].devi,
DDI_PROP_DONTPASS, "module-revision#", 0);
SCHPC_DEBUG2(D_FREQCHG, "Slot %d - mod_rev=%x", slot, mod_rev);
/*
* Check for XMITS 3.0 or greater.
*/
if (mod_rev >= XMITS_30) {
/*
* The leaf attached to C5V0 (slot 1) should
* not be reset.
*/
if ((slot & 3) == 1) {
SCHPC_DEBUG1(D_FREQCHG, "Slot %d - Leaf Reset "
"Not Required - C5V0", slot);
return (0);
}
SCHPC_DEBUG1(D_FREQCHG, "Slot %d - Leaf Reset "
"Required", slot);
return (1);
}
}
SCHPC_DEBUG1(D_FREQCHG, "Slot %d - Leaf Reset NOT Required", slot);
return (0);
}
/*
* Returns TRUE if the bus can change frequencies.
*/
static int
schpc_is_freq_switchable(int slot)
{
char *name;
int32_t mod_rev;
name = ddi_binding_name(schpc_p->schpc_slot[slot].devi);
if (strcmp(name, "pci108e,8002") == 0) {
mod_rev = ddi_prop_get_int(DDI_DEV_T_ANY,
schpc_p->schpc_slot[slot].devi,
DDI_PROP_DONTPASS, "module-revision#", 0);
SCHPC_DEBUG2(D_FREQCHG, "Slot %d - mod_rev=%x", slot, mod_rev);
/*
* We will only report back that XMITS 2.0 (mod_rev = 2)
* or greater will have the ability to switch frequencies.
*/
if (mod_rev >= XMITS_20) {
SCHPC_DEBUG1(D_FREQCHG, "Slot %d - "
"Frequency is switchable", slot);
return (1);
}
}
SCHPC_DEBUG1(D_FREQCHG, "Slot %d - Frequency is NOT switchable", slot);
return (0);
}
/*
* schpc_slot_freq
*
* Convert the slot frequency setting to integer value.
*/
static int
schpc_slot_freq(pci_getslot_t *getslotp)
{
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
schpc_find_dip(dev_info_t *dip, void *arg)
{
find_dev_t *find_dev = (find_dev_t *)arg;
char *pathname = find_dev->caddr;
(void) ddi_pathname(dip, pathname);
if (strcmp(find_dev->cname, pathname) == 0) {
ndi_hold_devi(dip);
find_dev->dip = dip;
return (DDI_WALK_TERMINATE);
}
return (DDI_WALK_CONTINUE);
}