/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/sgevents.h>
#include <sys/serengeti.h>
#include <sys/sgsbbc_iosram.h>
#include <sys/sgsbbc_mailbox.h>
#include <sys/machsystm.h>
#include <sys/sysevent.h>
#include <sys/lw8_impl.h>
#include <sys/plat_ecc_unum.h>
/*
* Global Variables - can be patched from Solaris
* ==============================================
*/
/*
* Module Variables
* ================
*/
/*
* functions local to this driver.
*/
static int lw8_add_intr_handlers(void);
static int lw8_remove_intr_handlers(void);
static void lw8_wakeup_sleepers(void);
static uint_t lw8_event_data_handler(char *);
static uint_t lw8_dr_data_handler(char *);
static uint_t lw8_env_data_handler(char *);
static uint_t lw8_cap_ecc_msg_handler(char *);
static void lw8_logger_start(void);
static void lw8_logger_destroy(void);
static void lw8_logger_wakeup(void);
/*
* Driver entry points
*/
lw8_open, /* open */
lw8_close, /* close */
nodev, /* strategy() */
nodev, /* print() */
nodev, /* dump() */
nodev, /* read() */
nodev, /* write() */
lw8_ioctl, /* ioctl() */
nodev, /* devmap() */
nodev, /* mmap() */
ddi_segmap, /* segmap() */
nochpoll, /* poll() */
ddi_prop_op, /* prop_op() */
NULL, /* cb_str */
};
0, /* ref count */
ddi_getinfo_1to1, /* getinfo() */
nulldev, /* identify() */
nulldev, /* probe() */
lw8_attach, /* attach() */
lw8_detach, /* detach */
nodev, /* reset */
&lw8_cb_ops, /* pointer to cb_ops structure */
nulldev, /* power() */
ddi_quiesce_not_needed, /* quiesce() */
};
/*
* Loadable module support.
*/
extern struct mod_ops mod_driverops;
&mod_driverops, /* Type of module. This is a driver */
"Netra-T12 control driver", /* Name of the module */
&lw8_ops /* pointer to the dev_ops structure */
};
&modldrv,
};
/*
* messages
*/
"SC request.\n"
"voltage out of range.\n"
"temperature exceeding limits.\n"
"too many fan failures.\n"
"no system configuration card.\n"
/*
* led table - the following provides a cache of the led state - needed
* to avoid the overhead of readoing from the SC each time
*/
struct led_info {
int position;
int status;
};
static struct fru_led_info {
};
"SB0",
"PS0",
"SB2",
"PS1",
"SB4",
"PS2",
"IB6",
"PS3",
"SCC",
"SSC1",
};
/*
* mutexes which protect the interrupt handlers.
*/
/*
* state booleans
*/
/*
* Payloads of the event handlers.
*/
/*
* The IDs of the soft interrupts
*/
/*
* Logger commands..
*/
#define LW8_LOGGER_WAIT 0
/*
* Logger thread state
*/
extern pri_t maxclsyspri;
int
_init(void)
{
int error = 0;
if (error) {
}
return (error);
}
int
{
}
int
_fini(void)
{
int error = 0;
if (error)
return (error);
return (error);
}
static int
{
int instance;
int err;
switch (cmd) {
case DDI_ATTACH:
/*
* only allow one instance
*/
if (instance != 0)
return (DDI_FAILURE);
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
if (err != 0) {
"handler for lw8. Err=%d", err);
return (DDI_FAILURE);
}
if (err != 0) {
"handler for lw8. Err=%d", err);
return (DDI_FAILURE);
}
/*
* Add the handlers which watch for unsolicited messages
* and post event to Sysevent Framework.
*/
err = lw8_add_intr_handlers();
if (err != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
{
int instance;
int err;
switch (cmd) {
case DDI_DETACH:
if (instance != 0)
return (DDI_FAILURE);
/*
* Remove the handlers which watch for unsolicited messages
* and post event to Sysevent Framework.
*/
if (err != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
{
int err;
if (err != 0) {
" handler. Err=%d", err);
return (DDI_FAILURE);
}
if (err != 0) {
" handler. Err=%d", err);
(void) sbbc_mbox_unreg_intr(MBOX_EVENT_LW8,
return (DDI_FAILURE);
}
if (err != 0) {
" handler. Err=%d", err);
(void) sbbc_mbox_unreg_intr(MBOX_EVENT_LW8,
return (DDI_FAILURE);
}
if (err != 0) {
" handler. Err=%d", err);
(void) sbbc_mbox_unreg_intr(MBOX_EVENT_LW8,
(void) sbbc_mbox_unreg_intr(INFO_MBOX,
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
lw8_remove_intr_handlers(void)
{
int err;
if (err != 0) {
"handler. Err=%d", err);
rv = DDI_FAILURE;
}
if (err != 0) {
"handler. Err=%d", err);
rv = DDI_FAILURE;
}
if (err != 0) {
"handler. Err=%d", err);
rv = DDI_FAILURE;
}
if (err != 0) {
"handler. Err=%d", err);
rv = DDI_FAILURE;
}
return (rv);
}
static uint_t
{
int hint;
int rv = 0;
return (DDI_INTR_CLAIMED);
}
return (DDI_INTR_CLAIMED);
}
sizeof (char *)) {
return (DDI_INTR_CLAIMED);
}
/*
* if not SB send sysevent (SBs send sysevent from ssm driver)
*/
switch (payload->event_details) {
case SG_EVT_BOARD_ABSENT:
break;
case SG_EVT_BOARD_PRESENT:
break;
default:
hint = SE_NO_HINT;
break;
}
return (DDI_INTR_CLAIMED);
}
if (rv != 0) {
return (DDI_INTR_CLAIMED);
}
/*
* Add the hint
*/
if (rv != 0) {
return (DDI_INTR_CLAIMED);
}
"event", EC_DR);
return (DDI_INTR_CLAIMED);
}
if (rv != 0) {
"lw8_dr_event_handler: failed to log event");
}
}
return (DDI_INTR_CLAIMED);
}
static uint_t
{
return (DDI_INTR_CLAIMED);
switch (cap->capd_msg_type) {
break;
default:
break;
}
return (DDI_INTR_CLAIMED);
}
/*ARGSUSED*/
static uint_t
{
return (DDI_INTR_CLAIMED);
}
/*
* wakeup sleepers + mark led cache for this fru as invalid
*/
static void
{
}
/*
* This function is triggered by a soft interrupt and it's purpose is to call
* to kadmin() to shutdown the system.
*/
/*ARGSUSED*/
static uint_t
{
/*
* If kadmin fails for some reason then we bring the system down
* via power_down(), or failing that using halt().
*/
power_down("kadmin() failed, trying power_down()");
halt("power_down() failed, trying halt()");
/*
* We should never make it this far, so something must have gone
* horribly, horribly wrong.
*/
/*NOTREACHED*/
return (DDI_INTR_UNCLAIMED);
}
/*
* This function is triggered by a soft interrupt and it's purpose is to call
* to do_shutdown() to shutdown the system.
*/
/*ARGSUSED*/
static uint_t
{
do_shutdown();
return (DDI_SUCCESS);
}
static uint_t
{
return (DDI_INTR_CLAIMED);
}
return (DDI_INTR_CLAIMED);
}
switch (payload->event_type) {
/*
* Let the user know why the domain is going down.
*/
/*NOTREACHED*/
break;
/*
* Let the user know why the domain is going down.
*/
/*NOTREACHED*/
break;
/*
* Let the user know why the domain is going down.
*/
/*NOTREACHED*/
break;
/*
* Let the user know why the domain is going down.
*/
/*NOTREACHED*/
break;
/*
* Let the user know why the domain is going down.
*/
/*NOTREACHED*/
break;
case LW8_EVENT_NEW_LOG_MSG:
/*
* Wake up the log retrieval thread.
*/
break;
default:
return (DDI_INTR_CLAIMED);
}
return (DDI_INTR_CLAIMED);
}
/*ARGSUSED*/
static int
{
int error = 0;
static fn_t f = "lw8_open";
if (instance != 0)
return (ENXIO);
f, instance);
return (error);
}
return (error);
}
/*ARGSUSED*/
static int
{
return (DDI_SUCCESS);
}
static int
{
int rv = 0;
switch (cmd) {
case LW8_MBOX_GET_INFO:
break;
case LW8_MBOX_SET_CTL:
break;
case LW8_MBOX_UPDATE_FW:
break;
case LW8_MBOX_GET_LED:
break;
case LW8_MBOX_SET_LED:
break;
case LW8_MBOX_GET_EVENTS:
/*
* cast as lom_eventreq_t to minimise data traffic
*/
break;
case LW8_MBOX_GET_NEXT_MSG:
break;
default:
return (EINVAL);
}
/* errors from sgsbbc */
if (resp->msg_status > 0) {
return (resp->msg_status);
}
/* errors from SCAPP */
switch (resp->msg_status) {
/* internal SCAPP error */
return (EINTR);
return (EIO);
/* illegal ioctl parameter */
return (EINVAL);
/* board access denied */
return (EACCES);
/* stale contents */
return (ESTALE);
/* stale handle */
return (ENOENT);
/* seprom lacks space */
return (ENOSPC);
case SG_MBOX_STATUS_NO_MEMORY:
/* user prog. lacks space */
return (ENOMEM);
/* unsupported operation */
return (ENOTSUP);
default:
return (EIO);
}
}
return (0);
}
/*
* set the requested led, and mark cache as empty
*/
static int
{
int retval;
int i, j;
for (i = 0; i < MAX_FRUS; i++) {
MAX_LOCATION_LEN) != 0)
continue;
for (j = 0; j < MAX_LEDS_PER_FRU; j++) {
continue;
continue;
/*
* to minimise data transfer, the SC maintains
* just 3 values per fru - except for
* the chassis itself at the end which has
* MAX_LEDS_PER_FRU
*/
(intptr_t)&lw8_set_led);
if (retval != 0)
return (retval);
return (0);
}
}
return (EINVAL);
}
/*
* read led value from cache if possible, otherwise read from sc and
* update the cache
*/
static int
{
int retval;
int i, j, k;
for (i = 0; i < MAX_FRUS; i++) {
MAX_LOCATION_LEN) != 0)
continue;
return (0);
}
for (j = 0; j < MAX_LEDS_PER_FRU; j++) {
continue;
continue;
if (!led_state_cached) {
(intptr_t)&lw8_get_led);
if (retval != 0)
return (retval);
/*
* to minimise data transfer, the
* lw8_get_led_payload_t structure just has 3
* values per fru - except for the chassis
* itself at the end which has MAX_LEDS_PER_FRU
*/
}
for (k = 0; k < MAX_LEDS_PER_FRU; k++) {
}
}
if (j == MAX_LEDS_PER_FRU - 1) {
return (0);
}
return (0);
}
}
return (0);
}
return (EINVAL);
}
/*ARGSUSED*/
static int
int *rval_p)
{
int retval = 0;
int i, j;
if (instance != 0)
return (ENXIO);
switch (cmd) {
case LOMIOCWTMON:
if (!lw8_event_pending) {
break;
}
}
break;
case LOMIOCMREAD:
}
break;
case LOMIOCCTL2:
break;
}
break;
case LOMIOCPROG:
break;
}
break;
case LOMIOCINFO2:
if (retval != 0)
break;
}
break;
case LOMIOCINFO:
if (retval != 0)
break;
}
break;
case LOMIOCFLEDSTATE:
if (retval != 0)
break;
sizeof (lw8_fled_info), mode) != 0) {
}
break;
case LOMIOCALSTATE:
sizeof (lw8_aldata), mode) != 0) {
break;
}
else
if (retval != 0)
break;
sizeof (lw8_aldata), mode) != 0) {
}
break;
case LOMIOCGETLED:
sizeof (lw8_get_led), mode) != 0) {
break;
}
if (retval != 0)
break;
sizeof (lw8_get_led), mode) != 0) {
}
break;
case LOMIOCEVENTLOG2:
sizeof (*lw8_eventlogp), mode) != 0) {
break;
}
if (retval == 0) {
for (i = 0; i < lw8_eventresp->num; i++) {
for (j = 0; j < MAX_EVENT_STR; j++) {
lw8_eventlogp->string[i][j] =
lw8_eventresp->string[i][j];
}
}
sizeof (*lw8_eventlogp), mode) != 0) {
}
}
break;
case LOMIOCALCTL:
sizeof (lw8_aldata), mode) != 0) {
break;
}
else
break;
case LOMIOCSETLED:
sizeof (lw8_set_led), mode) != 0) {
break;
}
break;
case LOMIOCCTL:
/*
* for this ioctl, as well as setting the fault led in the
* LOMIOCCTL case in lw8_lomcmd(), we also need to set the
* escape character. To do this we must use LW8_MBOX_SET_CTL,
* but this also needs the serial_event value which we have
* to get via LW8_MBOX_GET_INFO
*/
break;
}
if (retval != 0)
break;
if (retval != 0)
break;
/*
* if fault_led != 0, then set the led
*/
break;
break;
default:
break;
}
return (retval);
}
/* ARGSUSED */
static void
{
char level;
int retval;
"lw8_logger");
for (;;) {
/*
* Wait for someone to tell me to continue.
*/
while (lw8_logger_sig == LW8_LOGGER_WAIT) {
}
/* LW8_LOGGER_EXITNOW implies signal by _detach(). */
if (lw8_logger_sig == LW8_LOGGER_EXITNOW) {
/* lw8_logger_lock is held at this point! */
thread_exit();
/* NOTREACHED */
}
/* Do lw8_event logging */
/*
* Get one message per iteration. We do not sleep if
* there are more to process. This makes exit from the
* routine much more reliable.
*/
if (retval == 0) {
if (lw8_logmsgp->msg_valid) {
switch (lw8_logmsgp->level) {
case 0: /* LOG_EMERG */
break;
case 1: /* LOG_ALERT */
break;
case 2: /* LOG_CRIT */
break;
case 3: /* LOG_ERR */
break;
case 4: /* LOG_WARNING */
break;
case 5: /* LOG_NOTICE */
break;
case 6: /* LOG_INFO */
break;
case 7: /* LOG_DEBUG */
break;
default: /* unknown */
break;
}
/* Ensure NUL termination */
lw8_logmsgp->msg);
}
if (lw8_logmsgp->num_remaining > 0)
}
/*
* Re-enter the lock to prepare for another iteration.
* We must have the lock here to protect lw8_logger_sig.
*/
/* We need to get more events */
}
}
static void
lw8_logger_start(void)
{
if (lw8_logger_tid == 0) {
/* Force retrieval of any pending messages */
}
}
static void
lw8_logger_destroy(void)
{
if (tid != 0) {
lw8_logger_tid = 0;
}
/*
* Wait for lw8_logger() to finish.
*/
if (tid != 0)
}
static void
lw8_logger_wakeup(void)
{
if (lw8_logger_sig != LW8_LOGGER_EXITNOW)
}