bscv.c revision 1c42de6d020629af774dd9e9fc81be3f3ed9398e
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 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/sysevent.h>
#include <sys/bscv_impl.h>
/*
* Variables defined here and visible internally only
*/
static void *bscv_statep = NULL;
/*
* Forward declarations
*/
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 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_setclear8(bscv_soft_state_t *, int,
static void bscv_setclear8_volatile(bscv_soft_state_t *, int,
static void bscv_rep_rw8(bscv_soft_state_t *, int,
static void bscv_rep_get8_locked(bscv_soft_state_t *, int,
static void bscv_clear_fault(bscv_soft_state_t *);
static void bscv_set_fault(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_resync_comms(bscv_soft_state_t *, int);
unsigned, boolean_t);
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_build_eventstring(bscv_soft_state_t *,
lom_event_t *, char *, char *);
static int bscv_level_of_event(lom_event_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 *);
uint8_t *, int);
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_setup_static_info(bscv_soft_state_t *);
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_clear_err();
static boolean_t bscv_idi_err();
#endif /* __sparc */
static void bscv_write_wdog_cfg(bscv_soft_state_t *,
static void bscv_watchdog_pat_request(void *);
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 *);
#define WDOG_ON 1
#define WDOG_OFF 0
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_SIG, &bscv_sig_set },
{BSCV_IDI_NULL, NULL }
};
static struct bscv_idi_callout_mgr bscv_idi_mgr;
#endif /* __sparc */
/*
* Local Definitions
*/
#define MYNAME "bscv"
#define BSCV_INST_TO_MINOR(i) (i)
#define BSCV_MINOR_TO_INST(m) (m)
/*
* 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 */
};
/*
* 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 modlinkage modlinkage = {
&modldrv,
};
/*
* 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) {
}
#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
{
}
/*
* 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
#endif /* __sparc */
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
{
int instance;
int error;
switch (cmd) {
case DDI_INFO_DEVT2INSTANCE:
error = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2DEVINFO:
return (DDI_FAILURE);
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
break;
}
return (error);
}
#ifdef __sparc
void
{
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.
*/
}
static void
{
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_mgr.errs++;
return (B_TRUE);
return (B_FALSE);
}
void
{
/*
* 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.
*/
}
}
void
{
}
#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
{
int instance;
switch (cmd) {
case DDI_ATTACH:
DDI_SUCCESS) {
return (DDI_FAILURE);
}
ssp->task_flags = 0;
DDI_PROP_DONTPASS, "debug", 0);
#endif /* __i386 || __amd64 */
/*
* initialise the mutexes
*/
"bscv_attach: mutexes and condition vars initialised");
/* Map in physical communication channels */
(void) bscv_cleanup(ssp);
return (DDI_FAILURE);
}
/* Associate logical channels to physical channels */
(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.
*/
#endif /* __sparc */
/*
* now create the minor nodes
*/
DDI_PSEUDO, 0) != DDI_SUCCESS) {
(void) bscv_cleanup(ssp);
return (DDI_FAILURE);
}
"bscv_attach: device minor nodes created");
if (!ssp->prog_mode_only)
bscv_watchdog_enable = 1;
watchdog_activated = 0;
"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.
*/
if (bscv_watchdog_available) {
(void) bscv_set_watchdog_timer(ssp,
}
#endif /* __i386 || __amd64 */
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
{
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
{
int instance;
switch (cmd) {
case DDI_RESET_FORCE:
return (DDI_FAILURE);
}
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
* 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
{
int instance;
return (ENXIO); /* not attached yet */
}
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
{
int instance;
return (ENXIO);
}
return (0);
}
static int
{
int i;
int retval;
int *props;
unsigned int nelements;
/*
* Work out how many channels are available by looking at the number
* of elements of the regs property array.
*/
/* We don't need props anymore. Free memory if it was allocated */
if (retval == DDI_PROP_SUCCESS)
/* Check for sanity of nelements */
if (retval != DDI_PROP_SUCCESS) {
" 0x%x", retval);
goto cleanup_exit;
} else if (nelements % LOMBUS_REGSPEC_SIZE != 0) {
goto cleanup_exit;
", probably a misconfiguration", nelements);
goto cleanup_exit;
", need to have at least a general and a wdog channel",
goto cleanup_exit;
}
if (retval != DDI_SUCCESS) {
" 0x%x on space %d", retval, i);
/* Rewind all current mappings - avoiding failed one */
i--;
for (; i >= 0; i--) {
}
goto cleanup_exit;
}
}
return (DDI_SUCCESS);
/*
* 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.
*/
return (DDI_FAILURE);
}
static void
{
int i;
}
}
/*
* Map logical services onto physical XBus channels.
*/
static void
{
/*
* 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 = 2;
chan_eeprom = 3;
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
{
uint8_t bits2clear = 0;
"turning off watchdog");
if (!ddi_in_panic()) {
/* Stop the event daemon if we are not panicking. */
(void) bscv_pause_event_daemon(ssp);
}
if (ddi_in_panic()) {
} else {
}
#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 */
break;
case LOM_SER_EVENTS_OFF:
/* Make sure serial event reporting is on */
break;
default:
break;
}
}
/*
* 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.
*
*
* 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
{
}
static void
{
}
#ifdef DEBUG
static int
{
}
#endif /* DEBUG */
static void
{
int num_failures;
if (bscv_faulty(ssp)) {
return;
}
for (num_failures = 0;
num_failures++) {
if (!needretry) {
break;
}
}
if (ssp->command_error != 0) {
}
if (needretry) {
/* Failure - we ran out of retries */
"write %d times, giving up",
} else if (num_failures > 0) {
"addr 0x%x.%02x retried write %d times, succeeded",
}
}
static void
{
}
static void
{
}
static uint8_t
{
int num_failures;
if (bscv_faulty(ssp)) {
return (0);
}
for (num_failures = 0;
num_failures++) {
if (!needretry) {
break;
}
}
if (ssp->command_error != 0) {
}
if (needretry) {
/* Failure */
"read %d times, giving up",
} else if (num_failures > 0) {
"addr 0x%x.%02x retried read %d times, succeeded",
}
return (retval);
}
static uint16_t
{
return (retval);
}
static uint32_t
{
return (retval);
}
static void
{
"addr 0x%x.%02x, set %02x, clear %02x => %02x",
}
static void
{
int num_failures;
if (bscv_faulty(ssp)) {
return;
}
"addr 0x%x.%02x => set %02x clear %02x",
for (num_failures = 0;
num_failures++) {
/* Re-read the stale register from the lom */
needretry = 1;
} else {
if (!needretry) {
break;
}
}
}
if (ssp->command_error != 0) {
}
if (needretry) {
/* Failure */
"retried write %d times, giving up",
}
} else if (num_failures > 0) {
"addr 0x%x.%02x retried write %d times, succeeded",
}
}
static void
{
if (flags & DDI_DEV_AUTOINCR) {
if (is_write) {
} else {
}
} else {
if (is_write) {
} else {
dev_addr);
}
/* We need this because _once routines don't do it */
if (ssp->command_error != 0) {
}
}
/*
* will have retried. For NO_AUTOINCR we cannot retry
* because the data would be corrupted.
*/
break;
}
}
}
static uint8_t
{
/* Can be called with or without the lock held */
}
static uint8_t
{
return (retval);
}
static void
{
}
static boolean_t
{
}
static void
{
}
static void
{
}
static boolean_t
{
return (ssp->had_session_error);
}
static int
{
return (ssp->command_error);
}
static int
{
/* 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
{
}
}
static void
{
ssp->command_error = 0;
if (bscv_faulty(ssp)) {
/* Bail out things are not working */
return;
/* Didn't manage to map handles so ddi_{get,put}* broken */
"nchannels is 0x0 so cannot do IO");
return;
}
/* Clear any pending fault */
/* Do the access and get fault code - may take a long time */
if (fault == 0) {
/* Things were ok - update cache entry */
if (addr < BSC_ADDR_CACHE_LIMIT) {
/* Store cacheable entries */
}
} else if (fault >= LOMBUS_ERR_BASE) {
/* lombus problem - do a resync session */
"for address 0x%x.%02x - data 0x%x, fault 0x%x",
/* Attempt to resync with the lom */
/*
* 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!
*/
}
}
static uint8_t
{
ssp->command_error = 0;
if (bscv_faulty(ssp)) {
/* Bail out things are not working */
return (0xff);
/* Didn't manage to map handles so ddi_{get,put}* broken */
"nchannels is 0x0 so cannot do IO");
return (0xff);
}
/* Clear any pending fault */
/* Do the access and get fault code - may take a long time */
if (fault >= LOMBUS_ERR_BASE) {
/* lombus problem - do a resync session */
"for address 0x%x.%02x - data 0x%x, fault 0x%x",
/* Attempt to resync with the lom */
/*
* 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 */
}
}
return (val);
}
static uint32_t
{
/*
* Failed to map handles, so cannot do any IO. Set the
* fault indicator and return a dummy value.
*/
"nchannels is 0x0 so cannot do any IO");
return ((~(int8_t)0));
}
/* Clear faults */
/* Probe and Check faults */
/* Read status */
return (async_reg);
}
static void
{
int try;
/*
* Didn't manage to map handles so ddi_{get,put}* broken.
* Therefore, there is no way to resync comms.
*/
"nchannels is 0x0 so not possible to resync comms");
return;
}
if (command_error >= LOMBUS_ERR_BASE &&
/* Resync here to make sure that the lom is talking */
"Attempting comms resync after comms fault 0x%x",
/* Probe */
if (fault == 0) {
break;
} else {
"comms resync (probing) - try 0x%x "
}
}
if (fault != 0) {
"Failed to resync comms - giving up");
ssp->bad_resync++;
} else {
"resync comms after 0x%x tries", try);
ssp->bad_resync = 0;
}
}
}
/*
* LOMLite configuration/event eeprom access routines
*
* This must be called prior to calling bscv_eerw().
*/
/*
* 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
{
if (ssp->eeinfo_valid) {
/* Already have good cached values */
return (ssp->eeinfo_valid);
}
ssp->eeprom_size =
/*
* 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);
} else if ((ssp->eeprom_size == 0) ||
/* Sanity check values */
"!bscv_window_setup: read invalid eeprom parameters");
} else {
}
return (ssp->eeinfo_valid);
}
/*
* function - bscv_eerw
* It takes care of setting the window on the eeprom correctly.
* outputs - B_TRUE if the eeprom is ok, B_FALSE if the eeprom is not OK.
*/
static int
{
int res = 0;
while (remaining > 0) {
} else {
}
/* Select correct eeprom page */
"%s data @0x%x.%02x, size 0x%x, 0x%x bytes remaining",
break;
}
}
return (res);
}
static boolean_t
{
e->ev_event == EVENT_NONE) {
/*
* This marks a NULL event.
*/
"EVENT_SUBSYS_NONE/EVENT_NONE null event");
return (B_TRUE);
/*
* Under some circumstances, we've seen all 1s to represent
* a manually cleared event log at the BSC prompt. Only
*/
return (B_TRUE);
} else {
/*
* Not a NULL event.
*/
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
{
int instance;
int res = 0;
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;
return (ENXIO);
cmd != LOMIOCPROG) {
return (ENXIO);
}
/*
* Check that the caller has appropriate access permissions
* (FWRITE set in mode) for those ioctls which change lom
* state
*/
switch (cmd) {
case LOMIOCMPROG2:
case LOMIOCMREAD2:
case LOMIOCPROG:
case LOMIOCTEST:
return (EACCES);
/* NOTREACHED */
default:
/* Does not require write access */
break;
}
}
switch (cmd) {
case LOMIOCDOGSTATE:
break;
case LOMIOCPROG:
break;
case LOMIOCPSUSTATE:
break;
case LOMIOCFANSTATE:
break;
case LOMIOCFLEDSTATE:
break;
case LOMIOCLEDSTATE:
break;
case LOMIOCINFO:
break;
case LOMIOCMREAD:
break;
case LOMIOCVOLTS:
break;
case LOMIOCSTATS:
break;
case LOMIOCTEMP:
break;
case LOMIOCCONS:
break;
case LOMIOCEVENTLOG2:
break;
case LOMIOCINFO2:
break;
case LOMIOCTEST:
break;
case LOMIOCMPROG2:
break;
case LOMIOCMREAD2:
break;
default:
}
return (res);
}
/*
* LOMIOCDOGSTATE
* TSIOCDOGSTATE - indicate whether the alarm watchdog and reset
* circuitry is enabled or not.
*/
static int
{
int res = 0;
if ((res == 0) &&
}
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
{
int i;
int res = 0;
for (i = 0; i < MAX_PSUS; i++) {
EBUS_IDX_PSU1_STAT + i, &res);
}
mode) < 0) {
}
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
{
int numfans;
int i;
int res = 0;
EBUS_IDX_FAN1_LOW + i);
}
}
if ((res == 0) &&
mode) < 0)) {
}
return (res);
}
/*
* LOMIOCFLEDSTATE - returns the state of the fault LED
*/
static int
{
int res = 0;
/* Decode of 0x0F is off and 0x00-0x07 is on. */
} else {
/* has +1 here - not 2 as in the info ioctl */
}
if ((res == 0) &&
}
return (res);
}
/*
* LOMIOCLEDSTATE - returns the state of the requested LED
*/
static int
{
int fw_led_state;
int res = 0;
/* copy in arguments supplied */
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.
*/
}
/* is the index in a valid range */
} else {
/* read the relevant led info */
/* set the state values accordingly */
switch (fw_led_state) {
case LOM_LED_STATE_OFF:
break;
case LOM_LED_STATE_ON_STEADY:
break;
break;
break;
case LOM_LED_STATE_STANDBY:
default:
break;
}
/* set the label info */
}
/* copy out lom_state */
if ((res == 0) &&
sizeof (lom_led_state_t), mode) < 0)) {
}
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
{
int i;
int res = 0;
&res);
<< 8;
&res);
}
} else {
}
if ((res == 0) &&
mode) < 0)) {
}
return (res);
}
/*
* LOMIOCMREAD - used to query the LOMlite configuration parameters
*/
static int
{
int i;
int fanz;
int res = 0;
EBUS_IDX_MODEL_ID1 + i, &res);
}
&res);
&res);
/* Read the fan calibration values */
for (i = 0; i < fanz; i++) {
EBUS_IDX_FAN1_CAL + i);
EBUS_IDX_FAN1_LOW + i);
}
if ((res == 0) &&
mode) < 0)) {
}
return (res);
}
/*
* LOMIOCVOLTS
*/
static int
{
int i;
int res = 0;
&res);
}
if ((res == 0) &&
}
return (res);
}
/*
* LOMIOCSTATS
*/
static int
{
int i;
int res = 0;
&res);
}
if ((res == 0) &&
}
return (res);
}
/*
* LOMIOCTEMP
*/
static int
{
int i;
int idx;
int res = 0;
idx = 0;
idx++;
}
}
&res);
}
if ((res == 0) &&
mode) < 0)) {
}
return (res);
}
/*
* LOMIOCCONS
*/
static int
{
int datasize;
int res = 0;
/* Ensure that we do not overfill cbuf and that it is NUL terminated */
}
/* This is always within the array due to the checks above */
if ((res == 0) &&
mode) < 0)) {
}
return (res);
}
/*
* LOMIOCEVENTLOG2
*/
static int
{
int events_recorded;
int level;
int res = 0;
KM_SLEEP);
/*
* First get number of events and level requested.
*/
sizeof (lom_eventlog2_t), mode) < 0) {
return (EFAULT);
}
/*
* 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)) {
return (res);
}
/*
* Read count, next event ptr MSB,LSB. Note a read of count
* is necessary to latch values for the next event ptr
*/
events_recorded = 0;
/*
* 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!
*/
/* Wrap to the end of the buffer */
" around to end of buffer; next_offset 0x%x",
}
next_offset -= sizeof (event);
/* Fault reading data - stop */
" failure for offset 0x%x", next_offset);
break;
}
/*
* No more events in this log so give up.
*/
" events left at offset 0x%x", next_offset);
break;
}
/*
* Are we interested in this event
*/
/* Arggh why the funny byte ordering 3, 2, 0, 1 */
}
}
if ((res == 0) &&
sizeof (lom_eventlog2_t), mode) < 0)) {
}
return (res);
}
/*
* LOMIOCINFO2
*/
static int
{
int i;
int res = 0;
sizeof (info2.escape_chars));
<< 8;
EBUS_IDX_MODEL_ID1 + i, &res);
}
}
}
}
EBUS_IDX_SER_BAUD, &res);
EBUS_IDX_SER_CHARMODE, &res) |
/*
* There is no phone home support on the blade platform. We hardcode
* FALSE and NUL for config and script respectively.
*/
}
if ((res == 0) &&
mode) < 0)) {
}
return (res);
}
/*
* LOMIOCTEST
*/
static int
{
int res = 0;
mode) < 0) {
return (EFAULT);
}
/*
* Extract num iterations.
*/
"LOMIOCTEST data 0x%x (test 0x%x, arg 0x%x)",
switch (testnum + EBUS_IDX_SELFTEST0) {
default:
/* Invalid test */
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 */
/*
* Then we simply write the argument to the relevant register
* and wait for the return code.
*/
if (bscv_faulty(ssp)) {
} else {
/* Get hold of the SunVTS error code */
}
break;
}
if ((res == 0) &&
mode) < 0)) {
}
return (res);
}
/*
* LOMIOCMPROG2
*/
static int
{
int res = 0;
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.
*/
return (EINVAL);
}
/* Calculate required data location */
data_size = 255;
1024;
if (bscv_faulty(ssp)) {
return (EIO);
"Request extends past end of eeprom");
return (ENXIO);
}
if (bscv_faulty(ssp)) {
return (EIO);
}
if (bscv_faulty(ssp)) {
return (EIO);
}
}
/* Read a probe key to release the lock. */
if (bscv_faulty(ssp)) {
}
return (res);
}
/*
* LOMIOCMREAD2
*/
static int
{
int res = 0;
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.
*/
return (EINVAL);
}
/* Calculate required data location */
data_size = 255;
1024;
if (bscv_faulty(ssp)) {
return (EIO);
"Request extends past end of eeprom");
return (ENXIO);
}
}
if (bscv_faulty(ssp)) {
}
if ((res == 0) &&
mode) < 0)) {
}
return (res);
}
static void
{
int i = STATUS_READ_LIMIT;
/* Are there any changes to process? */
if (!change)
break;
/* Clarify the pending change */
}
}
/*
* *********************************************************************
* 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)
{
int fault_cnt = 0;
"bscv_event_daemon: started");
/* Acquire task daemon lock. */
for (;;) {
/* Stop request seen - terminate */
break;
}
/* Poll for events reported to the nexus */
/* Probe and Check faults */
"process event: async_reg 0x%x, fault 0x%x",
if (!fault) {
/* Treat non-fault conditions */
/*
* The BSC has become available again.
*/
fault_cnt = 0;
(void) bscv_attach_common(ssp);
} else if (fault_cnt > 0) {
/* Previous fault has cleared */
fault_cnt = 0;
"!bscv_event_daemon previous fault "
"cleared.");
} else if (bscv_faulty(ssp)) {
/* Previous fault has cleared */
/* Sleep to avoid busy waiting */
}
if (async_reg) {
}
/*
* Expect radio silence or error values
* when the CSSP is upgrading the BSC firmware
* so throw away any fault indication.
*/
} else if (fault_cnt == BSC_PROBE_FAULT_LIMIT) {
/* Count previous faults and maybe fail */
/* Declare the lom broken */
"!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
*/
} else if (fault_cnt < BSC_PROBE_FAULT_LIMIT) {
if (bscv_faulty(ssp)) {
/*
* No recovery messages in this case
* because there was never a fault
* message here.
*/
fault_cnt = 0;
} else {
/* Getting ready to explode */
fault_cnt++;
"!bscv_event_daemon had fault 0x%x",
fault);
}
}
}
/*
* 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.
*/
"utsname.nodename='%s' possible change detected",
sizeof (ssp->last_nodename));
/* enforce null termination */
'\0';
}
#endif /* __i386 || __amd64 */
if (ssp->watchdog_change) {
}
/*
* We must not hold task_mu whilst processing
* events because this can lead to priority
* inversion and hence our interrupts getting
* locked out.
*/
if (do_events) {
}
if (do_nodename) {
"do_nodename task");
}
if (do_watchdog) {
"do_watchdog task");
}
/*
* 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) {
}
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.
*/
"had session error - sleeping");
}
/*
* 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.
*/
}
} else {
/* There was nothing to do - sleep */
}
if (ssp->event_sleep) {
/* Sleep until there is something to do */
poll_period + ddi_get_lbolt());
}
}
/*
* We are going away so wake up any event consumer.
* Pretend that any pending events have been processed.
*/
}
ssp->task_flags &=
"exiting.");
}
/*
* function - bscv_start_event_daemon
* description - Create the event daemon thread.
* inputs - LOM soft state structure pointer
* outputs - none
*/
static void
{
return;
/* Start the event thread after the queue has started */
}
/*
* 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
{
int try;
int res = DDI_SUCCESS;
/* Wait for task daemon to stop running. */
for (try = 0;
try++) {
/* Signal that the task daemon should stop */
/* Release task daemon lock. */
/*
* 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.
*/
}
res = DDI_FAILURE;
}
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
{
int try;
/* Nothing to do */
return (BSCV_SUCCESS);
}
"Attempting to pause event daemon");
/* Signal that the task daemon should pause */
/* Wait for task daemon to pause. */
for (try = 0;
try < 10);
try++) {
/* Paranoia */
/* Release task daemon lock. */
}
"Pause event daemon - success");
return (BSCV_SUCCESS);
}
"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
{
/* Nothing to do */
return;
}
/* Allow the task daemon to resume event processing */
"Event daemon resumed");
}
/*
* function - bscv_event_process
* description - process (report) events
* inputs - Soft state ptr, process event request
* outputs - none
*/
static void
{
unsigned int count;
/* Raw values read from the lom */
if (do_events) {
/*
* Read count, next event ptr MSB,LSB. Note a read of count
* latches values for the next event ptr
*/
/* Sanity check the values from the lom */
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.
*/
goto logdone;
}
if (ssp->event_fault_reported) {
/* Clear down any old status - things are fixed */
}
/* Compute the first entry that we need to read. */
"processing %d events from 0x%x in 0x%x:0x%x",
/* Ensure window is positioned correctly */
/* Fault reading data - stop */
break;
}
ssp->eventlog_size) {
}
}
/*
* 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*/
evcount = 0;
} else {
}
/* Remember where we were for next time */
;
}
}
/*
* 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
{
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 (!ssp->event_fault_reported) {
"Cannot read events.");
}
return (-1);
}
/* Now sanity check log pointer against count */
/*
* Must have wrapped add eventlog_size to get the
* correct relative values - this makes the checks
* below work!
*/
}
if (!ssp->oldeeptr_valid) {
/* We have just started up - we have to trust lom */
/* Nothing to do - we were just polling */
return (-1);
/* Ok - got as many events as we expected */
/*
* 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!
*/
"bscv_event_process: lom reported "
"more events (%d) than expected (%d).",
} else {
/* Less messages - perhaps the lom has been reset */
"lom reported less events (%d) than expected (%d)"
" - the lom may have been reset",
}
/* Whatever happens only read a maximum of 255 entries */
if ((count >= 0xff)) {
"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
{
int level;
char eventstr[100];
int msg_type = 0;
/* Cleared entry - do not report it */
return;
}
switch (level) {
default:
break;
case EVENT_LEVEL_FATAL:
case EVENT_LEVEL_FAULT:
break;
}
sizeof (eventstr));
/*
* The message is important enough to be shown on the console
* as well as the log.
*/
} else {
/*
* The message goes only to the log.
*/
}
}
/*
* 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.
*/
#define BSC_TIME_SANITY 1000000000
/*
* render a formatted time for display
*/
static size_t
{
int year;
/* tod_year is base 1900 so this code needs to adjust */
}
/*
* function - bscv_build_eventstring
* description - reports on state changes to the host.
*
* inputs - LOM soft state structure pointer.
*
* outputs - none.
*/
static void
{
/*
* We accept bad subsystems and event type codes here.
* The code decodes as much as possible and then produces
* suitable output.
*/
/* time */
if (bsctm < BSC_TIME_SANITY) {
/* offset */
} else {
/* absolute time */
utc_to_tod(bsctm));
}
/* subsysp */
if (subsystem <
(sizeof (eventSubsysStrings)/sizeof (*eventSubsysStrings))) {
} else {
"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:
break;
case EVENT_SUBSYS_LED:
break;
default:
break;
}
/* fatal */
} else {
}
}
/* eventp */
if (eventtype <
(sizeof (eventTypeStrings)/sizeof (*eventTypeStrings))) {
} else {
"unknown event 0x%02x%02x%02x%02x",
}
/* detail */
switch (subsystem) {
case EVENT_SUBSYS_TEMP:
if ((eventtype != EVENT_RECOVERED) &&
}
break;
case EVENT_SUBSYS_FAN:
if (eventtype == EVENT_FAILED) {
}
break;
case EVENT_SUBSYS_LOM:
switch (eventtype) {
case EVENT_FLASH_DOWNLOAD:
": v%d.%d to v%d.%d",
break;
case EVENT_WATCHDOG_TRIGGER:
break;
case EVENT_UNEXPECTED_RESET:
" - unclaimed exception 0x%x",
}
break;
case EVENT_RESET:
case LOM_RESET_DETAIL_BYUSER:
break;
" after flash download");
break;
default:
" - unknown reason");
break;
}
break;
default:
break;
}
break;
case EVENT_SUBSYS_LED:
case LOM_LED_STATE_OFF:
break;
case LOM_LED_STATE_ON_STEADY:
break;
break;
break;
case LOM_LED_STATE_STANDBY:
break;
break;
default:
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_LOGINFAIL:
case EVENT_USER_LOGOUT:
event->ev_resource);
default:
break;
}
break;
case EVENT_SUBSYS_PSU:
== LOM_PSU_STATUS_MASK) {
} else {
/*
* If both inputs are seen to have failed then simply
* indicate that the PSU input has failed
*/
(LOM_PSU_INPUT_A_OK | LOM_PSU_INPUT_B_OK))) {
} else {
/* At least one input is ok */
" InA");
}
" InB");
}
/*
* Only flag an output error if an input is
* still present
*/
" Output");
}
}
}
break;
case EVENT_SUBSYS_NONE:
if (eventtype == EVENT_FAULT_LED) {
case 0:
break;
case 255:
break;
default:
break;
}
}
break;
case EVENT_SUBSYS_HOST:
if (eventtype == EVENT_BOOTMODE_CHANGE) {
" - no boot");
break;
" - reset defaults");
break;
case EBUS_BOOTMODE_FULLDIAG:
" - full diag");
break;
case EBUS_BOOTMODE_SKIPDIAG:
" - skip diag");
break;
default:
break;
}
}
if (eventtype == EVENT_SCC_STATUS) {
case 0:
" - inserted");
break;
case 1:
" - removed");
break;
default:
break;
}
}
break;
default:
break;
}
/* shutd */
}
/* Ensure newline at end of string */
#ifdef DEBUG
#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
{
int level;
/*
* This is the same criteria that the firmware uses except we
* log the fault led on as being EVENT_LEVEL_FAULT
*/
/*
* 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.
*/
/* None of FAULT, FATAL or SHUTDOWN REQD are set */
/* Only FAULT set i.e not FATAL or SHUTDOWN REQD */
} else {
}
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
{
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 changed to 0x%x",
/*
* 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.
*/
"completed delay");
} else if (dev_no == EBUS_DETAIL_RESET) {
/*
* The bsc has reset
*/
"BSC reset occured, re-synching");
(void) bscv_attach_common(ssp);
"completed attach_common");
}
}
/*
* Only remember fanspeeds which are real values or
* NOT PRESENT values.
*/
if ((fanspeed <= LOM_FAN_MAX_SPEED) ||
(fanspeed == LOM_FAN_NOT_PRESENT)) {
}
}
}
if (state_chng & EBUS_STATE_GP) {
}
if (state_chng & EBUS_STATE_CB) {
}
if ((state_chng & EBUS_STATE_TEMPERATURE) &&
/*
* Only remember temperatures which are real values or
* a NOT PRESENT value.
*/
if ((temp <= LOM_TEMP_MAX_VALUE) ||
(temp == LOM_TEMP_STATE_NOT_PRESENT)) {
}
}
if (state_chng & EBUS_STATE_RAIL) {
}
}
char *
{
return ("");
return ("-");
}
static void
{
int rv;
"nvlist alloc failure");
return;
}
"nvlist ENV_VERSION failure");
return;
}
"nvlist ENV_FRU_ID failure");
return;
}
"nvlist ENV_FRU_RESOURCE_ID failure");
return;
}
"nvlist ENV_FRU_DEVICE failure");
return;
}
"nvlist ENV_FRU_STATE failure");
return;
}
"nvlist ENV_MSG failure");
return;
}
if (rv == DDI_SUCCESS) {
} else {
}
}
/*
* 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
{
char *res_id;
case EVENT_SUBSYS_NONE:
break;
case EVENT_SUBSYS_ALARM:
break;
case EVENT_SUBSYS_TEMP:
case EVENT_SEVERE_OVERHEAT:
break;
case EVENT_OVERHEAT:
break;
case EVENT_NO_OVERHEAT:
break;
default:
return;
}
break;
case EVENT_SUBSYS_OVERTEMP:
break;
case EVENT_SUBSYS_FAN:
case EVENT_FAILED:
break;
case EVENT_RECOVERED:
break;
default:
return;
}
break;
case EVENT_SUBSYS_SUPPLY:
case EVENT_FAILED:
break;
case EVENT_RECOVERED:
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:
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.
*/
case LOM_LED_STATE_ON_STEADY:
break;
break;
case LOM_LED_STATE_OFF:
break;
break;
case LOM_LED_STATE_STANDBY:
break;
break;
default:
break;
}
break;
default :
break;
}
return;
}
}
/*
* *********************************************************************
* 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.
* and restart the event daemon
*
* inputs - soft state pointer, arg ptr, ioctl mode
* outputs - status
*/
static int
{
int res = 0;
/*
* We will get repeatedly called with bits of data first for
* loader, then for main image.
*/
mode) < 0) {
return (EFAULT);
}
/*
* 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].
*/
sizeof (lom_prog_t), mode) < 0) {
}
} else {
}
} else {
}
}
return (res);
}
static int
{
"loader_running %d, is_image2 %d",
/*
* 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.
*/
"with loader image already active");
"failed firmware download - ignoring download attempt");
return (B_FALSE);
"without loader image active");
return (B_FALSE);
}
return (B_TRUE);
}
static uint32_t
{
return (pagesize);
}
/*
* Sets the pagesize, returning the old value.
*/
static uint32_t
{
/*
* The microcontroller remembers this value until until someone
* changes it.
*/
return (old_pagesize);
}
static uint8_t
{
return (retval);
}
static void
{
if (with_jmp) {
"jumptoaddr");
} else {
"prgmode_off");
}
}
static void
{
"set jump to loadaddr 0x%x", loadaddr);
}
static uint8_t
{
/*
* 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
*/
loadaddr);
/* set PSIZ to full size of image to be programmed */
/* write ERASE to PCSR */
/* read PCSR to check status */
return (retval);
}
static uint8_t
{
int retryable = BSC_ERASE_RETRY_LIMIT;
while (retryable--) {
if (PSR_SUCCESS(retval))
break;
else
", base 0x%x, size 0x%x, %s image",
}
return (retval);
}
static uint8_t
{
int retryable = BSC_PAGE_RETRY_LIMIT;
while (retryable--) {
/*
* Write the page address and read it back for confirmation.
*/
addr);
break;
else {
"set page 0x%x, read back 0x%x",
}
}
}
static uint8_t
{
int i;
/* write PSIZ bytes to PDAT */
} else {
"Sending last block, last 0x%x bytes",
(image_size % pagesize));
/* Now pad the rest of the page with zeros */
0);
}
}
/* write the checksum to PCSM */
chksum = 0;
for (i = 0; i < size; i++) {
}
/* Cope with non-pagesize sized bufers */
for (; i < pagesize; i++) {
}
*calcd_chksum = chksum;
return (retval);
}
{
int retryable = BSC_PAGE_RETRY_LIMIT;
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.
*/
if (!PSR_SUCCESS(retval)) {
"could not setup page address 0x%x, %s image",
break;
}
/*
* Send down the data for the page
*/
if (PSR_SUCCESS(retval))
break;
else
" attempt %d, index 0x%x, checksum 0x%x, %s image",
}
return (retval);
}
static uint8_t
{
"Failed to program lom (status 0x%x)", retval);
break;
}
}
return (retval);
}
static int
{
int res = 0;
"image 0x%x, imagep %p, size 0x%x",
/*
* Return no error to allow userland to continue on with
* downloading the image.
*/
return (0);
goto BSCV_PROG_IMAGE_END;
}
/*
* Only issue an erase if we are downloading the image. The loader
* does not need this step.
*/
if (is_image2 && (image_size != 0)) {
"lom: Erase failed during programming, status 0x%x",
retval);
goto BSCV_PROG_IMAGE_END;
} else {
"erase complete - programming...");
}
}
"Failed to program lom (status 0x%x)", retval);
goto BSCV_PROG_IMAGE_END;
}
/*
* We've downloaded the loader successfully. Now make the
* microcontroller jump to it.
*/
} 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) {
"got error 0x%x - leaving programming mode",
res);
} else {
"programming complete - leaving programming mode");
}
}
return (res);
}
static int
{
int res = 0;
"Got wrong buffer 0x%x, expected 0x%x",
return (EINVAL);
}
/*
* We want to get the whole image and then do the download.
* It is assumed the device is now in programming mode.
*/
/* Starting a new image */
}
"lom image exceeded maximum size: got 0x%x, maximum 0x%x",
return (EFAULT);
}
ssp->prog_index++;
/*
* OK we have the whole image so synch up and start download.
*/
/* Old style programming data */
/* Take care image may not fill all of structure */
/* sign extend loadaddr from 16 to 32 bits */
imagep[3]));
" expected 0x%x, got 0x%x",
}
/*
* Done the loading so set the flag to say we are doing
* the other image.
*/
/* Image too small for new style image */
} else {
/* New style programming image */
case PROG_PLAT_BSCV_IMAGE:
break;
case PROG_PLAT_BSCV_LOADER:
break;
default:
break;
}
}
ssp->prog_index = 0;
}
return (res);
}
static int
{
if (ssp->programming) {
/*
* Already programming - this may be a retry of a failed
* programming attempt or just a software error!
*/
goto queue_stopped;
}
"failed to pause event daemon thread");
return (EAGAIN);
}
ssp->prog_index = 0;
return (0);
}
static int
{
int res = 0;
if (!ssp->programming) {
/* Not programming so this is not a valid command */
return (EINVAL);
}
}
/*
* OK we are out of reset now so:
* Probe the firmware and set everything up.
*/
/* Explicit clear fault because things may have been mended now */
if (ssp->loader_running) {
"performing forced exit");
/* Must try to restart the lom here. */
/* Ensure prog mode entry to enable PRGMODE_OFF */
/* give the lom chance to recover */
}
}
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.
*/
}
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
{
/*
* Set the threshold for reporting messages to the console to
* Warnings or higher.
*/
/*
* When the system is not running the Operating System, make
* the microcontroller print event messages straight onto the
* console.
*/
/* Setup capabilities */
/*
* We want lom -G to talk to this driver upon broken firmware
* so we prematurely return success here.
*/
return (DDI_SUCCESS);
}
#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.
*/
#ifdef __sparc
#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
{
int instance;
}
/* Fail the cleanup - may be able to cleanup later */
}
return (DDI_FAILURE);
}
}
}
/*
* switch back on serial event reporting - cover all configs.
*/
bits2set = 0;
bits2clear = 0;
}
/*
* 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_cleanup - disable wdog");
0, EBUS_WDOG_RST | EBUS_WDOG_ENABLE);
}
}
/*
* unmap registers
*/
}
/*
* release any memory allocated for mutexes and condition
* variables before deallocating the structures containing them
*/
}
}
#endif /* __i386 || __amd64 */
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
*/
{
if (ssp->prog_mode_only) {
/* Turn off all capabilities */
return;
}
if (!bscv_faulty(ssp)) {
"Capability flags cap0=0x%x cap1=0x%x, cap2=0x%x",
} else {
}
}
/*
* 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
*/
{
int i;
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.
*/
if (bscv_faulty(ssp)) {
} else if (probeval != 0xAA) {
"LOMlite out of sync");
/*
* It may be that the LOMlite was out of
* sync so lets try the read again.
*/
if (bscv_faulty(ssp)) {
"Init readAA1 failed");
} else if (probeval != 0xAA) {
/*
* OK that is twice we are out so I
* guess the LOMlite is in trouble
*/
"Init readAA probe failed - got 0x%x",
probeval);
}
}
}
/*
* 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) {
if (bscv_faulty(ssp)) {
"Read of state change register failed");
}
}
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:
break;
}
if (bscv_faulty(ssp)) {
"Initial read or register %2x failed", i);
/* Might as well give up now! */
break;
}
}
}
/*
* Check the probe keys so we know the lom is OK
*/
if (!ssp->prog_mode_only) {
"LOMlite Probe failed");
for (i = 0; i < 0x8; i++) {
"%2x %2x %2x %2x %2x %2x %2x %2x %2x "
"%2x %2x %2x %2x %2x %2x %2x %2x %2x",
bscv_get8_cached(ssp, i),
}
}
}
}
#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
{
struct bscv_idi_callout *tbl;
if (bscv_idi_err())
"bscv_callout_table");
return;
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.
*/
"driver instance of "
MYNAME);
return;
}
/*
* We service the request with a valid instance number
* for the driver.
*/
/*
* If the request was serviced, clear any accumulated
* error counters so future warnings will be reported if
* seen.
*/
return;
} else {
tbl++;
}
}
if (bscv_idi_err())
}
/*
* 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
{
if (bscv_idi_err())
return (B_FALSE);
}
/* Get a lock on the SSP, notify our change, then exit */
return (B_TRUE);
}
/*
* function - bscv_sig_set
* description - write a signature
* inputs - data from client driver
* outputs - none.
*/
static boolean_t
{
if (bscv_idi_err())
return (B_FALSE);
}
/* Service the request */
return (B_TRUE);
}
#endif /* __sparc */
static void
{
/*
* The value of the dog pat is a sequence number which wraps around,
* bounded by BSCV_WDOG_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.
*/
pat);
}
#ifdef __sparc
/*
* function - bscv_wdog_pat
* description - pat the watchdog
* inputs - data from client driver
* outputs - none.
*/
/*ARGSUSED*/
static boolean_t
{
/*
* This function remembers if it has ever been called with the
* configure option set.
*/
if (bscv_idi_err())
return (B_FALSE);
/* Didn't manage to map handles so ddi_{get,put}* broken */
if (bscv_idi_err())
return (B_FALSE);
}
return (B_TRUE);
}
/*
* function - bscv_wdog_cfg
* description - configure the watchdog
* inputs - data from client driver
* outputs - none.
*/
static boolean_t
{
if (bscv_idi_err())
return (B_FALSE);
/* Didn't manage to map handles so ddi_{get,put}* broken */
if (bscv_idi_err())
return (B_FALSE);
}
sizeof (bscv_wdog_t));
return (B_FALSE);
}
"wdog_timeout_s %d, reset_system_on_timeout %s",
return (B_TRUE);
}
#endif /* __sparc */
static void
{
/*
* Configure the timeout value (1 to 127 seconds).
* the value further. The bounding here is to fit the timeout value
* into the 7 bits the bsc uses.
*/
if (wdog_timeout_s < 1)
else if (wdog_timeout_s > 127)
else
/*
* Configure the watchdog on or off.
*/
if (enable_wdog)
else
cfg &= ~EBUS_WDOG_NB_CFG_ENB;
/*
* Configure whether the microcontroller should reset the system when
* the watchdog expires.
*/
/* have the event daemon set the timeout value and whether to reset */
"configured the dog with cfg 0x%x", cfg);
}
/*
* function - bscv_setup_watchdog
* description - setup the bsc watchdog
* inputs - soft state ptr
* outputs -
*/
{
#ifdef __sparc
extern int watchdog_activated;
#endif /* __sparc */
/* Set the 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 {
}
/* Set other host defaults */
/* start the cyclic based watchdog patter */
#endif /* __i386 || __amd64 */
}
/*
* function - bscv_setup_hostname
* description - setup the lom hostname if different from the nodename
* inputs - soft state ptr
* outputs - none
*/
{
char host_nodename[128];
char lom_nodename[128];
/*
* Check machine label is the same as the
* system nodename.
*/
sizeof (host_nodename));
/* read in lom hostname */
/* Enforce null termination */
if ((nodelen > 0) &&
(const char *)&host_nodename)) ||
(hostlen == 0))) {
"nodename(%s,%d) != bsc label(%s,%d)",
/* Write new label into LOM EEPROM */
}
}
/*
* 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
{
int num_failures;
int length;
int i;
/*
* 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++) {
if (bscv_faulty(ssp)) {
needretry = 1;
} else {
needretry = 0;
for (i = 0; i < length; i++) {
/* Retry on any error */
if (bscv_retcode(ssp) != 0) {
needretry = 1;
break;
}
}
/* null terminate for strcmp later */
}
if (!needretry) {
break;
}
/* Force the nodename to be empty */
lom_nodename[0] = '\0';
}
if (needretry) {
/* Failure - we ran out of retries */
"bscv_read_hostname: retried %d times, giving up",
} else if (num_failures > 0) {
"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
{
int num_failures;
int i;
/*
* 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++) {
if (bscv_faulty(ssp)) {
needretry = 1;
} else {
needretry = 0;
for (i = 0; i < length; i++) {
/* Retry on any error */
if (bscv_retcode(ssp) != 0) {
needretry = 1;
break;
}
}
}
if (!needretry) {
break;
}
}
if (needretry) {
/* Failure - we ran out of retries */
"bscv_write_hostname: retried %d times, giving up",
} else if (num_failures > 0) {
"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
{
int i;
/*
* 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
*/
"lom: firmware reported too many voltage lines. ");
}
(void) bscv_read_env_name(ssp,
EBUS_IDX2_SUPPLY_FATAL_MASK1)) << 8;
}
/*
* 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.
*/
"lom: firmware reported too many temperatures being "
"monitored.");
}
"lom: firmware reported too many over temperatures being "
"monitored.");
}
"num temps %d, over temps %d",
/*
* If shutdown is not enabled then set it as zero so
* it is not displayed by the utility.
*/
EBUS_IDX4_TEMP_FATAL_MASK)) >> i) & 0x01) {
} else {
}
}
if ((temp <= LOM_TEMP_MAX_VALUE) ||
(temp == LOM_TEMP_STATE_NOT_PRESENT)) {
} else {
/* New value is not known - use old value */
}
}
/*
* Check for and skip a single 0xff character between the
* temperature and over temperature names
*/
}
(void) bscv_read_env_name(ssp,
/*
* To get the CB static info we need address space 3
*/
"lom: firmware reported too many status flags.");
"Reported %d, maximum is %d",
}
(void) bscv_read_env_name(ssp,
/*
* To get the fan static info we need address space 5
*/
"lom: firmware reported too many fans. ");
"Reported %d, maximum is %d",
}
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.
*/
}
}
(void) bscv_read_env_name(ssp,
/* Get led static information from address space 10 */
(void) bscv_read_env_name(ssp,
}
/*
* 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
char namebuf[][MAX_LOM2_NAME_STR],
int numnames)
{
int i;
int nameidx;
int namemax;
unsigned int addr_space_ptr;
"bscv_read_env_name, space %d, start 0x%x, end 0x%x, numnames %d",
for (i = 0; i < numnames; i++) {
nameidx = 0;
while (addr_space_ptr <= addr_end) {
/*
* Read the current character.
*/
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
*/
if (this_char == '\0') {
/* Found end of string - done */
break;
}
/*
* Buffer not full - record character
* NOTE we always leave room for the NUL
* terminator.
*/
}
}
/* Ensure null termination */
}
/* 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
{
/*
* deal with event reporting - cover all cases
*/
bits2set = 0;
bits2clear = 0;
}
}
#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
{
/* Upload the 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. */
} 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.
*/
s.cpu %= 256;
}
}
}
#endif /* __sparc */
/*
* 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: state=%d", state);
}
/*
* 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)
{
}
/*
* 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
{
"watchdog_activated=%d", watchdog_activated);
}
/*
* 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
{
"timeout=%d", timeoutval);
/*
* We get started during bscv_attach only
* if bscv_watchdog_enable is set.
*/
if (bscv_watchdog_available && (!watchdog_activated ||
(timeoutval != bscv_watchdog_timeout_seconds)))) {
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
{
if (bscv_watchdog_available && watchdog_activated) {
bscv_watchdog_enable = 0;
}
}
/*
* 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
{
"disabling watchdog");
/*
* We dont get interrupts during the panic callback. But bscbus
* takes care of all this
*/
return (DDI_SUCCESS);
}
/*
* function - bscv_watchdog_cyclic_add
* description - add the watchdog patter cyclic
* inputs - driver soft state
* outputs - none
*/
static void
{
return;
}
"cyclic added");
}
/*
* function - bscv_watchdog_cyclic_remove
* description - remove the watchdog patter cyclic
* inputs - soft state ptr
* outputs - none
*/
static void
{
return;
}
"cyclic removed");
}
#endif /* __i386 || __amd64 */
/*
* General utility routines ...
*/
#ifdef DEBUG
static void
const char *fmt, ...)
{
char buf[256];
char *p;
p = buf;
p += strlen(p);
}
}
#else /* DEBUG */
static void
const char *fmt, ...)
{
}
#endif /* DEBUG */