summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/uts/common/io/bscbus.c2641
-rw-r--r--usr/src/uts/common/io/bscv.c6408
-rw-r--r--usr/src/uts/common/sys/Makefile2
-rw-r--r--usr/src/uts/common/sys/bscbus.h63
-rw-r--r--usr/src/uts/common/sys/bscv_impl.h369
-rw-r--r--usr/src/uts/intel/Makefile.files2
-rw-r--r--usr/src/uts/intel/Makefile.intel.shared4
-rw-r--r--usr/src/uts/intel/bscbus/Makefile93
-rw-r--r--usr/src/uts/intel/bscv/Makefile94
-rw-r--r--usr/src/uts/intel/io/bscbus.conf31
-rw-r--r--usr/src/uts/intel/io/bscv.conf38
-rw-r--r--usr/src/uts/sun4u/Makefile.files7
-rw-r--r--usr/src/uts/sun4u/Makefile.sun4u.shared8
-rw-r--r--usr/src/uts/sun4u/blade/Makefile.blade.shared6
-rw-r--r--usr/src/uts/sun4u/blade/Makefile.files9
-rw-r--r--usr/src/uts/sun4u/blade/bscbus/Makefile100
-rw-r--r--usr/src/uts/sun4u/blade/bscv/Makefile94
-rw-r--r--usr/src/uts/sun4u/blade/io/bscbus.conf31
-rw-r--r--usr/src/uts/sun4u/blade/io/bscv.conf33
-rw-r--r--usr/src/uts/sun4u/gptwo_cpu/Makefile115
-rw-r--r--usr/src/uts/sun4u/gptwocfg/Makefile95
-rw-r--r--usr/src/uts/sun4u/i2bsc/Makefile112
-rw-r--r--usr/src/uts/sun4u/io/gptwo_cpu.c1023
-rw-r--r--usr/src/uts/sun4u/io/gptwocfg.c683
-rw-r--r--usr/src/uts/sun4u/io/i2c/nexus/i2bsc.c1257
-rw-r--r--usr/src/uts/sun4u/io/i2c/nexus/i2bsc.conf31
-rw-r--r--usr/src/uts/sun4u/io/todm5819p_rmc.c409
-rw-r--r--usr/src/uts/sun4u/io/todstarcat.c263
-rw-r--r--usr/src/uts/sun4u/todm5819p_rmc/Makefile89
-rw-r--r--usr/src/uts/sun4u/todstarcat/Makefile88
30 files changed, 14184 insertions, 14 deletions
diff --git a/usr/src/uts/common/io/bscbus.c b/usr/src/uts/common/io/bscbus.c
new file mode 100644
index 0000000000..2a69eae390
--- /dev/null
+++ b/usr/src/uts/common/io/bscbus.c
@@ -0,0 +1,2641 @@
+/*
+ * 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.
+ *
+ * The "bscbus" driver provides access to the LOMlite2 virtual registers,
+ * so that its clients (children) need not be concerned with the details
+ * of the access mechanism, which in this case is implemented via a
+ * packet-based protocol over a Xbus (similar to ebus) parallel link to the
+ * H8 host interface registers.
+ *
+ * On the other hand, this driver doesn't generally know what the virtual
+ * registers signify - only the clients need this information.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/note.h>
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/debug.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+
+#if defined(__sparc)
+#include <sys/intr.h>
+#include <sys/membar.h>
+#endif
+
+#include <sys/kmem.h>
+#include <sys/modctl.h>
+#include <sys/note.h>
+#include <sys/open.h>
+#include <sys/poll.h>
+#include <sys/spl.h>
+#include <sys/stat.h>
+#include <sys/strlog.h>
+#include <sys/atomic.h>
+
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+
+#include <sys/bscbus.h>
+
+#if defined(NDI_ACC_HDL_V2)
+
+/*
+ * Compiling for Solaris 10+ with access handle enhancements
+ */
+#define HANDLE_TYPE ndi_acc_handle_t
+#define HANDLE_ADDR(hdlp) (hdlp->ah_addr)
+#define HANDLE_FAULT(hdlp) (hdlp->ah_fault)
+#define HANDLE_MAPLEN(hdlp) (hdlp->ah_len)
+#define HANDLE_PRIVATE(hdlp) (hdlp->ah_bus_private)
+
+#else
+
+/*
+ * Compatibility definitions for backport to Solaris 8/9
+ */
+#define HANDLE_TYPE ddi_acc_impl_t
+#define HANDLE_ADDR(hdlp) (hdlp->ahi_common.ah_addr)
+#define HANDLE_FAULT(hdlp) (hdlp->ahi_fault)
+#define HANDLE_MAPLEN(hdlp) (hdlp->ahi_common.ah_len)
+#define HANDLE_PRIVATE(hdlp) (hdlp->ahi_common.ah_bus_private)
+
+#define ddi_driver_major(dip) ddi_name_to_major(ddi_binding_name(dip))
+
+#endif /* NDI_ACC_HDL_V2 */
+
+
+/*
+ * Local definitions
+ */
+#define MYNAME "bscbus"
+#define NOMAJOR (~(major_t)0)
+#define DUMMY_VALUE (~(int8_t)0)
+
+#define BSCBUS_INST_TO_MINOR(i) (i)
+#define BSCBUS_MINOR_TO_INST(m) (m)
+
+#define BSCBUS_MAX_CHANNELS (4)
+
+#define BSCBUS_DUMMY_ADDRESS ((caddr_t)0x0CADD1ED)
+#define ADDR_TO_OFFSET(a, hdlp) ((caddr_t)(a) - HANDLE_ADDR(hdlp))
+#define ADDR_TO_VREG(a) ((caddr_t)(a) - BSCBUS_DUMMY_ADDRESS)
+#define VREG_TO_ADDR(v) (BSCBUS_DUMMY_ADDRESS + (v))
+
+#ifdef DEBUG
+#define BSCBUS_LOGSTATUS
+#endif /* DEBUG */
+
+#ifdef BSCBUS_LOGSTATUS
+/*
+ * BSC command logging routines.
+ * Record the data passing to and from the BSC
+ */
+
+typedef enum {
+ BSC_CMD_BUSY = 1, /* bsc reports busy */
+ BSC_CMD_CLEARING = 2, /* clearing bsc busy */
+ BSC_CMD_CLEARED = 3, /* cleared bsc busy */
+ BSC_CMD_SENDING = 4, /* sending next byte */
+ BSC_CMD_SENT = 5, /* sending last byte */
+ BSC_CMD_PENDING = 6, /* got sent byte ack */
+ BSC_CMD_REPLY = 7, /* got reply byte */
+ BSC_CMD_COMPLETE = 8, /* command complete */
+ BSC_CMD_ERROR_SEQ = 9, /* error status */
+ BSC_CMD_ERROR_STATUS = 10, /* error status */
+ BSC_CMD_ERROR_OFLOW = 11, /* error status */
+ BSC_CMD_ERROR_TOUT = 12, /* error status */
+
+ BSC_CMD_PROCESS = 13, /* async intr */
+ BSC_CMD_V1INTR = 14, /* v1 intr */
+ BSC_CMD_V1INTRUNCL = 15, /* v1 intr unclaim */
+ BSC_CMD_DOGPAT = 17 /* watchdog pat */
+} bsc_cmd_stamp_t;
+
+typedef struct {
+ hrtime_t bcl_now;
+ int bcl_seq;
+ bsc_cmd_stamp_t bcl_cat;
+ uint8_t bcl_chno;
+ uint8_t bcl_cmdstate;
+ uint8_t bcl_status;
+ uint8_t bcl_data;
+} bsc_cmd_log_t;
+
+uint32_t bscbus_cmd_log_size = 1024;
+
+uint32_t bscbus_cmd_log_flags = 0xffffffff;
+
+#endif /* BSCBUS_LOGSTATUS */
+
+/*
+ * The following definitions are taken from the Hardware Manual for
+ * the Hitachi H8S/2148 in conjunction with the hardware specification
+ * for the Stiletto blade.
+ *
+ * Each instance of the host interface has 3 registers on the H8:
+ * IDRn - Input Data Register - write-only for Solaris.
+ * writes to this can be done via two
+ * addresses - control and data.
+ * The H8 can determine which address was
+ * written by examining the C/D bit in
+ * the status register.
+ * ODRn - Output Data Register - read-only for Solaris.
+ * A read has the side effect of acknowledging
+ * interrupts.
+ * STRn - Status Register - read-only for Solaris.
+ *
+ *
+ *
+ * In terms of host access to this the Input and Output data registers are
+ * mapped at the same address.
+ */
+#define H8_IDRD 0
+#define H8_IDRC 1
+#define H8_ODR 0
+#define H8_STR 1
+
+#define H8_STR_OBF 0x01 /* data available in ODR */
+#define H8_STR_IBF 0x02 /* data for H8 in IDR */
+#define H8_STR_IDRC 0x08 /* last write to IDR was to IDRC */
+ /* 0=data, 1=command */
+#define H8_STR_BUSY 0x04 /* H8 busy processing command */
+#define H8_STR_TOKENPROTOCOL 0x80 /* token-passing protocol */
+
+/*
+ * Packet format ...
+ */
+#define BSCBUS_MASK 0xc0 /* Byte-type bits */
+#define BSCBUS_PARAM 0x00 /* Parameter byte: 0b0xxxxxxx */
+#define BSCBUS_LAST 0x80 /* Last byte of packet */
+#define BSCBUS_CMD 0x80 /* Command byte: 0b10###XWV */
+#define BSCBUS_STATUS 0xc0 /* Status byte: 0b11###AEV */
+
+#define BSCBUS_SEQ 0x38 /* Sequence number bits */
+#define BSCBUS_SEQ_LSB 0x08 /* Sequence number LSB */
+#define BSCBUS_CMD_XADDR 0x04 /* Extended (2-byte) addressing */
+#define BSCBUS_CMD_WRITE 0x02 /* Write command */
+#define BSCBUS_CMD_WMSB 0x01 /* Set MSB on Write */
+#define BSCBUS_CMD_READ 0x01 /* Read command */
+#define BSCBUS_CMD_NOP 0x00 /* NOP command */
+
+#define BSCBUS_STATUS_ASYNC 0x04 /* Asynchronous event pending */
+#define BSCBUS_STATUS_ERR 0x02 /* Error in command processing */
+#define BSCBUS_STATUS_MSB 0x01 /* MSB of Value read */
+
+#define BSCBUS_VREG_LO(x) ((x) & ((1 << 7) - 1))
+#define BSCBUS_VREG_HI(x) ((x) >> 7)
+
+#define BSCBUS_BUFSIZE 8
+
+#define BSCBUS_CHANNEL_TO_OFFSET(chno) ((chno) * 2) /* Register offset */
+
+/*
+ * Time periods, in nanoseconds
+ *
+ * Note that LOMBUS_ONE_SEC and some other time
+ * periods are defined in <sys/lombus.h>
+ */
+#define BSCBUS_CMD_POLL (LOMBUS_ONE_SEC)
+#define BSCBUS_CMD_POLLNOINTS (LOMBUS_ONE_SEC/20)
+#define BSCBUS_HWRESET_POLL (LOMBUS_ONE_SEC/20)
+#define BSCBUS_HWRESET_TIMEOUT (LOMBUS_ONE_SEC*2)
+
+#define BSCBUS_DOG_PAT_POLL_LIMIT (1000)
+#define BSCBUS_DOG_PAT_POLL (1)
+#define BSCBUS_PAT_RETRY_LIMIT 5
+
+/*
+ * Local datatypes
+ */
+enum bscbus_cmdstate {
+ BSCBUS_CMDSTATE_IDLE, /* No transaction in progress */
+ BSCBUS_CMDSTATE_BUSY, /* Setting up command */
+ BSCBUS_CMDSTATE_CLEARING, /* Clearing firmware busy status */
+ BSCBUS_CMDSTATE_SENDING, /* Waiting to send data to f/w */
+ BSCBUS_CMDSTATE_PENDING, /* Waiting for ack from f/w */
+ BSCBUS_CMDSTATE_WAITING, /* Waiting for status from f/w */
+ BSCBUS_CMDSTATE_READY, /* Status received/command done */
+ BSCBUS_CMDSTATE_ERROR /* Command failed with error */
+};
+
+struct bscbus_channel_state {
+ /* Changes to these are protected by the instance ch_mutex mutex */
+ struct bscbus_state *ssp;
+ uint8_t *ch_regs;
+ ddi_acc_handle_t ch_handle; /* per channel access handle */
+ unsigned int chno;
+ unsigned int map_count; /* Number of mappings to channel */
+ boolean_t map_dog; /* channel is mapped for watchdog */
+
+ /*
+ * Flag to indicate that we've incurred a hardware fault on
+ * accesses to the H8; once this is set, we fake all further
+ * accesses in order not to provoke additional bus errors.
+ */
+ boolean_t xio_fault;
+
+ /*
+ * Data protected by the dog_mutex: the watchdog-patting
+ * protocol data (since the dog can be patted from a high-level
+ * cyclic), and the interrupt-enabled flag.
+ */
+ kmutex_t dog_mutex[1];
+ unsigned int pat_retry_count;
+ unsigned int pat_fail_count;
+
+ /*
+ * Serial protocol state data, protected by lo_mutex
+ * (which is initialised using <lo_iblk>)
+ */
+ kmutex_t lo_mutex[1];
+ ddi_iblock_cookie_t lo_iblk;
+ kcondvar_t lo_cv[1];
+ int unclaimed_count;
+
+ volatile enum bscbus_cmdstate cmdstate;
+ clock_t deadline;
+ clock_t poll_hz;
+ boolean_t interrupt_failed;
+ uint8_t cmdbuf[BSCBUS_BUFSIZE];
+ uint8_t *cmdp; /* Points to last tx'd in cmdbuf */
+ uint8_t reply[BSCBUS_BUFSIZE];
+ uint8_t async;
+ uint8_t index;
+ uint8_t result;
+ uint8_t sequence;
+ uint32_t error;
+};
+
+#define BSCBUS_TX_PENDING(csp) ((csp)->cmdp > (csp)->cmdbuf)
+
+/*
+ * This driver's soft-state structure
+ */
+
+struct bscbus_state {
+ /*
+ * Configuration data, set during attach
+ */
+ dev_info_t *dip;
+ major_t majornum;
+ int instance;
+
+ ddi_acc_handle_t h8_handle;
+ uint8_t *h8_regs;
+
+ /*
+ * Parameters derived from .conf properties
+ */
+ uint32_t debug;
+
+ /*
+ * Flag to indicate that we are using per channel
+ * mapping of the register sets and interrupts.
+ * reg set 0 is chan 0
+ * reg set 1 is chan 1 ...
+ *
+ * Interrupts are specified in that order but later
+ * channels may not have interrupts.
+ */
+ boolean_t per_channel_regs;
+
+ /*
+ * channel state data, protected by ch_mutex
+ * channel claim/release requests are protected by this mutex.
+ */
+ kmutex_t ch_mutex[1];
+ struct bscbus_channel_state channel[BSCBUS_MAX_CHANNELS];
+
+#ifdef BSCBUS_LOGSTATUS
+ /*
+ * Command logging buffer for recording transactions with the
+ * BSC. This is useful for debugging failed transactions and other
+ * such funnies.
+ */
+ bsc_cmd_log_t *cmd_log;
+ uint32_t cmd_log_idx;
+ uint32_t cmd_log_size;
+ uint32_t cmd_log_flags;
+#endif /* BSCBUS_LOGSTATUS */
+};
+
+/*
+ * The auxiliary structure attached to each child
+ * (the child's parent-private-data points to this).
+ */
+struct bscbus_child_info {
+ lombus_regspec_t *rsp;
+ int nregs;
+};
+
+#ifdef BSCBUS_LOGSTATUS
+void bscbus_cmd_log(struct bscbus_channel_state *, bsc_cmd_stamp_t,
+ uint8_t, uint8_t);
+#else /* BSCBUS_LOGSTATUS */
+#define bscbus_cmd_log(state, stamp, status, data)
+#endif /* BSCBUS_LOGSTATUS */
+
+
+/*
+ * Local data
+ */
+
+static void *bscbus_statep;
+
+static major_t bscbus_major = NOMAJOR;
+
+static ddi_device_acc_attr_t bscbus_dev_acc_attr[1] = {
+ DDI_DEVICE_ATTR_V0,
+ DDI_STRUCTURE_LE_ACC,
+ DDI_STRICTORDER_ACC
+};
+
+
+/*
+ * General utility routines ...
+ */
+
+#ifdef DEBUG
+static void
+bscbus_trace(struct bscbus_channel_state *csp, char code, const char *caller,
+ const char *fmt, ...)
+{
+ char buf[256];
+ char *p;
+ va_list va;
+
+ if (csp->ssp->debug & (1 << (code-'@'))) {
+ p = buf;
+ (void) snprintf(p, sizeof (buf) - (p - buf),
+ "%s/%s: ", MYNAME, caller);
+ p += strlen(p);
+
+ va_start(va, fmt);
+ (void) vsnprintf(p, sizeof (buf) - (p - buf), fmt, va);
+ va_end(va);
+
+ buf[sizeof (buf) - 1] = '\0';
+ (void) strlog(csp->ssp->majornum, csp->ssp->instance,
+ code, SL_TRACE, buf);
+ }
+}
+#else /* DEBUG */
+#define bscbus_trace
+#endif /* DEBUG */
+
+static struct bscbus_state *
+bscbus_getstate(dev_info_t *dip, int instance, const char *caller)
+{
+ struct bscbus_state *ssp = NULL;
+ dev_info_t *sdip = NULL;
+ major_t dmaj = NOMAJOR;
+
+ if (dip != NULL) {
+ /*
+ * Use the instance number from the <dip>; also,
+ * check that it really corresponds to this driver
+ */
+ instance = ddi_get_instance(dip);
+ dmaj = ddi_driver_major(dip);
+ if (bscbus_major == NOMAJOR && dmaj != NOMAJOR)
+ bscbus_major = dmaj;
+ else if (dmaj != bscbus_major) {
+ cmn_err(CE_WARN,
+ "%s: major number mismatch (%d vs. %d) in %s(),"
+ "probably due to child misconfiguration",
+ MYNAME, bscbus_major, dmaj, caller);
+ instance = -1;
+ }
+ }
+
+ if (instance >= 0)
+ ssp = ddi_get_soft_state(bscbus_statep, instance);
+ if (ssp != NULL) {
+ sdip = ssp->dip;
+ if (dip == NULL && sdip == NULL)
+ ssp = NULL;
+ else if (dip != NULL && sdip != NULL && sdip != dip) {
+ cmn_err(CE_WARN,
+ "%s: devinfo mismatch (%p vs. %p) in %s(), "
+ "probably due to child misconfiguration",
+ MYNAME, (void *)dip, (void *)sdip, caller);
+ ssp = NULL;
+ }
+ }
+
+ return (ssp);
+}
+
+/*
+ * Lowest-level I/O register read/write
+ */
+
+static void
+bscbus_put_reg(struct bscbus_channel_state *csp, uint_t reg, uint8_t val)
+{
+ if (csp->ch_handle != NULL && !csp->xio_fault) {
+ ddi_put8(csp->ch_handle,
+ csp->ch_regs + reg, val);
+ }
+}
+
+static uint8_t
+bscbus_get_reg(struct bscbus_channel_state *csp, uint_t reg)
+{
+ uint8_t val;
+
+ if (csp->ch_handle != NULL && !csp->xio_fault)
+ val = ddi_get8(csp->ch_handle,
+ csp->ch_regs + reg);
+ else
+ val = DUMMY_VALUE;
+
+ return (val);
+}
+
+static void
+bscbus_check_fault_status(struct bscbus_channel_state *csp)
+{
+ csp->xio_fault =
+ ddi_check_acc_handle(csp->ch_handle) != DDI_SUCCESS;
+}
+
+static boolean_t
+bscbus_faulty(struct bscbus_channel_state *csp)
+{
+ if (!csp->xio_fault)
+ bscbus_check_fault_status(csp);
+ return (csp->xio_fault);
+}
+
+/*
+ * Write data into h8 registers
+ */
+static void
+bscbus_pat_dog(struct bscbus_channel_state *csp, uint8_t val)
+{
+ uint8_t status;
+ uint32_t doglimit = BSCBUS_DOG_PAT_POLL_LIMIT;
+
+ bscbus_trace(csp, 'W', "bscbus_pat_dog:", "");
+
+ bscbus_cmd_log(csp, BSC_CMD_DOGPAT, 0, val);
+ status = bscbus_get_reg(csp, H8_STR);
+ while (status & H8_STR_IBF) {
+ if (csp->pat_retry_count > BSCBUS_PAT_RETRY_LIMIT) {
+ /*
+ * Previous attempts to contact BSC have failed.
+ * Do not bother waiting for it to eat previous
+ * data.
+ * Pat anyway just in case the BSC is really alive
+ * and the IBF bit is lying.
+ */
+ bscbus_put_reg(csp, H8_IDRC, val);
+ bscbus_trace(csp, 'W', "bscbus_pat_dog:",
+ "retry count exceeded");
+ return;
+ }
+ if (--doglimit == 0) {
+ /* The BSC is not responding - give up */
+ csp->pat_fail_count++;
+ csp->pat_retry_count++;
+ /* Pat anyway just in case the BSC is really alive */
+ bscbus_put_reg(csp, H8_IDRC, val);
+ bscbus_trace(csp, 'W', "bscbus_pat_dog:",
+ "poll limit exceeded");
+ return;
+ }
+ drv_usecwait(BSCBUS_DOG_PAT_POLL);
+ status = bscbus_get_reg(csp, H8_STR);
+ }
+ bscbus_put_reg(csp, H8_IDRC, val);
+ csp->pat_retry_count = 0;
+}
+
+/*
+ * State diagrams for how bscbus_process works.
+ * BSCBUS_CMDSTATE_IDLE No transaction in progress
+ * BSCBUS_CMDSTATE_BUSY Setting up command
+ * BSCBUS_CMDSTATE_CLEARING Clearing firmware busy status
+ * BSCBUS_CMDSTATE_SENDING Waiting to send data to f/w
+ * BSCBUS_CMDSTATE_PENDING Waiting for ack from f/w
+ * BSCBUS_CMDSTATE_WAITING Waiting for status from f/w
+ * BSCBUS_CMDSTATE_READY Status received/command done
+ * BSCBUS_CMDSTATE_ERROR Command failed with error
+ *
+ * +----------+
+ * | |
+ * | IDLE/BUSY|
+ * | (0/1) | abnormal
+ * +----------+ state
+ * | \ detected
+ * | \------>------+ +----<---+
+ * bsc | | | |
+ * is | V V |
+ * ready| +----------+ |
+ * | | | ^
+ * | | CLEARING | |
+ * | | (2) | |
+ * | +----------+ |
+ * | cleared / | \ | more to clear
+ * | / | \-->--+
+ * | +-------<-------/ V
+ * | | |
+ * V V |timeout
+ * +----------+ timeout |
+ * | |------>---------+--------+
+ * | SENDING | |
+ * | (3) |------<-------+ |
+ * +----------+ | V
+ * sent| \ send ^ack |
+ * last| \ next |received |
+ * | \ +----------+ |
+ * | \ | | |
+ * | \------>| PENDING |-->-+
+ * | | (4) | |
+ * | +----------+ |timeout
+ * | +---<----+ |
+ * | | | |
+ * V V | |
+ * +----------+ | |
+ * | | | |
+ * | WAITING | ^ |
+ * | (5) | | |
+ * +----------+ | |
+ * | | |more | |
+ * | V |required| |
+ * done| | +--->----+ |
+ * | +--->--------------+ +---<---+
+ * | error/timeout | |
+ * V V V
+ * +----------+ +----------+
+ * | | | |
+ * | READY | | ERROR |
+ * | (7) | | (6) |
+ * +----------+ +----------+
+ * | |
+ * V V
+ * | |
+ * +------>---+---<------+
+ * |
+ * |
+ * Back to
+ * Idle
+ */
+
+static void
+bscbus_process_sending(struct bscbus_channel_state *csp, uint8_t status)
+{
+ /*
+ * When we get here we actually expect H8_STR_IBF to
+ * be clear but we check just in case of problems.
+ */
+ ASSERT(BSCBUS_TX_PENDING(csp));
+ if (!(status & H8_STR_IBF)) {
+ bscbus_put_reg(csp, H8_IDRD, *--csp->cmdp);
+ bscbus_trace(csp, 'P', "bscbus_process_sending",
+ "state %d; val $%x",
+ csp->cmdstate, *csp->cmdp);
+ if (!BSCBUS_TX_PENDING(csp)) {
+ bscbus_cmd_log(csp, BSC_CMD_SENT,
+ status, *csp->cmdp);
+ /* No more pending - move to waiting state */
+ bscbus_trace(csp, 'P', "bscbus_process_sending",
+ "moving to waiting");
+ csp->cmdstate = BSCBUS_CMDSTATE_WAITING;
+ /* Extend deadline because time has moved on */
+ csp->deadline = ddi_get_lbolt() +
+ drv_usectohz(LOMBUS_CMD_TIMEOUT/1000);
+ } else {
+ /* Wait for ack of this byte */
+ bscbus_cmd_log(csp, BSC_CMD_SENDING,
+ status, *csp->cmdp);
+ csp->cmdstate = BSCBUS_CMDSTATE_PENDING;
+ bscbus_trace(csp, 'P', "bscbus_process_sending",
+ "moving to pending");
+ }
+ }
+}
+
+static void
+bscbus_process_clearing(struct bscbus_channel_state *csp,
+ uint8_t status, uint8_t data)
+{
+ /*
+ * We only enter this state if H8_STR_BUSY was set when
+ * we started the transaction. We just ignore all received
+ * data until we see OBF set AND BUSY cleared.
+ * It is not good enough to see BUSY clear on its own
+ */
+ if ((status & H8_STR_OBF) && !(status & H8_STR_BUSY)) {
+ bscbus_cmd_log(csp, BSC_CMD_CLEARED, status, data);
+ csp->cmdstate = BSCBUS_CMDSTATE_SENDING;
+ /* Throw away any data received up until now */
+ bscbus_trace(csp, 'P', "bscbus_process_clearing",
+ "busy cleared");
+ /*
+ * Send the next byte immediately.
+ * At this stage we should clear the OBF flag because that
+ * data has been used. IBF is still valid so do not clear that.
+ */
+ status &= ~(H8_STR_OBF);
+ bscbus_process_sending(csp, status);
+ } else {
+ if (status & H8_STR_OBF) {
+ bscbus_cmd_log(csp, BSC_CMD_CLEARING, status, data);
+ }
+ }
+}
+
+static void
+bscbus_process_pending(struct bscbus_channel_state *csp, uint8_t status)
+{
+ /* We are waiting for an acknowledgement of a byte */
+ if (status & H8_STR_OBF) {
+ bscbus_cmd_log(csp, BSC_CMD_PENDING,
+ status, *csp->cmdp);
+ bscbus_trace(csp, 'P', "bscbus_process_pending",
+ "moving to sending");
+ csp->cmdstate = BSCBUS_CMDSTATE_SENDING;
+ /*
+ * Send the next byte immediately.
+ * At this stage we should clear the OBF flag because that
+ * data has been used. IBF is still valid so do not clear that.
+ */
+ status &= ~(H8_STR_OBF);
+ bscbus_process_sending(csp, status);
+ }
+}
+
+static boolean_t
+bscbus_process_waiting(struct bscbus_channel_state *csp,
+ uint8_t status, uint8_t data)
+{
+ uint8_t rcvd = 0;
+ boolean_t ready = B_FALSE;
+ uint8_t tmp;
+
+ if (status & H8_STR_OBF) {
+ csp->reply[rcvd = csp->index] = data;
+ if (++rcvd < BSCBUS_BUFSIZE)
+ csp->index = rcvd;
+
+ bscbus_trace(csp, 'D', "bscbus_process_waiting",
+ "rcvd %d: $%02x $%02x $%02x $%02x $%02x $%02x $%02x $%02x",
+ rcvd,
+ csp->reply[0], csp->reply[1],
+ csp->reply[2], csp->reply[3],
+ csp->reply[4], csp->reply[5],
+ csp->reply[6], csp->reply[7]);
+ }
+
+ if (rcvd == 0) {
+ /*
+ * No bytes received this time through (though there
+ * might be a partial packet sitting in the buffer).
+ */
+ /* EMPTY */
+ ;
+ } else if (rcvd >= BSCBUS_BUFSIZE) {
+ /*
+ * Buffer overflow; discard the data & treat as an error
+ * (even if the last byte read did claim to terminate a
+ * packet, it can't be a valid one 'cos it's too long!)
+ */
+ bscbus_cmd_log(csp, BSC_CMD_ERROR_OFLOW, status, data);
+ csp->index = 0;
+ csp->cmdstate = BSCBUS_CMDSTATE_ERROR;
+ csp->error = LOMBUS_ERR_OFLOW;
+ ready = B_TRUE;
+ } else if ((data & BSCBUS_LAST) == 0) {
+ /*
+ * Packet not yet complete; leave the partial packet in
+ * the buffer for later ...
+ */
+ bscbus_cmd_log(csp, BSC_CMD_REPLY, status, data);
+ } else if ((data & BSCBUS_MASK) != BSCBUS_STATUS) {
+ /* Invalid "status" byte - maybe an echo of the command? */
+ bscbus_cmd_log(csp, BSC_CMD_ERROR_STATUS, status, data);
+
+ csp->cmdstate = BSCBUS_CMDSTATE_ERROR;
+ csp->error = LOMBUS_ERR_BADSTATUS;
+ ready = B_TRUE;
+ } else if ((data & BSCBUS_SEQ) != csp->sequence) {
+ /* Wrong sequence number! Flag this as an error */
+ bscbus_cmd_log(csp, BSC_CMD_ERROR_SEQ, status, data);
+
+ csp->cmdstate = BSCBUS_CMDSTATE_ERROR;
+ csp->error = LOMBUS_ERR_SEQUENCE;
+ ready = B_TRUE;
+ } else {
+ /*
+ * Finally, we know that's it's a valid reply to our
+ * last command. Update the ASYNC status, derive the
+ * reply parameter (if any), and check the ERROR bit
+ * to find out what the parameter means.
+ *
+ * Note that not all the values read/assigned here
+ * are meaningful, but it doesn't matter; the waiting
+ * thread will know which one(s) it should check.
+ */
+ bscbus_cmd_log(csp, BSC_CMD_COMPLETE, status, data);
+ csp->async = (data & BSCBUS_STATUS_ASYNC) ? 1 : 0;
+
+ tmp = ((data & BSCBUS_STATUS_MSB) ? 0x80 : 0) | csp->reply[0];
+ if (data & BSCBUS_STATUS_ERR) {
+ csp->cmdstate = BSCBUS_CMDSTATE_ERROR;
+ csp->error = tmp;
+ } else {
+ csp->cmdstate = BSCBUS_CMDSTATE_READY;
+ csp->result = tmp;
+ }
+ ready = B_TRUE;
+ }
+ return (ready);
+}
+
+/*
+ * Packet receive handler
+ *
+ * This routine should be called from the low-level softint,
+ * or bscbus_cmd() (for polled operation), with the
+ * low-level mutex already held.
+ */
+static void
+bscbus_process(struct bscbus_channel_state *csp,
+ uint8_t status, uint8_t data)
+{
+ boolean_t ready = B_FALSE;
+
+ ASSERT(mutex_owned(csp->lo_mutex));
+
+ if ((status & H8_STR_OBF) || (status & H8_STR_IBF)) {
+ bscbus_trace(csp, 'D', "bscbus_process",
+ "state %d; error $%x",
+ csp->cmdstate, csp->error);
+ }
+
+ switch (csp->cmdstate) {
+ case BSCBUS_CMDSTATE_CLEARING:
+ bscbus_process_clearing(csp, status, data);
+ break;
+ case BSCBUS_CMDSTATE_SENDING:
+ bscbus_process_sending(csp, status);
+ break;
+ case BSCBUS_CMDSTATE_PENDING:
+ bscbus_process_pending(csp, status);
+ break;
+ case BSCBUS_CMDSTATE_WAITING:
+ ready = bscbus_process_waiting(csp, status, data);
+ break;
+ default:
+ /* Nothing to do */
+ break;
+ }
+
+ /*
+ * Check for timeouts - but only if the command has not yet
+ * completed (ready is true when command completes in this
+ * call to bscbus_process OR cmdstate is READY or ERROR if
+ * this is a spurious call to bscbus_process i.e. a spurious
+ * interrupt)
+ */
+ if (!ready &&
+ ((ddi_get_lbolt() - csp->deadline) > 0) &&
+ csp->cmdstate != BSCBUS_CMDSTATE_READY &&
+ csp->cmdstate != BSCBUS_CMDSTATE_ERROR) {
+ bscbus_trace(csp, 'P', "bscbus_process",
+ "timeout previous state %d; error $%x",
+ csp->cmdstate, csp->error);
+ bscbus_cmd_log(csp, BSC_CMD_ERROR_TOUT, status, data);
+ if (csp->cmdstate == BSCBUS_CMDSTATE_CLEARING) {
+ /* Move onto sending because busy might be stuck */
+ csp->cmdstate = BSCBUS_CMDSTATE_SENDING;
+ /* Extend timeout relative to original start time */
+ csp->deadline += drv_usectohz(LOMBUS_CMD_TIMEOUT/1000);
+ } else if (csp->cmdstate != BSCBUS_CMDSTATE_IDLE) {
+ csp->cmdstate = BSCBUS_CMDSTATE_ERROR;
+ csp->error = LOMBUS_ERR_TIMEOUT;
+ }
+ ready = B_TRUE;
+ }
+
+ if ((status & H8_STR_OBF) || (status & H8_STR_IBF) || ready) {
+ bscbus_trace(csp, 'D', "bscbus_process",
+ "last $%02x; state %d; error $%x; ready %d",
+ data, csp->cmdstate, csp->error, ready);
+ }
+ if (ready)
+ cv_broadcast(csp->lo_cv);
+}
+
+static uint_t
+bscbus_hwintr(caddr_t arg)
+{
+ struct bscbus_channel_state *csp = (void *)arg;
+
+ uint8_t status;
+ uint8_t data = 0xb0 /* Dummy value */;
+
+ mutex_enter(csp->lo_mutex);
+ /*
+ * Read the registers to ensure that the interrupt is cleared.
+ * Status must be read first because reading data changes the
+ * status.
+ * We always read the data because that clears the interrupt down.
+ * This is horrible hardware semantics but we have to do it!
+ */
+ status = bscbus_get_reg(csp, H8_STR);
+ data = bscbus_get_reg(csp, H8_ODR);
+ if (!(status & H8_STR_OBF)) {
+ bscbus_cmd_log(csp, BSC_CMD_V1INTRUNCL, status, data);
+ csp->unclaimed_count++;
+ } else {
+ bscbus_cmd_log(csp, BSC_CMD_V1INTR, status, data);
+ }
+ if (status & H8_STR_TOKENPROTOCOL) {
+ bscbus_process(csp, status, data);
+ if (csp->interrupt_failed) {
+ bscbus_trace(csp, 'I', "bscbus_hwintr:",
+ "interrupt fault cleared channel %d", csp->chno);
+ csp->interrupt_failed = B_FALSE;
+ csp->poll_hz = drv_usectohz(BSCBUS_CMD_POLL / 1000);
+ }
+ }
+
+ mutex_exit(csp->lo_mutex);
+ return (DDI_INTR_CLAIMED);
+}
+
+void
+bscbus_poll(struct bscbus_channel_state *csp)
+{
+ /*
+ * This routine is only called if we timeout in userland
+ * waiting for an interrupt. This generally means that we have
+ * lost interrupt capabilities or that something has gone
+ * wrong. In this case we are allowed to access the hardware
+ * and read the data register if necessary.
+ * If interrupts return then recovery actions should mend us!
+ */
+ uint8_t status;
+ uint8_t data = 0xfa; /* Dummy value */
+
+ ASSERT(mutex_owned(csp->lo_mutex));
+
+ /* Should look for data to receive */
+ status = bscbus_get_reg(csp, H8_STR);
+ if (status & H8_STR_OBF) {
+ /* There is data available */
+ data = bscbus_get_reg(csp, H8_ODR);
+ bscbus_cmd_log(csp, BSC_CMD_PROCESS, status, data);
+ }
+ bscbus_process(csp, status, data);
+}
+
+/*
+ * Serial protocol
+ *
+ * This routine builds a command and sets it in progress.
+ */
+static uint8_t
+bscbus_cmd(HANDLE_TYPE *hdlp, ptrdiff_t vreg, uint_t val, uint_t cmd)
+{
+ struct bscbus_channel_state *csp;
+ clock_t start;
+ clock_t tick;
+ uint8_t status;
+
+ /*
+ * First of all, wait for the interface to be available.
+ *
+ * NOTE: we blow through all the mutex/cv/state checking and
+ * preempt any command in progress if the system is panicking!
+ */
+ csp = HANDLE_PRIVATE(hdlp);
+ mutex_enter(csp->lo_mutex);
+ while (csp->cmdstate != BSCBUS_CMDSTATE_IDLE && !ddi_in_panic())
+ cv_wait(csp->lo_cv, csp->lo_mutex);
+
+ csp->cmdstate = BSCBUS_CMDSTATE_BUSY;
+ csp->sequence = (csp->sequence + BSCBUS_SEQ_LSB) & BSCBUS_SEQ;
+
+ /*
+ * We have exclusive ownership, so assemble the command (backwards):
+ *
+ * [byte 0] Command: modified by XADDR and/or WMSB bits
+ * [Optional] Parameter: Value to write (low 7 bits)
+ * [Optional] Parameter: Register number (high 7 bits)
+ * [Optional] Parameter: Register number (low 7 bits)
+ */
+ csp->cmdp = &csp->cmdbuf[0];
+ *csp->cmdp++ = BSCBUS_CMD | csp->sequence | cmd;
+ switch (cmd) {
+ case BSCBUS_CMD_WRITE:
+ *csp->cmdp++ = val & 0x7f;
+ if (val >= 0x80)
+ csp->cmdbuf[0] |= BSCBUS_CMD_WMSB;
+ /*FALLTHRU*/
+ case BSCBUS_CMD_READ:
+ if (BSCBUS_VREG_HI(vreg) != 0) {
+ *csp->cmdp++ = BSCBUS_VREG_HI(vreg);
+ csp->cmdbuf[0] |= BSCBUS_CMD_XADDR;
+ }
+ *csp->cmdp++ = BSCBUS_VREG_LO(vreg);
+ /*FALLTHRU*/
+ case BSCBUS_CMD_NOP:
+ break;
+ }
+
+ /*
+ * Check and update the H8 h/w fault status before accessing
+ * the chip registers. If there's a (new or previous) fault,
+ * we'll run through the protocol but won't really touch the
+ * hardware and all commands will timeout. If a previously
+ * discovered fault has now gone away (!), then we can (try to)
+ * proceed with the new command (probably a probe).
+ */
+ bscbus_check_fault_status(csp);
+
+ /*
+ * Prepare for the command (to be processed by the interrupt
+ * handler and/or polling loop below), and wait for a response
+ * or timeout.
+ */
+ start = ddi_get_lbolt();
+ csp->deadline = start + drv_usectohz(LOMBUS_CMD_TIMEOUT/1000);
+ csp->error = 0;
+ csp->index = 0;
+ csp->result = DUMMY_VALUE;
+
+ status = bscbus_get_reg(csp, H8_STR);
+ if (status & H8_STR_BUSY) {
+ bscbus_cmd_log(csp, BSC_CMD_BUSY, status, 0xfd);
+ /*
+ * Must ensure that the busy state has cleared before
+ * sending the command
+ */
+ csp->cmdstate = BSCBUS_CMDSTATE_CLEARING;
+ bscbus_trace(csp, 'P', "bscbus_cmd",
+ "h8 reporting status (%x) busy - clearing", status);
+ } else {
+ /* It is clear to send the command immediately */
+ csp->cmdstate = BSCBUS_CMDSTATE_SENDING;
+ bscbus_trace(csp, 'P', "bscbus_cmd",
+ "sending first byte of command, status %x", status);
+ bscbus_poll(csp);
+ }
+
+ csp->poll_hz = drv_usectohz(
+ (csp->interrupt_failed ?
+ BSCBUS_CMD_POLLNOINTS : BSCBUS_CMD_POLL) / 1000);
+
+ while ((csp->cmdstate != BSCBUS_CMDSTATE_READY) &&
+ (csp->cmdstate != BSCBUS_CMDSTATE_ERROR)) {
+ ASSERT(csp->cmdstate != BSCBUS_CMDSTATE_IDLE);
+
+ tick = ddi_get_lbolt() + csp->poll_hz;
+ if ((cv_timedwait(csp->lo_cv, csp->lo_mutex, tick) == -1) &&
+ csp->cmdstate != BSCBUS_CMDSTATE_READY &&
+ csp->cmdstate != BSCBUS_CMDSTATE_ERROR) {
+ if (!csp->interrupt_failed) {
+ bscbus_trace(csp, 'I', "bscbus_cmd:",
+ "interrupt_failed channel %d", csp->chno);
+ csp->interrupt_failed = B_TRUE;
+ csp->poll_hz = drv_usectohz(
+ BSCBUS_CMD_POLLNOINTS / 1000);
+ }
+ bscbus_poll(csp);
+ }
+ }
+
+ /*
+ * The return value may not be meaningful but retrieve it anyway
+ */
+ val = csp->result;
+ if (bscbus_faulty(csp)) {
+ val = DUMMY_VALUE;
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_SIOHW;
+ } else if (csp->cmdstate != BSCBUS_CMDSTATE_READY) {
+ /*
+ * Some problem here ... transfer the error code from
+ * the per-instance state to the per-handle fault flag.
+ * The error code shouldn't be zero!
+ */
+ if (csp->error != 0)
+ HANDLE_FAULT(hdlp) = csp->error;
+ else
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_BADERRCODE;
+ }
+
+ /*
+ * All done now!
+ */
+ csp->index = 0;
+ csp->cmdstate = BSCBUS_CMDSTATE_IDLE;
+ cv_broadcast(csp->lo_cv);
+ mutex_exit(csp->lo_mutex);
+
+ return (val);
+}
+
+/*
+ * Space 0 - LOM virtual register access
+ * Only 8-bit accesses are supported.
+ */
+static uint8_t
+bscbus_vreg_get8(HANDLE_TYPE *hdlp, uint8_t *addr)
+{
+ ptrdiff_t offset;
+
+ /*
+ * Check the offset that the caller has added to the base address
+ * against the length of the mapping originally requested.
+ */
+ offset = ADDR_TO_OFFSET(addr, hdlp);
+ if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
+ /*
+ * Invalid access - flag a fault and return a dummy value
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
+ return (DUMMY_VALUE);
+ }
+
+ /*
+ * Derive the virtual register number and run the command
+ */
+ return (bscbus_cmd(hdlp, ADDR_TO_VREG(addr), 0, BSCBUS_CMD_READ));
+}
+
+static void
+bscbus_vreg_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val)
+{
+ ptrdiff_t offset;
+
+ /*
+ * Check the offset that the caller has added to the base address
+ * against the length of the mapping originally requested.
+ */
+ offset = ADDR_TO_OFFSET(addr, hdlp);
+ if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
+ /*
+ * Invalid access - flag a fault and return
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
+ return;
+ }
+
+ /*
+ * Derive the virtual register number and run the command
+ */
+ (void) bscbus_cmd(hdlp, ADDR_TO_VREG(addr), val, BSCBUS_CMD_WRITE);
+}
+
+static void
+bscbus_vreg_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
+ uint8_t *dev_addr, size_t repcount, uint_t flags)
+{
+ size_t inc;
+
+ inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
+ for (; repcount--; dev_addr += inc)
+ *host_addr++ = bscbus_vreg_get8(hdlp, dev_addr);
+}
+
+static void
+bscbus_vreg_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
+ uint8_t *dev_addr, size_t repcount, uint_t flags)
+{
+ size_t inc;
+
+ inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
+ for (; repcount--; dev_addr += inc)
+ bscbus_vreg_put8(hdlp, dev_addr, *host_addr++);
+}
+
+
+/*
+ * Space 1 - LOM watchdog pat register access
+ * Only 8-bit accesses are supported.
+ *
+ * Reads have no effect and return 0.
+ *
+ * Multi-byte reads (using ddi_rep_get8(9F)) are a fairly inefficient
+ * way of zeroing the destination area ;-) and still won't pat the dog.
+ *
+ * Multi-byte writes (using ddi_rep_put8(9F)) will almost certainly
+ * only count as a single pat, no matter how many bytes the caller
+ * says to write, as the inter-pat time is VERY long compared with
+ * the time it will take to read the memory source area.
+ */
+
+static uint8_t
+bscbus_pat_get8(HANDLE_TYPE *hdlp, uint8_t *addr)
+{
+ ptrdiff_t offset;
+
+ /*
+ * Check the offset that the caller has added to the base address
+ * against the length of the mapping originally requested.
+ */
+ offset = ADDR_TO_OFFSET(addr, hdlp);
+ if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
+ /*
+ * Invalid access - flag a fault and return a dummy value
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
+ return (DUMMY_VALUE);
+ }
+
+ return (0);
+}
+
+static void
+bscbus_pat_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val)
+{
+ struct bscbus_channel_state *csp;
+ ptrdiff_t offset;
+
+ /*
+ * Check the offset that the caller has added to the base address
+ * against the length of the mapping originally requested.
+ */
+ offset = ADDR_TO_OFFSET(addr, hdlp);
+ if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
+ /*
+ * Invalid access - flag a fault and return
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
+ return;
+ }
+
+ csp = HANDLE_PRIVATE(hdlp);
+ mutex_enter(csp->dog_mutex);
+ bscbus_pat_dog(csp, val);
+ mutex_exit(csp->dog_mutex);
+}
+
+static void
+bscbus_pat_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
+ uint8_t *dev_addr, size_t repcount, uint_t flags)
+{
+ size_t inc;
+
+ inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
+ for (; repcount--; dev_addr += inc)
+ *host_addr++ = bscbus_pat_get8(hdlp, dev_addr);
+}
+
+static void
+bscbus_pat_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
+ uint8_t *dev_addr, size_t repcount, uint_t flags)
+{
+ size_t inc;
+
+ inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
+ for (; repcount--; dev_addr += inc)
+ bscbus_pat_put8(hdlp, dev_addr, *host_addr++);
+}
+
+
+/*
+ * Space 2 - LOM async event flag register access
+ * Only 16-bit accesses are supported.
+ */
+static uint16_t
+bscbus_event_get16(HANDLE_TYPE *hdlp, uint16_t *addr)
+{
+ struct bscbus_channel_state *csp;
+ ptrdiff_t offset;
+
+ /*
+ * Check the offset that the caller has added to the base address
+ * against the length of the mapping orignally requested.
+ */
+ offset = ADDR_TO_OFFSET(addr, hdlp);
+ if (offset < 0 || (offset%2) != 0 || offset >= HANDLE_MAPLEN(hdlp)) {
+ /*
+ * Invalid access - flag a fault and return a dummy value
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
+ return (DUMMY_VALUE);
+ }
+
+ /*
+ * Return the value of the asynchronous-event-pending flag
+ * as passed back by the LOM at the end of the last command.
+ */
+ csp = HANDLE_PRIVATE(hdlp);
+ return (csp->async);
+}
+
+static void
+bscbus_event_put16(HANDLE_TYPE *hdlp, uint16_t *addr, uint16_t val)
+{
+ ptrdiff_t offset;
+
+ _NOTE(ARGUNUSED(val))
+
+ /*
+ * Check the offset that the caller has added to the base address
+ * against the length of the mapping originally requested.
+ */
+ offset = ADDR_TO_OFFSET(addr, hdlp);
+ if (offset < 0 || (offset%2) != 0 || offset >= HANDLE_MAPLEN(hdlp)) {
+ /*
+ * Invalid access - flag a fault and return
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
+ return;
+ }
+
+ /*
+ * The user can't overwrite the asynchronous-event-pending flag!
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_RO;
+}
+
+static void
+bscbus_event_rep_get16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
+ uint16_t *dev_addr, size_t repcount, uint_t flags)
+{
+ size_t inc;
+
+ inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
+ for (; repcount--; dev_addr += inc)
+ *host_addr++ = bscbus_event_get16(hdlp, dev_addr);
+}
+
+static void
+bscbus_event_rep_put16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
+ uint16_t *dev_addr, size_t repcount, uint_t flags)
+{
+ size_t inc;
+
+ inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
+ for (; repcount--; dev_addr += inc)
+ bscbus_event_put16(hdlp, dev_addr, *host_addr++);
+}
+
+
+/*
+ * All spaces - access handle fault information
+ * Only 32-bit accesses are supported.
+ */
+static uint32_t
+bscbus_meta_get32(HANDLE_TYPE *hdlp, uint32_t *addr)
+{
+ struct bscbus_channel_state *csp;
+ ptrdiff_t offset;
+
+ /*
+ * Derive the offset that the caller has added to the base
+ * address originally returned, and use it to determine
+ * which meta-register is to be accessed ...
+ */
+ offset = ADDR_TO_OFFSET(addr, hdlp);
+ switch (offset) {
+ case LOMBUS_FAULT_REG:
+ /*
+ * This meta-register provides a code for the most
+ * recent virtual register access fault, if any.
+ */
+ return (HANDLE_FAULT(hdlp));
+
+ case LOMBUS_PROBE_REG:
+ /*
+ * Reading this meta-register clears any existing fault
+ * (at the virtual, not the hardware access layer), then
+ * runs a NOP command and returns the fault code from that.
+ */
+ HANDLE_FAULT(hdlp) = 0;
+ (void) bscbus_cmd(hdlp, 0, 0, BSCBUS_CMD_NOP);
+ return (HANDLE_FAULT(hdlp));
+
+ case LOMBUS_ASYNC_REG:
+ /*
+ * Obsolescent - but still supported for backwards
+ * compatibility. This is an alias for the newer
+ * LOMBUS_EVENT_REG, but doesn't require a separate
+ * "reg" entry and ddi_regs_map_setup() call.
+ *
+ * It returns the value of the asynchronous-event-pending
+ * flag as passed back by the BSC at the end of the last
+ * completed command.
+ */
+ csp = HANDLE_PRIVATE(hdlp);
+ return (csp->async);
+
+ default:
+ /*
+ * Invalid access - flag a fault and return a dummy value
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
+ return (DUMMY_VALUE);
+ }
+}
+
+static void
+bscbus_meta_put32(HANDLE_TYPE *hdlp, uint32_t *addr, uint32_t val)
+{
+ ptrdiff_t offset;
+
+ /*
+ * Derive the offset that the caller has added to the base
+ * address originally returned, and use it to determine
+ * which meta-register is to be accessed ...
+ */
+ offset = ADDR_TO_OFFSET(addr, hdlp);
+ switch (offset) {
+ case LOMBUS_FAULT_REG:
+ /*
+ * This meta-register contains a code for the most
+ * recent virtual register access fault, if any.
+ * It can be cleared simply by writing 0 to it.
+ */
+ HANDLE_FAULT(hdlp) = val;
+ return;
+
+ case LOMBUS_PROBE_REG:
+ /*
+ * Writing this meta-register clears any existing fault
+ * (at the virtual, not the hardware acess layer), then
+ * runs a NOP command. The caller can check the fault
+ * code later if required.
+ */
+ HANDLE_FAULT(hdlp) = 0;
+ (void) bscbus_cmd(hdlp, 0, 0, BSCBUS_CMD_NOP);
+ return;
+
+ default:
+ /*
+ * Invalid access - flag a fault
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
+ return;
+ }
+}
+
+static void
+bscbus_meta_rep_get32(HANDLE_TYPE *hdlp, uint32_t *host_addr,
+ uint32_t *dev_addr, size_t repcount, uint_t flags)
+{
+ size_t inc;
+
+ inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
+ for (; repcount--; dev_addr += inc)
+ *host_addr++ = bscbus_meta_get32(hdlp, dev_addr);
+}
+
+static void
+bscbus_meta_rep_put32(HANDLE_TYPE *hdlp, uint32_t *host_addr,
+ uint32_t *dev_addr, size_t repcount, uint_t flags)
+{
+ size_t inc;
+
+ inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
+ for (; repcount--; dev_addr += inc)
+ bscbus_meta_put32(hdlp, dev_addr, *host_addr++);
+}
+
+
+/*
+ * Finally, some dummy functions for all unsupported access
+ * space/size/mode combinations ...
+ */
+static uint8_t
+bscbus_no_get8(HANDLE_TYPE *hdlp, uint8_t *addr)
+{
+ _NOTE(ARGUNUSED(addr))
+
+ /*
+ * Invalid access - flag a fault and return a dummy value
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
+ return (DUMMY_VALUE);
+}
+
+static void
+bscbus_no_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val)
+{
+ _NOTE(ARGUNUSED(addr, val))
+
+ /*
+ * Invalid access - flag a fault
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
+}
+
+static void
+bscbus_no_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
+ uint8_t *dev_addr, size_t repcount, uint_t flags)
+{
+ _NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
+
+ /*
+ * Invalid access - flag a fault
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
+}
+
+static void
+bscbus_no_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
+ uint8_t *dev_addr, size_t repcount, uint_t flags)
+{
+ _NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
+
+ /*
+ * Invalid access - flag a fault
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
+}
+
+static uint16_t
+bscbus_no_get16(HANDLE_TYPE *hdlp, uint16_t *addr)
+{
+ _NOTE(ARGUNUSED(addr))
+
+ /*
+ * Invalid access - flag a fault and return a dummy value
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
+ return (DUMMY_VALUE);
+}
+
+static void
+bscbus_no_put16(HANDLE_TYPE *hdlp, uint16_t *addr, uint16_t val)
+{
+ _NOTE(ARGUNUSED(addr, val))
+
+ /*
+ * Invalid access - flag a fault
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
+}
+
+static void
+bscbus_no_rep_get16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
+ uint16_t *dev_addr, size_t repcount, uint_t flags)
+{
+ _NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
+
+ /*
+ * Invalid access - flag a fault
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
+}
+
+static void
+bscbus_no_rep_put16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
+ uint16_t *dev_addr, size_t repcount, uint_t flags)
+{
+ _NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
+
+ /*
+ * Invalid access - flag a fault
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
+}
+
+static uint64_t
+bscbus_no_get64(HANDLE_TYPE *hdlp, uint64_t *addr)
+{
+ _NOTE(ARGUNUSED(addr))
+
+ /*
+ * Invalid access - flag a fault and return a dummy value
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
+ return (DUMMY_VALUE);
+}
+
+static void
+bscbus_no_put64(HANDLE_TYPE *hdlp, uint64_t *addr, uint64_t val)
+{
+ _NOTE(ARGUNUSED(addr, val))
+
+ /*
+ * Invalid access - flag a fault
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
+}
+
+static void
+bscbus_no_rep_get64(HANDLE_TYPE *hdlp, uint64_t *host_addr,
+ uint64_t *dev_addr, size_t repcount, uint_t flags)
+{
+ _NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
+
+ /*
+ * Invalid access - flag a fault
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
+}
+
+static void
+bscbus_no_rep_put64(HANDLE_TYPE *hdlp, uint64_t *host_addr,
+ uint64_t *dev_addr, size_t repcount, uint_t flags)
+{
+ _NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
+
+ /*
+ * Invalid access - flag a fault
+ */
+ HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
+}
+
+static int
+bscbus_acc_fault_check(HANDLE_TYPE *hdlp)
+{
+ return (HANDLE_FAULT(hdlp) != 0);
+}
+
+/*
+ * Hardware setup - ensure that there are no pending transactions and
+ * hence no pending interrupts. We do this be ensuring that the BSC is
+ * not reporting a busy condition and that it does not have any data
+ * pending in its output buffer.
+ * This is important because if we have pending interrupts at attach
+ * time Solaris will hang due to bugs in ddi_get_iblock_cookie.
+ */
+static void
+bscbus_hw_reset(struct bscbus_channel_state *csp)
+{
+ int64_t timeout;
+ uint8_t status;
+
+ if (csp->map_count == 0) {
+ /* No-one using this instance - no need to reset hardware */
+ return;
+ }
+
+ bscbus_trace(csp, 'R', "bscbus_hw_reset",
+ "resetting channel %d", csp->chno);
+
+ status = bscbus_get_reg(csp, H8_STR);
+ if (status & H8_STR_BUSY) {
+ /*
+ * Give the h8 time to complete a reply.
+ * In practice we should never worry about this
+ * because whenever we get here it will have been
+ * long enough for the h8 to complete a reply
+ */
+ bscbus_cmd_log(csp, BSC_CMD_BUSY, status, 0);
+ bscbus_trace(csp, 'R', "bscbus_hw_reset",
+ "h8 reporting status (%x) busy - waiting", status);
+ if (ddi_in_panic()) {
+ drv_usecwait(BSCBUS_HWRESET_POLL/1000);
+ } else {
+ delay(drv_usectohz(BSCBUS_HWRESET_POLL/1000));
+ }
+ }
+ /* Reply should be completed by now. Try to clear busy status */
+ status = bscbus_get_reg(csp, H8_STR);
+ if (status & (H8_STR_BUSY | H8_STR_OBF)) {
+ bscbus_trace(csp, 'R', "bscbus_hw_reset",
+ "clearing busy status for channel %d", csp->chno);
+
+ for (timeout = BSCBUS_HWRESET_TIMEOUT;
+ (timeout > 0);
+ timeout -= BSCBUS_HWRESET_POLL) {
+ if (status & H8_STR_OBF) {
+ (void) bscbus_get_reg(csp, H8_ODR);
+ if (!(status & H8_STR_BUSY)) {
+ /* We are done */
+ break;
+ }
+ }
+ if (ddi_in_panic()) {
+ drv_usecwait(BSCBUS_HWRESET_POLL/1000);
+ } else {
+ delay(drv_usectohz(BSCBUS_HWRESET_POLL/1000));
+ }
+ status = bscbus_get_reg(csp, H8_STR);
+ }
+ if (timeout <= 0) {
+ cmn_err(CE_WARN, "bscbus_hw_reset: timed out "
+ "clearing busy status");
+ }
+ }
+ /*
+ * We read ODR just in case there is a pending interrupt with
+ * no data. This is potentially dangerous because we could get
+ * out of sync due to race conditions BUT at this point the
+ * channel should be idle so it is safe.
+ */
+ (void) bscbus_get_reg(csp, H8_ODR);
+}
+
+/*
+ * Higher-level setup & teardown
+ */
+
+static void
+bscbus_offline(struct bscbus_state *ssp)
+{
+ if (ssp->h8_handle != NULL)
+ ddi_regs_map_free(&ssp->h8_handle);
+ ssp->h8_handle = NULL;
+ ssp->h8_regs = NULL;
+}
+
+static int
+bscbus_online(struct bscbus_state *ssp)
+{
+ ddi_acc_handle_t h;
+ caddr_t p;
+ int nregs;
+ int err;
+
+ ssp->h8_handle = NULL;
+ ssp->h8_regs = (void *)NULL;
+ ssp->per_channel_regs = B_FALSE;
+
+ if (ddi_dev_nregs(ssp->dip, &nregs) != DDI_SUCCESS)
+ nregs = 0;
+
+ switch (nregs) {
+ case 1:
+ /*
+ * regset 0 represents the H8 interface registers
+ */
+ err = ddi_regs_map_setup(ssp->dip, 0, &p, 0, 0,
+ bscbus_dev_acc_attr, &h);
+ if (err != DDI_SUCCESS)
+ return (EIO);
+
+ ssp->h8_handle = h;
+ ssp->h8_regs = (void *)p;
+ break;
+
+ case 0:
+ /*
+ * If no registers are defined, succeed vacuously;
+ * commands will be accepted, but we fake the accesses.
+ */
+ break;
+
+ default:
+ /*
+ * Remember that we are using the new register scheme.
+ * reg set 0 is chan 0
+ * reg set 1 is chan 1 ...
+ * Interrupts are specified in that order but later
+ * channels may not have interrupts.
+ * We map the regs later on a per channel basis.
+ */
+ ssp->per_channel_regs = B_TRUE;
+ break;
+ }
+ return (0);
+}
+
+static int
+bscbus_claim_channel(struct bscbus_channel_state *csp, boolean_t map_dog)
+{
+ int err;
+
+ mutex_enter(csp->ssp->ch_mutex);
+ csp->map_count++;
+ bscbus_trace(csp, 'C', "bscbus_claim_channel",
+ "claim channel for channel %d, count %d",
+ csp->chno, csp->map_count);
+
+ if (csp->map_count == 1) {
+ /* No-one is using this channel - initialise it */
+ bscbus_trace(csp, 'C', "bscbus_claim_channel",
+ "initialise channel %d, count %d",
+ csp->chno, csp->map_count);
+
+ mutex_init(csp->dog_mutex, NULL, MUTEX_DRIVER,
+ (void *)(uintptr_t)__ipltospl(SPL7 - 1));
+ csp->map_dog = map_dog;
+ csp->interrupt_failed = B_FALSE;
+ csp->cmdstate = BSCBUS_CMDSTATE_IDLE;
+ csp->pat_retry_count = 0;
+ csp->pat_fail_count = 0;
+
+ /* Map appropriate register set for this channel */
+ if (csp->ssp->per_channel_regs == B_TRUE) {
+ ddi_acc_handle_t h;
+ caddr_t p;
+
+ err = ddi_regs_map_setup(csp->ssp->dip, csp->chno,
+ &p, 0, 0, bscbus_dev_acc_attr, &h);
+
+ if (err != DDI_SUCCESS) {
+ goto failed1;
+ }
+
+ csp->ch_handle = h;
+ csp->ch_regs = (void *)p;
+
+ bscbus_trace(csp, 'C', "bscbus_claim_channel",
+ "mapped chno=%d ch_handle=%d ch_regs=%p",
+ csp->chno, h, p);
+ } else {
+ /*
+ * if using the old reg property scheme use the
+ * common mapping.
+ */
+ csp->ch_handle = csp->ssp->h8_handle;
+ csp->ch_regs =
+ csp->ssp->h8_regs +
+ BSCBUS_CHANNEL_TO_OFFSET(csp->chno);
+ }
+
+ /* Ensure no interrupts pending prior to getting iblk cookie */
+ bscbus_hw_reset(csp);
+
+ if (csp->map_dog == 1) {
+ /*
+ * we don't want lo_mutex to be initialised
+ * with an iblock cookie if we are the wdog,
+ * because we don't use interrupts.
+ */
+ mutex_init(csp->lo_mutex, NULL,
+ MUTEX_DRIVER, NULL);
+ cv_init(csp->lo_cv, NULL,
+ CV_DRIVER, NULL);
+ csp->unclaimed_count = 0;
+ } else {
+ int ninterrupts;
+
+ /*
+ * check that there is an interrupt for this
+ * this channel. If we fail to setup interrupts we
+ * must unmap the registers and fail.
+ */
+ err = ddi_dev_nintrs(csp->ssp->dip, &ninterrupts);
+
+ if (err != DDI_SUCCESS) {
+ ninterrupts = 0;
+ }
+
+ if (ninterrupts <= csp->chno) {
+ cmn_err(CE_WARN,
+ "no interrupt available for "
+ "bscbus channel %d", csp->chno);
+ goto failed2;
+ }
+
+ if (ddi_intr_hilevel(csp->ssp->dip, csp->chno) != 0) {
+ cmn_err(CE_WARN,
+ "bscbus interrupts are high "
+ "level - channel not usable.");
+ goto failed2;
+ } else {
+ err = ddi_get_iblock_cookie(csp->ssp->dip,
+ csp->chno, &csp->lo_iblk);
+ if (err != DDI_SUCCESS) {
+ goto failed2;
+ }
+
+ mutex_init(csp->lo_mutex, NULL,
+ MUTEX_DRIVER, csp->lo_iblk);
+ cv_init(csp->lo_cv, NULL,
+ CV_DRIVER, NULL);
+ csp->unclaimed_count = 0;
+
+ err = ddi_add_intr(csp->ssp->dip, csp->chno,
+ &csp->lo_iblk, NULL,
+ bscbus_hwintr, (caddr_t)csp);
+ if (err != DDI_SUCCESS) {
+ cv_destroy(csp->lo_cv);
+ mutex_destroy(csp->lo_mutex);
+ goto failed2;
+ }
+ }
+ }
+ /*
+ * The channel is now live and may
+ * receive interrupts
+ */
+ } else if (csp->map_dog != map_dog) {
+ bscbus_trace(csp, 'C', "bscbus_claim_channel",
+ "request conflicts with previous mapping. old %x, new %x.",
+ csp->map_dog, map_dog);
+ goto failed1;
+ }
+ mutex_exit(csp->ssp->ch_mutex);
+ return (1);
+
+failed2:
+ /* unmap regs for failed channel */
+ if (csp->ssp->per_channel_regs == B_TRUE) {
+ ddi_regs_map_free(&csp->ch_handle);
+ }
+ csp->ch_handle = NULL;
+ csp->ch_regs = (void *)NULL;
+failed1:
+ csp->map_count--;
+ mutex_exit(csp->ssp->ch_mutex);
+ return (0);
+}
+
+static void
+bscbus_release_channel(struct bscbus_channel_state *csp)
+{
+ mutex_enter(csp->ssp->ch_mutex);
+ if (csp->map_count == 1) {
+ /* No-one is now using this channel - shutdown channel */
+ bscbus_trace(csp, 'C', "bscbus_release_channel",
+ "shutdown channel %d, count %d",
+ csp->chno, csp->map_count);
+
+ if (csp->map_dog == 0) {
+ ASSERT(!ddi_intr_hilevel(csp->ssp->dip, csp->chno));
+ ddi_remove_intr(csp->ssp->dip, csp->chno,
+ csp->lo_iblk);
+ }
+ cv_destroy(csp->lo_cv);
+ mutex_destroy(csp->lo_mutex);
+ mutex_destroy(csp->dog_mutex);
+ bscbus_hw_reset(csp);
+
+ /* unmap registers if using the new register scheme */
+ if (csp->ssp->per_channel_regs == B_TRUE) {
+ ddi_regs_map_free(&csp->ch_handle);
+ }
+ csp->ch_handle = NULL;
+ csp->ch_regs = (void *)NULL;
+ }
+ csp->map_count--;
+ bscbus_trace(csp, 'C', "bscbus_release_channel",
+ "release channel %d, count %d",
+ csp->chno, csp->map_count);
+ mutex_exit(csp->ssp->ch_mutex);
+}
+
+
+/*
+ * Nexus routines
+ */
+
+#if defined(NDI_ACC_HDL_V2)
+
+static const ndi_acc_fns_t bscbus_vreg_acc_fns = {
+ NDI_ACC_FNS_CURRENT,
+ NDI_ACC_FNS_V1,
+
+ bscbus_vreg_get8,
+ bscbus_vreg_put8,
+ bscbus_vreg_rep_get8,
+ bscbus_vreg_rep_put8,
+
+ bscbus_no_get16,
+ bscbus_no_put16,
+ bscbus_no_rep_get16,
+ bscbus_no_rep_put16,
+
+ bscbus_meta_get32,
+ bscbus_meta_put32,
+ bscbus_meta_rep_get32,
+ bscbus_meta_rep_put32,
+
+ bscbus_no_get64,
+ bscbus_no_put64,
+ bscbus_no_rep_get64,
+ bscbus_no_rep_put64,
+
+ bscbus_acc_fault_check
+};
+
+static const ndi_acc_fns_t bscbus_pat_acc_fns = {
+ NDI_ACC_FNS_CURRENT,
+ NDI_ACC_FNS_V1,
+
+ bscbus_pat_get8,
+ bscbus_pat_put8,
+ bscbus_pat_rep_get8,
+ bscbus_pat_rep_put8,
+
+ bscbus_no_get16,
+ bscbus_no_put16,
+ bscbus_no_rep_get16,
+ bscbus_no_rep_put16,
+
+ bscbus_meta_get32,
+ bscbus_meta_put32,
+ bscbus_meta_rep_get32,
+ bscbus_meta_rep_put32,
+
+ bscbus_no_get64,
+ bscbus_no_put64,
+ bscbus_no_rep_get64,
+ bscbus_no_rep_put64,
+
+ bscbus_acc_fault_check
+};
+
+static const ndi_acc_fns_t bscbus_event_acc_fns = {
+ NDI_ACC_FNS_CURRENT,
+ NDI_ACC_FNS_V1,
+
+ bscbus_no_get8,
+ bscbus_no_put8,
+ bscbus_no_rep_get8,
+ bscbus_no_rep_put8,
+
+ bscbus_event_get16,
+ bscbus_event_put16,
+ bscbus_event_rep_get16,
+ bscbus_event_rep_put16,
+
+ bscbus_meta_get32,
+ bscbus_meta_put32,
+ bscbus_meta_rep_get32,
+ bscbus_meta_rep_put32,
+
+ bscbus_no_get64,
+ bscbus_no_put64,
+ bscbus_no_rep_get64,
+ bscbus_no_rep_put64,
+
+ bscbus_acc_fault_check
+};
+
+static int
+bscbus_map_handle(struct bscbus_channel_state *csp, ddi_map_op_t op,
+ int space, caddr_t vaddr, off_t len,
+ ndi_acc_handle_t *hdlp, caddr_t *addrp)
+{
+ switch (op) {
+ default:
+ return (DDI_ME_UNIMPLEMENTED);
+
+ case DDI_MO_MAP_LOCKED:
+ if (bscbus_claim_channel(csp,
+ (space == LOMBUS_PAT_SPACE)) == 0) {
+ return (DDI_ME_GENERIC);
+ }
+
+ switch (space) {
+ default:
+ return (DDI_ME_REGSPEC_RANGE);
+
+ case LOMBUS_VREG_SPACE:
+ ndi_set_acc_fns(hdlp, &bscbus_vreg_acc_fns);
+ break;
+
+ case LOMBUS_PAT_SPACE:
+ ndi_set_acc_fns(hdlp, &bscbus_pat_acc_fns);
+ break;
+
+ case LOMBUS_EVENT_SPACE:
+ ndi_set_acc_fns(hdlp, &bscbus_event_acc_fns);
+ break;
+ }
+ hdlp->ah_addr = *addrp = vaddr;
+ hdlp->ah_len = len;
+ hdlp->ah_bus_private = csp;
+ return (DDI_SUCCESS);
+
+ case DDI_MO_UNMAP:
+ *addrp = NULL;
+ hdlp->ah_bus_private = NULL;
+ bscbus_release_channel(csp);
+ return (DDI_SUCCESS);
+ }
+}
+
+#else
+
+static int
+bscbus_map_handle(struct bscbus_channel_state *csp, ddi_map_op_t op,
+ int space, caddr_t vaddr, off_t len,
+ ddi_acc_hdl_t *hdlp, caddr_t *addrp)
+{
+ ddi_acc_impl_t *aip = hdlp->ah_platform_private;
+
+ switch (op) {
+ default:
+ return (DDI_ME_UNIMPLEMENTED);
+
+ case DDI_MO_MAP_LOCKED:
+ if (bscbus_claim_channel(csp,
+ (space == LOMBUS_PAT_SPACE)) == 0) {
+ return (DDI_ME_GENERIC);
+ }
+
+ switch (space) {
+ default:
+ return (DDI_ME_REGSPEC_RANGE);
+
+ case LOMBUS_VREG_SPACE:
+ aip->ahi_get8 = bscbus_vreg_get8;
+ aip->ahi_put8 = bscbus_vreg_put8;
+ aip->ahi_rep_get8 = bscbus_vreg_rep_get8;
+ aip->ahi_rep_put8 = bscbus_vreg_rep_put8;
+
+ aip->ahi_get16 = bscbus_no_get16;
+ aip->ahi_put16 = bscbus_no_put16;
+ aip->ahi_rep_get16 = bscbus_no_rep_get16;
+ aip->ahi_rep_put16 = bscbus_no_rep_put16;
+
+ aip->ahi_get32 = bscbus_meta_get32;
+ aip->ahi_put32 = bscbus_meta_put32;
+ aip->ahi_rep_get32 = bscbus_meta_rep_get32;
+ aip->ahi_rep_put32 = bscbus_meta_rep_put32;
+
+ aip->ahi_get64 = bscbus_no_get64;
+ aip->ahi_put64 = bscbus_no_put64;
+ aip->ahi_rep_get64 = bscbus_no_rep_get64;
+ aip->ahi_rep_put64 = bscbus_no_rep_put64;
+
+ aip->ahi_fault_check = bscbus_acc_fault_check;
+ break;
+
+ case LOMBUS_PAT_SPACE:
+ aip->ahi_get8 = bscbus_pat_get8;
+ aip->ahi_put8 = bscbus_pat_put8;
+ aip->ahi_rep_get8 = bscbus_pat_rep_get8;
+ aip->ahi_rep_put8 = bscbus_pat_rep_put8;
+
+ aip->ahi_get16 = bscbus_no_get16;
+ aip->ahi_put16 = bscbus_no_put16;
+ aip->ahi_rep_get16 = bscbus_no_rep_get16;
+ aip->ahi_rep_put16 = bscbus_no_rep_put16;
+
+ aip->ahi_get32 = bscbus_meta_get32;
+ aip->ahi_put32 = bscbus_meta_put32;
+ aip->ahi_rep_get32 = bscbus_meta_rep_get32;
+ aip->ahi_rep_put32 = bscbus_meta_rep_put32;
+
+ aip->ahi_get64 = bscbus_no_get64;
+ aip->ahi_put64 = bscbus_no_put64;
+ aip->ahi_rep_get64 = bscbus_no_rep_get64;
+ aip->ahi_rep_put64 = bscbus_no_rep_put64;
+
+ aip->ahi_fault_check = bscbus_acc_fault_check;
+ break;
+
+ case LOMBUS_EVENT_SPACE:
+ aip->ahi_get8 = bscbus_no_get8;
+ aip->ahi_put8 = bscbus_no_put8;
+ aip->ahi_rep_get8 = bscbus_no_rep_get8;
+ aip->ahi_rep_put8 = bscbus_no_rep_put8;
+
+ aip->ahi_get16 = bscbus_event_get16;
+ aip->ahi_put16 = bscbus_event_put16;
+ aip->ahi_rep_get16 = bscbus_event_rep_get16;
+ aip->ahi_rep_put16 = bscbus_event_rep_put16;
+
+ aip->ahi_get32 = bscbus_meta_get32;
+ aip->ahi_put32 = bscbus_meta_put32;
+ aip->ahi_rep_get32 = bscbus_meta_rep_get32;
+ aip->ahi_rep_put32 = bscbus_meta_rep_put32;
+
+ aip->ahi_get64 = bscbus_no_get64;
+ aip->ahi_put64 = bscbus_no_put64;
+ aip->ahi_rep_get64 = bscbus_no_rep_get64;
+ aip->ahi_rep_put64 = bscbus_no_rep_put64;
+
+ aip->ahi_fault_check = bscbus_acc_fault_check;
+ break;
+ }
+ hdlp->ah_addr = *addrp = vaddr;
+ hdlp->ah_len = len;
+ hdlp->ah_bus_private = csp;
+ return (DDI_SUCCESS);
+
+ case DDI_MO_UNMAP:
+ *addrp = NULL;
+ hdlp->ah_bus_private = NULL;
+ bscbus_release_channel(csp);
+ return (DDI_SUCCESS);
+ }
+}
+
+#endif /* NDI_ACC_HDL_V2 */
+
+static int
+bscbus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
+ off_t off, off_t len, caddr_t *addrp)
+{
+ struct bscbus_child_info *lcip;
+ struct bscbus_state *ssp;
+ lombus_regspec_t *rsp;
+
+ if ((ssp = bscbus_getstate(dip, -1, "bscbus_map")) == NULL)
+ return (DDI_FAILURE); /* this "can't happen" */
+
+ /*
+ * Validate mapping request ...
+ */
+
+ if (mp->map_flags != DDI_MF_KERNEL_MAPPING)
+ return (DDI_ME_UNSUPPORTED);
+ if (mp->map_handlep == NULL)
+ return (DDI_ME_UNSUPPORTED);
+ if (mp->map_type != DDI_MT_RNUMBER)
+ return (DDI_ME_UNIMPLEMENTED);
+ if ((lcip = ddi_get_parent_data(rdip)) == NULL)
+ return (DDI_ME_INVAL);
+ if ((rsp = lcip->rsp) == NULL)
+ return (DDI_ME_INVAL);
+ if (mp->map_obj.rnumber >= lcip->nregs)
+ return (DDI_ME_RNUMBER_RANGE);
+ rsp += mp->map_obj.rnumber;
+ if (off < 0 || off >= rsp->lombus_size)
+ return (DDI_ME_INVAL);
+ if (len == 0)
+ len = rsp->lombus_size-off;
+ if (len < 0)
+ return (DDI_ME_INVAL);
+ if (off+len < 0 || off+len > rsp->lombus_size)
+ return (DDI_ME_INVAL);
+
+ return (bscbus_map_handle(
+ &ssp->channel[
+ LOMBUS_SPACE_TO_CHANNEL(rsp->lombus_space)],
+ mp->map_op,
+ LOMBUS_SPACE_TO_REGSET(rsp->lombus_space),
+ VREG_TO_ADDR(rsp->lombus_base+off), len,
+ mp->map_handlep, addrp));
+}
+
+
+static int
+bscbus_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op,
+ void *arg, void *result)
+{
+ struct bscbus_child_info *lcip;
+ lombus_regspec_t *rsp;
+ dev_info_t *cdip;
+ char addr[32];
+ uint_t nregs;
+ uint_t rnum;
+ int *regs;
+ int limit;
+ int err;
+ int i;
+
+ if (bscbus_getstate(dip, -1, "bscbus_ctlops") == NULL)
+ return (DDI_FAILURE); /* this "can't happen" */
+
+ switch (op) {
+ default:
+ break;
+
+ case DDI_CTLOPS_INITCHILD:
+ /*
+ * First, look up and validate the "reg" property.
+ *
+ * It must be a non-empty integer array containing a set
+ * of triples. Once we've verified that, we can treat it
+ * as an array of type lombus_regspec_t[], which defines
+ * the meaning of the elements of each triple:
+ * + the first element of each triple must be a valid space
+ * + the second and third elements (base, size) of each
+ * triple must define a valid subrange of that space
+ * If it passes all the tests, we save it away for future
+ * reference in the child's parent-private-data field.
+ */
+ cdip = arg;
+ err = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, cdip,
+ DDI_PROP_DONTPASS, "reg", &regs, &nregs);
+ if (err != DDI_PROP_SUCCESS)
+ return (DDI_FAILURE);
+
+ err = (nregs <= 0 || (nregs % LOMBUS_REGSPEC_SIZE) != 0);
+ nregs /= LOMBUS_REGSPEC_SIZE;
+ rsp = (lombus_regspec_t *)regs;
+ for (i = 0; i < nregs && !err; ++i) {
+ switch (LOMBUS_SPACE_TO_REGSET(rsp[i].lombus_space)) {
+ default:
+ limit = 0;
+ err = 1;
+ cmn_err(CE_WARN,
+ "child(%p): unknown reg space %d",
+ (void *)cdip, rsp[i].lombus_space);
+ break;
+
+ case LOMBUS_VREG_SPACE:
+ limit = LOMBUS_MAX_REG+1;
+ break;
+
+ case LOMBUS_PAT_SPACE:
+ limit = LOMBUS_PAT_REG+1;
+ break;
+
+ case LOMBUS_EVENT_SPACE:
+ limit = LOMBUS_EVENT_REG+1;
+ break;
+ }
+
+ err |= (rsp[i].lombus_base < 0);
+ err |= (rsp[i].lombus_base >= limit);
+
+ if (rsp[i].lombus_size == 0)
+ rsp[i].lombus_size = limit-rsp[i].lombus_base;
+
+ err |= (rsp[i].lombus_size < 0);
+ err |= (rsp[i].lombus_base+rsp[i].lombus_size < 0);
+ err |= (rsp[i].lombus_base+rsp[i].lombus_size > limit);
+
+ err |= (rsp[i].lombus_base+rsp[i].lombus_size > limit);
+
+ }
+
+ if (err) {
+ ddi_prop_free(regs);
+ return (DDI_FAILURE);
+ }
+
+ lcip = kmem_zalloc(sizeof (*lcip), KM_SLEEP);
+ lcip->nregs = nregs;
+ lcip->rsp = rsp;
+ ddi_set_parent_data(cdip, lcip);
+
+ (void) snprintf(addr, sizeof (addr),
+ "%x,%x", rsp[0].lombus_space, rsp[0].lombus_base);
+ ddi_set_name_addr(cdip, addr);
+
+ return (DDI_SUCCESS);
+
+ case DDI_CTLOPS_UNINITCHILD:
+ cdip = arg;
+ ddi_set_name_addr(cdip, NULL);
+ lcip = ddi_get_parent_data(cdip);
+ ddi_set_parent_data(cdip, NULL);
+ ddi_prop_free(lcip->rsp);
+ kmem_free(lcip, sizeof (*lcip));
+ return (DDI_SUCCESS);
+
+ case DDI_CTLOPS_REPORTDEV:
+ if (rdip == NULL)
+ return (DDI_FAILURE);
+
+ cmn_err(CE_CONT, "?BSC device: %s@%s, %s#%d\n",
+ ddi_node_name(rdip), ddi_get_name_addr(rdip),
+ ddi_driver_name(dip), ddi_get_instance(dip));
+
+ return (DDI_SUCCESS);
+
+ case DDI_CTLOPS_REGSIZE:
+ if ((lcip = ddi_get_parent_data(rdip)) == NULL)
+ return (DDI_FAILURE);
+ if ((rnum = *(uint_t *)arg) >= lcip->nregs)
+ return (DDI_FAILURE);
+ *(off_t *)result = lcip->rsp[rnum].lombus_size;
+ return (DDI_SUCCESS);
+
+ case DDI_CTLOPS_NREGS:
+ if ((lcip = ddi_get_parent_data(rdip)) == NULL)
+ return (DDI_FAILURE);
+ *(int *)result = lcip->nregs;
+ return (DDI_SUCCESS);
+ }
+
+ return (ddi_ctlops(dip, rdip, op, arg, result));
+}
+
+
+/*
+ * This nexus does not support passing interrupts to leaf drivers, so
+ * all the intrspec-related operations just fail as cleanly as possible.
+ */
+
+/*ARGSUSED*/
+static int
+bscbus_intr_op(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op,
+ ddi_intr_handle_impl_t *hdlp, void *result)
+{
+#if defined(__sparc)
+ return (i_ddi_intr_ops(dip, rdip, op, hdlp, result));
+#else
+ _NOTE(ARGUNUSED(dip, rdip, op, hdlp, result))
+ return (DDI_FAILURE);
+#endif
+}
+
+/*
+ * Clean up on detach or failure of attach
+ */
+static int
+bscbus_unattach(struct bscbus_state *ssp, int instance)
+{
+ int chno;
+
+ if (ssp != NULL) {
+ for (chno = 0; chno < BSCBUS_MAX_CHANNELS; chno++) {
+ ASSERT(ssp->channel[chno].map_count == 0);
+ }
+ bscbus_offline(ssp);
+ ddi_set_driver_private(ssp->dip, NULL);
+ mutex_destroy(ssp->ch_mutex);
+ }
+#ifdef BSCBUS_LOGSTATUS
+ if (ssp->cmd_log_size != 0) {
+ kmem_free(ssp->cmd_log,
+ ssp->cmd_log_size * sizeof (bsc_cmd_log_t));
+ }
+#endif /* BSCBUS_LOGSTATUS */
+
+
+ ddi_soft_state_free(bscbus_statep, instance);
+ return (DDI_FAILURE);
+}
+
+/*
+ * Autoconfiguration routines
+ */
+
+static int
+bscbus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ struct bscbus_state *ssp = NULL;
+ int chno;
+ int instance;
+ int err;
+
+ switch (cmd) {
+ default:
+ return (DDI_FAILURE);
+
+ case DDI_ATTACH:
+ break;
+ }
+
+ /*
+ * Allocate the soft-state structure
+ */
+ instance = ddi_get_instance(dip);
+ if (ddi_soft_state_zalloc(bscbus_statep, instance) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ if ((ssp = bscbus_getstate(dip, instance, "bscbus_attach")) == NULL)
+ return (bscbus_unattach(ssp, instance));
+ ddi_set_driver_private(dip, ssp);
+
+ /*
+ * Initialise devinfo-related fields
+ */
+ ssp->dip = dip;
+ ssp->majornum = ddi_driver_major(dip);
+ ssp->instance = instance;
+
+ /*
+ * Set various options from .conf properties
+ */
+ ssp->debug = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS, "debug", 0);
+
+ mutex_init(ssp->ch_mutex, NULL, MUTEX_DRIVER, NULL);
+
+#ifdef BSCBUS_LOGSTATUS
+ ssp->cmd_log_size = bscbus_cmd_log_size;
+ if (ssp->cmd_log_size != 0) {
+ ssp->cmd_log_idx = 0;
+ ssp->cmd_log =
+ kmem_zalloc(ssp->cmd_log_size *
+ sizeof (bsc_cmd_log_t),
+ KM_SLEEP);
+ }
+#endif /* BSCBUS_LOGSTATUS */
+
+ /*
+ * Online the hardware ...
+ */
+ err = bscbus_online(ssp);
+ if (err != 0)
+ return (bscbus_unattach(ssp, instance));
+
+ for (chno = 0; chno < BSCBUS_MAX_CHANNELS; chno++) {
+ struct bscbus_channel_state *csp = &ssp->channel[chno];
+
+ /*
+ * Initialise state
+ * The hardware/interrupts are setup at map time to
+ * avoid claiming hardware that OBP is using
+ */
+ csp->ssp = ssp;
+ csp->chno = chno;
+ csp->map_count = 0;
+ csp->map_dog = B_FALSE;
+ }
+
+ /*
+ * All done, report success
+ */
+ ddi_report_dev(dip);
+ return (DDI_SUCCESS);
+}
+
+static int
+bscbus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ struct bscbus_state *ssp;
+ int instance;
+
+ switch (cmd) {
+ default:
+ return (DDI_FAILURE);
+
+ case DDI_DETACH:
+ break;
+ }
+
+ instance = ddi_get_instance(dip);
+ if ((ssp = bscbus_getstate(dip, instance, "bscbus_detach")) == NULL)
+ return (DDI_FAILURE); /* this "can't happen" */
+
+ (void) bscbus_unattach(ssp, instance);
+ return (DDI_SUCCESS);
+}
+
+static int
+bscbus_reset(dev_info_t *dip, ddi_reset_cmd_t cmd)
+{
+ struct bscbus_state *ssp;
+ int chno;
+
+ _NOTE(ARGUNUSED(cmd))
+
+ if ((ssp = bscbus_getstate(dip, -1, "bscbus_reset")) == NULL)
+ return (DDI_FAILURE);
+
+ for (chno = 0; chno < BSCBUS_MAX_CHANNELS; chno++) {
+ bscbus_hw_reset(&ssp->channel[chno]);
+ }
+ return (DDI_SUCCESS);
+}
+
+
+/*
+ * System interface structures
+ */
+
+static struct cb_ops bscbus_cb_ops =
+{
+ nodev, /* b/c open */
+ nodev, /* b/c close */
+ nodev, /* b strategy */
+ nodev, /* b print */
+ nodev, /* b dump */
+ nodev, /* c read */
+ nodev, /* c write */
+ nodev, /* c ioctl */
+ nodev, /* c devmap */
+ nodev, /* c mmap */
+ nodev, /* c segmap */
+ nochpoll, /* c poll */
+ ddi_prop_op, /* b/c prop_op */
+ NULL, /* c streamtab */
+ D_MP | D_NEW /* b/c flags */
+};
+
+static struct bus_ops bscbus_bus_ops =
+{
+ BUSO_REV, /* revision */
+ bscbus_map, /* bus_map */
+ 0, /* get_intrspec */
+ 0, /* add_intrspec */
+ 0, /* remove_intrspec */
+ i_ddi_map_fault, /* map_fault */
+ ddi_no_dma_map, /* dma_map */
+ ddi_no_dma_allochdl, /* allocate DMA handle */
+ ddi_no_dma_freehdl, /* free DMA handle */
+ ddi_no_dma_bindhdl, /* bind DMA handle */
+ ddi_no_dma_unbindhdl, /* unbind DMA handle */
+ ddi_no_dma_flush, /* flush DMA */
+ ddi_no_dma_win, /* move DMA window */
+ ddi_no_dma_mctl, /* generic DMA control */
+ bscbus_ctlops, /* generic control */
+ ddi_bus_prop_op, /* prop_op */
+ ndi_busop_get_eventcookie, /* get_eventcookie */
+ ndi_busop_add_eventcall, /* add_eventcall */
+ ndi_busop_remove_eventcall, /* remove_eventcall */
+ ndi_post_event, /* post_event */
+ 0, /* interrupt control */
+ 0, /* bus_config */
+ 0, /* bus_unconfig */
+ 0, /* bus_fm_init */
+ 0, /* bus_fm_fini */
+ 0, /* bus_fm_access_enter */
+ 0, /* bus_fm_access_exit */
+ 0, /* bus_power */
+ bscbus_intr_op /* bus_intr_op */
+};
+
+static struct dev_ops bscbus_dev_ops =
+{
+ DEVO_REV,
+ 0, /* refcount */
+ ddi_no_info, /* getinfo */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ bscbus_attach, /* attach */
+ bscbus_detach, /* detach */
+ bscbus_reset, /* reset */
+ &bscbus_cb_ops, /* driver operations */
+ &bscbus_bus_ops /* bus operations */
+};
+
+static struct modldrv modldrv =
+{
+ &mod_driverops,
+ "bscbus driver, v%I%",
+ &bscbus_dev_ops
+};
+
+static struct modlinkage modlinkage =
+{
+ MODREV_1,
+ {
+ &modldrv,
+ NULL
+ }
+};
+
+
+/*
+ * Dynamic loader interface code
+ */
+
+int
+_init(void)
+{
+ int err;
+
+ err = ddi_soft_state_init(&bscbus_statep,
+ sizeof (struct bscbus_state), 0);
+ if (err == DDI_SUCCESS)
+ if ((err = mod_install(&modlinkage)) != DDI_SUCCESS) {
+ ddi_soft_state_fini(&bscbus_statep);
+ }
+
+ return (err);
+}
+
+int
+_info(struct modinfo *mip)
+{
+ return (mod_info(&modlinkage, mip));
+}
+
+int
+_fini(void)
+{
+ int err;
+
+ if ((err = mod_remove(&modlinkage)) == DDI_SUCCESS) {
+ ddi_soft_state_fini(&bscbus_statep);
+ bscbus_major = NOMAJOR;
+ }
+
+ return (err);
+}
+
+#ifdef BSCBUS_LOGSTATUS
+void bscbus_cmd_log(struct bscbus_channel_state *csp, bsc_cmd_stamp_t cat,
+ uint8_t status, uint8_t data)
+{
+ int idx;
+ bsc_cmd_log_t *logp;
+ struct bscbus_state *ssp;
+
+ if ((csp) == NULL)
+ return;
+ if ((ssp = (csp)->ssp) == NULL)
+ return;
+ if (ssp->cmd_log_size == 0)
+ return;
+ if ((bscbus_cmd_log_flags & (1 << cat)) == 0)
+ return;
+ idx = atomic_add_32_nv(&ssp->cmd_log_idx, 1);
+ logp = &ssp->cmd_log[idx % ssp->cmd_log_size];
+ logp->bcl_seq = idx;
+ logp->bcl_cat = cat;
+ logp->bcl_now = gethrtime();
+ logp->bcl_chno = csp->chno;
+ logp->bcl_cmdstate = csp->cmdstate;
+ logp->bcl_status = status;
+ logp->bcl_data = data;
+}
+#endif /* BSCBUS_LOGSTATUS */
diff --git a/usr/src/uts/common/io/bscv.c b/usr/src/uts/common/io/bscv.c
new file mode 100644
index 0000000000..9353af7323
--- /dev/null
+++ b/usr/src/uts/common/io/bscv.c
@@ -0,0 +1,6408 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * bscv.c - multi-threaded lom driver for the Stiletto platform.
+ */
+
+/*
+ * Included files.
+ */
+
+#include <sys/note.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <sys/open.h>
+#include <sys/cred.h>
+#include <sys/stream.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/cyclic.h>
+#include <sys/reboot.h>
+#include <sys/modctl.h>
+#include <sys/mkdev.h>
+#include <sys/errno.h>
+#include <sys/debug.h>
+#include <sys/kmem.h>
+#include <sys/consdev.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/disp.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/stream.h>
+#include <sys/strlog.h>
+#include <sys/log.h>
+#include <sys/utsname.h>
+#include <sys/callb.h>
+#include <sys/sysevent.h>
+#include <sys/nvpair.h>
+#include <sys/sysevent/eventdefs.h>
+#include <sys/sysevent/domain.h>
+#include <sys/sysevent/env.h>
+#include <sys/sysevent/dr.h>
+
+#include <sys/lom_io.h>
+#include <sys/bscbus.h>
+#include <sys/bscv_impl.h>
+
+/*
+ * Variables defined here and visible internally only
+ */
+
+static void *bscv_statep = NULL;
+
+/*
+ * Forward declarations
+ */
+
+static int bscv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
+static int bscv_attach(dev_info_t *, ddi_attach_cmd_t);
+static int bscv_detach(dev_info_t *, ddi_detach_cmd_t);
+static int bscv_reset(dev_info_t *, ddi_reset_cmd_t);
+static int bscv_map_regs(bscv_soft_state_t *);
+static void bscv_unmap_regs(bscv_soft_state_t *);
+static void bscv_map_chan_logical_physical(bscv_soft_state_t *);
+
+static int bscv_open(dev_t *, int, int, cred_t *);
+static int bscv_close(dev_t, int, int, cred_t *);
+static void bscv_full_stop(bscv_soft_state_t *);
+
+static void bscv_enter(bscv_soft_state_t *);
+static void bscv_exit(bscv_soft_state_t *);
+#ifdef DEBUG
+static int bscv_held(bscv_soft_state_t *);
+#endif /* DEBUG */
+
+static void bscv_put8(bscv_soft_state_t *, int, bscv_addr_t, uint8_t);
+static void bscv_put16(bscv_soft_state_t *, int, bscv_addr_t, uint16_t);
+static void bscv_put32(bscv_soft_state_t *, int, bscv_addr_t, uint32_t);
+static uint8_t bscv_get8(bscv_soft_state_t *, int, bscv_addr_t);
+static uint16_t bscv_get16(bscv_soft_state_t *, int, bscv_addr_t);
+static uint32_t bscv_get32(bscv_soft_state_t *, int, bscv_addr_t);
+static void bscv_setclear8(bscv_soft_state_t *, int,
+ bscv_addr_t, uint8_t, uint8_t);
+static void bscv_setclear8_volatile(bscv_soft_state_t *, int,
+ bscv_addr_t, uint8_t, uint8_t);
+static void bscv_rep_rw8(bscv_soft_state_t *, int,
+ uint8_t *, bscv_addr_t, size_t, uint_t, boolean_t);
+static uint8_t bscv_get8_cached(bscv_soft_state_t *, bscv_addr_t);
+
+static uint8_t bscv_get8_locked(bscv_soft_state_t *, int, bscv_addr_t, int *);
+static void bscv_rep_get8_locked(bscv_soft_state_t *, int,
+ uint8_t *, bscv_addr_t, size_t, uint_t, int *);
+
+static boolean_t bscv_faulty(bscv_soft_state_t *);
+static void bscv_clear_fault(bscv_soft_state_t *);
+static void bscv_set_fault(bscv_soft_state_t *);
+static boolean_t bscv_session_error(bscv_soft_state_t *);
+static int bscv_retcode(bscv_soft_state_t *);
+static int bscv_should_retry(bscv_soft_state_t *);
+static void bscv_locked_result(bscv_soft_state_t *, int *);
+
+static void bscv_put8_once(bscv_soft_state_t *, int, bscv_addr_t, uint8_t);
+static uint8_t bscv_get8_once(bscv_soft_state_t *, int, bscv_addr_t);
+static uint32_t bscv_probe(bscv_soft_state_t *, int, uint32_t *);
+static void bscv_resync_comms(bscv_soft_state_t *, int);
+
+static boolean_t bscv_window_setup(bscv_soft_state_t *);
+static int bscv_eerw(bscv_soft_state_t *, uint32_t, uint8_t *,
+ unsigned, boolean_t);
+
+static int bscv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+static int bscv_ioc_dogstate(bscv_soft_state_t *, intptr_t, int);
+static int bscv_ioc_psustate(bscv_soft_state_t *, intptr_t, int);
+static int bscv_ioc_fanstate(bscv_soft_state_t *, intptr_t, int);
+static int bscv_ioc_fledstate(bscv_soft_state_t *, intptr_t, int);
+static int bscv_ioc_ledstate(bscv_soft_state_t *, intptr_t, int);
+static int bscv_ioc_info(bscv_soft_state_t *, intptr_t, int);
+static int bscv_ioc_mread(bscv_soft_state_t *, intptr_t, int);
+static int bscv_ioc_volts(bscv_soft_state_t *, intptr_t, int);
+static int bscv_ioc_stats(bscv_soft_state_t *, intptr_t, int);
+static int bscv_ioc_temp(bscv_soft_state_t *, intptr_t, int);
+static int bscv_ioc_cons(bscv_soft_state_t *, intptr_t, int);
+static int bscv_ioc_eventlog2(bscv_soft_state_t *, intptr_t, int);
+static int bscv_ioc_info2(bscv_soft_state_t *, intptr_t, int);
+static int bscv_ioc_test(bscv_soft_state_t *, intptr_t, int);
+static int bscv_ioc_mprog2(bscv_soft_state_t *, intptr_t, int);
+static int bscv_ioc_mread2(bscv_soft_state_t *, intptr_t, int);
+
+static void bscv_event_daemon(void *);
+static void bscv_start_event_daemon(bscv_soft_state_t *);
+static int bscv_stop_event_daemon(bscv_soft_state_t *);
+static int bscv_pause_event_daemon(bscv_soft_state_t *);
+static void bscv_resume_event_daemon(bscv_soft_state_t *);
+static void bscv_event_process(bscv_soft_state_t *ssp, boolean_t);
+static int bscv_event_validate(bscv_soft_state_t *, uint32_t, uint8_t);
+static void bscv_event_process_one(bscv_soft_state_t *, lom_event_t *);
+static void bscv_build_eventstring(bscv_soft_state_t *,
+ lom_event_t *, char *, char *);
+static int bscv_level_of_event(lom_event_t *);
+static void bscv_status(bscv_soft_state_t *, uint8_t, uint8_t);
+char *bscv_get_label(char [][MAX_LOM2_NAME_STR], int, int);
+static void bscv_generic_sysevent(bscv_soft_state_t *, char *, char *, char *,
+ char *, int32_t, char *);
+static void bscv_sysevent(bscv_soft_state_t *, lom_event_t *);
+
+static int bscv_prog(bscv_soft_state_t *, intptr_t, int);
+static int bscv_prog_image(bscv_soft_state_t *, boolean_t,
+ uint8_t *, int, uint32_t);
+static int bscv_prog_receive_image(bscv_soft_state_t *, lom_prog_t *,
+ uint8_t *, int);
+static void bscv_leave_programming_mode(bscv_soft_state_t *, boolean_t);
+static int bscv_prog_stop_lom(bscv_soft_state_t *);
+static int bscv_prog_start_lom(bscv_soft_state_t *);
+
+static int bscv_attach_common(bscv_soft_state_t *);
+static int bscv_cleanup(bscv_soft_state_t *);
+static void bscv_setup_capability(bscv_soft_state_t *);
+static int bscv_probe_check(bscv_soft_state_t *);
+static void bscv_setup_hostname(bscv_soft_state_t *);
+static void bscv_read_hostname(bscv_soft_state_t *, char *);
+static void bscv_write_hostname(bscv_soft_state_t *, char *, uint8_t);
+static void bscv_setup_static_info(bscv_soft_state_t *);
+static uint8_t bscv_read_env_name(bscv_soft_state_t *, uint8_t,
+ uint8_t, uint8_t, char [][MAX_LOM2_NAME_STR], int);
+static void bscv_setup_events(bscv_soft_state_t *);
+
+static void bscv_trace(bscv_soft_state_t *, char, const char *,
+ const char *, ...);
+
+#ifdef __sparc
+static void bscv_idi_init();
+static void bscv_idi_fini();
+static void bscv_idi_new_instance(dev_info_t *dip);
+static void bscv_idi_clear_err();
+void bscv_idi_set(struct bscv_idi_info info);
+static boolean_t bscv_idi_err();
+static boolean_t bscv_nodename_set(struct bscv_idi_info info);
+static boolean_t bscv_sig_set(struct bscv_idi_info info);
+static boolean_t bscv_wdog_pat(struct bscv_idi_info info);
+static boolean_t bscv_wdog_cfg(struct bscv_idi_info info);
+static void bscv_write_sig(bscv_soft_state_t *ssp, bscv_sig_t s);
+#endif /* __sparc */
+
+static void bscv_setup_watchdog(bscv_soft_state_t *ssp);
+static void bscv_write_wdog_cfg(bscv_soft_state_t *,
+ uint_t, boolean_t, uint8_t);
+
+#if defined(__i386) || defined(__amd64)
+static void bscv_inform_bsc(bscv_soft_state_t *, uint32_t);
+static void bscv_watchdog_pat_request(void *);
+static void bscv_watchdog_cfg_request(bscv_soft_state_t *, uint8_t);
+static uint_t bscv_set_watchdog_timer(bscv_soft_state_t *, uint_t);
+static void bscv_clear_watchdog_timer(bscv_soft_state_t *);
+
+static boolean_t bscv_panic_callback(void *, int);
+static void bscv_watchdog_cyclic_add(bscv_soft_state_t *);
+static void bscv_watchdog_cyclic_remove(bscv_soft_state_t *);
+
+extern kmutex_t cpu_lock; /* needed for cyclics */
+static uint8_t wdog_reset_on_timeout = 1;
+
+#define WDOG_ON 1
+#define WDOG_OFF 0
+#define CLK_WATCHDOG_DEFAULT 10 /* 10 seconds */
+#define WATCHDOG_PAT_INTERVAL 1000000000 /* 1 second */
+
+static int bscv_watchdog_enable;
+static int bscv_watchdog_available;
+static int watchdog_activated;
+static uint_t bscv_watchdog_timeout_seconds;
+#endif /* __i386 || __amd64 */
+
+#ifdef __sparc
+struct bscv_idi_callout bscv_idi_callout_table[] = {
+ {BSCV_IDI_NODENAME, &bscv_nodename_set },
+ {BSCV_IDI_SIG, &bscv_sig_set },
+ {BSCV_IDI_WDOG_PAT, &bscv_wdog_pat },
+ {BSCV_IDI_WDOG_CFG, &bscv_wdog_cfg },
+ {BSCV_IDI_NULL, NULL }
+};
+
+static struct bscv_idi_callout_mgr bscv_idi_mgr;
+#endif /* __sparc */
+
+/*
+ * Local Definitions
+ */
+#define STATUS_READ_LIMIT 8 /* Read up to 8 status changes at a time */
+#define MYNAME "bscv"
+#define BSCV_INST_TO_MINOR(i) (i)
+#define BSCV_MINOR_TO_INST(m) (m)
+#define ddi_driver_major(dip) ddi_name_to_major(ddi_binding_name(dip))
+
+/*
+ * Strings for daemon event reporting
+ */
+
+static char *eventSubsysStrings[] =
+{ "", /* 00 */
+ "Alarm ", /* 01 */
+ "temperature sensor ", /* 02 */
+ "overheat sensor ", /* 03 */
+ "Fan ", /* 04 */
+ "supply rail ", /* 05 */
+ "circuit breaker ", /* 06 */
+ "PSU ", /* 07 */
+ "user ", /* 08 */
+ "phonehome ", /* 09; unutilized */
+ "LOM ", /* 0a */
+ "host ", /* 0b */
+ "event log ", /* 0c */
+ "", /* 0d; EVENT_SUBSYS_EXTRA unutilized */
+ "LED ", /* 0e */
+};
+
+static char *eventTypeStrings[] =
+{
+ "[null event]", /* 00 */
+ "ON", /* 01 */
+ "OFF", /* 02 */
+ "state change", /* 03 */
+ "power on", /* 04 */
+ "power off", /* 05 */
+ "powered off unexpectedly", /* 06 */
+ "reset unexpectedly", /* 07 */
+ "booted", /* 08 */
+ "watchdog enabled", /* 09 */
+ "watchdog disabled", /* 0a */
+ "watchdog triggered", /* 0b */
+ "failed", /* 0c */
+ "recovered", /* 0d */
+ "reset", /* 0e */
+ "XIR reset", /* 0f */
+ "console selected", /* 10 */
+ "time reference", /* 11 */
+ "script failure", /* 12 */
+ "modem access failure", /* 13 */
+ "modem dialing failure", /* 14 */
+ "bad checksum", /* 15 */
+ "added", /* 16 */
+ "removed", /* 17 */
+ "changed", /* 18 */
+ "login", /* 19 */
+ "password changed", /* 1a */
+ "login failed", /* 1b */
+ "logout", /* 1c */
+ "flash download", /* 1d */
+ "data lost", /* 1e */
+ "device busy", /* 1f */
+ "fault led state", /* 20 */
+ "overheat", /* 21 */
+ "severe overheat", /* 22 */
+ "no overheat", /* 23 */
+ "SCC", /* 24 */
+ "device inaccessible", /* 25 */
+ "Hostname change", /* 26 */
+ "CPU signature timeout", /* 27 */
+ "Bootmode change", /* 28 */
+ "Watchdog change policy", /* 29 */
+ "Watchdog change timeout", /* 2a */
+};
+
+/*
+ * These store to mapping between the logical service, e.g. chan_prog for
+ * programming, and the actual Xbus channel which carries that traffic.
+ * Any services can be shared on the same channel apart from chan_wdogpat.
+ */
+static int chan_general; /* General Traffic */
+static int chan_wdogpat; /* Watchdog Patting */
+static int chan_cpusig; /* CPU signatures */
+static int chan_eeprom; /* EEPROM I/O */
+static int chan_prog; /* Programming */
+
+/*
+ * cb_ops structure defining the driver entry points
+ */
+
+static struct cb_ops bscv_cb_ops = {
+ bscv_open, /* open */
+ bscv_close, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ bscv_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ ddi_prop_op, /* prop op */
+ NULL, /* ! STREAMS */
+ D_NEW | D_MP /* MT/MP Safe */
+};
+
+/*
+ * dev_ops structure defining autoconfiguration driver autoconfiguration
+ * routines
+ */
+
+static struct dev_ops bscv_dev_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* devo_refcnt */
+ bscv_getinfo, /* devo_getinfo */
+ nulldev, /* devo_identify */
+ nulldev, /* devo_probe */
+ bscv_attach, /* devo_attach */
+ bscv_detach, /* devo_detach */
+ bscv_reset, /* devo_reset */
+ &bscv_cb_ops, /* devo_cb_ops */
+ (struct bus_ops *)0 /* devo_bus_ops */
+};
+
+/*
+ * module configuration section
+ */
+
+#ifdef DEBUG
+#define BSCV_VERSION_STRING "bscv driver - Debug v%I%"
+#else /* DEBUG */
+#define BSCV_VERSION_STRING "bscv driver v%I%"
+#endif /* DEBUG */
+
+static struct modldrv modldrv = {
+ &mod_driverops,
+ BSCV_VERSION_STRING,
+ &bscv_dev_ops,
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ &modldrv,
+ NULL
+};
+
+/*
+ * kernel accessible routines. These routines are necessarily global so the
+ * driver can be loaded, and unloaded successfully
+ */
+
+/*
+ * function - _init
+ * description - initializes the driver state structure and installs the
+ * driver module into the kernel
+ * inputs - none
+ * outputs - success or failure of module installation
+ */
+
+int
+_init(void)
+{
+ register int e;
+
+ if ((e = ddi_soft_state_init(&bscv_statep,
+ sizeof (bscv_soft_state_t), 1)) != 0) {
+ return (e);
+ }
+
+ if ((e = mod_install(&modlinkage)) != 0) {
+ ddi_soft_state_fini(&bscv_statep);
+ }
+
+#ifdef __sparc
+ if (e == 0) bscv_idi_init();
+#endif /* __sparc */
+ return (e);
+}
+
+/*
+ * function - _info
+ * description - provide information about a kernel loaded module
+ * inputs - module infomation
+ * outputs - success or failure of information request
+ */
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+/*
+ * function - _fini
+ * description - removes a module from the kernel and frees the driver soft
+ * state memory
+ * inputs - none
+ * outputs - success or failure of module removal
+ */
+
+int
+_fini(void)
+{
+ register int e;
+
+ if ((e = mod_remove(&modlinkage)) != 0) {
+ return (e);
+ }
+
+#ifdef __sparc
+ bscv_idi_fini();
+#endif /* __sparc */
+ ddi_soft_state_fini(&bscv_statep);
+
+ return (e);
+}
+
+/*
+ * function - bscv_getinfo
+ * description - routine used to provide information on the driver
+ * inputs - device information structure, command, command arg, storage
+ * area for the result
+ * outputs - DDI_SUCCESS or DDI_FAILURE
+ */
+
+/*ARGSUSED*/
+static int
+bscv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
+{
+ bscv_soft_state_t *ssp;
+ dev_t dev = (dev_t)arg;
+ int instance;
+ int error;
+
+ instance = DEVICETOINSTANCE(dev);
+
+ switch (cmd) {
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)(uintptr_t)instance;
+ error = DDI_SUCCESS;
+ break;
+
+ case DDI_INFO_DEVT2DEVINFO:
+ ssp = ddi_get_soft_state(bscv_statep, instance);
+ if (ssp == NULL)
+ return (DDI_FAILURE);
+ *result = (void *) ssp->dip;
+ error = DDI_SUCCESS;
+ break;
+
+ default:
+ error = DDI_FAILURE;
+ break;
+ }
+
+ return (error);
+}
+
+#ifdef __sparc
+void
+bscv_idi_init()
+{
+ bscv_idi_mgr.valid_inst = (uint32_t)~0; /* No valid instances */
+ bscv_idi_mgr.tbl = bscv_idi_callout_table;
+ bscv_idi_mgr.errs = 0;
+
+ /*
+ * Now that all fields are initialized, set the magic flag. This is
+ * a kind of integrity check for the data structure.
+ */
+ bscv_idi_mgr.magic = BSCV_IDI_CALLOUT_MAGIC;
+}
+
+static void
+bscv_idi_clear_err()
+{
+ ASSERT(bscv_idi_mgr.magic == BSCV_IDI_CALLOUT_MAGIC);
+
+ bscv_idi_mgr.errs = 0;
+}
+
+/*
+ * function - bscv_idi_err
+ * description - error messaging service which throttles the number of error
+ * messages to avoid overflowing storage
+ * inputs - none
+ * returns - boolean to indicate whether a message should be reported
+ * side-effects - updates the error number counter
+ */
+static boolean_t
+bscv_idi_err()
+{
+ ASSERT(bscv_idi_mgr.magic == BSCV_IDI_CALLOUT_MAGIC);
+
+ bscv_idi_mgr.errs++;
+
+ if (bscv_idi_mgr.errs++ < BSCV_IDI_ERR_MSG_THRESHOLD)
+ return (B_TRUE);
+
+ return (B_FALSE);
+}
+
+void
+bscv_idi_new_instance(dev_info_t *dip)
+{
+ ASSERT(bscv_idi_mgr.magic == BSCV_IDI_CALLOUT_MAGIC);
+
+ /*
+ * We don't care how many instances we have, or their value, so long
+ * as we have at least one valid value. This is so service routines
+ * can get any required locks via a soft state pointer.
+ */
+ if (bscv_idi_mgr.valid_inst == (uint32_t)~0) {
+ bscv_idi_mgr.valid_inst = ddi_get_instance(dip);
+ }
+}
+
+void
+bscv_idi_fini()
+{
+ bscv_idi_mgr.valid_inst = (uint32_t)~0; /* No valid instances */
+ bscv_idi_mgr.tbl = NULL;
+}
+#endif /* __sparc */
+
+/*
+ * function - bscv_attach
+ * description - this routine is responsible for setting aside memory for the
+ * driver data structures, initialising the mutexes and creating
+ * the device minor nodes. Additionally, this routine calls the
+ * the callback routine.
+ * inputs - device information structure, DDI_ATTACH command
+ * outputs - DDI_SUCCESS or DDI_FAILURE
+ */
+
+int
+bscv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ bscv_soft_state_t *ssp;
+ int instance;
+
+ switch (cmd) {
+ case DDI_ATTACH:
+
+ instance = ddi_get_instance(dip);
+
+ if (ddi_soft_state_zalloc(bscv_statep, instance) !=
+ DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+
+
+ ssp = ddi_get_soft_state(bscv_statep, instance);
+
+ ssp->progress = 0;
+
+ ssp->dip = dip;
+ ssp->instance = instance;
+ ssp->event_waiting = B_FALSE;
+ ssp->status_change = B_FALSE;
+ ssp->nodename_change = B_FALSE;
+ ssp->cap0 = 0;
+ ssp->cap1 = 0;
+ ssp->cap2 = 0;
+ ssp->prog_mode_only = B_FALSE;
+ ssp->programming = B_FALSE;
+ ssp->cssp_prog = B_FALSE;
+ ssp->task_flags = 0;
+ ssp->debug = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS, "debug", 0);
+ ssp->majornum = ddi_driver_major(dip);
+ ssp->minornum = BSCV_INST_TO_MINOR(instance);
+#if defined(__i386) || defined(__amd64)
+ ssp->last_nodename[0] = '\0';
+#endif /* __i386 || __amd64 */
+
+ /*
+ * initialise the mutexes
+ */
+
+ mutex_init(&ssp->cmd_mutex, NULL, MUTEX_DRIVER, NULL);
+
+ mutex_init(&ssp->task_mu, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&ssp->task_cv, NULL, CV_DRIVER, NULL);
+ cv_init(&ssp->task_evnt_cv, NULL, CV_DRIVER, NULL);
+ mutex_init(&ssp->prog_mu, NULL, MUTEX_DRIVER, NULL);
+ ssp->progress |= BSCV_LOCKS;
+
+ bscv_trace(ssp, 'A', "bscv_attach",
+ "bscv_attach: mutexes and condition vars initialised");
+
+ /* Map in physical communication channels */
+
+ if (bscv_map_regs(ssp) != DDI_SUCCESS) {
+ (void) bscv_cleanup(ssp);
+ return (DDI_FAILURE);
+ }
+ ssp->progress |= BSCV_MAPPED_REGS;
+
+ /* Associate logical channels to physical channels */
+
+ bscv_map_chan_logical_physical(ssp);
+
+ bscv_enter(ssp);
+
+ bscv_leave_programming_mode(ssp, B_FALSE);
+
+ if (bscv_attach_common(ssp) == DDI_FAILURE) {
+ bscv_exit(ssp);
+ (void) bscv_cleanup(ssp);
+ return (DDI_FAILURE);
+ }
+
+#ifdef __sparc
+ /*
+ * At this point the inter-driver-interface is made available.
+ * The IDI uses the event thread service which
+ * bscv_attach_common() sets up.
+ */
+ bscv_idi_new_instance(dip);
+#endif /* __sparc */
+
+ bscv_exit(ssp);
+
+ /*
+ * now create the minor nodes
+ */
+ if (ddi_create_minor_node(ssp->dip, "lom", S_IFCHR,
+ BSCV_INST_TO_MINOR(instance),
+ DDI_PSEUDO, 0) != DDI_SUCCESS) {
+ (void) bscv_cleanup(ssp);
+ return (DDI_FAILURE);
+ }
+ bscv_trace(ssp, 'A', "bscv_attach",
+ "bscv_attach: device minor nodes created");
+ ssp->progress |= BSCV_NODES;
+
+ if (!ssp->prog_mode_only)
+ bscv_start_event_daemon(ssp);
+
+#if defined(__i386) || defined(__amd64)
+ bscv_watchdog_enable = 1;
+ bscv_watchdog_available = 1;
+ watchdog_activated = 0;
+ bscv_watchdog_timeout_seconds = CLK_WATCHDOG_DEFAULT;
+
+ if (bscv_watchdog_enable && (boothowto & RB_DEBUG)) {
+ bscv_watchdog_available = 0;
+ cmn_err(CE_WARN, "bscv: kernel debugger "
+ "detected: hardware watchdog disabled");
+ }
+
+ /*
+ * Before we enable the watchdog - register the panic
+ * callback so that we get called to stop the watchdog
+ * in the case of a panic.
+ */
+ ssp->callb_id = callb_add(bscv_panic_callback,
+ (void *)ssp, CB_CL_PANIC, "");
+
+ if (bscv_watchdog_available) {
+ (void) bscv_set_watchdog_timer(ssp,
+ CLK_WATCHDOG_DEFAULT);
+ bscv_enter(ssp);
+ bscv_setup_watchdog(ssp); /* starts cyclic callback */
+ bscv_exit(ssp);
+ }
+#endif /* __i386 || __amd64 */
+ ddi_report_dev(dip);
+ return (DDI_SUCCESS);
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+/*
+ * function - bscv_detach
+ * description - routine that prepares a module to be unloaded. It undoes all
+ * the work done by the bscv_attach)() routine. This is
+ * facilitated by the use of the progress indicator
+ * inputs - device information structure, DDI_DETACH command
+ * outputs - DDI_SUCCESS or DDI_FAILURE
+ */
+
+/*ARGSUSED*/
+static int
+bscv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ return (DDI_FAILURE);
+}
+
+/*
+ * function - bscv_reset
+ * description - routine called when system is being stopped - used to disable
+ * the watchdog.
+ * inputs - device information structure, DDI_RESET command
+ * outputs - DDI_SUCCESS or DDI_FAILURE
+ */
+static int
+bscv_reset(dev_info_t *dip, ddi_reset_cmd_t cmd)
+{
+ bscv_soft_state_t *ssp;
+ int instance;
+
+ switch (cmd) {
+ case DDI_RESET_FORCE:
+
+ instance = ddi_get_instance(dip);
+ ssp = ddi_get_soft_state(bscv_statep, instance);
+ if (ssp == NULL) {
+ return (DDI_FAILURE);
+ }
+ bscv_full_stop(ssp);
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+/*
+ * cb_ops routines
+ */
+
+/*
+ * function - bscv_open
+ * description - routine to provide association between user fd and device
+ * minor number. This routine is necessarily simple since a
+ * read/write interface is not provided. Additionally, the
+ * driver does not enforce exclusive access (FEXCL) or
+ * non-blocking during an open (FNDELAY). Deferred attach is
+ * supported.
+ * inputs - device number, flag specifying open type, device type,
+ * permissions
+ * outputs - success or failure of operation
+ */
+
+/*ARGSUSED*/
+static int
+bscv_open(dev_t *devp, int flag, int otype, cred_t *cred)
+{
+ bscv_soft_state_t *ssp;
+ int instance;
+
+ instance = DEVICETOINSTANCE(*devp);
+ ssp = ddi_get_soft_state(bscv_statep, instance);
+ if (ssp == NULL) {
+ return (ENXIO); /* not attached yet */
+ }
+ bscv_trace(ssp, 'O', "bscv_open", "instance 0x%x", instance);
+
+ if (otype != OTYP_CHR) {
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+/*
+ * function - bscv_close
+ * description - routine to perform the final close on the device. As per the
+ * open routine, neither FEXCL or FNDELAY accesses are enforced
+ * by the driver.
+ * inputs - device number,flag specifying open type, device type,
+ * permissions
+ * outputs - success or failure of operation
+ */
+
+/*ARGSUSED1*/
+static int
+bscv_close(dev_t dev, int flag, int otype, cred_t *cred)
+{
+ bscv_soft_state_t *ssp;
+ int instance;
+
+ instance = DEVICETOINSTANCE(dev);
+ ssp = ddi_get_soft_state(bscv_statep, instance);
+ if (ssp == NULL) {
+ return (ENXIO);
+ }
+ bscv_trace(ssp, 'O', "bscv_close", "instance 0x%x", instance);
+
+ return (0);
+}
+
+static int
+bscv_map_regs(bscv_soft_state_t *ssp)
+{
+ int i;
+ int retval;
+ int *props;
+ unsigned int nelements;
+
+ ASSERT(ssp);
+
+ ssp->nchannels = 0;
+
+ /*
+ * Work out how many channels are available by looking at the number
+ * of elements of the regs property array.
+ */
+ retval = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ssp->dip,
+ DDI_PROP_DONTPASS, "reg", &props, &nelements);
+
+ /* We don't need props anymore. Free memory if it was allocated */
+ if (retval == DDI_PROP_SUCCESS)
+ ddi_prop_free(props);
+
+ /* Check for sanity of nelements */
+ if (retval != DDI_PROP_SUCCESS) {
+ bscv_trace(ssp, 'A', "bscv_map_regs", "lookup reg returned"
+ " 0x%x", retval);
+ goto cleanup_exit;
+ } else if (nelements % LOMBUS_REGSPEC_SIZE != 0) {
+ bscv_trace(ssp, 'A', "bscv_map_regs", "nelements %d not"
+ " a multiple of %d", nelements, LOMBUS_REGSPEC_SIZE);
+ goto cleanup_exit;
+ } else if (nelements > BSCV_MAXCHANNELS * LOMBUS_REGSPEC_SIZE) {
+ bscv_trace(ssp, 'A', "bscv_map_regs", "nelements %d too large"
+ ", probably a misconfiguration", nelements);
+ goto cleanup_exit;
+ } else if (nelements < BSCV_MINCHANNELS * LOMBUS_REGSPEC_SIZE) {
+ bscv_trace(ssp, 'A', "bscv_map_regs", "nelements %d too small"
+ ", need to have at least a general and a wdog channel",
+ nelements);
+ goto cleanup_exit;
+ }
+
+ ssp->nchannels = nelements / LOMBUS_REGSPEC_SIZE;
+
+ ssp->attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
+ ssp->attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
+ ssp->attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
+
+ for (i = 0; i < ssp->nchannels; i++) {
+ retval = ddi_regs_map_setup(ssp->dip, i,
+ (caddr_t *)&ssp->channel[i].regs,
+ 0, 0, &ssp->attr, &ssp->channel[i].handle);
+ if (retval != DDI_SUCCESS) {
+ bscv_trace(ssp, 'A', "bscv_map_regs", "map failure"
+ " 0x%x on space %d", retval, i);
+
+ /* Rewind all current mappings - avoiding failed one */
+ i--;
+ for (; i >= 0; i--) {
+ ddi_regs_map_free(&ssp->channel[i].handle);
+ }
+
+ goto cleanup_exit;
+ }
+ }
+
+ return (DDI_SUCCESS);
+
+cleanup_exit:
+ /*
+ * It is important to set nchannels to 0 even if, say, only one of
+ * the two required handles was mapped. If we cannot achieve our
+ * minimum config its not safe to do any IO; this keeps our failure
+ * mode handling simpler.
+ */
+ ssp->nchannels = 0;
+ return (DDI_FAILURE);
+}
+
+static void
+bscv_unmap_regs(bscv_soft_state_t *ssp)
+{
+ int i;
+
+ ASSERT(ssp);
+
+ for (i = 0; i < ssp->nchannels; i++) {
+ ddi_regs_map_free(&ssp->channel[i].handle);
+ }
+}
+
+/*
+ * Map logical services onto physical XBus channels.
+ */
+static void
+bscv_map_chan_logical_physical(bscv_soft_state_t *ssp)
+{
+ ASSERT(ssp);
+
+ /*
+ * We can assert that there will always be at least two channels,
+ * to allow watchdog pats to be segregated from all other traffic.
+ */
+ chan_general = 0;
+ chan_wdogpat = 1;
+
+ /*
+ * By default move all other services onto the generic channel unless
+ * the hardware supports additional channels.
+ */
+
+ chan_cpusig = chan_eeprom = chan_prog = chan_general;
+
+ if (ssp->nchannels > 2)
+ chan_cpusig = 2;
+ if (ssp->nchannels > 3)
+ chan_eeprom = 3;
+ if (ssp->nchannels > 4)
+ chan_prog = 4;
+}
+
+
+/*
+ * function - bscv_full_stop
+ * description - gracefully shut the lom down during panic or reboot.
+ * Disables the watchdog, setup up serial event reporting
+ * and stops the event daemon running.
+ * inputs - soft state pointer
+ * outputs - none
+ */
+void
+bscv_full_stop(bscv_soft_state_t *ssp)
+{
+ uint8_t bits2set = 0;
+ uint8_t bits2clear = 0;
+
+ bscv_trace(ssp, 'W', "bscv_full_stop",
+ "turning off watchdog");
+
+ if (!ddi_in_panic()) {
+ /* Stop the event daemon if we are not panicking. */
+ (void) bscv_pause_event_daemon(ssp);
+ }
+
+ bscv_enter(ssp);
+
+#if defined(__i386) || defined(__amd64)
+ if (ddi_in_panic()) {
+ bscv_inform_bsc(ssp, BSC_INFORM_PANIC);
+ } else {
+ bscv_inform_bsc(ssp, BSC_INFORM_OFFLINE);
+ }
+#endif /* __i386 || __amd64 */
+
+ /* set serial event reporting */
+ switch (ssp->serial_reporting) {
+ case LOM_SER_EVENTS_ON:
+ case LOM_SER_EVENTS_DEF:
+ /* Make sure serial event reporting is on */
+ bits2clear = EBUS_ALARM_NOEVENTS;
+ break;
+ case LOM_SER_EVENTS_OFF:
+ /* Make sure serial event reporting is on */
+ bits2set = EBUS_ALARM_NOEVENTS;
+ break;
+ default:
+ break;
+ }
+ bscv_setclear8_volatile(ssp, chan_general,
+ EBUS_IDX_ALARM, bits2set, bits2clear);
+
+ bscv_exit(ssp);
+}
+
+/*
+ * LOM I/O routines.
+ *
+ * locking
+ *
+ * Two sets of routines are provided:
+ * normal - must be called after acquiring an appropriate lock.
+ * locked - perform all the locking required and return any error
+ * code in the supplied 'res' argument. If there is no
+ * error 'res' is not changed.
+ * The locked routines are designed for use in ioctl commands where
+ * only a single operation needs to be performed and the overhead of
+ * locking and result checking adds significantly to code complexity.
+ *
+ * locking primitives
+ *
+ * bscv_enter() - acquires an I/O lock for the calling thread.
+ * bscv_exit() - releases an I/O lock acquired by bscv_enter().
+ * bscv_held() - used to assert ownership of an I/O lock.
+ *
+ * normal I/O routines
+ *
+ * Note bscv_{put|get}{16|32} routines are big-endian. This assumes that
+ * the firmware works that way too.
+ *
+ * bscv_put8(), bscv_put16, bscv_put32 - write values to the LOM
+ * and handle any retries if necessary.
+ * 16 and 32 bit values are big-endian.
+ * bscv_get8(), bscv_get16, bscv_get32 - read values from the LOM
+ * and handle any retries if necessary.
+ * 16 and 32 bit values are big-endian.
+ * bscv_setclear8() - set or clear the specified bits in the register
+ * at the supplied address.
+ * bscv_setclear8_volatile() - set or clear the specified bits in the
+ * register at the supplied address. If the lom reports
+ * that the registers has changed since the last read
+ * re-read and apply the set or clear to the new bits.
+ * bscv_get8_cached() - Return a cached register value (addr < 0x80).
+ * Does not access the hardware. A read of the hardware
+ * automatically updates this cache.
+ *
+ * locked I/O routines
+ *
+ * bscv_get8_locked(), bscv_rep_get8_locked().
+ *
+ * Call the indicated function from above, but wrapping it with
+ * bscv_enter()/bscv_exit().
+ *
+ *
+ * Fault management
+ *
+ * LOM communications fault are grouped into three categories:
+ * 1) Faulty - the LOM is not responding and no attempt to communicate
+ * with it should be made.
+ * 2) Transient fault - something which might recover after a retry
+ * but which doesn't affect our ability to perform other
+ * commands.
+ * 3) Command error - an inappropriate command was executed. A retry
+ * will not fix it but the command failed.
+ *
+ * The current implementation of the bscv driver is not very good at
+ * noticing command errors due to the structure of the original code
+ * that it is based on. It is possible to extend the driver to do this
+ * and would probably involve having a concept of a "session error"
+ * which is less severe than a fault but means that a sequence of
+ * commands had some fault which cannot be recovered.
+ *
+ *
+ * faults
+ *
+ * bscv_faulty() - returns B_TRUE if the LOM (communications) have been
+ * declared faulty.
+ * bscv_clear_fault() - marks the LOM as not faulty.
+ * bscv_set_fault() - marks the LOM as being faulty.
+ *
+ * bscv_clear_fault and bscv_set_fault should generally not be called
+ * directly.
+ *
+ * command errors/transient faults
+ *
+ * bscv_retcode() - returns the actual error code of the last operation.
+ * bscv_should_retry() - determines if last operation may suceed if
+ * retried.
+ * bscv_locked_result() - Set the result of a locked register access.
+ *
+ * low level I/O primitives
+ *
+ * These are generally not called directly. These perform a single
+ * access to the LOM device. They do not handle retries.
+ *
+ * bscv_put8_once()
+ * bscv_get8_once()
+ * bscv_probe() - perform a probe (NOP) operation to check out lom comms.
+ * bscv_resync_comms() - resynchronise communications after a transient fault.
+ */
+
+static void
+bscv_enter(bscv_soft_state_t *ssp)
+{
+ bscv_trace(ssp, '@', "bscv_enter", "");
+ mutex_enter(&ssp->cmd_mutex);
+ ssp->had_session_error = B_FALSE;
+}
+
+static void
+bscv_exit(bscv_soft_state_t *ssp)
+{
+ mutex_exit(&ssp->cmd_mutex);
+ bscv_trace(ssp, '@', "bscv_exit", "");
+}
+
+#ifdef DEBUG
+static int
+bscv_held(bscv_soft_state_t *ssp)
+{
+ return (mutex_owned(&ssp->cmd_mutex));
+}
+#endif /* DEBUG */
+
+static void
+bscv_put8(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, uint8_t val)
+{
+ boolean_t needretry;
+ int num_failures;
+
+ ASSERT(bscv_held(ssp));
+
+ if (bscv_faulty(ssp)) {
+ return;
+ }
+
+ bscv_trace(ssp, '@', "bscv_put8",
+ "addr 0x%x.%02x <= 0x%02x", addr >> 8, addr & 0xff, val);
+
+ for (num_failures = 0;
+ num_failures < BSC_FAILURE_RETRY_LIMIT;
+ num_failures++) {
+ bscv_put8_once(ssp, chan, addr, val);
+ needretry = bscv_should_retry(ssp);
+ if (!needretry) {
+ break;
+ }
+ }
+ if (ssp->command_error != 0) {
+ ssp->had_session_error = B_TRUE;
+ }
+
+ if (needretry) {
+ /* Failure - we ran out of retries */
+ cmn_err(CE_WARN, "bscv_put8: addr 0x%x.%02x retried "
+ "write %d times, giving up",
+ addr >> 8, addr & 0xff, num_failures);
+ bscv_set_fault(ssp);
+ } else if (num_failures > 0) {
+ bscv_trace(ssp, 'R', "bscv_put8",
+ "addr 0x%x.%02x retried write %d times, succeeded",
+ addr >> 8, addr & 0xff, num_failures);
+ }
+}
+
+static void
+bscv_put16(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, uint16_t val)
+{
+ ASSERT(bscv_held(ssp));
+ bscv_trace(ssp, '@', "bscv_put16",
+ "addr 0x%x.%02x <= %04x", addr >> 8, addr & 0xff, val);
+ bscv_put8(ssp, chan, addr, val >> 8);
+ bscv_put8(ssp, chan, addr + 1, val & 0xff);
+}
+
+static void
+bscv_put32(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, uint32_t val)
+{
+ ASSERT(bscv_held(ssp));
+ bscv_trace(ssp, '@', "bscv_put32",
+ "addr 0x%x.%02x <= %08x", addr >> 8, addr & 0xff, val);
+ bscv_put8(ssp, chan, addr, (val >> 24) & 0xff);
+ bscv_put8(ssp, chan, addr + 1, (val >> 16) & 0xff);
+ bscv_put8(ssp, chan, addr + 2, (val >> 8) & 0xff);
+ bscv_put8(ssp, chan, addr + 3, val & 0xff);
+}
+
+static uint8_t
+bscv_get8(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr)
+{
+ uint8_t retval;
+ boolean_t needretry;
+ int num_failures;
+
+ ASSERT(bscv_held(ssp));
+
+ if (bscv_faulty(ssp)) {
+ return (0);
+ }
+
+ for (num_failures = 0;
+ num_failures < BSC_FAILURE_RETRY_LIMIT;
+ num_failures++) {
+ retval = bscv_get8_once(ssp, chan, addr);
+ needretry = bscv_should_retry(ssp);
+ if (!needretry) {
+ break;
+ }
+ }
+ if (ssp->command_error != 0) {
+ ssp->had_session_error = B_TRUE;
+ }
+
+ if (needretry) {
+ /* Failure */
+ cmn_err(CE_WARN, "bscv_get8: addr 0x%x.%02x retried "
+ "read %d times, giving up",
+ addr >> 8, addr & 0xff, num_failures);
+ bscv_set_fault(ssp);
+ } else if (num_failures > 0) {
+ bscv_trace(ssp, 'R', "bscv_get8",
+ "addr 0x%x.%02x retried read %d times, succeeded",
+ addr >> 8, addr & 0xff, num_failures);
+ }
+
+ bscv_trace(ssp, '@', "bscv_get8",
+ "addr 0x%x.%02x => %02x", addr >> 8, addr & 0xff, retval);
+ return (retval);
+}
+
+static uint16_t
+bscv_get16(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr)
+{
+ uint16_t retval;
+
+ ASSERT(bscv_held(ssp));
+
+ retval = bscv_get8(ssp, chan, addr) << 8;
+ retval |= bscv_get8(ssp, chan, addr + 1);
+
+ bscv_trace(ssp, '@', "bscv_get16",
+ "addr 0x%x.%02x => %04x", addr >> 8, addr & 0xff, retval);
+ return (retval);
+}
+
+static uint32_t
+bscv_get32(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr)
+{
+ uint32_t retval;
+
+ ASSERT(bscv_held(ssp));
+
+ retval = bscv_get8(ssp, chan, addr) << 24;
+ retval |= bscv_get8(ssp, chan, addr + 1) << 16;
+ retval |= bscv_get8(ssp, chan, addr + 2) << 8;
+ retval |= bscv_get8(ssp, chan, addr + 3);
+
+ bscv_trace(ssp, '@', "bscv_get32",
+ "addr 0x%x.%02x => %08x", addr >> 8, addr & 0xff, retval);
+ return (retval);
+}
+
+static void
+bscv_setclear8(bscv_soft_state_t *ssp, int chan,
+ bscv_addr_t addr, uint8_t set, uint8_t clear)
+{
+ uint8_t val;
+
+ ASSERT(bscv_held(ssp));
+ ASSERT(addr < BSC_ADDR_CACHE_LIMIT);
+
+ val = ssp->lom_regs[addr] | set;
+ val &= ~clear;
+
+ bscv_trace(ssp, '@', "bscv_setclear8",
+ "addr 0x%x.%02x, set %02x, clear %02x => %02x",
+ addr >> 8, addr & 0xff,
+ set, clear, val);
+
+ bscv_put8(ssp, chan, addr, val);
+}
+
+static void
+bscv_setclear8_volatile(bscv_soft_state_t *ssp, int chan,
+ bscv_addr_t addr, uint8_t set, uint8_t clear)
+{
+ uint8_t val;
+ boolean_t needretry;
+ int num_failures;
+
+ ASSERT(bscv_held(ssp));
+ ASSERT(addr < BSC_ADDR_CACHE_LIMIT);
+
+ if (bscv_faulty(ssp)) {
+ return;
+ }
+
+ bscv_trace(ssp, '@', "bscv_setclear8_volatile",
+ "addr 0x%x.%02x => set %02x clear %02x",
+ addr >> 8, addr & 0xff, set, clear);
+
+ val = bscv_get8_cached(ssp, addr);
+ for (num_failures = 0;
+ num_failures < BSC_FAILURE_RETRY_LIMIT;
+ num_failures++) {
+ val |= set;
+ val &= ~clear;
+ bscv_put8_once(ssp, chan, addr, val);
+ if (ssp->command_error == EBUS_ERROR_STALEDATA) {
+ /* Re-read the stale register from the lom */
+ val = bscv_get8_once(ssp, chan, addr);
+ needretry = 1;
+ } else {
+ needretry = bscv_should_retry(ssp);
+ if (!needretry) {
+ break;
+ }
+ }
+ }
+ if (ssp->command_error != 0) {
+ ssp->had_session_error = B_TRUE;
+ }
+
+ if (needretry) {
+ /* Failure */
+ cmn_err(CE_WARN, "bscv_setclear8_volatile: addr 0x%x.%02x "
+ "retried write %d times, giving up",
+ addr >> 8, addr & 0xff, num_failures);
+ if (ssp->command_error != EBUS_ERROR_STALEDATA) {
+ bscv_set_fault(ssp);
+ }
+ } else if (num_failures > 0) {
+ bscv_trace(ssp, 'R', "bscv_setclear8_volatile",
+ "addr 0x%x.%02x retried write %d times, succeeded",
+ addr >> 8, addr & 0xff, num_failures);
+ }
+}
+
+static void
+bscv_rep_rw8(bscv_soft_state_t *ssp, int chan, uint8_t *host_addr,
+ bscv_addr_t dev_addr, size_t repcount, uint_t flags,
+ boolean_t is_write)
+{
+ size_t inc;
+
+ ASSERT(bscv_held(ssp));
+
+ inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
+ for (; repcount--; dev_addr += inc) {
+ if (flags & DDI_DEV_AUTOINCR) {
+ if (is_write) {
+ bscv_put8(ssp, chan, dev_addr, *host_addr++);
+ } else {
+ *host_addr++ = bscv_get8(ssp, chan, dev_addr);
+ }
+ } else {
+ if (is_write) {
+ bscv_put8_once(ssp, chan,
+ dev_addr, *host_addr++);
+ } else {
+ *host_addr++ = bscv_get8_once(ssp, chan,
+ dev_addr);
+ }
+ /* We need this because _once routines don't do it */
+ if (ssp->command_error != 0) {
+ ssp->had_session_error = B_TRUE;
+ }
+ }
+ if (bscv_faulty(ssp) || bscv_session_error(ssp)) {
+ /*
+ * No retry here. If we were AUTOINCR then get/put
+ * will have retried. For NO_AUTOINCR we cannot retry
+ * because the data would be corrupted.
+ */
+ break;
+ }
+ }
+}
+
+static uint8_t
+bscv_get8_cached(bscv_soft_state_t *ssp, bscv_addr_t addr)
+{
+ ASSERT(addr < BSC_ADDR_CACHE_LIMIT);
+ /* Can be called with or without the lock held */
+
+ return (ssp->lom_regs[addr]);
+}
+
+static uint8_t
+bscv_get8_locked(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, int *res)
+{
+ uint8_t retval;
+
+ ASSERT(addr < BSC_ADDR_CACHE_LIMIT);
+ bscv_enter(ssp);
+ retval = bscv_get8(ssp, chan, addr);
+ bscv_locked_result(ssp, res);
+ bscv_exit(ssp);
+ bscv_trace(ssp, '@', "bscv_get8_locked",
+ "addr 0x%x.%02x => %02x", addr >> 8, addr & 0xff, retval);
+ return (retval);
+}
+
+static void
+bscv_rep_get8_locked(bscv_soft_state_t *ssp, int chan, uint8_t *host_addr,
+ bscv_addr_t dev_addr, size_t repcount, uint_t flags, int *res)
+{
+ bscv_enter(ssp);
+ bscv_rep_rw8(ssp, chan, host_addr, dev_addr, repcount,
+ flags, B_FALSE /* read */);
+ bscv_locked_result(ssp, res);
+ bscv_exit(ssp);
+}
+
+static boolean_t
+bscv_faulty(bscv_soft_state_t *ssp)
+{
+ ASSERT(bscv_held(ssp));
+ return (ssp->had_fault);
+}
+
+static void
+bscv_clear_fault(bscv_soft_state_t *ssp)
+{
+ ASSERT(bscv_held(ssp));
+ bscv_trace(ssp, 'J', "bscv_clear_fault", "clearing fault flag");
+ ssp->had_fault = B_FALSE;
+ ssp->had_session_error = B_FALSE;
+}
+
+static void
+bscv_set_fault(bscv_soft_state_t *ssp)
+{
+ ASSERT(bscv_held(ssp));
+ bscv_trace(ssp, 'J', "bscv_set_fault", "setting fault flag");
+ ssp->had_fault = B_TRUE;
+}
+
+static boolean_t
+bscv_session_error(bscv_soft_state_t *ssp)
+{
+ ASSERT(bscv_held(ssp));
+ return (ssp->had_session_error);
+}
+
+static int
+bscv_retcode(bscv_soft_state_t *ssp)
+{
+ bscv_trace(ssp, '@', "bscv_retcode",
+ "code 0x%x", ssp->command_error);
+ return (ssp->command_error);
+}
+
+static int
+bscv_should_retry(bscv_soft_state_t *ssp)
+{
+ if ((ssp->command_error == EBUS_ERROR_DEVICEFAIL) ||
+ (ssp->command_error >= LOMBUS_ERR_BASE)) {
+ /* This command is due to an I/O fault - retry might fix */
+ return (1);
+ } else {
+ /*
+ * The command itself was bad - there is no point in fixing
+ * Note. Whatever happens we should know that if we were
+ * doing EBUS_IDX_SELFTEST0..EBUS_IDX_SELFTEST7 and we
+ * had 0x80 set then this is a test error not a retry
+ * error.
+ */
+ return (0);
+ }
+}
+
+static void
+bscv_locked_result(bscv_soft_state_t *ssp, int *res)
+{
+ if (bscv_faulty(ssp) || (bscv_retcode(ssp) != 0)) {
+ *res = EIO;
+ }
+}
+
+static void
+bscv_put8_once(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, uint8_t val)
+{
+ uint32_t fault;
+
+ ASSERT(bscv_held(ssp));
+
+ ssp->command_error = 0;
+
+ if (bscv_faulty(ssp)) {
+ /* Bail out things are not working */
+ return;
+ } else if (ssp->nchannels == 0) {
+ /* Didn't manage to map handles so ddi_{get,put}* broken */
+ bscv_trace(ssp, '@', "bscv_put8_once",
+ "nchannels is 0x0 so cannot do IO");
+ return;
+ }
+
+ /* Clear any pending fault */
+ ddi_put32(ssp->channel[chan].handle,
+ (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG), 0);
+
+ /* Do the access and get fault code - may take a long time */
+ ddi_put8(ssp->channel[chan].handle,
+ &ssp->channel[chan].regs[addr], val);
+ fault = ddi_get32(ssp->channel[chan].handle,
+ (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG));
+
+ ssp->command_error = fault;
+
+ if (fault == 0) {
+ /* Things were ok - update cache entry */
+ if (addr < BSC_ADDR_CACHE_LIMIT) {
+ /* Store cacheable entries */
+ ssp->lom_regs[addr] = val;
+ }
+ } else if (fault >= LOMBUS_ERR_BASE) {
+ /* lombus problem - do a resync session */
+ cmn_err(CE_WARN, "!bscv_put8_once: Had comms fault "
+ "for address 0x%x.%02x - data 0x%x, fault 0x%x",
+ addr >> 8, addr & 0xff, val, fault);
+ /* Attempt to resync with the lom */
+ bscv_resync_comms(ssp, chan);
+ /*
+ * Note: we do not set fault status here. That
+ * is done if our caller decides to give up talking to
+ * the lom. The observant might notice that this means
+ * that if we mend things on the last attempt we still
+ * get the fault set - we just live with that!
+ */
+ }
+
+ bscv_trace(ssp, '@', "bscv_put8_once",
+ "addr 0x%x.%02x <= 0x%02x", addr >> 8, addr & 0xff, val);
+}
+
+static uint8_t
+bscv_get8_once(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr)
+{
+ uint8_t val;
+ uint32_t fault;
+
+ ASSERT(bscv_held(ssp));
+
+ ssp->command_error = 0;
+
+ if (bscv_faulty(ssp)) {
+ /* Bail out things are not working */
+ return (0xff);
+ } else if (ssp->nchannels == 0) {
+ /* Didn't manage to map handles so ddi_{get,put}* broken */
+ bscv_trace(ssp, '@', "bscv_get8_once",
+ "nchannels is 0x0 so cannot do IO");
+ return (0xff);
+ }
+
+ /* Clear any pending fault */
+ ddi_put32(ssp->channel[chan].handle,
+ (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG), 0);
+
+ /* Do the access and get fault code - may take a long time */
+ val = ddi_get8(ssp->channel[chan].handle,
+ &ssp->channel[chan].regs[addr]);
+ fault = ddi_get32(ssp->channel[chan].handle,
+ (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG));
+ ssp->command_error = fault;
+
+ if (fault >= LOMBUS_ERR_BASE) {
+ /* lombus problem - do a resync session */
+ cmn_err(CE_WARN, "!bscv_get8_once: Had comms fault "
+ "for address 0x%x.%02x - data 0x%x, fault 0x%x",
+ addr >> 8, addr & 0xff, val, fault);
+ /* Attempt to resync with the lom */
+ bscv_resync_comms(ssp, chan);
+ /*
+ * Note: we do not set fault status here. That
+ * is done if our caller decides to give up talking to
+ * the lom. The observant might notice that this means
+ * that if we mend things on the last attempt we still
+ * get the fault set - we just live with that!
+ */
+ }
+ /*
+ * FIXME - should report error if you get
+ * EBUS_ERROR_DEVICEFAIL reported from the BSC. That gets
+ * logged as a failure in bscv_should_retry and may contribute
+ * to a permanent failure. Reference issues seen by Mitac.
+ */
+
+ if (!bscv_faulty(ssp)) {
+ if (addr < BSC_ADDR_CACHE_LIMIT) {
+ /* Store cacheable entries */
+ ssp->lom_regs[addr] = val;
+ }
+ }
+
+ bscv_trace(ssp, '@', "bscv_get8_once",
+ "addr 0x%x.%02x => 0x%02x", addr >> 8, addr & 0xff, val);
+ return (val);
+}
+
+static uint32_t
+bscv_probe(bscv_soft_state_t *ssp, int chan, uint32_t *fault)
+{
+ uint32_t async_reg;
+
+ if (ssp->nchannels == 0) {
+ /*
+ * Failed to map handles, so cannot do any IO. Set the
+ * fault indicator and return a dummy value.
+ */
+ bscv_trace(ssp, '@', "bscv_probe",
+ "nchannels is 0x0 so cannot do any IO");
+ *fault = LOMBUS_ERR_REG_NUM;
+ return ((~(int8_t)0));
+ }
+
+ /* Clear faults */
+ ddi_put32(ssp->channel[chan].handle,
+ (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG), 0);
+ /* Probe and Check faults */
+ *fault = ddi_get32(ssp->channel[chan].handle,
+ (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_PROBE_REG));
+ /* Read status */
+ async_reg = ddi_get32(ssp->channel[chan].handle,
+ (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_ASYNC_REG));
+
+ bscv_trace(ssp, '@', "bscv_probe",
+ "async status 0x%x, fault 0x%x", async_reg, *fault);
+ return (async_reg);
+}
+
+static void
+bscv_resync_comms(bscv_soft_state_t *ssp, int chan)
+{
+ int try;
+ uint32_t command_error = ssp->command_error;
+ uint32_t fault = 0;
+
+ if (ssp->nchannels == 0) {
+ /*
+ * Didn't manage to map handles so ddi_{get,put}* broken.
+ * Therefore, there is no way to resync comms.
+ */
+ bscv_trace(ssp, '@', "bscv_resync_comms",
+ "nchannels is 0x0 so not possible to resync comms");
+ return;
+ }
+ if (command_error >= LOMBUS_ERR_BASE &&
+ command_error != LOMBUS_ERR_REG_NUM &&
+ command_error != LOMBUS_ERR_REG_SIZE &&
+ command_error != LOMBUS_ERR_TIMEOUT) {
+ /* Resync here to make sure that the lom is talking */
+ cmn_err(CE_WARN, "!bscv_resync_comms: "
+ "Attempting comms resync after comms fault 0x%x",
+ command_error);
+ for (try = 1; try <= 8; try++) {
+ /* Probe */
+ fault = ddi_get32(ssp->channel[chan].handle,
+ (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0,
+ LOMBUS_PROBE_REG));
+
+ if (fault == 0) {
+ break;
+ } else {
+ cmn_err(CE_WARN, "!bscv_resync_comms: "
+ "comms resync (probing) - try 0x%x "
+ "had fault 0x%x", try, fault);
+ }
+ }
+ if (fault != 0) {
+ cmn_err(CE_WARN, "!bscv_resync_comms: "
+ "Failed to resync comms - giving up");
+ ssp->bad_resync++;
+ } else {
+ cmn_err(CE_WARN, "!bscv_resync_comms: "
+ "resync comms after 0x%x tries", try);
+ ssp->bad_resync = 0;
+ }
+ }
+
+}
+
+
+/*
+ * LOMLite configuration/event eeprom access routines
+ *
+ * bscv_window_setup() - Read/Sanity check the eeprom parameters.
+ * This must be called prior to calling bscv_eerw().
+ * bscv_eerw() - Read/write data from/to the eeprom.
+ */
+
+/*
+ * function - bscv_window_setup
+ * description - this routine reads the eeprom parameters and sanity
+ * checks them to ensure that the lom is talking sense.
+ * inputs - soft state ptr
+ * outputs - B_TRUE if the eeprom is ok, B_FALSE if the eeprom is not OK.
+ */
+static boolean_t
+bscv_window_setup(bscv_soft_state_t *ssp)
+{
+ ASSERT(bscv_held(ssp));
+
+ if (ssp->eeinfo_valid) {
+ /* Already have good cached values */
+ return (ssp->eeinfo_valid);
+ }
+ ssp->eeprom_size =
+ bscv_get8(ssp, chan_general, EBUS_IDX_EEPROM_SIZE_KB) * 1024;
+ ssp->eventlog_start = bscv_get16(ssp, chan_general,
+ EBUS_IDX_LOG_START_HI);
+
+ /*
+ * The log does not run to the end of the EEPROM because it is a
+ * logical partition. The last 8K partition is reserved for FRUID
+ * usage.
+ */
+ ssp->eventlog_size = EBUS_LOG_END - ssp->eventlog_start;
+
+ bscv_trace(ssp, 'I', "bscv_window_setup", "eeprom size 0x%x log_start"
+ " 0x%x log_size 0x%x", ssp->eeprom_size, ssp->eventlog_start,
+ ssp->eventlog_size);
+
+ if (bscv_faulty(ssp) || bscv_session_error(ssp)) {
+ ssp->eeinfo_valid = B_FALSE;
+ } else if ((ssp->eeprom_size == 0) ||
+ (ssp->eventlog_start >= ssp->eeprom_size)) {
+ /* Sanity check values */
+ cmn_err(CE_WARN,
+ "!bscv_window_setup: read invalid eeprom parameters");
+ ssp->eeinfo_valid = B_FALSE;
+ } else {
+ ssp->eeinfo_valid = B_TRUE;
+ }
+
+ bscv_trace(ssp, 'I', "bscv_window_setup", "returning eeinfo_valid %s",
+ ssp->eeinfo_valid ? "true" : "false");
+ return (ssp->eeinfo_valid);
+}
+
+/*
+ * function - bscv_eerw
+ * description - this routine reads/write data from/to the eeprom.
+ * It takes care of setting the window on the eeprom correctly.
+ * inputs - soft state ptr, eeprom offset, data buffer, size, read/write
+ * outputs - B_TRUE if the eeprom is ok, B_FALSE if the eeprom is not OK.
+ */
+static int
+bscv_eerw(bscv_soft_state_t *ssp, uint32_t eeoffset, uint8_t *buf,
+ unsigned size, boolean_t is_write)
+{
+ uint32_t blk_addr = eeoffset;
+ unsigned remaining = size;
+ uint8_t page_idx;
+ uint8_t this_page;
+ uint8_t blk_size;
+ int res = 0;
+
+ while (remaining > 0) {
+ page_idx = blk_addr & 0xff;
+ if ((page_idx + remaining) > 0x100) {
+ blk_size = 0x100 - page_idx;
+ } else {
+ blk_size = remaining;
+ }
+
+ /* Select correct eeprom page */
+ this_page = blk_addr >> 8;
+ bscv_put8(ssp, chan_eeprom, EBUS_IDX_EEPROM_PAGESEL, this_page);
+
+ bscv_trace(ssp, 'M', "lom_eerw",
+ "%s data @0x%x.%02x, size 0x%x, 0x%x bytes remaining",
+ is_write ? "writing" : "reading",
+ this_page, page_idx, blk_size, remaining - blk_size);
+
+ bscv_rep_rw8(ssp, chan_eeprom,
+ buf, BSCVA(EBUS_CMD_SPACE_EEPROM, page_idx),
+ blk_size, DDI_DEV_AUTOINCR, is_write);
+
+ if (bscv_faulty(ssp) || bscv_session_error(ssp)) {
+ res = EIO;
+ break;
+ }
+
+ remaining -= blk_size;
+ blk_addr += blk_size;
+ buf += blk_size;
+ }
+
+ return (res);
+}
+
+static boolean_t
+bscv_is_null_event(bscv_soft_state_t *ssp, lom_event_t *e)
+{
+ ASSERT(e != NULL);
+
+ if (EVENT_DECODE_SUBSYS(e->ev_subsys) == EVENT_SUBSYS_NONE &&
+ e->ev_event == EVENT_NONE) {
+ /*
+ * This marks a NULL event.
+ */
+ bscv_trace(ssp, 'E', "bscv_is_null_event",
+ "EVENT_SUBSYS_NONE/EVENT_NONE null event");
+ return (B_TRUE);
+ } else if (e->ev_subsys == 0xff && e->ev_event == 0xff) {
+ /*
+ * Under some circumstances, we've seen all 1s to represent
+ * a manually cleared event log at the BSC prompt. Only
+ * a test/diagnosis environment is likely to show this.
+ */
+ bscv_trace(ssp, 'E', "bscv_is_null_event", "0xffff null event");
+ return (B_TRUE);
+ } else {
+ /*
+ * Not a NULL event.
+ */
+ bscv_trace(ssp, 'E', "bscv_is_null_event", "returning False");
+ return (B_FALSE);
+ }
+}
+
+/*
+ * *********************************************************************
+ * IOCTL Processing
+ * *********************************************************************
+ */
+
+/*
+ * function - bscv_ioctl
+ * description - routine that acts as a high level manager for ioctls. It
+ * calls the appropriate handler for ioctls on the alarm:mon and
+ * alarm:ctl minor nodes respectively
+ *
+ * Unsupported ioctls (now deprecated)
+ * LOMIOCALCTL
+ * LOMIOCALSTATE
+ * LOMIOCCLEARLOG
+ * LOMIOCCTL
+ * LOMIOCCTL2
+ * LOMIOCDAEMON
+ * LOMIOCDMON
+ * LOMIOCDOGCTL, TSIOCDOGCTL
+ * LOMIOCDOGPAT, TSIOCDOGPAT
+ * LOMIOCDOGTIME, TSIOCDOGTIME
+ * LOMIOCEVENTLOG
+ * LOMIOCEVNT
+ * LOMIOCGETMASK
+ * LOMIOCMPROG
+ * LOMIOCNBMON, TSIOCNBMON
+ * LOMIOCSLEEP
+ * LOMIOCUNLOCK, TSIOCUNLOCK
+ * LOMIOCWTMON, TSIOCWTMON
+ *
+ * Supported ioctls
+ * LOMIOCDOGSTATE, TSIOCDOGSTATE
+ * LOMIOCPROG
+ * LOMIOCPSUSTATE
+ * LOMIOCFANSTATE
+ * LOMIOCFLEDSTATE
+ * LOMIOCINFO
+ * LOMIOCMREAD
+ * LOMIOCVOLTS
+ * LOMIOCSTATS
+ * LOMIOCTEMP
+ * LOMIOCCONS
+ * LOMIOCEVENTLOG2
+ * LOMIOCINFO2
+ * LOMIOCTEST
+ * LOMIOCMPROG2
+ * LOMIOCMREAD2
+ *
+ * inputs - device number, command, user space arg, filemode, user
+ * credentials, return value
+ * outputs - the return value propagated back by the lower level routines.
+ */
+
+/*ARGSUSED*/
+static int
+bscv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp)
+{
+ bscv_soft_state_t *ssp;
+ int instance;
+ int res = 0;
+
+ instance = DEVICETOINSTANCE(dev);
+ ssp = ddi_get_soft_state(bscv_statep, instance);
+ if (ssp == NULL) {
+ return (ENXIO);
+ }
+
+ /*
+ * The Combined Switch and Service Processor takes care of configuration
+ * and control. The CSSP tells the BSC chip about it; therefore the
+ * bscv driver doesn't send such configuration and control to the BSC.
+ * Additionally Watchdog configuration is no longer done from userland
+ * lom.
+ */
+ switch (cmd) {
+ case LOMIOCALCTL:
+ case LOMIOCALSTATE:
+ case LOMIOCCLEARLOG:
+ case LOMIOCCTL:
+ case LOMIOCCTL2:
+ case LOMIOCDAEMON:
+ case LOMIOCDMON:
+ case LOMIOCDOGCTL:
+ case LOMIOCDOGPAT:
+ case LOMIOCDOGTIME:
+ case LOMIOCEVENTLOG:
+ case LOMIOCEVNT:
+ case LOMIOCGETMASK:
+ case LOMIOCMPROG:
+ case LOMIOCNBMON:
+ case LOMIOCSLEEP:
+ case LOMIOCUNLOCK:
+ case LOMIOCWTMON:
+ return (ENOTSUP);
+ }
+
+ /*
+ * set the default result.
+ */
+
+ *rvalp = 0;
+
+ if (ssp->cssp_prog) {
+ return (ENXIO);
+ } else if ((ssp->prog_mode_only || ssp->programming) &&
+ cmd != LOMIOCPROG) {
+ return (ENXIO);
+ }
+
+ /*
+ * Check that the caller has appropriate access permissions
+ * (FWRITE set in mode) for those ioctls which change lom
+ * state
+ */
+ if (!(mode & FWRITE)) {
+ switch (cmd) {
+ case LOMIOCMPROG2:
+ case LOMIOCMREAD2:
+ case LOMIOCPROG:
+ case LOMIOCTEST:
+ return (EACCES);
+ /* NOTREACHED */
+ default:
+ /* Does not require write access */
+ break;
+ }
+ }
+
+ switch (cmd) {
+
+ case LOMIOCDOGSTATE:
+ res = bscv_ioc_dogstate(ssp, arg, mode);
+ break;
+
+ case LOMIOCPROG:
+ res = bscv_prog(ssp, arg, mode);
+ break;
+
+ case LOMIOCPSUSTATE:
+ res = bscv_ioc_psustate(ssp, arg, mode);
+ break;
+
+ case LOMIOCFANSTATE:
+ res = bscv_ioc_fanstate(ssp, arg, mode);
+ break;
+
+ case LOMIOCFLEDSTATE:
+ res = bscv_ioc_fledstate(ssp, arg, mode);
+ break;
+
+ case LOMIOCLEDSTATE:
+ res = bscv_ioc_ledstate(ssp, arg, mode);
+ break;
+
+ case LOMIOCINFO:
+ res = bscv_ioc_info(ssp, arg, mode);
+ break;
+
+ case LOMIOCMREAD:
+ res = bscv_ioc_mread(ssp, arg, mode);
+ break;
+
+ case LOMIOCVOLTS:
+ res = bscv_ioc_volts(ssp, arg, mode);
+ break;
+
+ case LOMIOCSTATS:
+ res = bscv_ioc_stats(ssp, arg, mode);
+ break;
+
+ case LOMIOCTEMP:
+ res = bscv_ioc_temp(ssp, arg, mode);
+ break;
+
+ case LOMIOCCONS:
+ res = bscv_ioc_cons(ssp, arg, mode);
+ break;
+
+ case LOMIOCEVENTLOG2:
+ res = bscv_ioc_eventlog2(ssp, arg, mode);
+ break;
+
+ case LOMIOCINFO2:
+ res = bscv_ioc_info2(ssp, arg, mode);
+ break;
+
+ case LOMIOCTEST:
+ res = bscv_ioc_test(ssp, arg, mode);
+ break;
+
+ case LOMIOCMPROG2:
+ res = bscv_ioc_mprog2(ssp, arg, mode);
+ break;
+
+ case LOMIOCMREAD2:
+ res = bscv_ioc_mread2(ssp, arg, mode);
+ break;
+
+ default:
+ bscv_trace(ssp, 'I', "bscv_ioctl", "Invalid IOCTL 0x%x", cmd);
+ res = EINVAL;
+ }
+ return (res);
+}
+
+/*
+ * LOMIOCDOGSTATE
+ * TSIOCDOGSTATE - indicate whether the alarm watchdog and reset
+ * circuitry is enabled or not.
+ */
+static int
+bscv_ioc_dogstate(bscv_soft_state_t *ssp, intptr_t arg, int mode)
+{
+ lom_dogstate_t dogstate;
+ uint8_t dogval;
+ int res = 0;
+
+ dogval = bscv_get8_locked(ssp, chan_general, EBUS_IDX_WDOG_CTRL, &res);
+ dogstate.dog_enable = (dogval & EBUS_WDOG_ENABLE) ? 1 : 0;
+ dogstate.reset_enable = (dogval & EBUS_WDOG_RST) ? 1 : 0;
+ dogstate.dog_timeout = bscv_get8_locked(ssp, chan_general,
+ EBUS_IDX_WDOG_TIME, &res);
+
+ if ((res == 0) &&
+ (ddi_copyout((caddr_t)&dogstate,
+ (caddr_t)arg, sizeof (dogstate), mode) < 0)) {
+ res = EFAULT;
+ }
+ return (res);
+}
+
+/*
+ * LOMIOCPSUSTATE - returns full information for 4 PSUs. All this
+ * information is available from two bytes of LOMlite RAM, but if
+ * on the first read it is noticed that two or more of the PSUs are
+ * not present only 1 byte will be read subsequently.
+ */
+static int
+bscv_ioc_psustate(bscv_soft_state_t *ssp, intptr_t arg, int mode)
+{
+ lom_psudata_t psudata;
+ uint8_t psustat;
+ int i;
+ int res = 0;
+
+ for (i = 0; i < MAX_PSUS; i++) {
+ psustat = bscv_get8_locked(ssp, chan_general,
+ EBUS_IDX_PSU1_STAT + i, &res);
+ psudata.fitted[i] = psustat & EBUS_PSU_PRESENT;
+ psudata.output[i] = psustat & EBUS_PSU_OUTPUT;
+ psudata.supplyb[i] = psustat & EBUS_PSU_INPUTB;
+ psudata.supplya[i] = psustat & EBUS_PSU_INPUTA;
+ psudata.standby[i] = psustat & EBUS_PSU_STANDBY;
+ }
+
+ if (ddi_copyout((caddr_t)&psudata, (caddr_t)arg, sizeof (psudata),
+ mode) < 0) {
+ res = EFAULT;
+ }
+ return (res);
+}
+
+/*
+ * LOMIOCFANSTATE - returns full information including speed for 4
+ * fans and the minimum and maximum operating speeds for each fan as
+ * stored in the READ ONLY EEPROM data. As this EEPROM data is set
+ * at manufacture time, this data should only be read by the driver
+ * once and stored locally.
+ */
+static int
+bscv_ioc_fanstate(bscv_soft_state_t *ssp, intptr_t arg, int mode)
+{
+ lom_fandata_t fandata;
+ int numfans;
+ int i;
+ int res = 0;
+
+ bzero(&fandata, sizeof (lom_fandata_t));
+ numfans = EBUS_CONFIG_NFAN_DEC(bscv_get8_locked(ssp,
+ chan_general, EBUS_IDX_CONFIG, &res));
+ for (i = 0; (i < numfans) && (res == 0); i++) {
+ if (ssp->fanspeed[i] != LOM_FAN_NOT_PRESENT) {
+ fandata.fitted[i] = 1;
+ fandata.speed[i] = ssp->fanspeed[i];
+ fandata.minspeed[i] = bscv_get8_cached(ssp,
+ EBUS_IDX_FAN1_LOW + i);
+ }
+ }
+
+ if ((res == 0) &&
+ (ddi_copyout((caddr_t)&fandata, (caddr_t)arg, sizeof (fandata),
+ mode) < 0)) {
+ res = EFAULT;
+ }
+ return (res);
+}
+
+/*
+ * LOMIOCFLEDSTATE - returns the state of the fault LED
+ */
+static int
+bscv_ioc_fledstate(bscv_soft_state_t *ssp, intptr_t arg, int mode)
+{
+ lom_fled_info_t fled_info;
+ uint8_t fledstate;
+ int res = 0;
+
+ fledstate = bscv_get8_locked(ssp, chan_general, EBUS_IDX_ALARM, &res);
+
+ /* Decode of 0x0F is off and 0x00-0x07 is on. */
+ if (EBUS_ALARM_LED_DEC(fledstate) == 0x0F) {
+ fled_info.on = 0;
+ } else {
+ /* has +1 here - not 2 as in the info ioctl */
+ fled_info.on = EBUS_ALARM_LED_DEC(fledstate) + 1;
+ }
+ if ((res == 0) &&
+ (ddi_copyout((caddr_t)&fled_info, (caddr_t)arg,
+ sizeof (fled_info), mode) < 0)) {
+ res = EFAULT;
+ }
+ return (res);
+}
+
+/*
+ * LOMIOCLEDSTATE - returns the state of the requested LED
+ */
+static int
+bscv_ioc_ledstate(bscv_soft_state_t *ssp, intptr_t arg, int mode)
+{
+ lom_led_state_t led_state;
+ int fw_led_state;
+ int res = 0;
+
+ /* copy in arguments supplied */
+ if (ddi_copyin((caddr_t)arg, (caddr_t)&led_state,
+ sizeof (lom_led_state_t), mode) < 0) {
+ return (EFAULT);
+ }
+
+ /*
+ * check if led index is -1, if so set it to max value for
+ * this implementation.
+ */
+ if (led_state.index == -1) {
+ led_state.index = MAX_LED_ID;
+ }
+
+ /* is the index in a valid range */
+ if ((led_state.index > MAX_LED_ID) || (led_state.index < 0)) {
+ led_state.state = LOM_LED_OUTOFRANGE;
+ } else {
+ /* read the relevant led info */
+ fw_led_state = bscv_get8_locked(ssp, chan_general,
+ EBUS_IDX_LED1_STATUS + led_state.index, &res);
+
+ /* set the state values accordingly */
+ switch (fw_led_state) {
+ case LOM_LED_STATE_OFF:
+ led_state.state = LOM_LED_OFF;
+ led_state.colour = LOM_LED_COLOUR_ANY;
+ break;
+ case LOM_LED_STATE_ON_STEADY:
+ led_state.state = LOM_LED_ON;
+ led_state.colour = LOM_LED_COLOUR_ANY;
+ break;
+ case LOM_LED_STATE_ON_FLASHING:
+ case LOM_LED_STATE_ON_SLOWFLASH:
+ led_state.state = LOM_LED_BLINKING;
+ led_state.colour = LOM_LED_COLOUR_ANY;
+ break;
+ case LOM_LED_STATE_NOT_PRESENT:
+ led_state.state = LOM_LED_NOT_IMPLEMENTED;
+ led_state.colour = LOM_LED_COLOUR_NONE;
+ break;
+ case LOM_LED_STATE_INACCESSIBLE:
+ case LOM_LED_STATE_STANDBY:
+ default:
+ led_state.state = LOM_LED_ACCESS_ERROR;
+ led_state.colour = LOM_LED_COLOUR_NONE;
+ break;
+ }
+
+ /* set the label info */
+ (void) strcpy(led_state.label,
+ ssp->led_names[led_state.index]);
+ }
+
+ /* copy out lom_state */
+ if ((res == 0) &&
+ (ddi_copyout((caddr_t)&led_state, (caddr_t)arg,
+ sizeof (lom_led_state_t), mode) < 0)) {
+ res = EFAULT;
+ }
+ return (res);
+}
+
+/*
+ * LOMIOCINFO - returns with a structure containing any information
+ * stored on the LOMlite which a user should not need to access but
+ * may be useful for diagnostic problems. The structure contains: the
+ * serial escape character, alarm3 mode, version and checksum read from
+ * RAM and the Product revision and ID read from EEPROM.
+ */
+static int
+bscv_ioc_info(bscv_soft_state_t *ssp, intptr_t arg, int mode)
+{
+ lom_info_t info;
+ int i;
+ uint16_t csum;
+ int res = 0;
+
+ info.ser_char = bscv_get8_locked(ssp, chan_general, EBUS_IDX_ESCAPE,
+ &res);
+ info.a3mode = WATCHDOG;
+ info.fver = bscv_get8_locked(ssp, chan_general, EBUS_IDX_FW_REV, &res);
+ csum = bscv_get8_locked(ssp, chan_general, EBUS_IDX_CHECK_HI, &res)
+ << 8;
+ csum |= bscv_get8_locked(ssp, chan_general, EBUS_IDX_CHECK_LO, &res);
+ info.fchksum = csum;
+ info.prod_rev = bscv_get8_locked(ssp, chan_general, EBUS_IDX_MODEL_REV,
+ &res);
+ for (i = 0; i < sizeof (info.prod_id); i++) {
+ info.prod_id[i] = bscv_get8_locked(ssp,
+ chan_general, EBUS_IDX_MODEL_ID1 + i, &res);
+ }
+ if (bscv_get8_locked(ssp, chan_general, EBUS_IDX_ALARM, &res) &
+ EBUS_ALARM_NOEVENTS) {
+ info.events = OFF;
+ } else {
+ info.events = ON;
+ }
+
+ if ((res == 0) &&
+ (ddi_copyout((caddr_t)&info, (caddr_t)arg, sizeof (info),
+ mode) < 0)) {
+ res = EFAULT;
+ }
+ return (res);
+}
+
+/*
+ * LOMIOCMREAD - used to query the LOMlite configuration parameters
+ */
+static int
+bscv_ioc_mread(bscv_soft_state_t *ssp, intptr_t arg, int mode)
+{
+ lom_mprog_t mprog;
+ int i;
+ int fanz;
+ int res = 0;
+
+ for (i = 0; i < sizeof (mprog.mod_id); i++) {
+ mprog.mod_id[i] = bscv_get8_locked(ssp, chan_general,
+ EBUS_IDX_MODEL_ID1 + i, &res);
+ }
+ mprog.mod_rev = bscv_get8_locked(ssp, chan_general, EBUS_IDX_MODEL_REV,
+ &res);
+ mprog.config = bscv_get8_locked(ssp, chan_general, EBUS_IDX_CONFIG,
+ &res);
+
+ /* Read the fan calibration values */
+ fanz = sizeof (mprog.fanhz) / sizeof (mprog.fanhz[0]);
+ for (i = 0; i < fanz; i++) {
+ mprog.fanhz[i] = bscv_get8_cached(ssp,
+ EBUS_IDX_FAN1_CAL + i);
+ mprog.fanmin[i] = bscv_get8_cached(ssp,
+ EBUS_IDX_FAN1_LOW + i);
+ }
+
+ if ((res == 0) &&
+ (ddi_copyout((caddr_t)&mprog, (caddr_t)arg, sizeof (mprog),
+ mode) < 0)) {
+ res = EFAULT;
+ }
+ return (res);
+}
+
+/*
+ * LOMIOCVOLTS
+ */
+static int
+bscv_ioc_volts(bscv_soft_state_t *ssp, intptr_t arg, int mode)
+{
+ int i;
+ uint16_t supply;
+ int res = 0;
+
+ supply = (bscv_get8_locked(ssp, chan_general, EBUS_IDX_SUPPLY_HI, &res)
+ << 8) | bscv_get8_locked(ssp, chan_general, EBUS_IDX_SUPPLY_LO,
+ &res);
+
+ for (i = 0; i < ssp->volts.num; i++) {
+ ssp->volts.status[i] = (supply >> i) & 1;
+ }
+
+ if ((res == 0) &&
+ (ddi_copyout((caddr_t)&ssp->volts, (caddr_t)arg,
+ sizeof (ssp->volts), mode) < 0)) {
+ res = EFAULT;
+ }
+ return (res);
+}
+
+/*
+ * LOMIOCSTATS
+ */
+static int
+bscv_ioc_stats(bscv_soft_state_t *ssp, intptr_t arg, int mode)
+{
+ int i;
+ uint8_t status;
+ int res = 0;
+
+ status = bscv_get8_locked(ssp, chan_general, EBUS_IDX_CBREAK_STATUS,
+ &res);
+ for (i = 0; i < ssp->sflags.num; i++) {
+ ssp->sflags.status[i] = (int)((status >> i) & 1);
+ }
+
+ if ((res == 0) &&
+ (ddi_copyout((caddr_t)&ssp->sflags, (caddr_t)arg,
+ sizeof (ssp->sflags), mode) < 0)) {
+ res = EFAULT;
+ }
+ return (res);
+}
+
+/*
+ * LOMIOCTEMP
+ */
+static int
+bscv_ioc_temp(bscv_soft_state_t *ssp, intptr_t arg, int mode)
+{
+ int i;
+ int idx;
+ uint8_t status_ov;
+ lom_temp_t temps;
+ int res = 0;
+
+ bzero(&temps, sizeof (temps));
+ idx = 0;
+ for (i = 0; i < ssp->temps.num; i++) {
+ if (ssp->temps.temp[i] != LOM_TEMP_STATE_NOT_PRESENT) {
+ temps.temp[idx] = ssp->temps.temp[i];
+ bcopy(ssp->temps.name[i], temps.name[idx],
+ sizeof (temps.name[idx]));
+ temps.warning[idx] = ssp->temps.warning[i];
+ temps.shutdown[idx] = ssp->temps.shutdown[i];
+ idx++;
+ }
+ }
+ temps.num = idx;
+
+ bcopy(ssp->temps.name_ov, temps.name_ov, sizeof (temps.name_ov));
+ temps.num_ov = ssp->temps.num_ov;
+ status_ov = bscv_get8_locked(ssp, chan_general, EBUS_IDX_OTEMP_STATUS,
+ &res);
+ for (i = 0; i < ssp->temps.num_ov; i++) {
+ ssp->temps.status_ov[i] = (status_ov >> i) & 1;
+ }
+
+ if ((res == 0) &&
+ (ddi_copyout((caddr_t)&temps, (caddr_t)arg, sizeof (temps),
+ mode) < 0)) {
+ res = EFAULT;
+ }
+ return (res);
+}
+
+/*
+ * LOMIOCCONS
+ */
+static int
+bscv_ioc_cons(bscv_soft_state_t *ssp, intptr_t arg, int mode)
+{
+ lom_cbuf_t cbuf;
+ int datasize;
+ int res = 0;
+
+ bzero(&cbuf, sizeof (cbuf));
+ datasize = EBUS_IDX1_CONS_BUF_END - EBUS_IDX1_CONS_BUF_START + 1;
+ /* Ensure that we do not overfill cbuf and that it is NUL terminated */
+ if (datasize > (sizeof (cbuf) - 1)) {
+ datasize = sizeof (cbuf) - 1;
+ }
+ bscv_rep_get8_locked(ssp, chan_general, (uint8_t *)cbuf.lrbuf,
+ BSCVA(EBUS_CMD_SPACE1, (EBUS_IDX1_CONS_BUF_END - datasize + 1)),
+ datasize, DDI_DEV_AUTOINCR, &res);
+ /* This is always within the array due to the checks above */
+ cbuf.lrbuf[datasize] = '\0';
+
+ if ((res == 0) &&
+ (ddi_copyout((caddr_t)&cbuf, (caddr_t)arg, sizeof (cbuf),
+ mode) < 0)) {
+ res = EFAULT;
+ }
+ return (res);
+}
+
+/*
+ * LOMIOCEVENTLOG2
+ */
+static int
+bscv_ioc_eventlog2(bscv_soft_state_t *ssp, intptr_t arg, int mode)
+{
+ lom_eventlog2_t *eventlog2;
+ int events_recorded;
+ int level;
+ uint16_t next_offset;
+ lom_event_t event;
+ int res = 0;
+
+ eventlog2 = (lom_eventlog2_t *)kmem_zalloc(sizeof (*eventlog2),
+ KM_SLEEP);
+
+ /*
+ * First get number of events and level requested.
+ */
+
+ if (ddi_copyin((caddr_t)arg, (caddr_t)eventlog2,
+ sizeof (lom_eventlog2_t), mode) < 0) {
+ kmem_free((void *)eventlog2, sizeof (*eventlog2));
+ return (EFAULT);
+ }
+
+ bscv_enter(ssp);
+
+ /*
+ * OK we have full private access to the LOM now so loop
+ * over the eventlog addr spaces until we get the required
+ * number of events.
+ */
+
+ if (!bscv_window_setup(ssp)) {
+ res = EIO;
+ bscv_exit(ssp);
+ kmem_free((void *)eventlog2, sizeof (*eventlog2));
+ return (res);
+ }
+
+ /*
+ * Read count, next event ptr MSB,LSB. Note a read of count
+ * is necessary to latch values for the next event ptr
+ */
+ (void) bscv_get8(ssp, chan_general, EBUS_IDX_UNREAD_EVENTS);
+ next_offset = bscv_get16(ssp, chan_general, EBUS_IDX_LOG_PTR_HI);
+ bscv_trace(ssp, 'I', "bscv_ioc_eventlog2", "log_ptr_hi 0x%x",
+ next_offset);
+
+ events_recorded = 0;
+
+ while (events_recorded < eventlog2->num) {
+ /*
+ * Working backwards - read an event at a time.
+ * next_offset is one event on from where we want to be!
+ * Decrement next_offset and maybe wrap to the end of the
+ * buffer.
+ * Note the unsigned arithmetic, so check values first!
+ */
+ if (next_offset <= ssp->eventlog_start) {
+ /* Wrap to the end of the buffer */
+ next_offset = ssp->eventlog_start + ssp->eventlog_size;
+ bscv_trace(ssp, 'I', "bscv_ioc_eventlog2", "wrapping"
+ " around to end of buffer; next_offset 0x%x",
+ next_offset);
+ }
+ next_offset -= sizeof (event);
+
+ if (bscv_eerw(ssp, next_offset, (uint8_t *)&event,
+ sizeof (event), B_FALSE /* read */) != 0) {
+ /* Fault reading data - stop */
+ bscv_trace(ssp, 'I', "bscv_ioc_eventlog2", "read"
+ " failure for offset 0x%x", next_offset);
+ res = EIO;
+ break;
+ }
+
+ if (bscv_is_null_event(ssp, &event)) {
+ /*
+ * No more events in this log so give up.
+ */
+ bscv_trace(ssp, 'I', "bscv_ioc_eventlog2", "no more"
+ " events left at offset 0x%x", next_offset);
+ break;
+ }
+
+ /*
+ * Are we interested in this event
+ */
+
+ level = bscv_level_of_event(&event);
+ if (level <= eventlog2->level) {
+ /* Arggh why the funny byte ordering 3, 2, 0, 1 */
+ eventlog2->code[events_recorded] =
+ ((unsigned)event.ev_event |
+ ((unsigned)event.ev_subsys << 8) |
+ ((unsigned)event.ev_resource << 16) |
+ ((unsigned)event.ev_detail << 24));
+
+ eventlog2->time[events_recorded] =
+ ((unsigned)event.ev_data[0] |
+ ((unsigned)event.ev_data[1] << 8) |
+ ((unsigned)event.ev_data[3] << 16) |
+ ((unsigned)event.ev_data[2] << 24));
+
+ bscv_build_eventstring(ssp,
+ &event, eventlog2->string[events_recorded],
+ eventlog2->string[events_recorded] +
+ sizeof (eventlog2->string[events_recorded]));
+ events_recorded++;
+ }
+ }
+
+ eventlog2->num = events_recorded;
+
+ bscv_exit(ssp);
+
+ if ((res == 0) &&
+ (ddi_copyout((caddr_t)eventlog2, (caddr_t)arg,
+ sizeof (lom_eventlog2_t), mode) < 0)) {
+ res = EFAULT;
+ }
+
+ kmem_free((void *)eventlog2, sizeof (lom_eventlog2_t));
+ return (res);
+}
+
+/*
+ * LOMIOCINFO2
+ */
+static int
+bscv_ioc_info2(bscv_soft_state_t *ssp, intptr_t arg, int mode)
+{
+ lom2_info_t info2;
+ int i;
+ uint16_t csum;
+ int res = 0;
+
+ bzero(&info2, sizeof (info2));
+
+ (void) strncpy(info2.escape_chars, ssp->escape_chars,
+ sizeof (info2.escape_chars));
+ info2.serial_events = ssp->reporting_level | ssp->serial_reporting;
+ info2.a3mode = WATCHDOG;
+
+ info2.fver = bscv_get8_locked(ssp, chan_general, EBUS_IDX_FW_REV, &res);
+ csum = bscv_get8_locked(ssp, chan_general, EBUS_IDX_CHECK_HI, &res)
+ << 8;
+ csum |= bscv_get8_locked(ssp, chan_general, EBUS_IDX_CHECK_LO, &res);
+ info2.fchksum = csum;
+ info2.prod_rev = bscv_get8_locked(ssp, chan_general,
+ EBUS_IDX_MODEL_REV, &res);
+ for (i = 0; i < sizeof (info2.prod_id); i++) {
+ info2.prod_id[i] = bscv_get8_locked(ssp, chan_general,
+ EBUS_IDX_MODEL_ID1 + i, &res);
+ }
+ info2.serial_config = bscv_get8_locked(ssp, chan_general,
+ EBUS_IDX_SER_TIMEOUT, &res);
+ if (bscv_get8_locked(ssp, chan_general, EBUS_IDX_CONFIG_MISC, &res) &
+ EBUS_CONFIG_MISC_SECURITY_ENABLED) {
+ info2.serial_config |= LOM_SER_SECURITY;
+ }
+ if (bscv_get8_locked(ssp, chan_general, EBUS_IDX_CONFIG_MISC, &res) &
+ EBUS_CONFIG_MISC_AUTO_CONSOLE) {
+ info2.serial_config |= LOM_SER_RETURN;
+ }
+ if (bscv_get8_locked(ssp, chan_general, EBUS_IDX_WDOG_CTRL, &res) &
+ EBUS_WDOG_BREAK_DISABLE) {
+ info2.serial_config |= LOM_DISABLE_WDOG_BREAK;
+ }
+ info2.baud_rate = bscv_get8_locked(ssp, chan_general,
+ EBUS_IDX_SER_BAUD, &res);
+ info2.serial_hw_config =
+ ((int)bscv_get8_locked(ssp, chan_general,
+ EBUS_IDX_SER_CHARMODE, &res) |
+ ((int)bscv_get8_locked(ssp, chan_general,
+ EBUS_IDX_SER_FLOWCTL, &res) << 8) |
+ ((int)bscv_get8_locked(ssp, chan_general,
+ EBUS_IDX_SER_MODEMTYPE, &res) << 16));
+
+ /*
+ * There is no phone home support on the blade platform. We hardcode
+ * FALSE and NUL for config and script respectively.
+ */
+ info2.phone_home_config = B_FALSE;
+ info2.phone_home_script[0] = '\0';
+
+ for (i = 0; i < ssp->num_fans; i++) {
+ (void) strcpy(info2.fan_names[i], ssp->fan_names[i]);
+ }
+
+ if ((res == 0) &&
+ (ddi_copyout((caddr_t)&info2, (caddr_t)arg, sizeof (info2),
+ mode) < 0)) {
+ res = EFAULT;
+ }
+ return (res);
+}
+
+/*
+ * LOMIOCTEST
+ */
+static int
+bscv_ioc_test(bscv_soft_state_t *ssp, intptr_t arg, int mode)
+{
+ uint32_t test;
+ uint8_t testnum;
+ uint8_t testarg;
+ int res = 0;
+
+ if (ddi_copyin((caddr_t)arg, (caddr_t)&test, sizeof (test),
+ mode) < 0) {
+ return (EFAULT);
+ }
+
+ /*
+ * Extract num iterations.
+ */
+
+ testarg = (test & 0xff00) >> 8;
+ testnum = test & 0xff;
+
+ bscv_trace(ssp, 'F', "bscv_ioc_test",
+ "LOMIOCTEST data 0x%x (test 0x%x, arg 0x%x)",
+ test, (EBUS_IDX_SELFTEST0 + testnum), testarg);
+
+ switch (testnum + EBUS_IDX_SELFTEST0) {
+ default:
+ /* Invalid test */
+ res = EINVAL;
+ break;
+
+ case EBUS_IDX_SELFTEST0: /* power on self-test result */
+ case EBUS_IDX_SELFTEST1: /* not used currently */
+ case EBUS_IDX_SELFTEST2: /* not used currently */
+ case EBUS_IDX_SELFTEST3: /* not used currently */
+ case EBUS_IDX_SELFTEST4: /* not used currently */
+ case EBUS_IDX_SELFTEST5: /* not used currently */
+ case EBUS_IDX_SELFTEST6: /* LED self-test */
+ case EBUS_IDX_SELFTEST7: /* platform-specific tests */
+ /* Run the test */
+
+ /* Stop other things and then run the test */
+ bscv_enter(ssp);
+
+ /*
+ * Then we simply write the argument to the relevant register
+ * and wait for the return code.
+ */
+ bscv_put8(ssp, chan_general,
+ EBUS_IDX_SELFTEST0 + testnum, testarg);
+ if (bscv_faulty(ssp)) {
+ res = EIO;
+ } else {
+ /* Get hold of the SunVTS error code */
+ test = bscv_retcode(ssp);
+ }
+
+ bscv_exit(ssp);
+ break;
+ }
+
+ bscv_trace(ssp, 'F', "bscv_ioc_test",
+ "LOMIOCTEST status 0x%x, res 0x%x", test, res);
+ if ((res == 0) &&
+ (ddi_copyout((caddr_t)&test, (caddr_t)arg, sizeof (test),
+ mode) < 0)) {
+ res = EFAULT;
+ }
+ return (res);
+}
+
+/*
+ * LOMIOCMPROG2
+ */
+static int
+bscv_ioc_mprog2(bscv_soft_state_t *ssp, intptr_t arg, int mode)
+{
+ lom2_mprog_t mprog2;
+ uint32_t base_addr;
+ uint32_t data_size;
+ uint32_t eeprom_size;
+ int res = 0;
+
+ if (ddi_copyin((caddr_t)arg, (caddr_t)&mprog2, sizeof (mprog2),
+ mode) < 0) {
+ return (EFAULT);
+ }
+
+ /*
+ * Note that originally this was accessed as 255 byte pages
+ * in address spaces 240-255. We have to emulate this behaviour.
+ */
+ if ((mprog2.addr_space < 240) || (mprog2.addr_space > 255)) {
+ return (EINVAL);
+ }
+
+ bscv_enter(ssp);
+
+ /* Calculate required data location */
+ data_size = 255;
+ base_addr = (mprog2.addr_space - 240) * data_size;
+
+ eeprom_size = bscv_get8(ssp, chan_general, EBUS_IDX_EEPROM_SIZE_KB) *
+ 1024;
+
+ if (bscv_faulty(ssp)) {
+ bscv_exit(ssp);
+ return (EIO);
+ } else if ((base_addr + data_size) > eeprom_size) {
+ bscv_trace(ssp, 'M', "bscv_ioc_mprog2",
+ "Request extends past end of eeprom");
+ bscv_exit(ssp);
+ return (ENXIO);
+ }
+
+ bscv_put8(ssp, chan_general, EBUS_IDX_CMD_RES, EBUS_CMD_UNLOCK1);
+ if (bscv_faulty(ssp)) {
+ bscv_trace(ssp, 'M', "bscv_ioc_mprog2", "ML1 Write failed");
+ bscv_exit(ssp);
+ return (EIO);
+ }
+
+ bscv_put8(ssp, chan_general, EBUS_IDX_CMD_RES, EBUS_CMD_UNLOCK2);
+ if (bscv_faulty(ssp)) {
+ bscv_trace(ssp, 'M', "bscv_ioc_mprog2", "ML2 Write failed");
+ bscv_exit(ssp);
+ return (EIO);
+ }
+
+ if (bscv_eerw(ssp, base_addr, &mprog2.data[0],
+ data_size, B_TRUE /* write */) != 0) {
+ res = EIO;
+ }
+
+ /* Read a probe key to release the lock. */
+ (void) bscv_get8(ssp, chan_general, EBUS_IDX_PROBEAA);
+
+ if (bscv_faulty(ssp)) {
+ res = EIO;
+ }
+ bscv_exit(ssp);
+
+ return (res);
+}
+
+/*
+ * LOMIOCMREAD2
+ */
+static int
+bscv_ioc_mread2(bscv_soft_state_t *ssp, intptr_t arg, int mode)
+{
+ lom2_mprog_t mprog2;
+ uint32_t base_addr;
+ uint32_t data_size;
+ uint32_t eeprom_size;
+ int res = 0;
+
+ if (ddi_copyin((caddr_t)arg, (caddr_t)&mprog2, sizeof (mprog2),
+ mode) < 0) {
+ return (EFAULT);
+ }
+
+ /*
+ * Need to stop the queue and then just read
+ * the bytes blind to the relevant addresses.
+ * Note that originally this was accessed as 255 byte pages
+ * in address spaces 240-255. We have to emulate this behaviour.
+ */
+ if ((mprog2.addr_space < 240) || (mprog2.addr_space > 255)) {
+ return (EINVAL);
+ }
+
+ bscv_enter(ssp);
+
+ /* Calculate required data location */
+ data_size = 255;
+ base_addr = (mprog2.addr_space - 240) * data_size;
+ eeprom_size = bscv_get8(ssp, chan_general, EBUS_IDX_EEPROM_SIZE_KB) *
+ 1024;
+
+ if (bscv_faulty(ssp)) {
+ bscv_exit(ssp);
+ return (EIO);
+ } else if ((base_addr + data_size) > eeprom_size) {
+ bscv_trace(ssp, 'M', "bscv_ioc_mread2",
+ "Request extends past end of eeprom");
+ bscv_exit(ssp);
+ return (ENXIO);
+ }
+
+ if (bscv_eerw(ssp, base_addr, &mprog2.data[0],
+ data_size, B_FALSE /* read */) != 0) {
+ res = EIO;
+ }
+
+ if (bscv_faulty(ssp)) {
+ res = EIO;
+ }
+ bscv_exit(ssp);
+
+ if ((res == 0) &&
+ (ddi_copyout((caddr_t)&mprog2, (caddr_t)arg, sizeof (mprog2),
+ mode) < 0)) {
+ res = EFAULT;
+ }
+ return (res);
+}
+
+static void
+bscv_get_state_changes(bscv_soft_state_t *ssp)
+{
+ int i = STATUS_READ_LIMIT;
+ uint8_t change;
+ uint8_t detail;
+
+ ASSERT(bscv_held(ssp));
+
+ while (i-- && !ssp->cssp_prog) {
+ /* Are there any changes to process? */
+ change = bscv_get8(ssp, chan_general, EBUS_IDX_STATE_CHNG);
+ change &= EBUS_STATE_MASK;
+ if (!change)
+ break;
+
+ /* Clarify the pending change */
+ detail = bscv_get8(ssp, chan_general, EBUS_IDX_EVENT_DETAIL);
+
+ bscv_status(ssp, change, detail);
+ }
+
+ bscv_trace(ssp, 'D', "bscv_get_state_changes",
+ "loop index %d ssp->cssp_prog 0x%x", i, ssp->cssp_prog);
+}
+
+/*
+ * *********************************************************************
+ * Event Processing
+ * *********************************************************************
+ */
+
+/*
+ * function - bscv_event_daemon
+ * description - Perform periodic lom tasks in a separate thread.
+ * inputs - LOM soft state structure pointer
+ * outputs - none.
+ */
+static void
+bscv_event_daemon(void *arg)
+{
+ bscv_soft_state_t *ssp = (void *)arg;
+ boolean_t do_events;
+ boolean_t do_status;
+ boolean_t do_nodename;
+ boolean_t do_watchdog;
+ uint32_t async_reg;
+ uint32_t fault;
+ clock_t poll_period = BSC_EVENT_POLL_NORMAL;
+ int fault_cnt = 0;
+
+ bscv_trace(ssp, 'D', "bscv_event_daemon",
+ "bscv_event_daemon: started");
+
+ /* Acquire task daemon lock. */
+ mutex_enter(&ssp->task_mu);
+
+ ssp->task_flags |= TASK_ALIVE_FLG;
+
+ for (;;) {
+ if ((ssp->task_flags & TASK_STOP_FLG) != 0) {
+ /* Stop request seen - terminate */
+ break;
+ }
+ if ((ssp->task_flags & TASK_PAUSE_FLG) == 0) {
+ /* Poll for events reported to the nexus */
+ mutex_exit(&ssp->task_mu);
+ /* Probe and Check faults */
+ bscv_enter(ssp);
+ async_reg = bscv_probe(ssp, chan_general, &fault);
+ bscv_trace(ssp, 'D', "bscv_event_daemon",
+ "process event: async_reg 0x%x, fault 0x%x",
+ async_reg, fault);
+
+ if (!fault) {
+ /* Treat non-fault conditions */
+
+ if (ssp->cssp_prog || ssp->prog_mode_only) {
+ /*
+ * The BSC has become available again.
+ */
+ fault_cnt = 0;
+ ssp->cssp_prog = B_FALSE;
+ ssp->prog_mode_only = B_FALSE;
+ (void) bscv_attach_common(ssp);
+ } else if (fault_cnt > 0) {
+ /* Previous fault has cleared */
+ bscv_clear_fault(ssp);
+ fault_cnt = 0;
+ cmn_err(CE_WARN,
+ "!bscv_event_daemon previous fault "
+ "cleared.");
+ } else if (bscv_faulty(ssp)) {
+ /* Previous fault has cleared */
+ bscv_clear_fault(ssp);
+ /* Sleep to avoid busy waiting */
+ ssp->event_sleep = B_TRUE;
+ }
+ poll_period = BSC_EVENT_POLL_NORMAL;
+
+ if (async_reg) {
+ ssp->status_change = B_TRUE;
+ ssp->event_waiting = B_TRUE;
+ }
+ } else if (ssp->cssp_prog) {
+ /*
+ * Expect radio silence or error values
+ * when the CSSP is upgrading the BSC firmware
+ * so throw away any fault indication.
+ */
+ fault = B_FALSE;
+ } else if (fault_cnt == BSC_PROBE_FAULT_LIMIT) {
+ /* Count previous faults and maybe fail */
+ /* Declare the lom broken */
+ bscv_set_fault(ssp);
+ poll_period = BSC_EVENT_POLL_FAULTY;
+ cmn_err(CE_WARN,
+ "!bscv_event_daemon had faults probing "
+ "lom - marking it as faulty.");
+ /*
+ * Increment fault_cnt to ensure that
+ * next time we do not report a message
+ * i.e. we drop out of the bottom
+ */
+ fault_cnt = BSC_PROBE_FAULT_LIMIT + 1;
+ ssp->event_sleep = B_TRUE;
+ } else if (fault_cnt < BSC_PROBE_FAULT_LIMIT) {
+ if (bscv_faulty(ssp)) {
+ poll_period = BSC_EVENT_POLL_FAULTY;
+ /*
+ * No recovery messages in this case
+ * because there was never a fault
+ * message here.
+ */
+ fault_cnt = 0;
+ } else {
+ /* Getting ready to explode */
+ fault_cnt++;
+ cmn_err(CE_WARN,
+ "!bscv_event_daemon had fault 0x%x",
+ fault);
+ }
+ ssp->event_sleep = B_TRUE;
+ }
+ bscv_exit(ssp);
+ mutex_enter(&ssp->task_mu);
+ }
+
+#if defined(__i386) || defined(__amd64)
+ /*
+ * we have no platmod hook on Solaris x86 to report
+ * a change to the nodename so we keep a copy so
+ * we can detect a change and request that the bsc
+ * be updated when appropriate.
+ */
+ if (strcmp(ssp->last_nodename, utsname.nodename) != 0) {
+
+ bscv_trace(ssp, 'X', "bscv_event_daemon",
+ "utsname.nodename='%s' possible change detected",
+ utsname.nodename);
+ ssp->nodename_change = B_TRUE;
+ (void) strncpy(ssp->last_nodename, utsname.nodename,
+ sizeof (ssp->last_nodename));
+ /* enforce null termination */
+ ssp->last_nodename[sizeof (ssp->last_nodename) - 1] =
+ '\0';
+ }
+#endif /* __i386 || __amd64 */
+
+ if (((ssp->task_flags & TASK_PAUSE_FLG) == 0) &&
+ fault_cnt == 0 && ssp->cssp_prog == B_FALSE &&
+ (ssp->event_waiting || ssp->status_change ||
+ ssp->nodename_change || ssp->watchdog_change)) {
+
+ do_events = ssp->event_waiting;
+ ssp->event_waiting = B_FALSE;
+ ssp->task_flags |= do_events ?
+ TASK_EVENT_PENDING_FLG : 0;
+ do_status = ssp->status_change;
+ ssp->status_change = B_FALSE;
+ do_nodename = ssp->nodename_change;
+ ssp->nodename_change = B_FALSE;
+ do_watchdog = ssp->watchdog_change;
+ if (ssp->watchdog_change) {
+ ssp->watchdog_change = B_FALSE;
+ }
+
+ mutex_exit(&ssp->task_mu);
+ /*
+ * We must not hold task_mu whilst processing
+ * events because this can lead to priority
+ * inversion and hence our interrupts getting
+ * locked out.
+ */
+ bscv_enter(ssp);
+ if (do_events) {
+ bscv_event_process(ssp, do_events);
+ }
+ if (do_nodename) {
+ bscv_trace(ssp, 'D', "bscv_event_daemon",
+ "do_nodename task");
+ bscv_setup_hostname(ssp);
+ }
+ if (do_watchdog) {
+ bscv_trace(ssp, 'D', "bscv_event_daemon",
+ "do_watchdog task");
+ bscv_setup_watchdog(ssp);
+ }
+ /*
+ * Pending status changes are dealt with last because
+ * if we see that the BSC is about to be programmed,
+ * then it will expect us to to quiescent in the
+ * first second so it can cleanly tear down its comms
+ * protocols; this takes ~100 ms.
+ */
+ if (do_status) {
+ bscv_get_state_changes(ssp);
+ }
+ if (bscv_session_error(ssp)) {
+ /*
+ * Had fault during event session. We always
+ * sleep after one of these because there
+ * may be a problem with the lom which stops
+ * us doing useful work in the event daemon.
+ * If we don't sleep then we may livelock.
+ */
+ bscv_trace(ssp, 'D', "bscv_event_daemon",
+ "had session error - sleeping");
+ ssp->event_sleep = B_TRUE;
+ }
+ bscv_exit(ssp);
+
+ mutex_enter(&ssp->task_mu);
+
+ if (ssp->task_flags & TASK_EVENT_PENDING_FLG) {
+ /*
+ * We have read any events which were
+ * pending. Let the consumer continue.
+ * Ignore the race condition with new events
+ * arriving - just let the consumer have
+ * whatever was pending when they asked.
+ */
+ ssp->event_active_count++;
+ ssp->task_flags &= ~(TASK_EVENT_PENDING_FLG |
+ TASK_EVENT_CONSUMER_FLG);
+ cv_broadcast(&ssp->task_evnt_cv);
+ }
+ } else {
+ /* There was nothing to do - sleep */
+ ssp->event_sleep = B_TRUE;
+ }
+
+ if (ssp->event_sleep) {
+ ssp->task_flags |= TASK_SLEEPING_FLG;
+ /* Sleep until there is something to do */
+ (void) cv_timedwait(&ssp->task_cv,
+ &ssp->task_mu,
+ poll_period + ddi_get_lbolt());
+ ssp->task_flags &= ~TASK_SLEEPING_FLG;
+ ssp->event_sleep = B_FALSE;
+ }
+ }
+
+ if (ssp->task_flags & TASK_EVENT_CONSUMER_FLG) {
+ /*
+ * We are going away so wake up any event consumer.
+ * Pretend that any pending events have been processed.
+ */
+ ssp->event_active_count += 2;
+ cv_broadcast(&ssp->task_evnt_cv);
+ }
+
+ ASSERT(!(ssp->task_flags & TASK_EVENT_PENDING_FLG));
+ ssp->task_flags &=
+ ~(TASK_STOP_FLG | TASK_ALIVE_FLG | TASK_EVENT_CONSUMER_FLG);
+ mutex_exit(&ssp->task_mu);
+
+ bscv_trace(ssp, 'D', "bscv_event_daemon",
+ "exiting.");
+}
+
+/*
+ * function - bscv_start_event_daemon
+ * description - Create the event daemon thread.
+ * inputs - LOM soft state structure pointer
+ * outputs - none
+ */
+static void
+bscv_start_event_daemon(bscv_soft_state_t *ssp)
+{
+ if (ssp->progress & BSCV_THREAD)
+ return;
+
+ /* Start the event thread after the queue has started */
+ (void) thread_create(NULL, 0, (void (*)())bscv_event_daemon, ssp,
+ 0, &p0, TS_RUN, minclsyspri);
+
+ ssp->progress |= BSCV_THREAD;
+}
+
+/*
+ * function - bscv_stop_event_daemon
+ * description - Attempt to stop the event daemon thread.
+ * inputs - LOM soft state structure pointer
+ * outputs - DDI_SUCCESS OR DDI_FAILURE
+ */
+static int
+bscv_stop_event_daemon(bscv_soft_state_t *ssp)
+{
+ int try;
+ int res = DDI_SUCCESS;
+
+ mutex_enter(&ssp->task_mu);
+
+ /* Wait for task daemon to stop running. */
+ for (try = 0;
+ ((ssp->task_flags & TASK_ALIVE_FLG) && try < 10);
+ try++) {
+ /* Signal that the task daemon should stop */
+ ssp->task_flags |= TASK_STOP_FLG;
+ cv_signal(&ssp->task_cv);
+ /* Release task daemon lock. */
+ mutex_exit(&ssp->task_mu);
+ /*
+ * TODO - when the driver is modified to support
+ * system suspend or if this routine gets called
+ * during panic we should use drv_usecwait() rather
+ * than delay in those circumstances.
+ */
+ delay(drv_usectohz(1000000));
+ mutex_enter(&ssp->task_mu);
+ }
+
+ if (ssp->task_flags & TASK_ALIVE_FLG) {
+ res = DDI_FAILURE;
+ }
+ mutex_exit(&ssp->task_mu);
+
+ return (res);
+}
+
+/*
+ * function - bscv_pause_event_daemon
+ * description - Attempt to pause the event daemon thread.
+ * inputs - LOM soft state structure pointer
+ * outputs - DDI_SUCCESS OR DDI_FAILURE
+ */
+static int
+bscv_pause_event_daemon(bscv_soft_state_t *ssp)
+{
+ int try;
+
+ if (!(ssp->progress & BSCV_THREAD)) {
+ /* Nothing to do */
+ return (BSCV_SUCCESS);
+ }
+
+ bscv_trace(ssp, 'D', "bscv_pause_event_daemon",
+ "Attempting to pause event daemon");
+
+ mutex_enter(&ssp->task_mu);
+ /* Signal that the task daemon should pause */
+ ssp->task_flags |= TASK_PAUSE_FLG;
+
+ /* Wait for task daemon to pause. */
+ for (try = 0;
+ (!(ssp->task_flags & TASK_SLEEPING_FLG) &&
+ (ssp->task_flags & TASK_ALIVE_FLG) &&
+ try < 10);
+ try++) {
+ /* Paranoia */
+ ssp->task_flags |= TASK_PAUSE_FLG;
+ cv_signal(&ssp->task_cv);
+ /* Release task daemon lock. */
+ mutex_exit(&ssp->task_mu);
+ delay(drv_usectohz(1000000));
+ mutex_enter(&ssp->task_mu);
+ }
+ if ((ssp->task_flags & TASK_SLEEPING_FLG) ||
+ !(ssp->task_flags & TASK_ALIVE_FLG)) {
+ mutex_exit(&ssp->task_mu);
+ bscv_trace(ssp, 'D', "bscv_pause_event_daemon",
+ "Pause event daemon - success");
+ return (BSCV_SUCCESS);
+ }
+ mutex_exit(&ssp->task_mu);
+ bscv_trace(ssp, 'D', "bscv_pause_event_daemon",
+ "Pause event daemon - failed");
+ return (BSCV_FAILURE);
+}
+
+/*
+ * function - bscv_resume_event_daemon
+ * description - Resumethe event daemon thread.
+ * inputs - LOM soft state structure pointer
+ * outputs - None.
+ */
+static void
+bscv_resume_event_daemon(bscv_soft_state_t *ssp)
+{
+ if (!(ssp->progress & BSCV_THREAD)) {
+ /* Nothing to do */
+ return;
+ }
+
+ mutex_enter(&ssp->task_mu);
+ /* Allow the task daemon to resume event processing */
+ ssp->task_flags &= ~TASK_PAUSE_FLG;
+ cv_signal(&ssp->task_cv);
+ mutex_exit(&ssp->task_mu);
+
+ bscv_trace(ssp, 'D', "bscv_pause_event_daemon",
+ "Event daemon resumed");
+}
+
+/*
+ * function - bscv_event_process
+ * description - process (report) events
+ * inputs - Soft state ptr, process event request
+ * outputs - none
+ */
+static void
+bscv_event_process(bscv_soft_state_t *ssp, boolean_t do_events)
+{
+ uint32_t currptr;
+ unsigned int count;
+
+ /* Raw values read from the lom */
+ uint8_t evcount;
+ uint16_t logptr;
+
+ lom_event_t event;
+
+ if (do_events) {
+ /*
+ * Read count, next event ptr MSB,LSB. Note a read of count
+ * latches values for the next event ptr
+ */
+ evcount = bscv_get8(ssp, chan_general, EBUS_IDX_UNREAD_EVENTS);
+ logptr = bscv_get16(ssp, chan_general, EBUS_IDX_LOG_PTR_HI);
+
+ /* Sanity check the values from the lom */
+ count = bscv_event_validate(ssp, logptr, evcount);
+
+ if (count == -1) {
+ /*
+ * Nothing to do - or badly configured event log.
+ * We really do not want to touch the lom in this
+ * case because any data that we access may be bad!
+ * This differs from zero because if we have zero
+ * to read the lom probably things that unread is
+ * non-zero and we want that to be set to zero!
+ * Signal event fault to make the thread wait
+ * before attempting to re-read the log.
+ */
+ ssp->event_sleep = B_TRUE;
+
+ goto logdone;
+ }
+ if (ssp->event_fault_reported) {
+ /* Clear down any old status - things are fixed */
+ cmn_err(CE_NOTE, "Event pointer fault recovered.");
+ ssp->event_fault_reported = B_FALSE;
+ }
+
+ /* Compute the first entry that we need to read. */
+ currptr = logptr - ssp->eventlog_start;
+ currptr += ssp->eventlog_size;
+ currptr -= (count * sizeof (event));
+ currptr %= ssp->eventlog_size;
+ currptr += ssp->eventlog_start;
+
+ bscv_trace(ssp, 'E', "bscv_event_process",
+ "processing %d events from 0x%x in 0x%x:0x%x",
+ count, currptr,
+ ssp->eventlog_start,
+ ssp->eventlog_start + ssp->eventlog_size);
+
+ for (; count > 0; count--) {
+ /* Ensure window is positioned correctly */
+ if (bscv_eerw(ssp, currptr, (uint8_t *)&event,
+ sizeof (event), B_FALSE /* read */) != 0) {
+ /* Fault reading data - stop */
+ break;
+ }
+
+ bscv_event_process_one(ssp, &event);
+ bscv_sysevent(ssp, &event);
+
+ currptr += sizeof (event);
+ if (currptr >= ssp->eventlog_start +
+ ssp->eventlog_size) {
+ currptr = ssp->eventlog_start;
+ }
+ }
+ /*
+ * Clear event count - write the evcount value to remove that
+ * many from the unread total.
+ * Adjust the value to reflect how many we have left to
+ * read just in case we had a failure reading events.
+ */
+ if (count == 0) {
+ /*EMPTY*/
+ ASSERT(logptr == currptr);
+ } else if (count > evcount) {
+ evcount = 0;
+ } else {
+ evcount -= count;
+ }
+ bscv_put8(ssp, chan_general, EBUS_IDX_UNREAD_EVENTS, evcount);
+ /* Remember where we were for next time */
+ ssp->oldeeptr = currptr;
+ ssp->oldeeptr_valid = B_TRUE;
+logdone:
+ ;
+ }
+}
+
+/*
+ * function - bscv_event_validate
+ * description - validate the event data supplied by the lom and determine
+ * how many (if any) events to read.
+ * This function performs complex checks to ensure that
+ * events are not lost due to lom resets or host resets.
+ * A combination of lom reset and host reset (i.e. power fail)
+ * may cause some events to not be reported.
+ * inputs - Soft state ptr, next event pointer, number of unread events.
+ * outputs - the number of events to read. -1 on error.
+ * zero is a valid value because it forces the loms unread
+ * count to be cleared.
+ */
+static int
+bscv_event_validate(bscv_soft_state_t *ssp, uint32_t newptr, uint8_t unread)
+{
+ uint32_t oldptr;
+ unsigned int count;
+
+ if (!bscv_window_setup(ssp)) {
+ /* Problem with lom eeprom setup we cannot do anything */
+ return (-1);
+ }
+
+ /* Sanity check the event pointers */
+ if ((newptr < ssp->eventlog_start) ||
+ (newptr >= (ssp->eventlog_start + ssp->eventlog_size))) {
+ if (!ssp->event_fault_reported) {
+ cmn_err(CE_WARN, "Event pointer out of range. "
+ "Cannot read events.");
+ ssp->event_fault_reported = B_TRUE;
+ }
+ return (-1);
+ }
+ oldptr = ssp->oldeeptr;
+ /* Now sanity check log pointer against count */
+ if (newptr < oldptr) {
+ /*
+ * Must have wrapped add eventlog_size to get the
+ * correct relative values - this makes the checks
+ * below work!
+ */
+ newptr += ssp->eventlog_size;
+ }
+ if (!ssp->oldeeptr_valid) {
+ /* We have just started up - we have to trust lom */
+ count = unread;
+ } else if ((unread == 0) && (newptr == oldptr)) {
+ /* Nothing to do - we were just polling */
+ return (-1);
+ } else if (oldptr + (unread * sizeof (lom_event_t)) == newptr) {
+ /* Ok - got as many events as we expected */
+ count = unread;
+ } else if (oldptr + (unread * sizeof (lom_event_t)) > newptr) {
+ /*
+ * Errrm more messages than there should have been.
+ * Possible causes:
+ * 1. the event log has filled - we have been
+ * away for a long time
+ * 2. software bug in lom or driver.
+ * 3. something that I haven't thought of!
+ * Always warn about this we should really never
+ * see it!
+ */
+ count = (newptr - oldptr) / sizeof (lom_event_t);
+ bscv_trace(ssp, 'E', "bscv_event_process",
+ "bscv_event_process: lom reported "
+ "more events (%d) than expected (%d).",
+ unread, count);
+ cmn_err(CE_CONT, "only processing %d events", count);
+ } else {
+ /* Less messages - perhaps the lom has been reset */
+ count = (newptr - oldptr) / sizeof (lom_event_t);
+ bscv_trace(ssp, 'E', "bscv_event_process",
+ "lom reported less events (%d) than expected (%d)"
+ " - the lom may have been reset",
+ unread, count);
+ }
+ /* Whatever happens only read a maximum of 255 entries */
+ if ((count >= 0xff)) {
+ cmn_err(CE_WARN,
+ "bscv_event_process: too many events (%d) to "
+ "process - some may have been lost", count);
+ count = 0xff;
+ }
+ return (count);
+}
+
+/*
+ * function - bscv_event_process_one
+ * description - reports on state changes to the host.
+ *
+ * inputs - LOM soft state structure pointer.
+ *
+ * outputs - none.
+ */
+
+static void
+bscv_event_process_one(bscv_soft_state_t *ssp, lom_event_t *event)
+{
+ int level;
+ char eventstr[100];
+ int msg_type = 0;
+
+ if (bscv_is_null_event(ssp, event)) {
+ /* Cleared entry - do not report it */
+ return;
+ }
+
+ level = bscv_level_of_event(event);
+
+ switch (level) {
+ default:
+ msg_type = CE_NOTE;
+ break;
+
+ case EVENT_LEVEL_FATAL:
+ case EVENT_LEVEL_FAULT:
+ msg_type = CE_WARN;
+ break;
+ }
+
+ bscv_build_eventstring(ssp, event, eventstr, eventstr +
+ sizeof (eventstr));
+
+ if (level <= ssp->reporting_level) {
+ /*
+ * The message is important enough to be shown on the console
+ * as well as the log.
+ */
+ cmn_err(msg_type, "%s", eventstr);
+ } else {
+ /*
+ * The message goes only to the log.
+ */
+ cmn_err(msg_type, "!%s", eventstr);
+ }
+}
+
+/*
+ * time formats
+ *
+ * The BSC represents times as seconds since epoch 1970. Currently it gives
+ * us 32 bits, unsigned. In the future this might change to a 64-bit count,
+ * to allow a greater range.
+ *
+ * Timestamp values below BSC_TIME_SANITY do not represent an absolute time,
+ * but instead represent an offset from the last reset. This must be
+ * borne in mind by output routines.
+ */
+
+typedef uint32_t bsctime_t;
+
+#define BSC_TIME_SANITY 1000000000
+
+/*
+ * render a formatted time for display
+ */
+
+static size_t
+bscv_event_snprintgmttime(char *buf, size_t bufsz, todinfo_t t)
+{
+ int year;
+
+ /* tod_year is base 1900 so this code needs to adjust */
+ year = 1900 + t.tod_year;
+
+ return (snprintf(buf, bufsz, "%04d-%02d-%02d %02d:%02d:%02dZ",
+ year, t.tod_month, t.tod_day, t.tod_hour,
+ t.tod_min, t.tod_sec));
+}
+
+/*
+ * function - bscv_build_eventstring
+ * description - reports on state changes to the host.
+ *
+ * inputs - LOM soft state structure pointer.
+ *
+ * outputs - none.
+ */
+
+static void
+bscv_build_eventstring(bscv_soft_state_t *ssp, lom_event_t *event,
+ char *buf, char *bufend)
+{
+ uint8_t subsystem;
+ uint8_t eventtype;
+ bsctime_t bsctm;
+
+ bscv_trace(ssp, 'S', "bscv_build_eventstring", "event %2x%2x%2x%2x",
+ event->ev_subsys, event->ev_event,
+ event->ev_resource, event->ev_detail);
+ bscv_trace(ssp, 'S', "bscv_build_eventstring", "time %2x%2x%2x%2x",
+ event->ev_data[0], event->ev_data[1],
+ event->ev_data[2], event->ev_data[3]);
+
+ /*
+ * We accept bad subsystems and event type codes here.
+ * The code decodes as much as possible and then produces
+ * suitable output.
+ */
+ subsystem = EVENT_DECODE_SUBSYS(event->ev_subsys);
+ eventtype = event->ev_event;
+
+ /* time */
+ bsctm = (((uint32_t)event->ev_data[0]) << 24) |
+ (((uint32_t)event->ev_data[1]) << 16) |
+ (((uint32_t)event->ev_data[2]) << 8) |
+ ((uint32_t)event->ev_data[3]);
+ if (bsctm < BSC_TIME_SANITY) {
+ /* offset */
+ buf += snprintf(buf, bufend-buf, "+P%dd%02dh%02dm%02ds",
+ (int)(bsctm/86400), (int)(bsctm/3600%24),
+ (int)(bsctm/60%60), (int)(bsctm%60));
+ } else {
+ /* absolute time */
+ mutex_enter(&tod_lock);
+ buf += bscv_event_snprintgmttime(buf, bufend-buf,
+ utc_to_tod(bsctm));
+ mutex_exit(&tod_lock);
+ }
+ buf += snprintf(buf, bufend-buf, " ");
+
+ /* subsysp */
+ if (subsystem <
+ (sizeof (eventSubsysStrings)/sizeof (*eventSubsysStrings))) {
+ buf += snprintf(buf, bufend - buf, "%s",
+ eventSubsysStrings[subsystem]);
+ } else {
+ buf += snprintf(buf, bufend - buf,
+ "unknown subsystem %d ", subsystem);
+ }
+
+ /* resource */
+ switch (subsystem) {
+ case EVENT_SUBSYS_ALARM:
+ case EVENT_SUBSYS_TEMP:
+ case EVENT_SUBSYS_OVERTEMP:
+ case EVENT_SUBSYS_FAN:
+ case EVENT_SUBSYS_SUPPLY:
+ case EVENT_SUBSYS_BREAKER:
+ case EVENT_SUBSYS_PSU:
+ buf += snprintf(buf, bufend - buf, "%d ", event->ev_resource);
+ break;
+ case EVENT_SUBSYS_LED:
+ buf += snprintf(buf, bufend - buf, "%s ", bscv_get_label(
+ ssp->led_names, MAX_LED_ID, event->ev_resource - 1));
+ break;
+ default:
+ break;
+ }
+
+ /* fatal */
+ if (event->ev_subsys & EVENT_MASK_FAULT) {
+ if (event->ev_subsys & EVENT_MASK_FATAL) {
+ buf += snprintf(buf, bufend - buf, "FATAL FAULT: ");
+ } else {
+ buf += snprintf(buf, bufend - buf, "FAULT: ");
+ }
+ }
+
+ /* eventp */
+ if (eventtype <
+ (sizeof (eventTypeStrings)/sizeof (*eventTypeStrings))) {
+ buf += snprintf(buf, bufend - buf, "%s",
+ eventTypeStrings[eventtype]);
+ } else {
+ buf += snprintf(buf, bufend - buf,
+ "unknown event 0x%02x%02x%02x%02x",
+ event->ev_subsys, event->ev_event,
+ event->ev_resource, event->ev_detail);
+ }
+
+ /* detail */
+ switch (subsystem) {
+ case EVENT_SUBSYS_TEMP:
+ if ((eventtype != EVENT_RECOVERED) &&
+ eventtype != EVENT_DEVICE_INACCESSIBLE) {
+ buf += snprintf(buf, bufend - buf, " - %d degC",
+ (int8_t)event->ev_detail);
+ }
+ break;
+ case EVENT_SUBSYS_FAN:
+ if (eventtype == EVENT_FAILED) {
+ buf += snprintf(buf, bufend - buf,
+ " %d%%", event->ev_detail);
+ }
+ break;
+ case EVENT_SUBSYS_LOM:
+ switch (eventtype) {
+ case EVENT_FLASH_DOWNLOAD:
+ buf += snprintf(buf, bufend - buf,
+ ": v%d.%d to v%d.%d",
+ (event->ev_resource >> 4),
+ (event->ev_resource & 0x0f),
+ (event->ev_detail >> 4),
+ (event->ev_detail & 0x0f));
+ break;
+ case EVENT_WATCHDOG_TRIGGER:
+ buf += snprintf(buf, bufend - buf,
+ event->ev_detail ? "- soft" : " - hard");
+ break;
+ case EVENT_UNEXPECTED_RESET:
+ if (event->ev_detail &
+ LOM_UNEXPECTEDRESET_MASK_BADTRAP) {
+ buf += snprintf(buf, bufend - buf,
+ " - unclaimed exception 0x%x",
+ event->ev_detail &
+ ~LOM_UNEXPECTEDRESET_MASK_BADTRAP);
+ }
+ break;
+ case EVENT_RESET:
+ switch (event->ev_detail) {
+ case LOM_RESET_DETAIL_BYUSER:
+ buf += snprintf(buf, bufend - buf, " by user");
+ break;
+ case LOM_RESET_DETAIL_REPROGRAMMING:
+ buf += snprintf(buf, bufend - buf,
+ " after flash download");
+ break;
+ default:
+ buf += snprintf(buf, bufend - buf,
+ " - unknown reason");
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case EVENT_SUBSYS_LED:
+ switch (event->ev_detail) {
+ case LOM_LED_STATE_OFF:
+ buf += snprintf(buf, bufend - buf, ": OFF");
+ break;
+ case LOM_LED_STATE_ON_STEADY:
+ buf += snprintf(buf, bufend - buf, ": ON");
+ break;
+ case LOM_LED_STATE_ON_FLASHING:
+ case LOM_LED_STATE_ON_SLOWFLASH:
+ buf += snprintf(buf, bufend - buf, ": BLINKING");
+ break;
+ case LOM_LED_STATE_INACCESSIBLE:
+ buf += snprintf(buf, bufend - buf, ": inaccessible");
+ break;
+ case LOM_LED_STATE_STANDBY:
+ buf += snprintf(buf, bufend - buf, ": standby");
+ break;
+ case LOM_LED_STATE_NOT_PRESENT:
+ buf += snprintf(buf, bufend - buf, ": not present");
+ break;
+ default:
+ buf += snprintf(buf, bufend - buf, ": 0x%x",
+ event->ev_resource);
+ break;
+ }
+ break;
+ case EVENT_SUBSYS_USER:
+ switch (eventtype) {
+ case EVENT_USER_ADDED:
+ case EVENT_USER_REMOVED:
+ case EVENT_USER_PERMSCHANGED:
+ case EVENT_USER_LOGIN:
+ case EVENT_USER_PASSWORD_CHANGE:
+ case EVENT_USER_LOGINFAIL:
+ case EVENT_USER_LOGOUT:
+ buf += snprintf(buf, bufend - buf, " %d",
+ event->ev_resource);
+ default:
+ break;
+ }
+ break;
+ case EVENT_SUBSYS_PSU:
+ if (event->ev_detail & LOM_PSU_NOACCESS) {
+ buf += snprintf(buf, bufend - buf, " - inaccessible");
+ } else if ((event->ev_detail & LOM_PSU_STATUS_MASK)
+ == LOM_PSU_STATUS_MASK) {
+ buf += snprintf(buf, bufend - buf, " - OK");
+ } else {
+ buf += snprintf(buf, bufend - buf, " -");
+ /*
+ * If both inputs are seen to have failed then simply
+ * indicate that the PSU input has failed
+ */
+ if (!(event->ev_detail &
+ (LOM_PSU_INPUT_A_OK | LOM_PSU_INPUT_B_OK))) {
+ buf += snprintf(buf, bufend - buf, " Input");
+ } else {
+ /* At least one input is ok */
+ if (!(event->ev_detail & LOM_PSU_INPUT_A_OK)) {
+ buf += snprintf(buf, bufend - buf,
+ " InA");
+ }
+ if (!(event->ev_detail & LOM_PSU_INPUT_B_OK)) {
+ buf += snprintf(buf, bufend - buf,
+ " InB");
+ }
+ /*
+ * Only flag an output error if an input is
+ * still present
+ */
+ if (!(event->ev_detail & LOM_PSU_OUTPUT_OK)) {
+ buf += snprintf(buf, bufend - buf,
+ " Output");
+ }
+ }
+ buf += snprintf(buf, bufend - buf, " failed");
+ }
+ break;
+ case EVENT_SUBSYS_NONE:
+ if (eventtype == EVENT_FAULT_LED) {
+ switch (event->ev_detail) {
+ case 0:
+ buf += snprintf(buf, bufend - buf, " - ON");
+ break;
+ case 255:
+ buf += snprintf(buf, bufend - buf, " - OFF");
+ break;
+ default:
+ buf += snprintf(buf, bufend - buf,
+ " - %dHz", event->ev_detail);
+ break;
+ }
+ }
+ break;
+ case EVENT_SUBSYS_HOST:
+ if (eventtype == EVENT_BOOTMODE_CHANGE) {
+ switch (event->ev_detail &
+ ~EBUS_BOOTMODE_FORCE_CONSOLE) {
+ case EBUS_BOOTMODE_FORCE_NOBOOT:
+ buf += snprintf(buf, bufend - buf,
+ " - no boot");
+ break;
+ case EBUS_BOOTMODE_RESET_DEFAULT:
+ buf += snprintf(buf, bufend - buf,
+ " - reset defaults");
+ break;
+ case EBUS_BOOTMODE_FULLDIAG:
+ buf += snprintf(buf, bufend - buf,
+ " - full diag");
+ break;
+ case EBUS_BOOTMODE_SKIPDIAG:
+ buf += snprintf(buf, bufend - buf,
+ " - skip diag");
+ break;
+ default:
+ break;
+ }
+ }
+ if (eventtype == EVENT_SCC_STATUS) {
+ switch (event->ev_detail) {
+ case 0:
+ buf += snprintf(buf, bufend - buf,
+ " - inserted");
+ break;
+ case 1:
+ buf += snprintf(buf, bufend - buf,
+ " - removed");
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* shutd */
+ if (event->ev_subsys & EVENT_MASK_SHUTDOWN_REQD) {
+ buf += snprintf(buf, bufend - buf, " - shutdown req'd");
+ }
+
+ buf += snprintf(buf, bufend - buf, "\n");
+
+ if (buf >= bufend) {
+ /* Ensure newline at end of string */
+ bufend[-2] = '\n';
+ bufend[-1] = '\0';
+#ifdef DEBUG
+ cmn_err(CE_WARN, "!bscv_build_eventstring: buffer too small!");
+#endif /* DEBUG */
+ }
+}
+
+/*
+ * function - bscv_level_of_event
+ * description - This routine determines which level an event should be
+ * reported at.
+ * inputs - lom event structure pointer
+ * outputs - event level.
+ */
+static int
+bscv_level_of_event(lom_event_t *event)
+{
+ int level;
+ /*
+ * This is the same criteria that the firmware uses except we
+ * log the fault led on as being EVENT_LEVEL_FAULT
+ */
+ if (EVENT_DECODE_SUBSYS(event->ev_subsys) == EVENT_SUBSYS_USER) {
+ level = EVENT_LEVEL_USER;
+ } else if ((EVENT_DECODE_SUBSYS(event->ev_subsys) ==
+ EVENT_SUBSYS_ALARM) && (event->ev_event == EVENT_STATE_ON)) {
+ level = EVENT_LEVEL_FAULT;
+ } else if ((EVENT_DECODE_SUBSYS(event->ev_subsys) ==
+ EVENT_SUBSYS_NONE) &&
+ (event->ev_event == EVENT_FAULT_LED) &&
+ (event->ev_detail != 0xff)) {
+ level = EVENT_LEVEL_FAULT;
+ } else if ((EVENT_DECODE_SUBSYS(event->ev_subsys) ==
+ EVENT_SUBSYS_LOM) && event->ev_event == EVENT_TIME_REFERENCE) {
+ level = EVENT_LEVEL_NOTICE;
+ } else if (event->ev_event == EVENT_RECOVERED) {
+ /*
+ * All recovery messages need to be reported to the console
+ * because during boot, the faults which occurred whilst
+ * Solaris was not running are relayed to the console. There
+ * is a case whereby a fatal fault (eg. over temp) could
+ * have occurred and then recovered. The recovery condition
+ * needs to be reported so the user doesn't think that the
+ * failure (over temp) is still present.
+ */
+ level = EVENT_LEVEL_FAULT;
+ } else if (EVENT_DECODE_FAULT(event->ev_subsys) == 0) {
+ /* None of FAULT, FATAL or SHUTDOWN REQD are set */
+ level = EVENT_LEVEL_NOTICE;
+ } else if (EVENT_DECODE_FAULT(event->ev_subsys) == EVENT_MASK_FAULT) {
+ /* Only FAULT set i.e not FATAL or SHUTDOWN REQD */
+ level = EVENT_LEVEL_FAULT;
+ } else {
+ level = EVENT_LEVEL_FATAL;
+ }
+
+ return (level);
+}
+
+/*
+ * function - bscv_status
+ * description - This routine is called when any change in the LOMlite2 status
+ * is indicated by the status registers.
+ *
+ * inputs - LOM soft state structure pointer
+ *
+ * outputs - none.
+ */
+static void
+bscv_status(bscv_soft_state_t *ssp, uint8_t state_chng, uint8_t dev_no)
+{
+ int8_t temp;
+ uint8_t fanspeed;
+
+ ASSERT(bscv_held(ssp));
+
+ bscv_trace(ssp, 'D', "bscv_status", "state_chng 0x%x dev_no 0x%x",
+ state_chng, dev_no);
+
+ /*
+ * The device that has changed is given by the state change
+ * register and the event detail register so react
+ * accordingly.
+ */
+
+ if (state_chng == EBUS_STATE_NOTIFY) {
+ /*
+ * The BSC is indicating a self state change
+ */
+ if (dev_no == EBUS_DETAIL_FLASH) {
+ ssp->cssp_prog = B_TRUE;
+ bscv_trace(ssp, 'D', "bscv_status",
+ "ssp->cssp_prog changed to 0x%x",
+ ssp->cssp_prog);
+ /*
+ * It takes the BSC at least 100 ms to
+ * clear down the comms protocol.
+ * We back-off from talking to the
+ * BSC during this period.
+ */
+ delay(BSC_EVENT_POLL_NORMAL);
+ bscv_trace(ssp, 'D', "bscv_status",
+ "completed delay");
+ } else if (dev_no == EBUS_DETAIL_RESET) {
+ /*
+ * The bsc has reset
+ */
+ bscv_trace(ssp, 'D', "bscv_status",
+ "BSC reset occured, re-synching");
+ (void) bscv_attach_common(ssp);
+ bscv_trace(ssp, 'D', "bscv_status",
+ "completed attach_common");
+ }
+
+ }
+
+ if ((state_chng & EBUS_STATE_FAN) && ((dev_no - 1) < MAX_FANS)) {
+ fanspeed = bscv_get8(ssp, chan_general,
+ EBUS_IDX_FAN1_SPEED + dev_no - 1);
+ /*
+ * Only remember fanspeeds which are real values or
+ * NOT PRESENT values.
+ */
+ if ((fanspeed <= LOM_FAN_MAX_SPEED) ||
+ (fanspeed == LOM_FAN_NOT_PRESENT)) {
+ ssp->fanspeed[dev_no - 1] = fanspeed;
+ }
+ }
+
+ if ((state_chng & EBUS_STATE_PSU) && ((dev_no - 1) < MAX_PSUS)) {
+ (void) bscv_get8(ssp, chan_general,
+ EBUS_IDX_PSU1_STAT + dev_no - 1);
+ }
+
+ if (state_chng & EBUS_STATE_GP) {
+ (void) bscv_get8(ssp, chan_general, EBUS_IDX_GPIP);
+ }
+
+ if (state_chng & EBUS_STATE_CB) {
+ (void) bscv_get8(ssp, chan_general, EBUS_IDX_CBREAK_STATUS);
+ }
+
+ if ((state_chng & EBUS_STATE_TEMPERATURE) &&
+ ((dev_no - 1) < MAX_TEMPS)) {
+ temp = bscv_get8(ssp, chan_general,
+ EBUS_IDX_TEMP1 + dev_no - 1);
+ /*
+ * Only remember temperatures which are real values or
+ * a NOT PRESENT value.
+ */
+ if ((temp <= LOM_TEMP_MAX_VALUE) ||
+ (temp == LOM_TEMP_STATE_NOT_PRESENT)) {
+ ssp->temps.temp[dev_no - 1] = temp;
+ }
+ }
+
+ if (state_chng & EBUS_STATE_RAIL) {
+ (void) bscv_get8(ssp, chan_general, EBUS_IDX_SUPPLY_LO);
+ (void) bscv_get8(ssp, chan_general, EBUS_IDX_SUPPLY_HI);
+ }
+}
+
+char *
+bscv_get_label(char labels[][MAX_LOM2_NAME_STR], int limit, int index)
+{
+
+ if (labels == NULL)
+ return ("");
+
+ if (limit < 0 || index < 0 || index > limit)
+ return ("-");
+
+ return (labels[index]);
+}
+
+static void
+bscv_generic_sysevent(bscv_soft_state_t *ssp, char *class, char *subclass,
+ char *fru_id, char *res_id, int32_t fru_state, char *msg)
+{
+ int rv;
+ nvlist_t *attr_list;
+
+ bscv_trace(ssp, 'E', "bscv_generic_sysevent", "%s/%s:(%s,%s,%d) %s",
+ class, subclass, fru_id, res_id, fru_state, msg);
+
+
+ if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, KM_SLEEP)) {
+ bscv_trace(ssp, 'E', "bscv_generic_sysevent",
+ "nvlist alloc failure");
+ return;
+ }
+ if (nvlist_add_uint32(attr_list, ENV_VERSION, 1)) {
+ bscv_trace(ssp, 'E', "bscv_generic_sysevent",
+ "nvlist ENV_VERSION failure");
+ nvlist_free(attr_list);
+ return;
+ }
+ if (nvlist_add_string(attr_list, ENV_FRU_ID, fru_id)) {
+ bscv_trace(ssp, 'E', "bscv_generic_sysevent",
+ "nvlist ENV_FRU_ID failure");
+ nvlist_free(attr_list);
+ return;
+ }
+ if (nvlist_add_string(attr_list, ENV_FRU_RESOURCE_ID, res_id)) {
+ bscv_trace(ssp, 'E', "bscv_generic_sysevent",
+ "nvlist ENV_FRU_RESOURCE_ID failure");
+ nvlist_free(attr_list);
+ return;
+ }
+ if (nvlist_add_string(attr_list, ENV_FRU_DEVICE, ENV_RESERVED_ATTR)) {
+ bscv_trace(ssp, 'E', "bscv_generic_sysevent",
+ "nvlist ENV_FRU_DEVICE failure");
+ nvlist_free(attr_list);
+ return;
+ }
+ if (nvlist_add_int32(attr_list, ENV_FRU_STATE, fru_state)) {
+ bscv_trace(ssp, 'E', "bscv_generic_sysevent",
+ "nvlist ENV_FRU_STATE failure");
+ nvlist_free(attr_list);
+ return;
+ }
+ if (nvlist_add_string(attr_list, ENV_MSG, msg)) {
+ bscv_trace(ssp, 'E', "bscv_generic_sysevent",
+ "nvlist ENV_MSG failure");
+ nvlist_free(attr_list);
+ return;
+ }
+
+ rv = ddi_log_sysevent(ssp->dip, DDI_VENDOR_SUNW, class,
+ subclass, attr_list, NULL, DDI_SLEEP);
+
+ if (rv == DDI_SUCCESS) {
+ bscv_trace(ssp, 'E', "bscv_generic_sysevent", "sent sysevent");
+ } else {
+ cmn_err(CE_WARN, "!cannot deliver sysevent");
+ }
+
+ nvlist_free(attr_list);
+}
+
+/*
+ * function - bscv_sysevent
+ * description - send out a sysevent on the given change if needed
+ * inputs - soft state pointer, event to report
+ * outputs - none
+ */
+
+static void
+bscv_sysevent(bscv_soft_state_t *ssp, lom_event_t *event)
+{
+ char *class = NULL;
+ char *subclass = NULL;
+ char *fru_id = "Blade"; /* The blade is only one FRU */
+ char *res_id;
+ int32_t fru_state = 0;
+
+ bscv_trace(ssp, 'E', "bscv_sysevent", "processing event");
+
+ ASSERT(event != NULL);
+
+ /* Map ev_subsys to sysevent class/sub-class */
+
+ switch (EVENT_DECODE_SUBSYS(event->ev_subsys)) {
+ case EVENT_SUBSYS_NONE:
+ break;
+ case EVENT_SUBSYS_ALARM:
+ break;
+ case EVENT_SUBSYS_TEMP:
+ class = EC_ENV, subclass = ESC_ENV_TEMP;
+ res_id = bscv_get_label(ssp->temps.name, ssp->temps.num,
+ event->ev_resource - 1);
+ switch (event->ev_event) {
+ case EVENT_SEVERE_OVERHEAT:
+ fru_state = ENV_FAILED;
+ break;
+ case EVENT_OVERHEAT:
+ fru_state = ENV_WARNING;
+ break;
+ case EVENT_NO_OVERHEAT:
+ fru_state = ENV_OK;
+ break;
+ default:
+ return;
+ }
+ break;
+ case EVENT_SUBSYS_OVERTEMP:
+ break;
+ case EVENT_SUBSYS_FAN:
+ class = EC_ENV, subclass = ESC_ENV_FAN;
+ res_id = bscv_get_label(ssp->fan_names, ssp->num_fans,
+ event->ev_resource - 1);
+ switch (event->ev_event) {
+ case EVENT_FAILED:
+ fru_state = ENV_FAILED;
+ break;
+ case EVENT_RECOVERED:
+ fru_state = ENV_OK;
+ break;
+ default:
+ return;
+ }
+ break;
+ case EVENT_SUBSYS_SUPPLY:
+ class = EC_ENV, subclass = ESC_ENV_POWER;
+ res_id = bscv_get_label(ssp->sflags.name, ssp->sflags.num,
+ event->ev_resource - 1);
+ switch (event->ev_event) {
+ case EVENT_FAILED:
+ fru_state = ENV_FAILED;
+ break;
+ case EVENT_RECOVERED:
+ fru_state = ENV_OK;
+ break;
+ default:
+ return;
+ }
+ break;
+ case EVENT_SUBSYS_BREAKER:
+ break;
+ case EVENT_SUBSYS_PSU:
+ break;
+ case EVENT_SUBSYS_USER:
+ break;
+ case EVENT_SUBSYS_PHONEHOME:
+ break;
+ case EVENT_SUBSYS_LOM:
+ break;
+ case EVENT_SUBSYS_HOST:
+ break;
+ case EVENT_SUBSYS_EVENTLOG:
+ break;
+ case EVENT_SUBSYS_EXTRA:
+ break;
+ case EVENT_SUBSYS_LED:
+ if (event->ev_event != EVENT_FAULT_LED &&
+ event->ev_event != EVENT_STATE_CHANGE)
+ return;
+ /*
+ * There are 3 LEDs : Power, Service, Ready-to-Remove on a
+ * JBOS blade. We'll never report the Power since Solaris
+ * won't be running when it is _switched_ ON. Ready-to-Remove
+ * will only be lit when we're powered down which also means
+ * Solaris won't be running. We don't want to report it
+ * during system testing / Sun VTS exercising the LEDs.
+ *
+ * Therefore, we only report the Service Required LED.
+ */
+ class = EC_ENV, subclass = ESC_ENV_LED;
+ res_id = bscv_get_label(ssp->led_names, MAX_LED_ID,
+ event->ev_resource - 1);
+
+ switch (event->ev_detail) {
+ case LOM_LED_STATE_ON_STEADY:
+ fru_state = ENV_LED_ON;
+ break;
+ case LOM_LED_STATE_ON_FLASHING:
+ case LOM_LED_STATE_ON_SLOWFLASH:
+ fru_state = ENV_LED_BLINKING;
+ break;
+ case LOM_LED_STATE_OFF:
+ fru_state = ENV_LED_OFF;
+ break;
+ case LOM_LED_STATE_INACCESSIBLE:
+ fru_state = ENV_LED_INACCESSIBLE;
+ break;
+ case LOM_LED_STATE_STANDBY:
+ fru_state = ENV_LED_STANDBY;
+ break;
+ case LOM_LED_STATE_NOT_PRESENT:
+ fru_state = ENV_LED_NOT_PRESENT;
+ break;
+ default:
+ fru_state = ENV_LED_INACCESSIBLE;
+ break;
+ }
+ break;
+ default :
+ break;
+ }
+
+ if (class == NULL || subclass == NULL) {
+ bscv_trace(ssp, 'E', "bscv_sysevent", "class/subclass NULL");
+ return;
+ }
+
+ bscv_generic_sysevent(ssp, class, subclass, fru_id, res_id, fru_state,
+ ENV_RESERVED_ATTR);
+}
+
+/*
+ * *********************************************************************
+ * Firmware download (programming)
+ * *********************************************************************
+ */
+
+/*
+ * function - bscv_prog
+ * description - LOMlite2 flash programming code.
+ *
+ * bscv_prog_image - download a complete image to the lom.
+ * bscv_prog_receive_image - receive data to build up a
+ * complete image.
+ * bscv_prog_stop_lom - pause the event daemon and prepare
+ * lom for firmware upgrade.
+ * bscv_prog_start_lom - reinit the driver/lom after upgrade
+ * and restart the event daemon
+ *
+ * inputs - soft state pointer, arg ptr, ioctl mode
+ * outputs - status
+ */
+
+static int
+bscv_prog(bscv_soft_state_t *ssp, intptr_t arg, int mode)
+{
+ lom_prog_t *prog;
+ int res = 0;
+
+ /*
+ * We will get repeatedly called with bits of data first for
+ * loader, then for main image.
+ */
+ prog = (lom_prog_t *)kmem_alloc(sizeof (lom_prog_t), KM_SLEEP);
+
+ if (ddi_copyin((caddr_t)arg, (caddr_t)prog, sizeof (*prog),
+ mode) < 0) {
+ kmem_free((void *)prog, sizeof (*prog));
+ return (EFAULT);
+ }
+
+ bscv_trace(ssp, 'U', "bscv_prog",
+ "index 0x%x size 0x%x", prog->index, prog->size);
+
+ mutex_enter(&ssp->prog_mu);
+ if (prog->size == 0) {
+ if (prog->index == 2) {
+ /*
+ * This is the initial request for the chip type so we
+ * know what we are programming.
+ * The type will have been read in at init so just
+ * return it in data[0].
+ */
+ prog->data[0] = bscv_get8_cached(ssp,
+ EBUS_IDX_CPU_IDENT);
+
+ if (ddi_copyout((caddr_t)prog, (caddr_t)arg,
+ sizeof (lom_prog_t), mode) < 0) {
+ res = EFAULT;
+ }
+ } else if (prog->index == 0) {
+ res = bscv_prog_stop_lom(ssp);
+ } else if (prog->index == 1) {
+ res = bscv_prog_start_lom(ssp);
+ } else {
+ res = EINVAL;
+ }
+ } else {
+ if (ssp->image == NULL) {
+ ssp->image = (uint8_t *)kmem_zalloc(
+ BSC_IMAGE_MAX_SIZE, KM_SLEEP);
+ }
+ res = bscv_prog_receive_image(ssp, prog,
+ ssp->image, BSC_IMAGE_MAX_SIZE);
+ }
+ mutex_exit(&ssp->prog_mu);
+ kmem_free((void *)prog, sizeof (lom_prog_t));
+
+ return (res);
+}
+
+static int
+bscv_check_loader_config(bscv_soft_state_t *ssp, boolean_t is_image2)
+{
+ bscv_trace(ssp, 'U', "bscv_check_loader_config",
+ "loader_running %d, is_image2 %d",
+ ssp->loader_running, is_image2);
+
+ /*
+ * loader_running TRUE means that we have told the microcontroller to
+ * JUMP into the loader code which has been downloaded into its RAM.
+ * At this point its an error to try and download another loader. We
+ * should be downloading the actual image at this point.
+ * Conversely, it is an error to download an image when the loader is
+ * not already downloaded and the microcontroller hasn't JUMPed into it.
+ * is_image2 TRUE means the image is being downloaded.
+ * is_image2 FALSE means the loader is being downloaded.
+ */
+ if (ssp->loader_running && !is_image2) {
+ cmn_err(CE_WARN, "Attempt to download loader image "
+ "with loader image already active");
+ cmn_err(CE_CONT, "This maybe an attempt to restart a "
+ "failed firmware download - ignoring download attempt");
+ return (B_FALSE);
+ } else if (!ssp->loader_running && is_image2) {
+ cmn_err(CE_WARN, "Attempt to download firmware image "
+ "without loader image active");
+ return (B_FALSE);
+
+ }
+
+ return (B_TRUE);
+}
+
+static uint32_t
+bscv_get_pagesize(bscv_soft_state_t *ssp)
+{
+ uint32_t pagesize;
+
+ ASSERT(bscv_held(ssp));
+
+ pagesize = bscv_get32(ssp, chan_prog,
+ BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PAGE0));
+
+ bscv_trace(ssp, 'U', "bscv_get_pagesize", "pagesize 0x%x", pagesize);
+
+ return (pagesize);
+}
+
+/*
+ * Sets the pagesize, returning the old value.
+ */
+static uint32_t
+bscv_set_pagesize(bscv_soft_state_t *ssp, uint32_t pagesize)
+{
+ uint32_t old_pagesize;
+
+ ASSERT(bscv_held(ssp));
+
+ old_pagesize = bscv_get_pagesize(ssp);
+
+ /*
+ * The microcontroller remembers this value until until someone
+ * changes it.
+ */
+ bscv_put32(ssp, chan_prog,
+ BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PSIZ0), pagesize);
+
+ return (old_pagesize);
+}
+
+static uint8_t
+bscv_enter_programming_mode(bscv_soft_state_t *ssp)
+{
+ uint8_t retval;
+
+ ASSERT(bscv_held(ssp));
+
+ bscv_put8(ssp, chan_prog,
+ BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR),
+ EBUS_PROGRAM_PCR_PRGMODE_ON);
+
+ retval = bscv_get8(ssp, chan_prog, BSCVA(EBUS_CMD_SPACE_PROGRAM,
+ EBUS_PROGRAM_PCSR));
+
+ return (retval);
+}
+
+static void
+bscv_leave_programming_mode(bscv_soft_state_t *ssp, boolean_t with_jmp)
+{
+ uint8_t reg;
+ ASSERT(bscv_held(ssp));
+
+ if (with_jmp) {
+ reg = EBUS_PROGRAM_PCR_PROGOFF_JUMPTOADDR;
+ bscv_trace(ssp, 'U', "bscv_leave_programming_mode",
+ "jumptoaddr");
+ } else {
+ reg = EBUS_PROGRAM_PCR_PRGMODE_OFF;
+ bscv_trace(ssp, 'U', "bscv_leave_programming_mode",
+ "prgmode_off");
+ }
+
+ bscv_put8(ssp, chan_prog,
+ BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR), reg);
+}
+
+
+static void
+bscv_set_jump_to_addr(bscv_soft_state_t *ssp, uint32_t loadaddr)
+{
+ ASSERT(bscv_held(ssp));
+
+ bscv_put32(ssp, chan_prog,
+ BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PADR0), loadaddr);
+
+ bscv_trace(ssp, 'U', "bscv_set_jump_to_addr",
+ "set jump to loadaddr 0x%x", loadaddr);
+}
+
+static uint8_t
+bscv_erase_once(bscv_soft_state_t *ssp, uint32_t loadaddr, uint32_t image_size)
+{
+ uint8_t retval;
+
+ ASSERT(bscv_held(ssp));
+
+ /*
+ * write PADR, PSIZ to define area to be erased
+ * We do not send erase for zero size because the current
+ * downloader gets this wrong
+ */
+
+ /*
+ * start at 0
+ */
+ bscv_trace(ssp, 'U', "bscv_erase_once", "sending erase command");
+
+ bscv_put32(ssp, chan_prog,
+ BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PADR0),
+ loadaddr);
+
+ /* set PSIZ to full size of image to be programmed */
+ bscv_put32(ssp, chan_prog,
+ BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PSIZ0),
+ image_size);
+
+ /* write ERASE to PCSR */
+ bscv_put8(ssp, chan_prog,
+ BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR),
+ EBUS_PROGRAM_PCR_ERASE);
+
+ /* read PCSR to check status */
+ retval = bscv_get8(ssp, chan_prog,
+ BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR));
+ return (retval);
+}
+
+static uint8_t
+bscv_do_erase(bscv_soft_state_t *ssp, uint32_t loadaddr, uint32_t image_size,
+ boolean_t is_image2)
+{
+ int retryable = BSC_ERASE_RETRY_LIMIT;
+ uint8_t retval;
+
+ while (retryable--) {
+ retval = bscv_erase_once(ssp, loadaddr, image_size);
+ if (PSR_SUCCESS(retval))
+ break;
+ else
+ cmn_err(CE_WARN, "erase error 0x%x, attempt %d"
+ ", base 0x%x, size 0x%x, %s image",
+ retval, BSC_ERASE_RETRY_LIMIT - retryable,
+ loadaddr, image_size,
+ is_image2 ? "main" : "loader");
+ }
+
+ return (retval);
+}
+
+static uint8_t
+bscv_set_page(bscv_soft_state_t *ssp, uint32_t addr)
+{
+ uint32_t retval;
+ int retryable = BSC_PAGE_RETRY_LIMIT;
+
+ ASSERT(bscv_held(ssp));
+
+ while (retryable--) {
+
+ /*
+ * Write the page address and read it back for confirmation.
+ */
+ bscv_put32(ssp, chan_prog,
+ BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PADR0),
+ addr);
+ retval = bscv_get32(ssp, chan_prog,
+ BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PADR0));
+
+ if (retval == addr)
+ break;
+ else {
+ cmn_err(CE_WARN, "programmming error, attempt %d, "
+ "set page 0x%x, read back 0x%x",
+ BSC_PAGE_RETRY_LIMIT - retryable,
+ addr, retval);
+ }
+ }
+ return ((addr == retval) ? EBUS_PROGRAM_PSR_SUCCESS :
+ EBUS_PROGRAM_PSR_INVALID_OPERATION);
+}
+
+static uint8_t
+bscv_do_page_data_once(bscv_soft_state_t *ssp, uint32_t index,
+ uint32_t image_size, uint32_t pagesize, uint8_t *imagep,
+ uint16_t *calcd_chksum)
+{
+ uint32_t size;
+ uint16_t chksum;
+ int i;
+ uint8_t retval;
+
+ ASSERT(bscv_held(ssp));
+
+ bscv_trace(ssp, 'P', "bscv_do_page_data_once", "index 0x%x", index);
+
+ /* write PSIZ bytes to PDAT */
+ if (index + pagesize < image_size) {
+ bscv_rep_rw8(ssp, chan_prog, imagep + index,
+ BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_DATA),
+ pagesize, DDI_DEV_NO_AUTOINCR, B_TRUE /* write */);
+ size = pagesize;
+ } else {
+ bscv_trace(ssp, 'P', "bscv_do_page_once",
+ "Sending last block, last 0x%x bytes",
+ (image_size % pagesize));
+ size = (image_size - index);
+ bscv_rep_rw8(ssp, chan_prog, imagep + index,
+ BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_DATA),
+ size, DDI_DEV_NO_AUTOINCR, B_TRUE /* write */);
+ /* Now pad the rest of the page with zeros */
+ for (i = size; i < pagesize; i++) {
+ bscv_put8(ssp, chan_prog,
+ BSCVA(EBUS_CMD_SPACE_PROGRAM,
+ EBUS_PROGRAM_DATA),
+ 0);
+ }
+ }
+
+ /* write the checksum to PCSM */
+ chksum = 0;
+ for (i = 0; i < size; i++) {
+ chksum = ((chksum << 3) | (chksum >> 13)) ^
+ *(imagep + index + i);
+ }
+ /* Cope with non-pagesize sized bufers */
+ for (; i < pagesize; i++) {
+ chksum = ((chksum << 3) | (chksum >> 13)) ^ 0;
+ }
+ bscv_put16(ssp, chan_prog,
+ BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSM0), chksum);
+
+ bscv_put8(ssp, chan_prog,
+ BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR),
+ EBUS_PROGRAM_PCR_PROGRAM);
+
+ retval = bscv_get8(ssp, chan_prog,
+ BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR));
+
+ *calcd_chksum = chksum;
+ return (retval);
+}
+
+static uint8_t bscv_do_page(bscv_soft_state_t *ssp, uint32_t loadaddr,
+ uint32_t index, uint32_t image_size, uint32_t pagesize, uint8_t *imagep,
+ boolean_t is_image2)
+{
+ int retryable = BSC_PAGE_RETRY_LIMIT;
+ uint8_t retval;
+ uint16_t checksum;
+
+ bscv_trace(ssp, 'P', "bscv_do_page", "index 0x%x", index);
+
+ while (retryable--) {
+ /*
+ * Set the page address (with retries). If this is not
+ * successful, then there is no point carrying on and sending
+ * the page's data since that could cause random memory
+ * corruption in the microcontroller.
+ */
+ retval = bscv_set_page(ssp, loadaddr + index);
+ if (!PSR_SUCCESS(retval)) {
+ cmn_err(CE_WARN, "programming error 0x%x, "
+ "could not setup page address 0x%x, %s image",
+ retval, loadaddr + index,
+ is_image2 ? "main" : "loader");
+ break;
+ }
+
+ /*
+ * Send down the data for the page
+ */
+
+ bscv_trace(ssp, 'P', "bscv_do_page", "sending data for page");
+
+ retval = bscv_do_page_data_once(ssp, index, image_size,
+ pagesize, imagep, &checksum);
+ if (PSR_SUCCESS(retval))
+ break;
+ else
+ cmn_err(CE_WARN, "programming error 0x%x,"
+ " attempt %d, index 0x%x, checksum 0x%x, %s image",
+ retval, BSC_PAGE_RETRY_LIMIT - retryable,
+ index, checksum, is_image2 ? "main" : "loader");
+ }
+
+ bscv_trace(ssp, 'U', "bscv_do_page", "Returning 0x%x for index 0x%x,"
+ " checksum 0x%x, %s image", retval, index, checksum,
+ is_image2 ? "main" : "loader");
+
+ return (retval);
+}
+
+static uint8_t
+bscv_do_pages(bscv_soft_state_t *ssp, uint32_t loadaddr, uint32_t image_size,
+ uint32_t pagesize, uint8_t *imagep, boolean_t is_image2)
+{
+ uint8_t retval;
+ uint32_t index;
+
+ bscv_trace(ssp, 'P', "bscv_do_pages", "entered");
+
+ for (index = 0; index < image_size; index += pagesize) {
+ retval = bscv_do_page(ssp, loadaddr, index, image_size,
+ pagesize, imagep, is_image2);
+ if (bscv_faulty(ssp) || !PSR_SUCCESS(retval)) {
+ bscv_trace(ssp, 'U', "bscv_do_pages",
+ "Failed to program lom (status 0x%x)", retval);
+ break;
+ }
+ }
+
+ return (retval);
+}
+
+static int
+bscv_prog_image(bscv_soft_state_t *ssp, boolean_t is_image2,
+ uint8_t *imagep, int image_size, uint32_t loadaddr)
+{
+ uint32_t pagesize;
+ int res = 0;
+ uint8_t retval;
+
+ bscv_trace(ssp, 'U', "bscv_prog_image",
+ "image 0x%x, imagep %p, size 0x%x",
+ is_image2 ? 2 : 1, imagep, image_size);
+
+ if (!bscv_check_loader_config(ssp, is_image2))
+ /*
+ * Return no error to allow userland to continue on with
+ * downloading the image.
+ */
+ return (0);
+
+ bscv_enter(ssp);
+
+ pagesize = bscv_get_pagesize(ssp);
+
+ retval = bscv_enter_programming_mode(ssp);
+ if (bscv_faulty(ssp) || !PSR_PROG(retval)) {
+ cmn_err(CE_WARN, "lom: Failed to enter program mode, error 0x%x"
+ ", %s image", retval, is_image2 ? "main" : "loader");
+ res = EIO;
+ goto BSCV_PROG_IMAGE_END;
+ }
+ bscv_trace(ssp, 'U', "bscv_prog_image", "entered programming mode");
+
+ /*
+ * Only issue an erase if we are downloading the image. The loader
+ * does not need this step.
+ */
+ if (is_image2 && (image_size != 0)) {
+ retval = bscv_do_erase(ssp, loadaddr, image_size, is_image2);
+ if (bscv_faulty(ssp) || !PSR_SUCCESS(retval)) {
+ cmn_err(CE_WARN,
+ "lom: Erase failed during programming, status 0x%x",
+ retval);
+ res = EIO;
+ goto BSCV_PROG_IMAGE_END;
+ } else {
+ bscv_trace(ssp, 'U', "bscv_prog_image",
+ "erase complete - programming...");
+
+ }
+ }
+
+ (void) bscv_set_pagesize(ssp, pagesize);
+
+ retval = bscv_do_pages(ssp, loadaddr, image_size, pagesize, imagep,
+ is_image2);
+ if (bscv_faulty(ssp) || !PSR_SUCCESS(retval)) {
+ bscv_trace(ssp, 'U', "bscv_prog_image",
+ "Failed to program lom (status 0x%x)", retval);
+ res = EIO;
+ goto BSCV_PROG_IMAGE_END;
+ }
+
+BSCV_PROG_IMAGE_END:
+ if (res == 0 && !is_image2) {
+ /*
+ * We've downloaded the loader successfully. Now make the
+ * microcontroller jump to it.
+ */
+ bscv_set_jump_to_addr(ssp, loadaddr);
+ ssp->loader_running = B_TRUE;
+ bscv_leave_programming_mode(ssp, B_TRUE);
+ } else {
+ /*
+ * We've just downloaded either the loader which failed, or
+ * the image (which may or may not have been successful).
+ */
+ bscv_set_jump_to_addr(ssp, 0);
+
+ if (res != 0) {
+ bscv_trace(ssp, 'U', "bscv_prog_image",
+ "got error 0x%x - leaving programming mode",
+ res);
+ cmn_err(CE_WARN, "programming error 0x%x, %s image",
+ res, is_image2 ? "main" : "loader");
+ } else {
+ bscv_trace(ssp, 'U', "bscv_prog_image",
+ "programming complete - leaving programming mode");
+ }
+
+ bscv_leave_programming_mode(ssp, B_FALSE);
+ ssp->loader_running = B_FALSE;
+ }
+
+ bscv_exit(ssp);
+
+ return (res);
+}
+
+
+static int
+bscv_prog_receive_image(bscv_soft_state_t *ssp, lom_prog_t *prog,
+ uint8_t *imagep, int max_size)
+{
+ int res = 0;
+ uint_t size;
+ int32_t loadaddr;
+ lom_prog_data_t *prog_data;
+
+ if ((prog->index & 0x7FFF) != ssp->prog_index) {
+ bscv_trace(ssp, 'U', "bscv_prog_receive_image",
+ "Got wrong buffer 0x%x, expected 0x%x",
+ prog->index & 0x7fff, ssp->prog_index);
+ return (EINVAL);
+ }
+
+ /*
+ * We want to get the whole image and then do the download.
+ * It is assumed the device is now in programming mode.
+ */
+
+ if ((prog->index & 0x7fff) == 0) {
+ /* Starting a new image */
+ ssp->image_ptr = 0;
+ }
+
+ if ((ssp->image_ptr + prog->size) > max_size) {
+ cmn_err(CE_WARN,
+ "lom image exceeded maximum size: got 0x%x, maximum 0x%x",
+ (ssp->image_ptr + prog->size), max_size);
+ return (EFAULT);
+ }
+ bcopy(prog->data, &imagep[ssp->image_ptr], prog->size);
+ ssp->image_ptr += prog->size;
+
+ ssp->prog_index++;
+
+ if (prog->index & 0x8000) {
+ /*
+ * OK we have the whole image so synch up and start download.
+ */
+ prog_data = (lom_prog_data_t *)imagep;
+ if (prog_data->header.magic != PROG_MAGIC) {
+ /* Old style programming data */
+ /* Take care image may not fill all of structure */
+
+ /* sign extend loadaddr from 16 to 32 bits */
+ loadaddr = (int16_t)((uint16_t)((imagep[2] << 8) +
+ imagep[3]));
+
+ size = (imagep[0] << 8) + imagep[1];
+ if (size != (ssp->image_ptr - 4)) {
+ cmn_err(CE_WARN, "Image size mismatch:"
+ " expected 0x%x, got 0x%x",
+ size, (ssp->image_ptr - 1));
+ }
+
+ res = bscv_prog_image(ssp,
+ ssp->image2_processing,
+ imagep + 4, ssp->image_ptr - 4, loadaddr);
+
+ /*
+ * Done the loading so set the flag to say we are doing
+ * the other image.
+ */
+ ssp->image2_processing = !ssp->image2_processing;
+ } else if ((ssp->image_ptr < sizeof (*prog_data)) ||
+ (prog_data->platform.bscv.size !=
+ (ssp->image_ptr - sizeof (*prog_data)))) {
+ /* Image too small for new style image */
+ cmn_err(CE_WARN, "image too small");
+ res = EINVAL;
+ } else {
+ /* New style programming image */
+ switch (prog_data->platmagic) {
+ case PROG_PLAT_BSCV_IMAGE:
+ res = bscv_prog_image(ssp, B_TRUE,
+ imagep + sizeof (*prog_data),
+ prog_data->platform.bscv.size,
+ prog_data->platform.bscv.loadaddr);
+ ssp->image2_processing = B_FALSE;
+ break;
+ case PROG_PLAT_BSCV_LOADER:
+ res = bscv_prog_image(ssp, B_FALSE,
+ imagep + sizeof (*prog_data),
+ prog_data->platform.bscv.size,
+ prog_data->platform.bscv.loadaddr);
+ ssp->image2_processing = B_TRUE;
+ break;
+ default:
+ cmn_err(CE_WARN, "unknown platmagic 0x%x",
+ prog_data->platmagic);
+ res = EINVAL;
+ break;
+ }
+ }
+ ssp->prog_index = 0;
+ ssp->image_ptr = 0;
+ }
+ return (res);
+}
+
+static int
+bscv_prog_stop_lom(bscv_soft_state_t *ssp)
+{
+ if (ssp->programming) {
+ /*
+ * Already programming - this may be a retry of a failed
+ * programming attempt or just a software error!
+ */
+ goto queue_stopped;
+ }
+
+ if (bscv_pause_event_daemon(ssp) == BSCV_FAILURE) {
+ bscv_trace(ssp, 'Q', "bscv_prog_stop_lom",
+ "failed to pause event daemon thread");
+ return (EAGAIN);
+ }
+
+ bscv_enter(ssp);
+
+ ssp->programming = B_TRUE;
+
+ bscv_exit(ssp);
+
+queue_stopped:
+
+ ssp->prog_index = 0;
+ ssp->image2_processing = B_FALSE;
+
+ return (0);
+}
+
+static int
+bscv_prog_start_lom(bscv_soft_state_t *ssp)
+{
+ int res = 0;
+
+ if (!ssp->programming) {
+ /* Not programming so this is not a valid command */
+ return (EINVAL);
+ }
+
+ if (ssp->image != NULL) {
+ kmem_free((void *)ssp->image, BSC_IMAGE_MAX_SIZE);
+ ssp->image = NULL;
+ }
+
+ /*
+ * OK we are out of reset now so:
+ * Probe the firmware and set everything up.
+ */
+
+ bscv_enter(ssp);
+
+ /* Explicit clear fault because things may have been mended now */
+ bscv_clear_fault(ssp);
+
+ if (ssp->loader_running) {
+ cmn_err(CE_WARN, "Firmware upgrade failed to exit loader - "
+ "performing forced exit");
+ /* Must try to restart the lom here. */
+ /* Ensure prog mode entry to enable PRGMODE_OFF */
+ bscv_put8(ssp, chan_prog,
+ BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR),
+ EBUS_PROGRAM_PCR_PRGMODE_ON);
+ bscv_put8(ssp, chan_prog,
+ BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR),
+ EBUS_PROGRAM_PCR_PRGMODE_OFF);
+ ssp->loader_running = B_FALSE;
+ /* give the lom chance to recover */
+ delay(drv_usectohz(5000000)); /* 5 seconds */
+ }
+
+ ssp->prog_mode_only = B_FALSE;
+ ssp->programming = B_FALSE;
+
+ if (bscv_attach_common(ssp) == DDI_FAILURE) {
+ ssp->prog_mode_only = B_TRUE;
+ res = EIO;
+ }
+
+ bscv_exit(ssp);
+
+ if (!ssp->prog_mode_only) {
+ /*
+ * Start the event thread after the queue has started
+ *
+ * Not sure if this is entirely correct because
+ * the other code at the end of bscv_attach()
+ * does not get run here.
+ */
+ bscv_start_event_daemon(ssp);
+ bscv_resume_event_daemon(ssp);
+ }
+
+ return (res);
+}
+
+
+/*
+ * *********************************************************************
+ * Attach processing
+ * *********************************************************************
+ */
+
+/*
+ * function - bscv_attach_common
+ * description - this routine co-ordinates the initialisation of the
+ * driver both at attach time and after firmware programming.
+ * sequence - bscv_setup_capability - read LOMlite2 capabilities
+ * bscv_probe_check - test comms and setup register cache
+ * bscv_setup_hostname - sync stored name in lom with nodename.
+ * bscv_setup_static_info - read device names etc.
+ * bscv_setup_events - start event daemon etc.
+ *
+ * inputs - device information structure, DDI_ATTACH command
+ * outputs - DDI_SUCCESS or DDI_FAILURE
+ */
+
+static int
+bscv_attach_common(bscv_soft_state_t *ssp)
+{
+ ASSERT(bscv_held(ssp));
+
+ bscv_trace(ssp, 'A', "bscv_attach_common:", "");
+
+ /*
+ * Set the threshold for reporting messages to the console to
+ * Warnings or higher.
+ */
+ ssp->reporting_level = 2;
+
+ /*
+ * When the system is not running the Operating System, make
+ * the microcontroller print event messages straight onto the
+ * console.
+ */
+ ssp->serial_reporting = LOM_SER_EVENTS_DEF;
+
+ /* Setup capabilities */
+ bscv_setup_capability(ssp);
+
+ if (bscv_probe_check(ssp) == DDI_FAILURE) {
+ cmn_err(CE_WARN, "BSC chip not responding");
+ /*
+ * We want lom -G to talk to this driver upon broken firmware
+ * so we prematurely return success here.
+ */
+ return (DDI_SUCCESS);
+ }
+
+ bscv_setup_hostname(ssp);
+ bscv_setup_static_info(ssp);
+ bscv_setup_events(ssp);
+
+#if defined(__i386) || defined(__amd64)
+ bscv_inform_bsc(ssp, BSC_INFORM_ONLINE);
+#endif /* __i386 || __amd64 */
+ /*
+ * Watchdog configuration and CPU signatures are sent asynchronously
+ * with respect to attach so only inform the BSC if we've already
+ * sent the data in the past.
+ */
+
+ if (ssp->progress & BSCV_WDOG_CFG)
+ bscv_setup_watchdog(ssp);
+
+#ifdef __sparc
+ if (ssp->progress & BSCV_SIG_SENT)
+ bscv_write_sig(ssp, ssp->last_sig);
+#endif /* __sparc */
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * function - bscv_cleanup
+ * description - routine that does the necessary tidying up if the attach
+ * request fails or the driver is to be detached.
+ * If the event thread has been started we may fail to
+ * stop it (because it is busy) so we fail the cleanup
+ * and hence the detach. All other calls to bscv_cleanup
+ * are done before the event daemon is started.
+ * inputs - soft state structure address.
+ * outputs - DDI_SUCCESS or DDI_FAILURE.
+ */
+
+static int
+bscv_cleanup(bscv_soft_state_t *ssp)
+{
+ int instance;
+ uint8_t bits2set;
+ uint8_t bits2clear;
+
+ instance = ssp->instance;
+
+ if (ssp->progress & BSCV_LOCKS) {
+ bscv_enter(ssp);
+ }
+
+ if (ssp->progress & BSCV_THREAD) {
+ if (bscv_stop_event_daemon(ssp) == DDI_FAILURE) {
+ /* Fail the cleanup - may be able to cleanup later */
+ if (ssp->progress & BSCV_LOCKS) {
+ bscv_exit(ssp);
+ }
+ return (DDI_FAILURE);
+ }
+ }
+
+ if (ssp->progress & BSCV_NODES) {
+ ddi_remove_minor_node(ssp->dip, NULL);
+ }
+
+ if (ssp->progress & BSCV_MAPPED_REGS) {
+ /*
+ * switch back on serial event reporting - cover all configs.
+ */
+ bits2set = 0;
+ bits2clear = 0;
+ if (ssp->serial_reporting == LOM_SER_EVENTS_ON) {
+ bits2clear |= EBUS_ALARM_NOEVENTS;
+ } else if (ssp->serial_reporting == LOM_SER_EVENTS_OFF) {
+ bits2set |= EBUS_ALARM_NOEVENTS;
+ } else if (ssp->serial_reporting == LOM_SER_EVENTS_DEF) {
+ bits2clear |= EBUS_ALARM_NOEVENTS;
+ }
+ bscv_setclear8_volatile(ssp, chan_general, EBUS_IDX_ALARM,
+ bits2set, bits2clear);
+
+ /*
+ * disable the reset function if we have enabled
+ * it. We don't want any nasty surprises like system
+ * rebooting unexpectedly. If we timeout on the busy
+ * flag we just have to carry on.
+ */
+
+ bscv_trace(ssp, 'W', "bscv_cleanup",
+ "bscv_cleanup - disable wdog");
+ if (bscv_get8_cached(ssp, EBUS_IDX_WDOG_CTRL) &
+ EBUS_WDOG_ENABLE) {
+ bscv_setclear8(ssp, chan_general, EBUS_IDX_WDOG_CTRL,
+ 0, EBUS_WDOG_RST | EBUS_WDOG_ENABLE);
+ }
+ }
+
+ /*
+ * unmap registers
+ */
+
+ if (ssp->progress & BSCV_MAPPED_REGS) {
+ bscv_unmap_regs(ssp);
+ }
+
+ /*
+ * release any memory allocated for mutexes and condition
+ * variables before deallocating the structures containing them
+ */
+
+ if (ssp->progress & BSCV_LOCKS) {
+ bscv_exit(ssp);
+ cv_destroy(&ssp->task_cv);
+ cv_destroy(&ssp->task_evnt_cv);
+ mutex_destroy(&ssp->task_mu);
+ mutex_destroy(&ssp->prog_mu);
+ mutex_destroy(&ssp->cmd_mutex);
+ }
+
+ if (ssp->image != NULL) {
+ kmem_free((void *)ssp->image, BSC_IMAGE_MAX_SIZE);
+ }
+
+#if defined(__i386) || defined(__amd64)
+ mutex_enter(&cpu_lock);
+ bscv_watchdog_cyclic_remove(ssp);
+ mutex_exit(&cpu_lock);
+#endif /* __i386 || __amd64 */
+ ddi_soft_state_free(bscv_statep, instance);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * function - bscv_setup_capability
+ * description - probe the lom find what capabilities are present for
+ * us to use.
+ * inputs - soft state ptr
+ * outputs - returns DDI_SUCCESS or DDI_FAILURE
+ */
+static void bscv_setup_capability(bscv_soft_state_t *ssp)
+{
+ ASSERT(bscv_held(ssp));
+
+ if (ssp->prog_mode_only) {
+ /* Turn off all capabilities */
+ ssp->cap0 = 0;
+ ssp->cap1 = 0;
+ ssp->cap2 = 0;
+ return;
+ }
+
+ ssp->cap0 = bscv_get8(ssp, chan_general, EBUS_IDX_CAP0);
+ ssp->cap1 = bscv_get8(ssp, chan_general, EBUS_IDX_CAP1);
+ ssp->cap2 = bscv_get8(ssp, chan_general, EBUS_IDX_CAP2);
+ if (!bscv_faulty(ssp)) {
+ bscv_trace(ssp, 'A', "bscv_setup_capability",
+ "Capability flags cap0=0x%x cap1=0x%x, cap2=0x%x",
+ ssp->cap0, ssp->cap1, ssp->cap2);
+ } else {
+ cmn_err(CE_WARN, "!Could not read capability flags");
+ ssp->cap0 = 0; ssp->cap1 = 0; ssp->cap2 = 0;
+ }
+}
+
+/*
+ * function - bscv_probe_check
+ * description - probe the lom to check for correct operation
+ * has a side effect of setting up the cached registers and
+ * updates ssp->prog_mode_only.
+ * inputs - soft state ptr
+ * outputs - returns DDI_SUCCESS or DDI_FAILURE
+ */
+
+static int bscv_probe_check(bscv_soft_state_t *ssp)
+{
+ int i;
+ uint8_t probeval;
+
+ ASSERT(bscv_held(ssp));
+
+ bscv_trace(ssp, 'A', "bscv_probe_check", "");
+
+ if (!ssp->prog_mode_only) {
+ /*
+ * Make sure probe location is OK so that we are
+ * in sync.
+ * We want to make sure that this is not faulty so we
+ * do a bscv_clear_fault to clear any existing
+ * fault records down.
+ */
+ bscv_clear_fault(ssp);
+ probeval = bscv_get8(ssp, chan_general, EBUS_IDX_PROBEAA);
+ if (bscv_faulty(ssp)) {
+ ssp->prog_mode_only = B_TRUE;
+ } else if (probeval != 0xAA) {
+ bscv_trace(ssp, 'A', "bscv_probe_check",
+ "LOMlite out of sync");
+
+ /*
+ * It may be that the LOMlite was out of
+ * sync so lets try the read again.
+ */
+ probeval = bscv_get8(ssp, chan_general,
+ EBUS_IDX_PROBEAA);
+ if (bscv_faulty(ssp)) {
+ bscv_trace(ssp, 'A', "bscv_probe_check",
+ "Init readAA1 failed");
+ ssp->prog_mode_only = B_TRUE;
+ } else if (probeval != 0xAA) {
+ /*
+ * OK that is twice we are out so I
+ * guess the LOMlite is in trouble
+ */
+ bscv_trace(ssp, 'A', "bscv_probe_check",
+ "Init readAA probe failed - got 0x%x",
+ probeval);
+ ssp->prog_mode_only = B_TRUE;
+ }
+ }
+ }
+
+ /*
+ * Read in all page zero lom registers.
+ * Read state change 1st so we dont miss anything and clear it.
+ * Note: we discard the values because we rely on bscv_get8 to
+ * setup the cache of register values.
+ */
+
+ if (!ssp->prog_mode_only) {
+ (void) bscv_get8(ssp, chan_general, EBUS_IDX_STATE_CHNG);
+ if (bscv_faulty(ssp)) {
+ bscv_trace(ssp, 'A', "bscv_probe_check",
+ "Read of state change register failed");
+ ssp->prog_mode_only = B_TRUE;
+ }
+ }
+
+ if (!ssp->prog_mode_only) {
+ for (i = 1; i < 0x80; i++) {
+ switch (i) {
+ case EBUS_IDX_STATE_CHNG:
+ case EBUS_IDX_CMD_RES:
+ case EBUS_IDX_HNAME_CHAR:
+ /*
+ * Should not read these - they have side
+ * effects.
+ */
+ break;
+ default:
+ (void) bscv_get8(ssp, chan_general, i);
+ break;
+ }
+ if (bscv_faulty(ssp)) {
+ bscv_trace(ssp, 'A', "bscv_probe_check",
+ "Initial read or register %2x failed", i);
+ ssp->prog_mode_only = B_TRUE;
+ /* Might as well give up now! */
+ break;
+ }
+ }
+ }
+
+ /*
+ * Check the probe keys so we know the lom is OK
+ */
+
+ if (!ssp->prog_mode_only) {
+ if ((bscv_get8_cached(ssp, EBUS_IDX_PROBE55) != 0x55) ||
+ (bscv_get8_cached(ssp, EBUS_IDX_PROBEAA) != 0xAA)) {
+
+ bscv_trace(ssp, 'A', "bscv_probe_check",
+ "LOMlite Probe failed");
+ for (i = 0; i < 0x8; i++) {
+ bscv_trace(ssp, 'A', "bscv_probe_check",
+ "%2x %2x %2x %2x %2x %2x %2x %2x %2x "
+ "%2x %2x %2x %2x %2x %2x %2x %2x %2x",
+ bscv_get8_cached(ssp, i),
+ bscv_get8_cached(ssp, i + 1),
+ bscv_get8_cached(ssp, i + 2),
+ bscv_get8_cached(ssp, i + 3),
+ bscv_get8_cached(ssp, i + 4),
+ bscv_get8_cached(ssp, i + 5),
+ bscv_get8_cached(ssp, i + 6),
+ bscv_get8_cached(ssp, i + 7),
+ bscv_get8_cached(ssp, i + 8),
+ bscv_get8_cached(ssp, i + 9),
+ bscv_get8_cached(ssp, i + 10),
+ bscv_get8_cached(ssp, i + 11),
+ bscv_get8_cached(ssp, i + 12),
+ bscv_get8_cached(ssp, i + 13),
+ bscv_get8_cached(ssp, i + 14),
+ bscv_get8_cached(ssp, i + 15));
+ }
+ ssp->prog_mode_only = B_TRUE;
+ }
+ }
+
+ return ((ssp->prog_mode_only == B_FALSE) ? DDI_SUCCESS : DDI_FAILURE);
+}
+
+#ifdef __sparc
+/*
+ * function - bscv_idi_set
+ * description - bscv inter driver interface set function
+ * inputs - structure which defines type of service required and data
+ * ouputs - none
+ *
+ * This is the Entry Point function for the platmod driver. It works out which
+ * X Bus channel ought to deliver the service requested.
+ */
+void
+bscv_idi_set(struct bscv_idi_info info)
+{
+ struct bscv_idi_callout *tbl;
+ boolean_t retval;
+
+ ASSERT(bscv_idi_mgr.magic == BSCV_IDI_CALLOUT_MAGIC);
+
+ if (bscv_idi_mgr.tbl == NULL) {
+ if (bscv_idi_err())
+ cmn_err(CE_WARN, "!bscv_idi_set : cannot find "
+ "bscv_callout_table");
+ return;
+ } else if (bscv_idi_mgr.valid_inst == (uint32_t)~0) {
+ if (bscv_idi_err())
+ /*
+ * This error message can appear in the context of
+ * another driver, say platmod or todblade. We want
+ * to clearly indicate the culprit driver so put in
+ * the driver name.
+ */
+ cmn_err(CE_WARN, "!bscv_idi_set : no valid "
+ "driver instance of "
+ MYNAME);
+ return;
+ }
+
+ tbl = bscv_idi_mgr.tbl;
+
+ while (tbl->type != BSCV_IDI_NULL) {
+ if (tbl->type == info.type) {
+ /*
+ * We service the request with a valid instance number
+ * for the driver.
+ */
+ retval = ((tbl->fn) (info));
+
+ /*
+ * If the request was serviced, clear any accumulated
+ * error counters so future warnings will be reported if
+ * seen.
+ */
+ if (retval == B_TRUE)
+ bscv_idi_clear_err();
+ return;
+ } else {
+ tbl++;
+ }
+ }
+
+ if (bscv_idi_err())
+ cmn_err(CE_WARN, "!bscv_idi_set : cannot match info.type %d",
+ info.type);
+}
+
+/*
+ * function - bscv_nodename_set
+ * description - notify the event thread that a nodename change has occurred.
+ * inputs - data from client driver
+ * outputs - none.
+ * side-effects - the event thread will schedule an update to the lom firmware.
+ */
+/*ARGSUSED*/
+static boolean_t
+bscv_nodename_set(struct bscv_idi_info info)
+{
+ bscv_soft_state_t *ssp;
+
+ ssp = ddi_get_soft_state(bscv_statep, bscv_idi_mgr.valid_inst);
+
+ if (ssp == NULL) {
+ if (bscv_idi_err())
+ cmn_err(CE_WARN, "!blade_nodename_set: cannot get ssp");
+ return (B_FALSE);
+ }
+
+ /* Get a lock on the SSP, notify our change, then exit */
+ mutex_enter(&ssp->task_mu);
+ ssp->nodename_change = B_TRUE;
+ cv_signal(&ssp->task_cv);
+ mutex_exit(&ssp->task_mu);
+
+ return (B_TRUE);
+}
+
+/*
+ * function - bscv_sig_set
+ * description - write a signature
+ * inputs - data from client driver
+ * outputs - none.
+ */
+static boolean_t
+bscv_sig_set(struct bscv_idi_info info)
+{
+ bscv_soft_state_t *ssp;
+ bscv_sig_t sig;
+
+ ssp = ddi_get_soft_state(bscv_statep, bscv_idi_mgr.valid_inst);
+
+ if (ssp == NULL) {
+ if (bscv_idi_err())
+ cmn_err(CE_WARN, "!blade_nodename_set: cannot get ssp");
+ return (B_FALSE);
+ }
+
+ /* Service the request */
+ bcopy(info.data, &sig, sizeof (sig));
+ bscv_enter(ssp);
+ bscv_write_sig(ssp, sig);
+ bscv_exit(ssp);
+
+ return (B_TRUE);
+}
+#endif /* __sparc */
+
+static void
+bscv_wdog_do_pat(bscv_soft_state_t *ssp)
+{
+ uint8_t pat;
+
+ /*
+ * The value of the dog pat is a sequence number which wraps around,
+ * bounded by BSCV_WDOG_PAT_SEQ_MASK.
+ */
+ pat = ssp->pat_seq++;
+ pat &= EBUS_WDOG_NB_PAT_SEQ_MASK;
+
+ /* Set top nibble to indicate a pat */
+ pat |= EBUS_WDOG_NB_PAT;
+
+ /*
+ * Now pat the dog. This exercises a special protocol in the
+ * bus nexus that offers : non-blocking IO, and timely delivery,
+ * callable from high-level interrupt context. The requirement
+ * on us is that the channel is not shared for any other use.
+ * This means for chan_wdogpat, nothing may use channel[chan].regs
+ * or channel.[chan].handle.
+ */
+
+ ddi_put8(ssp->channel[chan_wdogpat].handle,
+ ssp->channel[chan_wdogpat].regs, pat);
+
+ bscv_trace(ssp, 'W', "bscv_wdog_pat", "patted the dog with seq %d",
+ pat);
+}
+
+#ifdef __sparc
+/*
+ * function - bscv_wdog_pat
+ * description - pat the watchdog
+ * inputs - data from client driver
+ * outputs - none.
+ */
+/*ARGSUSED*/
+static boolean_t
+bscv_wdog_pat(struct bscv_idi_info info)
+{
+ /*
+ * This function remembers if it has ever been called with the
+ * configure option set.
+ */
+ bscv_soft_state_t *ssp;
+
+ ssp = ddi_get_soft_state(bscv_statep, bscv_idi_mgr.valid_inst);
+
+ if (ssp == NULL) {
+ if (bscv_idi_err())
+ cmn_err(CE_WARN, "!bscv_wdog_pat: cannot get ssp");
+ return (B_FALSE);
+ } else if (ssp->nchannels == 0) {
+ /* Didn't manage to map handles so ddi_{get,put}* broken */
+ if (bscv_idi_err())
+ cmn_err(CE_WARN, "!bscv_wdog_pat: handle not mapped");
+ return (B_FALSE);
+ }
+
+ bscv_wdog_do_pat(ssp);
+ return (B_TRUE);
+}
+
+/*
+ * function - bscv_wdog_cfg
+ * description - configure the watchdog
+ * inputs - data from client driver
+ * outputs - none.
+ */
+static boolean_t
+bscv_wdog_cfg(struct bscv_idi_info info)
+{
+ bscv_soft_state_t *ssp;
+
+ ssp = ddi_get_soft_state(bscv_statep, bscv_idi_mgr.valid_inst);
+
+ if (ssp == NULL) {
+ if (bscv_idi_err())
+ cmn_err(CE_WARN, "!bscv_wdog_cfg: cannot get ssp");
+ return (B_FALSE);
+ } else if (ssp->nchannels == 0) {
+ /* Didn't manage to map handles so ddi_{get,put}* broken */
+ if (bscv_idi_err())
+ cmn_err(CE_WARN, "!bscv_wdog_cfg: handle not mapped");
+ return (B_FALSE);
+ }
+
+ if (sizeof (bscv_wdog_t) != info.size) {
+ bscv_trace(ssp, 'W', "bscv_wdog_set", "data passed in is size"
+ " %d instead of %d", info.size,
+ sizeof (bscv_wdog_t));
+ return (B_FALSE);
+ }
+
+ bscv_trace(ssp, 'W', "bscv_wdog_cfg", "enable_wdog %s, "
+ "wdog_timeout_s %d, reset_system_on_timeout %s",
+ ((bscv_wdog_t *)info.data)->enable_wdog ? "enabled" : "disabled",
+ ((bscv_wdog_t *)info.data)->wdog_timeout_s,
+ ((bscv_wdog_t *)info.data)->reset_system_on_timeout ? "yes" : "no");
+ bscv_write_wdog_cfg(ssp,
+ ((bscv_wdog_t *)info.data)->wdog_timeout_s,
+ ((bscv_wdog_t *)info.data)->enable_wdog,
+ ((bscv_wdog_t *)info.data)->reset_system_on_timeout);
+ return (B_TRUE);
+}
+#endif /* __sparc */
+
+static void
+bscv_write_wdog_cfg(bscv_soft_state_t *ssp,
+ uint_t wdog_timeout_s,
+ boolean_t enable_wdog,
+ uint8_t reset_system_on_timeout)
+{
+ uint8_t cfg = EBUS_WDOG_NB_CFG;
+
+ /*
+ * Configure the timeout value (1 to 127 seconds).
+ * Note that a policy is implemented at the bsc/ssp which bounds
+ * the value further. The bounding here is to fit the timeout value
+ * into the 7 bits the bsc uses.
+ */
+ if (wdog_timeout_s < 1)
+ ssp->watchdog_timeout = 1;
+ else if (wdog_timeout_s > 127)
+ ssp->watchdog_timeout = 127;
+ else
+ ssp->watchdog_timeout = wdog_timeout_s;
+
+ /*
+ * Configure the watchdog on or off.
+ */
+ if (enable_wdog)
+ cfg |= EBUS_WDOG_NB_CFG_ENB;
+ else
+ cfg &= ~EBUS_WDOG_NB_CFG_ENB;
+
+ /*
+ * Configure whether the microcontroller should reset the system when
+ * the watchdog expires.
+ */
+ ssp->watchdog_reset_on_timeout = reset_system_on_timeout;
+
+ ddi_put8(ssp->channel[chan_wdogpat].handle,
+ ssp->channel[chan_wdogpat].regs, cfg);
+
+ /* have the event daemon set the timeout value and whether to reset */
+ ssp->watchdog_change = B_TRUE;
+
+ bscv_trace(ssp, 'W', "bscv_wdog_cfg",
+ "configured the dog with cfg 0x%x", cfg);
+}
+
+/*
+ * function - bscv_setup_watchdog
+ * description - setup the bsc watchdog
+ * inputs - soft state ptr
+ * outputs -
+ */
+static void bscv_setup_watchdog(bscv_soft_state_t *ssp)
+{
+ uint8_t set = 0;
+ uint8_t clear = 0;
+#ifdef __sparc
+ extern int watchdog_activated;
+#endif /* __sparc */
+
+ ASSERT(bscv_held(ssp));
+
+ /* Set the timeout */
+ bscv_put8(ssp, chan_general,
+ EBUS_IDX_WDOG_TIME, ssp->watchdog_timeout);
+
+ /* Set whether to reset the system on timeout */
+ if (ssp->watchdog_reset_on_timeout) {
+ set |= EBUS_WDOG_RST;
+ } else {
+ clear |= EBUS_WDOG_RST;
+ }
+
+ if (watchdog_activated) {
+ set |= EBUS_WDOG_ENABLE;
+ } else {
+ clear |= EBUS_WDOG_ENABLE;
+ }
+
+ /* Set other host defaults */
+ clear |= (EBUS_WDOG_BREAK_DISABLE | EBUS_WDOG_AL3_FANPSU
+ | EBUS_WDOG_AL3_WDOG);
+
+ bscv_setclear8_volatile(ssp, chan_general, EBUS_IDX_WDOG_CTRL,
+ set, clear);
+
+#if defined(__i386) || defined(__amd64)
+ /* start the cyclic based watchdog patter */
+ mutex_enter(&cpu_lock);
+ bscv_watchdog_cyclic_add(ssp);
+ mutex_exit(&cpu_lock);
+#endif /* __i386 || __amd64 */
+ ssp->progress |= BSCV_WDOG_CFG;
+}
+
+
+/*
+ * function - bscv_setup_hostname
+ * description - setup the lom hostname if different from the nodename
+ * inputs - soft state ptr
+ * outputs - none
+ */
+
+static void bscv_setup_hostname(bscv_soft_state_t *ssp)
+{
+ char host_nodename[128];
+ char lom_nodename[128];
+ size_t hostlen;
+ size_t nodelen;
+
+ ASSERT(bscv_held(ssp));
+
+ /*
+ * Check machine label is the same as the
+ * system nodename.
+ */
+ (void) strncpy(host_nodename, utsname.nodename,
+ sizeof (host_nodename));
+
+ /* read in lom hostname */
+ bscv_read_hostname(ssp, lom_nodename);
+
+ /* Enforce null termination */
+ host_nodename[sizeof (host_nodename) - 1] = '\0';
+ lom_nodename[sizeof (lom_nodename) - 1] = '\0';
+
+ hostlen = (size_t)bscv_get8(ssp, chan_general, EBUS_IDX_HNAME_LENGTH);
+ nodelen = (size_t)strlen(host_nodename);
+ if ((nodelen > 0) &&
+ ((hostlen != nodelen) || (strcmp((const char *)&lom_nodename,
+ (const char *)&host_nodename)) ||
+ (hostlen == 0))) {
+ bscv_trace(ssp, 'A', "bscv_setup_hostname",
+ "nodename(%s,%d) != bsc label(%s,%d)",
+ host_nodename, nodelen, lom_nodename, hostlen);
+
+ /* Write new label into LOM EEPROM */
+ bscv_write_hostname(ssp,
+ host_nodename,
+ (uint8_t)strlen(host_nodename));
+ }
+
+ ssp->progress |= BSCV_HOSTNAME_DONE;
+}
+
+/*
+ * function - bscv_read_hostname
+ * description - read the current hostname from the lom
+ * inputs - soft state pointer and buffer to store the hostname in.
+ * outputs - none
+ */
+
+static void
+bscv_read_hostname(bscv_soft_state_t *ssp, char *lom_nodename)
+{
+ int num_failures;
+ boolean_t needretry;
+ int length;
+ int i;
+
+ ASSERT(bscv_held(ssp));
+
+ /*
+ * We have a special failure case here because a retry of a read
+ * causes data to be lost. Thus we handle the retries ourselves
+ * and are also responsible for detemining if the lom is faulty
+ */
+ for (num_failures = 0;
+ num_failures < BSC_FAILURE_RETRY_LIMIT;
+ num_failures++) {
+ bscv_clear_fault(ssp);
+ length = bscv_get8(ssp, chan_general, EBUS_IDX_HNAME_LENGTH);
+ if (bscv_faulty(ssp)) {
+ needretry = 1;
+ } else {
+ needretry = 0;
+ for (i = 0; i < length; i++) {
+ lom_nodename[i] = bscv_get8_once(ssp,
+ chan_general, EBUS_IDX_HNAME_CHAR);
+ /* Retry on any error */
+ if (bscv_retcode(ssp) != 0) {
+ needretry = 1;
+ break;
+ }
+ }
+ /* null terminate for strcmp later */
+ lom_nodename[length] = '\0';
+ }
+ if (!needretry) {
+ break;
+ }
+ /* Force the nodename to be empty */
+ lom_nodename[0] = '\0';
+ }
+
+ if (needretry) {
+ /* Failure - we ran out of retries */
+ cmn_err(CE_WARN,
+ "bscv_read_hostname: retried %d times, giving up",
+ num_failures);
+ ssp->had_fault = B_TRUE;
+ } else if (num_failures > 0) {
+ bscv_trace(ssp, 'R', "bscv_read_hostname",
+ "retried %d times, succeeded", num_failures);
+ }
+}
+
+/*
+ * function - bscv_write_hostname
+ * description - write a new hostname to the lom
+ * inputs - soft state pointer, pointer to new name, name length
+ * outputs - none
+ */
+static void
+bscv_write_hostname(bscv_soft_state_t *ssp,
+ char *host_nodename, uint8_t length)
+{
+ int num_failures;
+ boolean_t needretry;
+ int i;
+
+ ASSERT(bscv_held(ssp));
+
+ /*
+ * We have a special failure case here because a retry of a read
+ * causes data to be lost. Thus we handle the retries ourselves
+ * and are also responsible for detemining if the lom is faulty
+ */
+ for (num_failures = 0;
+ num_failures < BSC_FAILURE_RETRY_LIMIT;
+ num_failures++) {
+ bscv_clear_fault(ssp);
+ bscv_put8(ssp, chan_general, EBUS_IDX_HNAME_LENGTH, length);
+ if (bscv_faulty(ssp)) {
+ needretry = 1;
+ } else {
+ needretry = 0;
+ for (i = 0; i < length; i++) {
+ bscv_put8_once(ssp, chan_general,
+ EBUS_IDX_HNAME_CHAR, host_nodename[i]);
+ /* Retry on any error */
+ if (bscv_retcode(ssp) != 0) {
+ needretry = 1;
+ break;
+ }
+ }
+ }
+ if (!needretry) {
+ break;
+ }
+ }
+
+ if (needretry) {
+ /* Failure - we ran out of retries */
+ cmn_err(CE_WARN,
+ "bscv_write_hostname: retried %d times, giving up",
+ num_failures);
+ ssp->had_fault = B_TRUE;
+ } else if (num_failures > 0) {
+ bscv_trace(ssp, 'R', "bscv_write_hostname",
+ "retried %d times, succeeded", num_failures);
+ }
+}
+
+/*
+ * function - bscv_setup_static_info
+ * description - read in static information from the lom at attach time.
+ * inputs - soft state ptr
+ * outputs - none
+ */
+
+static void
+bscv_setup_static_info(bscv_soft_state_t *ssp)
+{
+ uint8_t addr_space_ptr;
+ uint16_t mask;
+ uint8_t fanspeed;
+ int oldtemps[MAX_TEMPS];
+ int8_t temp;
+ int i;
+
+ ASSERT(bscv_held(ssp));
+
+ /*
+ * Finally read in some static info like device names,
+ * shutdown enabled, etc before the queue starts.
+ */
+
+ /*
+ * To get the volts static info we need address space 2
+ */
+ bzero(&ssp->volts, sizeof (lom_volts_t));
+ ssp->volts.num = EBUS_CONFIG2_NSUPPLY_DEC(
+ bscv_get8(ssp, chan_general, EBUS_IDX_CONFIG2));
+ if (ssp->volts.num > MAX_VOLTS) {
+ cmn_err(CE_WARN,
+ "lom: firmware reported too many voltage lines. ");
+ cmn_err(CE_CONT, "Reported %d, maximum is %d",
+ ssp->volts.num, MAX_VOLTS);
+ ssp->volts.num = MAX_VOLTS;
+ }
+
+ bscv_trace(ssp, 'A', "bscv_setup_static_info",
+ "num volts %d", ssp->volts.num);
+ (void) bscv_read_env_name(ssp,
+ EBUS_CMD_SPACE2,
+ EBUS_IDX2_SUPPLY_NAME_START,
+ EBUS_IDX2_SUPPLY_NAME_END,
+ ssp->volts.name,
+ ssp->volts.num);
+
+ mask = bscv_get8(ssp, chan_general, BSCVA(EBUS_CMD_SPACE2,
+ EBUS_IDX2_SUPPLY_FATAL_MASK1)) << 8;
+ mask |= bscv_get8(ssp, chan_general, BSCVA(EBUS_CMD_SPACE2,
+ EBUS_IDX2_SUPPLY_FATAL_MASK2));
+
+ for (i = 0; i < ssp->volts.num; i++) {
+ ssp->volts.shutdown_enabled[i] =
+ (((mask >> i) & 1) == 0) ? 0 : 1;
+ }
+
+ /*
+ * Get the temperature static info and populate initial temperatures.
+ * Do not destroy old temperature values if the new value is not
+ * known i.e. if the device is inaccessible.
+ */
+ bcopy(ssp->temps.temp, oldtemps, sizeof (oldtemps));
+
+ bzero(&ssp->temps, sizeof (lom_temp_t));
+ ssp->temps.num = EBUS_CONFIG2_NTEMP_DEC(
+ bscv_get8(ssp, chan_general, EBUS_IDX_CONFIG2));
+ if (ssp->temps.num > MAX_TEMPS) {
+ cmn_err(CE_WARN,
+ "lom: firmware reported too many temperatures being "
+ "monitored.");
+ cmn_err(CE_CONT, "Reported %d, maximum is %d",
+ ssp->temps.num, MAX_TEMPS);
+ ssp->temps.num = MAX_TEMPS;
+ }
+ ssp->temps.num_ov = EBUS_CONFIG3_NOTEMP_DEC(
+ bscv_get8(ssp, chan_general, EBUS_IDX_CONFIG3));
+ if (ssp->temps.num_ov > MAX_TEMPS) {
+ cmn_err(CE_WARN,
+ "lom: firmware reported too many over temperatures being "
+ "monitored.");
+ cmn_err(CE_CONT, "Reported %d, maximum is %d",
+ ssp->temps.num_ov, MAX_TEMPS);
+ ssp->temps.num_ov = MAX_TEMPS;
+ }
+ bscv_trace(ssp, 'A', "bscv_setup_static_info",
+ "num temps %d, over temps %d",
+ ssp->temps.num, ssp->temps.num_ov);
+
+ addr_space_ptr = bscv_read_env_name(ssp,
+ EBUS_CMD_SPACE4,
+ EBUS_IDX4_TEMP_NAME_START,
+ EBUS_IDX4_TEMP_NAME_END,
+ ssp->temps.name,
+ ssp->temps.num);
+
+ for (i = 0; i < ssp->temps.num; i++) {
+ ssp->temps.warning[i] = (int8_t)bscv_get8(ssp, chan_general,
+ BSCVA(EBUS_CMD_SPACE4, EBUS_IDX4_TEMP_WARN1 + i));
+
+ /*
+ * If shutdown is not enabled then set it as zero so
+ * it is not displayed by the utility.
+ */
+ if ((bscv_get8(ssp, chan_general, BSCVA(EBUS_CMD_SPACE4,
+ EBUS_IDX4_TEMP_FATAL_MASK)) >> i) & 0x01) {
+ ssp->temps.shutdown[i] = (int8_t)bscv_get8(ssp,
+ chan_general,
+ BSCVA(EBUS_CMD_SPACE4, EBUS_IDX4_TEMP_SDOWN1 + i));
+ } else {
+ ssp->temps.shutdown[i] = 0;
+ }
+ }
+
+ for (i = 0; i < ssp->temps.num; i++) {
+ temp = bscv_get8(ssp, chan_general, EBUS_IDX_TEMP1 + i);
+ if ((temp <= LOM_TEMP_MAX_VALUE) ||
+ (temp == LOM_TEMP_STATE_NOT_PRESENT)) {
+ ssp->temps.temp[i] = temp;
+ } else {
+ /* New value is not known - use old value */
+ ssp->temps.temp[i] = oldtemps[i];
+ }
+ }
+
+ /*
+ * Check for and skip a single 0xff character between the
+ * temperature and over temperature names
+ */
+ if (bscv_get8(ssp, chan_general,
+ BSCVA(EBUS_CMD_SPACE4, addr_space_ptr)) == 0xff) {
+ addr_space_ptr++;
+ }
+
+ (void) bscv_read_env_name(ssp,
+ EBUS_CMD_SPACE4,
+ addr_space_ptr,
+ EBUS_IDX4_TEMP_NAME_END,
+ ssp->temps.name_ov,
+ ssp->temps.num_ov);
+
+ /*
+ * To get the CB static info we need address space 3
+ */
+ bzero(&ssp->sflags, sizeof (lom_sflags_t));
+ ssp->sflags.num = EBUS_CONFIG3_NBREAKERS_DEC(bscv_get8(ssp,
+ chan_general, EBUS_IDX_CONFIG3));
+ if (ssp->sflags.num > MAX_STATS) {
+ cmn_err(CE_WARN,
+ "lom: firmware reported too many status flags.");
+ cmn_err(CE_CONT,
+ "Reported %d, maximum is %d",
+ ssp->sflags.num, MAX_STATS);
+ ssp->sflags.num = MAX_STATS;
+ }
+ bscv_trace(ssp, 'A', "bscv_setup_static_info",
+ "num sflags %d", ssp->sflags.num);
+
+ (void) bscv_read_env_name(ssp,
+ EBUS_CMD_SPACE3,
+ EBUS_IDX3_BREAKER_NAME_START,
+ EBUS_IDX3_BREAKER_NAME_END,
+ ssp->sflags.name,
+ ssp->sflags.num);
+
+
+ /*
+ * To get the fan static info we need address space 5
+ */
+ ssp->num_fans = EBUS_CONFIG_NFAN_DEC(
+ bscv_get8(ssp, chan_general, EBUS_IDX_CONFIG));
+ if (ssp->num_fans > MAX_FANS) {
+ cmn_err(CE_WARN,
+ "lom: firmware reported too many fans. ");
+ cmn_err(CE_CONT,
+ "Reported %d, maximum is %d",
+ ssp->num_fans, MAX_FANS);
+ ssp->num_fans = MAX_FANS;
+ }
+
+ for (i = 0; i < ssp->num_fans; i++) {
+ fanspeed = bscv_get8(ssp, chan_general,
+ EBUS_IDX_FAN1_SPEED + i);
+ if ((fanspeed <= LOM_FAN_MAX_SPEED) ||
+ (fanspeed == LOM_FAN_NOT_PRESENT)) {
+ /*
+ * Do not destroy previous values unless the
+ * value is definitive.
+ */
+ ssp->fanspeed[i] = fanspeed;
+ }
+ }
+
+ bscv_trace(ssp, 'A', "bscv_setup_static_info",
+ "num fans %d", ssp->num_fans);
+
+ (void) bscv_read_env_name(ssp,
+ EBUS_CMD_SPACE5,
+ EBUS_IDX5_FAN_NAME_START,
+ EBUS_IDX5_FAN_NAME_END,
+ ssp->fan_names,
+ ssp->num_fans);
+
+ /* Get led static information from address space 10 */
+
+ (void) bscv_read_env_name(ssp,
+ EBUS_CMD_SPACE_LEDS,
+ EBUS_IDX10_LED_NAME_START,
+ EBUS_IDX10_LED_NAME_END,
+ ssp->led_names,
+ MAX_LED_ID);
+}
+
+/*
+ * function - bscv_read_env_name
+ * description - read in static environment names
+ * warning changes address space and the caller relies
+ * on this behaviour.
+ * inputs - soft state ptr, chosen address space,
+ * start of name data, end of name data,
+ * name storage, number of names.
+ * outputs - next address for reading name data.
+ */
+
+static uint8_t
+bscv_read_env_name(bscv_soft_state_t *ssp,
+ uint8_t addr_space,
+ uint8_t addr_start,
+ uint8_t addr_end,
+ char namebuf[][MAX_LOM2_NAME_STR],
+ int numnames)
+{
+ int i;
+ int nameidx;
+ int namemax;
+ unsigned int addr_space_ptr;
+ uint8_t this_char;
+
+ ASSERT(bscv_held(ssp));
+
+ bscv_trace(ssp, 'A', "bscv_read_env_name",
+ "bscv_read_env_name, space %d, start 0x%x, end 0x%x, numnames %d",
+ addr_space, addr_start, addr_end, numnames);
+
+ addr_space_ptr = addr_start;
+
+ for (i = 0; i < numnames; i++) {
+ nameidx = 0;
+ namemax = sizeof (namebuf[i]);
+ bzero(namebuf[i], namemax);
+
+ while (addr_space_ptr <= addr_end) {
+ /*
+ * Read the current character.
+ */
+ this_char = bscv_get8(ssp, chan_general,
+ BSCVA(addr_space, addr_space_ptr));
+
+ if (this_char == 0xff) {
+ /*
+ * Ran out of names - this must
+ * be the end of the name.
+ * This is really an error because
+ * we have just seen either a non-NUL
+ * terminated string or the number of
+ * strings did not match what was
+ * reported.
+ */
+ break;
+ }
+ /*
+ * We increment the buffer pointer now so that
+ * it is ready for the next read
+ */
+ addr_space_ptr++;
+
+ if (this_char == '\0') {
+ /* Found end of string - done */
+ break;
+ }
+ if (nameidx < (namemax - 1)) {
+ /*
+ * Buffer not full - record character
+ * NOTE we always leave room for the NUL
+ * terminator.
+ */
+ namebuf[i][nameidx++] = this_char;
+ }
+ }
+ /* Ensure null termination */
+ namebuf[i][nameidx] = '\0';
+ }
+ /* Clamp addr_space_ptr to 0xff because we return uint8_t */
+ if (addr_space_ptr > 0xff) {
+ addr_space_ptr = 0xff;
+ }
+ return (addr_space_ptr);
+}
+
+/*
+ * function - bscv_setup_events
+ * description - initialise the event reporting code
+ * inputs - soft state ptr
+ * outputs - DDI_SUCCESS or DDI_FAILURE
+ */
+
+static void
+bscv_setup_events(bscv_soft_state_t *ssp)
+{
+ uint8_t bits2set;
+ uint8_t bits2clear;
+
+ ASSERT(bscv_held(ssp));
+
+ /*
+ * deal with event reporting - cover all cases
+ */
+
+ bits2set = 0;
+ bits2clear = 0;
+ if (ssp->serial_reporting == LOM_SER_EVENTS_ON) {
+ bits2clear |= EBUS_ALARM_NOEVENTS;
+ } else if (ssp->serial_reporting == LOM_SER_EVENTS_OFF) {
+ bits2set |= EBUS_ALARM_NOEVENTS;
+ } else if (ssp->serial_reporting == LOM_SER_EVENTS_DEF) {
+ bits2set |= EBUS_ALARM_NOEVENTS;
+ }
+ bscv_setclear8_volatile(ssp, chan_general, EBUS_IDX_ALARM,
+ bits2set, bits2clear);
+}
+
+#ifdef __sparc
+/*
+ * function - bscv_write_sig
+ * description - write out a signature, taking care to deal with any strange
+ * values for CPU ID
+ * inputs - soft state ptr, signature
+ * outputs - none
+ */
+static void
+bscv_write_sig(bscv_soft_state_t *ssp, bscv_sig_t s)
+{
+ ASSERT(bscv_held(ssp));
+
+ /* Upload the signature */
+ bscv_put32(ssp, chan_cpusig,
+ BSCVA(EBUS_CMD_SPACE_CPUSIG, EBUS_IDX11_CPU_SIG_MSB),
+ s.sig_info.signature);
+
+ /*
+ * We always write the CPU ID last because this tells the firmware
+ * that the signature is fully uploaded and therefore to consume the
+ * data. This is required since the signature is > 1 byte in size
+ * and we transmit data in single bytes.
+ */
+ if (s.cpu == ~0) {
+ /* ~0 means the signature applies to any CPU. */
+ bscv_put8(ssp, chan_cpusig,
+ BSCVA(EBUS_CMD_SPACE_CPUSIG, EBUS_IDX11_CPU_ID),
+ EBUS_ANY_CPU_ID);
+ } else {
+ if (s.cpu > 255) {
+ /*
+ * The CPU ID supplied is unexpectedly large. Lets
+ * just use the bottom bits, in case other high order
+ * bits are being used for special meaning.
+ */
+ cmn_err(CE_WARN, "CPU Signature ID 0x%x > 255", s.cpu);
+ s.cpu %= 256;
+ cmn_err(CE_CONT, "using ID 0x%x instead ", s.cpu);
+ }
+ bscv_put8(ssp, chan_cpusig,
+ BSCVA(EBUS_CMD_SPACE_CPUSIG, EBUS_IDX11_CPU_ID),
+ (uint8_t)s.cpu);
+ }
+
+ ssp->last_sig = s;
+ ssp->progress |= BSCV_SIG_SENT;
+}
+#endif /* __sparc */
+
+#if defined(__i386) || defined(__amd64)
+
+/*
+ * function - bscv_inform_bsc
+ * description - inform bsc of driver state for logging purposes
+ * inputs - driver soft state, state
+ * outputs - none
+ *
+ */
+static void
+bscv_inform_bsc(bscv_soft_state_t *ssp, uint32_t state)
+{
+ ASSERT(bscv_held(ssp));
+
+ bscv_trace(ssp, 'X', "bscv_inform_bsc",
+ "bscv_inform_bsc: state=%d", state);
+
+ bscv_put32(ssp, chan_general,
+ BSCVA(EBUS_CMD_SPACE_CPUSIG, EBUS_IDX11_CPU_SIG_MSB), state);
+ bscv_put8(ssp, chan_cpusig,
+ BSCVA(EBUS_CMD_SPACE_CPUSIG, EBUS_IDX11_CPU_ID), EBUS_ANY_CPU_ID);
+}
+
+/*
+ * function - bscv_watchdog_pat_request
+ * description - request a heartbeat pat
+ * inputs - timeout value in seconds
+ * outputs - none
+ */
+static void
+bscv_watchdog_pat_request(void *arg)
+{
+ bscv_soft_state_t *ssp = (bscv_soft_state_t *)arg;
+
+ bscv_wdog_do_pat(ssp);
+}
+
+/*
+ * function - bscv_watchdog_cfg_request
+ * description - request configuration of the bsc hardware watchdog
+ * inputs - new state (0=disabled, 1=enabled)
+ * outputs - one if successful, zero if unsuccesful
+ */
+static void
+bscv_watchdog_cfg_request(bscv_soft_state_t *ssp, uint8_t new_state)
+{
+ ASSERT(new_state == WDOG_ON || new_state == WDOG_OFF);
+
+ watchdog_activated = new_state;
+ bscv_trace(ssp, 'X', "bscv_watchdog_cfg_request",
+ "watchdog_activated=%d", watchdog_activated);
+ bscv_write_wdog_cfg(ssp,
+ bscv_watchdog_timeout_seconds,
+ new_state,
+ wdog_reset_on_timeout);
+}
+
+/*
+ * function - bscv_set_watchdog_timer
+ * description - setup the heartbeat timeout value
+ * inputs - timeout value in seconds
+ * outputs - zero if the value was not changed
+ * otherwise the current value
+ */
+static uint_t
+bscv_set_watchdog_timer(bscv_soft_state_t *ssp, uint_t timeoutval)
+{
+ bscv_trace(ssp, 'X', "bscv_set_watchdog_timer:",
+ "timeout=%d", timeoutval);
+
+ /*
+ * We get started during bscv_attach only
+ * if bscv_watchdog_enable is set.
+ */
+ if (bscv_watchdog_available && (!watchdog_activated ||
+ (watchdog_activated &&
+ (timeoutval != bscv_watchdog_timeout_seconds)))) {
+ bscv_watchdog_timeout_seconds = timeoutval;
+ bscv_watchdog_cfg_request(ssp, WDOG_ON);
+ return (bscv_watchdog_timeout_seconds);
+ }
+ return (0);
+}
+
+/*
+ * function - bscv_clear_watchdog_timer
+ * description - add the watchdog patter cyclic
+ * inputs - driver soft state
+ * outputs - value of watchdog timeout in seconds
+ *
+ * This function is a copy of the SPARC implementation
+ * in the todblade clock driver.
+ */
+static void
+bscv_clear_watchdog_timer(bscv_soft_state_t *ssp)
+{
+ bscv_trace(ssp, 'X', "bscv_clear_watchdog_timer", "");
+
+ if (bscv_watchdog_available && watchdog_activated) {
+ bscv_watchdog_enable = 0;
+ bscv_watchdog_cfg_request(ssp, WDOG_OFF);
+ }
+}
+
+/*
+ * function - bscv_panic_callback
+ * description - called when we panic so we can disabled the watchdog
+ * inputs - driver soft state pointer
+ * outputs - DDI_SUCCESS
+ */
+/*ARGSUSED1*/
+static boolean_t
+bscv_panic_callback(void *arg, int code)
+{
+ bscv_soft_state_t *ssp = (bscv_soft_state_t *)arg;
+
+ bscv_trace(ssp, 'X', "bscv_panic_callback",
+ "disabling watchdog");
+
+ bscv_clear_watchdog_timer(ssp);
+ /*
+ * We dont get interrupts during the panic callback. But bscbus
+ * takes care of all this
+ */
+ bscv_full_stop(ssp);
+ return (DDI_SUCCESS);
+}
+
+/*
+ * function - bscv_watchdog_cyclic_add
+ * description - add the watchdog patter cyclic
+ * inputs - driver soft state
+ * outputs - none
+ */
+static void
+bscv_watchdog_cyclic_add(bscv_soft_state_t *ssp)
+{
+ cyc_handler_t hdlr;
+ cyc_time_t when;
+
+ ASSERT(MUTEX_HELD(&cpu_lock)); /* for cyclic_add */
+
+ if (ssp->cyclic_id != CYCLIC_NONE) {
+ return;
+ }
+
+ hdlr.cyh_level = CY_LOCK_LEVEL;
+ hdlr.cyh_func = (cyc_func_t)bscv_watchdog_pat_request;
+ hdlr.cyh_arg = (void *)ssp;
+
+ when.cyt_when = 0;
+ when.cyt_interval = WATCHDOG_PAT_INTERVAL;
+
+ ssp->cyclic_id = cyclic_add(&hdlr, &when);
+
+ bscv_trace(ssp, 'X', "bscv_watchdog_cyclic_add:",
+ "cyclic added");
+}
+
+/*
+ * function - bscv_watchdog_cyclic_remove
+ * description - remove the watchdog patter cyclic
+ * inputs - soft state ptr
+ * outputs - none
+ */
+static void
+bscv_watchdog_cyclic_remove(bscv_soft_state_t *ssp)
+{
+ ASSERT(MUTEX_HELD(&cpu_lock)); /* for cyclic_remove */
+
+ if (ssp->cyclic_id == CYCLIC_NONE) {
+ return;
+ }
+
+ cyclic_remove(ssp->cyclic_id);
+ ssp->cyclic_id = CYCLIC_NONE;
+ bscv_trace(ssp, 'X', "bscv_watchdog_cyclic_remove:",
+ "cyclic removed");
+}
+#endif /* __i386 || __amd64 */
+
+
+/*
+ * General utility routines ...
+ */
+
+#ifdef DEBUG
+
+static void
+bscv_trace(bscv_soft_state_t *ssp, char code, const char *caller,
+ const char *fmt, ...)
+{
+ char buf[256];
+ char *p;
+ va_list va;
+
+ if (ssp->debug & (1 << (code-'@'))) {
+ p = buf;
+ (void) snprintf(p, sizeof (buf) - (p - buf),
+ "%s/%s: ", MYNAME, caller);
+ p += strlen(p);
+
+ va_start(va, fmt);
+ (void) vsnprintf(p, sizeof (buf) - (p - buf), fmt, va);
+ va_end(va);
+
+ buf[sizeof (buf) - 1] = '\0';
+ (void) strlog((short)ssp->majornum, (short)ssp->minornum, code,
+ SL_TRACE, buf);
+ }
+}
+
+#else /* DEBUG */
+
+_NOTE(ARGSUSED(0))
+static void
+bscv_trace(bscv_soft_state_t *ssp, char code, const char *caller,
+ const char *fmt, ...)
+{
+}
+
+#endif /* DEBUG */
diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile
index 6b1b5bb8c3..eb3a21935c 100644
--- a/usr/src/uts/common/sys/Makefile
+++ b/usr/src/uts/common/sys/Makefile
@@ -594,6 +594,8 @@ AUDIOHDRS= \
g711.h
BSCHDRS= \
+ bscbus.h \
+ bscv_impl.h \
lom_ebuscodes.h \
lom_io.h \
lom_priv.h \
diff --git a/usr/src/uts/common/sys/bscbus.h b/usr/src/uts/common/sys/bscbus.h
new file mode 100644
index 0000000000..0c57cb93f3
--- /dev/null
+++ b/usr/src/uts/common/sys/bscbus.h
@@ -0,0 +1,63 @@
+/*
+ * 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 2001-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_BSCBUS_H
+#define _SYS_BSCBUS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The bscbus nexus driver provides the same client interface as the lombus
+ * nexus driver.
+ */
+#include <sys/lombus.h>
+
+/*
+ * Register spaces (as lombus.h but spaces now have a channel
+ * value encoded in it too)
+ *
+ * Space* Size Range Meaning
+ * (bits)
+ *
+ * xx00 8 [0 .. 16383] LOM virtual registers
+ * xx01 8 [0] Watchdog pat (on write)
+ * xx02 16 [0] Async event info (read only)
+ * All 32 [-4 .. -12] Access handle fault info
+ * * xx is the channel number.
+ */
+
+#define LOMBUS_SPACE_TO_REGSET(rsp) ((rsp) & 0xff)
+#define LOMBUS_SPACE_TO_CHANNEL(rsp) (((rsp) & 0xff00) >> 8)
+#define LOMBUS_SPACE(regset, channel) ((regset) | ((channel) << 8))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_BSCBUS_H */
diff --git a/usr/src/uts/common/sys/bscv_impl.h b/usr/src/uts/common/sys/bscv_impl.h
new file mode 100644
index 0000000000..da4654393d
--- /dev/null
+++ b/usr/src/uts/common/sys/bscv_impl.h
@@ -0,0 +1,369 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_BSCV_IMPL_H
+#define _SYS_BSCV_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Implementation private header file for bscv driver.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/lom_priv.h>
+
+
+/*
+ * Local #defines
+ */
+
+#define BSCV_SUCCESS DDI_SUCCESS
+#define BSCV_FAILURE DDI_FAILURE
+
+/*
+ * The following are used as progress indicators in bscv_attach()
+ */
+
+#define BSCV_LOCKS 0x01
+#define BSCV_MAPPED_REGS 0x02
+#define BSCV_NODES 0x04
+#define BSCV_THREAD 0x08
+#define BSCV_HOSTNAME_DONE 0x10
+#define BSCV_WDOG_CFG 0x20
+#define BSCV_SIG_SENT 0x40
+
+/*
+ * macros to encode device minors and provide mapping to device instances.
+ * The following is designed to get around the problem of a 32-bit app not
+ * supporting a 32-bit minor number on an LP64 model system.
+ */
+
+#ifdef NBITSMINOR
+#undef NBITSMINOR
+#define NBITSMINOR 18
+#endif
+
+#define BSCV_MONITOR_NODE 0
+#define BSCV_CONTROL_NODE (1 << (NBITSMINOR - 1))
+
+#define DEVICETOINSTANCE(x) ((getminor(x)) & (~BSCV_CONTROL_NODE));
+
+/*
+ * The maximum number of leds which are supported by this lom implementation.
+ */
+#define MAX_LED_ID 7
+
+/*
+ * general driver configuration constants which may be changed to improve
+ * performance/efficiency.
+ */
+
+#define INIT_BUSY_WAIT 10 /* 10 microsecs */
+
+#define MAX_WDOGTIMEOUT 127 /* maximum wdog timout - 127s */
+
+
+/*
+ * Event processing task status flags.
+ */
+#define TASK_ALIVE_FLG 0x01
+#define TASK_STOP_FLG 0x02
+#define TASK_SLEEPING_FLG 0x04
+#define TASK_PAUSE_FLG 0x08
+#define TASK_EVENT_PENDING_FLG 0x10
+#define TASK_EVENT_CONSUMER_FLG 0x20
+
+/*
+ * strace(1M) prints out the debug data once the debug value is set in
+ * the bscv.conf file and the debug driver is installed.
+ *
+ * Debug flags
+ *
+ * '@' - Register (@)ccess
+ * 'A' - (A)ttach
+ * 'B' - (B)lom1 attach extra
+ * 'C' - lom1 (C)allback
+ * 'D' - (D)aemon
+ * 'E' - (E)vents
+ * 'F' - Sel(F)test
+ * 'I' - (I)octl
+ * 'L' - TSa(L)arms
+ * 'M' - (M)odel parameters
+ * 'N' - I(N)terrupt Service Routine
+ * 'O' - (O)pen/Close
+ * 'P' - (P)rogramming
+ * 'Q' - (Q)ueue things
+ * 'R' - Read/Write (R)etry summary.
+ * 'S' - Event (S)trings
+ * 'U' - Programming ioctls
+ * 'V' - ???
+ * 'W' - (W)atchdog
+ * 'X' - additional X86 functional calls
+ * 'Z' - Temporary - just log things
+ */
+
+/*
+ * Debug tips :
+ *
+ * strace(1M) prints out the debug data.
+ * A nice way to work out the debug value set in bscv.conf is to use mdb
+ * Say we want to show 'D' Daemon and 'I' IOCTL processing,
+ * you calculate the debug value with the following mdb session :
+ * # mdb
+ * > 1<<('D'-'@') | 1<<('I'-'@') = X
+ * 210
+ * > $q
+ * When you insert "debug=0x210;" into bscv.conf, it causes the next
+ * reboot with the debug driver to trace Daemon and IOCTL functionality.
+ */
+
+/*
+ * Xbus channel access data
+ */
+
+struct xbus_channel {
+ ddi_acc_handle_t handle;
+ uint8_t *regs;
+};
+
+#define BSCV_MINCHANNELS 2
+#define BSCV_MAXCHANNELS 16
+
+/*
+ * soft state structure
+ */
+
+typedef
+struct {
+ /*
+ * Hardware instance variables
+ */
+ uint64_t debug; /* debugging turned on */
+ major_t majornum; /* debugging - major number */
+ minor_t minornum; /* debugging - minor number */
+
+ dev_info_t *dip; /* pointer to device info tree */
+ int instance; /* instance number for the device */
+ ddi_device_acc_attr_t attr; /* device access attributes */
+
+ struct xbus_channel channel[BSCV_MAXCHANNELS];
+ int nchannels;
+
+ int progress; /* progress indicator for attach */
+
+ int bad_resync; /* Number of bad resyncs */
+
+ /*
+ * lom data variables/arrays
+ */
+ uint8_t lom_regs[0x80]; /* registers on the lomlite */
+ int serial_reporting;
+ int reporting_level;
+
+ /*
+ * lom2 static information.
+ * setup at driver attach and restart after programming.
+ */
+ int num_fans;
+ char fan_names[MAX_FANS][MAX_LOM2_NAME_STR];
+ uint8_t fanspeed[MAX_FANS];
+ char led_names[MAX_LED_ID][MAX_LOM2_NAME_STR];
+ lom_volts_t volts; /* keep a static copy of this so */
+ /* dont have to re-read names */
+ lom_temp_t temps; /* keep a static copy of this so */
+ /* dont have to re-read names */
+ lom_sflags_t sflags; /* keep a static copy of this so */
+ /* dont have to re-read names */
+ char escape_chars[6]; /* local copy */
+
+ uint_t watchdog_timeout;
+ uint8_t watchdog_reset_on_timeout;
+
+ /*
+ * lom2 firmware communication
+ */
+
+ /*
+ * cmd_mutex protects the lom2 command progress variables.
+ * These should only be read/updated with the mutex held.
+ *
+ * command_error - acts as a return code and may be read
+ * without the mutex held if a command is not in progress.
+ * Note a read only returns failure if the lom does not respond.
+ * So you might need to check the error code to see if things really
+ * did work!
+ *
+ * addr_mu is used to protect stopping and starting of the queue.
+ * BUT when programming it has different semantics and relies
+ * on only the programming thread being in the ioctl routine
+ * whilst programming is in progress. The event queue must also
+ * be paused at this time.
+ */
+ kmutex_t cmd_mutex; /* LOM command mutual exclusion */
+
+ int command_error; /* error code from last command */
+ /* valid until the next command */
+ /* starts. */
+
+ boolean_t had_fault; /* Current command sequence faulted */
+ boolean_t had_session_error; /* Current session had error */
+
+ uint8_t pat_seq; /* Watchdog patting sequence number */
+ uint8_t cap0; /* capability byte */
+ uint8_t cap1; /* capability byte */
+ uint8_t cap2; /* capability byte */
+
+ /*
+ * Programming variables
+ */
+ kmutex_t prog_mu; /* Programming mutex. - lom 2 */
+ boolean_t prog_mode_only; /* If true we can only reprogram */
+ /* the lom */
+ boolean_t programming; /* TRUE is actually programming */
+ /* the BSC */
+ boolean_t cssp_prog; /* TRUE is CSSP programming the BSC */
+
+ int prog_index; /* data buffer number - bit */
+ /* 0x8000 set if last buffer */
+ int image_ptr; /* ptr to next byte in image buffer */
+ /* for programming */
+ uint8_t *image; /* ptr to image buffer for */
+ /* programming */
+ boolean_t image2_processing; /* boolean to say which of */
+ /* 2 BSC images being processed */
+ boolean_t loader_running; /* Still have the loader running */
+
+ /*
+ * LOM eeprom window access state
+ * Access under bscv_enter/bscv_exit protection.
+ */
+ boolean_t eeinfo_valid;
+ uint32_t eeprom_size;
+ uint32_t eventlog_start;
+ uint32_t eventlog_size;
+ boolean_t oldeeptr_valid;
+ uint16_t oldeeptr;
+
+ /*
+ * Communication with the event processing thread
+ *
+ * Change these variables with task_mu held and signal task_cv
+ * if an event/task needs processing.
+ */
+ kmutex_t task_mu; /* mutex for wait on event thread */
+ kcondvar_t task_cv; /* cv for wait on event thread */
+ kcondvar_t task_evnt_cv; /* cv for lom2 wait on event */
+ int task_flags; /* To monitor/stop the event thread */
+ volatile int event_active_count; /* Count of event thread runs */
+ boolean_t event_waiting; /* New events are waiting in the lom */
+ boolean_t status_change; /* A status change is waiting */
+ boolean_t nodename_change; /* Nodename has changed */
+ boolean_t event_sleep; /* Error reading events - wait a bit */
+ boolean_t event_fault_reported; /* Event fault reported */
+ boolean_t watchdog_change; /* Watchdog config has changed */
+#ifdef __sparc
+ bscv_sig_t last_sig; /* Record of last signature sent */
+#endif /* __sparc */
+ uint8_t last_event[8]; /* last event read and reported */
+#if defined(__i386) || defined(__amd64)
+ cyclic_id_t cyclic_id; /* watchdog patter cyclic timer */
+ callb_id_t callb_id; /* Need to store the ID so we can */
+ /* unschedule the panic callback */
+ char last_nodename[128]; /* copy of last utsname.nodename */
+#endif /* __i386 || __amd64 */
+} bscv_soft_state_t;
+
+struct bscv_idi_callout {
+ enum bscv_idi_type type; /* Type of service */
+ boolean_t (*fn)(struct bscv_idi_info); /* Function's address */
+};
+
+#define BSCV_IDI_CALLOUT_MAGIC 0xb5c1ca11
+#define BSCV_IDI_ERR_MSG_THRESHOLD 10
+struct bscv_idi_callout_mgr {
+ /*
+ * To allow for sanity check.
+ */
+ uint32_t magic;
+
+ /*
+ * The instance number of "an" instance of the driver. This is assigned
+ * during driver attach.
+ */
+ uint32_t valid_inst;
+
+ /*
+ * Table of services offered via the idi interface.
+ */
+ struct bscv_idi_callout *tbl;
+
+ /*
+ * Error message count since last successful use of the idi interface.
+ */
+ uint64_t errs;
+};
+
+
+
+#define BSC_IMAGE_MAX_SIZE (0x20000 + sizeof (lom_prog_data_t))
+
+#define BSC_PROBE_FAULT_LIMIT 8 /* Tries before declaring lom dead */
+#define BSC_EVENT_POLL_NORMAL (drv_usectohz(1000000)) /* 1 second */
+#define BSC_EVENT_POLL_FAULTY (drv_usectohz(10000000)) /* 10 second */
+
+#define BSC_FAILURE_RETRY_LIMIT 5 /* Access retries before giving up */
+#define BSC_ERASE_RETRY_LIMIT 5 /* Erase retries */
+#define BSC_PAGE_RETRY_LIMIT 5 /* Page write retries */
+
+#define BSC_ADDR_CACHE_LIMIT \
+ (sizeof (((bscv_soft_state_t *)NULL)->lom_regs))
+#define BSC_INFORM_ONLINE 0x4f530100
+#define BSC_INFORM_OFFLINE 0x4f530201
+#define BSC_INFORM_PANIC 0x4f530204
+
+#include <sys/lom_ebuscodes.h>
+
+typedef uint32_t bscv_addr_t;
+
+#define BSC_NEXUS_ADDR(ssp, chan, as, index) \
+ (&((ssp)->channel[chan].regs[((as) * 256) + (index)]))
+
+#define BSC_NEXUS_OFFSET(as, index) (((as) * 256) + (index))
+
+#define BSCVA(as, index) (((as) * 256) + (index))
+
+#define PSR_SUCCESS(status) (((status) & EBUS_PROGRAM_PSR_STATUS_MASK) == \
+ EBUS_PROGRAM_PSR_SUCCESS)
+
+#define PSR_PROG(status) (((status) & EBUS_PROGRAM_PSR_PROG_MODE) != 0)
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_BSCV_IMPL_H */
diff --git a/usr/src/uts/intel/Makefile.files b/usr/src/uts/intel/Makefile.files
index 5d010e8498..0cb64d3672 100644
--- a/usr/src/uts/intel/Makefile.files
+++ b/usr/src/uts/intel/Makefile.files
@@ -121,6 +121,8 @@ AMD64GART_OBJS += amd64_gart.o
ATA_OBJS += $(GHD_OBJS) ata_blacklist.o ata_common.o ata_disk.o \
ata_dma.o atapi.o atapi_fsm.o ata_debug.o \
sil3xxx.o
+BSCBUS_OBJS += bscbus.o
+BSCV_OBJS += bscv.o
CMDK_OBJS += cmdk.o
CMLB_OBJS += cmlb.o
DADK_OBJS += dadk.o
diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared
index 2a80b43646..c195cac44c 100644
--- a/usr/src/uts/intel/Makefile.intel.shared
+++ b/usr/src/uts/intel/Makefile.intel.shared
@@ -218,6 +218,8 @@ DRV_KMODS += audioixp
DRV_KMODS += bl
DRV_KMODS += bge
DRV_KMODS += bofi
+DRV_KMODS += bscbus
+DRV_KMODS += bscv
DRV_KMODS += clone
DRV_KMODS += cmdk
DRV_KMODS += cn
@@ -323,8 +325,6 @@ $(CLOSED_BUILD)DRV_KMODS += dca
$(CLOSED_BUILD)CLOSED_DRV_KMODS += audioens
$(CLOSED_BUILD)CLOSED_DRV_KMODS += audiovia823x
$(CLOSED_BUILD)CLOSED_DRV_KMODS += bmc
-$(CLOSED_BUILD)CLOSED_DRV_KMODS += bscbus
-$(CLOSED_BUILD)CLOSED_DRV_KMODS += bscv
$(CLOSED_BUILD)CLOSED_DRV_KMODS += elxl
$(CLOSED_BUILD)CLOSED_DRV_KMODS += glm
$(CLOSED_BUILD)CLOSED_DRV_KMODS += iprb
diff --git a/usr/src/uts/intel/bscbus/Makefile b/usr/src/uts/intel/bscbus/Makefile
new file mode 100644
index 0000000000..1e2944f362
--- /dev/null
+++ b/usr/src/uts/intel/bscbus/Makefile
@@ -0,0 +1,93 @@
+#
+# 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
+#
+#
+# uts/intel/bscbus/Makefile
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of the bscbus driver
+#
+# intel architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = bscbus
+OBJECTS = $(BSCBUS_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(BSCBUS_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/intel/io
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(CONFMOD)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# For now, disable these lint checks; maintainers should endeavor
+# to investigate and remove these for maximum lint coverage.
+# Please do not carry these forward to new Makefiles.
+#
+LINTTAGS += -erroff=E_PTRDIFF_OVERFLOW
+LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV
+LINTTAGS += -erroff=E_SUSPICIOUS_COMPARISON
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/bscv/Makefile b/usr/src/uts/intel/bscv/Makefile
new file mode 100644
index 0000000000..f01bd4e914
--- /dev/null
+++ b/usr/src/uts/intel/bscv/Makefile
@@ -0,0 +1,94 @@
+#
+# 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
+#
+#
+# uts/intel/bscv/Makefile
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of the bscv driver
+#
+# intel architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = bscv
+OBJECTS = $(BSCV_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(BSCV_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/intel/io
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(CONFMOD)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# For now, disable these lint checks; maintainers should endeavor
+# to investigate and remove these for maximum lint coverage.
+# Please do not carry these forward to new Makefiles.
+#
+LINTTAGS += -erroff=E_BAD_PTR_CAST_ALIGN
+LINTTAGS += -erroff=E_PTRDIFF_OVERFLOW
+LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV
+LINTTAGS += -erroff=E_SUSPICIOUS_COMPARISON
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/io/bscbus.conf b/usr/src/uts/intel/io/bscbus.conf
new file mode 100644
index 0000000000..c783e2619d
--- /dev/null
+++ b/usr/src/uts/intel/io/bscbus.conf
@@ -0,0 +1,31 @@
+#
+# 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 2003 Sun Microsystems, Inc.
+# All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Configuration file for "bscbus" driver
+#
+
+interrupt-priorities = 8, 8;
diff --git a/usr/src/uts/intel/io/bscv.conf b/usr/src/uts/intel/io/bscv.conf
new file mode 100644
index 0000000000..20f96109e4
--- /dev/null
+++ b/usr/src/uts/intel/io/bscv.conf
@@ -0,0 +1,38 @@
+#
+# 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 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Configuration file for bscv driver
+#
+
+ddi-forceattach=1;
+
+#
+# The DEBUG version of the bscv driver supports a debug option value.
+#
+debug=0x0;
+
+parent="bscbus" name="bscv" instance=0;
+reg=0x0, 0x0, 0x4000, 0x201, 0x0, 0x1;
diff --git a/usr/src/uts/sun4u/Makefile.files b/usr/src/uts/sun4u/Makefile.files
index 96cadbd9c8..04564949c8 100644
--- a/usr/src/uts/sun4u/Makefile.files
+++ b/usr/src/uts/sun4u/Makefile.files
@@ -20,7 +20,7 @@
#
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -133,6 +133,9 @@ PMUBUS_OBJS += pmubus.o
PMUGPIO_OBJS += pmugpio.o
PMC_OBJS += pmc.o
TRAPSTAT_OBJS += trapstat.o
+I2BSC_OBJS += i2bsc.o
+GPTWOCFG_OBJS += gptwocfg.o
+GPTWO_CPU_OBJS += gptwo_cpu.o
WRSM_OBJS += wci_common.o \
wrsm_barrier.o \
wrsm_cf.o \
@@ -182,8 +185,10 @@ TODMOSTEK_OBJS += todmostek.o
TODDS1287_OBJS += todds1287.o
TODDS1337_OBJS += todds1337.o
TODSTARFIRE_OBJS += todstarfire.o
+TODSTARCAT_OBJS += todstarcat.o
TODBLADE_OBJS += todblade.o
TODM5819_OBJS += todm5819.o
+TODM5819P_RMC_OBJS += todm5819p_rmc.o
TODBQ4802_OBJS += todbq4802.o
TODSG_OBJS += todsg.o
TODOPL_OBJS = todopl.o
diff --git a/usr/src/uts/sun4u/Makefile.sun4u.shared b/usr/src/uts/sun4u/Makefile.sun4u.shared
index c77a378664..618b59d3d2 100644
--- a/usr/src/uts/sun4u/Makefile.sun4u.shared
+++ b/usr/src/uts/sun4u/Makefile.sun4u.shared
@@ -405,9 +405,9 @@ DRV_KMODS += rmclomv
DRV_KMODS += wrsmd
DRV_KMODS += sf
DRV_KMODS += nxge
+DRV_KMODS += i2bsc
$(CLOSED_BUILD)CLOSED_DRV_KMODS += ctsmc
-$(CLOSED_BUILD)CLOSED_DRV_KMODS += i2bsc
$(CLOSED_BUILD)CLOSED_DRV_KMODS += m1535ppm
$(CLOSED_BUILD)CLOSED_DRV_KMODS += memtest
$(CLOSED_BUILD)CLOSED_DRV_KMODS += mi2cv
@@ -448,14 +448,13 @@ MISC_KMODS += sbd
MISC_KMODS += opl_cfg
MISC_KMODS += kmech_krb5
MISC_KMODS += zuluvm
+MISC_KMODS += gptwo_cpu gptwocfg
#
# Brand modules
#
BRAND_KMODS += sn1_brand
-$(CLOSED_BUILD)CLOSED_MISC_KMODS += gptwo_cpu gptwocfg
-
#
# Software Cryptographic Providers (/kernel/crypto):
#
@@ -491,8 +490,9 @@ CPU_KMODS += cheetah cheetahplus jalapeno serrano spitfire hummingbird
#
TOD_KMODS += todds1287 todds1337 todmostek todstarfire
TOD_KMODS += todm5819 todblade todbq4802 todsg todopl
+TOD_KMODS += todm5819p_rmc todstarcat
-$(CLOSED_BUILD)CLOSED_TOD_KMODS += todm5819p_rmc todstarcat todm5823
+$(CLOSED_BUILD)CLOSED_TOD_KMODS += todm5823
#
# Performance Counter BackEnd Modules (/usr/kernel/pcbe):
diff --git a/usr/src/uts/sun4u/blade/Makefile.blade.shared b/usr/src/uts/sun4u/blade/Makefile.blade.shared
index 6e24b19f6f..a89230bde3 100644
--- a/usr/src/uts/sun4u/blade/Makefile.blade.shared
+++ b/usr/src/uts/sun4u/blade/Makefile.blade.shared
@@ -21,7 +21,7 @@
#
#ident "%Z%%M% %I% %E% SMI"
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# Global definitions for sun4u jbos-blade implementation specific modules.
@@ -89,8 +89,8 @@ include $(UTSTREE)/sun4u/Makefile.sun4u
# Define modules (must come after Makefile.sun4u, for CLOSED_BUILD).
#
BLADE_KMODS = platmod
-$(CLOSED_BUILD)CLOSED_BLADE_KMODS += bscbus
-$(CLOSED_BUILD)CLOSED_BLADE_KMODS += bscv
+BLADE_KMODS += bscbus
+BLADE_KMODS += bscv
LINTS_DIR = $(OBJS_DIR)
LINT_LIB_DIR = $(UTSBASE)/$(PLATFORM)/blade/lint-libs/$(OBJS_DIR)
diff --git a/usr/src/uts/sun4u/blade/Makefile.files b/usr/src/uts/sun4u/blade/Makefile.files
index d834db1711..e28eaae1e6 100644
--- a/usr/src/uts/sun4u/blade/Makefile.files
+++ b/usr/src/uts/sun4u/blade/Makefile.files
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# 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.
@@ -22,7 +21,7 @@
#
#pragma ident "%Z%%M% %I% %E% SMI"
#
-# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# This Makefile defines all file modules for the directory
@@ -33,6 +32,8 @@
#
# Object lists
#
+BSCBUS_OBJS = bscbus.o
+BSCV_OBJS = bscv.o
#
# Miscellaneous
diff --git a/usr/src/uts/sun4u/blade/bscbus/Makefile b/usr/src/uts/sun4u/blade/bscbus/Makefile
new file mode 100644
index 0000000000..916901075b
--- /dev/null
+++ b/usr/src/uts/sun4u/blade/bscbus/Makefile
@@ -0,0 +1,100 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# uts/sun4u/blade/bscbus/Makefile
+#
+# This makefile drives the production of the bscbus driver kernel
+# module in the sun4u blade systems
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = bscbus
+OBJECTS = $(BSCBUS_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(BSCBUS_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_BLADE_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/sun4u/blade/io
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sun4u/blade/Makefile.blade
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# Overrides
+#
+ALL_BUILDS = $(ALL_BUILDSONLY64)
+DEF_BUILDS = $(DEF_BUILDSONLY64)
+CLEANLINTFILES += $(LINT32_FILES)
+
+#
+# lint pass one enforcement
+#
+CFLAGS += $(CCVERBOSE)
+
+#
+# Turn on doubleword alignment for 64 bit registers
+#
+CFLAGS += -dalign
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/sun4u/blade/Makefile.targ
diff --git a/usr/src/uts/sun4u/blade/bscv/Makefile b/usr/src/uts/sun4u/blade/bscv/Makefile
new file mode 100644
index 0000000000..6c95f9e137
--- /dev/null
+++ b/usr/src/uts/sun4u/blade/bscv/Makefile
@@ -0,0 +1,94 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of the sun4u "bscv" driver module.
+#
+# sun4u implementation architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = bscv
+OBJECTS = $(BSCV_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(BSCV_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_BLADE_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/sun4u/blade/io
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sun4u/blade/Makefile.blade
+
+#
+# Overrides
+#
+ALL_BUILDS = $(ALL_BUILDSONLY64)
+DEF_BUILDS = $(DEF_BUILDSONLY64)
+CLEANLINTFILES += $(LINT32_FILES)
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# lint pass one enforcement
+#
+CFLAGS += $(CCVERBOSE)
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/sun4u/blade/Makefile.targ
diff --git a/usr/src/uts/sun4u/blade/io/bscbus.conf b/usr/src/uts/sun4u/blade/io/bscbus.conf
new file mode 100644
index 0000000000..a00c7f80f6
--- /dev/null
+++ b/usr/src/uts/sun4u/blade/io/bscbus.conf
@@ -0,0 +1,31 @@
+#
+# 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 2002 Sun Microsystems, Inc.
+# All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Configuration file for "bscbus" driver
+#
+
+interrupt-priorities = 8, 8, 8, 8;
diff --git a/usr/src/uts/sun4u/blade/io/bscv.conf b/usr/src/uts/sun4u/blade/io/bscv.conf
new file mode 100644
index 0000000000..238e0ff7f3
--- /dev/null
+++ b/usr/src/uts/sun4u/blade/io/bscv.conf
@@ -0,0 +1,33 @@
+#
+# 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 2001-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Configuration file for bscv driver
+#
+
+#
+# The DEBUG version of the bscv driver supports a debug option value.
+#
+debug=0x0;
diff --git a/usr/src/uts/sun4u/gptwo_cpu/Makefile b/usr/src/uts/sun4u/gptwo_cpu/Makefile
new file mode 100644
index 0000000000..06bb109a42
--- /dev/null
+++ b/usr/src/uts/sun4u/gptwo_cpu/Makefile
@@ -0,0 +1,115 @@
+#
+# 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
+#
+#
+# uts/sun4u/gptwo_cpu/Makefile
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of the cpu portion of
+# of the Safari Configurator.
+#
+# sun4u implementation architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = gptwo_cpu
+OBJECTS = $(GPTWO_CPU_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(GPTWO_CPU_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sun4u/Makefile.sun4u
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+
+# Turn this on once compiler understands v9 in it's backend
+#INLINES += $(UTSBASE)/sun4u/io/gptwo_cpu.il
+
+#
+# lint pass one enforcement
+#
+CFLAGS += $(CCVERBOSE)
+
+#
+# Turn on doubleword alignment for 64 bit registers
+#
+CFLAGS += -dalign
+
+#
+# Pick up defines in cheetahregs.h.
+#
+CFLAGS += -DCHEETAH_PLUS
+LINTFLAGS += -DCHEETAH_PLUS
+
+#
+# module dependencies
+#
+LDFLAGS += -dy -Nmisc/gptwocfg
+
+#
+# For now, disable these lint checks; maintainers should endeavor
+# to investigate and remove these for maximum lint coverage.
+# Please do not carry these forward to new Makefiles.
+#
+LINTTAGS += -erroff=E_STATIC_UNUSED
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/sun4u/Makefile.targ
diff --git a/usr/src/uts/sun4u/gptwocfg/Makefile b/usr/src/uts/sun4u/gptwocfg/Makefile
new file mode 100644
index 0000000000..5fc69800a4
--- /dev/null
+++ b/usr/src/uts/sun4u/gptwocfg/Makefile
@@ -0,0 +1,95 @@
+#
+# 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
+#
+#
+# uts/sun4u/gptwocfg/Makefile
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of the gptwocfg Safari Configurator
+#
+# sun4u implementation architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = gptwocfg
+OBJECTS = $(GPTWOCFG_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(GPTWOCFG_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sun4u/Makefile.sun4u
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+# Turn this on once compiler understands v9 in it's backend
+#INLINES += $(UTSBASE)/sun4u/io/gptwocfg.il
+
+#
+# lint pass one enforcement
+#
+CFLAGS += $(CCVERBOSE)
+
+#
+# Turn on doubleword alignment for 64 bit registers
+#
+CFLAGS += -dalign
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/sun4u/Makefile.targ
diff --git a/usr/src/uts/sun4u/i2bsc/Makefile b/usr/src/uts/sun4u/i2bsc/Makefile
new file mode 100644
index 0000000000..0c984165b1
--- /dev/null
+++ b/usr/src/uts/sun4u/i2bsc/Makefile
@@ -0,0 +1,112 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+
+# This makefile drives the production of the i2bsc nexus driver.
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = i2bsc
+OBJECTS = $(I2BSC_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(I2BSC_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_PSM_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/sun4u/io/i2c/nexus
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sun4u/Makefile.sun4u
+
+#
+# lint pass one enforcement
+#
+CFLAGS += $(CCVERBOSE) -I$(UTSBASE)/sun4u
+
+LDFLAGS += -dy -N misc/i2c_svc
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# For now, disable these lint checks; maintainers should endeavor
+# to investigate and remove these for maximum lint coverage.
+# Please do not carry these forward to new Makefiles.
+#
+LINTTAGS += -erroff=E_BAD_PTR_CAST_ALIGN
+LINTTAGS += -erroff=E_PTRDIFF_OVERFLOW
+LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Defines for local commands.
+#
+WLCC = wlcc
+TOUCH = touch
+WARLOCK = warlock
+
+#
+# Warlock targets
+#
+
+I2BSC = $(I2BSC_OBJS:%.o=%.ll)
+
+warlock: $(MODULE).ok
+
+%.ok: $(I2BSC_FILES)
+ $(TOUCH) $@
+
+%.ll: $(UTSBASE)/sun4u/io/i2c/nexus/i2bsc/%.c
+ $(WLCC) $(CFLAGS) $(CPPFLAGS) -DDEBUG -o $@ $<
+
+#
+# Include common targets
+#
+include $(UTSBASE)/sun4u/Makefile.targ
diff --git a/usr/src/uts/sun4u/io/gptwo_cpu.c b/usr/src/uts/sun4u/io/gptwo_cpu.c
new file mode 100644
index 0000000000..c6b87a99b2
--- /dev/null
+++ b/usr/src/uts/sun4u/io/gptwo_cpu.c
@@ -0,0 +1,1023 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * CPU functions to the Safari Configurator (gptwo_cpu)
+ */
+
+#include <sys/types.h>
+#include <sys/cred.h>
+#include <sys/mman.h>
+#include <sys/kmem.h>
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/modctl.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/autoconf.h>
+#include <sys/ksynch.h>
+#include <sys/promif.h>
+#include <sys/ndi_impldefs.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/machsystm.h>
+#include <sys/gp2cfg.h>
+#include <sys/gptwo_cpu.h>
+#include <sys/cheetahregs.h>
+
+#ifdef DEBUG
+int gptwo_cpu_debug = 0;
+
+static void debug(char *, uintptr_t, uintptr_t,
+ uintptr_t, uintptr_t, uintptr_t);
+
+#define GPTWO_DEBUG0(level, flag, s) if (gptwo_cpu_debug >= level) \
+ cmn_err(flag, s)
+#define GPTWO_DEBUG1(level, flag, fmt, a1) if (gptwo_cpu_debug >= level) \
+ debug(fmt, (uintptr_t)(a1), 0, 0, 0, 0);
+#define GPTWO_DEBUG2(level, flag, fmt, a1, a2) if (gptwo_cpu_debug >= level) \
+ debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), 0, 0, 0);
+#define GPTWO_DEBUG3(level, flag, fmt, a1, a2, a3) \
+ if (gptwo_cpu_debug >= level) \
+ debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), (uintptr_t)(a3), 0, 0);
+#else
+#define GPTWO_DEBUG0(level, flag, s)
+#define GPTWO_DEBUG1(level, flag, fmt, a1)
+#define GPTWO_DEBUG2(level, flag, fmt, a1, a2)
+#define GPTWO_DEBUG3(level, flag, fmt, a1, a2, a3)
+#endif
+
+/*
+ * Devinfo branch create arg
+ */
+struct bca {
+ spcd_t *pcd;
+ uint_t portid;
+ uint_t cpuid;
+ uint_t coreid;
+ uint_t impl;
+ dev_info_t *new_child;
+};
+
+static dev_info_t *gptwocfg_create_cpu_node(dev_info_t *, spcd_t *,
+ uint_t, uint_t, uint_t, uint_t);
+static dev_info_t *gptwocfg_create_mc_node(dev_info_t *, spcd_t *, uint_t);
+static dev_info_t *gptwocfg_create_cmp_node(dev_info_t *, spcd_t *, uint_t);
+static int gptwocfg_create_core_node(dev_info_t *, spcd_t *, uint_t, uint_t);
+static int set_mc_props(dev_info_t *new_child, void *arg, uint_t flags);
+static int set_cmp_props(dev_info_t *new_child, void *arg, uint_t flags);
+static int set_cpu_props(dev_info_t *new_child, void *arg, uint_t flags);
+static int set_cpu_common_props(dev_info_t *new_child, struct bca *bcp);
+static int set_cpu_us3_props(dev_info_t *new_child, struct bca *bcp);
+static int set_cpu_us4_props(dev_info_t *new_child, struct bca *bcp);
+static void get_new_child(dev_info_t *rdip, void *arg, uint_t flags);
+
+
+/*
+ * Module linkage information for the kernel.
+ */
+
+extern struct mod_ops mod_miscops;
+
+static struct modlmisc modlmisc = {
+ &mod_miscops, /* Type of module */
+ "gptwo->cpu configurator %I%",
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&modlmisc, NULL
+};
+
+int
+_init(void)
+{
+ int err = 0;
+
+ /* register device with the configurator */
+ gptwocfg_register_ops(SAFPTYPE_CPU, gptwocfg_configure_cpu, NULL);
+
+ if ((err = mod_install(&modlinkage)) != 0) {
+ GPTWO_DEBUG1(1, CE_WARN, "gptwo_cpu (CPU/MC Functions) "
+ "failed to load, error=%d\n", err);
+ gptwocfg_unregister_ops(SAFPTYPE_CPU);
+ } else {
+ GPTWO_DEBUG0(1, CE_WARN, "gptwo_cpu (CPU/MC Functions) "
+ "has been loaded.\n");
+ }
+ return (err);
+}
+
+int
+_fini(void)
+{
+ /* cleanup/freeup structs with configurator */
+ gptwocfg_unregister_ops(SAFPTYPE_CPU);
+ return (mod_remove(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+gptwo_new_nodes_t *
+gptwocfg_configure_cpu(dev_info_t *ap, spcd_t *pcd, uint_t portid)
+{
+ dev_info_t *cpu_node[AGENTS_PER_PORT], *mc_node[AGENTS_PER_PORT];
+ dev_info_t *cmp_node = NULL;
+ gptwo_new_nodes_t *new_nodes;
+ int nodes = 0;
+ int i, j = 0;
+ uint_t implementation;
+
+ GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_configure_cpu: portid=%x pcd=%lx\n",
+ portid, pcd);
+
+ for (i = 0; i < AGENTS_PER_PORT; i++) {
+ cpu_node[i] = NULL;
+ mc_node[i] = NULL;
+ }
+
+ implementation = (pcd->spcd_ver_reg >> 32) & 0x000000000000ffff;
+
+ switch (implementation) {
+ case CHEETAH_IMPL:
+ case CHEETAH_PLUS_IMPL:
+ case JAGUAR_IMPL:
+ case PANTHER_IMPL:
+ break;
+ default:
+ cmn_err(CE_WARN, "Unsupported cpu implementation=0x%x : "
+ "skipping configure of portid=0x%x", implementation,
+ portid);
+ ASSERT(0);
+ return (NULL);
+ }
+
+ if (CPU_IMPL_IS_CMP(implementation)) {
+ if (cmp_node = gptwocfg_create_cmp_node(ap, pcd, portid))
+ nodes++;
+ else
+ return (NULL);
+ }
+
+ for (i = 0; i < AGENTS_PER_PORT; i++) {
+ if (pcd->spcd_agent[i] != SPCD_RSV_PASS)
+ continue;
+
+ if (cpu_node[i] = gptwocfg_create_cpu_node(cmp_node ?
+ cmp_node : ap, pcd, portid, pcd->spcd_cpuid[i], i,
+ implementation)) {
+ /*
+ * If the CPU is a CMP, the entire branch is
+ * manipulated using just the top node. Thus,
+ * the dips of the individual cores do not need
+ * to be held or stored in the new node list.
+ */
+ if (cmp_node) {
+ e_ddi_branch_rele(cpu_node[i]);
+ } else {
+ nodes++;
+ }
+ }
+ }
+
+ /* current implementations have 1 MC node per Safari port */
+ if (pcd->spcd_prsv == SPCD_RSV_PASS &&
+ (mc_node[0] = gptwocfg_create_mc_node(ap, pcd, portid)))
+ nodes++;
+
+ new_nodes = gptwocfg_allocate_node_list(nodes);
+
+ j = 0;
+ for (i = 0; i < AGENTS_PER_PORT; i++) {
+ if ((cpu_node[i] != NULL) && (!CPU_IMPL_IS_CMP(implementation)))
+ new_nodes->gptwo_nodes[j++] = cpu_node[i];
+ if (mc_node[i] != NULL)
+ new_nodes->gptwo_nodes[j++] = mc_node[i];
+ }
+
+ if (cmp_node)
+ new_nodes->gptwo_nodes[j++] = cmp_node;
+
+ return (new_nodes);
+}
+
+
+static dev_info_t *
+gptwocfg_create_cmp_node(dev_info_t *ap, spcd_t *pcd, uint_t portid)
+{
+ struct bca arg;
+ devi_branch_t b;
+
+ arg.pcd = pcd;
+ arg.portid = portid;
+ arg.cpuid = 0;
+ arg.coreid = 0;
+ arg.new_child = NULL;
+
+ b.arg = &arg;
+ b.type = DEVI_BRANCH_SID;
+ b.create.sid_branch_create = set_cmp_props;
+ b.devi_branch_callback = get_new_child;
+
+ if (e_ddi_branch_create(ap, &b, NULL, 0))
+ return (NULL);
+
+ return (arg.new_child);
+}
+
+/*ARGSUSED*/
+static int
+set_cmp_props(dev_info_t *new_child, void *arg, uint_t flags)
+{
+ struct bca *bap = (struct bca *)arg;
+ gptwo_regspec_t reg;
+ spcd_t *pcd;
+ uint_t portid;
+
+ pcd = bap->pcd;
+ portid = bap->portid;
+
+ GPTWO_DEBUG2(1, CE_CONT, "set_cmp_props: portid=%x pcd=%lx\n",
+ portid, pcd);
+
+ if (ndi_prop_update_string(DDI_DEV_T_NONE, new_child,
+ "name", "cmp") != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cmp_props: failed to "
+ "create name property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "portid", portid) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cmp_props: failed to "
+ "create portid property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ reg.gptwo_phys_hi = 0x400 | (portid >> 9);
+ reg.gptwo_phys_low = (portid << 23);
+ reg.gptwo_size_hi = 0;
+ reg.gptwo_size_low = 0x10000;
+
+ if (ndi_prop_update_int_array(DDI_DEV_T_NONE,
+ new_child, "reg", (int *)&reg,
+ sizeof (gptwo_regspec_t) / sizeof (int)) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cmp_props: failed to "
+ "create reg property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ return (DDI_WALK_TERMINATE);
+}
+
+static dev_info_t *
+gptwocfg_create_cpu_node(dev_info_t *ap, spcd_t *pcd, uint_t portid,
+ uint_t cpuid, uint_t coreid, uint_t impl)
+{
+ struct bca arg;
+ devi_branch_t b = {0};
+
+ arg.pcd = pcd;
+ arg.portid = portid;
+ arg.cpuid = cpuid;
+ arg.coreid = coreid;
+ arg.impl = impl;
+ arg.new_child = NULL;
+
+ b.arg = &arg;
+ b.type = DEVI_BRANCH_SID;
+ b.create.sid_branch_create = set_cpu_props;
+ b.devi_branch_callback = get_new_child;
+
+ if (e_ddi_branch_create(ap, &b, NULL, 0))
+ return (NULL);
+
+ return (arg.new_child);
+}
+
+/*ARGSUSED*/
+static int
+set_cpu_props(dev_info_t *new_child, void *arg, uint_t flags)
+{
+ struct bca *bcp = arg;
+ uint_t impl = bcp->impl;
+ int rc;
+
+ if (set_cpu_common_props(new_child, bcp) != DDI_WALK_CONTINUE)
+ return (DDI_WALK_ERROR);
+
+ switch (impl) {
+ case CHEETAH_IMPL:
+ case CHEETAH_PLUS_IMPL:
+ rc = set_cpu_us3_props(new_child, bcp);
+ break;
+ case JAGUAR_IMPL:
+ case PANTHER_IMPL:
+ rc = set_cpu_us4_props(new_child, bcp);
+ break;
+ default:
+ ASSERT(0);
+ return (DDI_WALK_ERROR);
+ }
+
+ return (rc);
+}
+
+/*
+ * Set properties common to cpu (non-CMP) and core (CMP) nodes.
+ *
+ * cpuid
+ * device_type
+ * manufacturer#
+ * implementation#
+ * mask#
+ * sparc-version
+ * clock-frequency
+ * #dtlb-entries
+ * #itlb-entries
+ */
+static int
+set_cpu_common_props(dev_info_t *new_child, struct bca *bcp)
+{
+ uint_t cpuid, impl;
+ spcd_t *pcd;
+ int mask, manufacturer;
+
+ cpuid = bcp->cpuid;
+ pcd = bcp->pcd;
+ impl = bcp->impl;
+
+ mask = (pcd->spcd_ver_reg >> 24) & 0x00000000000000ff;
+ manufacturer = (pcd->spcd_ver_reg >> 48) & 0x000000000000ffff;
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "cpuid", cpuid) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_common_props: failed "
+ "to create cpuid property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_string(DDI_DEV_T_NONE, new_child,
+ "device_type", "cpu") != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_common_props: failed "
+ "to create device_type property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child, "manufacturer#",
+ manufacturer) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_common_props: failed "
+ "to create manufacturer# property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child, "implementation#",
+ impl) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_common_props: failed "
+ "to create implementation# property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child, "mask#",
+ mask) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_common_props: failed "
+ "to create mask# property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "sparc-version", 9) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_common_props: failed "
+ "to create sparc-version property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "clock-frequency", (pcd->spcd_afreq * 1000000)) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_common_props: failed "
+ "to create clock-frequency property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "#dtlb-entries", 0x10) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_common_props: failed "
+ "to create #dtlb-entries property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "#itlb-entries", 0x10) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_common_props: failed "
+ "to create #itlb-entries property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ return (DDI_WALK_CONTINUE);
+}
+
+/*
+ * Set cpu node properties for Cheetah and Cheetah+.
+ *
+ * name
+ * portid
+ * reg
+ * icache-size
+ * icache-line-size
+ * icache-associativity
+ * dcache-size
+ * dcache-line-size
+ * dcache-associativity
+ * ecache-size
+ * ecache-line-size
+ * ecache-associativity
+ */
+static int
+set_cpu_us3_props(dev_info_t *new_child, struct bca *bcp)
+{
+ char *node_name;
+ gptwo_regspec_t reg;
+ int ecache_size, ecache_line_size;
+ int dimms, ecache_assoc;
+ spcd_t *pcd;
+ uint_t portid, impl;
+
+ pcd = bcp->pcd;
+ portid = bcp->portid;
+ impl = bcp->impl;
+
+ ASSERT(IS_CHEETAH(impl) || IS_CHEETAH_PLUS(impl));
+
+ switch (impl) {
+ case CHEETAH_IMPL:
+ ecache_assoc = CH_ECACHE_NWAY;
+ node_name = "SUNW,UltraSPARC-III";
+ break;
+ case CHEETAH_PLUS_IMPL:
+ /*
+ * Hard coding the ecache-associativity to 2 for Cheetah+.
+ * We probably should add this to the PCD.
+ */
+ ecache_assoc = CHP_ECACHE_NWAY;
+ node_name = "SUNW,UltraSPARC-III+";
+ break;
+ default:
+ GPTWO_DEBUG1(1, CE_CONT, "set_cpu_us3_props: invalid "
+ "implementation=0x%x\n", impl);
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_string(DDI_DEV_T_NONE, new_child,
+ "name", node_name) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
+ "to create name property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "portid", portid) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
+ "to create portid property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ reg.gptwo_phys_hi = 0x400 | (portid >> 9);
+ reg.gptwo_phys_low = (portid << 23);
+ reg.gptwo_size_hi = 0;
+ reg.gptwo_size_low = 0x10000;
+
+ if (ndi_prop_update_int_array(DDI_DEV_T_NONE,
+ new_child, "reg", (int *)&reg,
+ sizeof (gptwo_regspec_t) / sizeof (int)) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
+ "to create reg property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "icache-size", CH_ICACHE_SIZE) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
+ "to create icache-size property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "icache-line-size", CH_ICACHE_LSIZE) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
+ "to create icache-line-size property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "icache-associativity", CH_ICACHE_NWAY) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
+ "to create icache-associativity property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "dcache-size", CH_DCACHE_SIZE) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
+ "to create dcache-size property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "dcache-line-size", CH_DCACHE_LSIZE) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
+ "to create dcache-line-size property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "dcache-associativity", CH_DCACHE_NWAY) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
+ "to create dcache-associativity property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ /*
+ * Get the External Cache Size from the Common PCD.
+ */
+ ecache_size = pcd->spcd_cache * 0x100000;
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "ecache-size", ecache_size) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
+ "to create ecache-line-size property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ switch (ecache_size) {
+ case CH_ECACHE_1M_SIZE:
+ ecache_line_size = 64;
+ break;
+ case CH_ECACHE_4M_SIZE:
+ ecache_line_size = 256;
+ break;
+ case CH_ECACHE_8M_SIZE:
+ ecache_line_size = 512;
+ break;
+ default:
+ GPTWO_DEBUG1(1, CE_CONT, "set_cpu_us3_props: invalid "
+ "ecache-size 0x%x\b", ecache_size);
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "ecache-line-size", ecache_line_size) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
+ "to create ecache-line-size property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "ecache-associativity", ecache_assoc) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
+ "to create ecache-associativity property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ /*
+ * Create the ecache-dimm-label property.
+ */
+ dimms = 0;
+
+ while ((pcd->sprd_ecache_dimm_label[dimms] != NULL) &&
+ (dimms < MAX_DIMMS_PER_PORT))
+ dimms++;
+
+ if (dimms) {
+ (void) ndi_prop_update_string_array(DDI_DEV_T_NONE, new_child,
+ "ecache-dimm-label", (char **)pcd->sprd_ecache_dimm_label,
+ dimms);
+ }
+
+ return (DDI_WALK_TERMINATE);
+}
+
+/*
+ * Set cmp core node properties for Jaguar and Panther.
+ *
+ * name
+ * compatible
+ * reg
+ * l1-icache-size
+ * l1-icache-line-size
+ * l1-icache-associativity
+ * l1-dcache-size
+ * l1-dcache-line-size
+ * l1-dcache-associativity
+ * l2-cache-size
+ * l2-cache-line-size
+ * l2-cache-associativity
+ * l2-cache-sharing
+ * l3-cache-size
+ * l3-cache-line-size
+ * l3-cache-associativity
+ * l3-cache-sharing
+ */
+static int
+set_cpu_us4_props(dev_info_t *new_child, struct bca *bcp)
+{
+ uint_t l1_icache_size, l1_icache_line_size;
+ uint_t l2_cache_size, l2_cache_line_size, l2_cache_assoc;
+ uint_t l2_cache_share;
+ uint_t pcd_cache_size;
+ uint_t coreid, impl;
+ spcd_t *pcd;
+ char *compatible;
+ int dimms;
+ int i;
+
+ pcd = bcp->pcd;
+ coreid = bcp->coreid;
+ impl = bcp->impl;
+
+ ASSERT(IS_JAGUAR(impl) || IS_PANTHER(impl));
+
+ /*
+ * Get the External Cache Size from the Common PCD.
+ */
+ pcd_cache_size = pcd->spcd_cache * 0x100000;
+
+ switch (impl) {
+ case JAGUAR_IMPL:
+ compatible = "SUNW,UltraSPARC-IV";
+ l1_icache_size = CH_ICACHE_SIZE;
+ l1_icache_line_size = CH_ICACHE_LSIZE;
+ l2_cache_assoc = CHP_ECACHE_NWAY;
+
+ /*
+ * Jaguar has no logical sharing of L2 cache, so the sharing
+ * bit-map will represent this core only.
+ */
+ l2_cache_share = coreid ? 0x2 : 0x1;
+
+ /*
+ * Jaguar has a split ecache, so the total ecache must be
+ * divided in half to get the ecache for the individual core.
+ */
+ l2_cache_size = pcd_cache_size / 2;
+
+ switch (l2_cache_size) {
+ case JG_ECACHE_4M_SIZE:
+ l2_cache_line_size = 64;
+ break;
+ case JG_ECACHE_8M_SIZE:
+ l2_cache_line_size = 128;
+ break;
+ default:
+ GPTWO_DEBUG1(1, CE_CONT, "set_cpu_us4_props: "
+ "invalid l2_cache-size 0x%x\n", l2_cache_size);
+ return (DDI_WALK_ERROR);
+ }
+ break;
+ case PANTHER_IMPL:
+ ASSERT(pcd_cache_size == PN_L3_SIZE);
+ compatible = "SUNW,UltraSPARC-IV+";
+ l1_icache_size = PN_ICACHE_SIZE;
+ l1_icache_line_size = PN_ICACHE_LSIZE;
+ l2_cache_size = PN_L2_SIZE;
+ l2_cache_line_size = PN_L2_LINESIZE;
+ l2_cache_assoc = PN_ECACHE_NWAY;
+
+ /*
+ * For Panther, the L2 and L3 caches are logically shared by
+ * all enabled cores, so the sharing bit-map will represent
+ * all enabled cores. Panther split-mode is still considered
+ * shared.
+ *
+ * Check the PCD status to determine enabled cores.
+ */
+ ASSERT(pcd->spcd_ptype == SAFPTYPE_CPU);
+ l2_cache_share = 0;
+ for (i = 0; i < AGENTS_PER_PORT; i++) {
+ if (pcd->spcd_agent[i] == SPCD_RSV_PASS) {
+ l2_cache_share |= (1 << i);
+ }
+ }
+
+ break;
+ default:
+ GPTWO_DEBUG1(1, CE_CONT, "set_cpu_us4_props: invalid "
+ "implementation=0x%x\n", impl);
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_string(DDI_DEV_T_NONE, new_child,
+ "name", "cpu") != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
+ "to create name property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_string(DDI_DEV_T_NONE, new_child,
+ "compatible", compatible) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
+ "to create compatible property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "reg", coreid) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
+ "to create reg property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "l1-icache-size", l1_icache_size) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
+ "to create l1-icache-size property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "l1-icache-line-size", l1_icache_line_size) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
+ "to create icache-line-size property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "l1-icache-associativity", CH_ICACHE_NWAY) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
+ "to create l1-icache-associativity property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "l1-dcache-size", CH_DCACHE_SIZE) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
+ "to create l1-dcache-size property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "l1-dcache-line-size", CH_DCACHE_LSIZE) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
+ "to create dcache-line-size property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "l1-dcache-associativity", CH_DCACHE_NWAY) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
+ "to create l1-dcache-associativity property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "l2-cache-size", l2_cache_size) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
+ "to create l2-cache-size property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "l2-cache-line-size", l2_cache_line_size) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
+ "to create l2_cache-line-size property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "l2-cache-associativity", l2_cache_assoc) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
+ "to create l2-cache-associativity property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "l2-cache-sharing", l2_cache_share) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
+ "to create l2-cache-sharing property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ /*
+ * Create the ecache-dimm-label property.
+ */
+ dimms = 0;
+
+ while ((pcd->sprd_ecache_dimm_label[dimms] != NULL) &&
+ (dimms < MAX_DIMMS_PER_PORT))
+ dimms++;
+
+ if (dimms) {
+ (void) ndi_prop_update_string_array(DDI_DEV_T_NONE, new_child,
+ "ecache-dimm-label", (char **)pcd->sprd_ecache_dimm_label,
+ dimms);
+ }
+
+ if (IS_PANTHER(impl)) {
+ int l3_cache_share = l2_cache_share;
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "l3-cache-size", PN_L3_SIZE) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: "
+ "failed to create l3-cache-size property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "l3-cache-line-size", PN_L3_LINESIZE) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: "
+ "failed to create l3-cache-line-size property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "l3-cache-associativity", PN_ECACHE_NWAY) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: "
+ "failed to create l3-cache-associativity "
+ "property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "l3-cache-sharing", l3_cache_share) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: "
+ "failed to create l3-cache-sharing property\n");
+ return (DDI_WALK_ERROR);
+ }
+ }
+
+ return (DDI_WALK_TERMINATE);
+}
+
+static dev_info_t *
+gptwocfg_create_mc_node(dev_info_t *ap, spcd_t *pcd, uint_t portid)
+{
+ struct bca arg;
+ devi_branch_t b = {0};
+
+ arg.pcd = pcd;
+ arg.portid = portid;
+ arg.cpuid = portid;
+ arg.new_child = NULL;
+
+ b.arg = &arg;
+ b.type = DEVI_BRANCH_SID;
+ b.create.sid_branch_create = set_mc_props;
+ b.devi_branch_callback = get_new_child;
+
+ if (e_ddi_branch_create(ap, &b, NULL, 0))
+ return (NULL);
+
+ return (arg.new_child);
+}
+
+/*ARGSUSED*/
+static int
+set_mc_props(dev_info_t *new_child, void *arg, uint_t flags)
+{
+ struct bca *bcp = arg;
+ gptwo_regspec_t reg;
+ int banks, dimms;
+ spcd_t *pcd = bcp->pcd;
+ uint_t portid = bcp->portid;
+ uint_t cpuid = bcp->cpuid;
+
+ GPTWO_DEBUG3(1, CE_CONT, "set_mc_props: ap=0x%lx portid=0x%x "
+ "cpuid=0x%x\n", ddi_get_parent(new_child), portid, cpuid);
+
+ if (ndi_prop_update_string(DDI_DEV_T_NONE, new_child,
+ "name", "memory-controller") != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_mc_props: failed "
+ "to create name property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_string(DDI_DEV_T_NONE, new_child,
+ "compatible", "SUNW,UltraSPARC-III,mc") != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_mc_props: failed "
+ "to create compatible property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_string(DDI_DEV_T_NONE, new_child,
+ "device_type", "memory-controller") != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_mc_props: failed "
+ "to create device_type property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "portid", portid) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_mc_props: failed "
+ "to create portid property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
+ "cpuid", cpuid) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_mc_props: failed "
+ "to create cpuid property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ reg.gptwo_phys_hi = 0x400 | (portid >> 9);
+ reg.gptwo_phys_low = (portid << 23) | 0x400000;
+ reg.gptwo_size_hi = 0;
+ reg.gptwo_size_low = 0x48;
+
+ if (ndi_prop_update_int_array(DDI_DEV_T_NONE,
+ new_child, "reg", (int *)&reg,
+ sizeof (gptwo_regspec_t) / sizeof (int)) != DDI_SUCCESS) {
+ GPTWO_DEBUG0(1, CE_CONT, "set_mc_props: failed "
+ "to create reg property\n");
+ return (DDI_WALK_ERROR);
+ }
+
+ if (pcd->memory_layout) {
+ if (ndi_prop_update_byte_array(DDI_DEV_T_NONE,
+ new_child, "memory-layout", (uchar_t *)pcd->memory_layout,
+ pcd->memory_layout_size) != DDI_SUCCESS) {
+
+ GPTWO_DEBUG0(1, CE_CONT, "set_mc_props: failed "
+ "to create memory-layout property\n");
+
+ return (DDI_WALK_ERROR);
+ }
+ }
+
+ /*
+ * Create the bank-status property.
+ */
+ banks = 0;
+
+ while ((pcd->sprd_bank_rsv[banks] != NULL) &&
+ (banks < MAX_BANKS_PER_PORT))
+ banks++;
+
+ if (banks) {
+ (void) ndi_prop_update_string_array(DDI_DEV_T_NONE, new_child,
+ "bank-status", (char **)pcd->sprd_bank_rsv, banks);
+ }
+
+ /*
+ * Create the dimm-status property.
+ */
+ dimms = 0;
+
+ while ((pcd->sprd_dimm[dimms] != NULL) &&
+ (dimms < MAX_DIMMS_PER_PORT))
+ dimms++;
+
+ if (dimms) {
+ (void) ndi_prop_update_string_array(DDI_DEV_T_NONE, new_child,
+ "dimm-status", (char **)pcd->sprd_dimm, dimms);
+ }
+
+
+ return (DDI_WALK_TERMINATE);
+}
+
+/*ARGSUSED*/
+static void
+get_new_child(dev_info_t *rdip, void *arg, uint_t flags)
+{
+ struct bca *bcp = arg;
+
+ bcp->new_child = rdip;
+
+}
+
+#ifdef DEBUG
+static void
+debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3,
+ uintptr_t a4, uintptr_t a5)
+{
+ cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5);
+}
+#endif
diff --git a/usr/src/uts/sun4u/io/gptwocfg.c b/usr/src/uts/sun4u/io/gptwocfg.c
new file mode 100644
index 0000000000..e1a2bb6347
--- /dev/null
+++ b/usr/src/uts/sun4u/io/gptwocfg.c
@@ -0,0 +1,683 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Safari Configurator (gptwocfg)
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/cred.h>
+#include <sys/mman.h>
+#include <sys/kmem.h>
+#include <sys/conf.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/modctl.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/autoconf.h>
+#include <sys/ksynch.h>
+#include <sys/promif.h>
+#include <sys/ndi_impldefs.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/gp2cfg.h>
+#include <sys/machsystm.h>
+#include <sys/platform_module.h>
+#pragma weak starcat_dr_name
+
+#ifdef DEBUG
+int gptwocfg_debug = 0;
+
+static void debug(char *, uintptr_t, uintptr_t,
+ uintptr_t, uintptr_t, uintptr_t);
+
+#define GPTWO_DEBUG0(level, flag, s) if (gptwocfg_debug >= level) \
+ cmn_err(flag, s)
+#define GPTWO_DEBUG1(level, flag, fmt, a1) if (gptwocfg_debug >= level) \
+ debug(fmt, (uintptr_t)(a1), 0, 0, 0, 0);
+#define GPTWO_DEBUG2(level, flag, fmt, a1, a2) if (gptwocfg_debug >= level) \
+ debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), 0, 0, 0);
+#define GPTWO_DEBUG3(level, flag, fmt, a1, a2, a3) \
+ if (gptwocfg_debug >= level) \
+ debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), (uintptr_t)(a3), 0, 0);
+#else
+#define GPTWO_DEBUG0(level, flag, s)
+#define GPTWO_DEBUG1(level, flag, fmt, a1)
+#define GPTWO_DEBUG2(level, flag, fmt, a1, a2)
+#define GPTWO_DEBUG3(level, flag, fmt, a1, a2, a3)
+#endif
+
+kmutex_t gptwo_handle_list_lock;
+gptwocfg_handle_list_t *gptwocfg_handle_list;
+
+static kmutex_t gptwo_config_list_lock;
+static gptwocfg_config_t *gptwo_config_list;
+
+static gptwo_new_nodes_t *
+ gptwocfg_get_obp_created_nodes(dev_info_t *, uint_t);
+
+void (*gptwocfg_unclaim_address)(uint_t);
+
+extern caddr_t efcode_vaddr;
+extern int efcode_size;
+
+#define GPTWO_NUMBER_OF_DEVICE_TYPES 6
+
+static kmutex_t gptwocfg_ops_table_lock;
+gptwocfg_ops_t *gptwocfg_ops_table[GPTWO_NUMBER_OF_DEVICE_TYPES];
+
+/*
+ * Module linkage information for the kernel.
+ */
+
+extern struct mod_ops mod_miscops;
+
+static struct modlmisc modlmisc = {
+ &mod_miscops, /* Type of module */
+ "gptwo configurator %I%",
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&modlmisc, NULL
+};
+
+int
+_init(void)
+{
+ unsigned int i;
+
+ GPTWO_DEBUG0(1, CE_WARN, "gptwocfg (Safari Configurator) "
+ "has been loaded\n");
+
+ mutex_init(&gptwo_config_list_lock, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&gptwocfg_ops_table_lock, NULL, MUTEX_DRIVER, NULL);
+ gptwo_config_list = NULL;
+
+ mutex_init(&gptwo_handle_list_lock, NULL, MUTEX_DRIVER, NULL);
+ gptwocfg_handle_list = NULL;
+
+ for (i = 0; i < GPTWO_NUMBER_OF_DEVICE_TYPES; i++)
+ gptwocfg_ops_table[i] = NULL;
+
+ return (mod_install(&modlinkage));
+}
+
+int
+_fini(void)
+{
+ int error;
+
+ error = mod_remove(&modlinkage);
+ if (error != 0) {
+ return (error);
+ }
+ mutex_destroy(&gptwo_config_list_lock);
+ mutex_destroy(&gptwocfg_ops_table_lock);
+ mutex_destroy(&gptwo_handle_list_lock);
+
+ return (0);
+}
+
+int
+_info(modinfop)
+struct modinfo *modinfop;
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+gptwo_new_nodes_t *
+gptwocfg_allocate_node_list(int number_of_nodes)
+{
+ gptwo_new_nodes_t *gptwo_new_nodes;
+ int size;
+
+ GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_allocate_node_list- %d nodes",
+ number_of_nodes);
+
+ size = sizeof (gptwo_new_nodes_t) +
+ ((number_of_nodes -1) * sizeof (dev_info_t *));
+
+ gptwo_new_nodes = kmem_zalloc(size, KM_SLEEP);
+
+ gptwo_new_nodes->gptwo_number_of_nodes = number_of_nodes;
+ gptwo_new_nodes->gptwo_version = GP2_VERSION;
+
+ GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_allocate_node_list- returned %p\n",
+ gptwo_new_nodes);
+
+ return (gptwo_new_nodes);
+}
+
+void
+gptwocfg_free_node_list(gptwo_new_nodes_t *gptwo_new_nodes)
+{
+ int size;
+
+ GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_free_node_list- %p %d nodes",
+ gptwo_new_nodes, gptwo_new_nodes->gptwo_number_of_nodes);
+
+ size = sizeof (gptwo_new_nodes_t) +
+ ((gptwo_new_nodes->gptwo_number_of_nodes - 1) *
+ sizeof (dev_info_t *));
+
+ kmem_free(gptwo_new_nodes, size);
+}
+
+void
+gptwocfg_register_ops(uint_t type, gptwo_cfgfunc_t *cfg_func,
+ gptwo_uncfgfunc_t *uncfg_func)
+{
+ /* KM_SLEEP guarantees success */
+ gptwocfg_ops_t *ops = kmem_zalloc(sizeof (gptwocfg_ops_t), KM_SLEEP);
+
+ GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_register_ops: type=%x ops=%lx\n",
+ type, ops);
+ ASSERT(type < GPTWO_NUMBER_OF_DEVICE_TYPES);
+ ops->gptwocfg_type = type;
+ ops->gptwocfg_version = GPTWOCFG_OPS_VERSION;
+ ops->gptwocfg_configure = cfg_func;
+ ops->gptwocfg_unconfigure = uncfg_func;
+
+ mutex_enter(&gptwocfg_ops_table_lock);
+ gptwocfg_ops_table[type] = ops;
+ mutex_exit(&gptwocfg_ops_table_lock);
+}
+
+
+
+void
+gptwocfg_unregister_ops(uint_t type)
+{
+ GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_unregister_ops: type=%x\n", type);
+
+ ASSERT(type < GPTWO_NUMBER_OF_DEVICE_TYPES);
+
+ mutex_enter(&gptwocfg_ops_table_lock);
+ kmem_free(gptwocfg_ops_table[type], sizeof (gptwocfg_ops_t));
+ gptwocfg_ops_table[type] = NULL;
+ mutex_exit(&gptwocfg_ops_table_lock);
+}
+
+gptwocfg_cookie_t
+gptwocfg_configure(dev_info_t *ap, spcd_t *pcd, gptwo_aid_t id)
+{
+ gptwo_new_nodes_t *new_nodes = NULL;
+ gptwocfg_config_t *config;
+ gptwocfg_ops_t *ops;
+
+ GPTWO_DEBUG3(1, CE_CONT, "gptwocfg_configure: ap=0x%p pcd=%p id=%x\n",
+ ap, pcd, id);
+
+ /*
+ * Look to see if the port is already configured.
+ */
+ mutex_enter(&gptwo_config_list_lock);
+ config = gptwo_config_list;
+ while (config != NULL) {
+ if (&starcat_dr_name) {
+ if (starcat_dr_name(ddi_node_name(ap)) < 0) {
+ config = config->gptwo_next;
+ continue;
+ }
+ }
+ if (config->gptwo_portid == id) {
+ cmn_err(CE_WARN, "gptwocfg: gptwocfg_configure: "
+ "0x%x Port already configured\n", id);
+ mutex_exit(&gptwo_config_list_lock);
+ return (NULL);
+ }
+ config = config->gptwo_next;
+ }
+ mutex_exit(&gptwo_config_list_lock);
+
+ if (pcd == NULL) {
+ GPTWO_DEBUG0(1, CE_CONT, "gptwocfg_configure: pcd=NULL\n");
+ return (NULL);
+ }
+
+ if ((pcd->spcd_magic != PCD_MAGIC) ||
+ (pcd->spcd_version != PCD_VERSION)) {
+ cmn_err(CE_WARN, "gptwocfg: Invalid Port "
+ "Configuration Descriptor\n");
+ return (NULL);
+ }
+
+ if (pcd->spcd_ptype >= GPTWO_NUMBER_OF_DEVICE_TYPES) {
+ cmn_err(CE_WARN,
+ "gptwocfg: Invalid device type %x", pcd->spcd_ptype);
+ return (NULL);
+ }
+
+ if (pcd->spcd_prsv != SPCD_RSV_PASS) {
+ cmn_err(CE_WARN,
+ "gptwocfg: Agent at ID %x has not passed test(s)\n", id);
+ return (NULL);
+ }
+
+ mutex_enter(&gptwocfg_ops_table_lock);
+
+ ops = gptwocfg_ops_table[pcd->spcd_ptype];
+
+ if (ops == NULL) {
+ cmn_err(CE_WARN, "gptwocfg: Ops for type %x have not been "
+ "registered\n", pcd->spcd_ptype);
+ mutex_exit(&gptwocfg_ops_table_lock);
+ return (NULL);
+ }
+
+ if (ops->gptwocfg_configure == NULL) {
+ cmn_err(CE_WARN, "gptwocfg: no configure routine registered "
+ "for sfaari type %x\n", pcd->spcd_ptype);
+ mutex_exit(&gptwocfg_ops_table_lock);
+ return (NULL);
+ }
+
+ new_nodes = ops->gptwocfg_configure(ap, pcd, id);
+
+ mutex_exit(&gptwocfg_ops_table_lock);
+
+ if (new_nodes != NULL) {
+ config = kmem_zalloc(sizeof (gptwocfg_config_t), KM_SLEEP);
+ config->gptwo_version = GP2_VERSION;
+ config->gptwo_ap = ap;
+ config->gptwo_portid = id;
+ config->gptwo_nodes = new_nodes;
+ config->gptwo_ops = ops;
+
+ /*
+ * put config on config list
+ */
+ mutex_enter(&gptwo_config_list_lock);
+ config->gptwo_next = gptwo_config_list;
+ gptwo_config_list = config;
+ mutex_exit(&gptwo_config_list_lock);
+ } else {
+ config = NULL;
+ }
+
+ return ((gptwocfg_cookie_t)config);
+}
+
+gptwocfg_cookie_t
+gptwocfg_unconfigure(dev_info_t *ap, gptwo_aid_t id)
+{
+ int i, circ;
+ int failure = 0;
+ dev_info_t *saf_dip;
+ gptwocfg_config_t *config, *temp;
+ gptwo_new_nodes_t *obp_nodes;
+ gptwocfg_ops_t *ops;
+
+ GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_unconfigure: ap=0x%p id=0x%lx\n",
+ ap, id);
+
+ mutex_enter(&gptwo_config_list_lock);
+ config = gptwo_config_list;
+ while (config != NULL) {
+ if (config->gptwo_portid == id) {
+ break;
+ }
+ config = config->gptwo_next;
+ }
+ mutex_exit(&gptwo_config_list_lock);
+
+ if (config == NULL) {
+ /*
+ * There is no config structure associated with this agent id
+ * so it was probably built by firmware at start of day. We
+ * need to create a config structure before we can continue.
+ */
+ GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_unconfigure: id=0x%lx "
+ "No config structure - Need to build one\n", id);
+
+ obp_nodes = gptwocfg_get_obp_created_nodes(ap, id);
+
+ if (obp_nodes != NULL) {
+ config = kmem_zalloc(sizeof (gptwocfg_config_t),
+ KM_SLEEP);
+ config->gptwo_version = GP2_VERSION;
+ config->gptwo_ap = ap;
+ config->gptwo_portid = id;
+ config->gptwo_nodes = obp_nodes;
+
+ /*
+ * put config on config list
+ */
+ mutex_enter(&gptwo_config_list_lock);
+ config->gptwo_next = gptwo_config_list;
+ gptwo_config_list = config;
+ mutex_exit(&gptwo_config_list_lock);
+ } else {
+ cmn_err(CE_WARN, "gptwocfg: gptwocfg_unconfigure: "
+ "No OBP created nodes for ap=0x%lx agent id=0x%x",
+ (long)ap, id);
+ return (NULL);
+ }
+ }
+
+ GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_unconfigure config=0x%lx\n",
+ config);
+
+ ops = config->gptwo_ops;
+
+ GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_unconfigure: ops=%lx\n", ops);
+
+ ndi_devi_enter(ap, &circ);
+
+ for (i = 0; i < config->gptwo_nodes->gptwo_number_of_nodes; i++) {
+ dev_info_t *fdip = NULL;
+
+ saf_dip = config->gptwo_nodes->gptwo_nodes[i];
+
+ GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_unconfigure saf_dip=0x%lx\n",
+ saf_dip);
+
+ if (saf_dip == NULL) {
+ GPTWO_DEBUG0(1, CE_CONT, "gptwocfg_unconfigure: "
+ "skipping NULLL saf device\n");
+
+ continue;
+ }
+
+ config->gptwo_nodes->gptwo_nodes[i] = NULL;
+
+ if (ops) {
+ GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_configure "
+ "ops->gptwocfg_configure=%lx\n",
+ ops->gptwocfg_configure);
+
+ GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_unconfigure "
+ "ops->gptwocfg_unconfigure=%lx\n",
+ ops->gptwocfg_unconfigure);
+
+ if (ops->gptwocfg_unconfigure != NULL) {
+ config->gptwo_nodes->gptwo_nodes[i] =
+ ops->gptwocfg_unconfigure(saf_dip);
+
+ }
+ }
+
+ GPTWO_DEBUG1(1, CE_CONT, "e_ddi_branch_destroy <%s>\n",
+ ddi_get_name(saf_dip));
+
+ ASSERT(e_ddi_branch_held(saf_dip));
+
+ /*
+ * Don't hold parent busy when calling
+ * e_ddi_branch_unconfigure/destroy/referenced()
+ */
+ ndi_devi_exit(ap, circ);
+ if (e_ddi_branch_destroy(saf_dip, &fdip, 0)) {
+ char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+
+ /*
+ * If non-NULL, fdip is held and must be released.
+ */
+ if (fdip != NULL) {
+ (void) ddi_pathname(fdip, path);
+ ddi_release_devi(fdip);
+ } else {
+ (void) ddi_pathname(saf_dip, path);
+ }
+
+ cmn_err(CE_WARN, "saf node removal failed: %s (%p)",
+ path, fdip ? (void *)fdip : (void *)saf_dip);
+
+ kmem_free(path, MAXPATHLEN);
+
+ config->gptwo_nodes->gptwo_nodes[i] = saf_dip;
+ failure = 1;
+ }
+ ndi_devi_enter(ap, &circ);
+ }
+
+ ndi_devi_exit(ap, circ);
+
+ if (!failure) {
+ gptwocfg_free_node_list(config->gptwo_nodes);
+
+ mutex_enter(&gptwo_config_list_lock);
+ if (gptwo_config_list == config) {
+ gptwo_config_list = config->gptwo_next;
+ } else {
+ temp = gptwo_config_list;
+ while (temp->gptwo_next != config) {
+ temp = temp->gptwo_next;
+ }
+ temp->gptwo_next = config->gptwo_next;
+ }
+ mutex_exit(&gptwo_config_list_lock);
+
+ kmem_free(config, sizeof (gptwocfg_config_t));
+ config = NULL;
+ }
+
+ return (config);
+}
+
+int
+gptwocfg_next_node(gptwocfg_cookie_t c, dev_info_t *previous, dev_info_t **next)
+{
+ gptwocfg_config_t *cookie;
+ int i, j;
+
+ GPTWO_DEBUG3(1, CE_WARN, "gptwocfg_next_node"
+ "(c=0x%lx, previous=0x%lx, next=0x%lx)\n", c, previous, next);
+
+ cookie = (gptwocfg_config_t *)c;
+
+ for (i = 0; i < cookie->gptwo_nodes->gptwo_number_of_nodes; i++) {
+ GPTWO_DEBUG1(1, CE_WARN, "0x%lx\n",
+ cookie->gptwo_nodes->gptwo_nodes[i]);
+ }
+
+ if (previous == NULL) {
+ for (i = 0; i < cookie->gptwo_nodes->gptwo_number_of_nodes;
+ i++) {
+ if (cookie->gptwo_nodes->gptwo_nodes[i]) {
+ *next = cookie->gptwo_nodes->gptwo_nodes[i];
+ GPTWO_DEBUG1(1, CE_WARN, "returned 0x%lx\n",
+ *next);
+ return (1);
+ }
+ }
+ return (0);
+ }
+
+ for (i = 0; i < cookie->gptwo_nodes->gptwo_number_of_nodes; i++) {
+ if (cookie->gptwo_nodes->gptwo_nodes[i] == previous) {
+ for (j = i + 1;
+ j < cookie->gptwo_nodes->gptwo_number_of_nodes;
+ j++) {
+ if (cookie->gptwo_nodes->gptwo_nodes[j]) {
+ *next =
+ cookie->gptwo_nodes->gptwo_nodes[j];
+ GPTWO_DEBUG1(1, CE_WARN,
+ "returned 0x%lx\n", *next);
+ return (1);
+ }
+ }
+ *next = NULL;
+ GPTWO_DEBUG1(1, CE_WARN, "returned 0x%lx\n",
+ *next);
+ return (1);
+ }
+ }
+
+ /*
+ * previous is probably an invalid dev_info.
+ */
+ return (0);
+}
+
+static gptwo_new_nodes_t *
+gptwocfg_get_obp_created_nodes(dev_info_t *ap, uint_t id)
+{
+ gptwo_new_nodes_t *obp_nodes;
+ dev_info_t *saf_dev;
+ int i = 0, nodes = 0;
+ int circular_count;
+
+ GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_get_obp_created_nodes - ap=0x%lx "
+ "id=0x%x\n", ap, id);
+
+ ndi_devi_enter(ap, &circular_count);
+
+ /*
+ * First go through all the children of the attachment point
+ * to count matching safari agent ids
+ */
+ saf_dev = ddi_get_child(ap);
+ while (saf_dev != NULL) {
+ if (ddi_getprop(DDI_DEV_T_ANY, saf_dev, DDI_PROP_DONTPASS,
+ "portid", -1) == id) {
+ if (&starcat_dr_name) {
+ if (starcat_dr_name(ddi_node_name(saf_dev))
+ < 0) {
+ saf_dev = ddi_get_next_sibling(saf_dev);
+ continue;
+ }
+ }
+ nodes++;
+ }
+ saf_dev = ddi_get_next_sibling(saf_dev);
+ }
+
+ GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_get_obp_created_nodes - %d nodes "
+ "found\n", nodes);
+
+ obp_nodes = gptwocfg_allocate_node_list(nodes);
+
+ /*
+ * Then fill in the nodes structure.
+ */
+ saf_dev = ddi_get_child(ap);
+ while ((saf_dev != NULL) && (i < nodes)) {
+ if (ddi_getprop(DDI_DEV_T_ANY, saf_dev, DDI_PROP_DONTPASS,
+ "portid", -1) == id) {
+ if (&starcat_dr_name) {
+ if (starcat_dr_name(ddi_node_name(saf_dev))
+ < 0) {
+ saf_dev = ddi_get_next_sibling(saf_dev);
+ continue;
+ }
+ }
+ /*
+ * Branch rooted at this dip must have been
+ * held by the DR driver.
+ */
+ ASSERT(e_ddi_branch_held(saf_dev));
+ obp_nodes->gptwo_nodes[i++] = saf_dev;
+ }
+ saf_dev = ddi_get_next_sibling(saf_dev);
+ }
+
+ ndi_devi_exit(ap, circular_count);
+
+ GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_get_obp_created_nodes - "
+ "Returning 0x%lx\n", obp_nodes);
+
+ return (obp_nodes);
+}
+
+void
+gptwocfg_save_handle(dev_info_t *dip, fco_handle_t fco_handle)
+{
+ gptwocfg_handle_list_t *h;
+
+ GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_save_handle - "
+ "dip=%lx fco_handle=%lx\n", dip, fco_handle);
+
+ h = kmem_zalloc(sizeof (gptwocfg_handle_list_t), KM_SLEEP);
+
+ mutex_enter(&gptwo_handle_list_lock);
+
+ h->next = gptwocfg_handle_list;
+ h->dip = dip;
+ h->fco_handle = fco_handle;
+ gptwocfg_handle_list = h;
+
+ mutex_exit(&gptwo_handle_list_lock);
+}
+
+fco_handle_t
+gptwocfg_get_handle(dev_info_t *dip)
+{
+ gptwocfg_handle_list_t *h, *last;
+ fco_handle_t fco_handle;
+
+ mutex_enter(&gptwo_handle_list_lock);
+
+ h = last = gptwocfg_handle_list;
+
+ while (h != NULL) {
+ if (h->dip == dip) {
+ if (h == gptwocfg_handle_list)
+ gptwocfg_handle_list = h->next;
+ else
+ last->next = h->next;
+
+ mutex_exit(&gptwo_handle_list_lock);
+
+ fco_handle = h->fco_handle;
+
+ kmem_free(h, sizeof (gptwocfg_handle_list_t));
+
+ GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_get_handle - "
+ "dip=%lx fco_handle=%lx\n", dip, fco_handle);
+
+ return (fco_handle);
+ }
+ last = h;
+ h = h->next;
+ }
+
+ mutex_exit(&gptwo_handle_list_lock);
+
+ GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_get_handle - dip=%lx NO HANDLE\n",
+ dip);
+
+ return (0);
+}
+
+void
+gptwocfg_devi_attach_to_parent(dev_info_t *dip)
+{
+ (void) i_ndi_config_node(dip, DS_LINKED, 0);
+}
+
+#ifdef DEBUG
+static void
+debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3,
+ uintptr_t a4, uintptr_t a5)
+{
+ cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5);
+}
+#endif
diff --git a/usr/src/uts/sun4u/io/i2c/nexus/i2bsc.c b/usr/src/uts/sun4u/io/i2c/nexus/i2bsc.c
new file mode 100644
index 0000000000..969b8baa9e
--- /dev/null
+++ b/usr/src/uts/sun4u/io/i2c/nexus/i2bsc.c
@@ -0,0 +1,1257 @@
+/*
+ * 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"
+
+/*
+ * i2bsc.c is the nexus driver i2c traffic against devices hidden behind the
+ * Blade Support Chip (BSC). It supports both interrupt and polled
+ * mode operation, but defaults to interrupt.
+ */
+
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/open.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/modctl.h>
+#include <sys/stat.h>
+#include <sys/kmem.h>
+#include <sys/platform_module.h>
+#include <sys/stream.h>
+#include <sys/strlog.h>
+#include <sys/log.h>
+#include <sys/debug.h>
+#include <sys/note.h>
+
+#include <sys/bscbus.h>
+#include <sys/lom_ebuscodes.h>
+
+#include <sys/i2c/clients/i2c_client.h>
+#include <sys/i2c/misc/i2c_svc.h>
+#include <sys/i2c/misc/i2c_svc_impl.h>
+#include <sys/i2c/nexus/i2bsc_impl.h>
+
+/*
+ * static function declarations
+ */
+static void i2bsc_resume(dev_info_t *dip);
+static void i2bsc_suspend(dev_info_t *dip);
+static int i2bsc_bus_ctl(dev_info_t *dip, dev_info_t *rdip,
+ ddi_ctl_enum_t op, void *arg, void *result);
+static void i2bsc_acquire(i2bsc_t *, dev_info_t *dip,
+ i2c_transfer_t *tp);
+static void i2bsc_release(i2bsc_t *);
+static int i2bsc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+static int i2bsc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+static int i2bsc_open(dev_t *devp, int flag, int otyp,
+ cred_t *cred_p);
+static int i2bsc_close(dev_t dev, int flag, int otyp,
+ cred_t *cred_p);
+static int i2bsc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+static int i2bsc_initchild(dev_info_t *dip, dev_info_t *cdip);
+static int i2bsc_uninitchild(dev_info_t *dip, dev_info_t *cdip);
+static int i2bsc_setup_regs(i2bsc_t *);
+static void i2bsc_start_session(i2bsc_t *);
+static void i2bsc_fail_session(i2bsc_t *);
+static int i2bsc_end_session(i2bsc_t *);
+static void i2bsc_free_regs(i2bsc_t *);
+static int i2bsc_reportdev(dev_info_t *dip, dev_info_t *rdip);
+int i2bsc_transfer(dev_info_t *dip, i2c_transfer_t *tp);
+static void i2bsc_trace(i2bsc_t *, char, const char *,
+ const char *, ...);
+static int i2bsc_notify_max_transfer_size(i2bsc_t *);
+static int i2bsc_discover_capability(i2bsc_t *);
+static void i2bsc_put8(i2bsc_t *, uint8_t, uint8_t, uint8_t);
+static uint8_t i2bsc_get8(i2bsc_t *, uint8_t, uint8_t);
+static int i2bsc_safe_upload(i2bsc_t *, i2c_transfer_t *);
+static boolean_t i2bsc_is_firmware_broken(i2bsc_t *);
+
+static struct bus_ops i2bsc_busops = {
+ BUSO_REV,
+ nullbusmap, /* bus_map */
+ NULL, /* bus_get_intrspec */
+ NULL, /* bus_add_intrspec */
+ NULL, /* bus_remove_intrspec */
+ NULL, /* bus_map_fault */
+ ddi_no_dma_map, /* bus_dma_map */
+ ddi_no_dma_allochdl, /* bus_dma_allochdl */
+ ddi_no_dma_freehdl, /* bus_dma_freehdl */
+ ddi_no_dma_bindhdl, /* bus_dma_bindhdl */
+ ddi_no_dma_unbindhdl, /* bus_unbindhdl */
+ ddi_no_dma_flush, /* bus_dma_flush */
+ ddi_no_dma_win, /* bus_dma_win */
+ ddi_no_dma_mctl, /* bus_dma_ctl */
+ i2bsc_bus_ctl, /* bus_ctl */
+ ddi_bus_prop_op, /* bus_prop_op */
+ NULL, /* bus_get_eventcookie */
+ NULL, /* bus_add_eventcall */
+ NULL, /* bus_remove_eventcall */
+ NULL, /* bus_post_event */
+ 0, /* bus_intr_ctl */
+ 0, /* bus_config */
+ 0, /* bus_unconfig */
+ 0, /* bus_fm_init */
+ 0, /* bus_fm_fini */
+ 0, /* bus_fm_access_enter */
+ 0, /* bus_fm_access_exit */
+ 0, /* bus_power */
+ i_ddi_intr_ops /* bus_intr_op */
+
+};
+
+struct cb_ops i2bsc_cb_ops = {
+ i2bsc_open, /* open */
+ i2bsc_close, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ i2bsc_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ ddi_prop_op, /* cb_prop_op */
+ 0, /* streamtab */
+ D_MP | D_NEW /* Driver compatibility flag */
+};
+
+static struct dev_ops i2bsc_ops = {
+ DEVO_REV,
+ 0,
+ ddi_getinfo_1to1,
+ nulldev,
+ nulldev,
+ i2bsc_attach,
+ i2bsc_detach,
+ nodev,
+ &i2bsc_cb_ops,
+ &i2bsc_busops
+};
+
+#ifdef DEBUG
+#define I2BSC_VERSION_STRING "i2bsc driver - Debug v%I%"
+#else
+#define I2BSC_VERSION_STRING "i2bsc driver v%I%"
+#endif
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* Type of module. This one is a driver */
+ I2BSC_VERSION_STRING, /* Name of the module. */
+ &i2bsc_ops, /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ &modldrv,
+ NULL
+};
+
+/*
+ * i2bsc soft state
+ */
+static void *i2bsc_state;
+
+i2c_nexus_reg_t i2bsc_regvec = {
+ I2C_NEXUS_REV,
+ i2bsc_transfer,
+};
+
+int
+_init(void)
+{
+ int status;
+
+ status = ddi_soft_state_init(&i2bsc_state, sizeof (i2bsc_t),
+ I2BSC_INITIAL_SOFT_SPACE);
+ if (status != 0) {
+ return (status);
+ }
+
+ if ((status = mod_install(&modlinkage)) != 0) {
+ ddi_soft_state_fini(&i2bsc_state);
+ }
+
+ return (status);
+}
+
+int
+_fini(void)
+{
+ int status;
+
+ if ((status = mod_remove(&modlinkage)) == 0) {
+ ddi_soft_state_fini(&i2bsc_state);
+ }
+
+ return (status);
+}
+
+/*
+ * The loadable-module _info(9E) entry point
+ */
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+static void
+i2bsc_dodetach(dev_info_t *dip)
+{
+ i2bsc_t *i2c;
+ int instance = ddi_get_instance(dip);
+
+ i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, instance);
+
+ if ((i2c->i2bsc_attachflags & IMUTEX) != 0) {
+ mutex_destroy(&i2c->i2bsc_imutex);
+ cv_destroy(&i2c->i2bsc_icv);
+ }
+ if ((i2c->i2bsc_attachflags & SETUP_REGS) != 0) {
+ i2bsc_free_regs(i2c);
+ }
+ if ((i2c->i2bsc_attachflags & NEXUS_REGISTER) != 0) {
+ i2c_nexus_unregister(dip);
+ }
+ if ((i2c->i2bsc_attachflags & MINOR_NODE) != 0) {
+ ddi_remove_minor_node(dip, NULL);
+ }
+
+ ddi_soft_state_free(i2bsc_state, instance);
+}
+
+static int
+i2bsc_doattach(dev_info_t *dip)
+{
+ i2bsc_t *i2c;
+ int instance = ddi_get_instance(dip);
+
+ /*
+ * Allocate soft state structure.
+ */
+ if (ddi_soft_state_zalloc(i2bsc_state, instance) != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+
+ i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, instance);
+
+ i2c->majornum = ddi_driver_major(dip);
+ i2c->minornum = instance;
+ i2c->i2bsc_dip = dip;
+ i2c->debug = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS, "debug", 0);
+
+ (void) snprintf(i2c->i2bsc_name, sizeof (i2c->i2bsc_name),
+ "%s_%d", ddi_node_name(dip), instance);
+
+ if (i2bsc_setup_regs(i2c) != DDI_SUCCESS) {
+ goto bad;
+ }
+
+ i2c->i2bsc_attachflags |= SETUP_REGS;
+
+ mutex_init(&i2c->i2bsc_imutex, NULL, MUTEX_DRIVER,
+ (void *) 0);
+ cv_init(&i2c->i2bsc_icv, NULL, CV_DRIVER, NULL);
+ i2c->i2bsc_attachflags |= IMUTEX;
+
+ i2c_nexus_register(dip, &i2bsc_regvec);
+ i2c->i2bsc_attachflags |= NEXUS_REGISTER;
+
+ if (ddi_create_minor_node(dip, "devctl", S_IFCHR, instance,
+ DDI_NT_NEXUS, 0) == DDI_FAILURE) {
+ cmn_err(CE_WARN, "%s ddi_create_minor_node failed",
+ i2c->i2bsc_name);
+ goto bad;
+ }
+
+ i2c->i2bsc_attachflags |= MINOR_NODE;
+
+ /*
+ * Now actually start talking to the microcontroller. The first
+ * thing to check is whether the firmware is broken.
+ */
+ if (i2bsc_is_firmware_broken(i2c)) {
+ cmn_err(CE_WARN, "Underlying BSC hardware not communicating;"
+ " shutting down my i2c services");
+ goto bad;
+ }
+
+ i2c->i2bsc_attachflags |= FIRMWARE_ALIVE;
+
+ /*
+ * Now see if the BSC chip supports the i2c service we rely upon.
+ */
+ (void) i2bsc_discover_capability(i2c);
+
+ if (i2bsc_notify_max_transfer_size(i2c) == DDI_SUCCESS)
+ i2c->i2bsc_attachflags |= TRANSFER_SZ;
+
+ i2bsc_trace(i2c, 'A', "i2bsc_doattach", "attachflags %d",
+ i2c->i2bsc_attachflags);
+
+ return (DDI_SUCCESS);
+
+bad:
+ i2bsc_dodetach(dip);
+
+ return (DDI_FAILURE);
+}
+
+static int
+i2bsc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_ATTACH:
+ return (i2bsc_doattach(dip));
+
+ case DDI_RESUME:
+ i2bsc_resume(dip);
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+static int
+i2bsc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_DETACH:
+ i2bsc_dodetach(dip);
+ return (DDI_SUCCESS);
+
+ case DDI_SUSPEND:
+ i2bsc_suspend(dip);
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+/*ARGSUSED*/
+static int
+i2bsc_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
+{
+ int instance;
+ i2bsc_t *i2c;
+
+ /*
+ * Make sure the open is for the right file type
+ */
+ if (otyp != OTYP_CHR)
+ return (EINVAL);
+
+ instance = getminor(*devp);
+ i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, instance);
+ if (i2c == NULL)
+ return (ENXIO);
+
+ /*
+ * Enforce exclusive access
+ */
+ mutex_enter(&i2c->i2bsc_imutex);
+ if (i2c->i2bsc_open) {
+ mutex_exit(&i2c->i2bsc_imutex);
+ return (EBUSY);
+ } else
+ i2c->i2bsc_open = 1;
+
+ mutex_exit(&i2c->i2bsc_imutex);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+i2bsc_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
+{
+ int instance;
+ i2bsc_t *i2c;
+
+ /*
+ * Make sure the close is for the right file type
+ */
+ if (otyp != OTYP_CHR)
+ return (EINVAL);
+
+ instance = getminor(dev);
+ i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, instance);
+ if (i2c == NULL)
+ return (ENXIO);
+
+ mutex_enter(&i2c->i2bsc_imutex);
+ i2c->i2bsc_open = 0;
+ mutex_exit(&i2c->i2bsc_imutex);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+i2bsc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
+{
+ i2bsc_t *i2c;
+ dev_info_t *self;
+ dev_info_t *child;
+ struct devctl_iocdata *dcp;
+ int rv;
+
+ i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, getminor(dev));
+
+ if (i2c == NULL)
+ return (ENXIO);
+
+ self = (dev_info_t *)i2c->i2bsc_dip;
+
+ /*
+ * read devctl ioctl data
+ */
+ if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) {
+ return (EFAULT);
+ }
+
+ switch (cmd) {
+ case DEVCTL_BUS_DEV_CREATE:
+ rv = ndi_dc_devi_create(dcp, self, 0, NULL);
+ break;
+
+ case DEVCTL_DEVICE_REMOVE:
+ if (ndi_dc_getname(dcp) == NULL ||
+ ndi_dc_getaddr(dcp) == NULL) {
+ rv = EINVAL;
+ break;
+ }
+
+ /*
+ * lookup and hold child device
+ */
+ child = ndi_devi_find(self,
+ ndi_dc_getname(dcp), ndi_dc_getaddr(dcp));
+ if (child == NULL) {
+ rv = ENXIO;
+ break;
+ }
+
+ if ((rv = ndi_devi_offline(child, NDI_DEVI_REMOVE)) !=
+ NDI_SUCCESS) {
+ rv = (rv == NDI_BUSY) ? EBUSY : EIO;
+ }
+
+ break;
+
+ default:
+ rv = ENOTSUP;
+ }
+
+ ndi_dc_freehdl(dcp);
+
+ return (rv);
+}
+
+static int
+i2bsc_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op,
+ void *arg, void *result)
+{
+ i2bsc_t *i2c;
+ int instance = ddi_get_instance(dip);
+
+ i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, instance);
+
+ i2bsc_trace(i2c, 'A', "i2bsc_bus_ctl", "dip/rdip,op/arg"
+ " %p/%p,%d/%p", dip, rdip, (int)op, arg);
+
+ switch (op) {
+ case DDI_CTLOPS_INITCHILD:
+ return (i2bsc_initchild(dip, (dev_info_t *)arg));
+
+ case DDI_CTLOPS_UNINITCHILD:
+ return (i2bsc_uninitchild(dip, (dev_info_t *)arg));
+
+ case DDI_CTLOPS_REPORTDEV:
+ return (i2bsc_reportdev(dip, rdip));
+
+ case DDI_CTLOPS_DMAPMAPC:
+ case DDI_CTLOPS_POKE:
+ case DDI_CTLOPS_PEEK:
+ case DDI_CTLOPS_IOMIN:
+ case DDI_CTLOPS_REPORTINT:
+ case DDI_CTLOPS_SIDDEV:
+ case DDI_CTLOPS_SLAVEONLY:
+ case DDI_CTLOPS_AFFINITY:
+ case DDI_CTLOPS_PTOB:
+ case DDI_CTLOPS_BTOP:
+ case DDI_CTLOPS_BTOPR:
+ case DDI_CTLOPS_DVMAPAGESIZE:
+ return (DDI_FAILURE);
+
+ default:
+ return (ddi_ctlops(dip, rdip, op, arg, result));
+ }
+}
+
+/*
+ * i2bsc_suspend() is called before the system suspends. Existing
+ * transfer in progress or waiting will complete, but new transfers are
+ * effectively blocked by "acquiring" the bus.
+ */
+static void
+i2bsc_suspend(dev_info_t *dip)
+{
+ i2bsc_t *i2c;
+ int instance;
+
+ instance = ddi_get_instance(dip);
+ i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, instance);
+
+ i2bsc_acquire(i2c, NULL, NULL);
+}
+
+/*
+ * i2bsc_resume() is called when the system resumes from CPR. It releases
+ * the hold that was placed on the i2c bus, which allows any real
+ * transfers to continue.
+ */
+static void
+i2bsc_resume(dev_info_t *dip)
+{
+ i2bsc_t *i2c;
+ int instance;
+
+ instance = ddi_get_instance(dip);
+ i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, instance);
+
+ i2bsc_release(i2c);
+}
+
+/*
+ * i2bsc_acquire() is called by a thread wishing to "own" the I2C bus.
+ * It should not be held across multiple transfers.
+ */
+static void
+i2bsc_acquire(i2bsc_t *i2c, dev_info_t *dip, i2c_transfer_t *tp)
+{
+ mutex_enter(&i2c->i2bsc_imutex);
+ while (i2c->i2bsc_busy) {
+ cv_wait(&i2c->i2bsc_icv, &i2c->i2bsc_imutex);
+ }
+ i2c->i2bsc_busy = 1;
+ i2c->i2bsc_cur_tran = tp;
+ i2c->i2bsc_cur_dip = dip;
+ mutex_exit(&i2c->i2bsc_imutex);
+}
+
+/*
+ * i2bsc_release() is called to release a hold made by i2bsc_acquire().
+ */
+static void
+i2bsc_release(i2bsc_t *i2c)
+{
+ mutex_enter(&i2c->i2bsc_imutex);
+ i2c->i2bsc_busy = 0;
+ i2c->i2bsc_cur_tran = NULL;
+ cv_signal(&i2c->i2bsc_icv);
+ mutex_exit(&i2c->i2bsc_imutex);
+}
+
+static int
+i2bsc_initchild(dev_info_t *dip, dev_info_t *cdip)
+{
+ i2bsc_t *i2c;
+ int32_t address_cells;
+ int len;
+ int32_t regs[3];
+ int err;
+ i2bsc_ppvt_t *ppvt;
+ char name[30];
+
+ i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, ddi_get_instance(dip));
+
+ i2bsc_trace(i2c, 'A', "i2bsc_initchild", "dip/cdip %p/%p", dip, cdip);
+
+ ppvt = kmem_alloc(sizeof (i2bsc_ppvt_t), KM_SLEEP);
+
+ len = sizeof (address_cells);
+
+ err = ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip,
+ DDI_PROP_CANSLEEP, "#address-cells",
+ (caddr_t)&address_cells, &len);
+ if (err != DDI_PROP_SUCCESS || len != sizeof (address_cells)) {
+ return (DDI_FAILURE);
+ }
+
+ len = sizeof (regs);
+ err = ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip,
+ DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP,
+ "reg", (caddr_t)regs, &len);
+ if (err != DDI_PROP_SUCCESS)
+ return (DDI_FAILURE);
+
+ if (address_cells == 1) {
+ ppvt->i2bsc_ppvt_bus = I2BSC_DEFAULT_BUS;
+ ppvt->i2bsc_ppvt_addr = regs[0];
+ (void) sprintf(name, "%x", regs[0]);
+ i2bsc_trace(i2c, 'A', "i2bsc_initchild", "#address-cells = 1"
+ " regs[0] = %d", regs[0]);
+ } else if (address_cells == 2) {
+ ppvt->i2bsc_ppvt_bus = regs[0];
+ ppvt->i2bsc_ppvt_addr = regs[1];
+ (void) sprintf(name, "%x,%x", regs[0], regs[1]);
+ i2bsc_trace(i2c, 'A', "i2bsc_initchild", "#address-cells = 2"
+ " regs[0] = %d, regs[1] = %d", regs[0], regs[1]);
+ } else {
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Attach the parent's private data structure to the child's devinfo
+ * node, and store the child's address on the nexus in the child's
+ * devinfo node.
+ */
+ ddi_set_parent_data(cdip, ppvt);
+ ddi_set_name_addr(cdip, name);
+
+ i2bsc_trace(i2c, 'A', "i2bsc_initchild", "success(%s)",
+ ddi_node_name(cdip));
+
+ return (DDI_SUCCESS);
+}
+
+static int
+i2bsc_uninitchild(dev_info_t *dip, dev_info_t *cdip)
+{
+ i2bsc_t *i2c;
+ i2bsc_ppvt_t *ppvt;
+
+ i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state, ddi_get_instance(dip));
+
+ i2bsc_trace(i2c, 'D', "i2bsc_uninitchild", "dip/cdip %p/%p", dip, cdip);
+
+ ppvt = ddi_get_parent_data(cdip);
+ kmem_free(ppvt, sizeof (i2bsc_ppvt_t));
+
+ ddi_set_parent_data(cdip, NULL);
+ ddi_set_name_addr(cdip, NULL);
+
+ i2bsc_trace(i2c, 'D', "i2bsc_uninitchild", "success(%s)",
+ ddi_node_name(cdip));
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * i2bsc_setup_regs() is called to map in registers specific to
+ * the i2bsc.
+ */
+static int
+i2bsc_setup_regs(i2bsc_t *i2c)
+{
+ int nregs;
+
+ i2c->bscbus_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
+ i2c->bscbus_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
+ i2c->bscbus_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
+
+ if (ddi_dev_nregs(i2c->i2bsc_dip, &nregs) != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+
+ if (nregs < 1) {
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_regs_map_setup(i2c->i2bsc_dip, 0,
+ (caddr_t *)&i2c->bscbus_regs, 0, 0, &i2c->bscbus_attr,
+ &i2c->bscbus_handle) != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * i2bsc_free_regs() frees any registers previously
+ * allocated.
+ */
+static void
+i2bsc_free_regs(i2bsc_t *i2c)
+{
+ if (i2c->bscbus_regs != NULL) {
+ ddi_regs_map_free(&i2c->bscbus_handle);
+ }
+}
+
+/*ARGSUSED*/
+static int
+i2bsc_reportdev(dev_info_t *dip, dev_info_t *rdip)
+{
+ if (rdip == (dev_info_t *)0)
+ return (DDI_FAILURE);
+
+ cmn_err(CE_CONT, "?i2bsc-device: %s@%s, %s%d\n",
+ ddi_node_name(rdip), ddi_get_name_addr(rdip), ddi_driver_name(rdip),
+ ddi_get_instance(rdip));
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * I/O Functions
+ *
+ * i2bsc_{put,get}8_once are wrapper functions to ddi_{get,put}8.
+ * i2bsc_{put,get}8 are equivalent functions but with retry code.
+ * i2bsc_bscbus_state determines underlying bus error status.
+ * i2bsc_clear_acc_fault clears the underlying bus error status.
+ *
+ * I/O Flags
+ *
+ * bscbus_fault - Error register in underlying bus for last IO operation.
+ * session_failure - Set by any failed IO command. This is a sticky flag
+ * reset explicitly using i2bsc_start_session
+ *
+ * Session Management
+ *
+ * i2bsc_{start,end}_session need to be used to detect an error across multiple
+ * gets/puts rather than having to test for an error on each get/put.
+ */
+
+static int i2bsc_bscbus_state(i2bsc_t *i2c)
+{
+ uint32_t retval;
+
+ retval = ddi_get32(i2c->bscbus_handle,
+ (uint32_t *)I2BSC_NEXUS_ADDR(i2c, EBUS_CMD_SPACE_GENERIC,
+ LOMBUS_FAULT_REG));
+ i2c->bscbus_fault = retval;
+
+ return ((retval == 0) ? DDI_SUCCESS : DDI_FAILURE);
+}
+
+static void i2bsc_clear_acc_fault(i2bsc_t *i2c)
+{
+ i2bsc_trace(i2c, '@', "i2bsc_clear_acc_fault", "clearing acc fault");
+ ddi_put32(i2c->bscbus_handle,
+ (uint32_t *)I2BSC_NEXUS_ADDR(i2c, EBUS_CMD_SPACE_GENERIC,
+ LOMBUS_FAULT_REG), 0);
+}
+
+static void
+i2bsc_start_session(i2bsc_t *i2c)
+{
+ i2bsc_trace(i2c, 'S', "i2bsc_start_session", "session started");
+ i2c->bscbus_session_failure = 0;
+}
+
+static void
+i2bsc_fail_session(i2bsc_t *i2c)
+{
+ i2bsc_trace(i2c, 'S', "i2bsc_fail_session", "session failed");
+ i2c->bscbus_session_failure = 1;
+}
+
+static int
+i2bsc_end_session(i2bsc_t *i2c)
+{
+ /*
+ * The ONLY way to get the session status is to end the session.
+ * If clients of the session interface ever wanted the status mid-way
+ * then they are really working with multiple contigious sessions.
+ */
+ i2bsc_trace(i2c, 'S', "i2bsc_end_session", "session ended with %d",
+ i2c->bscbus_session_failure);
+ return ((i2c->bscbus_session_failure) ? DDI_FAILURE : DDI_SUCCESS);
+}
+
+static boolean_t
+i2bsc_is_firmware_broken(i2bsc_t *i2c)
+{
+ int i;
+ int niterations = I2BSC_SHORT_RETRY_LIMIT;
+
+ i2bsc_trace(i2c, 'A', "i2bsc_is_firmware_broken", "called");
+
+ for (i = 0; i < niterations; i++) {
+ (void) ddi_get8(i2c->bscbus_handle,
+ I2BSC_NEXUS_ADDR(i2c, EBUS_CMD_SPACE_I2C,
+ EBUS_IDX12_RESULT));
+ if (i2bsc_bscbus_state(i2c) != DDI_SUCCESS) {
+ i2bsc_clear_acc_fault(i2c);
+ continue;
+ } else {
+ /*
+ * Firmware communication succeeded.
+ */
+ i2bsc_trace(i2c, 'A', "i2bsc_is_firmware_broken",
+ "firmware communications okay");
+ return (B_FALSE);
+ }
+ }
+
+ /*
+ * Firmware is not communicative. Some possible causes :
+ * Broken hardware
+ * BSC held in reset
+ * Corrupt BSC image
+ * OBP incompatiblity preventing drivers loading properly
+ */
+ i2bsc_trace(i2c, 'A', "i2bsc_is_firmware_broken", "%d read fails",
+ niterations);
+ return (B_TRUE);
+}
+
+static void
+i2bsc_put8(i2bsc_t *i2c, uint8_t space, uint8_t index, uint8_t value)
+{
+ int retryable = I2BSC_RETRY_LIMIT;
+
+ i2bsc_trace(i2c, '@', "i2bsc_put8", "(space,index)<-val (%d,%d)<-%d",
+ space, index, value);
+
+ i2bsc_clear_acc_fault(i2c);
+
+ /*
+ * If a session failure has already occurred, reduce the level of
+ * retries to a minimum. This is a optimization of the failure
+ * recovery strategy.
+ */
+ if (i2c->bscbus_session_failure)
+ retryable = 1;
+
+ while (retryable--) {
+ ddi_put8(i2c->bscbus_handle,
+ I2BSC_NEXUS_ADDR(i2c, space, index), value);
+ if (i2bsc_bscbus_state(i2c) != DDI_SUCCESS) {
+ i2bsc_clear_acc_fault(i2c);
+ } else
+ break;
+ }
+
+ if (i2bsc_bscbus_state(i2c) != DDI_SUCCESS)
+ i2bsc_fail_session(i2c);
+
+ i2bsc_trace(i2c, '@', "i2bsc_put8", "tried %d time(s)",
+ I2BSC_RETRY_LIMIT - retryable);
+}
+
+static uint8_t
+i2bsc_get8(i2bsc_t *i2c, uint8_t space, uint8_t index)
+{
+ uint8_t value;
+ int retryable = I2BSC_RETRY_LIMIT;
+
+ i2bsc_clear_acc_fault(i2c);
+
+ /*
+ * If a session failure has already occurred, reduce the level of
+ * retries to a minimum. This is a optimization of the failure
+ * recovery strategy.
+ */
+ if (i2c->bscbus_session_failure)
+ retryable = 1;
+
+ while (retryable--) {
+ value = ddi_get8(i2c->bscbus_handle,
+ I2BSC_NEXUS_ADDR(i2c, space, index));
+ if (i2bsc_bscbus_state(i2c) != DDI_SUCCESS) {
+ i2bsc_clear_acc_fault(i2c);
+ } else
+ break;
+ }
+
+ if (i2bsc_bscbus_state(i2c) != DDI_SUCCESS)
+ i2bsc_fail_session(i2c);
+
+ i2bsc_trace(i2c, '@', "i2bsc_get8", "tried %d time(s)",
+ I2BSC_RETRY_LIMIT - retryable);
+
+ i2bsc_trace(i2c, '@', "i2bsc_get8", "(space,index)->val (%d,%d)->%d",
+ space, index, value);
+ return (value);
+}
+
+static void
+i2bsc_put8_once(i2bsc_t *i2c, uint8_t space, uint8_t index, uint8_t value)
+{
+ i2bsc_trace(i2c, '@', "i2bsc_put8_once",
+ "(space,index)<-val (%d,%d)<-%d", space, index, value);
+
+ i2bsc_clear_acc_fault(i2c);
+
+ ddi_put8(i2c->bscbus_handle,
+ I2BSC_NEXUS_ADDR(i2c, space, index), value);
+
+ if (i2bsc_bscbus_state(i2c) != DDI_SUCCESS)
+ i2bsc_fail_session(i2c);
+}
+
+static uint8_t
+i2bsc_get8_once(i2bsc_t *i2c, uint8_t space, uint8_t index)
+{
+ uint8_t value;
+
+ i2bsc_clear_acc_fault(i2c);
+
+ value = ddi_get8(i2c->bscbus_handle,
+ I2BSC_NEXUS_ADDR(i2c, space, index));
+
+ if (i2bsc_bscbus_state(i2c) != DDI_SUCCESS)
+ i2bsc_fail_session(i2c);
+
+ i2bsc_trace(i2c, '@', "i2bsc_get8_once",
+ "(space,index)->val (%d,%d)->%d", space, index, value);
+
+ return (value);
+}
+
+static int
+i2bsc_notify_max_transfer_size(i2bsc_t *i2c)
+{
+ /*
+ * If the underlying hardware does not support the i2c service and
+ * we are not running in fake_mode, then we cannot set the
+ * MAX_TRANSFER_SZ.
+ */
+ if (i2c->i2c_proxy_support == 0)
+ return (DDI_FAILURE);
+
+ i2bsc_start_session(i2c);
+
+ i2bsc_put8(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_MAX_TRANSFER_SZ,
+ I2BSC_MAX_TRANSFER_SZ);
+
+ if (i2bsc_end_session(i2c) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Discover if the microcontroller implements the I2C Proxy Service this
+ * driver requires. If it does not, i2c transactions will abort with
+ * I2C_FAILURE, unless fake_mode is being used.
+ */
+static int
+i2bsc_discover_capability(i2bsc_t *i2c)
+{
+ i2bsc_start_session(i2c);
+
+ i2c->i2c_proxy_support = i2bsc_get8(i2c, EBUS_CMD_SPACE_GENERIC,
+ EBUS_IDX_CAP0);
+ i2c->i2c_proxy_support &= EBUS_CAP0_I2C_PROXY;
+
+ if (i2bsc_end_session(i2c) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ return (DDI_SUCCESS);
+}
+
+static int
+i2bsc_upload_preamble(i2bsc_t *i2c, i2c_transfer_t *tp)
+{
+ i2bsc_ppvt_t *ppvt;
+ int wr_rd;
+
+ ppvt = ddi_get_parent_data(i2c->i2bsc_cur_dip);
+
+ /* Get a lock on the i2c devices owned by the microcontroller */
+ i2bsc_put8(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_TRANSACTION_LOCK, 1);
+ if (!i2bsc_get8(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_TRANSACTION_LOCK)) {
+ /*
+ * i2c client driver must timeout retry, NOT this nexus
+ */
+ tp->i2c_result = I2C_INCOMPLETE;
+ i2bsc_trace(i2c, 'U', "i2bsc_upload_preamble",
+ "Couldn't get transaction lock");
+ return (tp->i2c_result);
+ }
+
+ i2bsc_put8(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_BUS_ADDRESS,
+ ppvt->i2bsc_ppvt_bus);
+
+ /*
+ * The Solaris architecture for I2C uses 10-bit I2C addresses where
+ * bit-0 is zero (the read/write bit). The microcontroller uses 7 bit
+ * I2C addresses (read/write bit excluded). Hence we need to convert
+ * the address by bit-shifting.
+ */
+ i2bsc_put8(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_CLIENT_ADDRESS,
+ ppvt->i2bsc_ppvt_addr >> 1);
+
+ i2bsc_put8(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_TRANSFER_TYPE,
+ tp->i2c_flags);
+
+ /*
+ * We have only one register used for data input and output. When
+ * a WR_RD is issued, this means we want to do a Random-Access-Read.
+ * First a series of bytes are written which define the address to
+ * read from. In hardware this sets an address pointer. Then a series
+ * of bytes are read. The read/write boundary tells you how many
+ * bytes are to be written before reads will be issued.
+ */
+ if (tp->i2c_flags == I2C_WR_RD)
+ wr_rd = tp->i2c_wlen;
+ else
+ wr_rd = 0;
+
+ i2bsc_put8(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_WR_RD_BOUNDARY, wr_rd);
+
+ return (I2C_SUCCESS);
+}
+
+/*
+ * Function i2bsc_upload
+ *
+ * Description This function runs the i2c transfer protocol down to the
+ * microcontroller. Its goal is to be as reliable as possible.
+ * This is achieved by making all the state-less aspects
+ * re-tryable. For stateful aspects, we take care to ensure the
+ * counters are decremented only when data transfer has been
+ * successful.
+ */
+static int
+i2bsc_upload(i2bsc_t *i2c, i2c_transfer_t *tp)
+{
+ int quota = I2BSC_MAX_TRANSFER_SZ;
+ uint8_t res;
+ int residual;
+
+ /*
+ * Total amount of data outstanding
+ */
+ residual = tp->i2c_w_resid + tp->i2c_r_resid;
+
+ /*
+ * Anything in this session *could* be re-tried without side-effects.
+ * Therefore, error exit codes are I2C_INCOMPLETE rather than
+ * I2C_FAILURE.
+ */
+ i2bsc_start_session(i2c);
+ if (i2bsc_upload_preamble(i2c, tp) != I2C_SUCCESS)
+ return (I2C_INCOMPLETE);
+ if (i2bsc_end_session(i2c) != DDI_SUCCESS)
+ return (I2C_INCOMPLETE);
+
+ /* The writes done here are not retryable */
+ while (tp->i2c_w_resid && quota) {
+ i2bsc_put8_once(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_DATA_INOUT,
+ tp->i2c_wbuf[tp->i2c_wlen - tp->i2c_w_resid]);
+ if (i2bsc_bscbus_state(i2c) == DDI_SUCCESS) {
+ tp->i2c_w_resid--;
+ quota--;
+ residual--;
+ } else {
+ i2bsc_trace(i2c, 'T', "i2bsc_upload", "write failed");
+ return (tp->i2c_result = I2C_INCOMPLETE);
+ }
+ }
+
+ /* The reads done here are not retryable */
+ while (tp->i2c_r_resid && quota) {
+ tp->i2c_rbuf[tp->i2c_rlen - tp->i2c_r_resid] =
+ i2bsc_get8_once(i2c, EBUS_CMD_SPACE_I2C,
+ EBUS_IDX12_DATA_INOUT);
+ if (i2bsc_bscbus_state(i2c) == DDI_SUCCESS) {
+ tp->i2c_r_resid--;
+ quota--;
+ residual--;
+ } else {
+ i2bsc_trace(i2c, 'T', "i2bsc_upload", "read failed");
+ return (tp->i2c_result = I2C_INCOMPLETE);
+ }
+ }
+
+ i2bsc_start_session(i2c);
+
+ /*
+ * A possible future enhancement would be to allow early breakout of the
+ * loops seen above. In such circumstances, "residual" would be non-
+ * zero. This may be useful if we want to support the interruption of
+ * transfer part way through an i2c_transfer_t.
+ */
+ i2bsc_put8(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_RESIDUAL_DATA, residual);
+ res = i2bsc_get8(i2c, EBUS_CMD_SPACE_I2C, EBUS_IDX12_RESULT);
+ if (i2bsc_end_session(i2c) != DDI_SUCCESS)
+ return (tp->i2c_result = I2C_INCOMPLETE);
+
+ switch (res) {
+ case EBUS_I2C_SUCCESS:
+ tp->i2c_result = I2C_SUCCESS;
+ break;
+ case EBUS_I2C_FAILURE:
+ /*
+ * This is rare but possible. A retry may still fix this
+ * so lets allow that by returning I2C_INCOMPLETE.
+ * "hifTxRing still contains 1 bytes" is reported by the
+ * microcontroller when this return value is seen.
+ */
+ i2bsc_trace(i2c, 'T', "i2bsc_upload", "EBUS_I2C_FAILURE"
+ " but returning I2C_INCOMPLETE for possible re-try");
+ tp->i2c_result = I2C_INCOMPLETE;
+ break;
+ case EBUS_I2C_INCOMPLETE:
+ tp->i2c_result = I2C_INCOMPLETE;
+ break;
+ default:
+ tp->i2c_result = I2C_FAILURE;
+ }
+
+ return (tp->i2c_result);
+}
+
+/*
+ * Function i2bsc_safe_upload
+ *
+ * Description This function is called "safe"-upload because it attempts to
+ * do transaction re-tries for cases where state is not spoiled
+ * by a transaction-level retry.
+ */
+static int
+i2bsc_safe_upload(i2bsc_t *i2c, i2c_transfer_t *tp)
+{
+ int retryable = I2BSC_RETRY_LIMIT;
+ int result;
+
+ i2bsc_trace(i2c, 'T', "i2bsc_safe_upload", "Transaction %s",
+ (tp->i2c_flags == I2C_WR_RD) ? "retryable" : "single-shot");
+
+ /*
+ * The only re-tryable transaction type is I2C_WR_RD. If we don't
+ * have this we can only use session-based recovery offered by
+ * i2bsc_upload.
+ */
+ if (tp->i2c_flags != I2C_WR_RD)
+ return (i2bsc_upload(i2c, tp));
+
+ while (retryable--) {
+ result = i2bsc_upload(i2c, tp);
+ if (result == I2C_INCOMPLETE) {
+ /* Have another go */
+ tp->i2c_r_resid = tp->i2c_rlen;
+ tp->i2c_w_resid = tp->i2c_wlen;
+ tp->i2c_result = I2C_SUCCESS;
+ i2bsc_trace(i2c, 'T', "i2bsc_safe_upload",
+ "Retried (%d)", I2BSC_RETRY_LIMIT - retryable);
+ continue;
+ } else {
+ i2bsc_trace(i2c, 'T', "i2bsc_safe_upload",
+ "Exiting while loop on result %d", result);
+ return (result);
+ }
+ }
+
+ i2bsc_trace(i2c, 'T', "i2bsc_safe_upload", "Exiting on %d", result);
+ return (result);
+}
+
+/*
+ * Function i2bsc_transfer
+ *
+ * Description This is the entry-point that clients use via the Solaris i2c
+ * framework. It kicks off the servicing of i2c transfer requests.
+ */
+int
+i2bsc_transfer(dev_info_t *dip, i2c_transfer_t *tp)
+{
+ i2bsc_t *i2c;
+
+ i2c = (i2bsc_t *)ddi_get_soft_state(i2bsc_state,
+ ddi_get_instance(ddi_get_parent(dip)));
+
+ i2bsc_acquire(i2c, dip, tp);
+
+ tp->i2c_r_resid = tp->i2c_rlen;
+ tp->i2c_w_resid = tp->i2c_wlen;
+ tp->i2c_result = I2C_SUCCESS;
+
+ i2bsc_trace(i2c, 'T', "i2bsc_transfer", "Transaction i2c_version/flags"
+ " %d/%d", tp->i2c_version, tp->i2c_flags);
+ i2bsc_trace(i2c, 'T', "i2bsc_transfer", "Transaction buffer rlen/wlen"
+ " %d/%d", tp->i2c_rlen, tp->i2c_wlen);
+ i2bsc_trace(i2c, 'T', "i2bsc_transfer", "Transaction ptrs wbuf/rbuf"
+ " %p/%p", tp->i2c_wbuf, tp->i2c_rbuf);
+
+ if (i2c->i2c_proxy_support)
+ (void) i2bsc_safe_upload(i2c, tp);
+ else
+ tp->i2c_result = I2C_FAILURE;
+
+ i2bsc_trace(i2c, 'T', "i2bsc_transfer", "Residual writes/reads"
+ " %d/%d", tp->i2c_w_resid, tp->i2c_r_resid);
+ i2bsc_trace(i2c, 'T', "i2bsc_transfer", "i2c_result"
+ " %d", tp->i2c_result);
+
+ i2bsc_release(i2c);
+
+ return (tp->i2c_result);
+}
+
+/*
+ * General utility routines ...
+ */
+
+#ifdef DEBUG
+
+static void
+i2bsc_trace(i2bsc_t *ssp, char code, const char *caller,
+ const char *fmt, ...)
+{
+ char buf[256];
+ char *p;
+ va_list va;
+
+ if (ssp->debug & (1 << (code-'@'))) {
+ p = buf;
+ (void) snprintf(p, sizeof (buf) - (p - buf),
+ "%s/%s: ", ssp->i2bsc_name, caller);
+ p += strlen(p);
+
+ va_start(va, fmt);
+ (void) vsnprintf(p, sizeof (buf) - (p - buf), fmt, va);
+ va_end(va);
+
+ buf[sizeof (buf) - 1] = '\0';
+ (void) strlog(ssp->majornum, ssp->minornum, code, SL_TRACE,
+ buf);
+ }
+}
+
+#else /* DEBUG */
+
+_NOTE(ARGSUSED(0))
+static void
+i2bsc_trace(i2bsc_t *ssp, char code, const char *caller,
+ const char *fmt, ...)
+{
+}
+
+#endif /* DEBUG */
diff --git a/usr/src/uts/sun4u/io/i2c/nexus/i2bsc.conf b/usr/src/uts/sun4u/io/i2c/nexus/i2bsc.conf
new file mode 100644
index 0000000000..6608ada6b9
--- /dev/null
+++ b/usr/src/uts/sun4u/io/i2c/nexus/i2bsc.conf
@@ -0,0 +1,31 @@
+#
+# 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 2001-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# Configuration file for i2bsc driver
+#
+
+# 64-bit Debug bit-vector; requires DEBUG driver to be installed
+debug=0;
diff --git a/usr/src/uts/sun4u/io/todm5819p_rmc.c b/usr/src/uts/sun4u/io/todm5819p_rmc.c
new file mode 100644
index 0000000000..e9bda93105
--- /dev/null
+++ b/usr/src/uts/sun4u/io/todm5819p_rmc.c
@@ -0,0 +1,409 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * tod driver module for ALI M5819P part
+ */
+
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/open.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+#include <sys/todm5819p.h>
+#include <sys/rmc_comm_dp.h>
+#include <sys/rmc_comm_drvintf.h>
+#include <sys/modctl.h>
+#include <sys/stat.h>
+#include <sys/clock.h>
+#include <sys/reboot.h>
+#include <sys/machsystm.h>
+
+static timestruc_t todm5819p_rmc_get(void);
+static void todm5819p_rmc_set(timestruc_t);
+static uint_t todm5819p_rmc_set_watchdog_timer(uint_t);
+static uint_t todm5819p_rmc_clear_watchdog_timer(void);
+static void todm5819p_rmc_set_power_alarm(timestruc_t);
+static void todm5819p_rmc_clear_power_alarm(void);
+static uint64_t todm5819p_rmc_get_cpufrequency(void);
+
+extern uint64_t find_cpufrequency(volatile uint8_t *);
+
+/*
+ * External variables
+ */
+extern int watchdog_enable;
+extern int watchdog_available;
+extern int boothowto;
+
+/*
+ * Global variables
+ */
+int m5819p_debug_flags;
+
+/*
+ * Module linkage information for the kernel.
+ */
+static struct modlmisc modlmisc = {
+ &mod_miscops, "tod module for ALI M5819P"
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&modlmisc, NULL
+};
+
+static todinfo_t rtc_to_tod(struct rtc_t *);
+static void read_rtc(struct rtc_t *);
+static void write_rtc_time(struct rtc_t *);
+static void write_rtc_alarm(struct rtc_t *);
+
+
+int
+_init(void)
+{
+ if (strcmp(tod_module_name, "todm5819p_rmc") == 0) {
+ M5819P_ADDR_REG = RTC_B;
+ M5819P_DATA_REG = (RTC_DM | RTC_HM);
+
+ tod_ops.tod_get = todm5819p_rmc_get;
+ tod_ops.tod_set = todm5819p_rmc_set;
+
+ tod_ops.tod_set_watchdog_timer =
+ todm5819p_rmc_set_watchdog_timer;
+ tod_ops.tod_clear_watchdog_timer =
+ todm5819p_rmc_clear_watchdog_timer;
+ tod_ops.tod_set_power_alarm = todm5819p_rmc_set_power_alarm;
+ tod_ops.tod_clear_power_alarm = todm5819p_rmc_clear_power_alarm;
+ tod_ops.tod_get_cpufrequency = todm5819p_rmc_get_cpufrequency;
+ if (boothowto & RB_DEBUG) {
+ cmn_err(CE_WARN, "todm5819p_rmc: kernel debugger "
+ "detected: hardware watchdog disabled");
+ }
+ }
+
+ return (mod_install(&modlinkage));
+}
+
+int
+_fini(void)
+{
+ if (strcmp(tod_module_name, "todm5819p_rmc") == 0)
+ return (EBUSY);
+
+ return (mod_remove(&modlinkage));
+}
+
+/*
+ * The loadable-module _info(9E) entry point
+ */
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+
+/*
+ * Read the current time from the clock chip and convert to UNIX form.
+ * Assumes that the year in the clock chip is valid.
+ * Must be called with tod_lock held.
+ */
+static timestruc_t
+todm5819p_rmc_get(void)
+{
+ int i;
+ int s;
+ timestruc_t ts;
+ struct rtc_t rtc;
+
+ ASSERT(MUTEX_HELD(&tod_lock));
+
+ /* set the hw watchdog timer if it's been activated */
+ if (watchdog_activated) {
+ int ret = 0;
+ ret = tod_ops.tod_set_watchdog_timer(0);
+ /*
+ * The empty set_watchdog routine returns a 0. So if a
+ * coded routine fails we will look for a -1 for a failure.
+ */
+ if (ret == -1)
+ cmn_err(CE_WARN, "todm5819p: failed to set hardware "
+ "watchdog timer.");
+ }
+
+ /*
+ * Read current time from the tod. If the tod isn't accessible, wait and
+ * retry.
+ * Run critical in the time critical section to avoid being interrupted
+ */
+ for (i = 0; i < TODM5819_UIP_RETRY_THRESH; i++) {
+ s = ddi_enter_critical();
+ M5819P_ADDR_REG = RTC_A;
+ if (!(M5819P_DATA_REG & RTC_UIP)) {
+ read_rtc(&rtc);
+ ddi_exit_critical(s);
+ break;
+ }
+ ddi_exit_critical(s);
+ drv_usecwait(TODM5819_UIP_WAIT_USEC);
+ }
+ if (i == TODM5819_UIP_RETRY_THRESH) {
+ /*
+ * tod is inaccessible: just return current software time
+ */
+ tod_fault_reset();
+ return (hrestime);
+ }
+
+ ts.tv_sec = tod_to_utc(rtc_to_tod(&rtc));
+ ts.tv_nsec = 0;
+ return (ts);
+}
+
+static todinfo_t
+rtc_to_tod(struct rtc_t *rtc)
+{
+ todinfo_t tod;
+
+ /*
+ * tod_year is base 1900 so this code needs to adjust the true year
+ * retrieved from the rtc's century and year fields.
+ */
+ tod.tod_year = rtc->rtc_year + (rtc->rtc_century * 100) - 1900;
+ tod.tod_month = rtc->rtc_mon;
+ tod.tod_day = rtc->rtc_dom;
+ tod.tod_dow = rtc->rtc_dow;
+ tod.tod_hour = rtc->rtc_hrs;
+ tod.tod_min = rtc->rtc_min;
+ tod.tod_sec = rtc->rtc_sec;
+
+ return (tod);
+}
+
+static void
+read_rtc(struct rtc_t *rtc)
+{
+ M5819P_ADDR_REG = RTC_SEC;
+ rtc->rtc_sec = M5819P_DATA_REG;
+ M5819P_ADDR_REG = RTC_ASEC;
+ rtc->rtc_asec = M5819P_DATA_REG;
+ M5819P_ADDR_REG = RTC_MIN;
+ rtc->rtc_min = M5819P_DATA_REG;
+ M5819P_ADDR_REG = RTC_AMIN;
+ rtc->rtc_amin = M5819P_DATA_REG;
+ M5819P_ADDR_REG = RTC_HRS;
+ rtc->rtc_hrs = M5819P_DATA_REG;
+ M5819P_ADDR_REG = RTC_AHRS;
+ rtc->rtc_ahrs = M5819P_DATA_REG;
+ M5819P_ADDR_REG = RTC_DOW;
+ rtc->rtc_dow = M5819P_DATA_REG;
+ M5819P_ADDR_REG = RTC_DOM;
+ rtc->rtc_dom = M5819P_DATA_REG;
+ M5819P_ADDR_REG = RTC_MON;
+ rtc->rtc_mon = M5819P_DATA_REG;
+ M5819P_ADDR_REG = RTC_YEAR;
+ rtc->rtc_year = M5819P_DATA_REG;
+ M5819P_ADDR_REG = RTC_CENTURY;
+ rtc->rtc_century = M5819P_DATA_REG;
+
+ /* Read date alarm */
+ M5819P_ADDR_REG = RTC_ADOM_REG;
+ rtc->rtc_adom = (M5819P_DATA_REG) & RTC_ADOM;
+}
+
+/*
+ * Write the specified time into the clock chip.
+ * Must be called with tod_lock held.
+ */
+static void
+todm5819p_rmc_set(timestruc_t ts)
+{
+ struct rtc_t rtc;
+ todinfo_t tod = utc_to_tod(ts.tv_sec);
+ int year;
+ rmc_comm_msg_t request;
+ dp_set_date_time_t set_time_msg;
+
+ ASSERT(MUTEX_HELD(&tod_lock));
+
+ /* tod_year is base 1900 so this code needs to adjust */
+ year = 1900 + tod.tod_year;
+ rtc.rtc_year = year % 100;
+ rtc.rtc_century = year / 100;
+ rtc.rtc_mon = (uint8_t)tod.tod_month;
+ rtc.rtc_dom = (uint8_t)tod.tod_day;
+ rtc.rtc_dow = (uint8_t)tod.tod_dow;
+ rtc.rtc_hrs = (uint8_t)tod.tod_hour;
+ rtc.rtc_min = (uint8_t)tod.tod_min;
+ rtc.rtc_sec = (uint8_t)tod.tod_sec;
+
+ write_rtc_time(&rtc);
+
+ set_time_msg.year = year - 1900;
+ set_time_msg.month = tod.tod_month - 1;
+ set_time_msg.day = tod.tod_day;
+ set_time_msg.hour = tod.tod_hour;
+ set_time_msg.minute = tod.tod_min;
+ set_time_msg.second = tod.tod_sec;
+
+ request.msg_type = DP_SET_DATE_TIME;
+ request.msg_len = sizeof (set_time_msg);
+ request.msg_buf = (caddr_t)&set_time_msg;
+
+ (void) rmc_comm_request_nowait(&request, 0);
+}
+
+void
+write_rtc_time(struct rtc_t *rtc)
+{
+ uint8_t regb;
+
+ /*
+ * Freeze
+ */
+ M5819P_ADDR_REG = RTC_B;
+ regb = M5819P_DATA_REG;
+ M5819P_DATA_REG = (regb | RTC_SET);
+
+ M5819P_ADDR_REG = RTC_SEC;
+ M5819P_DATA_REG = rtc->rtc_sec;
+ M5819P_ADDR_REG = RTC_MIN;
+ M5819P_DATA_REG = rtc->rtc_min;
+ M5819P_ADDR_REG = RTC_HRS;
+ M5819P_DATA_REG = rtc->rtc_hrs;
+ M5819P_ADDR_REG = RTC_DOW;
+ M5819P_DATA_REG = rtc->rtc_dow;
+ M5819P_ADDR_REG = RTC_DOM;
+ M5819P_DATA_REG = rtc->rtc_dom;
+ M5819P_ADDR_REG = RTC_MON;
+ M5819P_DATA_REG = rtc->rtc_mon;
+ M5819P_ADDR_REG = RTC_YEAR;
+ M5819P_DATA_REG = rtc->rtc_year;
+ M5819P_ADDR_REG = RTC_CENTURY;
+ M5819P_DATA_REG = rtc->rtc_century;
+
+ /*
+ * Unfreeze
+ */
+ M5819P_ADDR_REG = RTC_B;
+ M5819P_DATA_REG = regb;
+}
+
+void
+write_rtc_alarm(struct rtc_t *rtc)
+{
+ M5819P_ADDR_REG = RTC_ASEC;
+ M5819P_DATA_REG = rtc->rtc_asec;
+ M5819P_ADDR_REG = RTC_AMIN;
+ M5819P_DATA_REG = rtc->rtc_amin;
+ M5819P_ADDR_REG = RTC_AHRS;
+ M5819P_DATA_REG = rtc->rtc_ahrs;
+
+ M5819P_ADDR_REG = RTC_ADOM_REG;
+ M5819P_DATA_REG = rtc->rtc_adom;
+}
+
+/*
+ * program the rtc registers for alarm to go off at the specified time
+ */
+static void
+todm5819p_rmc_set_power_alarm(timestruc_t ts)
+{
+ todinfo_t tod;
+ uint8_t regb;
+ struct rtc_t rtc;
+
+ ASSERT(MUTEX_HELD(&tod_lock));
+ tod = utc_to_tod(ts.tv_sec);
+
+ /*
+ * disable alarms and clear AF flag by reading reg C
+ */
+ M5819P_ADDR_REG = RTC_B;
+ regb = M5819P_DATA_REG;
+ M5819P_DATA_REG = regb & ~RTC_AIE;
+ M5819P_ADDR_REG = RTC_C;
+ (void) M5819P_DATA_REG;
+
+ rtc.rtc_asec = (uint8_t)tod.tod_sec;
+ rtc.rtc_amin = (uint8_t)tod.tod_min;
+ rtc.rtc_ahrs = (uint8_t)tod.tod_hour;
+ rtc.rtc_adom = (uint8_t)tod.tod_day;
+
+ /*
+ * Write alarm values and enable alarm
+ */
+ write_rtc_alarm(&rtc);
+
+ M5819P_ADDR_REG = RTC_B;
+ M5819P_DATA_REG = regb | RTC_AIE;
+}
+
+/*
+ * clear alarm interrupt
+ */
+static void
+todm5819p_rmc_clear_power_alarm(void)
+{
+ uint8_t regb;
+
+ ASSERT(MUTEX_HELD(&tod_lock));
+
+ M5819P_ADDR_REG = RTC_B;
+ regb = M5819P_DATA_REG;
+ M5819P_DATA_REG = regb & ~RTC_AIE;
+}
+
+/*
+ * Determine the cpu frequency by watching the TOD chip rollover twice.
+ * Cpu clock rate is determined by computing the ticks added (in tick register)
+ * during one second interval on TOD.
+ */
+uint64_t
+todm5819p_rmc_get_cpufrequency(void)
+{
+ ASSERT(MUTEX_HELD(&tod_lock));
+ M5819P_ADDR_REG = RTC_SEC;
+ return (find_cpufrequency(v_rtc_data_reg));
+}
+
+/*ARGSUSED*/
+static uint_t
+todm5819p_rmc_set_watchdog_timer(uint_t timeoutval)
+{
+ ASSERT(MUTEX_HELD(&tod_lock));
+ return (0);
+}
+
+static uint_t
+todm5819p_rmc_clear_watchdog_timer(void)
+{
+ ASSERT(MUTEX_HELD(&tod_lock));
+ return (0);
+}
diff --git a/usr/src/uts/sun4u/io/todstarcat.c b/usr/src/uts/sun4u/io/todstarcat.c
new file mode 100644
index 0000000000..a203ee27e6
--- /dev/null
+++ b/usr/src/uts/sun4u/io/todstarcat.c
@@ -0,0 +1,263 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * tod driver module for Starcat
+ * This module implements a soft tod since
+ * starcat has no tod part.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/modctl.h>
+#include <sys/autoconf.h>
+#include <sys/debug.h>
+#include <sys/clock.h>
+#include <sys/cmn_err.h>
+#include <sys/promif.h>
+#include <sys/cpuvar.h>
+#include <sys/sunddi.h>
+#include <sys/iosramio.h>
+#include <sys/domaind.h>
+
+#define abs(x) ((x) < 0 ? -(x) : (x))
+
+#define TODSC_SET_THRESHOLD 30
+
+static timestruc_t todsc_get(void);
+static void todsc_set(timestruc_t);
+static uint_t todsc_set_watchdog_timer(uint_t);
+static uint_t todsc_clear_watchdog_timer(void);
+static void todsc_set_power_alarm(timestruc_t);
+static void todsc_clear_power_alarm(void);
+static uint64_t todsc_get_cpufrequency(void);
+
+/*
+ * Module linkage information for the kernel.
+ */
+static struct modlmisc modlmisc = {
+ &mod_miscops, "Soft tod module for Sun Fire 15000"
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&modlmisc, NULL
+};
+
+static uint32_t heartbeat = 0;
+
+int
+_init(void)
+{
+ if (strcmp(tod_module_name, "todstarcat") == 0) {
+ uint32_t ssc_time32 = 0;
+ char obp_string[40];
+
+ /*
+ * To obtain the initial start of day time, we use an
+ * OBP callback; this is because the iosram is not yet
+ * accessible from the OS at this early stage of startup.
+ */
+
+ /*
+ * Set the string to pass to OBP
+ * for now, we assume we always get a 32bit value
+ */
+ (void) sprintf(obp_string, "h# %p unix-gettod",
+ (void *) &ssc_time32);
+
+ prom_interpret(obp_string, 0, 0, 0, 0, 0);
+
+ hrestime.tv_sec = (time_t)ssc_time32;
+
+ /*
+ * A date of zero causes the root filesystem driver
+ * to try to set the date from the last shutdown.
+ */
+
+ /*
+ * Check for a zero date.
+ */
+ if (ssc_time32 == 0) {
+ cmn_err(CE_WARN, "Initial date is invalid.");
+ cmn_err(CE_CONT, "Attempting to set the date and time "
+ "based on the last shutdown.\n");
+ cmn_err(CE_CONT, "Please inspect the date and time and "
+ "correct if necessary.\n");
+ }
+
+ /*
+ * Check that the date has not overflowed a 32-bit integer.
+ */
+ if (TIMESPEC_OVERFLOW(&hrestime)) {
+ cmn_err(CE_WARN, "Date overflow detected.");
+ cmn_err(CE_CONT, "Attempting to set the date and time "
+ "based on the last shutdown.\n");
+ cmn_err(CE_CONT, "Please inspect the date and time and "
+ "correct if necessary.\n");
+
+ hrestime.tv_sec = (time_t)0;
+ }
+
+ tod_ops.tod_get = todsc_get;
+ tod_ops.tod_set = todsc_set;
+ tod_ops.tod_set_watchdog_timer = todsc_set_watchdog_timer;
+ tod_ops.tod_clear_watchdog_timer = todsc_clear_watchdog_timer;
+ tod_ops.tod_set_power_alarm = todsc_set_power_alarm;
+ tod_ops.tod_clear_power_alarm = todsc_clear_power_alarm;
+ tod_ops.tod_get_cpufrequency = todsc_get_cpufrequency;
+
+ /*
+ * Flag warning if user tried to use hardware watchdog
+ */
+ if (watchdog_enable) {
+ cmn_err(CE_WARN, "Hardware watchdog unavailable");
+ }
+ }
+
+ return (mod_install(&modlinkage));
+}
+
+int
+_fini(void)
+{
+ if (strcmp(tod_module_name, "todstarcat") == 0)
+ return (EBUSY);
+ else
+ return (mod_remove(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+
+/*
+ * Starcat tod_get is simplified to return hrestime and to
+ * update the domain heartbeat.
+ * Must be called with tod_lock held.
+ */
+static timestruc_t
+todsc_get(void)
+{
+ ASSERT(MUTEX_HELD(&tod_lock));
+
+ heartbeat++;
+ (void) iosram_wr(DOMD_MAGIC, DOMD_HEARTBEAT_OFFSET,
+ sizeof (uint32_t), (caddr_t)&heartbeat);
+ return (hrestime);
+}
+
+/*
+ * Must be called with tod_lock held.
+ *
+ * When running NTP, tod_set is called at least once per second in order
+ * to update the hardware clock - for Starcat, we don't want to sync
+ * the non-existent hardware clock, and only want to record significant
+ * time changes on the SC (i.e. when date(1M) is run). So, we have a
+ * threshold requirement before recording the time change.
+ */
+/* ARGSUSED */
+static void
+todsc_set(timestruc_t ts)
+{
+ char obp_string[40];
+
+ ASSERT(MUTEX_HELD(&tod_lock));
+
+ heartbeat++;
+ (void) iosram_wr(DOMD_MAGIC, DOMD_HEARTBEAT_OFFSET,
+ sizeof (uint32_t), (caddr_t)&heartbeat);
+
+ if (abs(hrestime.tv_sec - ts.tv_sec) > TODSC_SET_THRESHOLD) {
+ /*
+ * Update the SSC with the new UTC domain time
+ */
+ (void) sprintf(obp_string, "h# %x unix-settod",
+ (int)ts.tv_sec);
+
+ prom_interpret(obp_string, 0, 0, 0, 0, 0);
+#ifdef DEBUG
+ cmn_err(CE_NOTE, "todsc_set: new domain time 0x%lx\n",
+ ts.tv_sec);
+#endif
+ }
+}
+
+/*
+ * No watchdog function.
+ */
+/* ARGSUSED */
+static uint_t
+todsc_set_watchdog_timer(uint_t timeoutval)
+{
+ ASSERT(MUTEX_HELD(&tod_lock));
+ return (0);
+}
+
+/*
+ * No watchdog function
+ */
+static uint_t
+todsc_clear_watchdog_timer(void)
+{
+ ASSERT(MUTEX_HELD(&tod_lock));
+ return (0);
+}
+
+/*
+ * Null function.
+ */
+/* ARGSUSED */
+static void
+todsc_set_power_alarm(timestruc_t ts)
+{
+ ASSERT(MUTEX_HELD(&tod_lock));
+}
+
+/*
+ * Null function
+ */
+static void
+todsc_clear_power_alarm()
+{
+ ASSERT(MUTEX_HELD(&tod_lock));
+}
+
+/*
+ * Get clock freq from the cpunode. This function is only called
+ * when use_stick = 0, otherwise, system_clock_freq gets used instead.
+ */
+uint64_t
+todsc_get_cpufrequency(void)
+{
+ return (cpunodes[CPU->cpu_id].clock_freq);
+}
diff --git a/usr/src/uts/sun4u/todm5819p_rmc/Makefile b/usr/src/uts/sun4u/todm5819p_rmc/Makefile
new file mode 100644
index 0000000000..7725458687
--- /dev/null
+++ b/usr/src/uts/sun4u/todm5819p_rmc/Makefile
@@ -0,0 +1,89 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of todm5819p_rmc kernel module.
+#
+# sun4u implementation architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = todm5819p_rmc
+OBJECTS = $(TODM5819P_RMC_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(TODM5819P_RMC_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_PSM_TOD_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sun4u/Makefile.sun4u
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# lint pass one enforcement
+#
+CFLAGS += $(CCVERBOSE)
+LDFLAGS += -dy -Ndrv/rmc_comm -Ndrv/pmugpio
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/sun4u/Makefile.targ
+
+
diff --git a/usr/src/uts/sun4u/todstarcat/Makefile b/usr/src/uts/sun4u/todstarcat/Makefile
new file mode 100644
index 0000000000..f7fd26cc89
--- /dev/null
+++ b/usr/src/uts/sun4u/todstarcat/Makefile
@@ -0,0 +1,88 @@
+#
+# 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
+#
+#
+# uts/sun4u/todstarcat/Makefile
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of the todstarcat
+# kernel module.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = todstarcat
+OBJECTS = $(TODSTARCAT_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(TODSTARCAT_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_PSM_TOD_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sun4u/Makefile.sun4u
+
+INC_PATH += -I$(UTSBASE)/sun4u/starcat
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# module dependencies
+#
+LDFLAGS += -dy -Ndrv/sbbc -Ndrv/iosram
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/sun4u/Makefile.targ