#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>
#include <sys/modctl.h>
#include <sys/stat.h>
#include <sys/open.h>
#include <sys/uio.h>
#include <sys/envctrl.h>
static int envctrl_open(queue_t *, dev_t *, int, int, cred_t *);
static int envctrl_close(queue_t *, int, cred_t *);
static uint_t envctrl_bus_isr(caddr_t);
static uint_t envctrl_dev_isr(caddr_t);
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);
static void envctrl_init_bus(struct envctrlunit *);
static int envctrl_xmit(struct envctrlunit *, caddr_t *, int);
static void envctrl_recv(struct envctrlunit *, caddr_t *, int);
static void envctrl_get_sys_temperatures(struct envctrlunit *, uint8_t *);
static int envctrl_get_lm75_temp(struct envctrlunit *);
static int envctrl_get_ps_temp(struct envctrlunit *, uint8_t);
static int envctrl_get_cpu_temp(struct envctrlunit *, int);
static void envctrl_fan_fail_service(struct envctrlunit *);
static void envctrl_PS_intr_service(struct envctrlunit *, uint8_t);
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_reset_dflop(struct envctrlunit *);
static void envctrl_enable_devintrs(struct envctrlunit *);
static void envctrl_stop_clock(struct envctrlunit *);
static void envctrl_reset_watchdog(struct envctrlunit *, uint8_t *);
static void envctrl_abort_seq_handler(char *msg);
static uint8_t envctrl_get_fpm_status(struct envctrlunit *);
static void envctrl_set_fsp(struct envctrlunit *, uint8_t *);
static int envctrl_set_dskled(struct envctrlunit *,
struct envctrl_pcf8574_chip *);
static int envctrl_get_dskled(struct envctrlunit *,
struct envctrl_pcf8574_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 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 void envctrl_init_fan_kstats(struct envctrlunit *);
static void envctrl_init_encl_kstats(struct envctrlunit *);
static void envctrl_add_encl_kstats(struct envctrlunit *, int, int,
uint8_t);
static void envctrl_mod_encl_kstats(struct envctrlunit *, int, int,
uint8_t);
static int envctrl_wput(queue_t *, mblk_t *);
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 envctrl_debug_flags = 0;
static int envctrl_afb_present = 0;
static int envctrl_power_off_overide = 0;
static int envctrl_max_retries = 100;
static int envctrl_allow_detach = 0;
static int envctrl_numcpus = 1;
static int envctrl_p0_enclosure = 0;
static int envctrl_handler = 1;
static clock_t overtemp_timeout_hz;
static clock_t blink_timeout_hz;
static clock_t pshotplug_timeout_hz;
static int controller_present[] = {-1, -1, -1};
#ifdef MULTIFAN
static int envctrl_fan_debug = 0;
#endif
static int eHc_debug = 0;
static int power_supply_previous_state[] = {-1, -1, -1};
extern void pci_thermal_rem_intr(dev_info_t *, uint_t);
#define LOOP_TIMEOUT 25
#define INIT_FAN_VAL 35
#define DCMNERR if (eHc_debug & 0x1) cmn_err
#define DCMN2ERR if (eHc_debug & 0x2) cmn_err
#define MAX_FAN_FAIL_RETRY 3
uint8_t backaddrs[] = {ENVCTRL_PCF8574_DEV0, ENVCTRL_PCF8574_DEV1,
ENVCTRL_PCF8574_DEV2};
struct module_info envctrlinfo = {
42, "envctrl", 0, 2048, (1024 * 20), (1024 * 1)
};
static struct qinit envctrl_rinit = {
putq, NULL, envctrl_open, envctrl_close, NULL, &envctrlinfo, NULL
};
static struct qinit envctrl_wint = {
envctrl_wput, NULL, envctrl_open, envctrl_close,
NULL, &envctrlinfo, NULL
};
struct streamtab envctrl_str_info = {
&envctrl_rinit, &envctrl_wint, NULL, NULL
};
static struct cb_ops envctrl_cb_ops = {
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
&envctrl_str_info,
D_MP
};
struct dev_ops envctrl_ops = {
DEVO_REV,
0,
envctrl_getinfo,
nulldev,
nulldev,
envctrl_attach,
envctrl_detach,
nodev,
&envctrl_cb_ops,
(struct bus_ops *)NULL,
nulldev,
ddi_quiesce_not_supported,
};
extern struct mod_ops mod_driverops;
static struct modldrv envctrlmodldrv = {
&mod_driverops,
"I2C ENVCTRL_driver",
&envctrl_ops,
};
static struct modlinkage envctrlmodlinkage = {
MODREV_1,
&envctrlmodldrv,
0
};
#define EHC_SUCCESS 0
#define EHC_FAILURE (-1)
#define EHC_NO_SLAVE_ACK 3
#define EHC_MAX_WAIT 7
#define EHC_S1_PIN 0x80
#define EHC_S1_ES1 0x20
#define EHC_S1_ES0 0x40
#define EHC_S1_NBB 0x01
#define EHC_S1_ACK 0x01
#define EHC_S1_STA 0x04
#define EHC_S1_STO 0x02
#define EHC_S1_LRB 0x08
#define EHC_S1_BER 0x10
#define EHC_S1_LAB 0x02
#define EHC_S0_OWN 0x55
#define EHC_S0_CLK 0x1c
#define EHC_BYTE_READ 0x01
#define EHC_LONGEST_MSG 1000
#define EHC_PCF8591_MAX_DEVS 0x08
#define EHC_DEV0 0x00
#define EHC_DEV1 0x02
#define EHC_DEV2 0x04
#define EHC_DEV3 0x06
#define EHC_DEV4 0x08
#define EHC_DEV5 0x0A
#define EHC_DEV6 0x0C
#define EHC_DEV7 0x0E
#define EHC_PCF8591_ANALOG_OUTPUT_EN 0x40
#define EHC_PCF8591_ANALOG_INPUT_EN 0x00
#define EHC_PCF8591_READ_BIT 0x01
#define EHC_PCF8591_AUTO_INCR 0x04
#define EHC_PCF8591_OSCILATOR 0x40
#define EHC_PCF8591_MAX_PORTS 0x04
#define EHC_PCF8591_CH_0 0x00
#define EHC_PCF8591_CH_1 0x01
#define EHC_PCF8591_CH_2 0x02
#define EHC_PCF8591_CH_3 0x03
#define EHC_PCF8574_PORT0 0x01
#define EHC_PCF8574_PORT1 0x02
#define EHC_PCF8574_PORT2 0x04
#define EHC_PCF8574_PORT3 0x08
#define EHC_PCF8574_PORT4 0x10
#define EHC_PCF8574_PORT5 0x20
#define EHC_PCF8574_PORT6 0x40
#define EHC_PCF8574_PORT7 0x80
#define EHC_PCF8583_READ_BIT 0x01
#define ALARM_CTR_REG_MINS 0x03
#define ALARM_REG_MINS 0x0B
#define ALARM_TIMER_REG 0x0F
struct eHc_pcd8584_regs {
uint8_t s0;
uint8_t s1;
uint8_t clock_s2;
};
struct eHc_envcunit {
struct eHc_pcd8584_regs *bus_ctl_regs;
ddi_acc_handle_t ctlr_handle;
kmutex_t umutex;
};
static int eHc_write_tda8444(struct eHc_envcunit *, int, int, int, uint8_t *,
int);
static int eHc_read_pcf8591(struct eHc_envcunit *, int, int, int, int, int,
uint8_t *, int);
static int eHc_read_pcf8574a(struct eHc_envcunit *, int, uint8_t *, int);
static int eHc_write_pcf8574a(struct eHc_envcunit *, int, uint8_t *, int);
static int eHc_read_pcf8574(struct eHc_envcunit *, int, uint8_t *, int);
static int eHc_write_pcf8574(struct eHc_envcunit *, int, uint8_t *, int);
static int eHc_read_lm75(struct eHc_envcunit *, int, uint8_t *, int);
static int eHc_write_pcf8583(struct eHc_envcunit *, int, uint8_t *, int);
static int eHc_start_pcf8584(struct eHc_envcunit *, uint8_t);
static void eHc_stop_pcf8584(struct eHc_envcunit *);
static int eHc_read_pcf8584(struct eHc_envcunit *, uint8_t *);
static int eHc_write_pcf8584(struct eHc_envcunit *, uint8_t);
static int eHc_after_read_pcf8584(struct eHc_envcunit *, uint8_t *);
int
_init(void)
{
int error;
if ((error = mod_install(&envctrlmodlinkage)) == 0) {
(void) ddi_soft_state_init(&envctrlsoft_statep,
sizeof (struct envctrlunit), 1);
}
return (error);
}
int
_fini(void)
{
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)
{
int instance;
char name[16];
uint8_t fspval;
struct envctrlunit *unitp;
struct ddi_device_acc_attr attr;
int *reg_prop;
uchar_t *creg_prop;
uint_t len, tblsz;
int i, cputemp, status;
uint8_t buf[3];
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;
mutex_exit(&unitp->umutex);
unitp->initting = B_TRUE;
envctrl_init_bus(unitp);
unitp->initting = B_FALSE;
mutex_enter(&unitp->umutex);
envctrl_ps_probe(unitp);
envctrl_probe_cpus(unitp);
mutex_exit(&unitp->umutex);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
overtemp_timeout_hz = drv_usectohz(OVERTEMP_TIMEOUT_USEC);
blink_timeout_hz = drv_usectohz(BLINK_TIMEOUT_USEC);
pshotplug_timeout_hz = drv_usectohz(BLINK_TIMEOUT_USEC * 6);
if (ddi_soft_state_zalloc(envctrlsoft_statep, instance) != 0) {
cmn_err(CE_WARN, "envctrl failed to zalloc softstate\n");
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 envctrl_pcd8584_regs), &attr,
&unitp->ctlr_handle) != DDI_SUCCESS) {
cmn_err(CE_WARN, "I2c failed to map in bus_control regs\n");
return (DDI_FAILURE);
}
pci_thermal_rem_intr(dip, (uint_t)0);
if (ddi_get_iblock_cookie(dip, 1,
&unitp->ic_trap_cookie) != DDI_SUCCESS) {
cmn_err(CE_WARN, "ddi_get_iblock_cookie FAILED \n");
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, "envctrl_attach failed to add hard intr %d\n",
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, "envctrl_attach failed to add hard intr %d\n",
instance);
goto remhardintr;
}
(void) sprintf(name, "envctrl%d", instance);
if (ddi_create_minor_node(dip, name, S_IFCHR, instance, DDI_PSEUDO,
0) == DDI_FAILURE) {
ddi_remove_minor_node(dip, NULL);
goto remhardintr1;
}
mutex_enter(&unitp->umutex);
switch (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
ENVCTRL_LED_BLINK, -1)) {
case 1:
unitp->activity_led_blink = B_TRUE;
break;
case 0:
default:
unitp->activity_led_blink = B_FALSE;
break;
}
unitp->shutdown = B_FALSE;
unitp->num_ps_present = unitp->num_encl_present = 0;
unitp->num_fans_present = MIN_FAN_BANKS;
unitp->num_fans_failed = ENVCTRL_CHAR_ZERO;
unitp->AFB_present = B_TRUE;
unitp->dip = dip;
#ifdef DEBUG
if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, ENVCTRL_PANEL_LEDS_PR,
®_prop, &len) == DDI_PROP_SUCCESS)
ddi_prop_free((void *)reg_prop);
ASSERT(len != 0);
len = 0;
if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, ENVCTRL_PANEL_LEDS_STA,
®_prop, &len) == DDI_PROP_SUCCESS)
ddi_prop_free((void *)reg_prop);
ASSERT(len != 0);
len = 0;
if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, ENVCTRL_DISK_LEDS_STA,
®_prop, &len) == DDI_PROP_SUCCESS)
ddi_prop_free((void *)reg_prop);
ASSERT(len != 0);
#endif
if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "cpu-fan-speeds",
&creg_prop, &len) == DDI_PROP_SUCCESS) {
tblsz = (sizeof (acme_cpu_fanspd) / sizeof (short));
if (len <= tblsz) {
for (i = 0; i < len; i++) {
acme_cpu_fanspd[i] = creg_prop[i];
}
}
ddi_prop_free((void *)creg_prop);
}
len = 0;
if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "ps-fan-speeds",
&creg_prop, &len) == DDI_PROP_SUCCESS) {
tblsz = (sizeof (acme_ps_fanspd) / sizeof (short));
if (len <= tblsz) {
for (i = 0; i < len; i++) {
acme_ps_fanspd[i] = creg_prop[i];
}
}
ddi_prop_free((void *)creg_prop);
}
switch (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"fan-override", -1)) {
case 1:
case 2:
unitp->AFB_present = B_TRUE;
break;
case 0:
default:
unitp->AFB_present = B_FALSE;
break;
}
if (envctrl_afb_present) {
unitp->AFB_present = B_TRUE;
}
if (unitp->AFB_present == B_TRUE)
unitp->num_fans_present++;
mutex_exit(&unitp->umutex);
unitp->initting = B_TRUE;
envctrl_init_bus(unitp);
unitp->initting = B_FALSE;
drv_usecwait(1000);
mutex_enter(&unitp->umutex);
buf[0] = ALARM_CTR_REG_MINS;
buf[1] = 0x0;
status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
PCF8583_BASE_ADDR | 0, buf, 2);
if (status != DDI_SUCCESS)
cmn_err(CE_WARN, "write to PCF8583 failed\n");
buf[0] = ALARM_REG_MINS;
buf[1] = 0x58;
status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
PCF8583_BASE_ADDR | 0, buf, 2);
if (status != DDI_SUCCESS)
cmn_err(CE_WARN, "write to PCF8583 failed\n");
buf[0] = ALARM_TIMER_REG;
buf[1] = 0x80;
status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
PCF8583_BASE_ADDR | 0, buf, 2);
if (status != DDI_SUCCESS)
cmn_err(CE_WARN, "write to PCF8583 failed\n");
unitp->timeout_id = 0;
unitp->blink_timeout_id = 0;
if (envctrl_numcpus > 1) {
unitp->num_cpus_present = envctrl_numcpus;
}
envctrl_probe_cpus(unitp);
envctrl_ps_probe(unitp);
unitp->initting = B_TRUE;
envctrl_fan_fail_service(unitp);
unitp->initting = B_FALSE;
envctrl_add_kstats(unitp);
envctrl_init_fan_kstats(unitp);
envctrl_init_encl_kstats(unitp);
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_FSP_ACTIVE;
envctrl_set_fsp(unitp, &fspval);
}
#ifndef TESTBED
for (i = 0; i < ENVCTRL_MAX_CPUS; i++) {
if (unitp->cpu_pr_location[i] == B_TRUE) {
cputemp = envctrl_get_cpu_temp(unitp, i);
envctrl_add_encl_kstats(unitp, ENVCTRL_ENCL_CPUTEMPR,
i, cputemp);
if (cputemp >= MAX_CPU_TEMP) {
if (!(envctrl_power_off_overide)) {
cmn_err(CE_WARN,
"CPU %d OVERHEATING!!", i);
unitp->shutdown = B_TRUE;
} else {
cmn_err(CE_WARN,
"CPU %d OVERHEATING!!", i);
}
}
}
}
#else
cputemp = envctrl_get_cpu_temp(unitp, 0);
envctrl_add_encl_kstats(unitp, ENVCTRL_ENCL_CPUTEMPR, INSTANCE_0,
cputemp);
#endif
mutex_exit(&unitp->umutex);
envctrl_tempr_poll((void *)unitp);
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, "envctrl_attach:failed.\n");
return (DDI_FAILURE);
}
static int
envctrl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
int instance;
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->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, "envctrl already suspended\n");
mutex_exit(&unitp->umutex);
return (DDI_FAILURE);
}
unitp->suspended = 1;
mutex_exit(&unitp->umutex);
return (DDI_SUCCESS);
default:
cmn_err(CE_WARN, "envctrl suspend general fault\n");
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 ret;
minor_t instance = getminor(dev);
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);
}
static int
envctrl_open(queue_t *q, dev_t *dev, int flag, int sflag, cred_t *credp)
{
struct envctrlunit *unitp;
int status = 0;
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);
if (flag & FWRITE) {
if ((unitp->oflag & FWRITE)) {
mutex_exit(&unitp->umutex);
return (EBUSY);
} else {
unitp->oflag |= FWRITE;
}
}
q->q_ptr = WR(q)->q_ptr = (caddr_t)unitp;
qprocson(q);
unitp->readq = RD(q);
unitp->writeq = WR(q);
unitp->msg = (mblk_t *)NULL;
mutex_exit(&unitp->umutex);
return (status);
}
static int
envctrl_close(queue_t *q, int flag, cred_t *cred_p)
{
struct envctrlunit *unitp;
unitp = (struct envctrlunit *)q->q_ptr;
mutex_enter(&unitp->umutex);
unitp->oflag = B_FALSE;
unitp->current_mode = ENVCTRL_NORMAL_MODE;
q->q_ptr = WR(q)->q_ptr = NULL;
qprocsoff(q);
mutex_exit(&unitp->umutex);
return (DDI_SUCCESS);
}
static int
envctrl_wput(queue_t *q, mblk_t *mp)
{
struct msgb *mp1;
struct envctrlunit *unitp;
struct iocblk *iocp;
struct copyresp *csp;
struct envctrl_tda8444t_chip *fanspeed;
struct envctrl_pcf8574_chip *ledchip;
struct envctrl_pcf8591_chip *temp, *a_fanspeed;
struct copyreq *cqp;
int cmd;
unitp = (struct envctrlunit *)q->q_ptr;
switch (DB_TYPE(mp)) {
case M_DATA:
while (mp) {
DB_TYPE(mp) = M_DATA;
mp1 = unlinkb(mp);
mp->b_cont = NULL;
if ((mp->b_wptr - mp->b_rptr) <= 0) {
freemsg(mp);
} else {
(void) putq(q, mp);
}
mp = mp1;
}
break;
case M_IOCTL:
{
iocp = (struct iocblk *)(void *)mp->b_rptr;
cmd = iocp->ioc_cmd;
switch (cmd) {
case ENVCTRL_IOC_SETMODE:
case ENVCTRL_IOC_GETMODE:
if (iocp->ioc_count == TRANSPARENT) {
mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
sizeof (uchar_t), NULL);
qreply(q, mp);
} else {
miocnak(q, mp, 0, EINVAL);
}
break;
case ENVCTRL_IOC_RESETTMPR:
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);
miocack(q, mp, 0, 0);
} else {
miocnak(q, mp, 0, EINVAL);
}
break;
case ENVCTRL_IOC_GETTEMP:
if (iocp->ioc_count == TRANSPARENT) {
mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
sizeof (struct envctrl_pcf8591_chip), NULL);
qreply(q, mp);
} else {
miocnak(q, mp, 0, EINVAL);
}
break;
case ENVCTRL_IOC_SETTEMP:
if (unitp->current_mode == ENVCTRL_DIAG_MODE &&
iocp->ioc_count == TRANSPARENT) {
mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
sizeof (uint8_t), NULL);
qreply(q, mp);
} else {
miocnak(q, mp, 0, EINVAL);
}
break;
case ENVCTRL_IOC_SETWDT:
if (unitp->current_mode == ENVCTRL_DIAG_MODE &&
iocp->ioc_count == TRANSPARENT) {
mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
sizeof (uint8_t), NULL);
qreply(q, mp);
} else {
miocnak(q, mp, 0, EINVAL);
}
break;
case ENVCTRL_IOC_SETFAN:
if (unitp->current_mode == ENVCTRL_DIAG_MODE &&
iocp->ioc_count == TRANSPARENT) {
mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
sizeof (struct envctrl_tda8444t_chip),
NULL);
qreply(q, mp);
} else {
miocnak(q, mp, 0, EINVAL);
}
break;
case ENVCTRL_IOC_GETFAN:
if (iocp->ioc_count == TRANSPARENT) {
mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
sizeof (struct envctrl_pcf8591_chip), NULL);
qreply(q, mp);
} else {
miocnak(q, mp, 0, EINVAL);
}
break;
case ENVCTRL_IOC_SETFSP:
if (iocp->ioc_count == TRANSPARENT) {
mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
sizeof (uint8_t), NULL);
qreply(q, mp);
} else {
miocnak(q, mp, 0, EINVAL);
}
break;
case ENVCTRL_IOC_SETDSKLED:
case ENVCTRL_IOC_GETDSKLED:
if (iocp->ioc_count == TRANSPARENT) {
mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
sizeof (struct envctrl_pcf8574_chip), NULL);
qreply(q, mp);
} else {
miocnak(q, mp, 0, EINVAL);
}
break;
default:
miocnak(q, mp, 0, EINVAL);
break;
}
break;
}
case M_IOCDATA:
{
uint8_t *tempr, *wdval;
long state;
csp = (struct copyresp *)(void *)mp->b_rptr;
if (csp->cp_rval != 0) {
miocnak(q, mp, 0, EINVAL);
return (0);
}
cqp = (struct copyreq *)(void *)mp->b_rptr;
cmd = csp->cp_cmd;
state = (long)cqp->cq_private;
switch (cmd) {
case ENVCTRL_IOC_SETFAN:
fanspeed = (struct envctrl_tda8444t_chip *)
(void *)mp->b_cont->b_rptr;
mutex_enter(&unitp->umutex);
if (envctrl_xmit(unitp, (caddr_t *)(void *)fanspeed,
fanspeed->type) == DDI_FAILURE) {
envctrl_fan_fail_service(unitp);
mutex_exit(&unitp->umutex);
miocnak(q, mp, 0, EINVAL);
} else {
mutex_exit(&unitp->umutex);
miocack(q, mp, 0, 0);
}
break;
case ENVCTRL_IOC_SETFSP:
wdval = (uint8_t *)(void *)mp->b_cont->b_rptr;
mutex_enter(&unitp->umutex);
if (unitp->current_mode == ENVCTRL_NORMAL_MODE) {
if (*wdval & ~ENVCTRL_FSP_USRMASK) {
mutex_exit(&unitp->umutex);
miocnak(q, mp, 0, EINVAL);
break;
}
}
envctrl_set_fsp(unitp, wdval);
mutex_exit(&unitp->umutex);
miocack(q, mp, 0, 0);
break;
case ENVCTRL_IOC_SETDSKLED:
ledchip = (struct envctrl_pcf8574_chip *)
(void *)mp->b_cont->b_rptr;
mutex_enter(&unitp->umutex);
if (envctrl_set_dskled(unitp, ledchip)) {
miocnak(q, mp, 0, EINVAL);
} else {
miocack(q, mp, 0, 0);
}
mutex_exit(&unitp->umutex);
break;
case ENVCTRL_IOC_GETDSKLED:
if (state == -1) {
miocack(q, mp, 0, 0);
break;
}
ledchip = (struct envctrl_pcf8574_chip *)
(void *)mp->b_cont->b_rptr;
mutex_enter(&unitp->umutex);
if (envctrl_get_dskled(unitp, ledchip)) {
miocnak(q, mp, 0, EINVAL);
} else {
mcopyout(mp, (void *)-1,
sizeof (struct envctrl_pcf8574_chip),
csp->cp_private, NULL);
qreply(q, mp);
}
mutex_exit(&unitp->umutex);
break;
case ENVCTRL_IOC_GETTEMP:
if (state == -1) {
miocack(q, mp, 0, 0);
break;
}
temp = (struct envctrl_pcf8591_chip *)
(void *)mp->b_cont->b_rptr;
mutex_enter(&unitp->umutex);
envctrl_recv(unitp, (caddr_t *)(void *)temp, PCF8591);
mutex_exit(&unitp->umutex);
mcopyout(mp, (void *)-1,
sizeof (struct envctrl_pcf8591_chip),
csp->cp_private, NULL);
qreply(q, mp);
break;
case ENVCTRL_IOC_GETFAN:
if (state == -1) {
miocack(q, mp, 0, 0);
break;
}
a_fanspeed = (struct envctrl_pcf8591_chip *)
(void *)mp->b_cont->b_rptr;
mutex_enter(&unitp->umutex);
envctrl_recv(unitp, (caddr_t *)(void *)a_fanspeed,
PCF8591);
mutex_exit(&unitp->umutex);
mcopyout(mp, (void *)-1,
sizeof (struct envctrl_pcf8591_chip),
csp->cp_private, NULL);
qreply(q, mp);
break;
case ENVCTRL_IOC_SETTEMP:
tempr = (uint8_t *)(void *)mp->b_cont->b_rptr;
if (*tempr > MAX_DIAG_TEMPR) {
miocnak(q, mp, 0, EINVAL);
} else {
mutex_enter(&unitp->umutex);
envctrl_get_sys_temperatures(unitp, tempr);
mutex_exit(&unitp->umutex);
miocack(q, mp, 0, 0);
}
break;
case ENVCTRL_IOC_SETWDT:
wdval = (uint8_t *)(void *)mp->b_cont->b_rptr;
if (*wdval > MAX_CL_VAL) {
miocnak(q, mp, 0, EINVAL);
} else {
mutex_enter(&unitp->umutex);
envctrl_reset_watchdog(unitp, wdval);
mutex_exit(&unitp->umutex);
miocack(q, mp, 0, 0);
}
break;
case ENVCTRL_IOC_GETMODE:
if (state == -1) {
miocack(q, mp, 0, 0);
break;
}
tempr = (uchar_t *)(void *)mp->b_cont->b_rptr;
*tempr = unitp->current_mode;
mcopyout(mp, (void *)-1, sizeof (uchar_t),
csp->cp_private, NULL);
qreply(q, mp);
break;
case ENVCTRL_IOC_SETMODE:
wdval = (uint8_t *)(void *)mp->b_cont->b_rptr;
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) {
envctrl_get_sys_temperatures(unitp,
(uint8_t *)NULL);
unitp->current_mode =
ENVCTRL_DIAG_MODE;
envctrl_fan_fail_service(unitp);
unitp->current_mode =
ENVCTRL_NORMAL_MODE;
}
mutex_exit(&unitp->umutex);
miocack(q, mp, 0, 0);
} else {
miocnak(q, mp, 0, EINVAL);
}
break;
default:
freemsg(mp);
break;
}
break;
}
case M_FLUSH:
if (*mp->b_rptr & FLUSHR) {
*mp->b_rptr &= ~FLUSHW;
qreply(q, mp);
} else {
freemsg(mp);
}
break;
default:
freemsg(mp);
break;
}
return (0);
}
uint_t
envctrl_bus_isr(caddr_t arg)
{
struct envctrlunit *unitp = (struct envctrlunit *)(void *)arg;
int ic = DDI_INTR_UNCLAIMED;
mutex_enter(&unitp->umutex);
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;
ic = DDI_INTR_UNCLAIMED;
mutex_enter(&unitp->umutex);
retry:
status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
PCF8574A_BASE_ADDR | ENVCTRL_PCF8574_DEV0, &recv_data, 1);
if (recv_data == 0xFF) {
status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
PCF8574A_BASE_ADDR | ENVCTRL_PCF8574_DEV0, &recv_data, 1);
}
if (envctrl_debug_flags)
cmn_err(CE_WARN, "envctrl_dev_isr: status= %d, data = %x\n",
status, recv_data);
if (status == DDI_FAILURE) {
drv_usecwait(1000);
if (retrys < envctrl_max_retries) {
retrys++;
goto retry;
} else {
if (envctrl_debug_flags)
cmn_err(CE_WARN,
"DEVISR FAILED received 0x%x\n", recv_data);
mutex_exit(&unitp->umutex);
envctrl_init_bus(unitp);
mutex_enter(&unitp->umutex);
envctrl_ps_probe(unitp);
mutex_exit(&unitp->umutex);
ic = DDI_INTR_CLAIMED;
return (ic);
}
}
if (!(recv_data & ENVCTRL_PCF8574_PORT0)) {
envctrl_PS_intr_service(unitp, PS1);
ic = DDI_INTR_CLAIMED;
}
if (!(recv_data & ENVCTRL_PCF8574_PORT1)) {
envctrl_PS_intr_service(unitp, PS2);
ic = DDI_INTR_CLAIMED;
}
if (!(recv_data & ENVCTRL_PCF8574_PORT2)) {
envctrl_PS_intr_service(unitp, PS3);
ic = DDI_INTR_CLAIMED;
}
if (!(recv_data & ENVCTRL_PCF8574_PORT3)) {
ic = DDI_INTR_CLAIMED;
}
if (!(recv_data & ENVCTRL_PCF8574_PORT4)) {
envctrl_fan_fail_service(unitp);
ic = DDI_INTR_CLAIMED;
}
if (!(recv_data & ENVCTRL_PCF8574_PORT5)) {
(void) envctrl_get_fpm_status(unitp);
ic = DDI_INTR_CLAIMED;
}
if (!(recv_data & ENVCTRL_PCF8574_PORT6)) {
ic = DDI_INTR_CLAIMED;
}
if (!(recv_data & ENVCTRL_PCF8574_PORT7)) {
ic = DDI_INTR_CLAIMED;
}
if ((recv_data == 0xFF)) {
ic = DDI_INTR_CLAIMED;
}
mutex_exit(&unitp->umutex);
return (ic);
}
static void
envctrl_init_bus(struct envctrlunit *unitp)
{
int i;
uint8_t noval = 0;
struct envctrl_tda8444t_chip fan;
int fans[] = {ENVCTRL_CPU_FANS, ENVCTRL_PS_FANS, ENVCTRL_AFB_FANS};
mutex_enter(&unitp->umutex);
ddi_put8(unitp->ctlr_handle,
&unitp->bus_ctl_regs->s0, ENVCTRL_CHAR_ZERO);
ddi_put8(unitp->ctlr_handle, &unitp->bus_ctl_regs->s1,
ENVCTRL_BUS_INIT0);
(void) ddi_put8(unitp->ctlr_handle, &unitp->bus_ctl_regs->s0,
ENVCTRL_BUS_INIT1);
ddi_put8(unitp->ctlr_handle,
&unitp->bus_ctl_regs->s1, ENVCTRL_BUS_CLOCK0);
ddi_put8(unitp->ctlr_handle,
&unitp->bus_ctl_regs->s0, ENVCTRL_BUS_CLOCK1);
ddi_put8(unitp->ctlr_handle,
&unitp->bus_ctl_regs->s1, ENVCTRL_BUS_ESI);
envctrl_stop_clock(unitp);
if (unitp->initting == B_TRUE) {
fan.chip_num = ENVCTRL_TDA8444T_DEV7;
fan.val = INIT_FAN_VAL;
for (i = 0; i < sizeof (fans)/sizeof (int); i++) {
fan.fan_num = fans[i];
if ((fans[i] == ENVCTRL_AFB_FANS) &&
(unitp->AFB_present == B_FALSE))
continue;
(void) envctrl_xmit(unitp, (caddr_t *)(void *)&fan,
TDA8444T);
}
}
envctrl_reset_dflop(unitp);
envctrl_enable_devintrs(unitp);
unitp->current_mode = ENVCTRL_NORMAL_MODE;
envctrl_reset_watchdog(unitp, &noval);
mutex_exit(&unitp->umutex);
}
static int
envctrl_xmit(struct envctrlunit *unitp, caddr_t *data, int chip_type)
{
struct envctrl_tda8444t_chip *fanspeed;
struct envctrl_pcf8574_chip *ioport;
uint8_t slave_addr;
uint8_t buf[2];
int retrys = 0;
int status;
ASSERT(MUTEX_HELD(&unitp->umutex));
switch (chip_type) {
case TDA8444T:
fanspeed = (struct envctrl_tda8444t_chip *)data;
if (fanspeed->chip_num > ENVCTRL_FAN_ADDR_MAX) {
return (DDI_FAILURE);
}
if (fanspeed->fan_num > ENVCTRL_PORT7) {
return (DDI_FAILURE);
}
if (fanspeed->val > MAX_FAN_VAL) {
return (DDI_FAILURE);
}
retry0:
slave_addr = (TDA8444T_BASE_ADDR | fanspeed->chip_num);
buf[0] = fanspeed->val;
status = eHc_write_tda8444((struct eHc_envcunit *)unitp,
TDA8444T_BASE_ADDR | fanspeed->chip_num, 0xF,
fanspeed->fan_num, buf, 1);
if (status != DDI_SUCCESS) {
drv_usecwait(1000);
if (retrys < envctrl_max_retries) {
retrys++;
goto retry0;
} else {
mutex_exit(&unitp->umutex);
envctrl_init_bus(unitp);
mutex_enter(&unitp->umutex);
if (envctrl_debug_flags)
cmn_err(CE_WARN,
"envctrl_xmit: Write to TDA8444 " \
"failed\n");
return (DDI_FAILURE);
}
}
switch (fanspeed->fan_num) {
case ENVCTRL_CPU_FANS:
unitp->fan_kstats[ENVCTRL_FAN_TYPE_CPU].fanspeed =
fanspeed->val;
break;
case ENVCTRL_PS_FANS:
unitp->fan_kstats[ENVCTRL_FAN_TYPE_PS].fanspeed =
fanspeed->val;
break;
case ENVCTRL_AFB_FANS:
unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].fanspeed =
fanspeed->val;
break;
default:
break;
}
break;
case PCF8574:
ioport = (struct envctrl_pcf8574_chip *)data;
buf[0] = ioport->val;
if (ioport->chip_num > ENVCTRL_PCF8574_DEV7)
return (DDI_FAILURE);
retry:
if (ioport->type == PCF8574A) {
slave_addr = (PCF8574A_BASE_ADDR | ioport->chip_num);
status =
eHc_write_pcf8574a((struct eHc_envcunit *)unitp,
PCF8574A_BASE_ADDR | ioport->chip_num, buf, 1);
} else {
slave_addr = (PCF8574_BASE_ADDR | ioport->chip_num);
status = eHc_write_pcf8574((struct eHc_envcunit *)unitp,
PCF8574_BASE_ADDR | ioport->chip_num, buf, 1);
}
if (status != DDI_SUCCESS) {
drv_usecwait(1000);
if (retrys < envctrl_max_retries) {
retrys++;
goto retry;
} else {
mutex_exit(&unitp->umutex);
envctrl_init_bus(unitp);
mutex_enter(&unitp->umutex);
if (envctrl_debug_flags)
cmn_err(CE_WARN, "Write to PCF8574 " \
"failed, addr = %X\n", slave_addr);
if (envctrl_debug_flags)
cmn_err(CE_WARN, "envctrl_xmit: PCF8574\
dev = %d, port = %d\n",
ioport->chip_num, ioport->type);
return (DDI_FAILURE);
}
}
break;
default:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
envctrl_recv(struct envctrlunit *unitp, caddr_t *data, int chip_type)
{
struct envctrl_pcf8591_chip *temp;
struct envctrl_pcf8574_chip *ioport;
uint8_t slave_addr, recv_data;
int retrys = 0;
int status;
uint8_t buf[1];
ASSERT(MUTEX_HELD(&unitp->umutex));
switch (chip_type) {
case PCF8591:
temp = (struct envctrl_pcf8591_chip *)data;
slave_addr = (PCF8591_BASE_ADDR | temp->chip_num);
retry:
status = eHc_read_pcf8591((struct eHc_envcunit *)unitp,
PCF8591_BASE_ADDR | temp->chip_num & 0xF,
temp->sensor_num, 0, 0, 1, &recv_data, 1);
if (status != DDI_SUCCESS) {
drv_usecwait(1000);
if (retrys < envctrl_max_retries) {
retrys++;
goto retry;
} else {
mutex_exit(&unitp->umutex);
envctrl_init_bus(unitp);
mutex_enter(&unitp->umutex);
if (envctrl_debug_flags)
cmn_err(CE_WARN, "Read from PCF8591 " \
"failed, slave_addr = %x\n",
slave_addr);
}
}
temp->temp_val = recv_data;
break;
case TDA8444T:
printf("envctrl_recv: attempting to read TDA8444T\n");
return;
case PCF8574:
ioport = (struct envctrl_pcf8574_chip *)data;
retry1:
if (ioport->chip_num > ENVCTRL_PCF8574_DEV7)
cmn_err(CE_WARN, "envctrl: dev out of range 0x%x\n",
ioport->chip_num);
if (ioport->type == PCF8574A) {
slave_addr = (PCF8574_READ_BIT | PCF8574A_BASE_ADDR |
ioport->chip_num);
status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
PCF8574A_BASE_ADDR | ioport->chip_num, buf, 1);
} else {
slave_addr = (PCF8574_READ_BIT | PCF8574_BASE_ADDR |
ioport->chip_num);
status = eHc_read_pcf8574((struct eHc_envcunit *)unitp,
PCF8574_BASE_ADDR | ioport->chip_num, buf, 1);
}
if (status != DDI_SUCCESS) {
drv_usecwait(1000);
if (retrys < envctrl_max_retries) {
retrys++;
goto retry1;
} else {
mutex_exit(&unitp->umutex);
envctrl_init_bus(unitp);
mutex_enter(&unitp->umutex);
if (envctrl_debug_flags)
cmn_err(CE_WARN, "Read from PCF8574 "\
"failed, addr = %X\n", slave_addr);
if (envctrl_debug_flags)
cmn_err(CE_WARN, "envctrl_recv: PCF8574\
dev = %d, port = %d\n",
ioport->chip_num, ioport->type);
}
}
ioport->val = buf[0];
break;
default:
break;
}
}
static int
envctrl_get_ps_temp(struct envctrlunit *unitp, uint8_t psaddr)
{
uint8_t tempr;
int i, retrys;
int status;
uint8_t buf[4];
ASSERT(MUTEX_HELD(&unitp->umutex));
tempr = 0;
retrys = 0;
retry:
status = eHc_read_pcf8591((struct eHc_envcunit *)unitp,
PCF8591_BASE_ADDR | psaddr & 0xF, 0, 1, 0, 1, buf, 4);
tempr = 0;
for (i = 0; i < PCF8591_MAX_PORTS; i++) {
if (envctrl_debug_flags) {
cmn_err(CE_WARN, "PS addr 0x%x recvd 0x%x on port %d\n",
psaddr, buf[i], i);
}
if (buf[i] > tempr && buf[i] < MAX_PS_ADVAL) {
tempr = buf[i];
}
}
if (status != DDI_SUCCESS) {
drv_usecwait(1000);
if (retrys < envctrl_max_retries) {
retrys++;
goto retry;
} else {
mutex_exit(&unitp->umutex);
envctrl_init_bus(unitp);
mutex_enter(&unitp->umutex);
if (envctrl_debug_flags)
cmn_err(CE_WARN,
"Cannot read Power Supply Temps addr = %X",
psaddr);
return (PS_DEFAULT_VAL);
}
}
return (ps_temps[tempr]);
}
static int
envctrl_get_cpu_temp(struct envctrlunit *unitp, int cpunum)
{
uint8_t recv_data;
int retrys;
int status;
ASSERT(MUTEX_HELD(&unitp->umutex));
retrys = 0;
retry:
status = eHc_read_pcf8591((struct eHc_envcunit *)unitp,
PCF8591_BASE_ADDR | PCF8591_DEV7, cpunum, 0, 0, 0,
&recv_data, 1);
if (status == DDI_FAILURE) {
drv_usecwait(1000);
if (retrys < envctrl_max_retries) {
retrys++;
goto retry;
} else {
mutex_exit(&unitp->umutex);
envctrl_init_bus(unitp);
mutex_enter(&unitp->umutex);
if (envctrl_debug_flags)
cmn_err(CE_WARN, "envctrl CPU TEMP read " \
"failed\n");
return (MAX_CPU_TEMP - 10);
}
}
return (cpu_temps[recv_data]);
}
static int
envctrl_get_lm75_temp(struct envctrlunit *unitp)
{
int k;
ushort_t lmval;
uint8_t tmp1;
uint8_t tmp2;
int status;
uint8_t buf[2];
ASSERT(MUTEX_HELD(&unitp->umutex));
status = eHc_read_lm75((struct eHc_envcunit *)unitp,
LM75_BASE_ADDR | LM75_CONFIG_ADDRA, buf, 2);
if (status != DDI_SUCCESS)
cmn_err(CE_WARN, "read of LM75 failed\n");
tmp1 = buf[0];
tmp2 = buf[1];
lmval = tmp1 << 8;
lmval = (lmval | tmp2);
lmval = (lmval >> 7);
if (lmval & LM75_COMP_MASK) {
tmp1 = (lmval & LM75_COMP_MASK_UPPER);
tmp1 = -tmp1;
tmp1 = tmp1/2;
k = 0 - tmp1;
} else {
k = lmval /2;
}
return (k);
}
static void
envctrl_tempr_poll(void *arg)
{
int diag_flag = 0;
struct envctrlunit *unitp = (struct envctrlunit *)arg;
mutex_enter(&unitp->umutex);
if (unitp->shutdown == B_TRUE) {
(void) power_down("Fatal System Environmental Control Error");
}
if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
diag_flag++;
if (envctrl_debug_flags) {
cmn_err(CE_WARN,
"Tempr poll went off while in DIAG MODE");
}
}
unitp->current_mode = ENVCTRL_NORMAL_MODE;
envctrl_get_sys_temperatures(unitp, (uint8_t *)NULL);
if (diag_flag == 0) {
envctrl_fan_fail_service(unitp);
}
unitp->timeout_id = (timeout(envctrl_tempr_poll,
(caddr_t)unitp, overtemp_timeout_hz));
mutex_exit(&unitp->umutex);
}
static void
envctrl_led_blink(void *arg)
{
struct envctrl_pcf8574_chip fspchip;
struct envctrlunit *unitp = (struct envctrlunit *)arg;
mutex_enter(&unitp->umutex);
fspchip.type = PCF8574A;
fspchip.chip_num = ENVCTRL_PCF8574_DEV6;
envctrl_recv(unitp, (caddr_t *)(void *)&fspchip, PCF8574);
if (unitp->present_led_state == B_TRUE) {
fspchip.val = (fspchip.val & ~(ENVCTRL_PCF8574_PORT4) |
0xC0);
unitp->present_led_state = B_FALSE;
} else {
fspchip.val = (fspchip.val | ENVCTRL_PCF8574_PORT4 | 0xC0);
unitp->present_led_state = B_TRUE;
}
(void) envctrl_xmit(unitp, (caddr_t *)(void *)&fspchip, PCF8574);
unitp->blink_timeout_id = (timeout(envctrl_led_blink,
(caddr_t)unitp, blink_timeout_hz));
mutex_exit(&unitp->umutex);
}
static void
envctrl_get_sys_temperatures(struct envctrlunit *unitp, uint8_t *diag_tempr)
{
int temperature, tmptemp, cputemp, hicputemp, ambtemp;
int i;
struct envctrl_tda8444t_chip fan;
uint8_t psaddr[] = {PSTEMP3, PSTEMP2, PSTEMP1, PSTEMP0};
uint8_t noval = 0;
uint8_t fspval;
ASSERT(MUTEX_HELD(&unitp->umutex));
fan.fan_num = ENVCTRL_CPU_FANS;
fan.chip_num = ENVCTRL_TDA8444T_DEV7;
tmptemp = 0;
if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
unitp->current_mode = ENVCTRL_NORMAL_MODE;
envctrl_reset_watchdog(unitp, &noval);
unitp->current_mode = ENVCTRL_DIAG_MODE;
} else {
envctrl_reset_watchdog(unitp, &noval);
}
envctrl_reset_dflop(unitp);
envctrl_enable_devintrs(unitp);
if (unitp->current_mode == ENVCTRL_DIAG_MODE && diag_tempr != NULL) {
if (unitp->timeout_id != 0) {
(void) untimeout(unitp->timeout_id);
}
ambtemp = *diag_tempr;
unitp->timeout_id = (timeout(envctrl_tempr_poll,
(caddr_t)unitp, overtemp_timeout_hz));
} else {
ambtemp = envctrl_get_lm75_temp(unitp);
if (ambtemp == -100) {
mutex_exit(&unitp->umutex);
envctrl_init_bus(unitp);
mutex_enter(&unitp->umutex);
ambtemp = envctrl_get_lm75_temp(unitp);
}
}
envctrl_mod_encl_kstats(unitp, ENVCTRL_ENCL_AMBTEMPR, INSTANCE_0,
ambtemp);
fspval = envctrl_get_fpm_status(unitp);
if (ambtemp > MAX_AMB_TEMP) {
fspval |= (ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_GEN_ERR);
if (!(envctrl_power_off_overide) &&
unitp->current_mode == ENVCTRL_NORMAL_MODE) {
unitp->shutdown = B_TRUE;
}
if (unitp->current_mode == ENVCTRL_NORMAL_MODE) {
cmn_err(CE_WARN,
"Ambient Temperature is %d C, shutdown now\n",
ambtemp);
}
} else {
if (envctrl_isother_fault_led(unitp, fspval,
ENVCTRL_FSP_TEMP_ERR)) {
fspval &= ~(ENVCTRL_FSP_TEMP_ERR);
} else {
fspval &= ~(ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_GEN_ERR);
}
}
envctrl_set_fsp(unitp, &fspval);
cputemp = hicputemp = 0;
#ifndef TESTBED
for (i = 0; i < ENVCTRL_MAX_CPUS; i++) {
if (unitp->cpu_pr_location[i] == B_TRUE) {
cputemp = envctrl_get_cpu_temp(unitp, i);
envctrl_mod_encl_kstats(unitp, ENVCTRL_ENCL_CPUTEMPR,
i, cputemp);
if (cputemp >= MAX_CPU_TEMP) {
if (!(envctrl_power_off_overide)) {
unitp->shutdown = B_TRUE;
}
cmn_err(CE_WARN,
"CPU %d OVERHEATING!!!", i);
}
if (cputemp > hicputemp) {
hicputemp = cputemp;
}
}
}
#else
cputemp = envctrl_get_cpu_temp(unitp, 0);
envctrl_mod_encl_kstats(unitp, ENVCTRL_ENCL_CPUTEMPR, 0, cputemp);
#endif
fspval = envctrl_get_fpm_status(unitp);
if (unitp->current_mode == ENVCTRL_DIAG_MODE && diag_tempr != NULL) {
temperature = ambtemp;
} else {
temperature = hicputemp - CPU_AMB_RISE;
}
if (temperature < 0) {
fan.val = MAX_FAN_SPEED;
} else if (temperature > MAX_AMB_TEMP) {
fan.val = MAX_FAN_SPEED;
fspval |= (ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_GEN_ERR);
if (unitp->current_mode == ENVCTRL_NORMAL_MODE) {
cmn_err(CE_WARN,
"CPU Fans set to MAX. CPU Temp is %d C\n",
hicputemp);
}
} else if (ambtemp < MAX_AMB_TEMP) {
if (!envctrl_p0_enclosure) {
fan.val = acme_cpu_fanspd[temperature];
} else {
fan.val = fan_speed[temperature];
}
if (envctrl_isother_fault_led(unitp, fspval,
ENVCTRL_FSP_TEMP_ERR)) {
fspval &= ~(ENVCTRL_FSP_TEMP_ERR);
} else {
fspval &= ~(ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_GEN_ERR);
}
}
envctrl_set_fsp(unitp, &fspval);
unitp->fan_kstats[ENVCTRL_FAN_TYPE_CPU].fanspeed = fan.val;
(void) envctrl_xmit(unitp, (caddr_t *)(void *)&fan, TDA8444T);
if (unitp->AFB_present == B_TRUE) {
fan.val = AFB_MAX;
unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].fanspeed = fan.val;
fan.fan_num = ENVCTRL_AFB_FANS;
(void) envctrl_xmit(unitp, (caddr_t *)(void *)&fan, TDA8444T);
}
tmptemp = temperature = 0;
for (i = 0; i <= MAXPS; i++) {
if (unitp->ps_present[i]) {
tmptemp = envctrl_get_ps_temp(unitp, psaddr[i]);
unitp->ps_kstats[i].ps_tempr = tmptemp & 0xFFFF;
if (tmptemp > temperature) {
temperature = tmptemp;
}
if (temperature >= MAX_PS_TEMP) {
if (!(envctrl_power_off_overide)) {
unitp->shutdown = B_TRUE;
}
cmn_err(CE_WARN,
"Power Supply %d OVERHEATING!!!\
Temp is %d C", i, temperature);
}
}
}
fan.fan_num = ENVCTRL_PS_FANS;
if (temperature > PS_TEMP_WARN) {
fspval = envctrl_get_fpm_status(unitp);
fspval |= (ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_GEN_ERR);
envctrl_set_fsp(unitp, &fspval);
fan.val = MAX_FAN_SPEED;
cmn_err(CE_WARN, "A Power Supply is close to OVERHEATING!!!");
} else {
if (temperature - ambtemp > PS_AMB_RISE) {
ambtemp = temperature - PS_AMB_RISE;
}
if (!envctrl_p0_enclosure) {
fan.val = acme_ps_fanspd[ambtemp];
} else {
fan.val = ps_fans[ambtemp];
}
}
unitp->fan_kstats[ENVCTRL_FAN_TYPE_PS].fanspeed = fan.val;
(void) envctrl_xmit(unitp, (caddr_t *)(void *)&fan, TDA8444T);
}
static void
envctrl_fan_fail_service(struct envctrlunit *unitp)
{
uint8_t recv_data, fpmstat;
int fantype;
int psfanflt, cpufanflt, afbfanflt;
int retries = 0, max_retry_count;
int status;
psfanflt = cpufanflt = afbfanflt = 0;
ASSERT(MUTEX_HELD(&unitp->umutex));
retry:
status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
PCF8574A_BASE_ADDR | ENVCTRL_PCF8574_DEV4, &recv_data, 1);
if (status != DDI_SUCCESS)
cmn_err(CE_WARN, "fan_fail_service: status = %d, data = %x\n",
status, recv_data);
if (recv_data == 0xff) {
unitp->fan_kstats[ENVCTRL_FAN_TYPE_PS].fans_ok = B_TRUE;
unitp->fan_kstats[ENVCTRL_FAN_TYPE_CPU].fans_ok = B_TRUE;
unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].fans_ok = B_TRUE;
unitp->fan_kstats[ENVCTRL_FAN_TYPE_PS].fanflt_num = 0;
unitp->fan_kstats[ENVCTRL_FAN_TYPE_CPU].fanflt_num = 0;
unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].fanflt_num = 0;
unitp->num_fans_failed = 0;
fpmstat = envctrl_get_fpm_status(unitp);
if (!(envctrl_isother_fault_led(unitp, fpmstat, 0))) {
fpmstat &= ~(ENVCTRL_FSP_GEN_ERR);
}
if (unitp->shutdown != B_TRUE) {
envctrl_set_fsp(unitp, &fpmstat);
}
return;
}
fantype = ENVCTRL_FAN_TYPE_PS;
if (!(recv_data & ENVCTRL_PCF8574_PORT0)) {
psfanflt = PS_FAN_3;
}
if (!(recv_data & ENVCTRL_PCF8574_PORT1)) {
psfanflt = PS_FAN_2;
}
if (!(recv_data & ENVCTRL_PCF8574_PORT2)) {
psfanflt = PS_FAN_1;
}
if (psfanflt != 0) {
unitp->fan_kstats[fantype].fans_ok = B_FALSE;
unitp->fan_kstats[fantype].fanflt_num = psfanflt - 1;
if (retries == MAX_FAN_FAIL_RETRY && status == DDI_SUCCESS &&
unitp->current_mode == ENVCTRL_NORMAL_MODE) {
cmn_err(CE_WARN, "PS Fan Number %d Failed",
psfanflt - 1);
}
} else {
unitp->fan_kstats[fantype].fans_ok = B_TRUE;
unitp->fan_kstats[fantype].fanflt_num = 0;
}
fantype = ENVCTRL_FAN_TYPE_CPU;
if (!(recv_data & ENVCTRL_PCF8574_PORT3)) {
cpufanflt = CPU_FAN_1;
}
if (!(recv_data & ENVCTRL_PCF8574_PORT4)) {
cpufanflt = CPU_FAN_2;
}
if (!(recv_data & ENVCTRL_PCF8574_PORT5)) {
cpufanflt = CPU_FAN_3;
}
if (cpufanflt != 0) {
unitp->fan_kstats[fantype].fans_ok = B_FALSE;
unitp->fan_kstats[fantype].fanflt_num = cpufanflt - 1;
if (retries == MAX_FAN_FAIL_RETRY && status == DDI_SUCCESS &&
unitp->current_mode == ENVCTRL_NORMAL_MODE) {
cmn_err(CE_WARN, "CPU Fan Number %d Failed",
cpufanflt - 1);
}
} else {
unitp->fan_kstats[fantype].fans_ok = B_TRUE;
unitp->fan_kstats[fantype].fanflt_num = 0;
}
if (!(recv_data & ENVCTRL_PCF8574_PORT6) &&
(unitp->AFB_present == B_TRUE)) {
afbfanflt++;
unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].fans_ok
= B_FALSE;
unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].fanflt_num =
AFB_FAN_1;
if (unitp->current_mode == ENVCTRL_NORMAL_MODE) {
cmn_err(CE_WARN, "AFB Fan Failed");
}
}
if (psfanflt == 0 && cpufanflt == 0 && afbfanflt == 0 &&
unitp->num_fans_failed != 0) {
fpmstat = envctrl_get_fpm_status(unitp);
if (!(envctrl_isother_fault_led(unitp,
fpmstat, 0))) {
fpmstat &= ~(ENVCTRL_FSP_GEN_ERR);
}
envctrl_set_fsp(unitp, &fpmstat);
} else if (psfanflt != 0 || cpufanflt != 0 || afbfanflt != 0) {
fpmstat = envctrl_get_fpm_status(unitp);
fpmstat |= ENVCTRL_FSP_GEN_ERR;
envctrl_set_fsp(unitp, &fpmstat);
}
if (unitp->AFB_present == B_FALSE) {
afbfanflt = 0;
}
if ((cpufanflt > 0 || psfanflt > 0 || afbfanflt > 0 ||
(status != DDI_SUCCESS)) && !unitp->initting &&
unitp->current_mode == ENVCTRL_NORMAL_MODE) {
if (status != DDI_SUCCESS)
max_retry_count = envctrl_max_retries;
else
max_retry_count = MAX_FAN_FAIL_RETRY;
if (retries <= max_retry_count) {
retries++;
drv_usecwait(1000);
if (retries == max_retry_count) {
cmn_err(CE_WARN,
"Fan Fail is 0x%x, retries = %d\n",
recv_data, retries);
}
envctrl_get_sys_temperatures(unitp,
(uint8_t *)NULL);
goto retry;
}
if (!(envctrl_power_off_overide)) {
unitp->shutdown = B_TRUE;
}
cmn_err(CE_WARN, "Fan Failure(s), System Shutdown");
}
unitp->num_fans_failed = (psfanflt + cpufanflt + afbfanflt);
}
static void
envctrl_PS_intr_service(struct envctrlunit *unitp, uint8_t psaddr)
{
uint8_t recv_data;
int status, retrys = 0;
ASSERT(MUTEX_HELD(&unitp->umutex));
if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
return;
}
retry:
status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
PCF8574A_BASE_ADDR | psaddr & 0xF, &recv_data, 1);
if (status != DDI_SUCCESS) {
drv_usecwait(1000);
if (retrys < envctrl_max_retries) {
retrys++;
goto retry;
} else {
mutex_exit(&unitp->umutex);
envctrl_init_bus(unitp);
mutex_enter(&unitp->umutex);
if (envctrl_debug_flags)
cmn_err(CE_WARN,
"PS_intr_service: Read from 8574A " \
"failed\n");
}
}
unitp->pshotplug_id = (timeout(envctrl_pshotplug_poll,
(caddr_t)unitp, pshotplug_timeout_hz));
}
static void
envctrl_reset_dflop(struct envctrlunit *unitp)
{
struct envctrl_pcf8574_chip initval;
ASSERT(MUTEX_HELD(&unitp->umutex));
initval.chip_num = ENVCTRL_PCF8574_DEV0;
initval.type = PCF8574A;
initval.val = ENVCTRL_DFLOP_INIT0;
(void) envctrl_xmit(unitp, (caddr_t *)(void *)&initval, PCF8574);
initval.val = ENVCTRL_DFLOP_INIT1;
(void) envctrl_xmit(unitp, (caddr_t *)(void *)&initval, PCF8574);
}
static void
envctrl_add_encl_kstats(struct envctrlunit *unitp, int type,
int instance, uint8_t val)
{
int i = 0;
boolean_t inserted = B_FALSE;
ASSERT(MUTEX_HELD(&unitp->umutex));
while (i < MAX_DEVS && inserted == B_FALSE) {
if (unitp->encl_kstats[i].instance == I2C_NODEV) {
unitp->encl_kstats[i].instance = instance;
unitp->encl_kstats[i].type = type;
unitp->encl_kstats[i].value = val;
inserted = B_TRUE;
}
i++;
}
unitp->num_encl_present++;
}
static void
envctrl_enable_devintrs(struct envctrlunit *unitp)
{
struct envctrl_pcf8574_chip initval;
ASSERT(MUTEX_HELD(&unitp->umutex));
initval.chip_num = ENVCTRL_PCF8574_DEV0;
initval.type = PCF8574A;
initval.val = ENVCTRL_DEVINTR_INTI0;
(void) envctrl_xmit(unitp, (caddr_t *)(void *)&initval, PCF8574);
initval.val = ENVCTRL_DEVINTR_INTI1;
(void) envctrl_xmit(unitp, (caddr_t *)(void *)&initval, PCF8574);
}
static void
envctrl_stop_clock(struct envctrlunit *unitp)
{
int status;
uint8_t buf[2];
ASSERT(MUTEX_HELD(&unitp->umutex));
buf[0] = CLOCK_CSR_REG;
buf[1] = CLOCK_DISABLE;
status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
PCF8583_BASE_ADDR | 0, buf, 2);
if (status != DDI_SUCCESS)
cmn_err(CE_WARN, "write to PCF8583 failed\n");
}
static void
envctrl_reset_watchdog(struct envctrlunit *unitp, uint8_t *wdval)
{
uint8_t w, r;
uint8_t res = 0;
int status;
uint8_t buf[3];
ASSERT(MUTEX_HELD(&unitp->umutex));
envctrl_stop_clock(unitp);
buf[0] = ALARM_CTR_REG_MINS;
buf[1] = 0x0;
status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
PCF8583_BASE_ADDR | 0, buf, 2);
if (status != DDI_SUCCESS)
cmn_err(CE_WARN, "write to PCF8583 failed\n");
buf[0] = CLOCK_ALARM_REG_A;
if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
if (unitp->timeout_id != 0) {
(void) untimeout(unitp->timeout_id);
unitp->timeout_id = 0;
unitp->timeout_id = (timeout(envctrl_tempr_poll,
(caddr_t)unitp, overtemp_timeout_hz));
}
buf[1] = CLOCK_ENABLE_TIMER_S;
} else {
buf[1] = CLOCK_ENABLE_TIMER;
}
status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
PCF8583_BASE_ADDR | 0, buf, 2);
if (status != DDI_SUCCESS)
cmn_err(CE_WARN, "Reset envctrl watchdog failed\n");
buf[0] = ALARM_CTRL_REG;
if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
if (*wdval > MAX_CL_VAL) {
buf[1] = EGG_TIMER_VAL;
} else {
w = *wdval/10;
r = *wdval%10;
res = res | r;
res = (0x99 - (res | (w << 4)));
buf[1] = res;
}
} else {
buf[1] = EGG_TIMER_VAL;
}
status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
PCF8583_BASE_ADDR | 0, buf, 2);
if (status != DDI_SUCCESS)
cmn_err(CE_WARN, "Reset envctrl watchdog failed\n");
buf[0] = CLOCK_CSR_REG;
buf[1] = CLOCK_ENABLE;
status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
PCF8583_BASE_ADDR | 0, buf, 2);
if (status != DDI_SUCCESS)
cmn_err(CE_WARN, "Reset envctrl watchdog failed\n");
}
static void
envctrl_ps_probe(struct envctrlunit *unitp)
{
uint8_t recv_data, fpmstat;
uint8_t psaddr[] = {PS1, PS2, PS3, PSTEMP0};
int i;
int ps_error = 0, retrys = 0;
int devaddr;
int status;
int twotimes = 0;
ASSERT(MUTEX_HELD(&unitp->umutex));
unitp->num_ps_present = 0;
for (i = 0; i <= MAXPS; i++) {
unitp->ps_present[i] = B_FALSE;
unitp->ps_kstats[i].ps_rating = 0;
unitp->ps_kstats[i].ps_tempr = 0;
switch (psaddr[i]) {
case PS1:
devaddr = ENVCTRL_PCF8574_DEV3;
break;
case PS2:
devaddr = ENVCTRL_PCF8574_DEV2;
break;
case PS3:
devaddr = ENVCTRL_PCF8574_DEV1;
break;
case PSTEMP0:
devaddr = 0;
break;
}
retrys = 0;
retry:
status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
PCF8574A_BASE_ADDR | devaddr, &recv_data, 1);
if (status != DDI_SUCCESS) {
drv_usecwait(1000);
if (retrys < envctrl_max_retries) {
retrys++;
goto retry;
} else {
mutex_exit(&unitp->umutex);
envctrl_init_bus(unitp);
mutex_enter(&unitp->umutex);
if (twotimes == 0) {
twotimes++;
retrys = 0;
goto retry;
} else {
cmn_err(CE_WARN,
"PS_probe: Read from 8574A failed\n");
}
}
}
if (!(recv_data & ENVCTRL_PCF8574_PORT0)) {
unitp->ps_present[i] = B_TRUE;
unitp->ps_kstats[i].instance = i;
unitp->ps_kstats[i].ps_tempr = ENVCTRL_INIT_TEMPR;
++unitp->num_ps_present;
if (power_supply_previous_state[i] == 0) {
cmn_err(CE_NOTE,
"Power Supply %d inserted\n", i);
}
power_supply_previous_state[i] = 1;
if (!(recv_data & ENVCTRL_PCF8574_PORT1)) {
unitp->ps_kstats[i].ps_rating = ENVCTRL_PS_550;
}
if (!(recv_data & ENVCTRL_PCF8574_PORT2)) {
unitp->ps_kstats[i].ps_rating = ENVCTRL_PS_650;
}
if (!(recv_data & ENVCTRL_PCF8574_PORT3)) {
cmn_err(CE_WARN,
"Power Supply %d NOT okay\n", i);
unitp->ps_kstats[i].ps_ok = B_FALSE;
ps_error++;
} else {
unitp->ps_kstats[i].ps_ok = B_TRUE;
}
if (!(recv_data & ENVCTRL_PCF8574_PORT4)) {
cmn_err(CE_WARN,
"Power Supply %d Overloaded\n", i);
unitp->ps_kstats[i].limit_ok = B_FALSE;
ps_error++;
} else {
unitp->ps_kstats[i].limit_ok = B_TRUE;
}
if (!(recv_data & ENVCTRL_PCF8574_PORT5)) {
cmn_err(CE_WARN,
"Power Supply %d load share err\n", i);
unitp->ps_kstats[i].curr_share_ok = B_FALSE;
ps_error++;
} else {
unitp->ps_kstats[i].curr_share_ok = B_TRUE;
}
if (!(recv_data & ENVCTRL_PCF8574_PORT6)) {
cmn_err(CE_WARN,
"PS %d Shouln't interrupt\n", i);
ps_error++;
}
if (!(recv_data & ENVCTRL_PCF8574_PORT7)) {
cmn_err(CE_WARN,
"PS %d Shouln't interrupt\n", i);
ps_error++;
}
} else {
if (power_supply_previous_state[i] == 1) {
cmn_err(CE_NOTE,
"Power Supply %d removed\n", i);
}
power_supply_previous_state[i] = 0;
}
}
fpmstat = envctrl_get_fpm_status(unitp);
if (ps_error) {
fpmstat |= (ENVCTRL_FSP_PS_ERR | ENVCTRL_FSP_GEN_ERR);
} else {
if (envctrl_isother_fault_led(unitp, fpmstat,
ENVCTRL_FSP_PS_ERR)) {
fpmstat &= ~(ENVCTRL_FSP_PS_ERR);
} else {
fpmstat &= ~(ENVCTRL_FSP_PS_ERR |
ENVCTRL_FSP_GEN_ERR);
}
}
envctrl_set_fsp(unitp, &fpmstat);
if (unitp->current_mode == ENVCTRL_NORMAL_MODE) {
envctrl_get_sys_temperatures(unitp, (uint8_t *)NULL);
}
}
static void
envctrl_abort_seq_handler(char *msg)
{
struct envctrlunit *unitp;
int i;
uint8_t secure = 0;
for (i = 0; i < MAX_DEVS; i++) {
if (unitp = (struct envctrlunit *)
ddi_get_soft_state(envctrlsoft_statep, i))
break;
}
ASSERT(unitp);
for (i = 0; i < MAX_DEVS; i++) {
if ((unitp->encl_kstats[i].type == ENVCTRL_ENCL_FSP) &&
(unitp->encl_kstats[i].instance != I2C_NODEV)) {
secure = unitp->encl_kstats[i].value;
break;
}
}
if ((secure & ENVCTRL_FSP_KEYMASK) == ENVCTRL_FSP_KEYLOCKED) {
cmn_err(CE_CONT,
"!envctrl: ignoring debug enter sequence\n");
} else {
if (envctrl_debug_flags) {
cmn_err(CE_CONT, "!envctrl: allowing debug enter\n");
}
debug_enter(msg);
}
}
static uint8_t
envctrl_get_fpm_status(struct envctrlunit *unitp)
{
uint8_t recv_data;
int status, retrys = 0;
ASSERT(MUTEX_HELD(&unitp->umutex));
retry:
status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
PCF8574A_BASE_ADDR | ENVCTRL_PCF8574_DEV6, &recv_data, 1);
if (status != DDI_SUCCESS) {
drv_usecwait(1000);
if (retrys < envctrl_max_retries) {
retrys++;
goto retry;
} else {
mutex_exit(&unitp->umutex);
envctrl_init_bus(unitp);
mutex_enter(&unitp->umutex);
if (envctrl_debug_flags)
cmn_err(CE_WARN, "Read from PCF8574 (FPM) "\
"failed\n");
}
}
recv_data = ~recv_data;
envctrl_mod_encl_kstats(unitp, ENVCTRL_ENCL_FSP,
INSTANCE_0, recv_data);
return (recv_data);
}
static void
envctrl_set_fsp(struct envctrlunit *unitp, uint8_t *val)
{
struct envctrl_pcf8574_chip chip;
ASSERT(MUTEX_HELD(&unitp->umutex));
chip.val = ENVCTRL_FSP_OFF;
chip.chip_num = ENVCTRL_PCF8574_DEV6;
chip.type = PCF8574A;
chip.val = (~(ENVCTRL_FSP_KEYMASK | ENVCTRL_FSP_POMASK) & (*val));
chip.val = ~chip.val;
(void) envctrl_xmit(unitp, (caddr_t *)(void *)&chip, PCF8574);
}
static int
envctrl_get_dskled(struct envctrlunit *unitp, struct envctrl_pcf8574_chip *chip)
{
uint_t oldtype;
ASSERT(MUTEX_HELD(&unitp->umutex));
if (chip->chip_num > ENVCTRL_PCF8574_DEV2 ||
chip->type != ENVCTRL_ENCL_BACKPLANE4 &&
chip->type != ENVCTRL_ENCL_BACKPLANE8) {
return (DDI_FAILURE);
}
oldtype = chip->type;
chip->type = PCF8574;
envctrl_recv(unitp, (caddr_t *)(void *)chip, PCF8574);
chip->type = oldtype;
chip->val = ~chip->val;
return (DDI_SUCCESS);
}
static int
envctrl_set_dskled(struct envctrlunit *unitp, struct envctrl_pcf8574_chip *chip)
{
struct envctrl_pcf8574_chip fspchip;
struct envctrl_pcf8574_chip backchip;
int i, instance;
int diskfault = 0;
uint8_t controller_addr[] = {ENVCTRL_PCF8574_DEV0, ENVCTRL_PCF8574_DEV1,
ENVCTRL_PCF8574_DEV2};
ASSERT(MUTEX_HELD(&unitp->umutex));
if (chip->chip_num > ENVCTRL_PCF8574_DEV2 ||
chip->val > ENVCTRL_DISK8LED_ALLOFF ||
chip->val < ENVCTRL_CHAR_ZERO) {
return (DDI_FAILURE);
}
if (chip->type != ENVCTRL_ENCL_BACKPLANE4 &&
chip->type != ENVCTRL_ENCL_BACKPLANE8) {
return (DDI_FAILURE);
}
backchip.type = PCF8574;
for (i = 0; i <= MAX_TAZ_CONTROLLERS; i++) {
if (controller_present[i] == -1)
continue;
backchip.chip_num = controller_addr[i];
envctrl_recv(unitp, (caddr_t *)(void *)&backchip, PCF8574);
if (chip->chip_num == controller_addr[i]) {
if (chip->val != ENVCTRL_CHAR_ZERO)
diskfault++;
} else if ((~backchip.val & 0xFF) != ENVCTRL_CHAR_ZERO) {
diskfault++;
}
}
fspchip.type = PCF8574A;
fspchip.chip_num = ENVCTRL_PCF8574_DEV6;
envctrl_recv(unitp, (caddr_t *)(void *)&fspchip, PCF8574);
if (diskfault) {
if (!(envctrl_isother_fault_led(unitp, fspchip.val & 0xFF,
ENVCTRL_FSP_DISK_ERR))) {
fspchip.val &= ~(ENVCTRL_FSP_DISK_ERR);
} else {
fspchip.val &= ~(ENVCTRL_FSP_DISK_ERR |
ENVCTRL_FSP_GEN_ERR);
}
fspchip.val = (fspchip.val &
~(ENVCTRL_FSP_DISK_ERR | ENVCTRL_FSP_GEN_ERR));
} else {
fspchip.val = (fspchip.val |
(ENVCTRL_FSP_DISK_ERR | ENVCTRL_FSP_GEN_ERR));
}
fspchip.type = PCF8574A;
fspchip.chip_num = ENVCTRL_PCF8574_DEV6;
(void) envctrl_xmit(unitp, (caddr_t *)(void *)&fspchip, PCF8574);
for (i = 0; i < (sizeof (backaddrs) / sizeof (uint8_t)); i++) {
if (chip->chip_num == backaddrs[i]) {
instance = i;
}
}
switch (chip->type) {
case ENVCTRL_ENCL_BACKPLANE4:
envctrl_mod_encl_kstats(unitp, ENVCTRL_ENCL_BACKPLANE4,
instance, chip->val);
break;
case ENVCTRL_ENCL_BACKPLANE8:
envctrl_mod_encl_kstats(unitp, ENVCTRL_ENCL_BACKPLANE8,
instance, chip->val);
break;
default:
break;
}
chip->type = PCF8574;
chip->val = ~(chip->val);
(void) envctrl_xmit(unitp, (caddr_t *)(void *)chip, PCF8574);
return (DDI_SUCCESS);
}
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, "envctrl%d: encl raw kstat_create failed",
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)) == NULL) {
cmn_err(CE_WARN, "envctrl%d: fans kstat_create failed",
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_PSNAME, "misc", KSTAT_TYPE_RAW,
sizeof (unitp->ps_kstats),
KSTAT_FLAG_PERSISTENT)) == NULL) {
cmn_err(CE_WARN, "envctrl%d: ps name kstat_create failed",
unitp->instance);
return;
}
unitp->psksp->ks_update = envctrl_ps_kstat_update;
unitp->psksp->ks_private = (void *)unitp;
kstat_install(unitp->psksp);
}
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) {
return (EACCES);
} else {
unitp->psksp->ks_ndata = unitp->num_ps_present;
bcopy(&unitp->ps_kstats, kstatp, sizeof (unitp->ps_kstats));
}
mutex_exit(&unitp->umutex);
return (DDI_SUCCESS);
}
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) {
return (EACCES);
} else {
unitp->fanksp->ks_ndata = unitp->num_fans_present;
bcopy(unitp->fan_kstats, kstatp, sizeof (unitp->fan_kstats));
}
mutex_exit(&unitp->umutex);
return (DDI_SUCCESS);
}
int
envctrl_encl_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) {
return (EACCES);
} else {
unitp->enclksp->ks_ndata = unitp->num_encl_present;
(void) envctrl_get_fpm_status(unitp);
bcopy(unitp->encl_kstats, kstatp, sizeof (unitp->encl_kstats));
}
mutex_exit(&unitp->umutex);
return (DDI_SUCCESS);
}
static void
envctrl_init_fan_kstats(struct envctrlunit *unitp)
{
int i;
ASSERT(MUTEX_HELD(&unitp->umutex));
for (i = 0; i < unitp->num_fans_present; i++) {
unitp->fan_kstats[i].instance = 0;
unitp->fan_kstats[i].type = 0;
unitp->fan_kstats[i].fans_ok = B_TRUE;
unitp->fan_kstats[i].fanflt_num = B_FALSE;
unitp->fan_kstats[i].fanspeed = B_FALSE;
}
unitp->fan_kstats[ENVCTRL_FAN_TYPE_PS].type = ENVCTRL_FAN_TYPE_PS;
unitp->fan_kstats[ENVCTRL_FAN_TYPE_CPU].type = ENVCTRL_FAN_TYPE_CPU;
if (unitp->AFB_present == B_TRUE)
unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].type =
ENVCTRL_FAN_TYPE_AFB;
}
static void
envctrl_init_encl_kstats(struct envctrlunit *unitp)
{
int i;
uint8_t val;
struct envctrl_pcf8574_chip chip;
int *reg_prop;
uint_t len = 0;
ASSERT(MUTEX_HELD(&unitp->umutex));
for (i = 0; i < MAX_DEVS; i++) {
unitp->encl_kstats[i].instance = I2C_NODEV;
}
chip.type = PCF8574A;
chip.chip_num = ENVCTRL_PCF8574_DEV6;
envctrl_recv(unitp, (caddr_t *)(void *)&chip, PCF8574);
envctrl_add_encl_kstats(unitp, ENVCTRL_ENCL_FSP, INSTANCE_0,
chip.val & 0xFF);
val = envctrl_get_lm75_temp(unitp) & 0xFF;
envctrl_add_encl_kstats(unitp, ENVCTRL_ENCL_AMBTEMPR, INSTANCE_0, val);
if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, unitp->dip,
DDI_PROP_DONTPASS, ENVCTRL_DISK_LEDS_PR,
®_prop, &len) != DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "prop lookup of %s failed\n",
ENVCTRL_DISK_LEDS_PR);
return;
}
ASSERT(len != 0);
chip.type = PCF8574;
for (i = 0; i < len; i++) {
chip.chip_num = backaddrs[i];
if (reg_prop[i] == ENVCTRL_4SLOT_BACKPLANE) {
envctrl_recv(unitp, (caddr_t *)(void *)&chip, PCF8574);
envctrl_add_encl_kstats(unitp, ENVCTRL_ENCL_BACKPLANE4,
i, ~chip.val);
controller_present[i] = 1;
}
if (reg_prop[i] == ENVCTRL_8SLOT_BACKPLANE) {
envctrl_recv(unitp, (caddr_t *)(void *)&chip, PCF8574);
envctrl_add_encl_kstats(unitp, ENVCTRL_ENCL_BACKPLANE8,
i, ~chip.val);
controller_present[i] = 1;
}
}
ddi_prop_free((void *)reg_prop);
}
static void
envctrl_mod_encl_kstats(struct envctrlunit *unitp, int type,
int instance, uint8_t val)
{
int i = 0;
boolean_t inserted = B_FALSE;
ASSERT(MUTEX_HELD(&unitp->umutex));
while (i < MAX_DEVS && inserted == B_FALSE) {
if (unitp->encl_kstats[i].instance == instance &&
unitp->encl_kstats[i].type == type) {
unitp->encl_kstats[i].value = val;
inserted = B_TRUE;
}
i++;
}
}
static void
envctrl_probe_cpus(struct envctrlunit *unitp)
{
int instance;
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_TAZCPU_STRING);
(void) sprintf(name1, "%s", ENVCTRL_TAZBLKBRDCPU_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, "envctrl no cpu upa-portid");
} else {
unitp->cpu_pr_location[cpu_slot] = B_TRUE;
unitp->num_cpus_present++;
}
}
return (DDI_WALK_CONTINUE);
}
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->num_fans_failed > 0 && thisled != 0) {
status = B_TRUE;
} else if (fspval & ENVCTRL_FSP_DISK_ERR) {
status = B_TRUE;
} else if (fspval & ENVCTRL_FSP_PS_ERR) {
status = B_TRUE;
} else if (fspval & ENVCTRL_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);
}
static int
eHc_start_pcf8584(struct eHc_envcunit *ehcp, uint8_t byteaddress)
{
uint8_t poll_status;
uint8_t discard;
int i;
i = 0;
do {
drv_usecwait(1000);
poll_status =
ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
i++;
} while (((poll_status & EHC_S1_NBB) == 0) && i < EHC_MAX_WAIT);
if (i == EHC_MAX_WAIT) {
DCMNERR(CE_WARN, "eHc_start_pcf8584: I2C bus busy");
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_BER) {
DCMN2ERR(CE_WARN, "eHc_start_pcf8584: I2C bus error");
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_LAB) {
DCMN2ERR(CE_WARN, "eHc_start_pcf8584: Lost arbitration");
return (EHC_FAILURE);
}
ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0, byteaddress);
ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1,
EHC_S1_PIN | EHC_S1_ES0 | EHC_S1_STA | EHC_S1_ACK);
i = 0;
do {
drv_usecwait(1000);
poll_status =
ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
i++;
} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);
if (i == EHC_MAX_WAIT) {
DCMNERR(CE_WARN, "eHc_start_pcf8584: I2C bus busy");
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_BER) {
DCMN2ERR(CE_WARN, "eHc_start_pcf8584: I2C bus error");
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_LAB) {
DCMN2ERR(CE_WARN, "eHc_start_pcf8584: Lost arbitration");
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_LRB) {
DCMNERR(CE_WARN, "eHc_start_pcf8584: No slave ACK");
return (EHC_NO_SLAVE_ACK);
}
i = 0;
if (byteaddress & EHC_BYTE_READ) {
discard = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
#ifdef lint
discard = discard;
#endif
do {
drv_usecwait(1000);
poll_status = ddi_get8(ehcp->ctlr_handle,
&ehcp->bus_ctl_regs->s1);
i++;
} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);
if (i == EHC_MAX_WAIT) {
DCMNERR(CE_WARN, "eHc_start_pcf8584: I2C bus busy");
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_BER) {
DCMN2ERR(CE_WARN,
"eHc_start_pcf8584: I2C bus error");
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_LAB) {
DCMN2ERR(CE_WARN,
"eHc_start_pcf8584: Lost arbitration");
return (EHC_FAILURE);
}
}
return (EHC_SUCCESS);
}
static void
eHc_stop_pcf8584(struct eHc_envcunit *ehcp)
{
ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1,
EHC_S1_PIN | EHC_S1_ES0 | EHC_S1_STO | EHC_S1_ACK);
}
static int
eHc_read_pcf8584(struct eHc_envcunit *ehcp, uint8_t *data)
{
uint8_t poll_status;
int i = 0;
*data = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
do {
drv_usecwait(1000);
poll_status =
ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
i++;
} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);
if (i == EHC_MAX_WAIT) {
DCMNERR(CE_WARN, "eHc_read_pcf8584: I2C bus busy");
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_BER) {
DCMN2ERR(CE_WARN, "eHc_read_pcf8584: I2C bus error");
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_LAB) {
DCMN2ERR(CE_WARN, "eHc_read_pcf8584: Lost arbitration");
return (EHC_FAILURE);
}
return (EHC_SUCCESS);
}
static int
eHc_write_pcf8584(struct eHc_envcunit *ehcp, uint8_t data)
{
uint8_t poll_status;
int i = 0;
ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0, data);
do {
drv_usecwait(1000);
poll_status =
ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
i++;
} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);
if (i == EHC_MAX_WAIT) {
DCMNERR(CE_WARN, "eHc_write_pcf8584: I2C bus busy");
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_BER) {
DCMN2ERR(CE_WARN, "eHc_write_pcf8584: I2C bus error");
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_LAB) {
DCMN2ERR(CE_WARN, "eHc_write_pcf8584: Lost arbitration");
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_LRB) {
DCMNERR(CE_WARN, "eHc_write_pcf8584: No slave ACK");
return (EHC_NO_SLAVE_ACK);
}
return (EHC_SUCCESS);
}
static int
eHc_after_read_pcf8584(struct eHc_envcunit *ehcp, uint8_t *data)
{
uint8_t discard;
uint8_t poll_status;
int i = 0;
ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1, EHC_S1_ES0);
*data = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
do {
drv_usecwait(1000);
poll_status =
ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
i++;
} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);
if (i == EHC_MAX_WAIT) {
DCMNERR(CE_WARN, "eHc_after_read_pcf8584: I2C bus busy");
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_BER) {
DCMN2ERR(CE_WARN,
"eHc_after_read_pcf8584: I2C bus error");
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_LAB) {
DCMN2ERR(CE_WARN, "eHc_after_read_pcf8584: Lost arbitration");
return (EHC_FAILURE);
}
eHc_stop_pcf8584(ehcp);
discard = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
#ifdef lint
discard = discard;
#endif
return (EHC_SUCCESS);
}
static int
eHc_write_tda8444(struct eHc_envcunit *ehcp, int byteaddress, int instruction,
int subaddress, uint8_t *buf, int size)
{
uint8_t control;
int i, status;
ASSERT((byteaddress & 0x1) == 0);
ASSERT(subaddress < 8);
ASSERT(instruction == 0xf || instruction == 0x0);
ASSERT(MUTEX_HELD(&ehcp->umutex));
control = (instruction << 4) | subaddress;
if ((status = eHc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK) {
eHc_stop_pcf8584(ehcp);
}
return (EHC_FAILURE);
}
if ((status = eHc_write_pcf8584(ehcp, control)) != EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK) {
eHc_stop_pcf8584(ehcp);
}
return (EHC_FAILURE);
}
for (i = 0; i < size; i++) {
if ((status = eHc_write_pcf8584(ehcp, (buf[i] & 0x3f))) !=
EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK)
eHc_stop_pcf8584(ehcp);
return (EHC_FAILURE);
}
}
eHc_stop_pcf8584(ehcp);
return (EHC_SUCCESS);
}
static int
eHc_read_pcf8574a(struct eHc_envcunit *ehcp, int byteaddress, uint8_t *buf,
int size)
{
int i;
int status;
uint8_t discard;
ASSERT((byteaddress & 0x1) == 0);
ASSERT(MUTEX_HELD(&ehcp->umutex));
if ((status = eHc_start_pcf8584(ehcp, EHC_BYTE_READ | byteaddress)) !=
EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK) {
eHc_stop_pcf8584(ehcp);
discard = ddi_get8(ehcp->ctlr_handle,
&ehcp->bus_ctl_regs->s0);
#ifdef lint
discard = discard;
#endif
}
return (EHC_FAILURE);
}
for (i = 0; i < size - 1; i++) {
if ((status = eHc_read_pcf8584(ehcp, &buf[i])) != EHC_SUCCESS) {
return (EHC_FAILURE);
}
}
if (eHc_after_read_pcf8584(ehcp, &buf[i]) != EHC_SUCCESS) {
return (EHC_FAILURE);
}
return (EHC_SUCCESS);
}
static int
eHc_write_pcf8574a(struct eHc_envcunit *ehcp, int byteaddress, uint8_t *buf,
int size)
{
int i;
int status;
ASSERT((byteaddress & 0x1) == 0);
ASSERT(MUTEX_HELD(&ehcp->umutex));
if ((status = eHc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK) {
eHc_stop_pcf8584(ehcp);
}
return (EHC_FAILURE);
}
for (i = 0; i < size; i++) {
if ((status = eHc_write_pcf8584(ehcp, buf[i])) != EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK)
eHc_stop_pcf8584(ehcp);
return (EHC_FAILURE);
}
}
eHc_stop_pcf8584(ehcp);
return (EHC_SUCCESS);
}
static int
eHc_read_pcf8574(struct eHc_envcunit *ehcp, int byteaddress, uint8_t *buf,
int size)
{
int i;
int status;
uint8_t discard;
ASSERT((byteaddress & 0x1) == 0);
ASSERT(MUTEX_HELD(&ehcp->umutex));
if ((status = eHc_start_pcf8584(ehcp, EHC_BYTE_READ | byteaddress)) !=
EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK) {
eHc_stop_pcf8584(ehcp);
discard = ddi_get8(ehcp->ctlr_handle,
&ehcp->bus_ctl_regs->s0);
#ifdef lint
discard = discard;
#endif
}
return (EHC_FAILURE);
}
for (i = 0; i < size - 1; i++) {
if ((status = eHc_read_pcf8584(ehcp, &buf[i])) != EHC_SUCCESS) {
return (EHC_FAILURE);
}
}
if (eHc_after_read_pcf8584(ehcp, &buf[i]) != EHC_SUCCESS) {
return (EHC_FAILURE);
}
return (EHC_SUCCESS);
}
static int
eHc_write_pcf8574(struct eHc_envcunit *ehcp, int byteaddress, uint8_t *buf,
int size)
{
int i;
int status;
ASSERT((byteaddress & 0x1) == 0);
ASSERT(MUTEX_HELD(&ehcp->umutex));
if ((status = eHc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK) {
eHc_stop_pcf8584(ehcp);
}
return (EHC_FAILURE);
}
for (i = 0; i < size; i++) {
if ((status = eHc_write_pcf8584(ehcp, buf[i])) != EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK)
eHc_stop_pcf8584(ehcp);
return (EHC_FAILURE);
}
}
eHc_stop_pcf8584(ehcp);
return (EHC_SUCCESS);
}
static int
eHc_read_lm75(struct eHc_envcunit *ehcp, int byteaddress, uint8_t *buf,
int size)
{
int i;
int status;
uint8_t discard;
ASSERT((byteaddress & 0x1) == 0);
ASSERT(MUTEX_HELD(&ehcp->umutex));
if ((status = eHc_start_pcf8584(ehcp, EHC_BYTE_READ | byteaddress)) !=
EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK) {
eHc_stop_pcf8584(ehcp);
discard = ddi_get8(ehcp->ctlr_handle,
&ehcp->bus_ctl_regs->s0);
#ifdef lint
discard = discard;
#endif
}
return (EHC_FAILURE);
}
for (i = 0; i < size - 1; i++) {
if ((status = eHc_read_pcf8584(ehcp, &buf[i])) != EHC_SUCCESS) {
return (EHC_FAILURE);
}
}
if (eHc_after_read_pcf8584(ehcp, &buf[i]) != EHC_SUCCESS) {
return (EHC_FAILURE);
}
return (EHC_SUCCESS);
}
static int
eHc_write_pcf8583(struct eHc_envcunit *ehcp, int byteaddress, uint8_t *buf,
int size)
{
int i;
int status;
ASSERT((byteaddress & 0x1) == 0);
ASSERT(MUTEX_HELD(&ehcp->umutex));
if ((status = eHc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK) {
eHc_stop_pcf8584(ehcp);
}
return (EHC_FAILURE);
}
for (i = 0; i < size; i++) {
if ((status = eHc_write_pcf8584(ehcp, buf[i])) != EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK)
eHc_stop_pcf8584(ehcp);
return (EHC_FAILURE);
}
}
eHc_stop_pcf8584(ehcp);
return (EHC_SUCCESS);
}
static int
eHc_read_pcf8591(struct eHc_envcunit *ehcp, int byteaddress, int channel,
int autoinc, int amode, int aenable, uint8_t *buf, int size)
{
int i;
int status;
uint8_t control;
uint8_t discard;
ASSERT((byteaddress & 0x1) == 0);
ASSERT(channel < 4);
ASSERT(amode < 4);
ASSERT(MUTEX_HELD(&ehcp->umutex));
control = ((aenable << 6) | (amode << 4) | (autoinc << 2) | channel);
if ((status = eHc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK) {
eHc_stop_pcf8584(ehcp);
}
return (EHC_FAILURE);
}
if ((status = eHc_write_pcf8584(ehcp, control)) != EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK)
eHc_stop_pcf8584(ehcp);
return (EHC_FAILURE);
}
ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1,
EHC_S1_ES0 | EHC_S1_STA | EHC_S1_ACK);
ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0,
EHC_BYTE_READ | byteaddress);
i = 0;
do {
drv_usecwait(1000);
status =
ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
i++;
} while ((status & EHC_S1_PIN) && i < EHC_MAX_WAIT);
if (i == EHC_MAX_WAIT) {
DCMNERR(CE_WARN, "eHc_read_pcf8591(): read of S1 failed");
return (EHC_FAILURE);
}
if (status & EHC_S1_LRB) {
DCMNERR(CE_WARN, "eHc_read_pcf8591(): No slave ACK");
eHc_stop_pcf8584(ehcp);
discard = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
#ifdef lint
discard = discard;
#endif
return (EHC_FAILURE);
}
if (status & EHC_S1_BER) {
DCMN2ERR(CE_WARN, "eHc_read_pcf8591(): Bus error");
return (EHC_FAILURE);
}
if (status & EHC_S1_LAB) {
DCMN2ERR(CE_WARN, "eHc_read_pcf8591(): Lost Arbitration");
return (EHC_FAILURE);
}
if ((status = eHc_read_pcf8584(ehcp, &discard)) != EHC_SUCCESS) {
return (EHC_FAILURE);
}
if ((status = eHc_read_pcf8584(ehcp, &discard)) != EHC_SUCCESS) {
return (EHC_FAILURE);
}
for (i = 0; i < size - 1; i++) {
if ((status = eHc_read_pcf8584(ehcp, &buf[i])) != EHC_SUCCESS) {
return (EHC_FAILURE);
}
}
if (eHc_after_read_pcf8584(ehcp, &buf[i]) != EHC_SUCCESS) {
return (EHC_FAILURE);
}
return (EHC_SUCCESS);
}