/*
* 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.
*/
/*
* ENVCTRLTWO_ Environment Monitoring driver for i2c on Javelin
*
*/
#include <sys/eucioctl.h>
#include <io/envctrl_targets.c>
#include <sys/priv_names.h>
/* driver entry point fn definitions */
/* configuration entry point fn definitions */
/* Driver private routines */
#ifdef GET_CPU_TEMP
static int envctrl_get_cpu_temp(struct envctrlunit *, int);
#endif
static void envctrl_fan_fail_service(struct envctrlunit *);
static void envctrl_PS_intr_service(struct envctrlunit *);
static void envctrl_ps_probe(struct envctrlunit *);
static void envctrl_tempr_poll(void *);
static void envctrl_pshotplug_poll(void *);
static void envctrl_led_blink(void *);
static void envctrl_init_bus(struct envctrlunit *);
static void envctrl_reset_dflop(struct envctrlunit *);
static void envctrl_enable_devintrs(struct envctrlunit *);
static void envctrl_intr_latch_clr(struct envctrlunit *);
static void envctrl_abort_seq_handler(char *msg);
static int envctrl_set_dskled(struct envctrlunit *,
struct envctrl_chip *);
static int envctrl_get_dskled(struct envctrlunit *,
struct envctrl_chip *);
static int envctrl_set_fanspeed(struct envctrlunit *,
struct envctrl_chip *);
static void envctrl_probe_cpus(struct envctrlunit *);
static int envctrl_match_cpu(dev_info_t *, void *);
static int envctrl_isother_fault_led(struct envctrlunit *,
static int envctrl_check_sys_temperatures(struct envctrlunit *);
static void envctrl_check_disk_kstats(struct envctrlunit *);
static void envctrl_update_disk_kstats(struct envctrlunit *,
static int envctrl_read_chip(struct envctrlunit *, int, int, int,
uint8_t *, int);
static int envctrl_write_chip(struct envctrlunit *, int, int, int,
uint8_t *, int);
static int envctrl_check_tempr_levels(struct envctrlunit *,
int, uint8_t *, int);
static void envctrl_update_fanspeed(struct envctrlunit *);
/* Kstat routines */
static void envctrl_add_kstats(struct envctrlunit *);
static int envctrl_ps_kstat_update(kstat_t *, int);
static int envctrl_fanstat_kstat_update(kstat_t *, int);
static int envctrl_encl_kstat_update(kstat_t *, int);
static int envctrl_temp_kstat_update(kstat_t *, int);
static int envctrl_disk_kstat_update(kstat_t *, int);
static void envctrl_init_encl_kstats(struct envctrlunit *);
extern void power_down(const char *);
extern int prom_getprop();
extern int prom_getproplen();
extern void prom_printf(const char *fmt, ...);
extern void (*abort_seq_handler)();
static void *envctrlsoft_statep;
static int power_flt_led_lit = 0;
/* Local Variables */
/* Indicates whether or not the overtemp thread has been started */
static int envctrl_debug_flags = 0;
static int envctrl_power_off_overide = 0;
static int envctrl_allow_detach = 0;
/*
* Temperature levels :
* green = OK - no action needed
* yellow = warning - display warning message and poll faster
* red = critical - shutdown system
*/
envctrl_open, /* cb_open */
envctrl_close, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
envctrl_ioctl, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
nochpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
NULL, /* cb_stream */
};
/*
* Declare ops vectors for auto configuration.
*/
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
envctrl_getinfo, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
envctrl_attach, /* devo_attach */
envctrl_detach, /* devo_detach */
nodev, /* devo_reset */
&envctrl_cb_ops, /* devo_cb_ops */
nulldev, /* devo_power */
ddi_quiesce_not_supported, /* devo_quiesce */
};
extern struct mod_ops mod_driverops;
&mod_driverops, /* type of module - driver */
"I2C ENVCTRLTWO_driver",
};
0
};
int
_init(void)
{
register int error;
(void) ddi_soft_state_init(&envctrlsoft_statep,
sizeof (struct envctrlunit), 1);
}
return (error);
}
int
_fini(void)
{
register int error;
return (error);
}
int
{
}
static int
{
register int instance;
int i, j, k, status;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_FAILURE);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/* Set up timer values */
/*
* On a cooling failure, either a fan failure or temperature
* exceeding a WARNING level, the temperature poll thread
* will run every 6 seconds.
*/
goto failed;
}
sizeof (struct ehc_pcd8584_regs), &attr,
return (DDI_FAILURE);
}
/*
* If the PCI nexus has added a thermal interrupt, we first need
* to remove that interrupt handler.
*
* WARNING: Removing another driver's interrupt handler is not
* allowed. The pci_thermal_rem_intr() call below is needed to retain
* the legacy behavior on Javelin systems.
*/
/* add interrupts */
goto failed;
}
(void *)unitp->ic_trap_cookie);
goto remlock;
}
goto remhardintr;
}
DDI_FAILURE) {
goto remhardintr1;
}
/*
* Javelin will not have a workstation configuration so activity
* LED will always blink.
*/
unitp->num_ps_present = 0;
if (envctrl_numcpus > 1) {
}
/* Only one CPU in the system */
else
"%s%d: Unable to read cpu-temp-factors property",
return (DDI_NOT_WELL_FORMED);
}
for (i = 0; i < len; i++) {
}
}
ddi_prop_free((void *)creg_prop);
"%s%d: Unable to read cpu-fan-speeds property",
return (DDI_NOT_WELL_FORMED);
}
for (i = 0; i < len; i++) {
}
}
ddi_prop_free((void *)creg_prop);
"%s%d: Unable to read thermisters property",
return (DDI_NOT_WELL_FORMED);
}
j = 0; k = 0;
for (i = 0; i < JAV_MAX_TEMP_SENSORS; i++) {
/* Type */
/* Address */
j += 4;
/* Port */
j += 4;
/* Min */
j += 4;
/* Warning threshold */
j += 4;
/* Shutdown threshold */
j += 4;
/* Numerator of scale factor */
j += 4;
/* Denominator of scale factor */
j += 4;
while (creg_prop[j] != '\0') j++;
j++;
if (t_addr[k] == ENVCTRL_UE250_CPU_TEMP_DEV) {
if (((t_port[k] == ENVCTRL_UE250_CPU0_PORT) &&
B_FALSE)) ||
((t_port[k] == ENVCTRL_UE250_CPU1_PORT) &&
/* Don't increment the kstat line count */
#ifdef lint
k = k;
#else
;
#endif
else
k++;
} else
k++;
}
ddi_prop_free((void *)creg_prop);
/* initialize the envctrl bus controller */
DPRINTF1("envctrl_attach(): Completed initialization of PCF8584");
drv_usecwait(1000);
unitp->timeout_id = 0;
unitp->blink_timeout_id = 0;
unitp->fan_failed = 0;
unitp->tempr_warning = 0;
/*
* Fans could be blasting, turn them down.
*/
fanspeed = 0x0;
&fanspeed, 1);
if (status == DDI_FAILURE)
/*
* we need to init the fan kstats before the tempr_poll
*/
envctrl_led_blink((void *)unitp);
} else {
}
envctrl_tempr_poll((void *)unitp);
/*
* interpose envctrl's abort sequence handler
*/
if (envctrl_handler) {
}
return (DDI_SUCCESS);
if (unitp->ctlr_handle)
return (DDI_FAILURE);
}
static int
{
int instance;
switch (cmd) {
case DDI_DETACH:
if (envctrl_allow_detach) {
}
}
}
}
}
if (unitp->timeout_id != 0) {
unitp->timeout_id = 0;
}
if (unitp->blink_timeout_id != 0) {
unitp->blink_timeout_id = 0;
}
return (DDI_SUCCESS);
} else {
return (DDI_FAILURE);
}
case DDI_SUSPEND:
return (DDI_FAILURE);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
int
void **result)
{
#ifdef lint
#endif
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
if ((unitp = (struct envctrlunit *)
ret = DDI_SUCCESS;
} else {
ret = DDI_FAILURE;
}
break;
case DDI_INFO_DEVT2INSTANCE:
ret = DDI_SUCCESS;
break;
default:
ret = DDI_FAILURE;
break;
}
return (ret);
}
/* ARGSUSED1 */
static int
{
int status = 0;
register int instance;
if (instance < 0)
return (ENXIO);
unitp = (struct envctrlunit *)
return (ENXIO);
return (EINVAL);
return (EBUSY);
} else {
}
}
return (status);
}
/*ARGSUSED1*/
static int
{
register int instance;
if (instance < 0)
return (ENXIO);
unitp = (struct envctrlunit *)
return (ENXIO);
return (DDI_SUCCESS);
}
/*
* standard put procedure for envctrl
*/
static int
int *rvalp)
{
register int instance;
#ifdef lint
#endif
unitp = (struct envctrlunit *)
if ((cmd == ENVCTRL_IOC_SETFAN2) ||
(cmd == ENVCTRL_IOC_GETFAN2) ||
(cmd == ENVCTRL_IOC_SETMODE) ||
(cmd == ENVCTRL_IOC_GETMODE) ||
(cmd == ENVCTRL_IOC_GETTEMP2) ||
(cmd == ENVCTRL_IOC_SETFSP2) ||
(cmd == ENVCTRL_IOC_GETFSP2) ||
(cmd == ENVCTRL_IOC_RESETTMPR) ||
(cmd == ENVCTRL_IOC_SETDSKLED2) ||
(cmd == ENVCTRL_IOC_GETDSKLED2))
return (EFAULT);
switch (cmd) {
case ENVCTRL_IOC_SETMODE:
/* Set mode */
flag)) {
break;
}
if (wdval == ENVCTRL_DIAG_MODE ||
wdval == ENVCTRL_NORMAL_MODE) {
if (unitp->timeout_id != 0 &&
wdval == ENVCTRL_DIAG_MODE) {
unitp->timeout_id =
}
if (wdval == ENVCTRL_NORMAL_MODE) {
/*
* Fans could be blasting, turn them down.
*/
tempr = 0x0;
ENVCTRL_PCF8591, EHC_DEV2, 0,
&tempr, 1);
if (status == DDI_FAILURE)
"%s%d: Write to PCF8591 "
"(SETMODE) failed\n",
/*
* This delay allows the fans to time to
* change speed
*/
drv_usecwait(100000);
(void) envctrl_check_sys_temperatures(unitp);
}
} else {
}
break;
case ENVCTRL_IOC_GETMODE:
}
break;
case ENVCTRL_IOC_RESETTMPR:
/*
* For diags, cancel the curent temp poll
* and reset it for a new one.
*/
if (unitp->timeout_id != 0) {
unitp->timeout_id = 0;
}
envctrl_tempr_poll((void *)unitp);
} else {
}
break;
case ENVCTRL_IOC_GETTEMP2:
/* Get the user buffer address */
sizeof (struct envctrl_chip), flag)) {
break;
}
break;
}
if (status == DDI_FAILURE) {
"%s%d: Read from PCF8591 (IOC_GETTEMP) failed",
break;
}
sizeof (struct envctrl_chip), flag)) {
}
break;
case ENVCTRL_IOC_SETTEMP:
break;
case ENVCTRL_IOC_SETWDT:
break;
case ENVCTRL_IOC_SETFAN2:
/* NOTE: need to sanity check values coming from userland */
sizeof (struct envctrl_chip), flag)) {
break;
}
break;
}
if (status == DDI_FAILURE) {
"%s%d: Write to PCF8591 "
"(IOC_SETFAN) failed",
}
} else {
}
break;
case ENVCTRL_IOC_GETFAN2:
sizeof (struct envctrl_chip), flag)) {
break;
}
break;
}
if (status == DDI_FAILURE) {
"%s%d: Read of PCF8591 (IOC_GETFAN) failed",
break;
}
/*
* Due to hardware limitation, the actual fan speed
* is always a little less than what it was set to by
* software. Hence, we scale up the read fan speed value
* to more closely match the set value.
*/
JAV_FAN_SPEED_SF_DEN) > 255)
else
sizeof (struct envctrl_chip), flag)) {
}
break;
case ENVCTRL_IOC_SETFSP2:
sizeof (struct envctrl_chip), flag)) {
break;
}
break;
}
/*
* If a user is in normal mode and they try
* to set anything other than a disk fault or
* a gen fault it is an invalid operation.
* in diag mode we allow everything to be
* twiddled.
*/
if (wdval & ~ENVCTRL_UE250_FSP_USRMASK) {
break;
}
}
if (wdval & ENVCTRL_UE250_FSP_PS_ERR)
power_flt_led_lit = 1;
if (status == DDI_FAILURE) {
"%s%d: Read of PCF8574A (IOC_SETFSP) failed",
}
break;
case ENVCTRL_IOC_GETFSP2:
sizeof (struct envctrl_chip), flag)) {
break;
}
break;
}
if (status == DDI_FAILURE) {
"%s%d: Read of PCF8574A (IOC_GETFSP) failed",
} else {
sizeof (struct envctrl_chip), flag)) {
}
}
break;
case ENVCTRL_IOC_SETDSKLED2:
sizeof (struct envctrl_chip), flag)) {
break;
}
break;
}
}
break;
case ENVCTRL_IOC_GETDSKLED2:
sizeof (struct envctrl_chip), flag)) {
break;
}
break;
}
} else {
sizeof (struct envctrl_chip), flag)) {
}
}
break;
case ENVCTRL_IOC_SETRAW:
break;
}
sizeof (struct envctrl_chip), flag)) {
break;
}
if (status == DDI_FAILURE) {
"%s%d: Write to chip (IOC_SETRAW) failed",
}
break;
case ENVCTRL_IOC_GETRAW:
sizeof (struct envctrl_chip), flag)) {
break;
}
if (status == DDI_FAILURE) {
"%s%d: Read of chip (IOC_GETRAW) failed",
}
sizeof (struct envctrl_chip), flag)) {
}
break;
default:
}
return (rval);
}
{
/*
* NOT USED
*/
return (ic);
}
{
int ic;
int retrys = 0;
int status;
static int spurious_intr_count = 0;
/*
* First check to see if it is an interrupt for us by
* looking at the "ganged" interrupt and vector
* according to the major type
* 0x70 is the addr of the ganged interrupt controller.
* Address map for the port byte read is as follows
* MSB
* -------------------------
* | | | | | | | | |
* -------------------------
* P7 P6 P5 P4 P3 P2 P1 P0
* P0 = Spare
* P1 = Thermal Interrupt
* P2 = Disk Interrupt
* P3 = Interrupt clock enable
* P4 = Fan Fail Interrupt
* P5 = Front Panel Interrupt
* P6 = Power Supply Interrupt
* P7 = Enable Interrupts
*/
do {
&recv_data, 1);
/*
* This extra read is needed since the first read is discarded
* and the second read seems to return 0xFF.
*/
if (recv_data == 0xFF) {
&recv_data, 1);
}
/*
* if the i2c bus is hung it is imperative that this
* be cleared on an interrupt or else it will
* hang the system with continuous interrupts
*/
if (status == DDI_FAILURE) {
drv_usecwait(1000);
if (retrys < envctrl_max_retries) {
retrys++;
} else {
"%s%d: Read of PCF8574A (INT) failed\n",
return (ic);
}
}
} while (status != DDI_SUCCESS);
if (!(recv_data & EHC_PCF8574_PORT0)) {
}
if (!(recv_data & EHC_PCF8574_PORT1)) {
DPRINTF1("Temperature interrupt detected\n");
(void) envctrl_check_sys_temperatures(unitp);
/*
* Clear the interrupt latches
*/
}
if (!(recv_data & EHC_PCF8574_PORT2)) {
DPRINTF1("Disk interrupt detected\n");
}
if (!(recv_data & EHC_PCF8574_PORT3)) {
}
if (!(recv_data & EHC_PCF8574_PORT4)) {
/*
* Check for a fan fail
*/
DPRINTF1("Fan interrupt detected\n");
/*
* Clear the interrupt latches
*/
}
if (!(recv_data & EHC_PCF8574_PORT5)) {
DPRINTF1("Keyswitch interrupt detected\n");
}
if (!(recv_data & EHC_PCF8574_PORT6)) {
DPRINTF1("Power supply interrupt detected\n");
}
if (!(recv_data & EHC_PCF8574_PORT7)) {
}
/*
* The interrupt routine got called but the interrupt chip
* shows no interrupt present. If this happens more than 256
* times in a row, there is probably some hardware problem so
* send a warning message to the console.
*/
if ((recv_data == 0xFF)) {
if (spurious_intr_count == 255)
"%s%d: Received 256 spurious interrupts\n",
} else
spurious_intr_count = 0;
return (ic);
}
static int
{
int status;
/*
* If more than one read is requested, set auto-increment bit
*/
if (num > 1)
autoincr = 1;
do {
if (type == ENVCTRL_PCF8574A) {
} else if (type == ENVCTRL_PCF8574) {
} else if (type == ENVCTRL_PCF8591) {
}
/*
* If the bus hangs, attempt a recovery
*/
if (status == DDI_FAILURE) {
drv_usecwait(1000);
if (retrys < envctrl_max_retries) {
retrys++;
} else {
break;
}
}
} while (status != DDI_SUCCESS);
return (status);
}
static int
{
int status;
/*
* Incase some applications mistakenly include the chips base addr
*/
/*
* If more than one write is requested, set auto-increment bit
*/
if (num > 1)
autoincr = 1;
do {
if (type == ENVCTRL_PCF8574A) {
(struct ehc_envcunit *)unitp,
} else if (type == ENVCTRL_PCF8574) {
} else if (type == ENVCTRL_PCF8591) {
}
/*
* If the bus hangs, attempt a recovery
*/
if (status == DDI_FAILURE) {
drv_usecwait(1000);
if (retrys < envctrl_max_retries) {
retrys++;
} else {
break;
}
}
} while (status != DDI_SUCCESS);
return (status);
}
#ifdef GET_CPU_TEMP
static int
{
int status;
/*
* This routine takes in the number of the port that
* we want to read in the 8591. This should be the
* location of the CPU thermistor for one of the 2
* cpu's. It will return a normalized value
* to the caller.
*/
&recv_data, 1);
if (status == DDI_FAILURE) {
return (ENVCTRL_UE250_MAX_CPU_TEMP - 10);
}
return (_cpu_temps[recv_data]);
}
#endif
static void
{
(void) power_down("Fatal System Environmental Control Error");
}
/*
* Clear the interrupt latches
*/
/*
* if we are in diag mode and the temp poll thread goes off,
* this means that the system is too heavily loaded and the 60 second
* window to execute the test is failing.
*/
diag_flag++;
if (envctrl_debug_flags) {
"Tempr poll went off while in DIAG MODE\n",
}
}
DPRINTF1("envctrl_tempr_poll(): Checking system temps\n");
if (status == DDI_FAILURE) {
"%s%d: Failure detected during temperature poll",
}
if (diag_flag == 0) {
}
/* Turn of the power fault LED if ps_ok is asserted */
/* now have this thread sleep for a while */
/*
* A thermal warning or fan failure condition exists.
* Temperature poll thread will run every 10 seconds.
*/
if (unitp->timeout_id != 0)
} else {
/*
* No thermal warning or fan failure condition exists.
* This thread is set to run every 60 seconds.
*/
}
}
static void
{
int status;
0, &val, 1);
if (status == DDI_FAILURE) {
/* now have this thread sleep for a while */
return;
}
/*
* Now we need to "or" in fault bits of the FSP
* module for the mass storage fault led.
* and set it.
*/
} else {
}
/*
* A static global variable, power_flt_led_lit, is used to keep
* track of periods when the software has lit the power fault LED.
* Whenever the power fault LED is lit and this variable is not set,
* then the power fault LED has been lit by hardware. In this case
* mask out the power fault LED in the byte. This is a fix for
* bug 4144872.
*/
if (tmpval & ENVCTRL_UE250_FSP_PS_ERR) {
if (power_flt_led_lit == 0) {
/*
* Turn off power fault bit in the FSP byte.
*/
tmpval &= ~(ENVCTRL_UE250_FSP_PS_ERR);
}
}
0, &val, 1);
if (status == DDI_FAILURE) {
/* now have this thread sleep for a while */
return;
}
/* now have this thread sleep for a while */
}
static int
{
0, buf, 4);
if (status == DDI_FAILURE) {
return (status);
}
buf, warning_count);
if (warning_level != green) {
if (warning_count == 0) {
drv_usecwait(1000);
goto retrytemp1;
}
if (warning_level == yellow)
else if (warning_level == red) {
}
}
warning_count = 0;
if (status == DDI_FAILURE) {
return (status);
}
if (warning_level != green) {
if (warning_count == 0) {
drv_usecwait(1000);
goto retrytemp2;
}
else if (warning_level == red) {
}
/*
* Current tempr. poll shows all levels normal.
* If the previous poll showed warning levels, we need
* to clear that status
*/
"TEMPERATURE NORMAL: all sensors back to normal readings");
}
if (status == DDI_FAILURE) {
"%s%d: Read of Front Status Panel LEDs failed",
}
else {
fspval &= ~(ENVCTRL_UE250_FSP_TEMP_ERR);
} else {
fspval &= ~(ENVCTRL_UE250_FSP_TEMP_ERR |
}
}
if (status == DDI_FAILURE) {
"%s%d: Setting of Front Status Panel LEDs failed",
}
/*
* Have this thread run again in about 10 seconds
*/
if (unitp->timeout_id != 0) {
}
}
return (status);
}
static int
{
int i, j;
int status;
int tval;
for (i = 0; i < 4; i++) {
if (i == 1) {
if (tval > 255)
else
continue;
} else if (i == 2)
continue;
}
(i == ENVCTRL_UE250_CPU1_PORT)))
continue;
j = 0;
(j < unitp->num_temps_present))
j++;
(i == ENVCTRL_UE250_CPU1_PORT)))
else
t_scale_den[j];
/*
* Javelin hardware will not control fan speeds based on
* cpu temperature values because the voltages corresponding
* to the cpu temperatures are based on an inverted scale
* compared to the ambient temperatures and thus can be
* fed to the same fan control circuit. As a result, it
* has been decided that software will control fan speed
* if cpu temperatures rise.
*/
(i == ENVCTRL_UE250_CPU1_PORT)) &&
fanspeed =
else
fanspeed =
if (status == DDI_FAILURE)
"%s%d: Write to PCF8591 (SETFAN) failed\n",
if (status == DDI_FAILURE)
"%s%d: Fan speed read failed (PDB)",
if (tval > 255)
else
}
if ((temp_degree_c >=
if (warning_level < yellow)
if (count != 0)
"TEMPERATURE WARNING: %d degrees "
"celsius at location %s",
}
if (temp_degree_c >=
if (warning_level < red)
warning_level = red;
if (count != 0) {
"TEMPERATURE CRITICAL: %d "
"degrees celsius at location %s",
"System shutdown in "
"10 seconds ...");
}
}
}
return (warning_level);
}
static void
{
int tval;
int status;
0, buf, 4);
if (status == DDI_FAILURE) {
}
if (tval > 255)
else
}
/* called with mutex held */
static void
{
int retrys = 0;
int status;
/*
* The fan fail interrupt is read from address 0x70
* on the envctrl bus.
*/
/*
* Clear the interrupt latches to handle spurious interrupts
*/
do {
&recv_data, 1);
/*
* This extra read is needed since the first read is discarded
* and the second read seems to return 0xFF.
*/
if (recv_data == 0xFF) {
&recv_data, 1);
}
if (status == DDI_FAILURE) {
drv_usecwait(1000);
if (retrys < envctrl_max_retries) {
retrys++;
} else {
"%s%d: Read of PCF8574A (INTFAN) failed",
return;
}
}
} while (status != DDI_SUCCESS);
/* If the fan fail interrupt is now absent */
if (recv_data & EHC_PCF8574_PORT4) {
"Fan failure has been cleared\n");
/*
* Clear general fault LED if no other faults
*/
if (status == DDI_FAILURE) {
"%s%d: Read of Front Status "
"Panel LEDs failed",
}
fpmstat &= ~(ENVCTRL_UE250_FSP_GEN_ERR);
}
if (status == DDI_FAILURE) {
"Setting of Front Status "
"Panel LEDs failed",
}
}
/*
* This should be set after envctrl_isother_fault_led()
* is called
*/
}
} else {
"Fan failure has been detected");
/*
* Set general fault LED
*/
if (status == DDI_FAILURE) {
"%s%d: Read of Front Status "
"Panel LEDs failed",
return;
}
if (status == DDI_FAILURE) {
"Setting of Front Status Panel LEDs failed",
}
/*
* A fan failure condition exists.
* Temperature poll thread should run every 10 seconds.
*/
if (unitp->timeout_id != 0) {
unitp->timeout_id =
}
}
}
}
/*
* Check for power supply insertion and failure.
* This is a bit tricky, because a power supply insertion will
* cause the ps_ok line to go active as well as PS present in the
* new supply. If we detect an insertion clear
* interrupts, disable interrupts, wait for a couple of seconds
* come back and see if the PSOK bit is set, PS_PRESENT is set
* and the share fail interrupts are gone. If not this is a
* real load share fail event.
* Called with mutex held
*/
static void
{
return;
}
/*
* setup a timeout thread to poll the ps after a
* couple of seconds. This allows for the PS to settle
* and doesn't report false errors on a hotplug
*/
}
static void
{
/*
* Clear the interrupt latches
*/
}
/* called with mutex held */
static void
{
int status;
0, &value, 1);
if (status == DDI_FAILURE) {
}
0, &value, 1);
if (status == DDI_FAILURE) {
}
}
/* called with mutex held */
static void
{
int status;
0, &value, 1);
if (status == DDI_FAILURE) {
}
0, &value, 1);
if (status == DDI_FAILURE) {
}
}
static void
{
int status;
0, &value, 1);
if (status == DDI_FAILURE) {
}
0, &value, 1);
if (status == DDI_FAILURE) {
}
}
/* Called with unitp mutex held */
static void
{
int i, j;
int status;
unitp->num_ps_present = 0;
0, &recv_data, 1);
if (status == DDI_FAILURE) {
return;
}
for (i = 0, j = 0; i < ENVCTRL_UE250_MAXPS; i++) {
/*
* Port 0 = PS0 Present
* Port 1 = PS1 Present
* Port 2 = SPARE
* Port 3 = SPARE
* Port 4 = PS0 OK
* Port 5 = PS1 OK
* Port 6 = SPARE
* Port 7 = SPARE
*/
/*
* Port 0 = PS Present
* Port is pulled LOW "0" to indicate
* present.
*/
switch (i) {
case 0:
break;
case 1:
break;
}
if (!(recv_data & ps_present_port)) {
/* update unit kstat array */
++unitp->num_ps_present;
if (pspr[i] == 0) {
"Power Supply %d inserted\n", i);
}
pspr[i] = 1;
if (!(recv_data & power_ok_port)) {
"Power Supply %d NOT okay\n", i);
ps_error++;
psok[i] = 0;
} else {
if (psok[i] == 0)
"Power Supply %d okay\n", i);
psok[i] = 1;
}
if (!(recv_data & EHC_PCF8574_PORT2)) {
"PS %d Shouln't interrupt\n", i);
ps_error++;
}
if (!(recv_data & EHC_PCF8574_PORT3)) {
"PS %d Shouln't interrupt\n", i);
ps_error++;
}
if (!(recv_data & EHC_PCF8574_PORT6)) {
"PS %d Shouln't interrupt\n", i);
ps_error++;
}
if (!(recv_data & EHC_PCF8574_PORT7)) {
"PS %d Shouln't interrupt\n", i);
ps_error++;
}
j++;
} else {
if (pspr[i] == 1) {
"Power Supply %d removed\n", i);
}
pspr[i] = 0;
}
}
if (status == DDI_FAILURE) {
}
if (ps_error) {
} else {
fpmstat &= ~(ENVCTRL_UE250_FSP_PS_ERR);
} else {
fpmstat &= ~(ENVCTRL_UE250_FSP_PS_ERR |
}
}
if (status == DDI_FAILURE) {
"%s%d: Setting of Front Status Panel LEDs failed",
}
if (ps_error) {
power_flt_led_lit = 1;
} else {
power_flt_led_lit = 0;
}
}
/*
* consider key switch position when handling an abort sequence
*/
static void
{
int i;
/*
* Find the instance of the device available on this host.
* Note that there may be only one, but the instance may
* not be zero.
*/
for (i = 0; i < MAX_DEVS; i++) {
if (unitp = (struct envctrlunit *)
break;
}
if ((secure & ENVCTRL_UE250_FSP_KEYMASK) ==
"%s%d: ignoring debug enter sequence\n",
} else {
if (envctrl_debug_flags) {
}
}
}
/*
* get the front Panel module LED and keyswitch status.
* this part is addressed at 0x7C on the i2c bus.
* called with mutex held
*/
static int
{
int status;
0, &recv_data, 1);
if (status == DDI_FAILURE) {
return (status);
}
/* Update kstats */
return (status);
}
static int
{
/*
* strip off bits that are R/O
*/
(*val));
while (confirm_count < confirm_max) {
0, &value, 1);
if (status == DDI_FAILURE) {
break;
} else {
/*
* Sometimes the i2c hardware status is not
* completely dependable as far as reporting
* a condition where the set does not take
* place. So we read back the set value to
* confirm what we set.
*/
if (status == DDI_FAILURE) {
"%s%d: Read of PCF8574A (FSP) failed",
break;
} else if (confirm_val != confirm_val_hold) {
drv_usecwait(1000);
continue;
} else
/*
* Set was confirmed.
*/
break;
}
}
if (confirm_count == confirm_max)
return (status);
}
static int
{
int status;
return (DDI_FAILURE);
}
if (status == DDI_FAILURE) {
}
return (status);
}
static int
{
int status;
/*
* We need to check the type of disk led being set. If it
* is a 4 slot backplane then the upper 4 bits (7, 6, 5, 4) are
* invalid.
*/
return (DDI_FAILURE);
return (DDI_FAILURE);
0, &val, 1);
if (status == DDI_FAILURE) {
return (status);
}
val &= ~(ENVCTRL_UE250_FSP_DISK_ERR);
} else {
val &= ~(ENVCTRL_UE250_FSP_DISK_ERR |
}
} else {
}
if (status == DDI_FAILURE) {
return (status);
}
0, &val, 1);
if (status == DDI_FAILURE) {
return (status);
}
/*
* we take the ones compliment of the val passed in
* because the hardware thinks that a "low" or "0"
* is the way to indicate a fault. of course software
* knows that a 1 is a TRUE state or fault. ;-)
*/
while (confirm_count < confirm_max) {
if (status == DDI_FAILURE) {
return (status);
} else {
/*
* Sometimes the i2c hardware status is not
* completely dependable as far as reporting
* a condition where the set does not take
* place. So we read back the set value to
* confirm what we set.
*/
if (status != DDI_SUCCESS) {
return (status);
drv_usecwait(1000);
continue;
} else
/*
* Set was confirmed.
*/
break;
}
}
if (confirm_count == confirm_max)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* After setting the fan speed, we read back the fan speed to confirm
* that the new value is within an acceptable range, else we retry.
* We do not confirm the fan speed if the set value is below the
* hardware determined speed (based on system temeratures).
*/
static int
{
int status;
while (confirm_count < confirm_max) {
if (status == DDI_FAILURE) {
"%s%d: Set fanspeed failed", driver_name,
return (status);
} else {
drv_usecwait(100000);
if (fanspeed_hold > idle_fanspeed) {
FAN_DRIFT) ||
(readback_speed > max_speed)) {
drv_usecwait(1000);
continue;
}
}
break;
}
}
if (confirm_count == confirm_max)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
static void
{
sizeof (unitp->encl_kstats),
KSTAT_FLAG_PERSISTENT)) == NULL) {
return;
}
sizeof (unitp->fan_kstats),
return;
}
KSTAT_FLAG_PERSISTENT)) == NULL) {
return;
}
sizeof (unitp->temp_kstats),
KSTAT_FLAG_PERSISTENT)) == NULL) {
return;
}
sizeof (unitp->disk_kstats),
KSTAT_FLAG_PERSISTENT)) == NULL) {
return;
}
}
static int
{
char *kstatp;
if (rw == KSTAT_WRITE) {
return (EACCES);
} else {
}
return (DDI_SUCCESS);
}
static int
{
char *kstatp;
if (rw == KSTAT_WRITE) {
return (EACCES);
} else {
sizeof (unitp->fan_kstats));
}
return (DDI_SUCCESS);
}
static int
{
char *kstatp;
int status;
if (rw == KSTAT_WRITE) {
return (EACCES);
} else {
if (status == DDI_SUCCESS)
sizeof (unitp->encl_kstats));
}
return (DDI_SUCCESS);
}
static int
{
char *kstatp;
if (rw == KSTAT_WRITE) {
return (EACCES);
} else {
sizeof (unitp->temp_kstats));
}
return (DDI_SUCCESS);
}
static int
{
char *kstatp;
if (rw == KSTAT_WRITE) {
return (EACCES);
} else {
sizeof (unitp->disk_kstats));
}
return (DDI_SUCCESS);
}
static void
{
int status;
0, &val, 1);
if (status == DDI_FAILURE) {
return;
}
}
static void
{
int status;
0, &diskpr, 1);
if (status == DDI_FAILURE) {
}
0, &diskfl, 1);
if (status == DDI_FAILURE) {
}
}
static void
{
int i, j, count = 0;
if (!(diskpr & j)) {
if (!(diskfl & j))
else
count++;
}
}
}
static void
{
int instance;
/*
* The cpu search is as follows:
* If there is only 1 CPU module it is named as
* SUNW,UltraSPARC. If this is a match we still don't
* know what slot the cpu module is in therefore
* we need to check the "upa-portid" property.
* If we have more than 1 cpu, then they are appended by
* instance numbers and slot locations. e.g.
* SUNW,UltraSPARC@1,0 (slot 1). it would have been
* nice to have the naming consistent for one CPU e.g.
* SUNW,UltraSPARC@0,0...sigh
*/
}
}
static int
{
int cpu_slot;
DDI_PROP_DONTPASS, "upa-portid",
-1)) == -1) {
} else {
}
}
return (DDI_WALK_CONTINUE);
}
/*
* This routine returns TRUE if some other error condition
* has set the GEN_ERR FAULT LED. Tp further complicate this
* LED panel we have overloaded the GEN_ERR LED to indicate
* that a fan fault has occurred without having a fan fault
* LED as does all other error conditions. So we just take the
* software state and return true. The whole purpose of this functon
* is to tell us wehther or not we can shut off the GEN_FAULT LED.
* NOTE: this ledval is usually one of the following FSP vals
* EXCEPT in the case of the fan fail.. we pass in a "0".
*/
static int
{
if (fspval != 0) {
}
} else if (fspval & ENVCTRL_UE250_FSP_DISK_ERR) {
} else if (fspval & ENVCTRL_UE250_FSP_PS_ERR) {
} else if (fspval & ENVCTRL_UE250_FSP_TEMP_ERR) {
}
return (status);
}
static void
{
}