scsb.c revision 193974072f41a843678abf5f61979c748687e66b
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Netra ct800 and Netra ct400 (MonteCarlo/Tonga)
* System Controller and Status Boards STREAMS driver.
*
* This driver handles all communications with the Netra ct400 and ct800
* System Controller Boards.
* I/O to the SCB is through the PCF8584 I2C controller.
* The SCB I2C interface and driver interface are provided by the
* Xilinx XCS40XL.
*
* N.B.: The design choice of using STREAMS was dictated because
* the original system monitor card had to multiplex 2 pcf8574's
* as one device.
*/
#include <sys/mct_topology.h>
#include <sys/netract_gen.h>
#include <sys/scsbioctl.h>
#include <sys/scsb_cbi.h>
#define CPCI_HOTSWAP_SUPPORT
#define ALARM_CARD_ON_SLOT 1
#define SCSB_FRU_OP_GET_REG 1
#define SCSB_FRU_OP_SET_REGBIT 2
#define SCSB_FRU_OP_GET_BITVAL 3
#define SCSB_FRU_OP_GET_REGDATA 4
/*
* (internal only)
* scsb build version format is "CCYYMMDD"
* for integer compares.
*/
#define SCSB_BUILD_VERSION "20001206"
#define MUTEX_UNINIT 0
#define MUTEX_INIT 2
static int scsb_err_threshold = 0; /* max allowed i2c errors */
static int scsb_in_postintr = 0; /* 1 if scsb is processing intr */
static int nct_mutex_init = MUTEX_UNINIT;
extern int scsb_hsc_board_healthy();
static char *scsb_name = SCSB_DEVICE_NAME;
static char *scsb_build_version = SCSB_BUILD_VERSION;
/*
* cb_ops section of scsb driver.
*/
static int initialize_scb(scsb_state_t *);
static struct module_info info = {
};
};
};
};
static struct cb_ops scsb_cb_ops = {
nulldev, /* open */
nulldev, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
nodev, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* cb_prop_op */
&sm_st, /* streamtab */
D_MP, /* Driver compatibility flag */
CB_REV, /* rev */
nodev, /* int (*cb_aread)() */
nodev /* int (*cb_awrite)() */
};
DEVO_REV, /* devo_rev, */
0, /* refcnt */
scsb_info, /* info */
nulldev, /* identify */
nulldev, /* probe */
scsb_attach, /* attach */
scsb_detach, /* detach */
nodev, /* reset */
&scsb_cb_ops, /* driver operations */
(struct bus_ops *)0, /* bus operations */
NULL, /* power */
ddi_quiesce_not_supported, /* devo_quiesce */
};
/*
* Module linkage information for the kernel.
*/
&mod_driverops, /* Type of module. This one is a pseudo driver */
#ifdef DEBUG
"SCB/SSB driver DBG" SCSB_BUILD_VERSION,
#else
#endif
&scsb_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
(void *)&modldrv,
};
/*
* local declarations and definitions
*/
#if defined(DEBUG)
#else
static uint32_t scsb_debug = 0;
#endif
static int scsb_pil = SCSB_INTR_PIL;
static int hsc_pil = SCSB_INTR_PIL;
static void *scsb_state;
static uint32_t scsb_global_state;
static struct system_info mct_system_info;
static int scsb_healthy_poll_count = 16;
static uint32_t evc_fifo_count = 0;
static void *evc_procs[EVC_PROCS_MAX];
static int evc_proc_count = 0;
static timeout_id_t scsb_intr_tid;
/*
* kstat functions
*/
static int scsb_alloc_kstats(scsb_state_t *);
static void scsb_free_kstats(scsb_state_t *);
static int update_ks_leddata(kstat_t *, int);
static int update_ks_state(kstat_t *, int);
static int update_ks_topology(kstat_t *, int);
static int update_ks_evcreg(kstat_t *, int);
/*
* local functions
*/
static void scsb_set_topology(scsb_state_t *);
static void scsb_free_topology(scsb_state_t *);
int scsb_read_slot_health(scsb_state_t *, int);
uchar_t);
uchar_t *, int);
static int scsb_readall_regs(scsb_state_t *);
int *, scsb_led_t);
static void check_fru_info(scsb_state_t *, int);
static int event_to_index(uint32_t);
static uint32_t del_event_code();
static uint32_t get_event_code();
static void rew_event_proc(scsb_state_t *);
static int event_proc_count(scsb_state_t *);
static void signal_evc_procs(scsb_state_t *);
static int check_event_procs();
static int scsb_is_alarm_card_slot(scsb_state_t *, int);
int scsb_get_slot_state(scsb_state_t *, int, int *);
static int scsb_queue_ops(scsb_state_t *, int, int, void *, char *);
static int scsb_toggle_psmint(scsb_state_t *, int);
static int scsb_quiesce_psmint(scsb_state_t *);
static int scsb_invoke_intr_chain();
int scsb_intr_register(int (*)(void *), void *, fru_id_t);
void scsb_intr_unregister(fru_id_t);
#ifdef DEBUG
static void mct_topology_dump(scsb_state_t *, int);
#endif
int
_init(void)
{
int i, status;
if (scsb_debug & 0x0005)
hsc_init();
if (scsb_debug & 0x0006)
hsc_fini();
return (status);
}
/*
* initialize the FRU ID Table, using real FRU IDs where available
* such as I2C Addresses for FRUs with I2C support
*/
for (i = 0; i < MCT_MAX_FRUS; ++i)
fru_id_table[i] = i + 1;
return (status);
}
int
_fini(void)
{
int status;
if (scsb_debug & 0x0005)
hsc_fini();
}
if (scsb_debug & 0x0006)
return (status);
}
int
{
if (scsb_debug & 0x0005)
}
static int
{
int instance;
register int i;
int *regs;
if (scsb_debug & 0x0005)
if (cmd != DDI_ATTACH) {
if (scsb_debug & 0x0006)
"scsb_attach[%d]: cmd 0x%x != DDI_ATTACH",
return (DDI_FAILURE);
}
instance);
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* make sure this is the SCB's known address
*/
"scsb%d: Failed to get \"reg\" property", instance);
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/* done with array lookup, free resource */
/*
* initialize synchronization mutex and condition var.
* for this instance.
*/
/*
* 1. Read interrupt property of the board and register its handler.
* 2. Get scsb private handle for communication via I2C Services.
* 3. Allocate and save an i2c_transfer_t for I2C transfers.
*/
/* 1 */
"interrupt-priorities") != 1) {
int tmp[2];
}
else
/*
* Look for the device-err-threshold property which specifies
* on how many errors will scsb send a warning event about it's
* health. The scsb_err_threshold is 10 by default.
*/
scsb_err_threshold = i;
#ifdef DEBUG
" property, value %d", scsb_err_threshold);
#endif
}
scsb->scsb_i2c_errcnt = 0;
/*
* If all went well, create the minor node for user level access.
*/
return (DDI_FAILURE);
}
== DDI_FAILURE) {
return (DDI_FAILURE);
}
/* CLONE */
/* 2 */
"scsb_attach: Failed I2C Services registration");
return (DDI_FAILURE);
}
/* 3 */
"scsb%d: i2c_transfer allocation failed", instance);
return (DDI_FAILURE);
}
/*
* Now it's time to INITIALIZE the boards.
*
* Read the SCB PROM version for a check.
* 2. set SCB_INITIALIZED bit in SysCommand registers (SYS_CMD_BASE)
* 3. clear all LED Data registers (8) by writing 0's to turn off
* all LEDs on the SSB.
* 4. read System Configuration Status registers (SCTRL_CFG)
* to find present FRUs and set corresponding FRU bits at
* LED_DATA_BASE.
* Also enable devices in Topology map for the current MP_ID
* and set the OK LEDs on the SSB.
* 5. read Brd_Hlthy registers (2 @ BRD_HLTHY_BASE)
* 6. Disable PSM Interrupts during initialization, mask all
* interrupts, and clear Interrupt Pointer registers
* by writing 0xFF to each register.
* 7. set SCB EEPROM address bits SPA2-SPA0 at SYS_CMD_BASE + 1
* 8. Install the interrupt handler if appropriate.
* 9. clear appropriate bits in Interrupt Mask register for those
* devices that can be present for this MP_ID Topology.
* 10. enable PSM Interrupt by writing '1' to PSM_INT_EN bit at
* SYS_CMD_BASE + 1
* Also update all shadow registers for test utility
* if scsb_debug is set.
* 11. Check if Alarm Card present at boot and set flags
* 12. Call hsc_attach() for slot registration.
* 13. Allocate, initialze, and install the kstat structures.
* 14. Set scsb_state_t flags to indicate SCB is ready
* and announce the driver is loaded.
*/
/* 1. through 7. */
if (!(scsb_debug)) {
return (DDI_FAILURE);
}
}
/* 8. */
/*
* P0.6 No Interrupt Support
* Instead of installing the handler, it will be called from a user
* program via smf_ioctl(). This flag provides knowledge of the
* necessary workarounds to several scsb routines.
*/
/*
* Now Install interrupt handler
*/
(void *)scsb->scsb_iblock);
"scsb_attach: failed interrupt "
"handler registration");
return (DDI_FAILURE);
}
} else {
"mutex initialization");
if (scsb_debug) {
} else {
return (DDI_FAILURE);
}
}
}
/* 9. */
if (i = scsb_clear_intmasks(scsb)) {
"scsb%d: I2C TRANSFER Failed", instance);
if (!scsb_debug) {
return (DDI_FAILURE);
}
}
/* 10. */
/*
* For P0.6 No Interrupt Support, don't enable PSM Interrupt
*/
rmask = 0x00;
reg = SCSB_REG_ADDR(i);
"scsb%d: I2C TRANSFER Failed", instance);
if (!scsb_debug) {
return (DDI_FAILURE);
}
} else
}
if (scsb_debug) {
/*
* For smctrl test utility,
* so all data is available in shadow registers
*
* DEBUG_MODE enables private testing interfaces
* DIAGS_MODE permits limited testing interfaces
*/
if (scsb_readall_regs(scsb))
"scsb_attach: scsb_readall FAILED");
}
/* 11. */
/* Check if Alarm Card present at boot and set flags */
/* 12. */
if (scsb_debug & 0x0004)
"scsb_attach: registering cPCI slots");
if (scsb_debug & 0x00008000) {
"scsb: Hotswap controller initialisation"
" failed\n");
}
} else
/* 13. */
/*
* allocate and install the kstat data structures
*/
if (scsb_debug & 0x0006)
}
/* 14. */
"Prom Version %s, Midplane Id %x\n",
return (DDI_SUCCESS);
}
/*
* This funciton is called from scsb_attach(), and from scsb_intr() as part
* of Hot Insertion support, to check the SCB PROM ID register and set
* scsb_state bits and register table pointers as necessary.
*/
static int
{
int hotswap = 0;
/*
* If driver is UP, then this call is from scsb_intr()
* as part of Hot Insertion support.
*/
hotswap = 1;
}
/* Read the SCB PROM ID */
&data, 1)) {
if (scsb_debug & 0x0006) {
"scsb_attach(%d): failed read of PROM ID",
}
return (DDI_FAILURE);
}
/*
* compare with stored version number, and if different,
* report a warning and keep the driver FROZEN
*/
if (hotswap) {
== (data & 0xf)) {
return (DDI_SUCCESS);
}
if (scsb_debug & 0x00020000) {
"scb_check_version: SCB version %d "
"replacing version %d", data,
fru_version & 0xf);
}
}
}
if (IS_SCB_P10) {
} else { /* if (IS_SCB_P15) */
}
if (!(IS_SCB_P15) && !(IS_SCB_P10)) {
if (hotswap)
if (!(scsb_debug)) {
return (DDI_FAILURE);
}
/*
* DEBUG: Assume SCB15
*/
}
return (DDI_SUCCESS);
}
/*
* SCB initialization steps to be called from scsb_attach()
* or from scsb_intr() calling scsb_restore() on Hot Insertion.
*/
static int
{
register int i;
/*
* If called from scsb_intr(), we've already done this
*/
return (DDI_FAILURE);
/*
* 2. Set the SCB_INIT bit in the System Command register
*/
reg = SCSB_REG_ADDR(i);
if (scsb_debug & 0x0006) {
"scsb_attach: failed to set SCB_INIT");
}
return (DDI_FAILURE);
}
/* 3. For P1.0 and previous system, turn off all LEDs */
if (IS_SCB_P10) {
if (scsb_debug & 0x0004) {
}
return (DDI_FAILURE);
}
}
/* 4. Read the SYSCFG registers, update FRU info and SSB LEDs */
if (scsb_debug & 0x0004)
if ((i = scsb_check_config_status(scsb)) == 0) {
if (scsb_debug & 0x0004)
} else {
/*
* walk through FRUs and update FRU info
*/
for (i = 0; i < SCSB_UNIT_TYPES; ++i) {
}
}
}
}
if (i) {
return (DDI_FAILURE);
}
/* 5. read the Board Healthy registers */
if (scsb_debug & 0x0004)
i = scsb_read_bhealthy(scsb);
if (i) {
return (DDI_FAILURE);
}
/* 6. Clear Interrupt Source registers */
/*
* Due to some registration problems, we must first disable
* global interrupts which may be the default reset value
* itself. However, this is a safe step to do in case of
* implementation changes.
*
* Disable Global SCB Interrupts now
*/
reg = SCSB_REG_ADDR(i);
return (DDI_FAILURE);
}
/* Mask all interrupt sources */
if (i = scsb_setall_intmasks(scsb)) {
return (DDI_FAILURE);
}
/* Clear any latched interrupts */
if (i = scsb_clear_intptrs(scsb)) {
return (DDI_FAILURE);
}
/* 7. set SCB EEPROM address: NOT USED */
return (DDI_SUCCESS);
}
/*
* Based on MC conditions, scsb_detach should eventually be made to always
* return FAILURE, as the driver should not be allowed to detach after some
* hs slots have been used.
*/
static int
{
int instance;
/*
* TBD: make sure there are no outstanding operations on the system
* monitor card before detaching.
*/
if (scsb_debug & 0x0005)
if (cmd != DDI_DETACH) {
if (scsb_debug & 0x0006)
"scsb_detach(%d): command %x is not DDI_DETACH\n",
return (DDI_FAILURE);
}
scsb_global_state &= ~SCSB_UP;
}
/*
* Disable Global SCB Interrupts now
*/
"scsb%d: Cannot turn off PSM_INT", instance);
if (!scsb_debug) {
return (DDI_FAILURE);
}
}
/* Mask all interrupts */
if (scsb_setall_intmasks(scsb)) {
"scsb%d: I2C TRANSFER Failed", instance);
if (!scsb_debug) {
return (DDI_FAILURE);
}
}
/* Clear all latched interrupts */
if (scsb_clear_intptrs(scsb)) {
"scsb%d: I2C TRANSFER Failed", instance);
if (!scsb_debug) {
return (DDI_FAILURE);
}
}
}
/* CLONE */
/*
* free the allocated resources
*/
return (DDI_SUCCESS);
}
static void
{
if (scsb_debug & 0x0005) {
drv_usecwait(500000);
}
}
}
}
}
}
}
}
"interrupt-priorities");
}
/* ddi_prop_remove_all(dip); */
}
}
}
/*
* Just for testing scsb's poll function
*/
static int
{
if (evcode == 0)
else
if (scsb_debug & 0x4001) {
}
/*
* Allow access to shadow registers even though SCB is removed
*
* if (scsb->scsb_state & SCSB_FROZEN) {
* return (EAGAIN);
* }
*/
if (scsb_debug & 0x00040000) {
}
/* just inform user-level via poll about this event */
== QOP_FAILED)
return (ENOMEM);
return (0);
}
/* ARGSUSED */
static int
{
int retval = DDI_FAILURE;
if (scsb_debug & 0x0001)
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
}
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
}
break;
default:
break;
}
return (retval);
}
/*
* SCSB STREAMS routines
*/
/*ARGSUSED*/
static int
{
return (ENXIO);
if (scsb_debug & 0x0009) {
}
return (ENODEV);
}
/*
* Don't fail the open if SCB removed since we still want to satisfy
* read requests from the shadow registers, the last know register
* contents. On new SCB insertion, all will be re-initialized,
* including envmond and it's policies.
*
* if (scsb->scsb_state & SCSB_FROZEN) {
* return (EAGAIN);
* }
*/
/*
* XXX check for root access here, return EPERM if not root open
*/
/* scsb module is being pushed */
if (scsb_debug & 0x0008)
/*
* this is no longer supported
*/
return (ENXIO);
/* scsb is being opened as a clonable driver */
if (scsb_debug & 0x0008)
/*
* The cloned stream is not handled via the clone driver.
* See the minor device code below.
*/
return (ENXIO);
} else if (minor_dev & SCSB_CLONE) {
/*
* First check for the SCSB_CLONE device.
* Find an available clone_devs[] entry, or return ENXIO.
* Make new dev_t and store in *devp.
*/
if (scsb_debug & 0x0008)
"sm_open(%d): SCSB_CLONE OPEN", instance);
"scsb_open")) == QOP_FAILED) {
return (ENXIO);
}
scsb->scsb_clopens++;
if (scsb_debug & 0x0008)
"sm_open(%d): new clone device minor: 0x%x"
" stream queue is 0x%p",
} else {
/* scsb is being opened as a regular driver */
if (scsb_debug & 0x0008)
if (scsb_debug & 0x0008)
"sm_open(%d): can't open, state is EXCL",
instance);
return (EBUSY);
}
if (scsb_debug & 0x0008)
instance);
if (scsb_debug & 0x0008)
"sm_open(%d): cannot open EXCL",
instance);
return (EBUSY);
}
}
if (scsb_debug & 0x000a)
"scsb_rq (0x%p)",
}
scsb->scsb_opens++;
}
qprocson(q);
return (0);
}
/*ARGSUSED*/
static int
{
int clone;
if (scsb_debug & 0x0009)
if (scsb->scsb_clopens) {
scsb->scsb_clopens--;
}
}
if (scsb_debug & 0x0008)
if (scsb_debug & 0x0008)
"sm_close(%d): DEVOPEN, q != scsb_rq",
}
scsb->scsb_opens = 0;
}
}
}
qprocsoff(q);
return (0);
}
/*ARGSUSED*/
static int
{
if (scsb_debug & 0x0010)
return (0);
}
static int
{
if (scsb_debug & 0x0010)
default:
break;
case M_FLUSH: /* canonical flush handling */
/* free any messages tied to scsb */
}
} else
break;
case M_IOCTL:
if (scsb_debug & 0x0010)
/* do ioctl */
break;
case M_DATA:
if (scsb_debug & 0x0010)
return (0);
}
break;
case M_CTL:
if (scsb_debug & 0x0010)
break;
}
return (0);
}
/*
* These are the system monitor upper ioctl functions.
*/
static void
{
if (scsb_debug & 0x0020)
return;
}
/*
* Don't fail ALL commands if the SCB removed, since we still want to
* satisfy some requests from the shadow registers, the last known
* register contents.
*
* if (scsb->scsb_state & SCSB_FROZEN) {
* iocp->ioc_error = EAGAIN;
* mp->b_datap->db_type = M_IOCNAK;
* qreply(q, mp);
* return;
* }
*/
default:
/* if we don't understand the ioctl */
if (scsb_debug & 0x0022)
break;
case ENVC_IOC_GETMODE:
{
break;
else
if (scsb_debug & 0x20) {
*curr_mode);
}
break;
}
case ENVC_IOC_SETMODE:
{
break;
switch (*curr_mode) {
case ENVCTRL_NORMAL_MODE:
scsb->scsb_state &=
break;
case ENVCTRL_DIAG_MODE:
break;
case ENVC_DEBUG_MODE:
if (scsb->scsb_state &
(SCSB_DIAGS_MODE | SCSB_DEBUG_MODE)) {
} else {
}
break;
default:
if (scsb_debug & 0x22) {
"IOC_SETMODE: Invalid mode 0x%x",
*curr_mode);
}
break;
}
break;
}
else {
}
break;
break;
/*
* Not an exposed interface, only used by development utilities.
*/
case SCSBIOC_GET_VERSIONS:
{
break;
} else {
break;
if (scsb_debug & 0x20) {
"IOC_GET_VERSIONS: sizeof(scsb_ids_t) "
"= %lu", sizeof (scsb_ids_t));
}
}
if (scsb_debug & 0x20) {
"IOC_GET_VERSIONS: SCB PROMID = 0x%x", promid);
}
break;
}
#ifdef DEBUG
case ENVC_IOC_REGISTER_PID:
}
break;
case ENVC_IOC_UNREGISTER_PID:
}
break;
case SCSBIOC_VALUE_MODE:
{
int three_vals = 0;
break;
}
three_vals = 1;
break;
}
break;
/*
* scsb_state is not valid for now. 0 == GET, 1 == SET
*/
if (mode_vals[0]) {
} else {
if (three_vals) {
} else
}
"0x%x/0x%x/0x%x; ioc_count = 0x%lx",
}
break;
}
#ifdef DEBUG
case SCSBIOC_GET_SLOT_INFO:
{
int pslotnum;
break;
}
break;
break;
}
if (scsb_debug & 0x20) {
"0x%x/0x%x; ioc_count = 0x%lx",
}
break;
}
#endif /* DEBUG */
case SCSBIOC_GET_FAN_STATUS:
case SCSBIOC_GET_INTR_ARRAY:
/* for now we don't understand these ioctls */
if (scsb_debug & 0x0022)
break;
#endif /* DEBUG */
case SCSBIOC_LED_OK_GET:
case SCSBIOC_LED_NOK_GET:
case SCSBIOC_LED_OK_SET:
case SCSBIOC_LED_NOK_SET:
case SCSBIOC_BHEALTHY_GET:
case SCSBIOC_SLOT_OCCUPANCY:
case SCSBIOC_RESET_UNIT:
break;
}
/*FALLTHROUGH*/
case ENVC_IOC_GETDSKLED:
case ENVC_IOC_SETDSKLED:
case ENVC_IOC_SETFSP:
{
break;
case SCSBIOC_LED_OK_GET:
break;
case SCSBIOC_LED_NOK_GET:
break;
case SCSBIOC_LED_OK_SET:
break;
case SCSBIOC_LED_NOK_SET:
break;
case SCSBIOC_BHEALTHY_GET:
break;
case SCSBIOC_SLOT_OCCUPANCY:
break;
case SCSBIOC_RESET_UNIT:
break;
case ENVC_IOC_GETDSKLED:
break;
}
break;
case ENVC_IOC_SETDSKLED:
break;
}
break;
case ENVC_IOC_SETFSP:
break;
}
break;
}
break;
}
case SCSBIOC_FAKE_INTR: {
break;
}
ui = 0;
else {
break;
}
break;
}
case SCSBIOC_GET_STATUS :
break;
}
break;
case SCSBIOC_ALL_LEDS_ON :
else
break;
case SCSBIOC_ALL_LEDS_OFF :
else
break;
case SCSBIOC_REG_READ:
case SCSBIOC_REG_WRITE:
} else {
break;
}
iocrdwrp =
return;
}
} else {
return;
}
}
break;
}
}
break;
case SCSBIOC_SHUTDOWN_POLL:
case SCSBIOC_INTEVENT_POLL:
break;
}
break;
case SCSBIOC_RESTORE :
else {
}
break;
case SCSBIOC_FREEZE :
else {
}
break;
/*
* envmond:alarmcard.so response to SCTRL_EVENT_ALARM_INSERTION
*/
case ENVC_IOC_ACCONF_RESTORED:
break;
/*
* envmond:alarmcard.so response to SCTRL_EVENT_ALARM_REMOVAL
*/
case ENVC_IOC_ACCONF_STORED:
break;
}
break;
#ifdef DEBUG
case SCSBIOC_TOPOLOGY_DUMP:
else {
}
break;
#endif
}
else
}
static fru_info_t *
{
int i;
if (scsb_debug & 0x00100001)
return ((fru_info_t *)NULL);
for (i = 0; i < SCSB_UNIT_TYPES; ++i) {
return (fru_ptr);
}
}
return ((fru_info_t *)NULL);
}
struct scsb_cb_entry {
void *cb_softstate_ptr;
void (*cb_func)
(void *, scsb_fru_event_t, scsb_fru_status_t);
struct scsb_cb_entry *cb_next;
};
#ifdef DEBUG
int scsb_cb_count = 0;
#else
static
#endif
struct scsb_cb_entry *scsb_cb_table;
/*
* global function for interested FRU drivers to register a callback function,
* to be called when FRU presence status changes.
*/
{
struct scsb_cb_entry *cbe_ptr;
if (scsb_debug & 0x00800001) {
"scsb_fru_register: FRU_ID 0x%x", (int)fru_id);
}
if (!(scsb_global_state & SCSB_UP)) {
return (FRU_NOT_AVAILABLE);
}
return (FRU_NOT_AVAILABLE);
if (scsb_cb_table == NULL)
scsb_cb_table = (struct scsb_cb_entry *)
kmem_zalloc(sizeof (struct scsb_cb_entry),
KM_SLEEP);
break;
}
}
#ifdef DEBUG
#endif
if (scsb_debug & 0x00800000) {
"scsb_fru_register: FRU_ID 0x%x, status=%d",
(int)fru_id,
}
return (FRU_NOT_AVAILABLE);
return (FRU_PRESENT);
return (FRU_NOT_PRESENT);
}
void
{
if (scsb_debug & 0x00800001) {
}
return;
do {
if (cbe_ptr == scsb_cb_table)
else
#ifdef DEBUG
#endif
return;
}
}
/*
* global function for interested FRU drivers to call to check
* FRU presence status.
*/
{
if (scsb_debug & 0x00800001) {
(int)fru_ptr->fru_status);
}
return (FRU_NOT_AVAILABLE);
return (fru_ptr->fru_status);
}
/*
* Global function for the other interruptible FRU device sharing the
* same interrupt line to register the interrupt handler with scsb.
* This enables all the handlers to be called whenever the interrupt
* line is asserted by anyone shaing the interrupt line.
*/
/*
* The interrupt handler table is currently a linked list. probably a
* hash table will be more efficient. Usage of these facilities can
* happen even before scsb is attached, so do not depend on scsb
* structure being present.
*/
struct fru_intr_entry {
void *softstate_ptr;
int (*fru_intr_handler)(void *);
struct fru_intr_entry *fru_intr_next;
} *fru_intr_table = NULL;
int
{
struct fru_intr_entry *intr_table_entry;
intr_table_entry = (struct fru_intr_entry *)
if (intr_table_entry == NULL) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* Removed interrupt_handler of fru from interrupt call chain
*/
void
{
*prev_entry = intr_entry;
if (fru_id == 0) {
return;
}
do {
/* found a match, remove entry */
if (intr_entry == fru_intr_table)
else
sizeof (struct fru_intr_entry));
return;
}
}
/*
* Invoke all the registered interrupt handlers, whenever scsb_intr
* is called. This function will go through the list of entries
* in the fru interrupt table and invoke each function. Returns
* whether interrupt is claimed or unclaimed.
*/
static int
{
int retval = DDI_INTR_UNCLAIMED;
while (intr_entry != NULL) {
retval = (*intr_entry->
if (retval == DDI_INTR_CLAIMED) {
return (retval);
}
}
return (retval);
}
/*
* The scsb_ioc_rdwr_t is similar enough to an i2c_transfer_t that we can
* translate the structures and use the i2c_transfer() service.
*/
static void
{
if (scsb_debug & 0x0040)
} else {
}
/*
*/
else
if (error) {
if (scsb_debug & 0x0042)
"sm_ioc_rdwr: rdwr_register failure: %d", error);
} else
}
/*
* names for (scsb_utype_t) FRU types
*/
static char *unit_type_name[SCSB_UNIT_TYPES] = {
"SLOT", "PDU", "POWER SUPPLY", "DISK", "FAN", "ALARM",
"SCB", "SSB", "CFTM", "CRTM", "PRTM"
};
/*
* Discover the register and bit-offset for LEDs and Reset registers,
* according to unit_type, unit_number, and led_type.
*/
static int
int *unitptr,
{
/* OK here means presence (OK) LEDs */
base = (SCTRL_LED_OK_BASE);
else
base = (SCTRL_LED_NOK_BASE);
error = 0;
if (scsb_debug & 0x0100) {
}
/*
* It was requested that the scsb driver allow accesses to SCB device
* registers for FRUs that cannot be present.
* So except for SLOTs, if the unit_number check fails, we now
* just log a message, but ONLY if scsb_debug error messages are
* enabled.
*/
case SLOT:
TG_MAX_SLOTS : MC_MAX_SLOTS)) {
break;
}
break;
case PDU:
TG_MAX_PDU : MC_MAX_PDU)) {
if (scsb_debug & 0x0002) {
"get_led_regnum: unit number %d "
}
break;
}
break;
case PS:
if (scsb_debug & 0x0002) {
"get_led_regnum: unit number %d "
}
break;
}
break;
case DISK:
TG_MAX_DISK : MC_MAX_DISK))) {
if (scsb_debug & 0x0002) {
"get_led_regnum: unit number %d "
}
if (!(scsb_debug & 0x20000000)) {
break;
}
}
break;
case FAN:
TG_MAX_FAN : MC_MAX_FAN)) {
if (scsb_debug & 0x0002) {
"get_led_regnum: unit number %d "
}
break;
}
break;
case CFTM:
TG_MAX_CFTM : MC_MAX_CFTM)) {
if (scsb_debug & 0x0002) {
"get_led_regnum: unit number %d "
}
break;
}
break;
case SCB:
TG_MAX_SCB : MC_MAX_SCB)) {
if (scsb_debug & 0x0002) {
"get_led_regnum: unit number %d "
}
break;
}
break;
case ALARM:
break;
default:
if (scsb_debug & 0x0102) {
"scsb_get_led_regnum(): unknown unit type %d",
}
break;
}
if (!error) {
if (scsb_debug & 0x0100) {
"regptr=%x, code = %x\n",
}
}
return (error);
}
/*
* P1.0 and P1.5
* Map 1.0 Tonga Slot Numbers: SCB to user interface and back.
* User interface means positional slot numbers, as on P1.0 SSB,
*/
/* HSC slotnum (Positional SLotnum) to SCB CFG bit-offset */
/*
* MAP Positional (HSC) slot number to SCB CFG register bit-offset
*/
static int
{
int base = SCTRL_SYSCFG_BASE;
return (sln);
}
return (sln);
}
/*
* Should move this to _init(), but for now,
* check for initialized table
*/
if (psl2sco[0]) {
psl2sco[0] = 0;
}
#ifdef DEBUG
if (scsb_debug & 0x10000000) {
}
#endif
}
/* positional slotnum to SCB slotnum */
static int psl2ssl[6] = {
0, 5, 2, 1, 3, 4
};
/* SCB slotnum to positional slotnum */
static int ssl2psl[6] = {
0, 3, 2, 4, 5, 1
};
/*
* P1.0 and P1.5
* HSC Slot numbers (physical positions or positional slotnum)
* to
* SCB slot numbers (reset,present,healthy)
*
* These requests come mainly from application interface and
* HSC using the scsb_uinfo_t structure.
*/
static void
{
return;
}
return;
}
#ifdef DEBUG
if (scsb_debug & 0x10000000) {
}
#endif
}
/*
* P1.0 and P1.5
*/
static int
{
return (slotnum);
}
return (slotnum);
}
#ifdef DEBUG
if (scsb_debug & 0x10000000) {
}
#endif
}
/*
* P1.0 and P1.5
*/
static int
{
return (slotnum);
}
return (slotnum);
}
#ifdef DEBUG
if (scsb_debug & 0x10000000) {
}
#endif
}
/*
* tonga_slotnum_led_shift: this function remaps slot bits ONLY for Slots 1-5
* and ONLY for the register sets in bit-offset groups 1,2:
*
* IN bits: SCB slot numbers (led,reset,present,healthy)
* to
* OUT bits: HSC Slot numbers (positional slot numbers as marked on the SSB)
*/
static uchar_t
{
int i;
#ifdef DEBUG
#endif
return (data);
}
/*
* P1.0 and P1.5 slot 1-5 offsets are the same
*/
for (i = 1; i <= TG_MAX_SLOTS; ++i) {
switch (i) {
case 1: /* map to slot 3 */
break;
case 2: /* map to slot 2 */
break;
case 3: /* map to slot 4 */
case 4: /* map to slot 5 */
break;
case 5: /* map to slot 1 */
break;
}
}
#ifdef DEBUG
if (scsb_debug & 0x10000000) {
}
#endif
return (new_data);
}
/*
* P1.0 and P1.5
*/
int
{
int error;
int unit_number;
int index;
/*
* Allow access to shadow registers even though SCB is removed
*
* if (scsb->scsb_state & SCSB_FROZEN) {
* return (EAGAIN);
* }
*/
return (EFAULT);
}
}
return (EINVAL);
}
error = 0;
if (scsb_debug & 0x0100) {
suip->unit_number);
}
/*
* Map to Tonga Slot Number, if NOT P1.0 SCB
* P1.0 SSB workaround
*/
}
/* discover the register and index we need to operate on */
led_type)) == 0) {
suip->unit_number);
(1 << unit_number))
}
} else {
}
}
return (error);
}
int
{
int error;
int unit_number;
/* we should really allow led state changes while frozen... */
return (EAGAIN);
return (EFAULT);
}
/*
* Sanity check, make sure we got plausible values for set command.
* Also check for application only control of slot leds using NOUSE
* interface
*/
!(scsb->scsb_state &
(SCSB_DIAGS_MODE | SCSB_DEBUG_MODE))) {
/*
* kernel modules using this interface need to think they are
* succeeding, so we won't return an error for this
* application configuration
*/
return (0);
}
return (EINVAL);
}
return (EINVAL);
}
return (EINVAL);
return (EINVAL);
}
if (scsb_debug & 0x0100) {
"scsb_led_set: led %s, type %s, unit %d, state %s",
}
/*
* Map to Tonga Slot Number, if NOT P1.0 SCB
* P1.0 SSB workaround
*/
}
/*
* discover the register and index we need to access
*/
led_type)) == 0) {
else
if (scsb_debug & 0x0100) {
}
if (error) {
goto ledset_done;
}
goto ledset_done;
}
else
if (scsb_debug & 0x0100) {
}
if (error) {
}
}
return (error);
}
struct ps_auto_on {
};
static struct ps_auto_on pao;
static void
scsb_ps_auto_on(void *arg)
{
/*
* Turn on the PSU.
* Notice: not checking Power Supply unit number
*/
}
}
/*
* called with mutex held from
* scsb_attach() with int_fru_ptr == NULL
* scsb_intr() with int_fru_ptr == info for FRU that caused interrupt
*/
static int
{
int i, error = 0;
uchar_t update_reg = 0;
return (EAGAIN);
}
for (i = 0; i < SCTRL_LED_OK_NUMREGS; ++i) {
led_data[i] = 0;
blink[i] = 0;
}
blink_reg = 0;
if (int_fru_ptr != NULL) {
}
int is_present;
is_present = 0;
break;
continue;
/*
* No LED exceptions: SSB,CRTM,PRTM
*/
continue;
}
continue;
continue;
/*
* exception: SCB
*/
is_present = 1;
} else {
}
if (IS_SCB_P10)
continue;
} else {
(1 << cfg_bit)) {
is_present = 1;
}
}
if (is_present) {
/*
* If the FRU is a Power Supply, AND
* the call is from scsb_attach() OR
* from scsb_intr() and FRUs match,
* turn it on.
*/
(int_fru_ptr == fru_ptr))) {
#ifdef PS_ON_DELAY
/*
* HW recommended not implementing
* this delay for now.
* The code is tested on PSUs:
* -06
* -07 rev 2
* -08 plus
*/
if (int_fru_ptr) {
/*
* Hot insertion, so give it
* the 3 seconds it needs to
* become stable
*/
&pao, (4 *
1000000)));
} else
#endif /* PS_ON_DELAY */
scsb_ps_auto_on((void *)&pao);
}
/*
* Special SLOT handling.
* Make sure the OK LED is on for the CPU Slot
* and for the FTC (CFTM) Slot for MonteCarlo.
* Both will report as FRU_PRESENT.
*/
(scsb_utype_t)OC_CPU ||
(scsb_utype_t)OC_CTC))) {
/*
* Set OK (green) LED register bit
*/
}
if (IS_SCB_P10)
continue;
/*
* Turn off BLINK register bit.
* If single register update, then save the
* corresponding blink register in blink_reg.
*/
if (!reg)
continue;
}
}
}
if (update_reg) {
reg = update_reg;
i = SCSB_REG_INDEX(reg);
i = 1;
} else {
i = SCTRL_LED_OK_NUMREGS;
}
if (scsb_debug & 0x0100) {
"to 0x%x", i, reg);
}
if (scsb_debug & 0x0102)
"I2C write to 0x%x failed", reg);
} else {
/*
* Now see which BLINK bits need to be turned off for the
* corresponding OK LED bits.
*/
for (i = 0; i < SCTRL_BLINK_NUMREGS; ++i, ++reg) {
continue;
if (!blink[i]) {
continue;
}
if (scsb_debug & 0x0100) {
"OFF Blink bits 0x%x in 0x%x",
}
if (scsb_debug & 0x0102)
"scsb_set_scfg_pres(): "
"Write to 0x%x failed", reg);
break;
}
}
}
return (error);
}
static int
{
int error;
if (scsb_debug & 0x0201) {
}
/*
* Base of register set
*/
/*
* SCB P0.6 workaround: read registers twice, use 2nd value set
*/
p06 = 2;
do {
break;
}
if (p06 == 1) {
if (scsb_debug & 0x0200)
"scsb_check_config_status: P0.6 workaround");
}
/*
* If not P0.6 PROM, just break here
*/
break;
} while (--p06);
if (error == 0) {
else
}
return (error);
}
static void
{
int pad = 0;
/*
* Get the presence status from the SysConfigStatus shadow registers
* in scsb->scsb_data_reg[]
*/
/* Mid Plane */
t_uchar = SCSB_REG_ADDR(i);
t = SYS_OFFSET(SCTRL_CFG_MPID0);
(SCTRL_MPID_MASK << t)) >> t);
case SCTRL_MPID_HALF: /* Monte Carlo */
if (scsb_debug & 0x00100005)
break;
case SCTRL_MPID_QUARTER_NODSK: /* Tonga w/o disk */
case SCTRL_MPID_QUARTER: /* Tonga w/ disk */
is_tonga = 1;
ctc_slot_num = -1;
ctcslot_ptr = NULL;
if (scsb_debug & 0x00100005)
", no disk" : " with disk");
break;
default:
if (scsb_debug & 0x00100005)
return;
}
/*
* cPCI Slots
*
* NOTE: The Tonga slot fru_unit needs to get mapped to the logical
* slot number in slot_table[]. The field is not in the slot_table
* at least until we know the format of the OBP slot table for the FCS
* release.
*/
kmem_zalloc(sizeof (fru_info_t) *
int iunit;
if (unit == cpu_slot_num) {
} else if (unit == ctc_slot_num) {
/* fru_ptr saved for Transition Card Presence check */
} else if (unit == alarm_slot_num) {
/* fru_ptr saved for Alarm Card Presence check below */
} else {
}
/*
* Get the slot event code (t), then use it to get the
* slot bit-offsets for LED, BLINK, and SYSCFG registers.
* On a P1.5 Tonga, the internal slot number must be used to
* find the event code.
* The P1.0 Tonga does not get mapped due to a SSB difference.
*/
if (IS_SCB_P15) {
} else {
}
} else {
}
/*
* get the registers addresses and shadow register index for
* the SYSCFG register
*/
/*
* check and set presence status
*/
} else {
}
fru_ptr++;
}
/*
* PDU
*/
kmem_zalloc(sizeof (fru_info_t) *
/* SCB15 */
/*
* get the FRU event code (t), then use it to get the
* FRU bit-offsets for LED and SYSCFG registers
*/
if (IS_SCB_P15) {
i = FRU_REG_INDEX(t, SCTRL_BLINK_OK_BASE);
blink_reg = SCSB_REG_ADDR(i);
} else {
blink_bit = 0;
blink_reg = 0;
}
/*
* get the registers addresses and shadow register index for
* the SYSCFG register
*/
i = FRU_REG_INDEX(t, SCTRL_SYSCFG_BASE);
syscfg = SCSB_REG_ADDR(i);
i = FRU_REG_INDEX(t, SCTRL_LED_OK_BASE);
led_reg = SCSB_REG_ADDR(i);
/*
* check and set presence status
*/
} else {
}
fru_ptr++;
}
/*
* Power Supplies
*/
kmem_zalloc(sizeof (fru_info_t) *
/*
* get the FRU event code (t), then use it to get the
* FRU bit-offsets for LED and SYSCFG registers
*/
if (IS_SCB_P15) {
i = FRU_REG_INDEX(t, SCTRL_BLINK_OK_BASE);
blink_reg = SCSB_REG_ADDR(i);
} else {
blink_bit = 0;
blink_reg = 0;
}
/*
* get the registers addresses and shadow register index for
* the SYSCFG register
*/
i = FRU_REG_INDEX(t, SCTRL_SYSCFG_BASE);
syscfg = SCSB_REG_ADDR(i);
i = FRU_REG_INDEX(t, SCTRL_LED_OK_BASE);
led_reg = SCSB_REG_ADDR(i);
/*
* check and set presence status
*/
} else {
}
fru_ptr++;
}
/*
* SCSI Disks and removable media
*/
kmem_zalloc(sizeof (fru_info_t) *
/* SCB15 */
/*
* get the FRU event code (t), then use it to get the
* FRU bit-offsets for LED and SYSCFG registers
*/
if (IS_SCB_P15) {
i = FRU_REG_INDEX(t, SCTRL_BLINK_OK_BASE);
blink_reg = SCSB_REG_ADDR(i);
} else {
blink_bit = 0;
blink_reg = 0;
}
/*
* get the registers addresses and shadow register index for
* the SYSCFG register
*/
i = FRU_REG_INDEX(t, SCTRL_SYSCFG_BASE);
syscfg = SCSB_REG_ADDR(i);
i = FRU_REG_INDEX(t, SCTRL_LED_OK_BASE);
led_reg = SCSB_REG_ADDR(i);
/*
* check and set presence status
*/
} else
fru_ptr++;
}
/*
* Fan Trays
*/
kmem_zalloc(sizeof (fru_info_t) *
int bit_num;
/* SCB15 */
/*
* get the FRU event code (t), then use it to get the
* FRU bit-offsets for LED and SYSCFG registers
*/
if (IS_SCB_P15) {
i = FRU_REG_INDEX(t, SCTRL_BLINK_OK_BASE);
blink_reg = SCSB_REG_ADDR(i);
} else {
blink_bit = 0;
blink_reg = 0;
}
/*
* get the registers addresses and shadow register index for
* the SYSCFG register
*/
i = FRU_REG_INDEX(t, SCTRL_SYSCFG_BASE);
syscfg = SCSB_REG_ADDR(i);
i = FRU_REG_INDEX(t, SCTRL_LED_OK_BASE);
led_reg = SCSB_REG_ADDR(i);
/*
* check and set presence status
*/
} else {
}
fru_ptr++;
}
/*
* Alarm Cards
*/
kmem_zalloc(sizeof (fru_info_t) *
int bit_num;
/*
* get the FRU event code (t), then use it to get the
* FRU bit-offsets for SYSCFG register
*/
/*
* get the registers addresses and shadow register index for
* the SYSCFG register
*/
i = FRU_REG_INDEX(t, SCTRL_SYSCFG_BASE);
syscfg = SCSB_REG_ADDR(i);
/*
* check and set presence status
*/
FRU_PRESENT) {
/*
* acslot_ptr->fru_id =
* fru_id_table[event_to_index(t)];
*/
}
} else {
}
fru_ptr++;
}
/*
* SCB
*/
kmem_zalloc(sizeof (fru_info_t) *
unit = 1;
/* SCB15 */
/*
* get the FRU event code (t), then use it to get the
* FRU bit-offset for LED register
*/
i = FRU_REG_INDEX(t, SCTRL_LED_OK_BASE);
led_reg = SCSB_REG_ADDR(i);
if (IS_SCB_P15) {
i = FRU_REG_INDEX(t, SCTRL_BLINK_OK_BASE);
blink_reg = SCSB_REG_ADDR(i);
} else {
blink_bit = 0;
blink_reg = 0;
}
index = SCSB_REG_ADDR(i);
/*
* check and set presence status
*/
} else {
}
/* get PROM_VERSION from shadow registers */
else
/*
* SSB
*/
kmem_zalloc(sizeof (fru_info_t) *
unit = 1;
/* SCB15 */
/*
* get the FRU event code (t), then use it to get the
* FRU bit-offset for SYSCFG register
*/
/*
* get the registers addresses and shadow register index for
* the SYSCFG register
*/
i = FRU_REG_INDEX(t, SCTRL_SYSCFG_BASE);
syscfg = SCSB_REG_ADDR(i);
/*
* check and set presence status
*/
} else {
}
/*
* CFTM
*/
kmem_zalloc(sizeof (fru_info_t) *
unit = 1;
/* SCB15 */
/*
* get the FRU event code (t), then use it to get the
* FRU bit-offsets for LED and SYSCFG registers
*/
if (IS_SCB_P15) {
i = FRU_REG_INDEX(t, SCTRL_BLINK_OK_BASE);
blink_reg = SCSB_REG_ADDR(i);
} else {
blink_bit = 0;
blink_reg = 0;
}
/*
* get the registers addresses and shadow register index for
* the SYSCFG register
*/
i = FRU_REG_INDEX(t, SCTRL_SYSCFG_BASE);
syscfg = SCSB_REG_ADDR(i);
i = FRU_REG_INDEX(t, SCTRL_LED_OK_BASE);
led_reg = SCSB_REG_ADDR(i);
/*
* check and set presence status
*/
FRU_PRESENT) {
}
} else {
}
/*
* CRTM
*/
kmem_zalloc(sizeof (fru_info_t) *
KM_SLEEP);
unit = 1;
/* SCB15 */
/*
* get the FRU event code (t), then use it to get the
* FRU bit-offsets for LED and SYSCFG registers
*/
/*
* get the registers addresses and shadow register index for
* the SYSCFG register
*/
i = FRU_REG_INDEX(t, SCTRL_SYSCFG_BASE);
syscfg = SCSB_REG_ADDR(i);
/*
* check and set presence status
*/
} else {
}
/*
* PRTM
*/
kmem_zalloc(sizeof (fru_info_t) *
unit = 1;
/*
* SCB15
* get the FRU event code (t), then use it to get the
* FRU bit-offsets for LED and SYSCFG registers
*/
/*
* get the registers addresses and shadow register index for
* the SYSCFG register
*/
i = FRU_REG_INDEX(t, SCTRL_SYSCFG_BASE);
syscfg = SCSB_REG_ADDR(i);
/*
* check and set presence status
*/
} else {
}
#ifdef DEBUG
mct_topology_dump(scsb, 0);
#endif
}
/*ARGSUSED*/
static void
{
int i;
if (scsb_debug & 0x00100005)
for (i = 0; i < SCSB_UNIT_TYPES; ++i) {
sizeof (fru_i2c_info_t));
}
(fru_info_t *)NULL) {
mct_system_info.max_units[i]);
}
}
}
#ifdef DEBUG
static void
{
int i;
return;
return;
return;
}
for (i = 0; i < SCSB_UNIT_TYPES; ++i) {
switch ((scsb_utype_t)i) {
case SLOT:
break;
case ALARM:
break;
case DISK:
break;
case FAN:
break;
case PDU:
break;
case PS:
"MCT: MAX Number of Power Supplies: %d",
break;
case SCB:
break;
case SSB:
break;
}
"MCT: type=%d, unit=%d, id=0x%x, "
"version=0x%x",
}
}
}
}
/*
* Sends an event when the system controller board I2C errors
* exceed the threshold.
*/
static void
{
"scsb_intr");
}
#endif
int
{
int error;
int index;
if (scsb_debug & 0x8001) {
}
return (error);
}
/*
* Returns the health status of a slot
*/
int
{
}
/*
* DIAGNOSTIC and DEBUG only.
* Called from ioctl command (SCSBIOC_BHEALTHY_GET)
*/
int
{
int error = 0;
int index;
return (EAGAIN);
/* operation valid for slots only */
return (EINVAL);
}
if (scsb_debug & 0x8001)
suip->unit_number);
return (EINVAL);
}
/*
* Map 1.0 Tonga Slot Number, if necessary
*/
}
/* else shadow regs are updated by interrupt handler */
if (error == 0) {
else
}
return (error);
}
/*
* Called from HSC and ioctl command (SCSBIOC_RESET_UNIT)
* to reset one specified slot
*/
int
{
int error;
int unit_number;
return (EAGAIN);
if (scsb_debug & 0x8001) {
suip->unit_state);
}
(SCSB_DIAGS_MODE | SCSB_DEBUG_MODE))) {
return (EINVAL);
}
return (EINVAL);
}
error = 0;
case ALARM:
{
int i, code;
return (EINVAL);
reg = SCSB_REG_ADDR(i);
break;
}
case SLOT:
if (slotnum > TG_MAX_SLOTS ||
slotnum == SC_TG_CPU_SLOT) {
return (EINVAL);
}
} else {
if (slotnum > MC_MAX_SLOTS ||
slotnum == SC_MC_CPU_SLOT ||
slotnum == SC_MC_CTC_SLOT)) {
return (EINVAL);
}
}
default:
return (EINVAL);
}
else /* OFF */
if (scsb_debug & 0x8002)
"scsb_leds: write failure to 0x%x", reg);
return (error);
}
return (error);
}
/*
* Diagnostic and DEBUG
* This is a helper function for the helper ioctl to pretend that
* scsb h/w is doing its job!!!
*/
int
{
int error;
int saved_unit_number;
return (EACCES);
return (EAGAIN);
}
error = 0;
case ALARM:
if (suip->unit_number !=
return (EINVAL);
}
break;
case SLOT:
/*
* All slots are acceptable, except slots 11 & 12.
*/
break;
}
/* Map 1.0 Tonga Slot Numbers if necessary */
break;
default:
break;
}
if (error)
return (error);
!= 0)
} else {
!= 0)
}
return (error);
}
static int
{
int i, error;
error = 0;
for (i = 1; i <= SCTRL_INTR_NUMREGS; ++i) {
wbuf[i] = 0xff;
}
if (scsb_debug & 0x0402)
"write to 0x%x failed",
}
return (error);
}
static int
{
int error;
int i;
/*
* write loop for Interrupt Mask registers
*/
if (scsb_debug & 0x0401)
error = 0;
rmask = 0;
wdata = 0xff;
for (i = 0; i < SCTRL_MASK_NUMREGS; ++i, ++reg) {
if (scsb_debug & 0x0402)
break;
}
}
return (error);
}
/*
* Clear Interrupt masks based on the FRUs that could be installed
* for this particular topology, determined by the MidPlane ID
* from SCTRL_SYSCFG registers
* case SCTRL_MPID_HALF:
* case SCTRL_MPID_QUARTER:
* case SCTRL_MPID_QUARTER_NODSK:
*/
static int
{
int error;
return (EAGAIN);
}
error = 0;
if (scsb_debug & 0x0400) {
}
continue; /* handle below, 2 reg offsets */
if (scsb_debug & 0x0400)
"clear_intmasks:%d:%d: PRES mask[%d]:0x%x",
/*
* Unmask the corresponding Slot HLTHY mask
* Use Slot bit and register offsets,
* but with SCTRL_INTMASK_HLTHY_BASE
*/
if (scsb_debug & 0x0400) {
"clear_intmasks:Slot:%d: HLTHY mask[%d]:0x%x"
"; reg=0x%x, idx=%d, mbid=%d",
}
}
}
}
/*
* Now unmask these non-fru interrupt events
* SCTRL_EVENT_PWRDWN (almost normal)
* SCTRL_EVENT_REPLACE (not used)
* SCTRL_EVENT_SCB (SCB 1.5 ONLY; plus SCB_INT_OFFSET)
*/
if (IS_SCB_P15) {
}
rmask = 0;
if (scsb_debug & 0x0400)
if (scsb_debug & 0x0402)
"write to 0x%x failed: %d",
break;
}
++msk_reg;
}
return (error);
}
static int
{
register int i;
return (EFAULT);
}
if (scsb_debug & 0x40000000 &&
return (EAGAIN);
}
if (scsb_debug & 0x80000000) {
if ((i = scsb_readall_regs(scsb)) != 0 &&
"scsb_readall_regs() FAILED");
} else {
if ((i = scsb_check_config_status(scsb)) == 0) {
}
}
if (i) {
"scsb_get_status: FAILED Presence LEDs update");
return (EIO);
}
}
for (i = 0; i < SCSB_DATA_REGISTERS; ++i)
return (0);
}
/*
* scsb_freeze_check:
* Turn all the leds off on the system monitor card, without changing
* the state of what we have for scsb. This routine is called only when
* replacing system monitor card, so the state of the card leds could be
* restored, using scsb_restore().
* Also, set state to SCSB_FROZEN which denies access to scsb while in
* freeze mode.
*/
static char *BAD_BOARD_MSG =
"SCSB: Should NOT remove SCB(%d) while cPCI Slot %d is "
"in RESET with a possible bad board.";
static int slots_in_reset[SCTRL_MAX_GROUP_NUMREGS];
static void
{
register int i;
int offset;
int index;
if (scsb_debug & 0x20001)
return;
}
for (i = 0; i < SCTRL_MAX_GROUP_NUMREGS; ++i)
slots_in_reset[i] = 0;
/*
* We allow the SCB to be removed only if none of
* the cPCI resets are asserted for occupied slots.
* There shouldn't be a bad board plugged in the system
* while swapping the SCB.
*/
if (IS_SCB_P15) {
} else {
}
}
}
}
}
static void
{
if (scsb_debug & 0x00020002) {
}
return;
/*
* Send the EVENT_SCB since there is evidence that the
* System Controller Board has been removed.
*/
}
/*
* scsb_restore will only be called from the interrupt handler context on
* INIT_SCB interrupt for newly inserted SCB.
* Called with mutex held.
*/
static void
{
if (scsb_debug & 0x20001)
if (scsb_debug & 0x00020002) {
return;
}
}
/* 9. Clear all Interrupts */
if (scsb_clear_intmasks(scsb)) {
if (scsb_debug & 0x00020002) {
}
return;
}
/* 10. */
/* Check if Alarm Card present at boot and set flags */
else
}
/*
* Given an Event Code,
* Return:
* FRU type in LSByte
* unit number in MSByte
*/
{
for (i = li = 0; i < SCSB_UNIT_TYPES; ++i) {
if (evcode == type_to_code1[i]) {
return (ret);
}
if (evcode < type_to_code1[i]) {
unit = 1;
return (ret);
}
li = i;
}
return ((uint16_t)0xffff);
}
/*
* scsb interrupt handler for (MC) PSM_INT vector
* P0.6: HW shipped to beta customers
* 1. did not have Slot Occupant Presense support
* 2. I2C interrupt-map properties not yet tested, using polling daemon
* 3. Polling detects each event reliably twice.
* clr_bits# are used to keep track of events to be ignored 2nd time
*
* retval flags allow all events to be checked, and still returning the
* correct DDI value.
*
*/
#define SCSB_INTR_CLAIMED 1
#define SCSB_INTR_UNCLAIMED 2
#define SCSB_INTR_EVENT 4
/*
* Does preprocessing of the interrupt. The only thing this
* needs to do is to ask scsb to release the interrupt line.
* and then schedule delayed actual processing using timeout()
*/
{
/*
* If SCSB_IN_INTR is already set in scsb_state,
* it means we are being interrupted by someone else. This can
* happen only if the interrupt does not belong to scsb, and some
* other device, e.g. a FAN or PS is interrupting. So, we
* cancel the previous timeout().
*/
goto intr_end;
}
/*
* Stop scsb from interrupting first.
*/
goto intr_end;
}
/*
* Schedule a timeout to actually process the
* interrupt.
*/
drv_usectohz(1000));
return (DDI_INTR_CLAIMED);
}
void
{
int ac_present;
/*
* Avoid mayhem, make sure we have only one timeout thread running.
*/
while (scsb_in_postintr)
scsb_in_postintr = 1;
scb_post_s = gethrtime();
if (scsb_debug & 0x00002000)
retval = 0;
tmp_reg = 0;
/*
* XXX: Problem, when we want to support swapping between SCB
* versions, then we need to check the SCB PROM ID (CF) register here
* before assuming the same SCB version was re-inserted.
* We will have to duplicate some of the scb_initialization()
* code to set the scsb_state PROM ID bits and to set up the
* register table pointers.
*
* Only if NOT SSB_PRESENT, check the SCB PROM ID
*/
#ifdef DEBUG
#endif
goto intr_error;
}
}
if (IS_SCB_P15) {
} else {
}
/*
* Now check the INTSRC registers for set bits.
* Do a quick check by OR'ing INTSRC registers together as we copy
* them from the transfer buffer. For P1.0 or earlier we had already
* read the interrupt source registers and wrote them back to stop
* interrupt. So we need to do this step only for P1.5 or later.
* We already read INTSRC6 to take care of SCB insertion case, so
* do not read INTSRC6 again.
*/
if (IS_SCB_P15) {
/* read the interrupt register from scsb */
" Failed read of interrupt registers.");
#ifdef DEBUG
#endif
goto intr_error;
}
}
/*
* We have seen that an interrupt source bit can be set
* even though the corresponding interrupt mask bit
* has been set to mask the interrupt. So we must
* clear all bits set in the interrupt source register.
*/
for (i = 0; i < SCTRL_INTR_NUMREGS; ++i) {
#ifdef DEBUG
if (scsb_debug & 0x08000000) {
if (tmp_reg || scb_intr_regs[i]) {
i + 1, scb_intr_regs[i]);
++tmp_reg;
}
}
#endif
}
/*
* Any bits from quick check? If this is not our interrupt,
* blocked, but we can not be sure. So, go ahead and call the
* their interrupts, if they aren't already masked.
*/
if (retval == 0) {
goto intr_error;
}
retval = 0;
/*
* If SCB 1.5 or 2.0, check for the INIT_SCB Interrupt
* to support Hot SCB Insertion.
* The check was moved here during debugging of the SCB hot insertion.
* Theoretically, this code could be moved back to the check for
* SCTRL_EVENT_SCB in the processing loop below.
*/
if (IS_SCB_P15) {
int iid;
/*
* Must be newly inserted SCB
* Time to re-initialize.
*/
if (scsb_debug & 0x00023000) {
"scsb_intr(%d): INIT_SCB INT",
}
/*
* The INTSRC bit will be cleared by the
* scsb_restore() function.
* Also, leave the bit set in scb_intr_regs[] so we can
* report the event code as we check for other
* interrupt source bits.
*
* scsb_write_mask(scsb, tmp_reg, 0, clr_bits, 0);
* scb_intr_regs[intr_idx] &= ~clr_bits;
*/
}
/*
* In case this is a power down interrupt, check the validity
* of the request to make sure it's not an I2C noise
*/
/*
* A shutdown request has been detected. Poll
* the corresponding register ? more times to
* make sure it's a genuine shutdown request.
*/
for (i = 0; i < scsb_shutdown_count; i++) {
drv_usecwait(1000);
" interrupt register");
goto intr_error;
}
if (scsb_debug & 0x08000000) {
" INTSRC6[%d]=0x%x", i,
intr_reg);
}
break;
}
}
}
}
/*
* if retval == 0, then we didn't call scsb_restore,
* so we update the shadow copy of SYSCFG registers
* We *MUST* read the syscfg registers before any attempt
* to clear the interrupt source registers is made.
*/
if (!scsb_debug) {
goto intr_error;
}
}
#ifdef DEBUG
}
#endif
/*
* Allow to go on so we clear the INTSRC bits
*/
}
/*
* Read the board healthy registers here, if any of the healthy
* interrupts are set.
*/
if (IS_SCB_P15) {
for (i = 0; i < SCTRL_BHLTHY_NUMREGS; ++i, ++intr_idx) {
intr_reg |= scb_intr_regs[i];
}
#ifdef DEBUG
}
#endif
goto intr_error;
}
}
/*
* We clear the interrupt source registers now itself so that
* future interrupts can be latched quickly, instead of after
* finishing processing of all interrupt conditions. The global
* interrupt mask however remain disabled.
*/
if (IS_SCB_P15) {
" registers.");
#ifdef DEBUG
}
#endif
goto intr_error;
}
}
/*
* At this point, all interrupt source registers are read.
* We only handle interrups which are not masked
*/
for (i = 0; i < SCTRL_INTR_NUMREGS; ++i) {
scb_intr_regs[i] &= int_masks[i];
}
/*
* We are here means that there was some bit set in the interrupt
* source register. So we must claim the interrupt no matter
* whatever error we may encounter in the course of processing.
*/
/* store config status data */
for (i = 0; i < SCTRL_CFG_NUMREGS; ++i)
/*
* Clear the event code,
* then check to see what kind(s) of events we were interrupted for.
* Check all SCTRL_INTSRC registers
*/
scsb_event_code = 0;
clr_bits = 0;
intr_idx = 0;
/*
* If SCB 1.5, adjust some variables to skip the SCTRL_BHLTHY_REGS
* which will be handled last in this function.
*/
if (IS_SCB_P15) {
i = SCTRL_BHLTHY_NUMREGS;
intr_idx += i;
intr_addr += i;
index += i;
}
/*
* For the rest of the INTSRC registers, we walk through the
* scb_fru_offset[] table, matching register offsets with our offset
* counter. Then we check for the scb_fru_offset[] bit in intr_reg.
* The scb_fru_offset[] index is now the SCTRL_EVENT code.
* The code is then compared to type_to_code1[] entries to find the
* fru_type. The fru_type will help us recognize when to do
* SLOT Hot Swap processing.
*
* offset_base: the appropriate scb_fru_offset[] base index
* for the INTPTR_BASE register group
* offset: bit offset found in INTSRC register
* intr_idx: index to temporary INTSRC register copies
* intr: modified copy of current INTR register
* intr_addr: SCB register address of current INTR register
* index: index to current INTR shadow register
* idx: bit-number of current INTR event bit
* uc: uchar_t from scb_fru_offset[] table,
* containing register and FRU offsets.
* j: used to walk fru_offset[] table, which is also
* the bit-number of the current event code
* code: manufactured event code for current INT event
*/
while (intr_reg) { /* for each INTSRC bit that's set */
int j;
for (j = 0; j < MCT_MAX_FRUS; ++j) {
/*
* Get register offset from table and check
* for a match with our loop offset counter.
* Then check for intr_reg bit-offset match
* with bit-offset from table entry.
*/
if (IS_SCB_P10)
continue;
if (j != FRU_INDEX(SCTRL_EVENT_SCB))
continue;
continue;
}
break;
}
if (uc == 0xff) {
/*
* bit idx not recognized, check another.
*/
continue;
}
/*
* We found the fru_offset[] entry, now use the index
* to get the event code.
*/
if (scsb_debug & 0x00002000) {
}
/*
* Now check for the NON-FRU type events.
*/
if (code == SCTRL_EVENT_PWRDWN) {
if (scsb_debug & 0x1002) {
"scsb_intr(%d): power down req."
}
scsb_event_code |= code;
/*
* inform applications using poll(2)
* about this event, and provide the
* event code to EnvMon scsb policy
*/
if (!(scsb_debug & 0x00040000))
&scsb_event_code, "scsb_intr");
goto intr_error;
}
continue;
} else if (code == SCTRL_EVENT_REPLACE) {
if (scsb_debug & 0x1002) {
"scsb_intr(%d): replacement "
"req. INT.",
}
scsb_event_code |= code;
continue;
} else if (code == SCTRL_EVENT_SCB) {
int tmp;
/*
* Must be newly inserted SCB
* Time to re-initialize.
*/
if (scsb_debug & 0x1002) {
"scsb_intr(%d): INIT SCB INTR",
}
/*
* SCB initialization already handled, but we
* set the event code bit here in order to
* report the event to interested utilities.
*
* scsb_restore(scsb);
* The INTSRC bit is already cleared,
* so we won't do it again.
*/
scsb_event_code |= code;
continue;
} else if (code == SCTRL_EVENT_ALARM_INT) {
/*
* set and cannot be cleared, so ignore it.
*/
if (!IS_SCB_P15) {
continue;
}
if (scsb_debug & 0x1002) {
"scsb_intr(%d): Alarm INT.",
}
scsb_event_code |= code;
/*
* XXX:
* Must service the Alarm INT by clearing INT
* condition on Alarm Card,
* then clear the SCTRL_INTR_ALARM_INT bit here.
* Waiting for specs and test environment.
*/
continue;
/*
* FRU type not found
*/
break;
}
/*
* Check for special processing
* now that we found the FRU type.
*/
if (scsb_debug & 0x00002000) {
}
switch (fru_type) {
case PDU:
break;
case PS:
break;
case DISK:
break;
case FAN:
break;
case SSB:
/*
* in check_fru_info() below, we see if the
* SSB has been removed, then check for
* occupied slots in reset to see if we should
* WARN agains SCB removal
*/
break;
case CFTM:
break;
case CRTM:
break;
case PRTM:
break;
case SLOT:
if (scsb_debug & 0x00002000) {
}
/*
* If the slot number is not valid, continue.
*/
if (slotnum > TG_MAX_SLOTS ||
slotnum == SC_TG_CPU_SLOT) {
continue;
}
/*
* For a tonga, we need to return
* the code corresponding to the
* actual physical slot
*/
slotnum);
} else {
if (slotnum > MC_MAX_SLOTS ||
slotnum == SC_MC_CPU_SLOT ||
(scsb->scsb_hsc_state &
slotnum == SC_MC_CTC_SLOT)) {
continue;
}
}
/* FALLTHROUGH */
case ALARM:
/*
* INDENT CHEATING, 2 indentations
*/
ac_present = 0;
/*
* If it is an Alarm Card Interrupt, we just do some sanity
* checks and then wait for the slot interrupt to take
* connect or disconnect action.
* XXX - Is there a gaurantee that ALARM int will occur first ?
*/
/*
* It is observed that slot presence and Alarm
* presence bits do not go ON at the same time.
* Hence we wait till both events happen.
*/
#ifdef DEBUG
if ((((val) && (!ac_present)) ||
((!val) && (ac_present))) &&
(scsb->scsb_hsc_state &
"state bits do not match! (%x,%x)",
val, ac_present);
#endif
else
break; /* we break and wait for slot interrupt. */
}
/*
* cPCI slot interrupt event
*/
if (slotnum > TG_MAX_SLOTS ||
slotnum == SC_TG_CPU_SLOT) {
continue;
}
} else {
if (slotnum > MC_MAX_SLOTS ||
slotnum == SC_MC_CPU_SLOT ||
slotnum == SC_MC_CTC_SLOT)) {
continue;
}
}
}
#ifdef DEBUG
if ((((val) && (!ac_present)) ||
((!val) && (ac_present))) &&
(scsb->scsb_hsc_state &
"state bits do not match! (%x,%x)",
val, ac_present);
}
#endif
else
}
if (val) {
if (ac_present) {
if (scsb_debug & 0x00010000) {
"AC_PRES slot %d", slotnum);
}
}
#ifndef lint
else
#endif
/*
* Special case : check MPID type.
* If MC midplane type,
* check to make sure the Alarm Card present
* bit is ON. If not, this is a regular IO card.
*/
} else {
#ifdef DEBUG
if (scsb_debug & 0x00010000) {
"!AC_PRES slot %d",
slotnum);
}
#endif /* DEBUG */
}
#ifndef lint
else
#endif
}
/*
* END INDENT CHEATING, 2 indentations
*/
break;
default:
/*
* ERROR: Did not find cause of INTSRC bit
*/
if (scsb_debug & 0x00000002) {
"scsb_intr: FRU type %d"
" not recognized", fru_type);
}
continue;
}
scsb_event_code |= code;
continue;
error = 0;
continue;
ledata_reg) == 0)
continue;
if (error) {
"I2C write error to 0x%x",
tmp_reg);
if (!(scsb->scsb_state &
SCSB_DEBUG_MODE)) {
goto intr_error;
}
}
break;
}
}
if (clr_bits) {
clr_bits = 0;
}
}
/*
* Check for SCB 1.5 interrupt for SLOT HEALTHY changes
*/
clr_bits = 0;
intr_idx = 0;
if (IS_SCB_P15) {
for (i = 0; i < SCTRL_BHLTHY_NUMREGS;
intr_reg = scb_intr_regs[i];
while (intr_reg) {
/* idx + 1 because bit 0 is for Slot 1 */
if (slotnum > TG_MAX_SLOTS ||
slotnum == SC_TG_CPU_SLOT) {
continue;
}
} else {
if (slotnum > MC_MAX_SLOTS ||
slotnum == SC_MC_CPU_SLOT ||
(scsb->scsb_hsc_state &
slotnum == SC_MC_CTC_SLOT)) {
continue;
}
}
}
if (clr_bits) {
clr_bits = 0;
}
}
}
if (retval & SCSB_INTR_EVENT &&
"scsb_intr");
}
scb_post_e = gethrtime();
if (scsb_debug & 0x8000000)
scb_post_e - scb_post_s);
scsb_in_postintr = 0;
/*
* Re-enable interrupt now.
*/
}
static int
{
if (scsb_debug & 0x4000)
*set = 0;
if (cmd == SCSBIOC_SHUTDOWN_POLL) {
return (EINVAL);
}
if (cmd != SCSBIOC_INTEVENT_POLL) {
return (EINVAL);
}
/*
* scsb_intr() may modify scsb_event_code
*/
*set = scsb_event_code;
scsb_event_code = 0;
} else {
/*
* SCSB_P06_INTR_ON, we know there was an event
* and we're retrieving the event code from the event FIFO.
*/
*set = get_event_code();
}
if (scsb_debug & 0x01004000) {
}
return (0);
}
static int
{
register int i;
int index;
return (EAGAIN);
}
if (scsb_debug & 0x0101) {
}
if (scsb_debug & 0x0100) {
}
idata = 0xff;
else /* off */
idata = 0x00;
for (i = 0; i < SCTRL_LED_NOK_NUMREGS; ++i) {
}
rwbuf, 1);
if (i) {
if (scsb_debug & 0x0102)
"Failed to turn %s NOK LEDs",
}
if (scsb_debug & 0x0100) {
}
for (i = 0; i < SCTRL_LED_OK_NUMREGS; ++i) {
}
rwbuf, 1);
if (i) {
if (scsb_debug & 0x0102)
"Failed to turn %s NOK LEDs",
}
/* Step 3: turn OFF all BLINK LEDs. */
for (i = 0; i < SCTRL_BLINK_NUMREGS; ++i) {
}
rwbuf, 1);
if (i) {
if (scsb_debug & 0x0102)
"Failed to turn %s BLINK BITs",
}
}
return (0);
}
static int
{
int error;
int index;
if (!(scsb_debug & 0x40000000))
return (0);
if (scsb_debug & 0x0005) {
}
return (EAGAIN);
}
return (error);
}
/*
* read 1-byte register, mask with read bits (rmask),
* turn ON bits in on_mask, turn OFF bits in off_mask
* write the byte back to register
* NOTE: MUST be called with mutex held
*/
static int
{
if (scsb_debug & 0x0800) {
}
return (EAGAIN);
}
/* select the register address and read the register */
goto wm_error;
}
scsb->scsb_i2c_errcnt = 0;
if (scsb_debug & 0x0800)
if (rmask)
if (off_mask)
if (on_mask)
goto wm_error;
}
/* keep shadow registers updated */
if (scsb_debug & 0x0800)
scsb->scsb_i2c_errcnt = 0;
return (error);
scsb->scsb_i2c_errcnt++;
if (scsb_debug & 0x0802)
"scsb_write_mask(): reg %x %s error, data=%x",
reg,
} else {
return (EAGAIN);
}
return (error);
}
/*
* NOTE: should be called with mutex held
*/
static int
{
if (scsb_debug & 0x0800) {
}
return (EAGAIN);
}
if (i2c_alloc) {
if (scsb_debug & 0x0042)
"i2ctx allocation failure");
return (ENOMEM);
}
} else {
}
switch (op) {
case I2C_WR:
rlen = 0;
for (i = 0; i < len; ++i) {
if (scsb_debug & 0x0080)
"scsb_rdwr_register: writing rwbuf[%d]=0x%x",
i, rwbuf[i]);
}
break;
case I2C_WR_RD:
break;
default:
if (i2c_alloc)
return (EINVAL);
}
/* select the register address */
} else if (rlen) {
/* copy to rwbuf[] and keep shadow registers updated */
for (i = 0; i < len; ++i) {
if (scsb_debug & 0x0080)
"scsb_rdwr_register: read rwbuf[%d]=0x%x",
i, rwbuf[i]);
}
}
if (i2c_alloc)
if (error) {
scsb->scsb_i2c_errcnt++;
return (EAGAIN);
} else {
"scsb_rdwr_register(): I2C read error from %x",
reg);
}
} else {
scsb->scsb_i2c_errcnt = 0;
}
return (error);
}
/*
* Called from scsb_intr()
* First find the fru_info for this fru_id, and set fru_status for callback.
* Then check for a registered call_back entry for this fru_id,
* and if found, call it.
* Recursize call until no EVENTS left in evcode.
*/
static void
{
struct scsb_cb_entry *cbe_ptr;
int i, new_evcode;
if (scsb_debug & 0x00100001)
if (evcode == 0)
return;
if (i > MCT_MAX_FRUS) {
if (scsb_debug & 0x00100000)
"check_fru_info: index %d out of range", i);
return;
}
fru_id = fru_id_table[i];
return;
}
} else {
/*
* WARN against SCB removal if any
* occupied slots are in reset
*/
}
}
/*
* check for an entry in the CallBack table
*/
if (scsb_debug & 0x00800000)
"check_fru_info: callback for FRU_ID "
"0x%x; device is %spresent",
(int)fru_id,
fru_status == FRU_PRESENT ?
"" : "not ");
break;
}
}
}
/*
* -----------------------------
* scsb kstat support functions.
* -----------------------------
*/
/*
* Create and initialize the kstat data structures
*/
static int
{
/*
* scsb_ks_leddata_t for "scsb_leddata"
*/
if (scsb_debug & 0x00080001)
"scsb_alloc_kstats: create scsb_leddata: %lu bytes",
sizeof (scsb_ks_leddata_t));
sizeof (scsb_ks_leddata_t), KSTAT_FLAG_PERSISTENT))
== NULL) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* scsb_ks_state_t for "scsb_state"
*/
if (scsb_debug & 0x00080000)
"scsb_alloc_kstats: create scsb_state: %lu bytes",
sizeof (scsb_ks_state_t));
sizeof (scsb_ks_state_t), KSTAT_FLAG_PERSISTENT))
== NULL) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* mct_topology_t for "env_topology"
*/
if (scsb_debug & 0x00080000)
"scsb_alloc_kstats: create env_toploogy: %lu bytes",
sizeof (mct_topology_t));
sizeof (mct_topology_t), KSTAT_FLAG_PERSISTENT))
== NULL) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* kstat_named_t * 2 for "scsb_evc_register"
*/
if (scsb_debug & 0x00080001)
"scsb_alloc_kstats: create scsb_evc_register: %lu bytes",
sizeof (kstat_named_t) * 2);
return (DDI_FAILURE);
}
/*
* Done, set the flag for scsb_detach() and other checks
*/
return (DDI_SUCCESS);
}
static int
{
if (scsb_debug & 0x00080001)
/*
* Since this is satisfied from the shadow registers, let it succeed
* even if the SCB is not present. It would be nice to return the
* shadow values with a warning.
*
* if (scsb->scsb_state & SCSB_FROZEN) {
* return (DDI_FAILURE);
* }
*/
if (rw == KSTAT_WRITE) {
return (EACCES);
}
return (EINTR);
}
}
if (scsb_debug & 0x00080001)
/*
* Call tonga_slotnum_led_shift() for each register that
* contains Slot 1-5 information, the first register at each base:
* NOK_BASE, OK_BASE, BLINK_OK_BASE
* XXX: breaking register table access rules by not using macros.
*/
/* NOK */
i = 0;
if (IS_SCB_P15)
else
/* OK */
if (IS_SCB_P15)
else
/* BLINK */
if (IS_SCB_P15)
else
if (scsb_debug & 0x00080001)
return (error);
}
static int
{
int error = 0;
if (scsb_debug & 0x00080001)
/*
* Let this registration succeed
*
* if (scsb->scsb_state & SCSB_FROZEN) {
* return (DDI_FAILURE);
* }
*/
return (EINTR);
}
}
if (rw == KSTAT_READ) {
} else if (rw == KSTAT_WRITE) {
/*
* kn[0] is "pid_register", kn[1] is "pid_unregister"
*/
if (scsb_debug & 0x02000002) {
"update_ks_evcreg: "
"process add failed for %d",
pid);
}
}
if (scsb_debug & 0x02000000) {
"update_ks_evcreg: "
"process delete failed for %d",
pid);
}
}
/*
* rewind the pointers and counts, zero the table.
*/
} else {
}
} else {
}
return (error);
}
static int
{
int error = DDI_SUCCESS;
if (scsb_debug & 0x00080001)
/*
* Let this succeed based on last known data
*
* if (scsb->scsb_state & SCSB_FROZEN) {
* return (DDI_FAILURE);
* }
*/
if (rw == KSTAT_WRITE) {
return (EACCES);
}
return (EINTR);
}
}
/*
* If SSB not present and scsb not SCSB_FROZEN, check for SCB presence
* by initiating an I2C read from the SCB. If an error occurs,
* scsb_freeze() will be called to update SCB info and scsb state.
*/
/* Read the SCB PROM ID */
if (scsb_debug & 0x00080002)
"failure %d", data);
}
else
/*
* If scsb_attach() has not completed the kstat installs,
* then there are no event processes to check for.
*/
switch (check_event_procs(¤t_evc)) {
case EVC_NO_EVENT_CODE:
pks_state->event_code = 0;
break;
case EVC_NEW_EVENT_CODE:
/* FALLTHROUGH */
case EVC_NO_CURR_PROC:
break;
case EVC_OR_EVENT_CODE:
break;
case EVC_FAILURE:
pks_state->event_code = 0;
error = DDI_FAILURE;
break;
}
} else {
pks_state->event_code = 0;
}
return (error);
}
static int
{
if (scsb_debug & 0x00080001)
/*
* Let this succeed based on last known data
*
* if (scsb->scsb_state & SCSB_FROZEN) {
* return (DDI_FAILURE);
* }
*/
if (rw == KSTAT_WRITE) {
return (EACCES);
}
return (EINTR);
}
}
/*
* If SSB not present and scsb not SCSB_FROZEN, check for SCB presence
* by initiating an I2C read from the SCB. If an error occurs,
* scsb_freeze() will be called to update SCB info and scsb state.
*/
/* Read the SCB PROM ID */
if (scsb_debug & 0x00080002)
"failure %d", data);
}
for (i = SLOT; i < SCSB_UNIT_TYPES; ++i) {
}
/*
* XXX: need to check healthy regs to set fru_health
*/
}
}
}
}
}
/*
* To get the scsb health, if there was no i2c transaction
* until this read, generate an i2c transaction.
*/
}
? MCT_HEALTH_NOK : MCT_HEALTH_OK);
#ifdef DEBUG
mct_scb[i].fru_health);
#endif
}
}
}
}
}
}
return (error);
}
static void
{
return;
/*
* free the allocated kstat data
*/
}
}
}
}
}
/*
* --------------------------------------
* Miscellaneous scsb internal functions.
* --------------------------------------
*
* allocate I2C transfer structure
*/
static i2c_transfer_t *
{
return (NULL);
}
return (tp);
}
/*
* free I2C transfer structure
*/
static void
{
}
static void
{
int index;
if (scsb_debug & 0x00100001)
return;
/*
* If this is an Alarm Card update, then we also need to get
* Alarm Card Slot fru_ptr to update it's fru_type, and maybe fru_id
*/
/*
* SCTRL_EVENT_SLOT1 == 0x01 so
* fru_id_table[] index for Slot 1 == 0
*/
}
else
} else if (reg) {
/*
* XXX: need to add version register, and maybe a
* method, to the fru_ptr->i2c_info structure.
*
* fru_ptr->fru_version = (fru_version_t)0;
*/
/*
* Because scsb_intr() sometimes gets the AC present
* INT before the ACSLOT present INT,
* do not check the ACSLOT fru_status
*
* if (acslot_ptr != NULL && acslot_ptr->fru_status ==
* FRU_PRESENT)
*/
if (acslot_ptr != NULL)
} else {
/*
* fru_ptr->fru_version = (fru_version_t)0;
*/
if (acslot_ptr != NULL) {
/* AC just removed, but AC Slot is occupied? */
/* for now it's unknown */
else
}
}
}
if (scsb_debug & 0x00100000)
"update_fru_info: type %d unit %d is %spresent",
? "" : "not ");
}
/*
* Convert EVENT code to FRU index
* by finding the highest bit number in 32 bit word
*/
static int
{
int i = 0;
if (evcode == 0)
return (MCT_MAX_FRUS - 1);
for (; (evcode >>= 1); i++)
;
return (i);
}
#ifdef DEBUG
void
{
if (scsb_debug & 0x8000 ||
if (*fmt == 'X')
++fmt;
prom_printf("scsb: ");
prom_printf("\n");
}
}
#endif
/*
* event code functions to deliver event codes
* and to manage:
* the event code fifo
* the process handle table for registered processes interested in
* event codes
*/
/*
* Send signal to processes registered for event code delivery
*/
static void
{
int i = 0, c = 0;
if (evc_proc_count == 0)
return;
for (; i < EVC_PROCS_MAX; ++i) {
if (scsb_debug & 0x02000002)
"scsb:signal_evc_procs: "
"signal to %d failed",
((struct pid *)
(void) del_event_proc(scsb,
}
if (++c >= evc_proc_count) {
if (scsb_debug & 0x02000000) {
"signal_evc_procs: signaled "
"%d/%d processes", c,
}
break;
}
}
}
}
/*
* bump FIFO ptr, taking care of wrap around
*/
static uint32_t *
{
return (ptr);
}
/* ARGSUSED */
static void
{
evc_fifo_count = 0;
}
/*
* Called from scsb_intr() when a new event occurs, to put new code in FIFO,
* and signal any interested processes in evc_procs[].
* Always succeeds.
*/
static void
{
if (event_proc_count(scsb) == 0) {
return;
}
*evc_wptr = event_code;
if (++evc_fifo_count > EVC_FIFO_SIZE) {
--evc_fifo_count; /* lose the oldest event */
}
if (scsb_debug & 0x01000000) {
}
}
/*
* called from check_event_procs() when the last registered process
* retrieved the oldest event
*/
static uint32_t
{
if (!evc_fifo_count)
return (scsb_event_code);
if (scsb_debug & 0x01000000) {
}
return (evc);
}
/*
* called from check_event_procs() to retrieve the current event code
*/
static uint32_t
{
if (!evc_fifo_count)
return (0);
return (*evc_rptr);
}
/*
* called from an application interface (ie: an ioctl command)
* to register a process id interested in SCB events.
* NOTE: proc_ref() must be called from USER context, so since this is a
* streams driver, a kstat interface is used for process registration.
* return:
* 0 = event_proc was added
* 1 = out of space
*/
/* ARGSUSED */
static int
{
int i = 0;
void *curr_proc;
if (evc_proc_count >= EVC_PROCS_MAX)
return (1);
if (scsb_debug & 0x02000000) {
"add_event_proc: current %d != requestor %d",
} else {
return (1);
}
}
for (; i < EVC_PROCS_MAX; ++i) {
if (scsb_debug & 0x02000000) {
"add_event_proc: %d; evc_proc_count=%d",
}
return (0);
}
}
return (1);
}
/*
* called from an application interface (ie: an ioctl command)
* to unregister a process id interested in SCB events.
* return:
* 0 = event_proc was deleted
* 1 = event_proc was not found, or table was empty
*/
/* ARGSUSED */
static int
{
int i = 0;
int cnt = 0;
void *this_proc;
if (evc_proc_count == 0)
return (1);
for (; i < EVC_PROCS_MAX; ++i) {
continue;
if (--evc_proc_count == 0) {
/*
* reset evc fifo cound and pointers
*/
}
if (scsb_debug & 0x02000000) {
"del_event_proc: %d; evc_proc_count=%d",
}
return (0);
}
if (++cnt >= evc_proc_count)
break;
}
return (1);
}
/*
* Can be called from an application interface
* to rewind the pointers and counters, and zero the table
* return:
*/
/* ARGSUSED */
static void
{
int i = 0;
if (scsb_debug & 0x02000001) {
}
for (; i < EVC_PROCS_MAX; ++i) {
proc_unref(evc_procs[i]);
}
}
evc_proc_count = 0;
}
/* ARGSUSED */
static int
{
return (evc_proc_count);
}
/*
* return:
* 1 = pid was found
* 0 = pid was not found, or table was empty
*/
static int
{
int i = 0;
int cnt = 0;
if (evc_proc_count == 0)
return (0);
for (; i < EVC_PROCS_MAX; ++i) {
continue;
return (1);
if (++cnt >= evc_proc_count)
break;
}
return (0);
}
/*
* called from update_ks_state() to compare evc_proc_count with
* evc_requests, also mainted by this same function
* This function could check the current process id, since this will be a user
* context call, and only bump evc_requests if the calling process is
* registered for event code delivery.
* return:
* EVC_NO_EVENT_CODE : no event_code on fifo
* EVC_NO_CURR_PROC : current process not in table,
* but have an event_code
* EVC_NEW_EVENT_CODE : return_evc is new ks_state->event_code
* EVC_OR_EVENT_CODE : OR return_evc with ks_state->event_code
* EVC_FAILURE : unrecoverable error condition.
*/
static int
{
void *curr_proc;
int return_val = 0;
static int evc_requests = 0;
/*
* get current process handle, and check the event_procs table
*/
if (evc_proc_count == 0) {
*return_evc = del_event_code();
} else {
if (!find_evc_proc(curr_pid)) {
*return_evc = get_event_code();
} else if (++evc_requests >= evc_proc_count) {
evc_requests = 0;
*return_evc = del_event_code();
} else {
*return_evc = get_event_code();
}
if (!return_val)
}
if (scsb_debug & 0x02000000) {
"requests=%d, returning 0x%x", curr_pid,
}
return (return_val);
}
static int
{
if (scsb_debug & 0x4001) {
}
caller);
return (B_FALSE);
}
while (count--) {
++data;
}
return (B_TRUE);
}
/* CLONE */
static int
int op,
int oparg,
void *opdata,
char *caller)
{
switch (op) {
case QPUT_INT32:
return (QOP_FAILED);
}
/*FALLTHROUGH*/ /* to look for opened clones */
case QPROCSOFF:
/*FALLTHROUGH*/
case QFIRST_OPEN:
case QFIND_QUEUE:
find_open = 1;
find_available = 0;
break;
case QFIRST_AVAILABLE:
find_available = 1;
find_open = 0;
break;
}
return (QOP_FAILED);
}
switch (op) {
case QPROCSOFF:
break;
case QPUT_INT32:
== B_FALSE) {
retval = QOP_FAILED;
}
break;
case QFIRST_OPEN:
return (clone);
case QFIND_QUEUE:
return (clone);
}
break;
}
switch (op) {
case QFIRST_AVAILABLE:
return (clone);
}
}
}
return (retval);
}
/*
* Find out if a bit is set for the FRU type and unit number in the register
* set defined by the register base table index, base.
* Returns TRUE if bit is set, or FALSE.
*/
static int
int op)
{
int rc;
#if 0
reg = SCSB_REG_ADDR(i);
#endif
/* get the event code based on which we get the reg and bit offsets */
/* get the bit offset in the 8bit register corresponding to the event */
/* register offset from the base register, based on the event code */
else
/* get the global offset of the register in the parent address space */
/* get the global index of the register in this SCSB's address space */
DEBUG4("scsb_fru_op(start): code=%x, offset=%x, tmp=%x, reg=%x\n",
switch (op) {
case SCSB_FRU_OP_GET_REG:
break;
case SCSB_FRU_OP_GET_BITVAL:
>> offset;
break;
case SCSB_FRU_OP_GET_REGDATA:
break;
case SCSB_FRU_OP_SET_REGBIT:
break;
default:
break;
}
return (rc);
}
/*
* All HSC related functions can fail, but an attempt is made to atleast
* return the right shadow state on get-state function when SCB is removed.
*/
int
{
/*
* When SCB is removed, we could be called with the lock held.
* We call check_config_status anyway since it is a read-only operation
* and HSC could be invoking this function at interrupt context.
* If scsb is already in the doing interrupt postprocess, wait..
*/
/* check if error is because SCB is removed */
return (DDI_FAILURE);
if (! val) {
*rstate = HPC_SLOT_EMPTY;
return (0);
}
/*
* now, lets determine if it is connected or disconnected.
* If reset is asserted, then the slot is disconnected.
*/
/* check if error is because SCB is removed */
return (DDI_FAILURE);
if (val)
else {
} else {
"Healthy# Failed slot %d!",
}
}
return (0);
}
int
{
if (scsb_debug & 0x8001)
return (EAGAIN);
I2C_NOSLEEP)) == NULL) {
return (ENOMEM);
}
alarm_card = 1;
}
scsb->scsb_i2c_errcnt = 0;
/*
* XXX: following statements assume 2 reset registers,
* which is the case for our current SCB revisions.
*/
} else {
scsb->scsb_i2c_errcnt++;
}
" reading Reset regs\n",
error = DDI_FAILURE;
}
return (error);
}
if (alarm_card) {
}
if (alarm_card) {
if (ac_val) {
condition_exists = 1;
DEBUG0("Alarm_RST# already active.\n");
}
#ifndef lint
else
DEBUG1("Alarm_RST# not active! "
"Slot%d_RST# active!\n", pslotnum);
#endif
} else {
condition_exists = 1;
}
}
else
if (alarm_card) {
if (!ac_val) {
DEBUG0("Alarm_RST# not active.\n");
condition_exists = 1;
}
#ifndef lint
else
DEBUG1("Alarm_RST# active"
" Slot%d_RST# not active!\n",
pslotnum);
#endif
} else {
condition_exists = 1;
DEBUG1("Slot%d_RST# already not active!\n",
pslotnum);
}
}
if (! condition_exists) {
if (reset_flag == SCSB_RESET_SLOT) {
#ifdef DEBUG /* dont reset Alarm Card line unless in debug mode */
if (alarm_card)
#endif
} else {
#ifdef DEBUG /* dont Unreset Alarm Card line unless in debug mode */
if (alarm_card)
#endif
}
scsb->scsb_i2c_errcnt++;
}
" Reset regs (op=%d, data=%x)\n",
return (DDI_FAILURE);
}
scsb->scsb_i2c_errcnt = 0;
/* now read back and update our scsb structure */
i2cxferp)) == 0) {
scsb->scsb_i2c_errcnt = 0;
} else {
scsb->scsb_i2c_errcnt++;
}
" reading Reset regs (post reset)\n",
return (DDI_FAILURE);
}
/* XXX: P1.5 */
#ifdef DEBUG
if (alarm_card)
#endif
#ifdef DEBUG
if (alarm_card) {
if (ac_val)
"Alarm_RST#.\n");
}
#endif
}
else
"reg=%x\n", pslotnum,
#ifdef DEBUG
if (alarm_card) {
if (!ac_val)
"Alarm_RST#.\n");
}
#endif
}
}
return (error);
}
int
{
int slot_flag = 0;
/*
* If Power needs to be handled, it should be done here.
* Since there is no power handling for now, lets disable
* reset, wait for healthy to come on and then call it
* connected.
* If HLTHY# does not come on (in how long is the question)
* then we stay disconnected.
*/
/*
* P1.5 doesnt require polling healthy as we get an
* interrupt. So we could just update our state as disconnected
* and return waiting for the healthy# interrupt. To make it
* more efficient, lets poll for healthy# a short while since we are
* in the interrupt context anyway. If we dont get a healthy# we
* return, and then wait for the interrupt. Probably the warning
* message needs to be removed then. Need a PROM check flag here.
*/
if (scsb_read_bhealthy(scsb) != 0)
return (DDI_FAILURE);
if (val) {
break;
}
count++;
}
if (scsb_debug & 0x00004000)
}
}
int
{
int slot_flag = 0;
/* Reset is must at extraction. Move on even if failure. */
/*
* If board is still in slot, which means there is a manual
* disconnection in progress, return failure.
* Otherwise, a board was removed anyway; so we need to
* update the status and move on.
*/
return (DDI_FAILURE);
}
/*
* the following bug needs to be fixed.
* When this function is called from scsb_intr, scsb_state already
* clears the 'AC card present' bit.
* However, hsc module doesn't depend on slot_flag during removal.
*/
}
static int
{
}
/*
* Invoked both by the hsc and the scsb module to exchanges necessary
* information regarding the alarm card.
* scsb calls this function to unconfigure the alarm card while the
* hsc calls this function at different times to check busy status,
* and during post hotswap insert operation so that the user process
* if one waiting can configure the alarm card.
*/
int
{
"scsb: HSC not initialized or AC not present!");
return (rc);
}
switch (op) {
/* hsc -> scsb */
case SCSB_HSC_AC_BUSY:
break;
/* API -> scsb */
/*
* NOTE: this could be called multiple times from envmond if
* the daemon is reinitialized with SIGHUP, or stopped and
* restarted.
*/
case SCSB_HSC_AC_SET_BUSY:
DEBUG0("AC SET BUSY\n");
if (scsb_debug & 0x00010000) {
"scsb_hsc_ac_op(SCSB_HSC_AC_SET_BUSY)");
}
break;
/* hsc -> scsb */
case SCSB_HSC_AC_CONFIGURED:
DEBUG0("AC configured\n");
if (scsb_debug & 0x00010000) {
"scsb_hsc_ac_op(SCSB_HSC_AC_CONFIGURED)");
}
/*
* wakeup anyone waiting on AC to be configured
* Send the ALARM_CARD_CONFIGURE Event to all scsb
* open streams.
*/
&event_code, "scsb_hsc_ac_op");
break;
/* hsc -> scsb */
DEBUG0("AC removal alert\n");
if (scsb_debug & 0x00010000) {
"scsb_hsc_ac_op(SCSB_HSC_AC_REMOVAL_ALERT)");
}
/*
* Inform (envmond)alarmcard.so that it should save
* the AC configuration, stop the
* heartbeat, and shutdown the RSC link.
*/
&event_code, "scsb_hsc_ac_op");
break;
/* API -> scsb -> hsc */
case SCSB_HSC_AC_UNCONFIGURE:
DEBUG0("AC unconfigure\n");
if (scsb_debug & 0x00010000) {
"scsb_hsc_ac_op(SCSB_HSC_AC_UNCONFIG"
"URE), AC NOT BUSY");
}
/*
* send notification back to HSC to
* unconfigure the AC, now that the env monitor
* has given permission to do so.
*/
break;
default:
break;
}
return (rc);
}
static void
{
/*
* The interrupt source register can have the healthy
* bit set for non-existing slot, e.g slot 7 on Tonga.
* It can also be seen on the Tonga CPU slot. So we make
* sure we have a valid slot before proceeding.
*/
if (scsb_debug & 0x08000000)
" slot %d", pslotnum);
return;
}
} else {
pslotnum == SC_MC_CTC_SLOT)) {
if (scsb_debug & 0x08000000)
" slot %d", pslotnum);
return;
}
}
/*
* The board healthy registers are already read before entering
* this routine
*/
/*
* P1.5. Following works since slots 1 through 8 are in the same reg
*/
if (val)
}
/*
* This function will try to read from scsb irrespective of whether
* SSB is present or SCB is frozen, to get the health kstat information.
*/
static int
{
if (scsb_debug & 0x0800) {
}
if (i2c_alloc) {
if (scsb_debug & 0x0042)
"i2ctx allocation failure");
return (ENOMEM);
}
} else {
}
switch (op) {
case I2C_WR:
rlen = 0;
for (i = 0; i < len; ++i) {
if (scsb_debug & 0x0080)
"scsb_rdwr_register: writing rwbuf[%d]=0x%x",
i, rwbuf[i]);
}
break;
case I2C_WR_RD:
break;
default:
if (i2c_alloc)
return (EINVAL);
}
/* select the register address */
} else if (rlen) {
/* copy to rwbuf[] */
for (i = 0; i < len; ++i) {
if (scsb_debug & 0x0080)
"scsb_rdwr_register: read rwbuf[%d]=0x%x",
i, rwbuf[i]);
}
}
if (i2c_alloc)
if (error) {
scsb->scsb_i2c_errcnt++;
} else {
scsb->scsb_i2c_errcnt = 0;
}
return (error);
}
/*
* This function will quiesce the PSM_INT line by masking the
* global PSM_INT and writing 1 to SCB_INIT ( for P1.5 and later )
* This effectively translates to writing 0x20 to 0xE1 register.
*/
static int
{
register int i;
/*
* For P1.5, set the SCB_INIT bit in the System Command register,
* and disable global PSM_INT. Before this we need to read the
* interrupt source register corresponding to INIT_SCB and
* clear if set.
*/
if (IS_SCB_P15) {
/*
* Read INTSRC6 and write back 0x20 in case INIT_SCB is set
*/
/*
* Now mask the global PSM_INT and write INIT_SCB in case
* this is an INIT_SCB interrupt
*/
reg = SCSB_REG_ADDR(i);
&wdata, 0);
/*
* There is an SCB_INIT interrupt, which we must clear
* first to keep SCB_INIT from keeping PSM_INT asserted.
*/
1, &clr_bits, 0);
}
if (error) {
if (scsb_debug & 0x0006) {
" failed to set SCB_INIT");
}
}
} else { /* P1.0 or earlier */
/*
* read the interrupt source registers, and then
* write them back.
*/
/* read the interrupt register from scsb */
SCTRL_INTR_NUMREGS, scb_intr_regs, 0)) {
" Failed read of interrupt registers.");
}
/*
* Write to the interrupt source registers to stop scsb
* from interrupting.
*/
SCTRL_INTR_NUMREGS, scb_intr_regs, 0)) {
" registers.");
}
}
if (error)
return (DDI_FAILURE);
else
return (DDI_SUCCESS);
}
/*
* Enables or disables the global PSM_INT interrupt for P1.5, depending
* on the flag, flag = 0 => disable, else enable.
*/
static int
{
int i;
} else {
}
reg = SCSB_REG_ADDR(i);
return (DDI_FAILURE);
}
if (enable == 0) {
} else {
}
return (DDI_SUCCESS);
}
/*
* This routine is to be used by all the drivers using this i2c bus
* to synchronize their transfer operations.
*/
int
{
/*
* If scsb interrupt mutex is initialized, also hold the
* interrupt mutex to let the i2c_transfer() to complete
*/
if (initmux & MUTEX_INIT) {
}
if (initmux & MUTEX_INIT) {
}
return (retval);
}