/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


/*
 * ENVCTRLTWO_ Environment Monitoring driver for i2c on Javelin
 *
 */
#include <sys/param.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/termio.h>
#include <sys/termios.h>
#include <sys/cmn_err.h>
#include <sys/stream.h>
#include <sys/strsun.h>
#include <sys/stropts.h>
#include <sys/strtty.h>
#include <sys/debug.h>
#include <sys/eucioctl.h>
#include <sys/cred.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <sys/kmem.h>

#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/obpdefs.h>
#include <sys/conf.h>		/* req. by dev_ops flags MTSAFE etc. */
#include <sys/modctl.h>		/* for modldrv */
#include <sys/stat.h>		/* ddi_create_minor_node S_IFCHR */
#include <sys/open.h>		/* for open params.	 */
#include <sys/uio.h>		/* for read/write */
#include <sys/envctrl_gen.h>	/* user level generic visible definitions */
#include <sys/envctrl_ue250.h>	/* user level UE250 visible definitions */
#include <javelin/sys/envctrltwo.h> /* definitions for Javelin */
#include <io/envctrl_targets.c>
#include <sys/priv_names.h>

/* driver entry point fn definitions */
static int 	envctrl_open(dev_t *, int, int, cred_t *);
static int	envctrl_close(dev_t, int, int, cred_t *);
static int	envctrl_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
static uint_t 	envctrl_bus_isr(caddr_t);
static uint_t 	envctrl_dev_isr(caddr_t);

/* configuration entry point fn definitions */
static int 	envctrl_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int	envctrl_attach(dev_info_t *, ddi_attach_cmd_t);
static int	envctrl_detach(dev_info_t *, ddi_detach_cmd_t);

/* 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_get_fpm_status(struct envctrlunit *, uint8_t *);
static int	envctrl_set_fsp(struct envctrlunit *, uint8_t *);
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 *,
		    uint8_t, uint8_t);
static int	envctrl_check_sys_temperatures(struct envctrlunit *);
static void	envctrl_check_disk_kstats(struct envctrlunit *);
static void	envctrl_update_disk_kstats(struct envctrlunit *,
			uint8_t, uint8_t);
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 char driver_name[] = "envctrltwo";
static uchar_t _cpu_temps[256];
static uchar_t _cpu_fan_speeds[256];
static int psok[2] = {-1, -1};
static int pspr[2] = {-1, -1};
static uint8_t idle_fanspeed;

static int power_flt_led_lit = 0;

extern void pci_thermal_rem_intr(dev_info_t *, uint_t);

/* 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_max_retries = 200;
static int envctrl_allow_detach = 0;
static int envctrl_numcpus = 1;
static int envctrl_handler = 1; /* 1 is the default */
static clock_t overtemp_timeout_hz;
static clock_t blink_timeout_hz;
static clock_t pshotplug_timeout_hz;
static clock_t warning_timeout_hz;
/*
 * Temperature levels :
 * green = OK  - no action needed
 * yellow = warning - display warning message and poll faster
 * red = critical - shutdown system
 */
enum levels {green, yellow, red};

#define	DPRINTF1 if (envctrl_debug_flags && (envctrl_debug_flags & 0x1)) printf
#define	DPRINTF2 if (envctrl_debug_flags && (envctrl_debug_flags & 0x2)) printf
#define	DPRINTF3 if (envctrl_debug_flags && (envctrl_debug_flags & 0x4)) printf

#define	JAV_FAN_SPEED_SF_NUM	107
#define	JAV_FAN_SPEED_SF_DEN	100
#define	JAV_MAX_TEMP_SENSORS	6
#define	JAV_FSP_MASK		0xC0
#define	FAN_DRIFT		25
#define	MAX_FAN_SPEED		255
#define	MAX_DEVS		16

#define	ENVCTRL_UE250_INTR_LATCH_INIT0 0xFE
#define	ENVCTRL_UE250_INTR_LATCH_INIT1 0xFF

static int t_scale_num[8];
static int t_scale_den[8];
static uint8_t t_addr[8];
static uint8_t t_port[8];
static int sensor_types[] = { ENVCTRL_UE250_CPU0_SENSOR,
			ENVCTRL_UE250_CPU1_SENSOR, ENVCTRL_UE250_MB0_SENSOR,
			ENVCTRL_UE250_MB1_SENSOR, ENVCTRL_UE250_PDB_SENSOR,
			ENVCTRL_UE250_SCSI_SENSOR };

static struct cb_ops envctrl_cb_ops = {
	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 */
	(int)(D_NEW | D_MP)	/* cb_flag */
};

/*
 * Declare ops vectors for auto configuration.
 */
struct dev_ops  envctrltwo_ops = {
	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 */
	(struct bus_ops *)NULL,	/* devo_bus_ops */
	nulldev,		/* devo_power */
	ddi_quiesce_not_supported,	/* devo_quiesce */
};

extern struct mod_ops mod_driverops;

static struct modldrv envctrlmodldrv = {
	&mod_driverops,		/* type of module - driver */
	"I2C ENVCTRLTWO_driver",
	&envctrltwo_ops,
};

static struct modlinkage envctrlmodlinkage = {
	MODREV_1,
	&envctrlmodldrv,
	0
};

int
_init(void)
{
	register int    error;

	if ((error = mod_install(&envctrlmodlinkage)) == 0) {
		(void) ddi_soft_state_init(&envctrlsoft_statep,
		    sizeof (struct envctrlunit), 1);
	}

	return (error);
}

int
_fini(void)
{
	register int    error;

	if ((error = mod_remove(&envctrlmodlinkage)) == 0)
		ddi_soft_state_fini(&envctrlsoft_statep);

	return (error);
}

int
_info(struct modinfo *modinfop)
{
	return (mod_info(&envctrlmodlinkage, modinfop));
}

static int
envctrl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
	register int	instance;
	char		name[16];
	uint8_t fspval;
	register struct	envctrlunit *unitp;
	struct ddi_device_acc_attr attr;
	uchar_t *creg_prop;
	uint_t len, tblsz;
	int i, j, k, status;
	uint8_t fanspeed;

	status = len = tblsz = 0;

	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;

	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;

	instance = ddi_get_instance(dip);

	switch (cmd) {
	case DDI_ATTACH:
		break;
	case DDI_RESUME:
		if (!(unitp = ddi_get_soft_state(envctrlsoft_statep, instance)))
			return (DDI_FAILURE);
		mutex_enter(&unitp->umutex);
		if (!unitp->suspended) {
			mutex_exit(&unitp->umutex);
			return (DDI_FAILURE);
		}
		unitp->suspended = 0;
		unitp->initting = B_TRUE;
		envctrl_init_bus(unitp);
		unitp->initting = B_FALSE;

		envctrl_ps_probe(unitp);
		envctrl_probe_cpus(unitp);
		mutex_exit(&unitp->umutex);

		return (DDI_SUCCESS);

	default:
		return (DDI_FAILURE);
	}

	/* Set up timer values */
	overtemp_timeout_hz = drv_usectohz(ENVCTRL_UE250_OVERTEMP_TIMEOUT_USEC);
	blink_timeout_hz = drv_usectohz(ENVCTRL_UE250_BLINK_TIMEOUT_USEC);
	pshotplug_timeout_hz =
	    drv_usectohz(ENVCTRL_UE250_BLINK_TIMEOUT_USEC * 2);
	/*
	 * On a cooling failure, either a fan failure or temperature
	 * exceeding a WARNING level, the temperature poll thread
	 * will run every 6 seconds.
	 */
	warning_timeout_hz =
	    drv_usectohz(ENVCTRL_UE250_OVERTEMP_TIMEOUT_USEC / 6);

	if (ddi_soft_state_zalloc(envctrlsoft_statep, instance) != 0) {
		cmn_err(CE_WARN, "%s%d: failed to zalloc softstate\n",
		    ddi_get_name(dip), instance);
		goto failed;
	}

	unitp = ddi_get_soft_state(envctrlsoft_statep, instance);

	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&unitp->bus_ctl_regs, 0,
	    sizeof (struct ehc_pcd8584_regs), &attr,
	    &unitp->ctlr_handle) != DDI_SUCCESS) {
		cmn_err(CE_WARN, "%s%d: failed to map in bus_control regs\n",
		    ddi_get_name(dip), instance);
		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.
	 */

	pci_thermal_rem_intr(dip, (uint_t)0);

	/* add interrupts */

	if (ddi_get_iblock_cookie(dip, 1,
	    &unitp->ic_trap_cookie) != DDI_SUCCESS)  {
		cmn_err(CE_WARN, "%s%d: ddi_get_iblock_cookie FAILED \n",
		    ddi_get_name(dip), instance);
		goto failed;
	}

	mutex_init(&unitp->umutex, NULL, MUTEX_DRIVER,
	    (void *)unitp->ic_trap_cookie);


	if (ddi_add_intr(dip, 0, &unitp->ic_trap_cookie, NULL, envctrl_bus_isr,
	    (caddr_t)unitp) != DDI_SUCCESS) {
		cmn_err(CE_WARN, "%s%d: failed to add hard intr \n",
		    ddi_get_name(dip), instance);
		goto remlock;
	}


	if (ddi_add_intr(dip, 1, &unitp->ic_trap_cookie, NULL, envctrl_dev_isr,
	    (caddr_t)unitp) != DDI_SUCCESS) {
		cmn_err(CE_WARN, "%s%d: failed to add hard intr \n",
		    ddi_get_name(dip), instance);
		goto remhardintr;
	}


	(void) sprintf(name, "envctrltwo%d", instance);

	if (ddi_create_priv_minor_node(dip, name, S_IFCHR, instance,
	    DDI_PSEUDO, 0, PRIV_SYS_CONFIG, PRIV_SYS_CONFIG, 0666) ==
	    DDI_FAILURE) {
		goto remhardintr1;
	}

	mutex_enter(&unitp->umutex);

	/*
	 * Javelin will not have a workstation configuration so activity
	 * LED will always blink.
	 */
	unitp->activity_led_blink = B_TRUE;
	unitp->shutdown = B_FALSE;
	unitp->num_ps_present = 0;
	unitp->num_encl_present = 1;
	unitp->current_mode = ENVCTRL_NORMAL_MODE;
	if (envctrl_numcpus > 1) {
		unitp->num_cpus_present = envctrl_numcpus;
	}
	envctrl_probe_cpus(unitp);
	if ((unitp->cpu_pr_location[ENVCTRL_CPU0] == B_FALSE) ||
	    (unitp->cpu_pr_location[ENVCTRL_CPU1] == B_FALSE))
		/* Only one CPU in the system */
		unitp->num_temps_present = 5;
	else
		unitp->num_temps_present = 6;
	unitp->num_fans_present = 1;
	unitp->dip = dip;

	mutex_exit(&unitp->umutex);

	if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
	    "cpu-temp-factors", &creg_prop, &len) != DDI_PROP_SUCCESS) {
		cmn_err(CE_WARN,
		    "%s%d: Unable to read cpu-temp-factors property",
		    ddi_get_name(dip), instance);
		return (DDI_NOT_WELL_FORMED);
	}
	tblsz = (sizeof (_cpu_temps) / sizeof (uchar_t));

	if (len <= tblsz && status == DDI_PROP_SUCCESS) {
		for (i = 0; i < len; i++) {
			_cpu_temps[i+2] = creg_prop[i];
		}
	}
	_cpu_temps[0] = _cpu_temps[1] = _cpu_temps[2];

	ddi_prop_free((void *)creg_prop);

	if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
	    "cpu-fan-speeds", &creg_prop, &len) != DDI_PROP_SUCCESS) {
		cmn_err(CE_WARN,
		    "%s%d: Unable to read cpu-fan-speeds property",
		    ddi_get_name(dip), instance);
		return (DDI_NOT_WELL_FORMED);
	}
	tblsz = (sizeof (_cpu_fan_speeds) / sizeof (uchar_t));

	if (len <= tblsz && status == DDI_PROP_SUCCESS) {
		for (i = 0; i < len; i++) {
			_cpu_fan_speeds[i+2] = creg_prop[i];
		}
	}
	_cpu_fan_speeds[0] = _cpu_fan_speeds[1] = _cpu_fan_speeds[2];

	ddi_prop_free((void *)creg_prop);

	if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
	    "thermisters", &creg_prop, &len) != DDI_PROP_SUCCESS) {
		cmn_err(CE_WARN,
		    "%s%d: Unable to read thermisters property",
		    ddi_get_name(dip), instance);
		return (DDI_NOT_WELL_FORMED);
	}

	mutex_enter(&unitp->umutex);

	j = 0; k = 0;
	for (i = 0; i < JAV_MAX_TEMP_SENSORS; i++) {
		/* Type */
		unitp->temp_kstats[k].type = sensor_types[i];
		/* Address */
		t_addr[k] = creg_prop[j] << 24 | creg_prop[j+1] << 16 |
		    creg_prop[j+2] << 8 | creg_prop[j+3];
		j += 4;
		/* Port */
		t_port[k] = creg_prop[j] << 24 | creg_prop[j+1] << 16 |
		    creg_prop[j+2] << 8 | creg_prop[j+3];
		j += 4;
		/* Min */
		unitp->temp_kstats[k].min =
		    creg_prop[j] << 24 | creg_prop[j+1] << 16 |
		    creg_prop[j+2] << 8 | creg_prop[j+3];
		j += 4;
		/* Warning threshold */
		unitp->temp_kstats[k].warning_threshold =
		    creg_prop[j] << 24 | creg_prop[j+1] << 16 |
		    creg_prop[j+2] << 8 | creg_prop[j+3];
		j += 4;
		/* Shutdown threshold */
		unitp->temp_kstats[k].shutdown_threshold =
		    creg_prop[j] << 24 | creg_prop[j+1] << 16 |
		    creg_prop[j+2] << 8 | creg_prop[j+3];
		j += 4;
		/* Numerator of scale factor */
		t_scale_num[k] = creg_prop[j] << 24 | creg_prop[j+1] << 16 |
		    creg_prop[j+2] << 8 | creg_prop[j+3];
		j += 4;
		/* Denominator of scale factor */
		t_scale_den[k] = creg_prop[j] << 24 | creg_prop[j+1] << 16 |
		    creg_prop[j+2] << 8 | creg_prop[j+3];
		j += 4;
		bcopy((caddr_t)&creg_prop[j], unitp->temp_kstats[k].label,
		    (size_t)sizeof (&creg_prop[j]));
		while (creg_prop[j] != '\0') j++;
		j++;
		if (t_addr[k] == ENVCTRL_UE250_CPU_TEMP_DEV) {
			if (((t_port[k] == ENVCTRL_UE250_CPU0_PORT) &&
			    (unitp->cpu_pr_location[ENVCTRL_CPU0] ==
			    B_FALSE)) ||
			    ((t_port[k] == ENVCTRL_UE250_CPU1_PORT) &&
			    (unitp->cpu_pr_location[ENVCTRL_CPU1] == B_FALSE)))
				/* 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 */

	unitp->initting = B_TRUE;
	envctrl_init_bus(unitp);
	DPRINTF1("envctrl_attach(): Completed initialization of PCF8584");
	unitp->initting = B_FALSE;
	drv_usecwait(1000);

	unitp->timeout_id = 0;
	unitp->blink_timeout_id = 0;

	unitp->fan_failed = 0;
	unitp->fan_kstats.fans_ok = B_TRUE;
	unitp->tempr_warning = 0;

	envctrl_ps_probe(unitp);

	unitp->initting = B_TRUE;
	envctrl_fan_fail_service(unitp);
	unitp->initting = B_FALSE;

	/*
	 * Fans could be blasting, turn them down.
	 */
	fanspeed = 0x0;
	status = envctrl_write_chip(unitp, ENVCTRL_PCF8591, EHC_DEV2, 0,
	    &fanspeed, 1);
	if (status == DDI_FAILURE)
		cmn_err(CE_WARN, "%s%d: Write to PCF8591 (SETFAN) failed\n",
		    ddi_get_name(dip), instance);

	/*
	 * we need to init the fan kstats before the tempr_poll
	 */
	envctrl_add_kstats(unitp);
	envctrl_init_encl_kstats(unitp);
	envctrl_check_disk_kstats(unitp);

	envctrl_update_fanspeed(unitp);
	idle_fanspeed = unitp->fan_kstats.fanspeed;

	if (unitp->activity_led_blink == B_TRUE) {
		unitp->present_led_state = B_FALSE;
		mutex_exit(&unitp->umutex);
		envctrl_led_blink((void *)unitp);
		mutex_enter(&unitp->umutex);
	} else {
		fspval = ENVCTRL_UE250_FSP_ACTIVE;
		(void) envctrl_set_fsp(unitp, &fspval);
	}

	mutex_exit(&unitp->umutex);

	envctrl_tempr_poll((void *)unitp);

	/*
	 * interpose envctrl's abort sequence handler
	 */
	if (envctrl_handler) {
		abort_seq_handler = envctrl_abort_seq_handler;
	}

	ddi_report_dev(dip);

	return (DDI_SUCCESS);

remhardintr1:
	ddi_remove_intr(dip, (uint_t)1, unitp->ic_trap_cookie);
remhardintr:
	ddi_remove_intr(dip, (uint_t)0, unitp->ic_trap_cookie);

remlock:
	mutex_destroy(&unitp->umutex);

failed:
	if (unitp->ctlr_handle)
		ddi_regs_map_free(&unitp->ctlr_handle);

	cmn_err(CE_WARN, "%s%d: attach failed\n", ddi_get_name(dip), instance);

	return (DDI_FAILURE);

}

static int
envctrl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
	int		instance;
	register struct envctrlunit *unitp;

	instance = ddi_get_instance(dip);
	unitp = ddi_get_soft_state(envctrlsoft_statep, instance);

	switch (cmd) {
	case DDI_DETACH:
		if (envctrl_allow_detach) {

			if (unitp->psksp != NULL) {
				kstat_delete(unitp->psksp);
			}
			if (unitp->fanksp != NULL) {
				kstat_delete(unitp->fanksp);
			}
			if (unitp->enclksp != NULL) {
				kstat_delete(unitp->enclksp);
			}
			if (unitp->tempksp != NULL) {
				kstat_delete(unitp->tempksp);
			}
			if (unitp->diskksp != NULL) {
				kstat_delete(unitp->diskksp);
			}

			if (unitp->timeout_id != 0) {
				(void) untimeout(unitp->timeout_id);
				unitp->timeout_id = 0;
			}
			if (unitp->blink_timeout_id != 0) {
				(void) untimeout(unitp->blink_timeout_id);
				unitp->blink_timeout_id = 0;
			}

			ddi_remove_minor_node(dip, NULL);

			ddi_remove_intr(dip, (uint_t)0, unitp->ic_trap_cookie);
			ddi_remove_intr(dip, (uint_t)1, unitp->ic_trap_cookie);

			ddi_regs_map_free(&unitp->ctlr_handle);

			mutex_destroy(&unitp->umutex);

			return (DDI_SUCCESS);
		} else {
			return (DDI_FAILURE);
		}

	case DDI_SUSPEND:
		if (!(unitp = ddi_get_soft_state(envctrlsoft_statep, instance)))
			return (DDI_FAILURE);
		mutex_enter(&unitp->umutex);
		if (unitp->suspended) {
			cmn_err(CE_WARN, "%s%d: envctrltwo already suspended\n",
			    ddi_get_name(dip), instance);
			mutex_exit(&unitp->umutex);
			return (DDI_FAILURE);
		}
		unitp->suspended = 1;
		mutex_exit(&unitp->umutex);
		return (DDI_SUCCESS);

	default:
		cmn_err(CE_WARN, "%s%d: suspend general fault\n",
		    ddi_get_name(dip), instance);
		return (DDI_FAILURE);
	}


}
int
envctrl_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
    void **result)
{
	dev_t	dev = (dev_t)arg;
	struct envctrlunit *unitp;
	int	instance, ret;

	instance = getminor(dev);

#ifdef lint
	dip = dip;
#endif


	switch (infocmd) {
		case DDI_INFO_DEVT2DEVINFO:
			if ((unitp = (struct envctrlunit *)
			    ddi_get_soft_state(envctrlsoft_statep,
			    instance)) != NULL) {
				*result = unitp->dip;
				ret = DDI_SUCCESS;
			} else {
				*result = NULL;
				ret = DDI_FAILURE;
			}
			break;
		case DDI_INFO_DEVT2INSTANCE:
			*result = (void *)(uintptr_t)instance;
			ret = DDI_SUCCESS;
			break;
		default:
			ret = DDI_FAILURE;
			break;
	}

	return (ret);
}

/* ARGSUSED1 */
static int
envctrl_open(dev_t *dev, int flag, int otyp, cred_t *cred_p)
{
	struct envctrlunit *unitp;
	int status = 0;
	register int	instance;

	instance = getminor(*dev);
	if (instance < 0)
		return (ENXIO);
	unitp = (struct envctrlunit *)
	    ddi_get_soft_state(envctrlsoft_statep, instance);

	if (unitp == NULL)
		return (ENXIO);

	if (otyp != OTYP_CHR)
		return (EINVAL);

	mutex_enter(&unitp->umutex);

	if (flag & FWRITE) {
		if ((unitp->oflag & FWRITE)) {
			mutex_exit(&unitp->umutex);
			return (EBUSY);
		} else {
			unitp->oflag |= FWRITE;
		}
	}

	mutex_exit(&unitp->umutex);
	return (status);
}

/*ARGSUSED1*/
static int
envctrl_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
{
	struct envctrlunit *unitp;
	register int    instance;

	instance = getminor(dev);
	if (instance < 0)
		return (ENXIO);
	unitp = (struct envctrlunit *)
	    ddi_get_soft_state(envctrlsoft_statep, instance);
	if (unitp == NULL)
		return (ENXIO);

	mutex_enter(&unitp->umutex);

	unitp->oflag = B_FALSE;
	unitp->current_mode = ENVCTRL_NORMAL_MODE;

	mutex_exit(&unitp->umutex);
	return (DDI_SUCCESS);
}


/*
 * standard put procedure for envctrl
 */
static int
envctrl_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cred_p,
	int *rvalp)
{
	struct envctrlunit *unitp;
	register int	instance;
	uint8_t wdval, tempr;
	struct envctrl_chip fanspeed;
	struct envctrl_chip ledchip, envcchip;
	struct envctrl_chip temp, a_fanspeed;
	int rval = 0, status, tfanspeed;

#ifdef lint
	cred_p = cred_p;
	rvalp = rvalp;
#endif
	instance = getminor(dev);
	unitp = (struct envctrlunit *)
	    ddi_get_soft_state(envctrlsoft_statep, instance);

	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))
		if ((caddr_t)arg == NULL)
			return (EFAULT);

	switch (cmd) {
	case ENVCTRL_IOC_SETMODE:
		/* Set mode */
		if (ddi_copyin((caddr_t)arg, (caddr_t)&wdval, sizeof (uint8_t),
		    flag)) {
			rval = EFAULT;
			break;
		}
		if (wdval == ENVCTRL_DIAG_MODE ||
		    wdval == ENVCTRL_NORMAL_MODE) {
			mutex_enter(&unitp->umutex);
			unitp->current_mode = wdval;
			if (unitp->timeout_id != 0 &&
			    wdval == ENVCTRL_DIAG_MODE) {
				(void) untimeout(unitp->timeout_id);
				unitp->timeout_id =
				    (timeout(envctrl_tempr_poll,
				    (caddr_t)unitp, overtemp_timeout_hz));
			}
			if (wdval == ENVCTRL_NORMAL_MODE) {
				/*
				 * Fans could be blasting, turn them down.
				 */
				tempr = 0x0;
				status = envctrl_write_chip(unitp,
				    ENVCTRL_PCF8591, EHC_DEV2, 0,
				    &tempr, 1);
				if (status == DDI_FAILURE)
					cmn_err(CE_WARN,
					    "%s%d: Write to PCF8591 "
					    "(SETMODE) failed\n",
					    driver_name, unitp->instance);

				/*
				 * This delay allows the fans to time to
				 * change speed
				 */
				drv_usecwait(100000);
				(void) envctrl_check_sys_temperatures(unitp);
				unitp->current_mode = ENVCTRL_DIAG_MODE;
				envctrl_fan_fail_service(unitp);
				unitp->current_mode = ENVCTRL_NORMAL_MODE;
			}
			mutex_exit(&unitp->umutex);
		} else {
			rval = EINVAL;
		}
		break;
	case ENVCTRL_IOC_GETMODE:
		wdval = unitp->current_mode;
		if (ddi_copyout((caddr_t)&wdval, (caddr_t)arg,
		    sizeof (uint8_t), flag)) {
			rval = EFAULT;
		}
		break;
	case ENVCTRL_IOC_RESETTMPR:
		/*
		 * For diags, cancel the curent temp poll
		 * and reset it for a new one.
		 */
		if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
			if (unitp->timeout_id != 0) {
				(void) untimeout(unitp->timeout_id);
				unitp->timeout_id = 0;
			}
			envctrl_tempr_poll((void *)unitp);
		} else {
			rval = EINVAL;
		}
		break;
	case ENVCTRL_IOC_GETTEMP2:
		/* Get the user buffer address */

		if (ddi_copyin((caddr_t)arg, (caddr_t)&temp,
			sizeof (struct envctrl_chip), flag)) {
			rval = EFAULT;
			break;
		}
		if (((temp.chip_num != ENVCTRL_DEV2) &&
		    (temp.chip_num != ENVCTRL_DEV7)) ||
		    (temp.index > EHC_PCF8591_CH_3)) {
			rval = EINVAL;
			break;
		}
		mutex_enter(&unitp->umutex);
		status = envctrl_read_chip(unitp, ENVCTRL_PCF8591,
		    temp.chip_num, temp.index, &temp.val, 1);
		mutex_exit(&unitp->umutex);
		if (status == DDI_FAILURE) {
			cmn_err(CE_WARN,
			    "%s%d: Read from PCF8591 (IOC_GETTEMP) failed",
			    driver_name, unitp->instance);
			rval = EINVAL;
			break;
		}
		if (ddi_copyout((caddr_t)&temp, (caddr_t)arg,
			sizeof (struct envctrl_chip), flag)) {
			rval = EFAULT;
		}
		break;
	case ENVCTRL_IOC_SETTEMP:
		rval = EINVAL;
		break;
	case ENVCTRL_IOC_SETWDT:
		rval = EINVAL;
		break;
	case ENVCTRL_IOC_SETFAN2:
		/* NOTE: need to sanity check values coming from userland */
		if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
			if (ddi_copyin((caddr_t)arg, (caddr_t)&fanspeed,
				sizeof (struct envctrl_chip), flag)) {
				rval = EFAULT;
				break;
			}
			if ((fanspeed.type != ENVCTRL_PCF8591) ||
			    (fanspeed.chip_num != ENVCTRL_DEV2) ||
			    (fanspeed.index > EHC_PCF8591_CH_3)) {
				rval = EINVAL;
				break;
			}
			mutex_enter(&unitp->umutex);
			status = envctrl_set_fanspeed(unitp, &fanspeed);
			if (status == DDI_FAILURE) {
				cmn_err(CE_WARN,
				    "%s%d: Write to PCF8591 "
				    "(IOC_SETFAN) failed",
				    driver_name, unitp->instance);
				rval = EINVAL;
			}
			mutex_exit(&unitp->umutex);
		} else {
			rval = EINVAL;
		}
		break;
	case ENVCTRL_IOC_GETFAN2:
		if (ddi_copyin((caddr_t)arg, (caddr_t)&a_fanspeed,
			sizeof (struct envctrl_chip), flag)) {
			rval = EFAULT;
			break;
		}
		if ((a_fanspeed.type != ENVCTRL_PCF8591) ||
		    (a_fanspeed.chip_num != ENVCTRL_DEV2) ||
		    (a_fanspeed.index != EHC_PCF8591_CH_1)) {
			rval = EINVAL;
			break;
		}
		mutex_enter(&unitp->umutex);
		status = envctrl_read_chip(unitp, ENVCTRL_PCF8591,
		    a_fanspeed.chip_num, a_fanspeed.index,
		    &a_fanspeed.val, 1);
		mutex_exit(&unitp->umutex);
		if (status == DDI_FAILURE) {
			cmn_err(CE_WARN,
			    "%s%d: Read of PCF8591 (IOC_GETFAN) failed",
			    driver_name, unitp->instance);
			rval = EINVAL;
			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.
		 */
		if ((tfanspeed = ((int)a_fanspeed.val * JAV_FAN_SPEED_SF_NUM) /
		    JAV_FAN_SPEED_SF_DEN) > 255)
			a_fanspeed.val = 255;
		else
			a_fanspeed.val = tfanspeed & 0xFF;
		unitp->fan_kstats.fanspeed = a_fanspeed.val;
		if (ddi_copyout((caddr_t)&a_fanspeed, (caddr_t)arg,
			sizeof (struct envctrl_chip), flag)) {
			rval = EFAULT;
		}
		break;
	case ENVCTRL_IOC_SETFSP2:
		if (ddi_copyin((caddr_t)arg, (caddr_t)&envcchip,
			sizeof (struct envctrl_chip), flag)) {
			rval = EFAULT;
			break;
		}
		if ((envcchip.type != ENVCTRL_PCF8574A) ||
		    (envcchip.chip_num != ENVCTRL_DEV6)) {
			rval = EINVAL;
			break;
		}
		wdval = envcchip.val;
		mutex_enter(&unitp->umutex);
		/*
		 * 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 (unitp->current_mode == ENVCTRL_NORMAL_MODE) {
			if (wdval & ~ENVCTRL_UE250_FSP_USRMASK) {
				mutex_exit(&unitp->umutex);
				rval = EINVAL;
				break;
			}
		}
		if (wdval & ENVCTRL_UE250_FSP_PS_ERR)
			power_flt_led_lit = 1;
		status = envctrl_set_fsp(unitp, &wdval);
		mutex_exit(&unitp->umutex);
		if (status == DDI_FAILURE) {
			cmn_err(CE_WARN,
			    "%s%d: Read of PCF8574A (IOC_SETFSP) failed",
			    driver_name, unitp->instance);
			rval = EINVAL;
		}
		break;
	case ENVCTRL_IOC_GETFSP2:
		if (ddi_copyin((caddr_t)arg, (caddr_t)&envcchip,
			sizeof (struct envctrl_chip), flag)) {
			rval = EFAULT;
			break;
		}
		if ((envcchip.type != ENVCTRL_PCF8574A) ||
		    (envcchip.chip_num != ENVCTRL_DEV6)) {
			rval = EINVAL;
			break;
		}
		mutex_enter(&unitp->umutex);
		status = envctrl_get_fpm_status(unitp, &wdval);
		mutex_exit(&unitp->umutex);
		if (status == DDI_FAILURE) {
			cmn_err(CE_WARN,
			    "%s%d: Read of PCF8574A (IOC_GETFSP) failed",
			    driver_name, unitp->instance);
			rval = EINVAL;
		} else {
			envcchip.val = wdval;
			if (ddi_copyout((caddr_t)&envcchip, (caddr_t)arg,
				sizeof (struct envctrl_chip), flag)) {
				rval = EFAULT;
			}
		}
		break;
	case ENVCTRL_IOC_SETDSKLED2:
		if (ddi_copyin((caddr_t)arg, (caddr_t)&ledchip,
			sizeof (struct envctrl_chip), flag)) {
			rval = EFAULT;
			break;
		}
		if ((ledchip.type != ENVCTRL_PCF8574A) ||
		    (ledchip.chip_num != ENVCTRL_DEV7)) {
			rval = EINVAL;
			break;
		}
		mutex_enter(&unitp->umutex);
		if (envctrl_set_dskled(unitp, &ledchip)) {
			rval = EINVAL;
		}
		mutex_exit(&unitp->umutex);
		break;
	case ENVCTRL_IOC_GETDSKLED2:
		if (ddi_copyin((caddr_t)arg, (caddr_t)&ledchip,
			sizeof (struct envctrl_chip), flag)) {
			rval = EFAULT;
			break;
		}
		if ((ledchip.type != ENVCTRL_PCF8574A) ||
		    (ledchip.chip_num != ENVCTRL_DEV7)) {
			rval = EINVAL;
			break;
		}
		mutex_enter(&unitp->umutex);
		if (envctrl_get_dskled(unitp, &ledchip)) {
			rval = EINVAL;
		} else {
			if (ddi_copyout((caddr_t)&ledchip, (caddr_t)arg,
				sizeof (struct envctrl_chip), flag)) {
				rval = EFAULT;
			}
		}
		mutex_exit(&unitp->umutex);
		break;
	case ENVCTRL_IOC_SETRAW:
		if (unitp->current_mode != ENVCTRL_DIAG_MODE) {
			rval = EINVAL;
			break;
		}
		if (ddi_copyin((caddr_t)arg, (caddr_t)&temp,
			sizeof (struct envctrl_chip), flag)) {
			rval = EFAULT;
			break;
		}
		mutex_enter(&unitp->umutex);
		status = envctrl_write_chip(unitp, temp.type, temp.chip_num,
		    temp.index, &temp.val, 1);
		if (status == DDI_FAILURE) {
			cmn_err(CE_WARN,
			    "%s%d: Write to chip (IOC_SETRAW) failed",
			    driver_name, unitp->instance);
			rval = EINVAL;
		}
		mutex_exit(&unitp->umutex);
		break;
	case ENVCTRL_IOC_GETRAW:
		if (ddi_copyin((caddr_t)arg, (caddr_t)&temp,
			sizeof (struct envctrl_chip), flag)) {
			rval = EFAULT;
			break;
		}
		mutex_enter(&unitp->umutex);
		status = envctrl_read_chip(unitp, temp.type, temp.chip_num,
		    temp.index, &temp.val, 1);
		if (status == DDI_FAILURE) {
			cmn_err(CE_WARN,
			    "%s%d: Read of chip (IOC_GETRAW) failed",
			    driver_name, unitp->instance);
			rval = EINVAL;
		}
		mutex_exit(&unitp->umutex);
		if (ddi_copyout((caddr_t)&temp, (caddr_t)arg,
			sizeof (struct envctrl_chip), flag)) {
			rval = EFAULT;
		}
		break;
	default:
		rval = EINVAL;
	}

	return (rval);
}

uint_t
envctrl_bus_isr(caddr_t arg)
{
	struct envctrlunit *unitp = (struct envctrlunit *)(void *)arg;
	int ic = DDI_INTR_UNCLAIMED;

	mutex_enter(&unitp->umutex);

	/*
	 * NOT USED
	 */

	mutex_exit(&unitp->umutex);
	return (ic);
}

uint_t
envctrl_dev_isr(caddr_t arg)
{
	struct envctrlunit *unitp = (struct envctrlunit *)(void *)arg;
	uint8_t recv_data;
	int ic;
	int retrys = 0;
	int status;
	static int spurious_intr_count = 0;

	ic = DDI_INTR_UNCLAIMED;

	mutex_enter(&unitp->umutex);


	/*
	 * 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 {
		status = ehc_read_pcf8574a((struct ehc_envcunit *)unitp,
		    ENVCTRL_UE250_PCF8574A_BASE_ADDR | EHC_DEV0,
		    &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) {
			status = ehc_read_pcf8574a((struct ehc_envcunit *)unitp,
			    ENVCTRL_UE250_PCF8574A_BASE_ADDR | EHC_DEV0,
			    &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 {
				cmn_err(CE_WARN,
				    "%s%d: Read of PCF8574A (INT) failed\n",
				    driver_name, unitp->instance);
				ehc_init_pcf8584((struct ehc_envcunit *)unitp);
				mutex_exit(&unitp->umutex);
				ic = DDI_INTR_CLAIMED;
				return (ic);
			}
		}
	} while (status != DDI_SUCCESS);

	DPRINTF1("Interrupt routine called, interrupt = %X\n", recv_data);
	if (!(recv_data & EHC_PCF8574_PORT0)) {
		ic = DDI_INTR_CLAIMED;
	}

	if (!(recv_data & EHC_PCF8574_PORT1)) {
		DPRINTF1("Temperature interrupt detected\n");
		(void) envctrl_check_sys_temperatures(unitp);

		/*
		 * Clear the interrupt latches
		 */
		envctrl_intr_latch_clr(unitp);

		ic = DDI_INTR_CLAIMED;
	}

	if (!(recv_data & EHC_PCF8574_PORT2)) {
		DPRINTF1("Disk interrupt detected\n");
		envctrl_check_disk_kstats(unitp);
		ic = DDI_INTR_CLAIMED;
	}

	if (!(recv_data & EHC_PCF8574_PORT3)) {
		ic = DDI_INTR_CLAIMED;
	}

	if (!(recv_data & EHC_PCF8574_PORT4)) {
		/*
		 * Check for a fan fail
		 */
		DPRINTF1("Fan interrupt detected\n");
		envctrl_fan_fail_service(unitp);

		/*
		 * Clear the interrupt latches
		 */
		envctrl_intr_latch_clr(unitp);

		ic = DDI_INTR_CLAIMED;
	}

	if (!(recv_data & EHC_PCF8574_PORT5)) {
		DPRINTF1("Keyswitch interrupt detected\n");
		(void) envctrl_get_fpm_status(unitp, (uint8_t *)NULL);
		ic = DDI_INTR_CLAIMED;
	}

	if (!(recv_data & EHC_PCF8574_PORT6)) {
		DPRINTF1("Power supply interrupt detected\n");
		envctrl_PS_intr_service(unitp);
		ic = DDI_INTR_CLAIMED;
	}

	if (!(recv_data & EHC_PCF8574_PORT7)) {
		ic = DDI_INTR_CLAIMED;
	}

	/*
	 * 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)
			cmn_err(CE_WARN,
			    "%s%d: Received 256 spurious interrupts\n",
			    driver_name, unitp->instance);
		spurious_intr_count++;
		ic = DDI_INTR_CLAIMED;
	} else
		spurious_intr_count = 0;

	mutex_exit(&unitp->umutex);
	return (ic);

}


static int
envctrl_read_chip(struct envctrlunit *unitp, int type, int chip_num, int port,
	uint8_t *data, int num)
{
	int retrys = 0, autoincr = 0;
	int status;

	/*
	 * If more than one read is requested, set auto-increment bit
	 */
	if (num > 1)
		autoincr = 1;

	do {
		if (type == ENVCTRL_PCF8574A) {
			status = ehc_read_pcf8574a((struct ehc_envcunit *)unitp,
			    ENVCTRL_UE250_PCF8574A_BASE_ADDR | chip_num,
			    data, num);
		} else if (type == ENVCTRL_PCF8574) {
			status = ehc_read_pcf8574((struct ehc_envcunit *)unitp,
			    ENVCTRL_UE250_PCF8574_BASE_ADDR | chip_num,
			    data, num);
		} else if (type == ENVCTRL_PCF8591) {
			status = ehc_read_pcf8591((struct ehc_envcunit *)unitp,
			    ENVCTRL_UE250_PCF8591_BASE_ADDR | chip_num,
			    port, autoincr, 0, 1, data, num);
		}
		/*
		 * If the bus hangs, attempt a recovery
		 */
		if (status == DDI_FAILURE) {
			drv_usecwait(1000);
			if (retrys < envctrl_max_retries) {
				retrys++;
			} else {
				ehc_init_pcf8584((struct ehc_envcunit *)unitp);
				break;
			}
		}
	} while (status != DDI_SUCCESS);

	return (status);
}

static int
envctrl_write_chip(struct envctrlunit *unitp, int type, int chip_num, int port,
	uint8_t *data, int num)
{
	int retrys = 0, autoincr = 0;
	int status;

	/*
	 * Incase some applications mistakenly include the chips base addr
	 */
	chip_num = chip_num & 0xF;

	/*
	 * If more than one write is requested, set auto-increment bit
	 */
	if (num > 1)
		autoincr = 1;

	do {
		if (type == ENVCTRL_PCF8574A) {
			status = ehc_write_pcf8574a(
			    (struct ehc_envcunit *)unitp,
			    ENVCTRL_UE250_PCF8574A_BASE_ADDR | chip_num,
			    data, num);
		} else if (type == ENVCTRL_PCF8574) {
			status = ehc_write_pcf8574((struct ehc_envcunit *)unitp,
			    ENVCTRL_UE250_PCF8574_BASE_ADDR | chip_num,
			    data, num);
		} else if (type == ENVCTRL_PCF8591) {
			status = ehc_write_pcf8591((struct ehc_envcunit *)unitp,
			    ENVCTRL_UE250_PCF8591_BASE_ADDR | chip_num,
			    port, autoincr, 0, 1, data, num);
		}

		/*
		 * If the bus hangs, attempt a recovery
		 */
		if (status == DDI_FAILURE) {
			drv_usecwait(1000);
			if (retrys < envctrl_max_retries) {
				retrys++;
			} else {
				ehc_init_pcf8584((struct ehc_envcunit *)unitp);
				break;
			}
		}
	} while (status != DDI_SUCCESS);

	return (status);
}

#ifdef GET_CPU_TEMP
static int
envctrl_get_cpu_temp(struct envctrlunit *unitp, int cpunum)
{
	uint8_t recv_data;
	int status;

	ASSERT(MUTEX_HELD(&unitp->umutex));

	/*
	 * 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.
	 */

	status = envctrl_read_chip(unitp, ENVCTRL_PCF8591, EHC_DEV7, cpunum,
	    &recv_data, 1);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: CPU TEMP read failed\n",
		    driver_name, unitp->instance);
		return (ENVCTRL_UE250_MAX_CPU_TEMP - 10);
	}

	return (_cpu_temps[recv_data]);
}
#endif

static void
envctrl_tempr_poll(void *arg)
{
	int diag_flag = 0, status;
	struct envctrlunit *unitp = (struct envctrlunit *)arg;

	mutex_enter(&unitp->umutex);

	if (unitp->shutdown == B_TRUE) {
		(void) power_down("Fatal System Environmental Control Error");
	}

	/*
	 * Clear the interrupt latches
	 */
	envctrl_intr_latch_clr(unitp);

	envctrl_reset_dflop(unitp);
	envctrl_enable_devintrs(unitp);
	/*
	 * 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.
	 */
	if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
		diag_flag++;
		if (envctrl_debug_flags) {
			cmn_err(CE_WARN, "%s%d: "
			    "Tempr poll went off while in DIAG MODE\n",
			    driver_name, unitp->instance);
		}
	}
	unitp->current_mode = ENVCTRL_NORMAL_MODE;
	DPRINTF1("envctrl_tempr_poll(): Checking system temps\n");
	status = envctrl_check_sys_temperatures(unitp);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN,
		    "%s%d: Failure detected during temperature poll",
		    driver_name, unitp->instance);
	}

	if (diag_flag == 0) {
		envctrl_fan_fail_service(unitp);
	}

	/* Turn of the power fault LED if ps_ok is asserted */
	envctrl_ps_probe(unitp);

	/* now have this thread sleep for a while */
	if ((unitp->fan_failed == B_TRUE) || (unitp->tempr_warning == B_TRUE)) {
		/*
		 * A thermal warning or fan failure condition exists.
		 * Temperature poll thread will run every 10 seconds.
		 */
		if (unitp->timeout_id != 0)
			(void) untimeout(unitp->timeout_id);
		unitp->timeout_id = (timeout(envctrl_tempr_poll,
		    (caddr_t)unitp, warning_timeout_hz));
	} else {
		/*
		 * No thermal warning or fan failure condition exists.
		 * This thread is set to run every 60 seconds.
		 */
		unitp->timeout_id = (timeout(envctrl_tempr_poll,
		    (caddr_t)unitp, overtemp_timeout_hz));
	}

	mutex_exit(&unitp->umutex);
}

static void
envctrl_led_blink(void *arg)
{
	uint8_t val, tmpval;
	int status;
	struct envctrlunit *unitp = (struct envctrlunit *)arg;

	mutex_enter(&unitp->umutex);

	status = envctrl_read_chip(unitp, ENVCTRL_PCF8574A, EHC_DEV6,
	    0, &val, 1);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Failed to read FSP LEDs",
		    driver_name, unitp->instance);
		/* now have this thread sleep for a while */
		unitp->blink_timeout_id = (timeout(envctrl_led_blink,
		    (caddr_t)unitp, blink_timeout_hz));
		mutex_exit(&unitp->umutex);
		return;
	}

	if (unitp->present_led_state == B_TRUE) {
		/*
		 * Now we need to "or" in fault bits of the FSP
		 * module for the mass storage fault led.
		 * and set it.
		 */
		val = (val & ~(EHC_PCF8574_PORT4) | JAV_FSP_MASK);
		unitp->present_led_state = B_FALSE;
	} else {
		val = (val | EHC_PCF8574_PORT4 | JAV_FSP_MASK);
		unitp->present_led_state = B_TRUE;
	}

	/*
	 * 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.
	 */
	tmpval = ~val;
	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);
		}
	}
	val = ~tmpval;

	status = envctrl_write_chip(unitp, ENVCTRL_PCF8574A, EHC_DEV6,
	    0, &val, 1);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Failed to blink activity LED",
		    driver_name, unitp->instance);
		/* now have this thread sleep for a while */
		unitp->blink_timeout_id = (timeout(envctrl_led_blink,
		    (caddr_t)unitp, blink_timeout_hz));
		mutex_exit(&unitp->umutex);
		return;
	}

	/* now have this thread sleep for a while */
	unitp->blink_timeout_id = (timeout(envctrl_led_blink,
	    (caddr_t)unitp, blink_timeout_hz));

	mutex_exit(&unitp->umutex);
}

static int
envctrl_check_sys_temperatures(struct envctrlunit *unitp)
{
	uint8_t buf[8];
	enum levels warning_level, level;
	uint8_t fspval;
	int status, warning_count = 0;

retrytemp1:
	status = envctrl_read_chip(unitp, ENVCTRL_PCF8591, EHC_DEV2,
	    0, buf, 4);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Temperature read failed (PDB)",
		    driver_name, unitp->instance);
		return (status);
	}

	warning_level = envctrl_check_tempr_levels(unitp, EHC_DEV2,
	    buf, warning_count);
	level = warning_level;

	if (warning_level != green) {
		if (warning_count == 0) {
			warning_count++;
			drv_usecwait(1000);
			goto retrytemp1;
		}
		if (warning_level == yellow)
			unitp->tempr_warning = B_TRUE;
		else if (warning_level == red) {
				unitp->tempr_warning = B_TRUE;
				if (!envctrl_power_off_overide)
					unitp->shutdown = B_TRUE;
		}
	}

	warning_count = 0;
retrytemp2:
	status = envctrl_read_chip(unitp, ENVCTRL_PCF8591, EHC_DEV7,
	    0, buf+4, 4);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Temperature read failed (MBD)",
		    driver_name, unitp->instance);
		return (status);
	}

	warning_level = envctrl_check_tempr_levels(unitp, EHC_DEV7,
	    buf+4, warning_count);

	if (warning_level != green) {
		if (warning_count == 0) {
			warning_count++;
			drv_usecwait(1000);
			goto retrytemp2;
		}
		if ((warning_level == yellow) && (unitp->shutdown == B_FALSE))
			unitp->tempr_warning = B_TRUE;
		else if (warning_level == red) {
				unitp->tempr_warning = B_TRUE;
				if (!envctrl_power_off_overide)
					unitp->shutdown = B_TRUE;
		}
	} else if ((level == green) && (unitp->tempr_warning == B_TRUE)) {
		/*
		 * Current tempr. poll shows all levels normal.
		 * If the previous poll showed warning levels, we need
		 * to clear that status
		 */
		cmn_err(CE_NOTE,
		"TEMPERATURE NORMAL: all sensors back to normal readings");
		unitp->tempr_warning = B_FALSE;
	}

	status = envctrl_get_fpm_status(unitp, &fspval);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN,
		    "%s%d: Read of Front Status Panel LEDs failed",
		    driver_name, unitp->instance);
	}

	if ((unitp->tempr_warning == B_TRUE) || (unitp->shutdown == B_TRUE))
		fspval |= (ENVCTRL_UE250_FSP_TEMP_ERR |
		    ENVCTRL_UE250_FSP_GEN_ERR);
	else {
		if (envctrl_isother_fault_led(unitp, fspval,
		    ENVCTRL_UE250_FSP_TEMP_ERR)) {
			fspval &= ~(ENVCTRL_UE250_FSP_TEMP_ERR);
		} else {
			fspval &= ~(ENVCTRL_UE250_FSP_TEMP_ERR |
			    ENVCTRL_UE250_FSP_GEN_ERR);
		}
	}
	status = envctrl_set_fsp(unitp, &fspval);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN,
		    "%s%d: Setting of Front Status Panel LEDs failed",
		    driver_name, unitp->instance);
	}

	/*
	 * Have this thread run again in about 10 seconds
	 */
	if (unitp->tempr_warning == B_TRUE) {
		if (unitp->timeout_id != 0) {
			(void) untimeout(unitp->timeout_id);
			unitp->timeout_id = (timeout(envctrl_tempr_poll,
			    (caddr_t)unitp, warning_timeout_hz));
		}
	}

	return (status);
}

static int
envctrl_check_tempr_levels(struct envctrlunit *unitp, int chip_num,
	uint8_t *data, int count)
{
	uint_t temp_degree_c;
	uint8_t buf[8];
	enum levels warning_level = green;
	int i, j;
	int status;
	uint8_t fanspeed;
	int tval;

	for (i = 0; i < 4; i++) {
		if (chip_num == EHC_DEV2) {
			if (i == 1) {
				tval = ((int)data[i] * JAV_FAN_SPEED_SF_NUM) /
				    JAV_FAN_SPEED_SF_DEN;
				if (tval > 255)
					unitp->fan_kstats.fanspeed = 255;
				else
					unitp->fan_kstats.fanspeed = tval;
				DPRINTF1("device %X, fan = %d %d\n", chip_num,
				    unitp->fan_kstats.fanspeed, data[i]);
				continue;
			} else if (i == 2)
				continue;
		}
		if ((chip_num == EHC_DEV7) && ((i == ENVCTRL_UE250_CPU0_PORT) ||
		    (i == ENVCTRL_UE250_CPU1_PORT)))
			if (unitp->cpu_pr_location[i] == B_FALSE)
				continue;

		j = 0;
		while ((((t_addr[j] & 0xF) != chip_num) || (t_port[j] != i)) &&
		    (j < unitp->num_temps_present))
			j++;
		if ((chip_num == EHC_DEV7) && ((i == ENVCTRL_UE250_CPU0_PORT) ||
		    (i == ENVCTRL_UE250_CPU1_PORT)))
			temp_degree_c = _cpu_temps[data[i]];
		else
			temp_degree_c = ((int)data[i] * t_scale_num[j]) /
			    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.
		 */
		if ((chip_num == EHC_DEV7) && ((i == ENVCTRL_UE250_CPU0_PORT) ||
		    (i == ENVCTRL_UE250_CPU1_PORT)) &&
		    (unitp->current_mode == ENVCTRL_NORMAL_MODE)) {
			if (_cpu_fan_speeds[data[ENVCTRL_UE250_CPU0_PORT]] >
			    _cpu_fan_speeds[data[ENVCTRL_UE250_CPU1_PORT]])
				fanspeed =
				    _cpu_fan_speeds[
				    data[ENVCTRL_UE250_CPU0_PORT]];
			else
				fanspeed =
				    _cpu_fan_speeds[
				    data[ENVCTRL_UE250_CPU1_PORT]];
			status = envctrl_write_chip(unitp, ENVCTRL_PCF8591,
			    EHC_DEV2, 0, &fanspeed, 1);
			if (status == DDI_FAILURE)
				cmn_err(CE_WARN,
				    "%s%d: Write to PCF8591 (SETFAN) failed\n",
				    driver_name, unitp->instance);
			status = envctrl_read_chip(unitp, ENVCTRL_PCF8591,
			    EHC_DEV2, 0, buf, 4);
			if (status == DDI_FAILURE)
				cmn_err(CE_WARN,
				    "%s%d: Fan speed read failed (PDB)",
				    driver_name, unitp->instance);
			tval = ((int)buf[1] * JAV_FAN_SPEED_SF_NUM) /
			    JAV_FAN_SPEED_SF_DEN;
			if (tval > 255)
				unitp->fan_kstats.fanspeed = 255;
			else
				unitp->fan_kstats.fanspeed = tval;
		}

		DPRINTF1("device %X, temp = %d %d loc = %s\n", chip_num,
		    temp_degree_c, data[i], unitp->temp_kstats[j].label);

		unitp->temp_kstats[j].value = temp_degree_c;
		if ((temp_degree_c >=
		    unitp->temp_kstats[j].warning_threshold) ||
		    (temp_degree_c < unitp->temp_kstats[j].min)) {
			if (warning_level < yellow)
				warning_level = yellow;
			if (count != 0)
				cmn_err(CE_WARN,
				    "TEMPERATURE WARNING: %d degrees "
				    "celsius at location %s",
				    temp_degree_c, unitp->temp_kstats[j].label);
		}
		if (temp_degree_c >=
		    unitp->temp_kstats[j].shutdown_threshold) {
			if (warning_level < red)
				warning_level = red;
			if (count != 0) {
				cmn_err(CE_WARN,
				    "TEMPERATURE CRITICAL: %d "
				    "degrees celsius at location %s",
				    temp_degree_c, unitp->temp_kstats[j].label);
				if (!envctrl_power_off_overide)
					cmn_err(CE_WARN,
					    "System shutdown in "
					    "10 seconds ...");
			}
		}
	}
	return (warning_level);
}

static void
envctrl_update_fanspeed(struct envctrlunit *unitp)
{
	uint8_t buf[8];
	int tval;
	int status;

	status = envctrl_read_chip(unitp, ENVCTRL_PCF8591, EHC_DEV2,
	    0, buf, 4);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Fan speed read failed ",
		    driver_name, unitp->instance);
	}

	tval = ((int)buf[ENVCTRL_PORT1] * JAV_FAN_SPEED_SF_NUM) /
	    JAV_FAN_SPEED_SF_DEN;
	if (tval > 255)
		unitp->fan_kstats.fanspeed = 255;
	else
		unitp->fan_kstats.fanspeed = tval;
}

/* called with mutex held */
static void
envctrl_fan_fail_service(struct envctrlunit *unitp)
{
	uint8_t recv_data, fpmstat;
	int retrys = 0;
	int status;

	/*
	 * The fan fail interrupt is read from address 0x70
	 * on the envctrl bus.
	 */

	ASSERT(MUTEX_HELD(&unitp->umutex));

	/*
	 * Clear the interrupt latches to handle spurious interrupts
	 */
	envctrl_intr_latch_clr(unitp);

	do {
		status = ehc_read_pcf8574a((struct ehc_envcunit *)unitp,
		    ENVCTRL_UE250_PCF8574A_BASE_ADDR | EHC_DEV0,
		    &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) {
			status = ehc_read_pcf8574a((struct ehc_envcunit *)unitp,
			    ENVCTRL_UE250_PCF8574A_BASE_ADDR | EHC_DEV0,
			    &recv_data, 1);
		}

		if (status == DDI_FAILURE) {
			drv_usecwait(1000);
			if (retrys < envctrl_max_retries) {
				retrys++;
			} else {
				cmn_err(CE_WARN,
				"%s%d: Read of PCF8574A (INTFAN) failed",
				    driver_name, unitp->instance);
				ehc_init_pcf8584((struct ehc_envcunit *)unitp);
				return;
			}
		}
	} while (status != DDI_SUCCESS);

	/* If the fan fail interrupt is now absent */
	if (recv_data & EHC_PCF8574_PORT4) {
		if (unitp->fan_failed == B_TRUE) {
			if (unitp->current_mode == ENVCTRL_NORMAL_MODE)
				cmn_err(CE_CONT,
				    "Fan failure has been cleared\n");
			unitp->fan_kstats.fans_ok = B_TRUE;
			/*
			 * Clear general fault LED if no other faults
			 */
			status = envctrl_get_fpm_status(unitp, &fpmstat);
			if (status == DDI_FAILURE) {
				cmn_err(CE_WARN,
				    "%s%d: Read of Front Status "
				    "Panel LEDs failed",
				    driver_name, unitp->instance);
			}
			if (!(envctrl_isother_fault_led(unitp, fpmstat, 0))) {
				fpmstat &= ~(ENVCTRL_UE250_FSP_GEN_ERR);
			}
			if (unitp->shutdown != B_TRUE) {
				status = envctrl_set_fsp(unitp, &fpmstat);
				if (status == DDI_FAILURE) {
					cmn_err(CE_WARN, "%s%d: "
					    "Setting of Front Status "
					    "Panel LEDs failed",
					    driver_name, unitp->instance);
				}
			}
			/*
			 * This should be set after envctrl_isother_fault_led()
			 * is called
			 */
			unitp->fan_failed = B_FALSE;
		}
	} else {
		if (unitp->fan_failed == B_FALSE) {
			if (unitp->current_mode == ENVCTRL_NORMAL_MODE)
				cmn_err(CE_WARN,
				    "Fan failure has been detected");
			unitp->fan_failed = B_TRUE;
			unitp->fan_kstats.fans_ok = B_FALSE;
			/*
			 * Set general fault LED
			 */
			status = envctrl_get_fpm_status(unitp, &fpmstat);
			if (status == DDI_FAILURE) {
				cmn_err(CE_WARN,
				    "%s%d: Read of Front Status "
				    "Panel LEDs failed",
				    driver_name, unitp->instance);
				return;
			}
			fpmstat |= ENVCTRL_UE250_FSP_GEN_ERR;
			status = envctrl_set_fsp(unitp, &fpmstat);
			if (status == DDI_FAILURE) {
				cmn_err(CE_WARN, "%s%d: "
				    "Setting of Front Status Panel LEDs failed",
				    driver_name, unitp->instance);
			}
			/*
			 * A fan failure condition exists.
			 * Temperature poll thread should run every 10 seconds.
			 */
			if (unitp->timeout_id != 0) {
				(void) untimeout(unitp->timeout_id);
				unitp->timeout_id =
				    (timeout(envctrl_tempr_poll,
				    (caddr_t)unitp, warning_timeout_hz));
			}
		}
	}
}

/*
 * 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
envctrl_PS_intr_service(struct envctrlunit *unitp)
{

	ASSERT(MUTEX_HELD(&unitp->umutex));

	if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
		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
	 */

	unitp->pshotplug_id = (timeout(envctrl_pshotplug_poll,
	    (caddr_t)unitp, pshotplug_timeout_hz));

}

static void
envctrl_init_bus(struct envctrlunit *unitp)
{
	ehc_init_pcf8584((struct ehc_envcunit *)unitp);

	/*
	 * Clear the interrupt latches
	 */
	envctrl_intr_latch_clr(unitp);

	envctrl_reset_dflop(unitp);

	envctrl_enable_devintrs(unitp);
}

/* called with mutex held */
static void
envctrl_reset_dflop(struct envctrlunit *unitp)
{
	int status;
	uint8_t value;

	ASSERT(MUTEX_HELD(&unitp->umutex));

	value = ENVCTRL_UE250_DFLOP_INIT0;
	status = envctrl_write_chip(unitp, ENVCTRL_PCF8574A, EHC_DEV0,
	    0, &value, 1);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Write to PCF8574A (DFLOP_INIT0) failed",
		    driver_name, unitp->instance);
	}

	value = ENVCTRL_UE250_DFLOP_INIT1;
	status = envctrl_write_chip(unitp, ENVCTRL_PCF8574A, EHC_DEV0,
	    0, &value, 1);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Write to PCF8574A (DFLOP_INIT1) failed",
		    driver_name, unitp->instance);
	}
}

/* called with mutex held */
static void
envctrl_enable_devintrs(struct envctrlunit *unitp)
{
	int status;
	uint8_t value;

	ASSERT(MUTEX_HELD(&unitp->umutex));

	value = ENVCTRL_UE250_DEVINTR_INIT0;
	status = envctrl_write_chip(unitp, ENVCTRL_PCF8574A, EHC_DEV0,
	    0, &value, 1);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Write to PCF8574A (INTR_INIT0) failed",
		    driver_name, unitp->instance);
	}

	value = ENVCTRL_UE250_DEVINTR_INIT1;
	status = envctrl_write_chip(unitp, ENVCTRL_PCF8574A, EHC_DEV0,
	    0, &value, 1);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Write to PCF8574A (INTR_INIT1) failed",
		    driver_name, unitp->instance);
	}
}

static void
envctrl_intr_latch_clr(struct envctrlunit *unitp)
{
	int status;
	uint8_t value;

	ASSERT(MUTEX_HELD(&unitp->umutex));

	value = ENVCTRL_UE250_INTR_LATCH_INIT0;
	status = envctrl_write_chip(unitp, ENVCTRL_PCF8574A, EHC_DEV0,
	    0, &value, 1);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Write to PCF8574A (INTR_LATCH0) failed",
		    driver_name, unitp->instance);
	}

	value = ENVCTRL_UE250_INTR_LATCH_INIT1;
	status = envctrl_write_chip(unitp, ENVCTRL_PCF8574A, EHC_DEV0,
	    0, &value, 1);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Write to PCF8574A (INTR_LATCH1) failed",
		    driver_name, unitp->instance);
	}
}

/* Called with unitp mutex held */
static void
envctrl_ps_probe(struct envctrlunit *unitp)
{

	uint8_t recv_data, fpmstat;
	int i, j;
	int ps_error = 0, ps_present_port, power_ok_port;
	int status;


	ASSERT(MUTEX_HELD(&unitp->umutex));

	unitp->num_ps_present = 0;

	status = envctrl_read_chip(unitp, ENVCTRL_PCF8574A, EHC_DEV1,
	    0, &recv_data, 1);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Read of PCF8574 (PS) failed",
		    driver_name, unitp->instance);
		return;
	}

	for (i = 0, j = 0; i < ENVCTRL_UE250_MAXPS; i++) {
		unitp->ps_kstats[i].slot = -1;

		/*
		 * 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:
			ps_present_port = EHC_PCF8574_PORT0;
			power_ok_port = EHC_PCF8574_PORT4;
			break;
		case 1:
			ps_present_port = EHC_PCF8574_PORT1;
			power_ok_port = EHC_PCF8574_PORT5;
			break;
		}

		if (!(recv_data & ps_present_port)) {
			/* update unit kstat array */
			unitp->ps_kstats[j].slot = i;
			++unitp->num_ps_present;

			if (pspr[i] == 0) {
				cmn_err(CE_NOTE,
				    "Power Supply %d inserted\n", i);
			}
			pspr[i] = 1;

			if (!(recv_data & power_ok_port)) {
				cmn_err(CE_WARN,
				    "Power Supply %d NOT okay\n", i);
				unitp->ps_kstats[j].ps_ok = B_FALSE;
				ps_error++;
				psok[i] = 0;
			} else {
				unitp->ps_kstats[j].ps_ok = B_TRUE;
				if (psok[i] == 0)
					cmn_err(CE_NOTE,
					    "Power Supply %d okay\n", i);
				psok[i] = 1;
			}

			if (!(recv_data & EHC_PCF8574_PORT2)) {
				cmn_err(CE_WARN,
				    "PS %d Shouln't interrupt\n", i);
				ps_error++;
			}

			if (!(recv_data & EHC_PCF8574_PORT3)) {
				cmn_err(CE_WARN,
				    "PS %d Shouln't interrupt\n", i);
				ps_error++;
			}

			if (!(recv_data & EHC_PCF8574_PORT6)) {
				cmn_err(CE_WARN,
				    "PS %d Shouln't interrupt\n", i);
				ps_error++;
			}

			if (!(recv_data & EHC_PCF8574_PORT7)) {
				cmn_err(CE_WARN,
				    "PS %d Shouln't interrupt\n", i);
				ps_error++;
			}
			j++;
		} else {
			if (pspr[i] == 1) {
				cmn_err(CE_NOTE,
				    "Power Supply %d removed\n", i);
			}
			pspr[i] = 0;
		}
	}

	status = envctrl_get_fpm_status(unitp, &fpmstat);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Read of Front Status Panel LEDs failed",
		    driver_name, unitp->instance);
	}
	if (ps_error) {
		fpmstat |= (ENVCTRL_UE250_FSP_PS_ERR |
		    ENVCTRL_UE250_FSP_GEN_ERR);
	} else {
		if (envctrl_isother_fault_led(unitp, fpmstat,
		    ENVCTRL_UE250_FSP_PS_ERR)) {
			fpmstat &= ~(ENVCTRL_UE250_FSP_PS_ERR);
		} else {
			fpmstat &= ~(ENVCTRL_UE250_FSP_PS_ERR |
			    ENVCTRL_UE250_FSP_GEN_ERR);
		}
	}
	status = envctrl_set_fsp(unitp, &fpmstat);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN,
		    "%s%d: Setting of Front Status Panel LEDs failed",
		    driver_name, unitp->instance);
	}

	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
envctrl_abort_seq_handler(char *msg)
{
	struct envctrlunit *unitp;
	int i;
	uint8_t secure = 0;

	/*
	 * 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 *)
		    ddi_get_soft_state(envctrlsoft_statep, i))
			break;
	}

	ASSERT(unitp);

	secure = unitp->encl_kstats.value;

	if ((secure & ENVCTRL_UE250_FSP_KEYMASK) ==
	    ENVCTRL_UE250_FSP_KEYLOCKED) {
			cmn_err(CE_CONT,
			    "%s%d: ignoring debug enter sequence\n",
			    driver_name, unitp->instance);
	} else {
		if (envctrl_debug_flags) {
			cmn_err(CE_CONT, "%s%d: allowing debug enter\n",
			    driver_name, unitp->instance);
		}
		debug_enter(msg);
	}
}

/*
 * 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
envctrl_get_fpm_status(struct envctrlunit *unitp, uint8_t *val)
{
	uint8_t recv_data;
	int status;

	ASSERT(MUTEX_HELD(&unitp->umutex));

	status = envctrl_read_chip(unitp, ENVCTRL_PCF8574A, EHC_DEV6,
	    0, &recv_data, 1);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Read from PCF8574A (FSP) failed",
		    driver_name, unitp->instance);
		return (status);
	}

	recv_data = ~recv_data;
	if (val != (uint8_t *)NULL)
		*val = recv_data;

	/* Update kstats */
	unitp->encl_kstats.value = recv_data;

	return (status);
}

static int
envctrl_set_fsp(struct envctrlunit *unitp, uint8_t *val)
{
	uint8_t value;
	int status = DDI_SUCCESS;
	uint8_t confirm_val = 0, confirm_val_hold;
	int confirm_count = 0, confirm_max = 20;

	ASSERT(MUTEX_HELD(&unitp->umutex));

	value = ENVCTRL_UE250_FSP_OFF; /* init all values to off */

	/*
	 * strip off bits that are R/O
	 */
	value = (~(ENVCTRL_UE250_FSP_KEYMASK | ENVCTRL_UE250_FSP_POMASK) &
	    (*val));

	confirm_val_hold = value;

	value = ~value;

	while (confirm_count < confirm_max) {
		status = envctrl_write_chip(unitp, ENVCTRL_PCF8574A, EHC_DEV6,
		    0, &value, 1);
		if (status == DDI_FAILURE) {
			cmn_err(CE_WARN, "%s%d: Write to PCF8574A (FSP) failed",
			    driver_name, unitp->instance);
			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.
			 */
			status = envctrl_get_fpm_status(unitp, &confirm_val);
			confirm_val = ~(ENVCTRL_UE250_FSP_KEYMASK |
			    ENVCTRL_UE250_FSP_POMASK) & confirm_val;
			if (status == DDI_FAILURE) {
				cmn_err(CE_WARN,
				"%s%d: Read of PCF8574A (FSP) failed",
				    driver_name, unitp->instance);
				break;
			} else if (confirm_val != confirm_val_hold) {
				confirm_count++;
				drv_usecwait(1000);
				continue;
			} else
				/*
				 * Set was confirmed.
				 */
				break;
		}
	}

	if (confirm_count == confirm_max)
		status = DDI_FAILURE;

	return (status);

}

static int
envctrl_get_dskled(struct envctrlunit *unitp, struct envctrl_chip *chip)
{
	int status;

	ASSERT(MUTEX_HELD(&unitp->umutex));

	if (chip->chip_num != EHC_DEV7 ||
	    chip->type != ENVCTRL_PCF8574A) {
		return (DDI_FAILURE);
	}

	status = envctrl_read_chip(unitp, ENVCTRL_PCF8574A, EHC_DEV7,
	    0, &chip->val, 1);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Read of PCF8574A (DISKFL) failed",
		    driver_name, unitp->instance);
	}
	chip->val = ~chip->val;

	return (status);
}

static int
envctrl_set_dskled(struct envctrlunit *unitp, struct envctrl_chip *chip)
{
	uint8_t val;
	int status;
	struct envctrl_chip confirm_chip;
	uint8_t confirm_val_hold;
	int confirm_count = 0, confirm_max = 20;

	/*
	 * 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.
	 */
	ASSERT(MUTEX_HELD(&unitp->umutex));


	if (chip->chip_num != EHC_DEV7)
		return (DDI_FAILURE);

	if (chip->type != ENVCTRL_PCF8574A)
		return (DDI_FAILURE);

	status = envctrl_read_chip(unitp, ENVCTRL_PCF8574A, EHC_DEV6,
	    0, &val, 1);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Read of PCF8574A (FSP) failed",
		    driver_name, unitp->instance);
		return (status);
	}

	val = ~val;
	if ((chip->val & 0x3F) == 0) {
		if (!(envctrl_isother_fault_led(unitp, val,
		    ENVCTRL_UE250_FSP_DISK_ERR))) {
			val &= ~(ENVCTRL_UE250_FSP_DISK_ERR);
		} else {
			val &= ~(ENVCTRL_UE250_FSP_DISK_ERR |
			    ENVCTRL_UE250_FSP_GEN_ERR);
		}
		val = (val & ~(ENVCTRL_UE250_FSP_DISK_ERR |
		    ENVCTRL_UE250_FSP_GEN_ERR));
	} else {
		val = (val | (ENVCTRL_UE250_FSP_DISK_ERR |
		    ENVCTRL_UE250_FSP_GEN_ERR));
	}

	status = envctrl_set_fsp(unitp, &val);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Write to PCF8574A (FSP) failed",
		    driver_name, unitp->instance);
		return (status);
	}


	status = envctrl_read_chip(unitp, ENVCTRL_PCF8574A, EHC_DEV5,
	    0, &val, 1);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Read of PCF8574A (DISKFL) failed",
		    driver_name, unitp->instance);
		return (status);
	}

	envctrl_update_disk_kstats(unitp, val, ~(chip->val));

	/*
	 * 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. ;-)
	 */

	confirm_val_hold = chip->val;

	chip->val = ~(chip->val);

	while (confirm_count < confirm_max) {
		status = envctrl_write_chip(unitp, ENVCTRL_PCF8574A, EHC_DEV7,
		    0, &chip->val, 1);
		if (status == DDI_FAILURE) {
			cmn_err(CE_WARN, "%s%d: Write PCF8574A (DISKFL) failed",
			    driver_name, unitp->instance);
			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.
			 */
			confirm_chip.type = chip->type;
			confirm_chip.chip_num = chip->chip_num;
			confirm_chip.index = chip->index;
			status = envctrl_get_dskled(unitp, &confirm_chip);
			if (status != DDI_SUCCESS) {
				return (status);
			} else if (confirm_chip.val != confirm_val_hold) {
				confirm_count++;
				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
envctrl_set_fanspeed(struct envctrlunit *unitp, struct envctrl_chip *fanspeed)
{
	int readback_speed, max_speed;
	int status;
	int confirm_count = 0, confirm_max = 20;
	uint8_t fanspeed_hold;

	fanspeed_hold = fanspeed->val;
	while (confirm_count < confirm_max) {
		status = envctrl_write_chip(unitp, ENVCTRL_PCF8591,
		    EHC_DEV2, 0, &fanspeed->val, 1);
		if (status == DDI_FAILURE) {
			envctrl_fan_fail_service(unitp);
			cmn_err(CE_WARN,
			"%s%d: Set fanspeed failed", driver_name,
			    unitp->instance);
			return (status);
		} else {
			drv_usecwait(100000);
			envctrl_update_fanspeed(unitp);
			readback_speed = unitp->fan_kstats.fanspeed;
			if (fanspeed_hold > idle_fanspeed) {
				max_speed =
				    (fanspeed->val + FAN_DRIFT >
				    MAX_FAN_SPEED) ?  MAX_FAN_SPEED :
				    (fanspeed->val + FAN_DRIFT);
				if ((readback_speed < fanspeed->val -
				    FAN_DRIFT) ||
				    (readback_speed > max_speed)) {
					confirm_count++;
					drv_usecwait(1000);
					continue;
				}
			}
			break;
		}
	}

	if (confirm_count == confirm_max)
		return (DDI_FAILURE);

	return (DDI_SUCCESS);
}

static void
envctrl_add_kstats(struct envctrlunit *unitp)
{

	ASSERT(MUTEX_HELD(&unitp->umutex));

	if ((unitp->enclksp = kstat_create(ENVCTRL_MODULE_NAME, unitp->instance,
	    ENVCTRL_KSTAT_ENCL, "misc", KSTAT_TYPE_RAW,
	    sizeof (unitp->encl_kstats),
	    KSTAT_FLAG_PERSISTENT)) == NULL) {
		cmn_err(CE_WARN, "%s%d: encl raw kstat_create failed",
		    driver_name, unitp->instance);
		return;
	}

	unitp->enclksp->ks_update = envctrl_encl_kstat_update;
	unitp->enclksp->ks_private = (void *)unitp;
	kstat_install(unitp->enclksp);


	if ((unitp->fanksp = kstat_create(ENVCTRL_MODULE_NAME, unitp->instance,
	    ENVCTRL_KSTAT_FANSTAT, "misc", KSTAT_TYPE_RAW,
	    sizeof (unitp->fan_kstats),
	    KSTAT_FLAG_PERSISTENT | KSTAT_FLAG_WRITABLE)) == NULL) {
		cmn_err(CE_WARN, "%s%d: fans kstat_create failed",
		    driver_name, unitp->instance);
		return;
	}

	unitp->fanksp->ks_update = envctrl_fanstat_kstat_update;
	unitp->fanksp->ks_private = (void *)unitp;
	kstat_install(unitp->fanksp);

	if ((unitp->psksp = kstat_create(ENVCTRL_MODULE_NAME, unitp->instance,
	    ENVCTRL_KSTAT_PSNAME2, "misc", KSTAT_TYPE_RAW,
	    sizeof (unitp->ps_kstats),
	    KSTAT_FLAG_PERSISTENT)) == NULL) {
		cmn_err(CE_WARN, "%s%d: ps name kstat_create failed",
		    driver_name, unitp->instance);
		return;
	}

	unitp->psksp->ks_update = envctrl_ps_kstat_update;
	unitp->psksp->ks_private = (void *)unitp;
	kstat_install(unitp->psksp);

	if ((unitp->tempksp = kstat_create(ENVCTRL_MODULE_NAME,
	    unitp->instance, ENVCTRL_KSTAT_TEMPERATURE, "misc", KSTAT_TYPE_RAW,
	    sizeof (unitp->temp_kstats),
	    KSTAT_FLAG_PERSISTENT)) == NULL) {
		cmn_err(CE_WARN, "%s%d: temp name kstat_create failed",
		    driver_name, unitp->instance);
		return;
	}

	unitp->tempksp->ks_update = envctrl_temp_kstat_update;
	unitp->tempksp->ks_private = (void *)unitp;
	kstat_install(unitp->tempksp);

	if ((unitp->diskksp = kstat_create(ENVCTRL_MODULE_NAME,
	    unitp->instance, ENVCTRL_KSTAT_DISK, "misc", KSTAT_TYPE_RAW,
	    sizeof (unitp->disk_kstats),
	    KSTAT_FLAG_PERSISTENT)) == NULL) {
		cmn_err(CE_WARN, "%s%d: disk name kstat_create failed",
		    driver_name, unitp->instance);
		return;
	}

	unitp->diskksp->ks_update = envctrl_disk_kstat_update;
	unitp->diskksp->ks_private = (void *)unitp;
	kstat_install(unitp->diskksp);

}

static int
envctrl_ps_kstat_update(kstat_t *ksp, int rw)
{
	struct envctrlunit *unitp;
	char *kstatp;



	unitp = (struct envctrlunit *)ksp->ks_private;

	mutex_enter(&unitp->umutex);
	ASSERT(MUTEX_HELD(&unitp->umutex));

	kstatp = (char *)ksp->ks_data;

	if (rw == KSTAT_WRITE) {
		mutex_exit(&unitp->umutex);
		return (EACCES);
	} else {

		unitp->psksp->ks_ndata = unitp->num_ps_present;
		bcopy((caddr_t)&unitp->ps_kstats, kstatp,
		    sizeof (unitp->ps_kstats));
	}
	mutex_exit(&unitp->umutex);
	return (DDI_SUCCESS);
}

static int
envctrl_fanstat_kstat_update(kstat_t *ksp, int rw)
{
	struct envctrlunit *unitp;
	char *kstatp;

	kstatp = (char *)ksp->ks_data;
	unitp = (struct envctrlunit *)ksp->ks_private;

	mutex_enter(&unitp->umutex);
	ASSERT(MUTEX_HELD(&unitp->umutex));

	if (rw == KSTAT_WRITE) {
		mutex_exit(&unitp->umutex);
		return (EACCES);
	} else {
		unitp->fanksp->ks_ndata = unitp->num_fans_present;
		bcopy((caddr_t)&unitp->fan_kstats, kstatp,
		    sizeof (unitp->fan_kstats));
	}
	mutex_exit(&unitp->umutex);
	return (DDI_SUCCESS);
}

static int
envctrl_encl_kstat_update(kstat_t *ksp, int rw)
{
	struct envctrlunit *unitp;
	char *kstatp;
	int status;


	kstatp = (char *)ksp->ks_data;
	unitp = (struct envctrlunit *)ksp->ks_private;

	mutex_enter(&unitp->umutex);
	ASSERT(MUTEX_HELD(&unitp->umutex));

	if (rw == KSTAT_WRITE) {
		mutex_exit(&unitp->umutex);
		return (EACCES);
	} else {

		unitp->enclksp->ks_ndata = unitp->num_encl_present;
		status = envctrl_get_fpm_status(unitp, (uint8_t *)NULL);
		if (status == DDI_SUCCESS)
			bcopy((caddr_t)&unitp->encl_kstats, kstatp,
			    sizeof (unitp->encl_kstats));
	}
	mutex_exit(&unitp->umutex);
	return (DDI_SUCCESS);
}

static int
envctrl_temp_kstat_update(kstat_t *ksp, int rw)
{
	struct envctrlunit *unitp;
	char *kstatp;

	kstatp = (char *)ksp->ks_data;
	unitp = (struct envctrlunit *)ksp->ks_private;

	mutex_enter(&unitp->umutex);
	ASSERT(MUTEX_HELD(&unitp->umutex));

	if (rw == KSTAT_WRITE) {
		mutex_exit(&unitp->umutex);
		return (EACCES);
	} else {
		unitp->tempksp->ks_ndata = unitp->num_temps_present;
		bcopy((caddr_t)unitp->temp_kstats, kstatp,
		    sizeof (unitp->temp_kstats));
	}
	mutex_exit(&unitp->umutex);
	return (DDI_SUCCESS);
}

static int
envctrl_disk_kstat_update(kstat_t *ksp, int rw)
{
	struct envctrlunit *unitp;
	char *kstatp;

	kstatp = (char *)ksp->ks_data;
	unitp = (struct envctrlunit *)ksp->ks_private;

	mutex_enter(&unitp->umutex);
	ASSERT(MUTEX_HELD(&unitp->umutex));

	if (rw == KSTAT_WRITE) {
		mutex_exit(&unitp->umutex);
		return (EACCES);
	} else {
		unitp->diskksp->ks_ndata = unitp->num_disks_present;
		bcopy((caddr_t)unitp->disk_kstats, kstatp,
		    sizeof (unitp->disk_kstats));
	}
	mutex_exit(&unitp->umutex);
	return (DDI_SUCCESS);
}

static void
envctrl_init_encl_kstats(struct envctrlunit *unitp)
{
	uint8_t val;
	int status;

	ASSERT(MUTEX_HELD(&unitp->umutex));

	status = envctrl_read_chip(unitp, ENVCTRL_PCF8574A, EHC_DEV6,
	    0, &val, 1);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Read of PCF8574A (FSP) failed",
		    driver_name, unitp->instance);
		return;
	}

	unitp->encl_kstats.value = val;
}

static void
envctrl_check_disk_kstats(struct envctrlunit *unitp)
{
	uint8_t diskpr, diskfl;
	int status;

	ASSERT(MUTEX_HELD(&unitp->umutex));

	status = envctrl_read_chip(unitp, ENVCTRL_PCF8574A, EHC_DEV5,
	    0, &diskpr, 1);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Read of PCF8574A (DISKPR) failed",
		    driver_name, unitp->instance);
	}

	status = envctrl_read_chip(unitp, ENVCTRL_PCF8574A, EHC_DEV7,
	    0, &diskfl, 1);
	if (status == DDI_FAILURE) {
		cmn_err(CE_WARN, "%s%d: Read of PCF8574A (DISKFL) failed",
		    driver_name, unitp->instance);
	}

	envctrl_update_disk_kstats(unitp, diskpr, diskfl);

}

static void
envctrl_update_disk_kstats(struct envctrlunit *unitp, uint8_t diskpr,
	uint8_t diskfl)
{
	int i, j, count = 0;

	DPRINTF1("diskpr = %X, diskfl = %X\n", diskpr, diskfl);
	for (i = 0, j = 1; i < ENVCTRL_UE250_MAX_DISKS; i++, j = j << 1) {
		if (!(diskpr & j)) {
			if (!(diskfl & j))
				unitp->disk_kstats[count].disk_ok = 0;
			else
				unitp->disk_kstats[count].disk_ok = 1;
			unitp->disk_kstats[count].slot = i;
			count++;
		}
	}

	unitp->num_disks_present = count;
}

static void
envctrl_probe_cpus(struct envctrlunit *unitp)
{
	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
	 */

	for (instance = 0; instance < ENVCTRL_MAX_CPUS; instance++) {
		unitp->cpu_pr_location[instance] = B_FALSE;
	}

	ddi_walk_devs(ddi_root_node(), envctrl_match_cpu, unitp);
}

static int
envctrl_match_cpu(dev_info_t *dip, void *arg)
{

	int cpu_slot;
	char name[32];
	char name1[32];
	struct envctrlunit *unitp = (struct envctrlunit *)arg;

	(void) sprintf(name, "%s", ENVCTRL_ULTRA1CPU_STRING);
	(void) sprintf(name1, "%s", ENVCTRL_ULTRA2CPU_STRING);

	if ((strcmp(ddi_node_name(dip), name) == 0) ||
	    (strcmp(ddi_node_name(dip), name1) == 0)) {
		if ((cpu_slot = (int)ddi_getprop(DDI_DEV_T_ANY, dip,
		    DDI_PROP_DONTPASS, "upa-portid",
		    -1)) == -1) {
			cmn_err(CE_WARN, "%s%d: no cpu upa-portid",
			    driver_name, unitp->instance);
		} else {
			unitp->cpu_pr_location[cpu_slot] = B_TRUE;
			unitp->num_cpus_present++;
		}
	}

	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
envctrl_isother_fault_led(struct envctrlunit *unitp, uint8_t fspval,
    uint8_t thisled)
{
	int status = B_FALSE;

	if (fspval != 0) {
		fspval = (fspval & ~(thisled));
	}
	if ((unitp->fan_failed == B_TRUE) && thisled != 0) {
		status = B_TRUE;
	} else if (fspval & ENVCTRL_UE250_FSP_DISK_ERR) {
		status = B_TRUE;
	} else if (fspval & ENVCTRL_UE250_FSP_PS_ERR) {
		status = B_TRUE;
	} else if (fspval & ENVCTRL_UE250_FSP_TEMP_ERR) {
		status = B_TRUE;
	}
	return (status);
}

static void
envctrl_pshotplug_poll(void *arg)
{
	struct envctrlunit *unitp = (struct envctrlunit *)arg;

	mutex_enter(&unitp->umutex);

	envctrl_ps_probe(unitp);

	mutex_exit(&unitp->umutex);
}