/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/rmc_comm_dp.h>
#include <sys/rmc_comm_dp_boot.h>
#include <sys/rmc_comm_drvintf.h>
#include <sys/rmc_comm.h>
#include <sys/machsystm.h>
#include <sys/sysevent.h>
#include <sys/rmclomv_impl.h>
#include <sys/cpu_sgnblk_defs.h>
#include <sys/systeminfo.h>
#include <sys/sysmacros.h>
extern void pmugpio_watchdog_pat();
extern int watchdog_activated;
extern int watchdog_enable;
extern int boothowto;
/*
* functions local to this driver.
*/
void **resultp);
static int rmclomv_add_intr_handlers(void);
static int rmclomv_remove_intr_handlers(void);
static uint_t rmclomv_event_data_handler(char *);
static void rmclomv_dr_data_handler(const char *, int);
static void rmclomv_checkrmc_start(void);
static void rmclomv_checkrmc_destroy(void);
static void rmclomv_checkrmc_wakeup(void *);
static void rmclomv_refresh_start(void);
static void rmclomv_refresh_destroy(void);
static void rmclomv_refresh_wakeup(void);
int index);
static void refresh_name_cache(int force_fail);
int detector_type);
static uint_t rmc_clear_watchdog_timer(void);
static void send_watchdog_msg(int msg);
static void plat_timesync(void *arg);
/*
* Driver entry points
*/
rmclomv_open, /* open */
rmclomv_close, /* close */
nodev, /* strategy() */
nodev, /* print() */
nodev, /* dump() */
nodev, /* read() */
nodev, /* write() */
rmclomv_ioctl, /* ioctl() */
nodev, /* devmap() */
nodev, /* mmap() */
ddi_segmap, /* segmap() */
nochpoll, /* poll() */
ddi_prop_op, /* prop_op() */
NULL, /* cb_str */
};
0, /* ref count */
rmclomv_getinfo, /* getinfo() */
nulldev, /* identify() */
nulldev, /* probe() */
rmclomv_attach, /* attach() */
rmclomv_detach, /* detach */
nodev, /* reset */
&rmclomv_cb_ops, /* pointer to cb_ops structure */
nulldev, /* power() */
ddi_quiesce_not_supported, /* devo_quiesce */
};
/*
* Loadable module support.
*/
extern struct mod_ops mod_driverops;
&mod_driverops, /* Type of module. This is a driver */
"rmclomv control driver", /* Name of the module */
&rmclomv_ops /* pointer to the dev_ops structure */
};
&modldrv,
};
/*
* Device info
*/
extern void (*abort_seq_handler)();
/* key_position is effective key-position. Set to locked if unknown */
/* real_key_position starts off as unknown and records value actually seen */
static void rmclomv_abort_seq_handler(char *msg);
/*
* mutexes which protect the interrupt handlers.
*/
/*
* mutex to protect the handle_name cache
*/
/*
* mutex to protect the RMC state
*/
/*
* Payloads of the event handlers.
*/
/*
* Checkrmc commands..
*/
#define RMCLOMV_CHECKRMC_WAIT 0
/*
* Checkrmc thread state
*/
/*
* RMC state data
*/
#define RMCLOMV_RMCSTATE_UNKNOWN 0
/*
* RMC error indicator values (status from last RMC command)
*/
#define RMCLOMV_RMCERROR_NONE 0
/* fail RMC after 5 minutes without a good response */
/*
* rmclomv_rmc_state is the state reported in OperationalStatus.
* rmclomv_rmc_error reflects the result of the last RMC interaction.
* rmclomv_rmcfailcount is used by the rmclomv_checkrmc thread to count
* failures in its regular status polls. Once RMCLOMV_RMCFAILTHRESHOLD
* is reached, rmclomv_rmc_state is marked as RMCLOMV_RMCSTATE_FAILED.
*/
static int rmclomv_rmcfailcount;
/*
* Refresh commands..
*/
#define RMCLOMV_REFRESH_WAIT 0
/*
* Refresh thread state
*/
/*
* timeout id
*/
/*
* Handle-name cache
*/
static int rmclomv_cache_valid;
extern pri_t maxclsyspri;
/*
* static strings
*/
int
_init(void)
{
int error = 0;
if (error) {
}
return (error);
}
int
{
}
int
_fini(void)
{
int error = 0;
if (error)
return (error);
return (error);
}
/* ARGSUSED */
static int
{
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
if ((m != 0) || (rmclomv_dip == NULL)) {
return (DDI_FAILURE);
}
*resultp = rmclomv_dip;
return (DDI_SUCCESS);
case DDI_INFO_DEVT2INSTANCE:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
{
int instance;
int err;
char *wdog_state;
switch (cmd) {
case DDI_ATTACH:
/*
* only allow one instance
*/
if (instance != 0)
return (DDI_FAILURE);
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
/*
* Register with rmc_comm to prevent it being detached
* (in the unlikely event that its attach succeeded on a
* platform whose platmod doesn't lock it down).
*/
err = rmc_comm_register();
if (err != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* Remember the dev info */
rmclomv_dip = dip;
/*
* Add the handlers which watch for unsolicited messages
* and post event to Sysevent Framework.
*/
if (err != DDI_SUCCESS) {
rmclomv_dip = NULL;
return (DDI_FAILURE);
}
/*
* Check whether we have an application watchdog
*/
&wdog_state) == DDI_PROP_SUCCESS) {
watchdog_enable = 0;
}
else
}
/*
* Now is a good time to activate hardware watchdog
* (if one exists).
*/
if (err != 0)
printf("Hardware watchdog enabled\n");
/*
* Set time interval and start timesync routine.
* Also just this once set the Solaris clock
* to the RMC clock.
*/
plat_timesync((void *) &attaching);
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);
}
tid = timesync_tid;
timesync_tid = 0;
timesync_interval = 0;
/* Forget the dev info */
rmclomv_dip = NULL;
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
{
int err;
return (DDI_FAILURE);
}
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
if (err != 0) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
{
if (err != 0) {
"handler. Err=%d", err);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
{
if (key_position == RMC_KEYSWITCH_POS_LOCKED)
"ignoring debug enter sequence");
else {
}
}
/* ARGSUSED */
static uint_t
{
if (rmclomv_break_requested) {
return (DDI_INTR_CLAIMED);
}
return (DDI_INTR_UNCLAIMED);
}
/*
* Create a cache section structure
*/
static rmclomv_cache_section_t *
{
num * sizeof (rmclomv_cache_entry_t);
return (ptr);
}
/*
* Free a cache_section.
*/
static void
{
}
/*
* adds supplied section to end of cache chain
* must be called with cache locked
*/
static void
{
}
/*
* This function releases all cache sections and exchanges the two
* chain heads for new values.
*/
static void
{
else
sizeof (rmclomv_sysinfo_data));
}
}
}
/*
* cache must be locked before calling rmclomv_find_section
*/
static rmclomv_cache_section_t *
{
return (next);
}
/*
* Return a string presenting the keyswitch position
* For unknown values returns "Unknown"
*/
static char *
{
switch (pos) {
case RMC_KEYSWITCH_POS_NORMAL:
return ("NORMAL");
case RMC_KEYSWITCH_POS_DIAG:
return ("DIAG");
case RMC_KEYSWITCH_POS_LOCKED:
return ("LOCKED");
case RMC_KEYSWITCH_POS_OFF:
return ("STBY");
default:
return ("UNKNOWN");
}
}
/*
* The sensor id name is sought in the supplied section and if found
* its index within the section is written to *index.
* Return value is zero for success, otherwise -1.
* The cache must be locked before calling get_sensor_by_name
*/
static int
{
int i;
for (i = 0; i < section->num_entries; i++) {
*index = i;
return (0);
}
}
*index = 0;
return (-1);
}
/*
* fills in the envmon_handle name
* if it is unknown (not cached), the dp_handle_t is returned as a hex-digit
* string
*/
static void
{
int i;
for (i = 0; i < next->num_entries; i++) {
return;
}
}
}
/*
* Sought handle not currently cached.
*/
"Unknown SC node 0x%x", hdl);
}
static void
{
int err = 0;
if (err != 0) {
"Failed to allocate name-value list for %s event", EC_DR);
return;
}
if (err != 0) {
return;
}
/*
* Add the hint
*/
if (err != 0) {
return;
}
if (err != 0) {
}
}
static void
{
int err;
if (err != 0) {
"Failed to allocate name-value list for %s/%s event",
return;
}
if (err != 0) {
return;
}
if (err != 0) {
return;
}
if (err != 0) {
return;
}
if (err != 0) {
return;
}
if (sub_event == RMC_ENV_FAULT_EVENT) {
} else {
}
if (err != 0) {
return;
}
if (err != 0) {
}
}
static void
char event_type)
{
int err;
char *subclass;
if (err != 0) {
"Failed to allocate name-value list for %s/%s event",
return;
}
if (err != 0) {
return;
}
if (err != 0) {
return;
}
if (err != 0) {
return;
}
switch (sub_event) {
case RMC_ENV_OK_EVENT:
break;
break;
break;
}
if (err != 0) {
return;
}
switch (sub_event) {
case RMC_ENV_OK_EVENT:
"sensor %s/%s is now ok", fru_name,
break;
"sensor %s/%s is now outside warning thresholds", fru_name,
break;
"sensor %s/%s is now outside shutdown thresholds", fru_name,
break;
}
if (err != 0) {
return;
}
if (err != 0) {
}
}
static uint_t
{
int hint;
return (DDI_INTR_CLAIMED);
}
return (DDI_INTR_CLAIMED);
}
case RMC_KEYSWITCH_EVENT:
if ((real_key_position != RMC_KEYSWITCH_POS_UNKNOWN) &&
} else {
/* treat unknown key position as locked */
}
break;
case RMC_HPU_EVENT:
/*
* send appropriate sysevent
*/
case RMC_HPU_REMOVE_EVENT:
break;
case RMC_HPU_INSERT_EVENT:
break;
default:
hint = SE_NO_HINT;
break;
}
&envhdl);
break;
case RMC_INIT_EVENT:
/*
* Wake up the refresh thread.
*/
/*
* Wake up the checkrmc thread for an early indication to PICL
*/
break;
case RMC_ENV_EVENT:
&envhdl);
/* split name into fru name and sensor name */
/* must have at least one '.' */
break;
/* find last '.' - convert the others to '/' */
for (;;) {
break;
}
*save_ptr = '/';
}
*ptr = '\0';
ptr++;
/* is it a voltage or temperature sensor? */
case RMC_ENV_OK_EVENT:
*ptr);
break;
default:
break;
}
}
/*
* is it a fan sensor?
* Fan sensor names end either in RS, F0 or F1
*/
case RMC_ENV_FAULT_EVENT:
case RMC_ENV_OK_EVENT:
break;
default:
break;
}
}
break;
case RMC_LOG_EVENT:
{
char *message =
/*
* Logs have a 10 character prefix - specifying the severity of
* the event being logged. Thus all the magic number 10s down
* here
*/
message += 10;
level = 0;
message += 10;
level = 5;
message += 10;
level = 10;
}
break;
}
default:
return (DDI_INTR_CLAIMED);
}
return (DDI_INTR_CLAIMED);
}
/*ARGSUSED*/
static int
{
int error = 0;
if (instance != 0)
return (ENXIO);
return (error);
return (0);
}
/*ARGSUSED*/
static int
{
return (DDI_SUCCESS);
}
static int
{
int rv = 0;
switch (req_cmd) {
case DP_GET_SYSINFO:
break;
case DP_GET_EVENT_LOG:
break;
case DP_GET_VOLTS:
break;
case DP_GET_TEMPERATURES:
break;
case DP_GET_CIRCUIT_BRKS:
break;
case DP_GET_FAN_STATUS:
break;
case DP_GET_PSU_STATUS:
break;
case DP_GET_LED_STATE:
break;
case DP_SET_LED_STATE:
break;
case DP_GET_FRU_STATUS:
break;
case DP_GET_HANDLE_NAME:
break;
case DP_GET_ALARM_STATE:
break;
case DP_SET_ALARM_STATE:
break;
case DP_GET_SDP_VERSION:
break;
case DP_GET_CHASSIS_SERIALNUM:
break;
case DP_GET_DATE_TIME:
break;
default:
return (EINVAL);
}
/*
* RMC returned an error or failed to respond.
* Where the RMC itself is implicated, rmclomv_rmc_error
* is set non-zero. It is cleared after an error free exchange.
* Two failure cases are distinguished:
* RMCLOMV_RMCSTATE_FAILED and RMCLOMV_RMCSTATE_DOWNLOAD.
*/
switch (rv) {
case RCENOSOFTSTATE:
return (EIO);
case RCENODATALINK:
/*
* firmware download in progress,
* can you come back later?
*/
return (EAGAIN);
case RCENOMEM:
/* memory problems */
return (ENOMEM);
case RCECANTRESEND:
/* resend failed */
return (EIO);
case RCEMAXRETRIES:
/* reply not received - retries exceeded */
return (EINTR);
case RCETIMEOUT:
/* reply not received - command has timed out */
return (EINTR);
case RCEINVCMD:
/* data protocol cmd not supported */
return (ENOTSUP);
case RCEINVARG:
/* invalid argument(s) */
return (ENOTSUP);
case RCEGENERIC:
/* generic error */
return (EIO);
default:
return (EIO);
}
}
return (0);
}
/*
* validate_section_entry checks that the entry at the specified index
* is valid and not duplicated by an entry above. If these tests fail
* the entry is removed and B_FALSE returned. Otherwise returns B_TRUE.
*/
static int
{
int i;
"rmclomv: empty handle_name, handle 0x%x type %x",
continue; /* skip special entries */
"rmclomv: null handle id for \"%s\" type %x",
} else if (i == index) {
continue;
"rmclomv: duplicate handle 0x%x type %x",
"rmclomv: duplicate handle_name \"%s\", "
} else
continue;
/*
* need to remove the entry at index
*/
section->num_entries--;
}
return (B_FALSE);
}
return (B_TRUE);
}
/*
* Populate a section containing handles with corresponding names
* The supplied section structure must not be publically visible and the
* name cache must not be locked either (because RMC i/o is required).
*
* This is the place where a sanity check is applied. Entries containing
* duplicate handles, duplicate names or empty names are removed and the
* structure is compacted. As a result num_entries may be reduced.
*/
static int
{
int retval = 0;
int index;
continue; /* skip special entries */
DP_GET_HANDLE_NAME_R, sizeof (handle_name_r),
if (retval == 0)
}
/*
* now ditch invalid and duplicate entries
*/
}
if (ditched)
return (retval);
}
/*
* The supplied (PSU) cache section is traversed and entries are created
* for the individual indicators belonging to a PSU. These entries are
* placed in a private chain. The caller, subsequently acquires the
* cache lock and copies the chain head to make it public.
* The handle-names for PSU indicators are derived from the parent PSU
* handle-name.
* NOTE: add_names_to_section() may have reduced psu_section->num_entries
* so DON'T USE psu_resp->num_psus
*/
static void
{
int index;
int subindex = 0;
DP_PSU_INPUT_STATUS) != 0) {
ENVMON_MAXNAMELEN, "%s.%s",
}
DP_PSU_SEC_INPUT_STATUS) != 0) {
ENVMON_MAXNAMELEN, "%s.%s",
}
DP_PSU_OUTPUT_STATUS) != 0) {
ENVMON_MAXNAMELEN, "%s.%s",
}
DP_PSU_OUTPUT_VLO_STATUS) != 0) {
ENVMON_MAXNAMELEN, "%s.%s",
}
DP_PSU_OUTPUT_VHI_STATUS) != 0) {
ENVMON_MAXNAMELEN, "%s.%s",
}
}
/*
* Adjust number of entries value in cache section
* to match the facts.
*/
subindex = 0;
if ((mask & DP_PSU_OUTPUT_AHI_STATUS) != 0) {
ENVMON_MAXNAMELEN, "%s.%s",
}
if ((mask & DP_PSU_NR_WARNING) != 0) {
ENVMON_MAXNAMELEN, "%s.%s",
}
}
subindex = 0;
DP_PSU_OVERTEMP_FAULT) != 0) {
ENVMON_MAXNAMELEN, "%s.%s",
}
}
subindex = 0;
if ((mask & DP_PSU_FAN_FAULT) != 0) {
ENVMON_MAXNAMELEN, "%s.%s",
}
if ((mask & DP_PSU_PDCT_FAN) != 0) {
ENVMON_MAXNAMELEN, "%s.%s",
}
}
}
static void
{
union {
} rmc_cmdbuf;
/* defines for accessing union fields */
/*
* Data area to read sensor data into
*/
static union {
} rmc_sensbuf;
/* defines for accessing union fields */
int index;
if (retval == 0) {
}
if (retval == 0) {
}
if (retval != 0)
/*
* Reserve space for special additional entries in the FRU section
*/
/*
* add special entry for RMC itself
*/
"SC");
/*
* populate any other FRU entries
*/
0;
}
if (retval == 0) {
}
if (retval == 0) {
}
}
if (retval == 0) {
}
if (retval == 0) {
}
}
if (retval == 0) {
}
if (retval == 0) {
}
}
if (retval == 0) {
}
if (retval == 0) {
}
}
if (retval == 0) {
}
if (retval == 0) {
}
}
/*
* The command DP_GET_ALARM_STATE may not be valid on
* some RMC versions, so we ignore the return value
* and proceed
*/
if (retval == 0) {
}
}
}
if (retval == 0) {
}
if (retval == 0) {
/*
* WARNING:
* =======
* The PSUs must be probed last so that the response data
* (psu_r) is available for make_psu_subsections() below.
* Note that all the responses share the same data area
* which is declared as a union.
*/
}
}
if (retval == 0) {
if (retval != 0) {
break;
}
}
}
/*
* now add nodes derived from PSUs
*/
if (retval == 0) {
/*
* name cache sections all set, exchange new for old
*/
} else {
/*
* RMC is not responding, ditch any existing cache
* and just leave the special SC FRU node
*/
}
}
static void
{
}
static void
{
}
static int
int detector_type)
{
int index;
sizeof (envmon_indicator_t), mode) != 0)
return (EFAULT);
/* ensure we've got PSU handles cached */
/* request for first handle */
else
} else {
/* ensure name is properly terminated */
else
}
if (sensor_status == ENVMON_SENSOR_OK) {
/*
* user correctly identified a sensor, note its
* handle value and request the indicator status
*/
}
}
/*
* copy results into buffer for user
*/
0 : 1;
}
sizeof (envmon_indicator_t), mode) != 0)
return (EFAULT);
return (0);
}
/*ARGSUSED*/
static int
int *rval_p)
{
union {
} env_buf;
union {
} rmc_reqbuf;
union {
} rmc_resbuf;
int retval = 0;
int special = 0;
int index;
if (instance != 0)
return (ENXIO);
switch (cmd) {
case ENVMONIOCSYSINFO:
/*
* (voltage, current, fan, temperature). So the maximum
* number of such indicators relates to the maximum number
* of power-supplies.
*/
if (rmclomv_sysinfo_valid) {
/*
* the ALOM-Solaris interface does not include
* amp sensors, so we can hard code this value
*/
lomv_sysinfo.maxAmpSens = 0;
} else {
}
sizeof (lomv_sysinfo), mode) != 0)
return (EFAULT);
break;
case ENVMONIOCVOLTSENSOR:
sizeof (envmon_sensor_t), mode) != 0)
return (EFAULT);
/* see if we've got volts handles cached */
if ((rmclomv_cache_valid == B_FALSE) ||
RMCLOMV_VOLT_SENS)) == NULL)) {
/* request for first handle */
if (section->num_entries == 0)
else
} else {
/* ensure name is properly terminated */
&index) != 0) {
else
}
if (sensor_status == ENVMON_SENSOR_OK) {
/*
* user correctly identified a sensor, note its
* handle value and request the sensor value
*/
}
(intptr_t)&rmc_volts_r) != 0)) {
}
if ((sensor_status == ENVMON_SENSOR_OK) &&
}
/*
* copy results into buffer for user
*/
}
sizeof (envmon_sensor_t), mode) != 0)
return (EFAULT);
break;
case ENVMONIOCVOLTIND:
case ENVMONIOCTEMPIND:
case ENVMONIOCFANIND:
case ENVMONIOCAMPSENSOR:
sizeof (envmon_sensor_t), mode) != 0)
return (EFAULT);
sizeof (envmon_sensor_t), mode) != 0)
return (EFAULT);
break;
case ENVMONIOCTEMPSENSOR:
sizeof (envmon_sensor_t), mode) != 0)
return (EFAULT);
/* see if we've got temperature handles cached */
if ((rmclomv_cache_valid == B_FALSE) ||
RMCLOMV_TEMP_SENS)) == NULL)) {
/* request for first handle */
if (section->num_entries == 0)
else
} else {
/* ensure name is properly terminated */
&index) != 0) {
else
}
if (sensor_status == ENVMON_SENSOR_OK) {
/*
* user correctly identified a sensor, note its
* handle value and request the sensor value
*/
}
(intptr_t)&rmc_temp_r) != 0)) {
}
if ((sensor_status == ENVMON_SENSOR_OK) &&
}
/*
* copy results into buffer for user
*/
}
sizeof (envmon_sensor_t), mode) != 0)
return (EFAULT);
break;
case ENVMONIOCFAN:
sizeof (envmon_fan_t), mode) != 0)
return (EFAULT);
/* see if we've got fan handles cached */
if ((rmclomv_cache_valid == B_FALSE) ||
RMCLOMV_FAN_SENS)) == NULL)) {
/* request for first handle */
if (section->num_entries == 0)
else
} else {
/* ensure name is properly terminated */
&index) != 0) {
else
}
if (sensor_status == ENVMON_SENSOR_OK) {
/*
* user correctly identified a sensor, note its
* handle value and request the sensor value
*/
}
}
if ((sensor_status == ENVMON_SENSOR_OK) &&
}
DP_FAN_PRESENCE) == 0)
/*
* copy results into buffer for user
*/
DP_FAN_SPEED_VAL_UNIT) == 0)
sizeof (str_rpm));
else
sizeof (str_percent));
}
}
sizeof (envmon_fan_t), mode) != 0)
return (EFAULT);
break;
case ENVMONIOCAMPIND:
sizeof (envmon_indicator_t), mode) != 0)
return (EFAULT);
/* see if we've got amp indicator handles cached */
if ((rmclomv_cache_valid == B_FALSE) ||
RMCLOMV_AMP_IND)) == NULL)) {
&rmc_psu_r, RMCLOMV_AMP_IND));
/* request for first handle */
if (section->num_entries == 0) {
}
} else {
/* ensure name is properly terminated */
&index) != 0) {
}
} else {
if ((sub_section == NULL) ||
(sub_section->num_entries == 0))
else
}
}
if (sensor_status == ENVMON_SENSOR_OK) {
/*
* user correctly identified an indicator, note its
* handle value and request the indicator status
*/
}
(intptr_t)&rmc_ampi_r) != 0)) {
}
if ((sensor_status == ENVMON_SENSOR_OK) &&
}
/*
* copy results into buffer for user
*/
}
/*
* If rmclomv_rmc_error is set there is no way
* that we read information from RSC. Just copy
* out an inaccessible evironmental.
*/
if (rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE) {
}
sizeof (envmon_indicator_t), mode) != 0)
return (EFAULT);
break;
case ENVMONIOCHPU:
sizeof (envmon_hpu_t), mode) != 0)
return (EFAULT);
/* see if we've got hpu handles cached */
if ((rmclomv_cache_valid == B_FALSE) ||
RMCLOMV_HPU_IND)) == NULL)) {
return (EAGAIN);
}
/*
* At this point the cache is locked and section points to
* the section relating to hpus.
*/
/* request for first handle */
if (section->num_entries == 0)
else
} else {
/* ensure name is properly terminated */
&index) != 0) {
else
}
if (sensor_status == ENVMON_SENSOR_OK) {
/*
* user correctly identified an hpu, note its
* handle value and request the hpu status
*/
}
if (special != 0) {
/* this is the pseudo SC node */
switch (rmclomv_rmc_state) {
case RMCLOMV_RMCSTATE_OK:
break;
case RMCLOMV_RMCSTATE_FAILED:
break;
break;
default:
break;
}
} else if (rmclomv_rmc_error ||
DP_GET_FRU_STATUS_R, sizeof (rmc_fru_r),
} else {
/*
* copy results into buffer for user
*/
!= DP_SENSOR_DATA_AVAILABLE) {
} else {
if (status == DP_FRU_STATUS_UNKNOWN) {
} else if (status != DP_FRU_STATUS_OK) {
}
}
}
}
/*
* If rmclomv_rmc_error is set there is no way
* that we read information from RSC. Just copy
* out an inaccessible environmental.
*/
if (rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE) {
}
sizeof (envmon_hpu_t), mode) != 0)
return (EFAULT);
break;
case ENVMONIOCGETLED:
sizeof (envmon_led_info_t), mode) != 0)
return (EFAULT);
/* see if we've got LED handles cached */
if ((rmclomv_cache_valid == B_FALSE) ||
RMCLOMV_LED_IND)) == NULL)) {
/* request for first handle */
if (section->num_entries == 0)
else
} else {
/* ensure name is properly terminated */
&index) != 0) {
else
}
if (sensor_status == ENVMON_SENSOR_OK) {
/*
* user correctly identified a LED, note its
* handle value and request the LED status
*/
}
}
if ((sensor_status == ENVMON_SENSOR_OK) &&
}
/*
* copy results into buffer for user
* start with some defaults then override
*/
else {
case (rsci8)DP_LED_OFF:
break;
break;
case (rsci8)DP_LED_BLINKING:
break;
case (rsci8)DP_LED_FLASHING:
break;
default:
break;
}
}
}
/*
* If rmclomv_rmc_error is set there is no way
* that we read information from RSC. Just copy
* out an inaccessible environmental.
*/
if (rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE) {
}
sizeof (envmon_led_info_t), mode) != 0)
return (EFAULT);
break;
case ENVMONIOCSETLED:
return (EACCES);
return (EPERM);
sizeof (envmon_led_ctl_t), mode) != 0)
return (EFAULT);
return (EINVAL);
/*
* Ensure name is properly terminated.
*/
/* see if we've got LED handles cached */
if ((rmclomv_cache_valid == B_FALSE) ||
RMCLOMV_LED_IND)) == NULL) ||
&index) != 0)) {
return (EINVAL); /* no such LED */
}
/*
* user correctly identified a LED, note its handle value
*/
switch (env_ledctl.led_state) {
case ENVMON_LED_ON:
break;
case ENVMON_LED_BLINKING:
break;
case ENVMON_LED_FLASHING:
break;
default:
break;
}
(intptr_t)&rmc_setled_r);
if (retval != 0) {
break;
}
if (rmc_setled_r.status != 0) {
return (EIO);
}
break;
case ENVMONIOCGETKEYSW:
{
/*
* Yes, I know this is ugly, but the V210 has no keyswitch,
* even though the ALOM returns a value for it
*/
return (ENOTSUP);
}
switch (rmc_pos) {
case RMC_KEYSWITCH_POS_NORMAL:
break;
case RMC_KEYSWITCH_POS_DIAG:
break;
case RMC_KEYSWITCH_POS_LOCKED:
break;
case RMC_KEYSWITCH_POS_OFF:
break;
default:
break;
}
sizeof (envmon_pos), mode) != 0)
return (EFAULT);
break;
}
case ENVMONIOCGETALARM:
sizeof (envmon_alarm_info_t), mode) != 0)
return (EFAULT);
/* see if we've got ALARM handles cached */
if ((rmclomv_cache_valid == B_FALSE) ||
RMCLOMV_ALARM_IND)) == NULL)) {
/* request for first handle */
if (section->num_entries == 0)
else
} else {
/* ensure name is properly terminated */
&index) != 0) {
else
}
if (sensor_status == ENVMON_SENSOR_OK) {
/*
* user correctly identified a ALARM, note its
* handle value and request the ALARM status
*/
}
if ((sensor_status == ENVMON_SENSOR_OK) &&
(intptr_t)&rmc_alarm_r) != 0)) {
}
/*
* copy results into buffer for user
* start with some defaults then override
*/
else {
switch (alarmState.state) {
case DP_ALARM_OFF:
break;
case DP_ALARM_ON:
break;
default:
break;
}
}
}
/*
* If rmclomv_rmc_error is set there is no way
* that we read information from RSC. Just copy
* out an inaccessible environmental.
*/
if (rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE) {
}
sizeof (envmon_alarm_info_t), mode) != 0)
return (EFAULT);
break;
case ENVMONIOCSETALARM:
return (EACCES);
return (EPERM);
sizeof (envmon_alarm_ctl_t), mode) != 0)
return (EFAULT);
return (EINVAL);
/*
* Ensure name is properly terminated.
*/
/* see if we've got ALARM handles cached */
if ((rmclomv_cache_valid == B_FALSE) ||
RMCLOMV_ALARM_IND)) == NULL) ||
&index) != 0)) {
return (EINVAL); /* no such ALARM */
}
/*
* user correctly identified a ALARM, note its handle value
*/
sizeof (rmc_setalarm_r),
if (retval != 0) {
break;
}
if (rmc_setalarm_r.status != 0) {
return (EIO);
}
break;
DP_GET_SDP_VERSION_R, sizeof (rmc_sdpver_r),
if (retval != 0) {
retval);
break;
break;
}
DP_GET_CHASSIS_SERIALNUM_R, sizeof (rmc_serialnum_r),
if (retval != 0) {
break;
}
sizeof (rmc_serialnum_r.chassis_serial_number));
return (EFAULT);
}
break;
default:
break;
}
return (retval);
}
/* ARGSUSED */
static void
{
int err;
int retries;
int state;
"rmclomv_checkrmc");
for (;;) {
/*
* Initial entry to this for loop is made with
* rmclomv_checkrmc_sig set to RMCLOMV_PROCESS_NOW. So the
* following while loop drops through the first time. A
* timeout call is made just before polling the RMC. Its
* interrupt routine sustains this loop by injecting additional
* state changes and cv events.
*/
/*
* Wait for someone to tell me to continue.
*/
while (rmclomv_checkrmc_sig == RMCLOMV_CHECKRMC_WAIT) {
}
/*
* mustn't hold same lock as timeout called with
* when cancelling timer
*/
if (timer_id != 0) {
timer_id = 0;
}
/* RMCLOMV_CHECKRMC_EXITNOW implies signal by _detach(). */
if (rmclomv_checkrmc_sig == RMCLOMV_CHECKRMC_EXITNOW) {
/* rmclomv_checkrmc_lock is held at this point! */
thread_exit();
/* NOTREACHED */
}
/*
* If the RMC is not responding, rmclomv_do_cmd() takes a
* long time and eventually times out. We conclude that the
* RMC is broken if it doesn't respond to a number of polls
* made 60 secs apart. So that the rmclomv_do_cmd() time-out
* period isn't added to our 60 second timer, make the
* timeout() call before calling rmclomv_do_cmd().
*/
if (timer_id == 0) {
}
if (err == 0) {
/* successful poll, reset fail count */
rmclomv_rmcfailcount = 0;
if (state != RMCLOMV_RMCSTATE_OK) {
}
}
if ((err != 0) &&
/*
* Failed response or no response from RMC.
* Count the failure.
* If threshold exceeded, send a DR event.
*/
if (retries == RMCLOMV_RMCFAILTHRESHOLD)
if (retries == RMCLOMV_RMCFAILTHRESHOLD) {
state == RMCLOMV_RMCSTATE_OK ?
"has stopped" : "is not");
}
}
/*
* Re-enter the lock to prepare for another iteration.
* We must have the lock here to protect rmclomv_checkrmc_sig.
*/
}
}
static void
rmclomv_checkrmc_start(void)
{
if (rmclomv_checkrmc_tid == 0) {
}
}
static void
rmclomv_checkrmc_destroy(void)
{
if (tid != 0) {
rmclomv_checkrmc_tid = 0;
}
/*
* Wait for rmclomv_checkrmc() to finish
*/
if (tid != 0)
}
/*ARGSUSED*/
static void
{
}
/* ARGSUSED */
static void
{
void (*plat_nodename_set_fun)(void);
int state;
int tmp_checkrmc_sig;
"rmclomv_refresh");
/*
* Wait until the rmclomv_checkrmc() thread has had a chance to
* run its main loop. This is done so that rmclomv_refresh will
* only run its main loop once at start of day; otherwise, it may
* run twice and generate warning messages when redundantly populating
* its internal cache.
*/
do {
} while (tmp_checkrmc_sig != RMCLOMV_CHECKRMC_WAIT);
for (;;) {
/*
* Wait for someone to tell me to continue.
*/
while (rmclomv_refresh_sig == RMCLOMV_REFRESH_WAIT) {
}
/* RMCLOMV_REFRESH_EXITNOW implies signal by _detach(). */
if (rmclomv_refresh_sig == RMCLOMV_REFRESH_EXITNOW) {
/* rmclomv_refresh_lock is held at this point! */
thread_exit();
/* NOTREACHED */
}
/*
* We're not going to access rmclomv_sysinfo_data here,
* so there's no point in locking it before reading
* rmclomv_sysinfo_valid. Also this avoids holding two
* locks at once and the concommitant worry about deadlocks.
*/
if (rmclomv_sysinfo_valid) {
/*
* We've just successfully read the RMC sysinfo
* so the RMC must be operational. Update its
* state and if it was previously not OK, refresh
* nodename, CPU signatures and watchdog settings.
*/
rmclomv_rmcfailcount = 0;
if (state != RMCLOMV_RMCSTATE_OK) {
if (state == RMCLOMV_RMCSTATE_FAILED) {
}
}
(void (*)(void))modgetsymvalue(
"plat_nodename_set", 0);
if (plat_nodename_set_fun != NULL)
}
"current_sgn", 0);
/*
* Delay before calling CPU_SIGNATURE, to allow
* any pending asynchronous communications (i.e.
* plat_timesync()) to complete. This helps to
* prevent the situation where the message associated
* with the CPU_SIGNATURE state cannot be sent to the
* system controller.
*/
if ((current_sgn_p != NULL) &&
/*
* Delay before calling
* send_watchdog_msg, to allow
* CPU_SIGNATURE() time to
* complete; this increases the
* chances of successfully sending
* the watchdog message to the
* system controller.
*/
}
}
}
/*
* update keyswitch value in case it changed while the
* RMC was out of action
*/
if (rmclomv_sysinfo_valid) {
if ((real_key_position != RMC_KEYSWITCH_POS_UNKNOWN) &&
} else {
/* treat unknown key position as locked */
}
} else {
/* treat unreadable key position as locked */
}
/*
* Re-enter the lock to prepare for another iteration.
* We must have the lock here to protect rmclomv_refresh_sig.
*/
}
}
static void
rmclomv_refresh_start(void)
{
if (rmclomv_refresh_tid == 0) {
}
}
static void
rmclomv_refresh_destroy(void)
{
if (tid != 0) {
rmclomv_refresh_tid = 0;
}
/*
* Wait for rmclomv_refresh() to finish
*/
if (tid != 0)
}
static void
rmclomv_refresh_wakeup(void)
{
}
static void
{
return;
RMC_COMM_DREQ_URGENT : 0);
}
/*ARGSUSED*/
static uint_t
{
if ((watchdog_enable == 0) || (watchdog_available == 0)) {
return (0);
}
/*
* If boothowto has RB_DEBUG set we never want to set the watchdog
* support on.
*/
return (0);
}
/*
* When the watchdog is shut off last_watchdog_msg goes from a
* 0 to a 1. So we must test to see that last_watchdog_msg is
* set to 1 indicating that watchdog was shut off and
* After which we set last_watchdog_msg back to 0 so that we do not
* run this code
* again.
*/
if (last_watchdog_msg == 1) {
last_watchdog_msg = 0;
}
watchdog_activated = 1;
return (1);
}
static uint_t
rmc_clear_watchdog_timer(void)
{
return (0);
last_watchdog_msg = 1;
watchdog_activated = 0;
return (0);
}
static void
{
int retval;
/* Is the system coming up? */
/* Request the time from the RMC clock. */
/*
* If we were able to get the time lets set the local clock.
* The time returned from RMC is in Unix time format.
*
* If we couldn't get the time we'll accept the drift so as not
* to cause congestion on the I2C bus or cause boot
* performance regressions.
*/
set_hrestime(&ts);
}
}
gethrestime(&now);
(void) rmc_comm_request_nowait(&request, 0);
if (timesync_interval != 0)
}
/*
*/
int
{
int index;
/* see if we've got ALARM handles cached */
if ((rmclomv_cache_valid == B_FALSE) ||
RMCLOMV_ALARM_IND)) == NULL)) {
}
if (sensor_status == ENVMON_SENSOR_OK) {
/*
* user correctly identified a ALARM, note its
* handle value and request the ALARM status
*/
index = alarm_type;
else
}
(intptr_t)&u_rmc_alarm_r) != 0)) {
}
if (sensor_status == ENVMON_SENSOR_OK) {
/*
* copy results into buffer for user
* start with some defaults then override
*/
*alarm_state = 0;
return (ENXIO);
else {
switch (alarmState.state) {
case DP_ALARM_OFF:
break;
case DP_ALARM_ON:
*alarm_state = 1;
break;
default:
break;
}
}
} else
return (ENXIO);
return (0);
}
int
{
int index;
/* see if we've got ALARM handles cached */
if ((rmclomv_cache_valid == B_FALSE) ||
RMCLOMV_ALARM_IND)) == NULL)) {
}
if (sensor_status == ENVMON_SENSOR_OK) {
/*
* user correctly identified a ALARM, note its
* handle value and request the ALARM status
*/
index = alarm_type;
else {
}
}
if ((sensor_status == ENVMON_SENSOR_OK) &&
(intptr_t)&u_rmc_setalarm_r) != 0)) {
}
return (EIO);
}
if (sensor_status != ENVMON_SENSOR_OK) {
return (ENXIO);
}
return (0);
}