xref: /illumos-gate/usr/src/uts/sun4u/tazmo/io/envctrl.c (revision eb6b10e6)
129949e86Sstevel /*
229949e86Sstevel  * CDDL HEADER START
329949e86Sstevel  *
429949e86Sstevel  * The contents of this file are subject to the terms of the
529949e86Sstevel  * Common Development and Distribution License (the "License").
629949e86Sstevel  * You may not use this file except in compliance with the License.
729949e86Sstevel  *
829949e86Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
929949e86Sstevel  * or http://www.opensolaris.org/os/licensing.
1029949e86Sstevel  * See the License for the specific language governing permissions
1129949e86Sstevel  * and limitations under the License.
1229949e86Sstevel  *
1329949e86Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1429949e86Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1529949e86Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1629949e86Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1729949e86Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1829949e86Sstevel  *
1929949e86Sstevel  * CDDL HEADER END
2029949e86Sstevel  */
2129949e86Sstevel 
2229949e86Sstevel /*
2319397407SSherry Moore  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
2429949e86Sstevel  * Use is subject to license terms.
2529949e86Sstevel  */
2629949e86Sstevel 
2729949e86Sstevel 
2829949e86Sstevel /*
2929949e86Sstevel  * ENVCTRL_ Environment Monitoring driver for i2c
3029949e86Sstevel  *
3129949e86Sstevel  */
3229949e86Sstevel #include <sys/param.h>
3329949e86Sstevel #include <sys/types.h>
3429949e86Sstevel #include <sys/signal.h>
3529949e86Sstevel #include <sys/errno.h>
3629949e86Sstevel #include <sys/file.h>
3729949e86Sstevel #include <sys/termio.h>
3829949e86Sstevel #include <sys/termios.h>
3929949e86Sstevel #include <sys/cmn_err.h>
4029949e86Sstevel #include <sys/stream.h>
4129949e86Sstevel #include <sys/strsun.h>
4229949e86Sstevel #include <sys/stropts.h>
4329949e86Sstevel #include <sys/strtty.h>
4429949e86Sstevel #include <sys/debug.h>
4529949e86Sstevel #include <sys/eucioctl.h>
4629949e86Sstevel #include <sys/cred.h>
4729949e86Sstevel #include <sys/uio.h>
4829949e86Sstevel #include <sys/stat.h>
4929949e86Sstevel #include <sys/kmem.h>
5029949e86Sstevel 
5129949e86Sstevel #include <sys/ddi.h>
5229949e86Sstevel #include <sys/sunddi.h>
5329949e86Sstevel #include <sys/obpdefs.h>
5429949e86Sstevel #include <sys/conf.h>		/* req. by dev_ops flags MTSAFE etc. */
5529949e86Sstevel #include <sys/modctl.h>		/* for modldrv */
5629949e86Sstevel #include <sys/stat.h>		/* ddi_create_minor_node S_IFCHR */
5729949e86Sstevel #include <sys/open.h>		/* for open params.	 */
5829949e86Sstevel #include <sys/uio.h>		/* for read/write */
5929949e86Sstevel #include <sys/envctrl.h>	/* Environment header */
6029949e86Sstevel 
6129949e86Sstevel /* driver entry point fn definitions */
62*eb6b10e6SToomas Soome static int	envctrl_open(queue_t *, dev_t *, int, int, cred_t *);
6329949e86Sstevel static int	envctrl_close(queue_t *, int, cred_t *);
64*eb6b10e6SToomas Soome static uint_t	envctrl_bus_isr(caddr_t);
65*eb6b10e6SToomas Soome static uint_t	envctrl_dev_isr(caddr_t);
6629949e86Sstevel 
6729949e86Sstevel /* configuration entry point fn definitions */
68*eb6b10e6SToomas Soome static int	envctrl_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
6929949e86Sstevel static int	envctrl_attach(dev_info_t *, ddi_attach_cmd_t);
7029949e86Sstevel static int	envctrl_detach(dev_info_t *, ddi_detach_cmd_t);
7129949e86Sstevel 
7229949e86Sstevel /* Driver private routines */
7329949e86Sstevel static void	envctrl_init_bus(struct envctrlunit *);
7429949e86Sstevel static int	envctrl_xmit(struct envctrlunit *, caddr_t *, int);
7529949e86Sstevel static void	envctrl_recv(struct envctrlunit *, caddr_t *, int);
7629949e86Sstevel static void	envctrl_get_sys_temperatures(struct envctrlunit *, uint8_t *);
7729949e86Sstevel static int	envctrl_get_lm75_temp(struct envctrlunit *);
7829949e86Sstevel static int	envctrl_get_ps_temp(struct envctrlunit *, uint8_t);
7929949e86Sstevel static int	envctrl_get_cpu_temp(struct envctrlunit *, int);
8029949e86Sstevel static void	envctrl_fan_fail_service(struct envctrlunit *);
8129949e86Sstevel static void	envctrl_PS_intr_service(struct envctrlunit *, uint8_t);
8229949e86Sstevel static void	envctrl_ps_probe(struct envctrlunit *);
8329949e86Sstevel static void	envctrl_tempr_poll(void *);
8429949e86Sstevel static void	envctrl_pshotplug_poll(void *);
8529949e86Sstevel static void	envctrl_led_blink(void *);
8629949e86Sstevel static void	envctrl_reset_dflop(struct envctrlunit *);
8729949e86Sstevel static void	envctrl_enable_devintrs(struct envctrlunit *);
8829949e86Sstevel static void	envctrl_stop_clock(struct envctrlunit *);
8929949e86Sstevel static void	envctrl_reset_watchdog(struct envctrlunit *, uint8_t *);
9029949e86Sstevel static void	envctrl_abort_seq_handler(char *msg);
9129949e86Sstevel static uint8_t	envctrl_get_fpm_status(struct envctrlunit *);
9229949e86Sstevel static void	envctrl_set_fsp(struct envctrlunit *, uint8_t *);
9329949e86Sstevel static int	envctrl_set_dskled(struct envctrlunit *,
9429949e86Sstevel 				struct envctrl_pcf8574_chip *);
9529949e86Sstevel static int	envctrl_get_dskled(struct envctrlunit *,
9629949e86Sstevel 				struct envctrl_pcf8574_chip *);
9729949e86Sstevel static void	envctrl_probe_cpus(struct envctrlunit *);
9829949e86Sstevel static int	envctrl_match_cpu(dev_info_t *, void *);
9929949e86Sstevel static int	envctrl_isother_fault_led(struct envctrlunit *,
10029949e86Sstevel 		    uint8_t, uint8_t);
10129949e86Sstevel 
10229949e86Sstevel /* Kstat routines */
10329949e86Sstevel static void	envctrl_add_kstats(struct envctrlunit *);
10429949e86Sstevel static int	envctrl_ps_kstat_update(kstat_t *, int);
10529949e86Sstevel static int	envctrl_fanstat_kstat_update(kstat_t *, int);
10629949e86Sstevel static int	envctrl_encl_kstat_update(kstat_t *, int);
10729949e86Sstevel static void	envctrl_init_fan_kstats(struct envctrlunit *);
10829949e86Sstevel static void	envctrl_init_encl_kstats(struct envctrlunit *);
10929949e86Sstevel static void	envctrl_add_encl_kstats(struct envctrlunit *, int, int,
11029949e86Sstevel 			uint8_t);
11129949e86Sstevel static void	envctrl_mod_encl_kstats(struct envctrlunit *, int, int,
11229949e86Sstevel 			uint8_t);
11329949e86Sstevel 
11429949e86Sstevel 
11529949e86Sstevel /* Streams Routines */
11629949e86Sstevel static int	envctrl_wput(queue_t *, mblk_t *);
11729949e86Sstevel 
11829949e86Sstevel /* External routines */
11929949e86Sstevel extern void power_down(const char *);
12029949e86Sstevel extern int prom_getprop();
12129949e86Sstevel extern int prom_getproplen();
12229949e86Sstevel extern	void	prom_printf(const char *fmt, ...);
12329949e86Sstevel extern void (*abort_seq_handler)();
12429949e86Sstevel 
12529949e86Sstevel static void    *envctrlsoft_statep;
12629949e86Sstevel 
12729949e86Sstevel /* Local Variables */
12829949e86Sstevel /* Indicates whether or not the overtemp thread has been started */
12929949e86Sstevel static int	envctrl_debug_flags = 0;
13029949e86Sstevel static int	envctrl_afb_present = 0;
13129949e86Sstevel static int	envctrl_power_off_overide = 0;
13229949e86Sstevel static int	envctrl_max_retries = 100;
13329949e86Sstevel static int	envctrl_allow_detach = 0;
13429949e86Sstevel static int	envctrl_numcpus = 1;
13529949e86Sstevel static int	envctrl_p0_enclosure = 0; /* set to 1 if it is a P0 */
13629949e86Sstevel static int envctrl_handler = 1; /* 1 is the default */
13729949e86Sstevel static clock_t overtemp_timeout_hz;
13829949e86Sstevel static clock_t blink_timeout_hz;
13929949e86Sstevel static clock_t pshotplug_timeout_hz;
14029949e86Sstevel static int controller_present[] = {-1, -1, -1};
14129949e86Sstevel #ifdef MULTIFAN
14229949e86Sstevel static int	envctrl_fan_debug = 0;
14329949e86Sstevel #endif
144*eb6b10e6SToomas Soome static int	eHc_debug = 0;
14529949e86Sstevel static int	power_supply_previous_state[] = {-1, -1, -1};
14629949e86Sstevel 
14729949e86Sstevel extern void	pci_thermal_rem_intr(dev_info_t *, uint_t);
14829949e86Sstevel 
14929949e86Sstevel #define	LOOP_TIMEOUT 25
15029949e86Sstevel #define	INIT_FAN_VAL 35
15129949e86Sstevel #define	DCMNERR if (eHc_debug & 0x1) cmn_err
15229949e86Sstevel #define	DCMN2ERR if (eHc_debug & 0x2) cmn_err
15329949e86Sstevel #define	MAX_FAN_FAIL_RETRY 3
15429949e86Sstevel 
15529949e86Sstevel uint8_t backaddrs[] = {ENVCTRL_PCF8574_DEV0, ENVCTRL_PCF8574_DEV1,
15629949e86Sstevel     ENVCTRL_PCF8574_DEV2};
15729949e86Sstevel 
15829949e86Sstevel struct module_info envctrlinfo = {
15929949e86Sstevel 	/* id, name, min pkt siz, max pkt siz, hi water, low water */
16029949e86Sstevel 	42, "envctrl", 0, 2048, (1024 * 20), (1024 * 1)
16129949e86Sstevel };
16229949e86Sstevel 
16329949e86Sstevel static struct qinit envctrl_rinit = {
16429949e86Sstevel 	putq, NULL, envctrl_open, envctrl_close, NULL, &envctrlinfo, NULL
16529949e86Sstevel };
16629949e86Sstevel 
16729949e86Sstevel static struct qinit envctrl_wint = {
16829949e86Sstevel 	envctrl_wput, NULL, envctrl_open, envctrl_close,
16929949e86Sstevel 	    NULL, &envctrlinfo, NULL
17029949e86Sstevel };
17129949e86Sstevel 
17229949e86Sstevel struct streamtab envctrl_str_info = {
17329949e86Sstevel 	&envctrl_rinit, &envctrl_wint, NULL, NULL
17429949e86Sstevel };
17529949e86Sstevel 
17629949e86Sstevel static struct cb_ops envctrl_cb_ops = {
17729949e86Sstevel 	nodev,			/* cb_open */
17829949e86Sstevel 	nodev,			/* cb_close */
17929949e86Sstevel 	nodev,			/* cb_strategy */
18029949e86Sstevel 	nodev,			/* cb_print */
18129949e86Sstevel 	nodev,			/* cb_dump */
18229949e86Sstevel 	nodev,			/* cb_read */
18329949e86Sstevel 	nodev,			/* cb_write */
18429949e86Sstevel 	nodev,			/* cb_ioctl */
18529949e86Sstevel 	nodev,			/* cb_devmap */
18629949e86Sstevel 	nodev,			/* cb_mmap */
18729949e86Sstevel 	nodev,			/* cb_segmap */
18829949e86Sstevel 	nochpoll,		/* cb_chpoll */
18929949e86Sstevel 	ddi_prop_op,		/* cb_prop_op */
19029949e86Sstevel 	&envctrl_str_info,	/* cb_stream */
19129949e86Sstevel 	D_MP			/* cb_flag */
19229949e86Sstevel };
19329949e86Sstevel 
19429949e86Sstevel /*
19529949e86Sstevel  * Declare ops vectors for auto configuration.
19629949e86Sstevel  */
19729949e86Sstevel struct dev_ops  envctrl_ops = {
19829949e86Sstevel 	DEVO_REV,		/* devo_rev */
19929949e86Sstevel 	0,			/* devo_refcnt */
20029949e86Sstevel 	envctrl_getinfo,	/* devo_getinfo */
20129949e86Sstevel 	nulldev,		/* devo_identify */
20229949e86Sstevel 	nulldev,		/* devo_probe */
20329949e86Sstevel 	envctrl_attach,		/* devo_attach */
20429949e86Sstevel 	envctrl_detach,		/* devo_detach */
20529949e86Sstevel 	nodev,			/* devo_reset */
20629949e86Sstevel 	&envctrl_cb_ops,	/* devo_cb_ops */
20729949e86Sstevel 	(struct bus_ops *)NULL,	/* devo_bus_ops */
20819397407SSherry Moore 	nulldev,		/* devo_power */
20919397407SSherry Moore 	ddi_quiesce_not_supported,	/* devo_quiesce */
21029949e86Sstevel };
21129949e86Sstevel 
21229949e86Sstevel extern struct mod_ops mod_driverops;
21329949e86Sstevel 
21429949e86Sstevel static struct modldrv envctrlmodldrv = {
21529949e86Sstevel 	&mod_driverops,		/* type of module - driver */
21619397407SSherry Moore 	"I2C ENVCTRL_driver",
21729949e86Sstevel 	&envctrl_ops,
21829949e86Sstevel };
21929949e86Sstevel 
22029949e86Sstevel static struct modlinkage envctrlmodlinkage = {
22129949e86Sstevel 	MODREV_1,
22229949e86Sstevel 	&envctrlmodldrv,
22329949e86Sstevel 	0
22429949e86Sstevel };
22529949e86Sstevel 
22629949e86Sstevel /*
22729949e86Sstevel  * The following defines are for the i2c protocol routines.
22829949e86Sstevel  * This section of defines should be removed once the envctrl_targets.c
22929949e86Sstevel  * file is included.
23029949e86Sstevel  */
23129949e86Sstevel 
23229949e86Sstevel #define	EHC_SUCCESS 0
23329949e86Sstevel #define	EHC_FAILURE (-1)
23429949e86Sstevel #define	EHC_NO_SLAVE_ACK 3
23529949e86Sstevel 
23629949e86Sstevel #define	EHC_MAX_WAIT 7 /* decimal */
23729949e86Sstevel 
23829949e86Sstevel #define	EHC_S1_PIN 0x80
23929949e86Sstevel #define	EHC_S1_ES1 0x20
24029949e86Sstevel #define	EHC_S1_ES0 0x40
24129949e86Sstevel #define	EHC_S1_NBB 0x01
24229949e86Sstevel #define	EHC_S1_ACK 0x01
24329949e86Sstevel #define	EHC_S1_STA 0x04
24429949e86Sstevel #define	EHC_S1_STO 0x02
24529949e86Sstevel #define	EHC_S1_LRB 0x08
24629949e86Sstevel #define	EHC_S1_BER 0x10
24729949e86Sstevel #define	EHC_S1_LAB 0x02
24829949e86Sstevel 
24929949e86Sstevel #define	EHC_S0_OWN 0x55
25029949e86Sstevel #define	EHC_S0_CLK 0x1c
25129949e86Sstevel 
25229949e86Sstevel #define	EHC_BYTE_READ 0x01
25329949e86Sstevel 
25429949e86Sstevel #define	EHC_LONGEST_MSG 1000 /* decimal */
25529949e86Sstevel 
25629949e86Sstevel /*
25729949e86Sstevel  * PCF8591 Chip Used for temperature sensors
25829949e86Sstevel  *
25929949e86Sstevel  * Addressing Register definition.
26029949e86Sstevel  * A0-A2 valid range is 0-7
26129949e86Sstevel  *
26229949e86Sstevel  *  7    6  5   4    3     2     1      0
26329949e86Sstevel  * ------------------------------------------------
26429949e86Sstevel  * | 1 | 0 | 0 | 1 | A2 | A1 | A0 | R/W |
26529949e86Sstevel  * ------------------------------------------------
26629949e86Sstevel  */
26729949e86Sstevel 
26829949e86Sstevel 
26929949e86Sstevel #define	EHC_PCF8591_MAX_DEVS	0x08
27029949e86Sstevel 
27129949e86Sstevel #define	EHC_DEV0	0x00
27229949e86Sstevel #define	EHC_DEV1	0x02
27329949e86Sstevel #define	EHC_DEV2	0x04
27429949e86Sstevel #define	EHC_DEV3	0x06
27529949e86Sstevel #define	EHC_DEV4	0x08
27629949e86Sstevel #define	EHC_DEV5	0x0A
277*eb6b10e6SToomas Soome #define	EHC_DEV6	0x0C
27829949e86Sstevel #define	EHC_DEV7	0x0E
27929949e86Sstevel 
28029949e86Sstevel 
28129949e86Sstevel /*
282*eb6b10e6SToomas Soome  *		CONTROL OF CHIP
28329949e86Sstevel  * PCF8591 Temp sensing control register definitions
28429949e86Sstevel  *
28529949e86Sstevel  *   7      6     5   4  3   2      1   0
28629949e86Sstevel  * ---------------------------------------------
28729949e86Sstevel  * | 0 | AOE | X | X | 0 | AIF | X | X |
28829949e86Sstevel  * ---------------------------------------------
28929949e86Sstevel  * AOE = Analog out enable.. not used on out implementation
29029949e86Sstevel  * 5 & 4 = Analog Input Programming.. see data sheet for bits..
29129949e86Sstevel  *
29229949e86Sstevel  * AIF = Auto increment flag
29329949e86Sstevel  * bits 1 & 0 are for the Chennel number.
29429949e86Sstevel  */
29529949e86Sstevel 
29629949e86Sstevel #define	EHC_PCF8591_ANALOG_OUTPUT_EN	0x40
29729949e86Sstevel #define	EHC_PCF8591_ANALOG_INPUT_EN	0x00
29829949e86Sstevel #define	EHC_PCF8591_READ_BIT		0x01
29929949e86Sstevel 
30029949e86Sstevel 
30129949e86Sstevel #define	EHC_PCF8591_AUTO_INCR 0x04
30229949e86Sstevel #define	EHC_PCF8591_OSCILATOR 0x40
30329949e86Sstevel 
30429949e86Sstevel #define	EHC_PCF8591_MAX_PORTS	0x04
30529949e86Sstevel 
30629949e86Sstevel #define	EHC_PCF8591_CH_0	0x00
30729949e86Sstevel #define	EHC_PCF8591_CH_1	0x01
30829949e86Sstevel #define	EHC_PCF8591_CH_2	0x02
30929949e86Sstevel #define	EHC_PCF8591_CH_3	0x03
31029949e86Sstevel 
31129949e86Sstevel 
31229949e86Sstevel /*
31329949e86Sstevel  * PCF8574 Fan Fail, Power Supply Fail Detector
31429949e86Sstevel  * This device is driven by interrupts. Each time it interrupts
31529949e86Sstevel  * you must look at the CSR to see which ports caused the interrupt
31629949e86Sstevel  * they are indicated by a 1.
31729949e86Sstevel  *
31829949e86Sstevel  * Address map of this chip
31929949e86Sstevel  *
32029949e86Sstevel  * -------------------------------------------
32129949e86Sstevel  * | 0 | 1 | 1 | 1 | A2 | A1 | A0 | 0 |
32229949e86Sstevel  * -------------------------------------------
32329949e86Sstevel  *
32429949e86Sstevel  */
32529949e86Sstevel 
32629949e86Sstevel #define	EHC_PCF8574_PORT0	0x01
32729949e86Sstevel #define	EHC_PCF8574_PORT1	0x02
32829949e86Sstevel #define	EHC_PCF8574_PORT2	0x04
32929949e86Sstevel #define	EHC_PCF8574_PORT3	0x08
33029949e86Sstevel #define	EHC_PCF8574_PORT4	0x10
33129949e86Sstevel #define	EHC_PCF8574_PORT5	0x20
33229949e86Sstevel #define	EHC_PCF8574_PORT6	0x40
33329949e86Sstevel #define	EHC_PCF8574_PORT7	0x80
33429949e86Sstevel 
33529949e86Sstevel /*
33629949e86Sstevel  * Defines for the PCF8583 Clock Calendar Chip.
33729949e86Sstevel  */
33829949e86Sstevel #define	EHC_PCF8583_READ_BIT	0x01
33929949e86Sstevel #define	ALARM_CTR_REG_MINS	0x03
34029949e86Sstevel #define	ALARM_REG_MINS		0x0B
34129949e86Sstevel #define	ALARM_TIMER_REG		0x0F
34229949e86Sstevel 
34329949e86Sstevel struct eHc_pcd8584_regs {
34429949e86Sstevel 	uint8_t s0;		/* Own Address S0' */
34529949e86Sstevel 	uint8_t s1;		/* Control Status register */
34629949e86Sstevel 	uint8_t clock_s2;	/* Clock programming register */
34729949e86Sstevel };
34829949e86Sstevel 
34929949e86Sstevel struct eHc_envcunit {
35029949e86Sstevel 	struct eHc_pcd8584_regs *bus_ctl_regs;
35129949e86Sstevel 	ddi_acc_handle_t ctlr_handle;
35229949e86Sstevel 	kmutex_t umutex;
35329949e86Sstevel };
35429949e86Sstevel 
35529949e86Sstevel 
35629949e86Sstevel /*
35729949e86Sstevel  * Prototypes for static routines
35829949e86Sstevel  */
35929949e86Sstevel 
36029949e86Sstevel static int eHc_write_tda8444(struct eHc_envcunit *, int, int, int, uint8_t *,
36129949e86Sstevel 	int);
36229949e86Sstevel static int eHc_read_pcf8591(struct eHc_envcunit *, int, int, int, int, int,
36329949e86Sstevel 	uint8_t *, int);
36429949e86Sstevel static int eHc_read_pcf8574a(struct eHc_envcunit *, int, uint8_t *, int);
36529949e86Sstevel static int eHc_write_pcf8574a(struct eHc_envcunit *, int, uint8_t *, int);
36629949e86Sstevel static int eHc_read_pcf8574(struct eHc_envcunit *, int, uint8_t *, int);
36729949e86Sstevel static int eHc_write_pcf8574(struct eHc_envcunit *, int, uint8_t *, int);
36829949e86Sstevel static int eHc_read_lm75(struct eHc_envcunit *, int, uint8_t *, int);
36929949e86Sstevel static int eHc_write_pcf8583(struct eHc_envcunit *, int, uint8_t *, int);
37029949e86Sstevel 
37129949e86Sstevel static int eHc_start_pcf8584(struct eHc_envcunit *, uint8_t);
37229949e86Sstevel static void eHc_stop_pcf8584(struct eHc_envcunit *);
37329949e86Sstevel static int eHc_read_pcf8584(struct eHc_envcunit *, uint8_t *);
37429949e86Sstevel static int eHc_write_pcf8584(struct eHc_envcunit *, uint8_t);
37529949e86Sstevel static int eHc_after_read_pcf8584(struct eHc_envcunit *, uint8_t *);
37629949e86Sstevel 
37729949e86Sstevel /*
37829949e86Sstevel  * End of i2c protocol definitions section
37929949e86Sstevel  */
38029949e86Sstevel 
38129949e86Sstevel int
_init(void)38229949e86Sstevel _init(void)
38329949e86Sstevel {
38429949e86Sstevel 	int    error;
38529949e86Sstevel 
38629949e86Sstevel 	if ((error = mod_install(&envctrlmodlinkage)) == 0) {
38729949e86Sstevel 		(void) ddi_soft_state_init(&envctrlsoft_statep,
38819397407SSherry Moore 		    sizeof (struct envctrlunit), 1);
38929949e86Sstevel 	}
39029949e86Sstevel 
39129949e86Sstevel 	return (error);
39229949e86Sstevel }
39329949e86Sstevel 
39429949e86Sstevel int
_fini(void)39529949e86Sstevel _fini(void)
39629949e86Sstevel {
39729949e86Sstevel 	int    error;
39829949e86Sstevel 
39929949e86Sstevel 	if ((error = mod_remove(&envctrlmodlinkage)) == 0)
40029949e86Sstevel 		ddi_soft_state_fini(&envctrlsoft_statep);
40129949e86Sstevel 
40229949e86Sstevel 	return (error);
40329949e86Sstevel }
40429949e86Sstevel 
40529949e86Sstevel int
_info(struct modinfo * modinfop)40629949e86Sstevel _info(struct modinfo *modinfop)
40729949e86Sstevel {
40829949e86Sstevel 	return (mod_info(&envctrlmodlinkage, modinfop));
40929949e86Sstevel }
41029949e86Sstevel 
41129949e86Sstevel static int
envctrl_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)41229949e86Sstevel envctrl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
41329949e86Sstevel {
41429949e86Sstevel 	int	instance;
41529949e86Sstevel 	char		name[16];
41629949e86Sstevel 	uint8_t fspval;
41729949e86Sstevel 	struct	envctrlunit *unitp;
41829949e86Sstevel 	struct ddi_device_acc_attr attr;
41929949e86Sstevel 	int *reg_prop;
42029949e86Sstevel 	uchar_t *creg_prop;
42129949e86Sstevel 	uint_t len, tblsz;
42229949e86Sstevel 	int i, cputemp, status;
42329949e86Sstevel 	uint8_t buf[3];
42429949e86Sstevel 
42529949e86Sstevel 	status = len = tblsz = 0;
42629949e86Sstevel 
42729949e86Sstevel 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
42829949e86Sstevel 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
42929949e86Sstevel 
43029949e86Sstevel 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
43129949e86Sstevel 
43229949e86Sstevel 	instance = ddi_get_instance(dip);
43329949e86Sstevel 
43429949e86Sstevel 	switch (cmd) {
43529949e86Sstevel 	case DDI_ATTACH:
43629949e86Sstevel 		break;
43729949e86Sstevel 	case DDI_RESUME:
43829949e86Sstevel 		if (!(unitp = ddi_get_soft_state(envctrlsoft_statep, instance)))
43929949e86Sstevel 			return (DDI_FAILURE);
44029949e86Sstevel 		mutex_enter(&unitp->umutex);
44129949e86Sstevel 		if (!unitp->suspended) {
44229949e86Sstevel 			mutex_exit(&unitp->umutex);
44329949e86Sstevel 			return (DDI_FAILURE);
44429949e86Sstevel 		}
44529949e86Sstevel 		unitp->suspended = 0;
44629949e86Sstevel 		mutex_exit(&unitp->umutex);
44729949e86Sstevel 		unitp->initting = B_TRUE;
44829949e86Sstevel 		envctrl_init_bus(unitp);
44929949e86Sstevel 		unitp->initting = B_FALSE;
45029949e86Sstevel 
45129949e86Sstevel 		mutex_enter(&unitp->umutex);
45229949e86Sstevel 		envctrl_ps_probe(unitp);
45329949e86Sstevel 		envctrl_probe_cpus(unitp);
45429949e86Sstevel 		mutex_exit(&unitp->umutex);
45529949e86Sstevel 
45629949e86Sstevel 		return (DDI_SUCCESS);
45729949e86Sstevel 
45829949e86Sstevel 	default:
45929949e86Sstevel 		return (DDI_FAILURE);
46029949e86Sstevel 	}
46129949e86Sstevel 
46229949e86Sstevel 	/* Set up timer values */
46329949e86Sstevel 	overtemp_timeout_hz = drv_usectohz(OVERTEMP_TIMEOUT_USEC);
46429949e86Sstevel 	blink_timeout_hz = drv_usectohz(BLINK_TIMEOUT_USEC);
46529949e86Sstevel 	pshotplug_timeout_hz = drv_usectohz(BLINK_TIMEOUT_USEC * 6);
46629949e86Sstevel 
46729949e86Sstevel 	if (ddi_soft_state_zalloc(envctrlsoft_statep, instance) != 0) {
46829949e86Sstevel 		cmn_err(CE_WARN, "envctrl failed to zalloc softstate\n");
46929949e86Sstevel 		goto failed;
47029949e86Sstevel 	}
47129949e86Sstevel 
47229949e86Sstevel 	unitp = ddi_get_soft_state(envctrlsoft_statep, instance);
47329949e86Sstevel 
47429949e86Sstevel 	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&unitp->bus_ctl_regs, 0,
47519397407SSherry Moore 	    sizeof (struct envctrl_pcd8584_regs), &attr,
47619397407SSherry Moore 	    &unitp->ctlr_handle) != DDI_SUCCESS) {
47729949e86Sstevel 		cmn_err(CE_WARN, "I2c failed to map in bus_control regs\n");
47829949e86Sstevel 		return (DDI_FAILURE);
47929949e86Sstevel 	}
48029949e86Sstevel 
48129949e86Sstevel 	/*
48229949e86Sstevel 	 * If the PCI nexus has added a thermal interrupt, we first need
48329949e86Sstevel 	 * to remove that interrupt handler.
48429949e86Sstevel 	 *
48529949e86Sstevel 	 * WARNING: Removing another driver's interrupt handler is not
48629949e86Sstevel 	 * allowed. The pci_thermal_rem_intr() call below is needed to retain
48729949e86Sstevel 	 * the legacy behavior on Tazmo systems.
48829949e86Sstevel 	 */
48929949e86Sstevel 
49029949e86Sstevel 	pci_thermal_rem_intr(dip, (uint_t)0);
49129949e86Sstevel 
49229949e86Sstevel 	/* add interrupts */
49329949e86Sstevel 
49429949e86Sstevel 	if (ddi_get_iblock_cookie(dip, 1,
49519397407SSherry Moore 	    &unitp->ic_trap_cookie) != DDI_SUCCESS)  {
49629949e86Sstevel 		cmn_err(CE_WARN, "ddi_get_iblock_cookie FAILED \n");
49729949e86Sstevel 		goto failed;
49829949e86Sstevel 	}
49929949e86Sstevel 
50029949e86Sstevel 	mutex_init(&unitp->umutex, NULL, MUTEX_DRIVER,
50119397407SSherry Moore 	    (void *)unitp->ic_trap_cookie);
50229949e86Sstevel 
50329949e86Sstevel 
50429949e86Sstevel 	if (ddi_add_intr(dip, 0, &unitp->ic_trap_cookie, NULL, envctrl_bus_isr,
50519397407SSherry Moore 	    (caddr_t)unitp) != DDI_SUCCESS) {
50629949e86Sstevel 		cmn_err(CE_WARN, "envctrl_attach failed to add hard intr %d\n",
50719397407SSherry Moore 		    instance);
50829949e86Sstevel 		goto remlock;
50929949e86Sstevel 	}
51029949e86Sstevel 
51129949e86Sstevel 
51229949e86Sstevel 	if (ddi_add_intr(dip, 1, &unitp->ic_trap_cookie, NULL, envctrl_dev_isr,
51319397407SSherry Moore 	    (caddr_t)unitp) != DDI_SUCCESS) {
51429949e86Sstevel 		cmn_err(CE_WARN, "envctrl_attach failed to add hard intr %d\n",
51519397407SSherry Moore 		    instance);
51629949e86Sstevel 		goto remhardintr;
51729949e86Sstevel 	}
51829949e86Sstevel 
51929949e86Sstevel 
52029949e86Sstevel 	(void) sprintf(name, "envctrl%d", instance);
52129949e86Sstevel 
52229949e86Sstevel 	if (ddi_create_minor_node(dip, name, S_IFCHR, instance, DDI_PSEUDO,
523*eb6b10e6SToomas Soome 	    0) == DDI_FAILURE) {
52429949e86Sstevel 		ddi_remove_minor_node(dip, NULL);
52529949e86Sstevel 		goto remhardintr1;
52629949e86Sstevel 	}
52729949e86Sstevel 
52829949e86Sstevel 	mutex_enter(&unitp->umutex);
52929949e86Sstevel 	switch (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
53029949e86Sstevel 	    ENVCTRL_LED_BLINK, -1)) {
53129949e86Sstevel 	case 1:
53229949e86Sstevel 		unitp->activity_led_blink = B_TRUE;
53329949e86Sstevel 		break;
53429949e86Sstevel 	case 0:
53529949e86Sstevel 	default:
53629949e86Sstevel 		unitp->activity_led_blink = B_FALSE;
53729949e86Sstevel 		break;
53829949e86Sstevel 	}
53929949e86Sstevel 	unitp->shutdown = B_FALSE;
54029949e86Sstevel 	unitp->num_ps_present = unitp->num_encl_present = 0;
54129949e86Sstevel 	unitp->num_fans_present = MIN_FAN_BANKS;
54229949e86Sstevel 	unitp->num_fans_failed = ENVCTRL_CHAR_ZERO;
54329949e86Sstevel 	unitp->AFB_present = B_TRUE;
54429949e86Sstevel 	unitp->dip = dip;
54529949e86Sstevel 
54629949e86Sstevel #ifdef	DEBUG
54729949e86Sstevel 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
54819397407SSherry Moore 	    DDI_PROP_DONTPASS, ENVCTRL_PANEL_LEDS_PR,
54919397407SSherry Moore 	    &reg_prop, &len) == DDI_PROP_SUCCESS)
55029949e86Sstevel 		ddi_prop_free((void *)reg_prop);
55129949e86Sstevel 	ASSERT(len != 0);
55229949e86Sstevel 
55329949e86Sstevel 	len = 0;
55429949e86Sstevel 
55529949e86Sstevel 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
55619397407SSherry Moore 	    DDI_PROP_DONTPASS, ENVCTRL_PANEL_LEDS_STA,
55719397407SSherry Moore 	    &reg_prop, &len) == DDI_PROP_SUCCESS)
55829949e86Sstevel 		ddi_prop_free((void *)reg_prop);
55929949e86Sstevel 	ASSERT(len != 0);
56029949e86Sstevel 
56129949e86Sstevel 	len = 0;
56229949e86Sstevel 
56329949e86Sstevel 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
56419397407SSherry Moore 	    DDI_PROP_DONTPASS, ENVCTRL_DISK_LEDS_STA,
56519397407SSherry Moore 	    &reg_prop, &len) == DDI_PROP_SUCCESS)
56629949e86Sstevel 		ddi_prop_free((void *)reg_prop);
56729949e86Sstevel 	ASSERT(len != 0);
56829949e86Sstevel #endif	/* DEBUG */
56929949e86Sstevel 
57029949e86Sstevel 	/*
57129949e86Sstevel 	 * if we have prom fan tables, overide the static tables in
57229949e86Sstevel 	 * header file.
57329949e86Sstevel 	 */
57429949e86Sstevel 
57529949e86Sstevel 	if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
57619397407SSherry Moore 	    DDI_PROP_DONTPASS, "cpu-fan-speeds",
57719397407SSherry Moore 	    &creg_prop, &len) == DDI_PROP_SUCCESS) {
57829949e86Sstevel 
57929949e86Sstevel 		tblsz = (sizeof (acme_cpu_fanspd) / sizeof (short));
58029949e86Sstevel 
58129949e86Sstevel 		if (len <= tblsz) {
58229949e86Sstevel 			for (i = 0; i < len; i++) {
58329949e86Sstevel 				acme_cpu_fanspd[i] = creg_prop[i];
58429949e86Sstevel 			}
58529949e86Sstevel 		}
58629949e86Sstevel 		ddi_prop_free((void *)creg_prop);
58729949e86Sstevel 	}
58829949e86Sstevel 
58929949e86Sstevel 	len = 0;
59029949e86Sstevel 
59129949e86Sstevel 	if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
59219397407SSherry Moore 	    DDI_PROP_DONTPASS, "ps-fan-speeds",
59319397407SSherry Moore 	    &creg_prop, &len) == DDI_PROP_SUCCESS) {
59429949e86Sstevel 
59529949e86Sstevel 		tblsz = (sizeof (acme_ps_fanspd) / sizeof (short));
59629949e86Sstevel 
59729949e86Sstevel 		if (len <= tblsz) {
59829949e86Sstevel 			for (i = 0; i < len; i++) {
59929949e86Sstevel 				acme_ps_fanspd[i] = creg_prop[i];
60029949e86Sstevel 			}
60129949e86Sstevel 		}
60229949e86Sstevel 		ddi_prop_free((void *)creg_prop);
60329949e86Sstevel 	}
60429949e86Sstevel 
60529949e86Sstevel 	switch (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
60629949e86Sstevel 	    "fan-override", -1)) {
60729949e86Sstevel 	case 1:
60829949e86Sstevel 	case 2:
60929949e86Sstevel 		unitp->AFB_present = B_TRUE;
61029949e86Sstevel 		break;
61129949e86Sstevel 	case 0:
61229949e86Sstevel 	default:
61329949e86Sstevel 		unitp->AFB_present = B_FALSE;
61429949e86Sstevel 		break;
61529949e86Sstevel 	}
61629949e86Sstevel 
61729949e86Sstevel 	/* For debug */
61829949e86Sstevel 	if (envctrl_afb_present) {
61929949e86Sstevel 		unitp->AFB_present = B_TRUE;
62029949e86Sstevel 	}
62129949e86Sstevel 
62229949e86Sstevel 	if (unitp->AFB_present == B_TRUE)
62329949e86Sstevel 		unitp->num_fans_present++;
62429949e86Sstevel 
62529949e86Sstevel 	/* initialize the envctrl bus controller */
62629949e86Sstevel 	mutex_exit(&unitp->umutex);
62729949e86Sstevel 
62829949e86Sstevel 	unitp->initting = B_TRUE;
62929949e86Sstevel 	envctrl_init_bus(unitp);
63029949e86Sstevel 	unitp->initting = B_FALSE;
63129949e86Sstevel 	drv_usecwait(1000);
63229949e86Sstevel 
63329949e86Sstevel 	mutex_enter(&unitp->umutex);
63429949e86Sstevel 
63529949e86Sstevel 	/* Initialize the PCF8583 eggtimer registers */
63629949e86Sstevel 	buf[0] = ALARM_CTR_REG_MINS;
63729949e86Sstevel 	buf[1] = 0x0;
63829949e86Sstevel 	status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
63919397407SSherry Moore 	    PCF8583_BASE_ADDR | 0, buf, 2);
64029949e86Sstevel 	if (status != DDI_SUCCESS)
64129949e86Sstevel 		cmn_err(CE_WARN, "write to PCF8583 failed\n");
64229949e86Sstevel 
64329949e86Sstevel 	buf[0] = ALARM_REG_MINS;
64429949e86Sstevel 	buf[1] = 0x58;
64529949e86Sstevel 	status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
64619397407SSherry Moore 	    PCF8583_BASE_ADDR | 0, buf, 2);
64729949e86Sstevel 	if (status != DDI_SUCCESS)
64829949e86Sstevel 		cmn_err(CE_WARN, "write to PCF8583 failed\n");
64929949e86Sstevel 
65029949e86Sstevel 	buf[0] = ALARM_TIMER_REG;
65129949e86Sstevel 	buf[1] = 0x80;
65229949e86Sstevel 	status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
65319397407SSherry Moore 	    PCF8583_BASE_ADDR | 0, buf, 2);
65429949e86Sstevel 	if (status != DDI_SUCCESS)
65529949e86Sstevel 		cmn_err(CE_WARN, "write to PCF8583 failed\n");
65629949e86Sstevel 
65729949e86Sstevel 	unitp->timeout_id = 0;
65829949e86Sstevel 	unitp->blink_timeout_id = 0;
65929949e86Sstevel 
66029949e86Sstevel 	if (envctrl_numcpus > 1) {
66129949e86Sstevel 		unitp->num_cpus_present = envctrl_numcpus;
66229949e86Sstevel 	}
66329949e86Sstevel 	envctrl_probe_cpus(unitp);
66429949e86Sstevel 	envctrl_ps_probe(unitp);
66529949e86Sstevel 	/*
66629949e86Sstevel 	 * clear the fan failures, if any before we do
66729949e86Sstevel 	 * real work
66829949e86Sstevel 	 */
66929949e86Sstevel 
67029949e86Sstevel 	unitp->initting = B_TRUE;
67129949e86Sstevel 	envctrl_fan_fail_service(unitp);
67229949e86Sstevel 	unitp->initting = B_FALSE;
67329949e86Sstevel 
67429949e86Sstevel 	/*
67529949e86Sstevel 	 * we need to init the fan kstats before the tempr_poll
67629949e86Sstevel 	 */
67729949e86Sstevel 	envctrl_add_kstats(unitp);
67829949e86Sstevel 	envctrl_init_fan_kstats(unitp);
67929949e86Sstevel 	envctrl_init_encl_kstats(unitp);
68029949e86Sstevel 	if (unitp->activity_led_blink == B_TRUE) {
68129949e86Sstevel 		unitp->present_led_state = B_FALSE;
68229949e86Sstevel 		mutex_exit(&unitp->umutex);
68329949e86Sstevel 		envctrl_led_blink((void *)unitp);
68429949e86Sstevel 		mutex_enter(&unitp->umutex);
68529949e86Sstevel 	} else {
68629949e86Sstevel 		fspval = ENVCTRL_FSP_ACTIVE;
68729949e86Sstevel 		envctrl_set_fsp(unitp, &fspval);
68829949e86Sstevel 	}
68929949e86Sstevel 
69029949e86Sstevel #ifndef TESTBED
69129949e86Sstevel 	for (i = 0; i < ENVCTRL_MAX_CPUS; i++) {
69229949e86Sstevel 		if (unitp->cpu_pr_location[i] == B_TRUE) {
69329949e86Sstevel 			cputemp = envctrl_get_cpu_temp(unitp, i);
69429949e86Sstevel 			envctrl_add_encl_kstats(unitp, ENVCTRL_ENCL_CPUTEMPR,
69529949e86Sstevel 			    i, cputemp);
69629949e86Sstevel 			if (cputemp >= MAX_CPU_TEMP) {
69729949e86Sstevel 				if (!(envctrl_power_off_overide)) {
69829949e86Sstevel 					cmn_err(CE_WARN,
69929949e86Sstevel 					    "CPU %d OVERHEATING!!", i);
70029949e86Sstevel 					unitp->shutdown = B_TRUE;
70129949e86Sstevel 				} else {
70229949e86Sstevel 					cmn_err(CE_WARN,
70329949e86Sstevel 					    "CPU %d OVERHEATING!!", i);
70429949e86Sstevel 				}
70529949e86Sstevel 			}
70629949e86Sstevel 		}
70729949e86Sstevel 	}
70829949e86Sstevel #else
70929949e86Sstevel 	cputemp = envctrl_get_cpu_temp(unitp, 0);
71029949e86Sstevel 	envctrl_add_encl_kstats(unitp, ENVCTRL_ENCL_CPUTEMPR, INSTANCE_0,
71129949e86Sstevel 	    cputemp);
71229949e86Sstevel #endif
71329949e86Sstevel 	mutex_exit(&unitp->umutex);
71429949e86Sstevel 
71529949e86Sstevel 	envctrl_tempr_poll((void *)unitp);
71629949e86Sstevel 
71729949e86Sstevel 	/*
71829949e86Sstevel 	 * interpose envctrl's abort sequence handler
71929949e86Sstevel 	 */
72029949e86Sstevel 	if (envctrl_handler) {
72129949e86Sstevel 		abort_seq_handler = envctrl_abort_seq_handler;
72229949e86Sstevel 	}
72329949e86Sstevel 
72429949e86Sstevel 	ddi_report_dev(dip);
72529949e86Sstevel 
72629949e86Sstevel 	return (DDI_SUCCESS);
72729949e86Sstevel 
72829949e86Sstevel remhardintr1:
72929949e86Sstevel 	ddi_remove_intr(dip, (uint_t)1, unitp->ic_trap_cookie);
73029949e86Sstevel remhardintr:
73129949e86Sstevel 	ddi_remove_intr(dip, (uint_t)0, unitp->ic_trap_cookie);
73229949e86Sstevel 
73329949e86Sstevel remlock:
73429949e86Sstevel 	mutex_destroy(&unitp->umutex);
73529949e86Sstevel 
73629949e86Sstevel failed:
73729949e86Sstevel 	if (unitp->ctlr_handle)
73829949e86Sstevel 		ddi_regs_map_free(&unitp->ctlr_handle);
73929949e86Sstevel 
74029949e86Sstevel 	cmn_err(CE_WARN, "envctrl_attach:failed.\n");
74129949e86Sstevel 
74229949e86Sstevel 	return (DDI_FAILURE);
74329949e86Sstevel 
74429949e86Sstevel }
74529949e86Sstevel 
74629949e86Sstevel static int
envctrl_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)74729949e86Sstevel envctrl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
74829949e86Sstevel {
74929949e86Sstevel 	int		instance;
75029949e86Sstevel 	struct envctrlunit *unitp;
75129949e86Sstevel 
75229949e86Sstevel 	instance = ddi_get_instance(dip);
75329949e86Sstevel 	unitp = ddi_get_soft_state(envctrlsoft_statep, instance);
75429949e86Sstevel 
75529949e86Sstevel 	switch (cmd) {
75629949e86Sstevel 	case DDI_DETACH:
75729949e86Sstevel 		if (envctrl_allow_detach) {
75829949e86Sstevel 
75929949e86Sstevel 			if (unitp->psksp != NULL) {
76029949e86Sstevel 				kstat_delete(unitp->psksp);
76129949e86Sstevel 			}
76229949e86Sstevel 			if (unitp->fanksp != NULL) {
76329949e86Sstevel 				kstat_delete(unitp->fanksp);
76429949e86Sstevel 			}
76529949e86Sstevel 			if (unitp->enclksp != NULL) {
76629949e86Sstevel 				kstat_delete(unitp->enclksp);
76729949e86Sstevel 			}
76829949e86Sstevel 
76929949e86Sstevel 			if (unitp->timeout_id != 0) {
77029949e86Sstevel 				(void) untimeout(unitp->timeout_id);
77129949e86Sstevel 				unitp->timeout_id = 0;
77229949e86Sstevel 			}
77329949e86Sstevel 			if (unitp->blink_timeout_id != 0) {
77429949e86Sstevel 				(void) untimeout(unitp->blink_timeout_id);
77529949e86Sstevel 				unitp->blink_timeout_id = 0;
77629949e86Sstevel 			}
77729949e86Sstevel 
77829949e86Sstevel 			ddi_remove_minor_node(dip, NULL);
77929949e86Sstevel 
78029949e86Sstevel 			ddi_remove_intr(dip, (uint_t)0, unitp->ic_trap_cookie);
78129949e86Sstevel 			ddi_remove_intr(dip, (uint_t)1, unitp->ic_trap_cookie);
78229949e86Sstevel 
78329949e86Sstevel 			ddi_regs_map_free(&unitp->ctlr_handle);
78429949e86Sstevel 
78529949e86Sstevel 			mutex_destroy(&unitp->umutex);
78629949e86Sstevel 
78729949e86Sstevel 			return (DDI_SUCCESS);
78829949e86Sstevel 		} else {
78929949e86Sstevel 			return (DDI_FAILURE);
79029949e86Sstevel 		}
79129949e86Sstevel 
79229949e86Sstevel 	case DDI_SUSPEND:
79329949e86Sstevel 		if (!(unitp = ddi_get_soft_state(envctrlsoft_statep, instance)))
79419397407SSherry Moore 			return (DDI_FAILURE);
79529949e86Sstevel 		mutex_enter(&unitp->umutex);
79629949e86Sstevel 		if (unitp->suspended) {
79729949e86Sstevel 			cmn_err(CE_WARN, "envctrl already suspended\n");
79829949e86Sstevel 			mutex_exit(&unitp->umutex);
79929949e86Sstevel 			return (DDI_FAILURE);
80029949e86Sstevel 		}
80129949e86Sstevel 		unitp->suspended = 1;
80229949e86Sstevel 		mutex_exit(&unitp->umutex);
80329949e86Sstevel 		return (DDI_SUCCESS);
80429949e86Sstevel 
80529949e86Sstevel 	default:
80629949e86Sstevel 		cmn_err(CE_WARN, "envctrl suspend general fault\n");
80729949e86Sstevel 		return (DDI_FAILURE);
80829949e86Sstevel 	}
80929949e86Sstevel 
81029949e86Sstevel 
81129949e86Sstevel }
81229949e86Sstevel 
81329949e86Sstevel /* ARGSUSED */
81429949e86Sstevel int
envctrl_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)81529949e86Sstevel envctrl_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
81629949e86Sstevel     void **result)
81729949e86Sstevel {
81829949e86Sstevel 	dev_t	dev = (dev_t)arg;
81929949e86Sstevel 	struct envctrlunit *unitp;
82029949e86Sstevel 	int	ret;
82129949e86Sstevel 	minor_t instance = getminor(dev);
82229949e86Sstevel 
82329949e86Sstevel 	switch (infocmd) {
82429949e86Sstevel 		case DDI_INFO_DEVT2DEVINFO:
82529949e86Sstevel 			if ((unitp = (struct envctrlunit *)
82619397407SSherry Moore 			    ddi_get_soft_state(envctrlsoft_statep,
82719397407SSherry Moore 			    instance)) != NULL) {
82829949e86Sstevel 				*result = unitp->dip;
82929949e86Sstevel 				ret = DDI_SUCCESS;
83029949e86Sstevel 			} else {
83129949e86Sstevel 				*result = NULL;
83229949e86Sstevel 				ret = DDI_FAILURE;
83329949e86Sstevel 			}
83429949e86Sstevel 			break;
83529949e86Sstevel 		case DDI_INFO_DEVT2INSTANCE:
83629949e86Sstevel 			*result = (void *)(uintptr_t)instance;
83729949e86Sstevel 			ret = DDI_SUCCESS;
83829949e86Sstevel 			break;
83929949e86Sstevel 		default:
84029949e86Sstevel 			ret = DDI_FAILURE;
84129949e86Sstevel 			break;
84229949e86Sstevel 	}
84329949e86Sstevel 
84429949e86Sstevel 	return (ret);
84529949e86Sstevel }
84629949e86Sstevel 
84729949e86Sstevel /* ARGSUSED */
84829949e86Sstevel static int
envctrl_open(queue_t * q,dev_t * dev,int flag,int sflag,cred_t * credp)84929949e86Sstevel envctrl_open(queue_t *q, dev_t *dev, int flag, int sflag, cred_t *credp)
85029949e86Sstevel {
85129949e86Sstevel 	struct envctrlunit *unitp;
85229949e86Sstevel 	int status = 0;
85329949e86Sstevel 	int	instance;
85429949e86Sstevel 
85529949e86Sstevel 	instance = getminor(*dev);
85629949e86Sstevel 	if (instance < 0)
85729949e86Sstevel 		return (ENXIO);
85829949e86Sstevel 	unitp = (struct envctrlunit *)
85919397407SSherry Moore 	    ddi_get_soft_state(envctrlsoft_statep, instance);
86029949e86Sstevel 
86129949e86Sstevel 	if (unitp == NULL)
86229949e86Sstevel 		return (ENXIO);
86329949e86Sstevel 
86429949e86Sstevel 	mutex_enter(&unitp->umutex);
86529949e86Sstevel 
86629949e86Sstevel 	if (flag & FWRITE) {
86729949e86Sstevel 		if ((unitp->oflag & FWRITE)) {
86829949e86Sstevel 			mutex_exit(&unitp->umutex);
86929949e86Sstevel 			return (EBUSY);
87029949e86Sstevel 		} else {
87129949e86Sstevel 			unitp->oflag |= FWRITE;
87229949e86Sstevel 		}
87329949e86Sstevel 	}
87429949e86Sstevel 
87529949e86Sstevel 	q->q_ptr = WR(q)->q_ptr = (caddr_t)unitp;
87629949e86Sstevel 
87729949e86Sstevel 	/*
87829949e86Sstevel 	 * if device is open with O_NONBLOCK flag set, let read(2) return 0
87929949e86Sstevel 	 * if no data waiting to be read.  Writes will block on flow control.
88029949e86Sstevel 	 */
88129949e86Sstevel 
88229949e86Sstevel 	/* enable the stream */
88329949e86Sstevel 	qprocson(q);
88429949e86Sstevel 
88529949e86Sstevel 	unitp->readq = RD(q);
88629949e86Sstevel 	unitp->writeq = WR(q);
88729949e86Sstevel 	unitp->msg = (mblk_t *)NULL;
88829949e86Sstevel 
88929949e86Sstevel 	mutex_exit(&unitp->umutex);
89029949e86Sstevel 	return (status);
89129949e86Sstevel }
89229949e86Sstevel 
89329949e86Sstevel /* ARGSUSED */
89429949e86Sstevel static int
envctrl_close(queue_t * q,int flag,cred_t * cred_p)89529949e86Sstevel envctrl_close(queue_t *q, int flag, cred_t *cred_p)
89629949e86Sstevel {
89729949e86Sstevel 	struct envctrlunit *unitp;
89829949e86Sstevel 
89929949e86Sstevel 	unitp = (struct envctrlunit *)q->q_ptr;
90029949e86Sstevel 
90129949e86Sstevel 	mutex_enter(&unitp->umutex);
90229949e86Sstevel 
90329949e86Sstevel 	unitp->oflag = B_FALSE;
90429949e86Sstevel 	unitp->current_mode = ENVCTRL_NORMAL_MODE;
90529949e86Sstevel 
90629949e86Sstevel 	/* disable the stream */
90729949e86Sstevel 	q->q_ptr = WR(q)->q_ptr = NULL;
90829949e86Sstevel 	qprocsoff(q);
90929949e86Sstevel 
91029949e86Sstevel 	mutex_exit(&unitp->umutex);
91129949e86Sstevel 	return (DDI_SUCCESS);
91229949e86Sstevel }
91329949e86Sstevel 
91429949e86Sstevel /*
91529949e86Sstevel  * standard put procedure for envctrl
91629949e86Sstevel  */
91729949e86Sstevel static int
envctrl_wput(queue_t * q,mblk_t * mp)91829949e86Sstevel envctrl_wput(queue_t *q, mblk_t *mp)
91929949e86Sstevel {
92029949e86Sstevel 	struct msgb *mp1;
92129949e86Sstevel 	struct envctrlunit *unitp;
92229949e86Sstevel 	struct iocblk *iocp;
92329949e86Sstevel 	struct copyresp *csp;
92429949e86Sstevel 	struct envctrl_tda8444t_chip *fanspeed;
92529949e86Sstevel 	struct envctrl_pcf8574_chip *ledchip;
92629949e86Sstevel 	struct envctrl_pcf8591_chip *temp, *a_fanspeed;
92729949e86Sstevel 	struct copyreq *cqp;
92829949e86Sstevel 	int cmd;
92929949e86Sstevel 
93029949e86Sstevel 	unitp = (struct envctrlunit *)q->q_ptr;
93129949e86Sstevel 
93229949e86Sstevel 	switch (DB_TYPE(mp)) {
93329949e86Sstevel 
93429949e86Sstevel 	case M_DATA:
93529949e86Sstevel 
93629949e86Sstevel 		while (mp) {
93729949e86Sstevel 			DB_TYPE(mp) = M_DATA;
93829949e86Sstevel 			mp1 = unlinkb(mp);
93929949e86Sstevel 			mp->b_cont = NULL;
94029949e86Sstevel 			if ((mp->b_wptr - mp->b_rptr) <= 0) {
94129949e86Sstevel 				freemsg(mp);
94229949e86Sstevel 			} else {
94329949e86Sstevel 				(void) putq(q, mp);
94429949e86Sstevel 			}
94529949e86Sstevel 			mp = mp1;
94629949e86Sstevel 		}
94729949e86Sstevel 
94829949e86Sstevel 		break;
94929949e86Sstevel 
95029949e86Sstevel 	case M_IOCTL:
95129949e86Sstevel 	{
95229949e86Sstevel 		iocp = (struct iocblk *)(void *)mp->b_rptr;
95329949e86Sstevel 		cmd = iocp->ioc_cmd;
95429949e86Sstevel 
95529949e86Sstevel 		switch (cmd) {
95629949e86Sstevel 		case ENVCTRL_IOC_SETMODE:
95729949e86Sstevel 		case ENVCTRL_IOC_GETMODE:
95829949e86Sstevel 			if (iocp->ioc_count == TRANSPARENT) {
95929949e86Sstevel 				mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
96029949e86Sstevel 				    sizeof (uchar_t), NULL);
96129949e86Sstevel 				qreply(q, mp);
96229949e86Sstevel 			} else {
96329949e86Sstevel 				miocnak(q, mp, 0, EINVAL);
96429949e86Sstevel 			}
96529949e86Sstevel 			break;
96629949e86Sstevel 		case ENVCTRL_IOC_RESETTMPR:
96729949e86Sstevel 			/*
96829949e86Sstevel 			 * For diags, cancel the current temp poll
96929949e86Sstevel 			 * and reset it for a new one.
97029949e86Sstevel 			 */
97129949e86Sstevel 			if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
97229949e86Sstevel 				if (unitp->timeout_id != 0) {
97329949e86Sstevel 					(void) untimeout(unitp->timeout_id);
97429949e86Sstevel 					unitp->timeout_id = 0;
97529949e86Sstevel 				}
97629949e86Sstevel 				envctrl_tempr_poll((void *)unitp);
97729949e86Sstevel 				miocack(q, mp, 0, 0);
97829949e86Sstevel 			} else {
97929949e86Sstevel 				miocnak(q, mp, 0, EINVAL);
98029949e86Sstevel 			}
98129949e86Sstevel 			break;
98229949e86Sstevel 		case ENVCTRL_IOC_GETTEMP:
98329949e86Sstevel 			if (iocp->ioc_count == TRANSPARENT) {
98429949e86Sstevel 				mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
98529949e86Sstevel 				    sizeof (struct envctrl_pcf8591_chip), NULL);
98629949e86Sstevel 				qreply(q, mp);
98729949e86Sstevel 			} else {
98829949e86Sstevel 				miocnak(q, mp, 0, EINVAL);
98929949e86Sstevel 			}
99029949e86Sstevel 			break;
99129949e86Sstevel 		case ENVCTRL_IOC_SETTEMP:
99229949e86Sstevel 			if (unitp->current_mode == ENVCTRL_DIAG_MODE &&
99329949e86Sstevel 			    iocp->ioc_count == TRANSPARENT) {
99429949e86Sstevel 				mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
99529949e86Sstevel 				    sizeof (uint8_t), NULL);
99629949e86Sstevel 				qreply(q, mp);
99729949e86Sstevel 			} else {
99829949e86Sstevel 				miocnak(q, mp, 0, EINVAL);
99929949e86Sstevel 			}
100029949e86Sstevel 			break;
100129949e86Sstevel 		case ENVCTRL_IOC_SETWDT:
100229949e86Sstevel 			if (unitp->current_mode == ENVCTRL_DIAG_MODE &&
100329949e86Sstevel 			    iocp->ioc_count == TRANSPARENT) {
100429949e86Sstevel 				mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
100529949e86Sstevel 				    sizeof (uint8_t), NULL);
100629949e86Sstevel 				qreply(q, mp);
100729949e86Sstevel 			} else {
100829949e86Sstevel 				miocnak(q, mp, 0, EINVAL);
100929949e86Sstevel 			}
101029949e86Sstevel 			break;
101129949e86Sstevel 		case ENVCTRL_IOC_SETFAN:
101229949e86Sstevel 			/*
101329949e86Sstevel 			 * we must be in diag mode before we can
101429949e86Sstevel 			 * set any fan speeds.
101529949e86Sstevel 			 */
101629949e86Sstevel 			if (unitp->current_mode == ENVCTRL_DIAG_MODE &&
101729949e86Sstevel 			    iocp->ioc_count == TRANSPARENT) {
101829949e86Sstevel 				mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
101929949e86Sstevel 				    sizeof (struct envctrl_tda8444t_chip),
102029949e86Sstevel 				    NULL);
102129949e86Sstevel 				qreply(q, mp);
102229949e86Sstevel 			} else {
102329949e86Sstevel 				miocnak(q, mp, 0, EINVAL);
102429949e86Sstevel 			}
102529949e86Sstevel 			break;
102629949e86Sstevel 		case ENVCTRL_IOC_GETFAN:
102729949e86Sstevel 			if (iocp->ioc_count == TRANSPARENT) {
102829949e86Sstevel 				mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
102929949e86Sstevel 				    sizeof (struct envctrl_pcf8591_chip), NULL);
103029949e86Sstevel 				qreply(q, mp);
103129949e86Sstevel 			} else {
103229949e86Sstevel 				miocnak(q, mp, 0, EINVAL);
103329949e86Sstevel 			}
103429949e86Sstevel 			break;
103529949e86Sstevel 		case ENVCTRL_IOC_SETFSP:
103629949e86Sstevel 			if (iocp->ioc_count == TRANSPARENT) {
103729949e86Sstevel 				mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
103829949e86Sstevel 				    sizeof (uint8_t), NULL);
103929949e86Sstevel 				qreply(q, mp);
104029949e86Sstevel 			} else {
104129949e86Sstevel 				miocnak(q, mp, 0, EINVAL);
104229949e86Sstevel 			}
104329949e86Sstevel 			break;
104429949e86Sstevel 		case ENVCTRL_IOC_SETDSKLED:
104529949e86Sstevel 		case ENVCTRL_IOC_GETDSKLED:
104629949e86Sstevel 			if (iocp->ioc_count == TRANSPARENT) {
104729949e86Sstevel 				mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
104829949e86Sstevel 				    sizeof (struct envctrl_pcf8574_chip), NULL);
104929949e86Sstevel 				qreply(q, mp);
105029949e86Sstevel 			} else {
105129949e86Sstevel 				miocnak(q, mp, 0, EINVAL);
105229949e86Sstevel 			}
105329949e86Sstevel 			break;
105429949e86Sstevel 		default:
105529949e86Sstevel 			miocnak(q, mp, 0, EINVAL);
105629949e86Sstevel 			break;
105729949e86Sstevel 		}
105829949e86Sstevel 
105929949e86Sstevel 		break;
106029949e86Sstevel 
106129949e86Sstevel 	}
106229949e86Sstevel 	case M_IOCDATA:
106329949e86Sstevel 	{
106429949e86Sstevel 		uint8_t *tempr, *wdval;
106529949e86Sstevel 		long state;
106629949e86Sstevel 
106729949e86Sstevel 		csp = (struct copyresp *)(void *)mp->b_rptr;
106829949e86Sstevel 
106929949e86Sstevel 		/*
107029949e86Sstevel 		 * If copy request failed, quit now
107129949e86Sstevel 		 */
107229949e86Sstevel 		if (csp->cp_rval != 0) {
107329949e86Sstevel 			miocnak(q, mp, 0, EINVAL);
107429949e86Sstevel 			return (0);
107529949e86Sstevel 		}
107629949e86Sstevel 
107729949e86Sstevel 		cqp = (struct copyreq *)(void *)mp->b_rptr;
107829949e86Sstevel 
107929949e86Sstevel 		cmd = csp->cp_cmd;
108029949e86Sstevel 		state = (long)cqp->cq_private;
108129949e86Sstevel 
108229949e86Sstevel 		switch (cmd) {
108329949e86Sstevel 		case ENVCTRL_IOC_SETFAN:
108429949e86Sstevel 			fanspeed = (struct envctrl_tda8444t_chip *)
108529949e86Sstevel 			    (void *)mp->b_cont->b_rptr;
108629949e86Sstevel 			mutex_enter(&unitp->umutex);
108729949e86Sstevel 			if (envctrl_xmit(unitp, (caddr_t *)(void *)fanspeed,
108829949e86Sstevel 			    fanspeed->type) == DDI_FAILURE) {
108929949e86Sstevel 				/*
109029949e86Sstevel 				 * Fix for a ADF bug
109129949e86Sstevel 				 * move mutex to after fan fail call
109229949e86Sstevel 				 * bugid 4016121
109329949e86Sstevel 				 */
109429949e86Sstevel 				envctrl_fan_fail_service(unitp);
109529949e86Sstevel 				mutex_exit(&unitp->umutex);
109629949e86Sstevel 				miocnak(q, mp, 0, EINVAL);
109729949e86Sstevel 			} else {
109829949e86Sstevel 				mutex_exit(&unitp->umutex);
109929949e86Sstevel 				miocack(q, mp, 0, 0);
110029949e86Sstevel 			}
110129949e86Sstevel 			break;
110229949e86Sstevel 		case ENVCTRL_IOC_SETFSP:
110329949e86Sstevel 			wdval = (uint8_t *)(void *)mp->b_cont->b_rptr;
110429949e86Sstevel 			mutex_enter(&unitp->umutex);
110529949e86Sstevel 			/*
110629949e86Sstevel 			 * If a user is in normal mode and they try
110729949e86Sstevel 			 * to set anything other than a disk fault or
110829949e86Sstevel 			 * a gen fault it is an invalid operation.
110929949e86Sstevel 			 * in diag mode we allow everything to be
111029949e86Sstevel 			 * twiddled.
111129949e86Sstevel 			 */
111229949e86Sstevel 			if (unitp->current_mode == ENVCTRL_NORMAL_MODE) {
111329949e86Sstevel 				if (*wdval & ~ENVCTRL_FSP_USRMASK) {
111429949e86Sstevel 					mutex_exit(&unitp->umutex);
111529949e86Sstevel 					miocnak(q, mp, 0, EINVAL);
111629949e86Sstevel 					break;
111729949e86Sstevel 				}
111829949e86Sstevel 			}
111929949e86Sstevel 			envctrl_set_fsp(unitp, wdval);
112029949e86Sstevel 			mutex_exit(&unitp->umutex);
112129949e86Sstevel 			miocack(q, mp, 0, 0);
112229949e86Sstevel 			break;
112329949e86Sstevel 		case ENVCTRL_IOC_SETDSKLED:
112429949e86Sstevel 			ledchip = (struct envctrl_pcf8574_chip *)
112529949e86Sstevel 			    (void *)mp->b_cont->b_rptr;
112629949e86Sstevel 			mutex_enter(&unitp->umutex);
112729949e86Sstevel 			if (envctrl_set_dskled(unitp, ledchip)) {
112829949e86Sstevel 				miocnak(q, mp, 0, EINVAL);
112929949e86Sstevel 			} else {
113029949e86Sstevel 				miocack(q, mp, 0, 0);
113129949e86Sstevel 			}
113229949e86Sstevel 			mutex_exit(&unitp->umutex);
113329949e86Sstevel 			break;
113429949e86Sstevel 		case ENVCTRL_IOC_GETDSKLED:
113529949e86Sstevel 			if (state  == -1) {
113629949e86Sstevel 				miocack(q, mp, 0, 0);
113729949e86Sstevel 				break;
113829949e86Sstevel 			}
113929949e86Sstevel 			ledchip = (struct envctrl_pcf8574_chip *)
114029949e86Sstevel 			    (void *)mp->b_cont->b_rptr;
114129949e86Sstevel 			mutex_enter(&unitp->umutex);
114229949e86Sstevel 			if (envctrl_get_dskled(unitp, ledchip)) {
114329949e86Sstevel 				miocnak(q, mp, 0, EINVAL);
114429949e86Sstevel 			} else {
114529949e86Sstevel 				mcopyout(mp, (void *)-1,
114629949e86Sstevel 				    sizeof (struct envctrl_pcf8574_chip),
114729949e86Sstevel 				    csp->cp_private, NULL);
114829949e86Sstevel 				qreply(q, mp);
114929949e86Sstevel 			}
115029949e86Sstevel 			mutex_exit(&unitp->umutex);
115129949e86Sstevel 			break;
115229949e86Sstevel 		case ENVCTRL_IOC_GETTEMP:
115329949e86Sstevel 			/* Get the user buffer address */
115429949e86Sstevel 
115529949e86Sstevel 			if (state  == -1) {
115629949e86Sstevel 				miocack(q, mp, 0, 0);
115729949e86Sstevel 				break;
115829949e86Sstevel 			}
115929949e86Sstevel 			temp = (struct envctrl_pcf8591_chip *)
116029949e86Sstevel 			    (void *)mp->b_cont->b_rptr;
116129949e86Sstevel 			mutex_enter(&unitp->umutex);
116229949e86Sstevel 			envctrl_recv(unitp, (caddr_t *)(void *)temp, PCF8591);
116329949e86Sstevel 			mutex_exit(&unitp->umutex);
116429949e86Sstevel 			mcopyout(mp, (void *)-1,
116529949e86Sstevel 			    sizeof (struct envctrl_pcf8591_chip),
116629949e86Sstevel 			    csp->cp_private, NULL);
116729949e86Sstevel 			qreply(q, mp);
116829949e86Sstevel 			break;
116929949e86Sstevel 		case ENVCTRL_IOC_GETFAN:
117029949e86Sstevel 			/* Get the user buffer address */
117129949e86Sstevel 
117229949e86Sstevel 			if (state == -1) {
117329949e86Sstevel 				miocack(q, mp, 0, 0);
117429949e86Sstevel 				break;
117529949e86Sstevel 			}
117629949e86Sstevel 			a_fanspeed = (struct envctrl_pcf8591_chip *)
117729949e86Sstevel 			    (void *)mp->b_cont->b_rptr;
117829949e86Sstevel 			mutex_enter(&unitp->umutex);
117929949e86Sstevel 			envctrl_recv(unitp, (caddr_t *)(void *)a_fanspeed,
118019397407SSherry Moore 			    PCF8591);
118129949e86Sstevel 			mutex_exit(&unitp->umutex);
118229949e86Sstevel 			mcopyout(mp, (void *)-1,
118329949e86Sstevel 			    sizeof (struct envctrl_pcf8591_chip),
118429949e86Sstevel 			    csp->cp_private, NULL);
118529949e86Sstevel 			qreply(q, mp);
118629949e86Sstevel 			break;
118729949e86Sstevel 		case ENVCTRL_IOC_SETTEMP:
118829949e86Sstevel 			tempr = (uint8_t *)(void *)mp->b_cont->b_rptr;
118929949e86Sstevel 			if (*tempr > MAX_DIAG_TEMPR) {
119029949e86Sstevel 				miocnak(q, mp, 0, EINVAL);
119129949e86Sstevel 			} else {
119229949e86Sstevel 				mutex_enter(&unitp->umutex);
119329949e86Sstevel 				envctrl_get_sys_temperatures(unitp, tempr);
119429949e86Sstevel 				mutex_exit(&unitp->umutex);
119529949e86Sstevel 				miocack(q, mp, 0, 0);
119629949e86Sstevel 			}
119729949e86Sstevel 			break;
119829949e86Sstevel 		case ENVCTRL_IOC_SETWDT:
119929949e86Sstevel 			/* reset watchdog timeout period */
120029949e86Sstevel 			wdval = (uint8_t *)(void *)mp->b_cont->b_rptr;
120129949e86Sstevel 			if (*wdval > MAX_CL_VAL) {
120229949e86Sstevel 				miocnak(q, mp, 0, EINVAL);
120329949e86Sstevel 			} else {
120429949e86Sstevel 				mutex_enter(&unitp->umutex);
120529949e86Sstevel 				envctrl_reset_watchdog(unitp, wdval);
120629949e86Sstevel 				mutex_exit(&unitp->umutex);
120729949e86Sstevel 				miocack(q, mp, 0, 0);
120829949e86Sstevel 			}
120929949e86Sstevel 			break;
121029949e86Sstevel 		case ENVCTRL_IOC_GETMODE:
121129949e86Sstevel 			/* Get the user buffer address */
121229949e86Sstevel 
121329949e86Sstevel 			if (state == -1) {
121429949e86Sstevel 				miocack(q, mp, 0, 0);
121529949e86Sstevel 				break;
121629949e86Sstevel 			}
121729949e86Sstevel 			tempr = (uchar_t *)(void *)mp->b_cont->b_rptr;
121829949e86Sstevel 			*tempr = unitp->current_mode;
121929949e86Sstevel 			mcopyout(mp, (void *)-1, sizeof (uchar_t),
122029949e86Sstevel 			    csp->cp_private, NULL);
122129949e86Sstevel 			qreply(q, mp);
122229949e86Sstevel 			break;
122329949e86Sstevel 		case ENVCTRL_IOC_SETMODE:
122429949e86Sstevel 			/* Set mode */
122529949e86Sstevel 			wdval = (uint8_t *)(void *)mp->b_cont->b_rptr;
122629949e86Sstevel 			if (*wdval == ENVCTRL_DIAG_MODE || *wdval ==
122729949e86Sstevel 			    ENVCTRL_NORMAL_MODE) {
122829949e86Sstevel 				mutex_enter(&unitp->umutex);
122929949e86Sstevel 				unitp->current_mode = *wdval;
123029949e86Sstevel 				if (unitp->timeout_id != 0 &&
123129949e86Sstevel 				    *wdval == ENVCTRL_DIAG_MODE) {
123229949e86Sstevel 					(void) untimeout(unitp->timeout_id);
123329949e86Sstevel 					unitp->timeout_id =
123429949e86Sstevel 					    (timeout(envctrl_tempr_poll,
123519397407SSherry Moore 					    (caddr_t)unitp,
123619397407SSherry Moore 					    overtemp_timeout_hz));
123729949e86Sstevel 
123829949e86Sstevel 				}
123929949e86Sstevel 				if (*wdval == ENVCTRL_NORMAL_MODE) {
124029949e86Sstevel 					envctrl_get_sys_temperatures(unitp,
124129949e86Sstevel 					    (uint8_t *)NULL);
124229949e86Sstevel 					/*
124329949e86Sstevel 					 * going to normal mode we
124429949e86Sstevel 					 * need to go to diag mode
124529949e86Sstevel 					 * just in case we have
124629949e86Sstevel 					 * injected a fan fault. It
124729949e86Sstevel 					 * may not be cleared and if
124829949e86Sstevel 					 * we call fan_failsrvc it will
124929949e86Sstevel 					 * power off the ystem if we are
125029949e86Sstevel 					 * in NORMAL_MODE. Also we need
125129949e86Sstevel 					 * to delay 1 bit of time here
125229949e86Sstevel 					 * to  allow the fans to rotate
125329949e86Sstevel 					 * back up and clear the intr
125429949e86Sstevel 					 * after we get the sys temps.
125529949e86Sstevel 					 */
125629949e86Sstevel 					unitp->current_mode =
125729949e86Sstevel 					    ENVCTRL_DIAG_MODE;
125829949e86Sstevel 					envctrl_fan_fail_service(unitp);
125929949e86Sstevel 					unitp->current_mode =
126029949e86Sstevel 					    ENVCTRL_NORMAL_MODE;
126129949e86Sstevel 				}
126229949e86Sstevel 				mutex_exit(&unitp->umutex);
126329949e86Sstevel 				miocack(q, mp, 0, 0);
126429949e86Sstevel 			} else {
126529949e86Sstevel 				miocnak(q, mp, 0, EINVAL);
126629949e86Sstevel 			}
126729949e86Sstevel 			break;
126829949e86Sstevel 		default:
126929949e86Sstevel 			freemsg(mp);
127029949e86Sstevel 			break;
127129949e86Sstevel 		}
127229949e86Sstevel 
127329949e86Sstevel 		break;
127429949e86Sstevel 	}
127529949e86Sstevel 
127629949e86Sstevel 	case M_FLUSH:
127729949e86Sstevel 		if (*mp->b_rptr & FLUSHR) {
127829949e86Sstevel 			*mp->b_rptr &= ~FLUSHW;
127929949e86Sstevel 			qreply(q, mp);
128029949e86Sstevel 		} else {
128129949e86Sstevel 			freemsg(mp);
128229949e86Sstevel 		}
128329949e86Sstevel 		break;
128429949e86Sstevel 
128529949e86Sstevel 	default:
128629949e86Sstevel 		freemsg(mp);
128729949e86Sstevel 		break;
128829949e86Sstevel 	}
128929949e86Sstevel 
129029949e86Sstevel 	return (0);
129129949e86Sstevel }
129229949e86Sstevel 
129329949e86Sstevel uint_t
envctrl_bus_isr(caddr_t arg)129429949e86Sstevel envctrl_bus_isr(caddr_t arg)
129529949e86Sstevel {
129629949e86Sstevel 	struct envctrlunit *unitp = (struct envctrlunit *)(void *)arg;
129729949e86Sstevel 	int ic = DDI_INTR_UNCLAIMED;
129829949e86Sstevel 
129929949e86Sstevel 	mutex_enter(&unitp->umutex);
130029949e86Sstevel 
130129949e86Sstevel 	/*
130229949e86Sstevel 	 * NOT USED
130329949e86Sstevel 	 */
130429949e86Sstevel 
130529949e86Sstevel 	mutex_exit(&unitp->umutex);
130629949e86Sstevel 	return (ic);
130729949e86Sstevel }
130829949e86Sstevel 
130929949e86Sstevel uint_t
envctrl_dev_isr(caddr_t arg)131029949e86Sstevel envctrl_dev_isr(caddr_t arg)
131129949e86Sstevel {
131229949e86Sstevel 	struct envctrlunit *unitp = (struct envctrlunit *)(void *)arg;
131329949e86Sstevel 	uint8_t recv_data;
131429949e86Sstevel 	int ic;
131529949e86Sstevel 	int retrys = 0;
131629949e86Sstevel 	int status;
131729949e86Sstevel 
131829949e86Sstevel 	ic = DDI_INTR_UNCLAIMED;
131929949e86Sstevel 
132029949e86Sstevel 	mutex_enter(&unitp->umutex);
132129949e86Sstevel 
132229949e86Sstevel 	/*
132329949e86Sstevel 	 * First check to see if it is an interrupt for us by
132429949e86Sstevel 	 * looking at the "ganged" interrrupt and vector
132529949e86Sstevel 	 * according to the major type
132629949e86Sstevel 	 * 0x70 is the addr of the ganged interrupt controller.
132729949e86Sstevel 	 * Address map for the port byte read is as follows
132829949e86Sstevel 	 * MSB
132929949e86Sstevel 	 * -------------------------
133029949e86Sstevel 	 * |  |  |  |  |  |  |  |  |
133129949e86Sstevel 	 * -------------------------
133229949e86Sstevel 	 *  P7 P6 P5 P4 P3 P2 P1 P0
133329949e86Sstevel 	 * P0 = Power Supply 1 intr
133429949e86Sstevel 	 * P1 = Power Supply 2 intr
133529949e86Sstevel 	 * P2 = Power Supply 3 intr
133629949e86Sstevel 	 * P3 = Dlfop enable for fan sped set
133729949e86Sstevel 	 * P4 = ENVCTRL_ Fan Fail intr
133829949e86Sstevel 	 * P5 =	Front Panel Interrupt
133929949e86Sstevel 	 * P6 = Power Fail Detect Low.
134029949e86Sstevel 	 * P7 = Enable Interrupts to system
134129949e86Sstevel 	 */
134229949e86Sstevel 
134329949e86Sstevel retry:
134429949e86Sstevel 
134529949e86Sstevel 	status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
134619397407SSherry Moore 	    PCF8574A_BASE_ADDR | ENVCTRL_PCF8574_DEV0, &recv_data, 1);
134729949e86Sstevel 
134829949e86Sstevel 	/*
134929949e86Sstevel 	 * This extra read is needed since the first read is discarded
135029949e86Sstevel 	 * and the second read seems to return 0xFF.
135129949e86Sstevel 	 */
135229949e86Sstevel 	if (recv_data == 0xFF) {
135329949e86Sstevel 		status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
135419397407SSherry Moore 		    PCF8574A_BASE_ADDR | ENVCTRL_PCF8574_DEV0, &recv_data, 1);
135529949e86Sstevel 	}
135629949e86Sstevel 	if (envctrl_debug_flags)
135729949e86Sstevel 		cmn_err(CE_WARN, "envctrl_dev_isr: status= %d, data = %x\n",
135819397407SSherry Moore 		    status, recv_data);
135929949e86Sstevel 
136029949e86Sstevel 	/*
136129949e86Sstevel 	 * if the i2c bus is hung it is imperative that this
136229949e86Sstevel 	 * be cleared on an interrupt or else it will
136329949e86Sstevel 	 * hang the system with continuous interrupts
136429949e86Sstevel 	 */
136529949e86Sstevel 
136629949e86Sstevel 	if (status == DDI_FAILURE) {
136729949e86Sstevel 		drv_usecwait(1000);
136829949e86Sstevel 		if (retrys < envctrl_max_retries) {
136929949e86Sstevel 			retrys++;
137029949e86Sstevel 			goto retry;
137129949e86Sstevel 		} else {
137229949e86Sstevel 			if (envctrl_debug_flags)
137329949e86Sstevel 				cmn_err(CE_WARN,
137429949e86Sstevel 				    "DEVISR FAILED received 0x%x\n", recv_data);
137529949e86Sstevel 			mutex_exit(&unitp->umutex);
137629949e86Sstevel 			envctrl_init_bus(unitp);
137729949e86Sstevel 			mutex_enter(&unitp->umutex);
137829949e86Sstevel 			envctrl_ps_probe(unitp);
137929949e86Sstevel 			mutex_exit(&unitp->umutex);
138029949e86Sstevel 			ic = DDI_INTR_CLAIMED;
138129949e86Sstevel 			return (ic);
138229949e86Sstevel 		}
138329949e86Sstevel 	}
138429949e86Sstevel 
138529949e86Sstevel 	/*
138629949e86Sstevel 	 * Port 0 = PS1 interrupt
138729949e86Sstevel 	 * Port 1 = PS2 Interrupt
138829949e86Sstevel 	 * Port 2 = PS3 Interrupt
138929949e86Sstevel 	 * Port 3 = SPARE
139029949e86Sstevel 	 * Port 4 = Fan Fail Intr
139129949e86Sstevel 	 * Port 5 = Front Panle Module intr
139229949e86Sstevel 	 * Port 6 = Keyswitch Intr
139329949e86Sstevel 	 * Port 7 = ESINTR ENABLE ???
139429949e86Sstevel 	 */
139529949e86Sstevel 
139629949e86Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT0)) {
139729949e86Sstevel 		envctrl_PS_intr_service(unitp, PS1);
139829949e86Sstevel 		ic = DDI_INTR_CLAIMED;
139929949e86Sstevel 	}
140029949e86Sstevel 
140129949e86Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT1)) {
140229949e86Sstevel 		envctrl_PS_intr_service(unitp, PS2);
140329949e86Sstevel 		ic = DDI_INTR_CLAIMED;
140429949e86Sstevel 	}
140529949e86Sstevel 
140629949e86Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT2)) {
140729949e86Sstevel 		envctrl_PS_intr_service(unitp, PS3);
140829949e86Sstevel 		ic = DDI_INTR_CLAIMED;
140929949e86Sstevel 	}
141029949e86Sstevel 
141129949e86Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT3)) {
141229949e86Sstevel 		ic = DDI_INTR_CLAIMED;
141329949e86Sstevel 	}
141429949e86Sstevel 
141529949e86Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT4)) {
141629949e86Sstevel 		/*
141729949e86Sstevel 		 * Check for a fan fail
141829949e86Sstevel 		 * Single fan fail
141929949e86Sstevel 		 * shutdown system
142029949e86Sstevel 		 */
142129949e86Sstevel 		envctrl_fan_fail_service(unitp);
142229949e86Sstevel 		ic = DDI_INTR_CLAIMED;
142329949e86Sstevel 	}
142429949e86Sstevel 
142529949e86Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT5)) {
142629949e86Sstevel 		(void) envctrl_get_fpm_status(unitp);
142729949e86Sstevel 		ic = DDI_INTR_CLAIMED;
142829949e86Sstevel 	}
142929949e86Sstevel 
143029949e86Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT6)) {
143129949e86Sstevel 		ic = DDI_INTR_CLAIMED;
143229949e86Sstevel 	}
143329949e86Sstevel 
143429949e86Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT7)) {
143529949e86Sstevel 		ic = DDI_INTR_CLAIMED;
143629949e86Sstevel 	}
143729949e86Sstevel 
143829949e86Sstevel 	if ((recv_data == 0xFF)) {
143929949e86Sstevel 		ic = DDI_INTR_CLAIMED;
144029949e86Sstevel 	}
144129949e86Sstevel 
144229949e86Sstevel 	mutex_exit(&unitp->umutex);
144329949e86Sstevel 	return (ic);
144429949e86Sstevel 
144529949e86Sstevel }
144629949e86Sstevel 
144729949e86Sstevel static void
envctrl_init_bus(struct envctrlunit * unitp)144829949e86Sstevel envctrl_init_bus(struct envctrlunit *unitp)
144929949e86Sstevel {
145029949e86Sstevel 
145129949e86Sstevel 	int i;
1452*eb6b10e6SToomas Soome 	uint8_t noval = 0;
145329949e86Sstevel 	struct envctrl_tda8444t_chip fan;
145429949e86Sstevel 	int fans[] = {ENVCTRL_CPU_FANS, ENVCTRL_PS_FANS, ENVCTRL_AFB_FANS};
145529949e86Sstevel 
145629949e86Sstevel 	mutex_enter(&unitp->umutex);
145729949e86Sstevel 	/* Sets the Mode to 808x type bus */
145829949e86Sstevel 	ddi_put8(unitp->ctlr_handle,
145929949e86Sstevel 	    &unitp->bus_ctl_regs->s0, ENVCTRL_CHAR_ZERO);
146029949e86Sstevel 
146129949e86Sstevel 	/* SET UP SLAVE ADDR XXX Required..send 0x80 */
146229949e86Sstevel 
146329949e86Sstevel 	ddi_put8(unitp->ctlr_handle, &unitp->bus_ctl_regs->s1,
146419397407SSherry Moore 	    ENVCTRL_BUS_INIT0);
146529949e86Sstevel 	(void) ddi_put8(unitp->ctlr_handle, &unitp->bus_ctl_regs->s0,
146619397407SSherry Moore 	    ENVCTRL_BUS_INIT1);
146729949e86Sstevel 
146829949e86Sstevel 	/* Set the clock now */
146929949e86Sstevel 	ddi_put8(unitp->ctlr_handle,
147029949e86Sstevel 	    &unitp->bus_ctl_regs->s1, ENVCTRL_BUS_CLOCK0);
147129949e86Sstevel 
147229949e86Sstevel 	/* S0 is now S2  necause of the previous write to S1 */
147329949e86Sstevel 	/* clock= 12MHz, SCL=90KHz */
147429949e86Sstevel 	ddi_put8(unitp->ctlr_handle,
147529949e86Sstevel 	    &unitp->bus_ctl_regs->s0, ENVCTRL_BUS_CLOCK1);
147629949e86Sstevel 
147729949e86Sstevel 	/* Enable serial interface */
147829949e86Sstevel 	ddi_put8(unitp->ctlr_handle,
147929949e86Sstevel 	    &unitp->bus_ctl_regs->s1, ENVCTRL_BUS_ESI);
148029949e86Sstevel 
148129949e86Sstevel 	envctrl_stop_clock(unitp);
148229949e86Sstevel 
148329949e86Sstevel 	/*
148429949e86Sstevel 	 * This has been added here because the DAC is powered
148529949e86Sstevel 	 * on at "0". When the reset_dflop routine is called
148629949e86Sstevel 	 * this switched the  fans from blast to DAC control.
148729949e86Sstevel 	 * if the DAC is at "0", then the fans momentarily lose
148829949e86Sstevel 	 * power until the temp polling and fan set routine is
148929949e86Sstevel 	 * first called. If the fans lose power, then there is
149029949e86Sstevel 	 * a fan fault generated and the system will power off.
149129949e86Sstevel 	 * We only want to do this IF the bus is first being
149229949e86Sstevel 	 * initted. This will cause errors in Sunvts if we reset
149329949e86Sstevel 	 * the fan speed under normal operation. Sometimes we need
149429949e86Sstevel 	 * to be able to induce fan faults. Init bus is a common
149529949e86Sstevel 	 * routine to unwedge the i2c bus in some cases.
149629949e86Sstevel 	 */
149729949e86Sstevel 
149829949e86Sstevel 	if (unitp->initting == B_TRUE) {
149929949e86Sstevel 		fan.chip_num = ENVCTRL_TDA8444T_DEV7;
150029949e86Sstevel 		fan.val = INIT_FAN_VAL;
150129949e86Sstevel 
150229949e86Sstevel 		for (i = 0; i < sizeof (fans)/sizeof (int); i++) {
150329949e86Sstevel 			fan.fan_num = fans[i];
150429949e86Sstevel 			if ((fans[i] == ENVCTRL_AFB_FANS) &&
150519397407SSherry Moore 			    (unitp->AFB_present == B_FALSE))
150629949e86Sstevel 				continue;
150729949e86Sstevel 			(void) envctrl_xmit(unitp, (caddr_t *)(void *)&fan,
150819397407SSherry Moore 			    TDA8444T);
150929949e86Sstevel 		}
151029949e86Sstevel 	}
151129949e86Sstevel 
151229949e86Sstevel 	envctrl_reset_dflop(unitp);
151329949e86Sstevel 
151429949e86Sstevel 	envctrl_enable_devintrs(unitp);
151529949e86Sstevel 
151629949e86Sstevel 	unitp->current_mode = ENVCTRL_NORMAL_MODE;
151729949e86Sstevel 	envctrl_reset_watchdog(unitp, &noval);
151829949e86Sstevel 
151929949e86Sstevel 	mutex_exit(&unitp->umutex);
152029949e86Sstevel }
152129949e86Sstevel 
152229949e86Sstevel static int
envctrl_xmit(struct envctrlunit * unitp,caddr_t * data,int chip_type)152329949e86Sstevel envctrl_xmit(struct envctrlunit *unitp, caddr_t *data, int chip_type)
152429949e86Sstevel {
152529949e86Sstevel 
152629949e86Sstevel 	struct envctrl_tda8444t_chip *fanspeed;
152729949e86Sstevel 	struct envctrl_pcf8574_chip *ioport;
152829949e86Sstevel 	uint8_t slave_addr;
152929949e86Sstevel 	uint8_t buf[2];
153029949e86Sstevel 	int retrys = 0;
153129949e86Sstevel 	int status;
153229949e86Sstevel 
153329949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
153429949e86Sstevel 
153529949e86Sstevel 	switch (chip_type) {
153629949e86Sstevel 	case TDA8444T:
153729949e86Sstevel 
153829949e86Sstevel 		fanspeed = (struct envctrl_tda8444t_chip *)data;
153929949e86Sstevel 
154029949e86Sstevel 		if (fanspeed->chip_num > ENVCTRL_FAN_ADDR_MAX) {
154129949e86Sstevel 			return (DDI_FAILURE);
154229949e86Sstevel 		}
154329949e86Sstevel 
154429949e86Sstevel 		if (fanspeed->fan_num > ENVCTRL_PORT7) {
154529949e86Sstevel 			return (DDI_FAILURE);
154629949e86Sstevel 		}
154729949e86Sstevel 
154829949e86Sstevel 		if (fanspeed->val > MAX_FAN_VAL) {
154929949e86Sstevel 			return (DDI_FAILURE);
155029949e86Sstevel 		}
155129949e86Sstevel 
155229949e86Sstevel retry0:
155329949e86Sstevel 		slave_addr = (TDA8444T_BASE_ADDR | fanspeed->chip_num);
155429949e86Sstevel 		buf[0] = fanspeed->val;
155529949e86Sstevel 
155629949e86Sstevel 		status = eHc_write_tda8444((struct eHc_envcunit *)unitp,
155719397407SSherry Moore 		    TDA8444T_BASE_ADDR | fanspeed->chip_num, 0xF,
155819397407SSherry Moore 		    fanspeed->fan_num, buf, 1);
155929949e86Sstevel 		if (status != DDI_SUCCESS) {
156029949e86Sstevel 			drv_usecwait(1000);
156129949e86Sstevel 			if (retrys < envctrl_max_retries) {
156229949e86Sstevel 				retrys++;
156329949e86Sstevel 				goto retry0;
156429949e86Sstevel 			} else {
156529949e86Sstevel 				mutex_exit(&unitp->umutex);
156629949e86Sstevel 				envctrl_init_bus(unitp);
156729949e86Sstevel 				mutex_enter(&unitp->umutex);
156829949e86Sstevel 				if (envctrl_debug_flags)
156929949e86Sstevel 					cmn_err(CE_WARN,
157029949e86Sstevel 					    "envctrl_xmit: Write to TDA8444 " \
157129949e86Sstevel 					    "failed\n");
157229949e86Sstevel 				return (DDI_FAILURE);
157329949e86Sstevel 			}
157429949e86Sstevel 		}
157529949e86Sstevel 
157629949e86Sstevel 		/*
157729949e86Sstevel 		 * Update the kstats.
157829949e86Sstevel 		 */
157929949e86Sstevel 		switch (fanspeed->fan_num) {
158029949e86Sstevel 		case ENVCTRL_CPU_FANS:
158129949e86Sstevel 			unitp->fan_kstats[ENVCTRL_FAN_TYPE_CPU].fanspeed =
158229949e86Sstevel 			    fanspeed->val;
158329949e86Sstevel 			break;
158429949e86Sstevel 		case ENVCTRL_PS_FANS:
158529949e86Sstevel 			unitp->fan_kstats[ENVCTRL_FAN_TYPE_PS].fanspeed =
158629949e86Sstevel 			    fanspeed->val;
158729949e86Sstevel 			break;
158829949e86Sstevel 		case ENVCTRL_AFB_FANS:
158929949e86Sstevel 			unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].fanspeed =
159029949e86Sstevel 			    fanspeed->val;
159129949e86Sstevel 			break;
159229949e86Sstevel 		default:
159329949e86Sstevel 			break;
159429949e86Sstevel 		}
159529949e86Sstevel 		break;
159629949e86Sstevel 	case PCF8574:
159729949e86Sstevel 		ioport = (struct envctrl_pcf8574_chip *)data;
159829949e86Sstevel 		buf[0] = ioport->val;
159929949e86Sstevel 		if (ioport->chip_num > ENVCTRL_PCF8574_DEV7)
160029949e86Sstevel 			return (DDI_FAILURE);
160129949e86Sstevel 
160229949e86Sstevel retry:
160329949e86Sstevel 		if (ioport->type == PCF8574A) {
160429949e86Sstevel 			slave_addr = (PCF8574A_BASE_ADDR | ioport->chip_num);
160529949e86Sstevel 			status =
160619397407SSherry Moore 			    eHc_write_pcf8574a((struct eHc_envcunit *)unitp,
160719397407SSherry Moore 			    PCF8574A_BASE_ADDR | ioport->chip_num, buf, 1);
160829949e86Sstevel 		} else {
160929949e86Sstevel 			slave_addr = (PCF8574_BASE_ADDR | ioport->chip_num);
161029949e86Sstevel 			status = eHc_write_pcf8574((struct eHc_envcunit *)unitp,
161119397407SSherry Moore 			    PCF8574_BASE_ADDR | ioport->chip_num, buf, 1);
161229949e86Sstevel 		}
161329949e86Sstevel 
161429949e86Sstevel 		if (status != DDI_SUCCESS) {
161529949e86Sstevel 			drv_usecwait(1000);
161629949e86Sstevel 			if (retrys < envctrl_max_retries) {
161729949e86Sstevel 				retrys++;
161829949e86Sstevel 				goto retry;
161929949e86Sstevel 			} else {
162029949e86Sstevel 				mutex_exit(&unitp->umutex);
162129949e86Sstevel 				envctrl_init_bus(unitp);
162229949e86Sstevel 				mutex_enter(&unitp->umutex);
162329949e86Sstevel 				if (envctrl_debug_flags)
162429949e86Sstevel 					cmn_err(CE_WARN, "Write to PCF8574 " \
162529949e86Sstevel 					    "failed, addr = %X\n", slave_addr);
162629949e86Sstevel 				if (envctrl_debug_flags)
162729949e86Sstevel 					cmn_err(CE_WARN, "envctrl_xmit: PCF8574\
162829949e86Sstevel 						dev = %d, port = %d\n",
162919397407SSherry Moore 					    ioport->chip_num, ioport->type);
163029949e86Sstevel 				return (DDI_FAILURE);
163129949e86Sstevel 			}
163229949e86Sstevel 		}
163329949e86Sstevel 		break;
163429949e86Sstevel 
163529949e86Sstevel 	default:
163629949e86Sstevel 		return (DDI_FAILURE);
163729949e86Sstevel 	}
163829949e86Sstevel 
163929949e86Sstevel 	return (DDI_SUCCESS);
164029949e86Sstevel }
164129949e86Sstevel 
164229949e86Sstevel static void
envctrl_recv(struct envctrlunit * unitp,caddr_t * data,int chip_type)164329949e86Sstevel envctrl_recv(struct envctrlunit *unitp, caddr_t *data, int chip_type)
164429949e86Sstevel {
164529949e86Sstevel 
164629949e86Sstevel 	struct envctrl_pcf8591_chip *temp;
164729949e86Sstevel 	struct envctrl_pcf8574_chip *ioport;
164829949e86Sstevel 	uint8_t slave_addr, recv_data;
164929949e86Sstevel 	int retrys = 0;
165029949e86Sstevel 	int status;
165129949e86Sstevel 	uint8_t buf[1];
165229949e86Sstevel 
165329949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
165429949e86Sstevel 
165529949e86Sstevel 	switch (chip_type) {
165629949e86Sstevel 	case PCF8591:
165729949e86Sstevel 		temp = (struct envctrl_pcf8591_chip *)data;
165829949e86Sstevel 		slave_addr = (PCF8591_BASE_ADDR | temp->chip_num);
165929949e86Sstevel 
166029949e86Sstevel retry:
166129949e86Sstevel 		status = eHc_read_pcf8591((struct eHc_envcunit *)unitp,
166219397407SSherry Moore 		    PCF8591_BASE_ADDR | temp->chip_num & 0xF,
166319397407SSherry Moore 		    temp->sensor_num, 0, 0, 1, &recv_data, 1);
166429949e86Sstevel 
166529949e86Sstevel 		/*
166629949e86Sstevel 		 * another place to catch the i2c bus hang on an 8591 read
166729949e86Sstevel 		 * In this instance we will just return the data that is read
166829949e86Sstevel 		 * after the max_retry because this could be a valid value.
166929949e86Sstevel 		 */
167029949e86Sstevel 		if (status != DDI_SUCCESS) {
167129949e86Sstevel 			drv_usecwait(1000);
167229949e86Sstevel 			if (retrys < envctrl_max_retries) {
167329949e86Sstevel 				retrys++;
167429949e86Sstevel 				goto retry;
167529949e86Sstevel 			} else {
167629949e86Sstevel 				mutex_exit(&unitp->umutex);
167729949e86Sstevel 				envctrl_init_bus(unitp);
167829949e86Sstevel 				mutex_enter(&unitp->umutex);
167929949e86Sstevel 				if (envctrl_debug_flags)
168029949e86Sstevel 					cmn_err(CE_WARN, "Read from PCF8591 " \
168129949e86Sstevel 					    "failed, slave_addr = %x\n",
168229949e86Sstevel 					    slave_addr);
168329949e86Sstevel 			}
168429949e86Sstevel 		}
168529949e86Sstevel 		temp->temp_val = recv_data;
168629949e86Sstevel 		break;
168729949e86Sstevel 	case TDA8444T:
168829949e86Sstevel 		printf("envctrl_recv: attempting to read TDA8444T\n");
168929949e86Sstevel 		return;
169029949e86Sstevel 	case PCF8574:
169129949e86Sstevel 		ioport = (struct envctrl_pcf8574_chip *)data;
169229949e86Sstevel 
169329949e86Sstevel retry1:
169429949e86Sstevel 		if (ioport->chip_num > ENVCTRL_PCF8574_DEV7)
169529949e86Sstevel 			cmn_err(CE_WARN, "envctrl: dev out of range 0x%x\n",
169619397407SSherry Moore 			    ioport->chip_num);
169729949e86Sstevel 
169829949e86Sstevel 		if (ioport->type == PCF8574A) {
169929949e86Sstevel 			slave_addr = (PCF8574_READ_BIT | PCF8574A_BASE_ADDR |
170029949e86Sstevel 			    ioport->chip_num);
170129949e86Sstevel 			status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
170219397407SSherry Moore 			    PCF8574A_BASE_ADDR | ioport->chip_num, buf, 1);
170329949e86Sstevel 		} else {
170429949e86Sstevel 			slave_addr = (PCF8574_READ_BIT | PCF8574_BASE_ADDR |
170529949e86Sstevel 			    ioport->chip_num);
170629949e86Sstevel 			status = eHc_read_pcf8574((struct eHc_envcunit *)unitp,
170719397407SSherry Moore 			    PCF8574_BASE_ADDR | ioport->chip_num, buf, 1);
170829949e86Sstevel 		}
170929949e86Sstevel 
171029949e86Sstevel 		if (status != DDI_SUCCESS) {
171129949e86Sstevel 			drv_usecwait(1000);
171229949e86Sstevel 			if (retrys < envctrl_max_retries) {
171329949e86Sstevel 				retrys++;
171429949e86Sstevel 				goto retry1;
171529949e86Sstevel 			} else {
171629949e86Sstevel 				mutex_exit(&unitp->umutex);
171729949e86Sstevel 				envctrl_init_bus(unitp);
171829949e86Sstevel 				mutex_enter(&unitp->umutex);
171929949e86Sstevel 				if (envctrl_debug_flags)
172029949e86Sstevel 					cmn_err(CE_WARN, "Read from PCF8574 "\
172129949e86Sstevel 					    "failed, addr = %X\n", slave_addr);
172229949e86Sstevel 				if (envctrl_debug_flags)
172329949e86Sstevel 					cmn_err(CE_WARN, "envctrl_recv: PCF8574\
172429949e86Sstevel 						dev = %d, port = %d\n",
172519397407SSherry Moore 					    ioport->chip_num, ioport->type);
172629949e86Sstevel 			}
172729949e86Sstevel 		}
172829949e86Sstevel 		ioport->val = buf[0];
172929949e86Sstevel 		break;
173029949e86Sstevel 	default:
173129949e86Sstevel 		break;
173229949e86Sstevel 	}
173329949e86Sstevel }
173429949e86Sstevel 
173529949e86Sstevel static int
envctrl_get_ps_temp(struct envctrlunit * unitp,uint8_t psaddr)173629949e86Sstevel envctrl_get_ps_temp(struct envctrlunit *unitp, uint8_t psaddr)
173729949e86Sstevel {
173829949e86Sstevel 	uint8_t tempr;
173929949e86Sstevel 	int i, retrys;
174029949e86Sstevel 	int status;
174129949e86Sstevel 	uint8_t buf[4];
174229949e86Sstevel 
174329949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
174429949e86Sstevel 
174529949e86Sstevel 	tempr = 0;
174629949e86Sstevel 	retrys = 0;
174729949e86Sstevel 
174829949e86Sstevel retry:
174929949e86Sstevel 	status = eHc_read_pcf8591((struct eHc_envcunit *)unitp,
175019397407SSherry Moore 	    PCF8591_BASE_ADDR | psaddr & 0xF, 0, 1, 0, 1, buf, 4);
175129949e86Sstevel 
175229949e86Sstevel 	tempr = 0;
175329949e86Sstevel 	for (i = 0; i < PCF8591_MAX_PORTS; i++) {
175429949e86Sstevel 		/*
175529949e86Sstevel 		 * The pcf8591 will return 0xff if no port
175629949e86Sstevel 		 * is there.. this is bogus for setting temps.
175729949e86Sstevel 		 * so just ignore it!
175829949e86Sstevel 		 */
175929949e86Sstevel 		if (envctrl_debug_flags) {
176029949e86Sstevel 			cmn_err(CE_WARN, "PS addr 0x%x recvd 0x%x on port %d\n",
176129949e86Sstevel 			    psaddr, buf[i], i);
176229949e86Sstevel 		}
176329949e86Sstevel 		if (buf[i] > tempr && buf[i] < MAX_PS_ADVAL) {
176429949e86Sstevel 			tempr = buf[i];
176529949e86Sstevel 		}
176629949e86Sstevel 	}
176729949e86Sstevel 
176829949e86Sstevel 	/*
176929949e86Sstevel 	 * This routine is a safeguard to make sure that if the
177029949e86Sstevel 	 * powersupply temps cannot be read that we do something
177129949e86Sstevel 	 * to make sure that the system will notify the user and
177229949e86Sstevel 	 * it will stay running with the fans at 100%. The calling
177329949e86Sstevel 	 * routine should take care of that.
177429949e86Sstevel 	 */
177529949e86Sstevel 	if (status != DDI_SUCCESS) {
177629949e86Sstevel 		drv_usecwait(1000);
177729949e86Sstevel 		if (retrys < envctrl_max_retries) {
177829949e86Sstevel 			retrys++;
177929949e86Sstevel 			goto retry;
178029949e86Sstevel 		} else {
178129949e86Sstevel 			mutex_exit(&unitp->umutex);
178229949e86Sstevel 			envctrl_init_bus(unitp);
178329949e86Sstevel 			mutex_enter(&unitp->umutex);
178429949e86Sstevel 			if (envctrl_debug_flags)
178529949e86Sstevel 				cmn_err(CE_WARN,
178629949e86Sstevel 				    "Cannot read Power Supply Temps addr = %X",
178729949e86Sstevel 				    psaddr);
178829949e86Sstevel 			return (PS_DEFAULT_VAL);
178929949e86Sstevel 		}
179029949e86Sstevel 	}
179129949e86Sstevel 
179229949e86Sstevel 	return (ps_temps[tempr]);
179329949e86Sstevel }
179429949e86Sstevel 
179529949e86Sstevel static int
envctrl_get_cpu_temp(struct envctrlunit * unitp,int cpunum)179629949e86Sstevel envctrl_get_cpu_temp(struct envctrlunit *unitp, int cpunum)
179729949e86Sstevel {
179829949e86Sstevel 	uint8_t recv_data;
179929949e86Sstevel 	int retrys;
180029949e86Sstevel 	int status;
180129949e86Sstevel 
180229949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
180329949e86Sstevel 
180429949e86Sstevel 	/*
180529949e86Sstevel 	 * This routine takes in the number of the port that
180629949e86Sstevel 	 * we want to read in the 8591. This should be the
180729949e86Sstevel 	 * location of the COU thermistor for one of the 4
180829949e86Sstevel 	 * cpu's. It will return the temperature in degrees C
180929949e86Sstevel 	 * to the caller.
181029949e86Sstevel 	 */
181129949e86Sstevel 
181229949e86Sstevel 	retrys = 0;
181329949e86Sstevel 
181429949e86Sstevel retry:
181529949e86Sstevel 	status = eHc_read_pcf8591((struct eHc_envcunit *)unitp,
181619397407SSherry Moore 	    PCF8591_BASE_ADDR | PCF8591_DEV7, cpunum, 0, 0, 0,
181719397407SSherry Moore 	    &recv_data, 1);
181829949e86Sstevel 
181929949e86Sstevel 	/*
182029949e86Sstevel 	 * We need to take a sledge hammer to the bus if we get back
182129949e86Sstevel 	 * value of the chip. This means that the i2c bus got wedged.
182229949e86Sstevel 	 * On the 1.4 systems this happens sometimes while running
182329949e86Sstevel 	 * sunvts. We will return the max cpu temp minus 10 to make
182429949e86Sstevel 	 * the fans run at full speed so that we don;t cook the
182529949e86Sstevel 	 * system.
182629949e86Sstevel 	 * At this point this is a workaround for hardware glitch.
182729949e86Sstevel 	 */
182829949e86Sstevel 	if (status == DDI_FAILURE) {
182929949e86Sstevel 		drv_usecwait(1000);
183029949e86Sstevel 		if (retrys < envctrl_max_retries) {
183129949e86Sstevel 			retrys++;
183229949e86Sstevel 			goto retry;
183329949e86Sstevel 		} else {
183429949e86Sstevel 			mutex_exit(&unitp->umutex);
183529949e86Sstevel 			envctrl_init_bus(unitp);
183629949e86Sstevel 			mutex_enter(&unitp->umutex);
183729949e86Sstevel 			if (envctrl_debug_flags)
183829949e86Sstevel 				cmn_err(CE_WARN, "envctrl CPU TEMP read " \
183929949e86Sstevel 				    "failed\n");
184029949e86Sstevel 			/* we don't want to power off the system */
184129949e86Sstevel 			return (MAX_CPU_TEMP - 10);
184229949e86Sstevel 		}
184329949e86Sstevel 	}
184429949e86Sstevel 
184529949e86Sstevel 	return (cpu_temps[recv_data]);
184629949e86Sstevel }
184729949e86Sstevel 
184829949e86Sstevel static int
envctrl_get_lm75_temp(struct envctrlunit * unitp)184929949e86Sstevel envctrl_get_lm75_temp(struct envctrlunit *unitp)
185029949e86Sstevel {
185129949e86Sstevel 
185229949e86Sstevel 	int k;
185329949e86Sstevel 	ushort_t lmval;
185429949e86Sstevel 	uint8_t tmp1;
185529949e86Sstevel 	uint8_t tmp2;
185629949e86Sstevel 	int status;
185729949e86Sstevel 	uint8_t buf[2];
185829949e86Sstevel 
185929949e86Sstevel 
186029949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
186129949e86Sstevel 
186229949e86Sstevel 	status = eHc_read_lm75((struct eHc_envcunit *)unitp,
186319397407SSherry Moore 	    LM75_BASE_ADDR | LM75_CONFIG_ADDRA, buf, 2);
186429949e86Sstevel 	if (status != DDI_SUCCESS)
186529949e86Sstevel 		cmn_err(CE_WARN, "read of LM75 failed\n");
186629949e86Sstevel 
186729949e86Sstevel 	tmp1 = buf[0];
186829949e86Sstevel 	tmp2 = buf[1];
186929949e86Sstevel 
187029949e86Sstevel 	/*
187129949e86Sstevel 	 * Store the forst 8 bits in the upper nibble of the
187229949e86Sstevel 	 * short, then store the lower 8 bits in the lower nibble
187329949e86Sstevel 	 * of the short, shift 7 to the right to get the 9 bit value
187429949e86Sstevel 	 * that the lm75 is really sending.
187529949e86Sstevel 	 */
187629949e86Sstevel 	lmval = tmp1 << 8;
187729949e86Sstevel 	lmval = (lmval | tmp2);
187829949e86Sstevel 	lmval = (lmval >> 7);
187929949e86Sstevel 	/*
188029949e86Sstevel 	 * Check the 9th bit to see if it is a negative
188129949e86Sstevel 	 * temperature. If so change into 2's compliment
188229949e86Sstevel 	 * and divide by 2 since each value is equal to a
188329949e86Sstevel 	 * half degree strp in degrees C
188429949e86Sstevel 	 */
188529949e86Sstevel 	if (lmval & LM75_COMP_MASK) {
188629949e86Sstevel 		tmp1 = (lmval & LM75_COMP_MASK_UPPER);
188729949e86Sstevel 		tmp1 = -tmp1;
188829949e86Sstevel 		tmp1 = tmp1/2;
188929949e86Sstevel 		k = 0 - tmp1;
189029949e86Sstevel 	} else {
189129949e86Sstevel 		k = lmval /2;
189229949e86Sstevel 	}
1893*eb6b10e6SToomas Soome 	return (k);
189429949e86Sstevel }
189529949e86Sstevel 
189629949e86Sstevel 
189729949e86Sstevel static void
envctrl_tempr_poll(void * arg)189829949e86Sstevel envctrl_tempr_poll(void *arg)
189929949e86Sstevel {
190029949e86Sstevel 	int diag_flag = 0;
190129949e86Sstevel 	struct envctrlunit *unitp = (struct envctrlunit *)arg;
190229949e86Sstevel 
190329949e86Sstevel 	mutex_enter(&unitp->umutex);
190429949e86Sstevel 
190529949e86Sstevel 	if (unitp->shutdown == B_TRUE) {
190629949e86Sstevel 		(void) power_down("Fatal System Environmental Control Error");
190729949e86Sstevel 	}
190829949e86Sstevel 
190929949e86Sstevel 	/*
191029949e86Sstevel 	 * if we are in diag mode and the temp poll thread goes off,
191129949e86Sstevel 	 * this means that the system is too heavily loaded and the 60 second
191229949e86Sstevel 	 * window to execute the test is failing. We will change the fanspeed
191329949e86Sstevel 	 * but will not check for a fanfault. This will cause a system shutdown
191429949e86Sstevel 	 * if the system has had a fanfault injected.
191529949e86Sstevel 	 */
191629949e86Sstevel 	if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
191729949e86Sstevel 		diag_flag++;
191829949e86Sstevel 		if (envctrl_debug_flags) {
191929949e86Sstevel 			cmn_err(CE_WARN,
192029949e86Sstevel 			    "Tempr poll went off while in DIAG MODE");
192129949e86Sstevel 		}
192229949e86Sstevel 	}
192329949e86Sstevel 	unitp->current_mode = ENVCTRL_NORMAL_MODE;
192429949e86Sstevel 	envctrl_get_sys_temperatures(unitp, (uint8_t *)NULL);
192529949e86Sstevel 	if (diag_flag == 0) {
192629949e86Sstevel 		envctrl_fan_fail_service(unitp);
192729949e86Sstevel 	}
192829949e86Sstevel 	/* now have this thread sleep for a while */
192929949e86Sstevel 	unitp->timeout_id = (timeout(envctrl_tempr_poll,
193029949e86Sstevel 	    (caddr_t)unitp, overtemp_timeout_hz));
193129949e86Sstevel 
193229949e86Sstevel 	mutex_exit(&unitp->umutex);
193329949e86Sstevel }
193429949e86Sstevel 
193529949e86Sstevel static void
envctrl_led_blink(void * arg)193629949e86Sstevel envctrl_led_blink(void *arg)
193729949e86Sstevel {
193829949e86Sstevel 	struct envctrl_pcf8574_chip fspchip;
193929949e86Sstevel 	struct envctrlunit *unitp = (struct envctrlunit *)arg;
194029949e86Sstevel 
194129949e86Sstevel 	mutex_enter(&unitp->umutex);
194229949e86Sstevel 
194329949e86Sstevel 	fspchip.type = PCF8574A;
194429949e86Sstevel 	fspchip.chip_num = ENVCTRL_PCF8574_DEV6; /* 0x01 port 1 */
194529949e86Sstevel 	envctrl_recv(unitp, (caddr_t *)(void *)&fspchip, PCF8574);
194629949e86Sstevel 
194729949e86Sstevel 	if (unitp->present_led_state == B_TRUE) {
194829949e86Sstevel 		/*
194929949e86Sstevel 		 * Now we need to "or" in fault bits of the FSP
195029949e86Sstevel 		 * module for the mass storage fault led.
195129949e86Sstevel 		 * and set it.
195229949e86Sstevel 		 */
195329949e86Sstevel 		fspchip.val = (fspchip.val & ~(ENVCTRL_PCF8574_PORT4) |
195419397407SSherry Moore 		    0xC0);
195529949e86Sstevel 		unitp->present_led_state = B_FALSE;
195629949e86Sstevel 	} else {
195729949e86Sstevel 		fspchip.val = (fspchip.val | ENVCTRL_PCF8574_PORT4 | 0xC0);
195829949e86Sstevel 		unitp->present_led_state = B_TRUE;
195929949e86Sstevel 	}
196029949e86Sstevel 
196129949e86Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)&fspchip, PCF8574);
196229949e86Sstevel 
196329949e86Sstevel 	/* now have this thread sleep for a while */
196429949e86Sstevel 	unitp->blink_timeout_id = (timeout(envctrl_led_blink,
196529949e86Sstevel 	    (caddr_t)unitp, blink_timeout_hz));
196629949e86Sstevel 
196729949e86Sstevel 	mutex_exit(&unitp->umutex);
196829949e86Sstevel }
196929949e86Sstevel 
197029949e86Sstevel /* called with mutex held */
197129949e86Sstevel static void
envctrl_get_sys_temperatures(struct envctrlunit * unitp,uint8_t * diag_tempr)197229949e86Sstevel envctrl_get_sys_temperatures(struct envctrlunit *unitp, uint8_t *diag_tempr)
197329949e86Sstevel {
197429949e86Sstevel 	int temperature, tmptemp, cputemp, hicputemp, ambtemp;
197529949e86Sstevel 	int i;
197629949e86Sstevel 	struct envctrl_tda8444t_chip fan;
197729949e86Sstevel 	uint8_t psaddr[] = {PSTEMP3, PSTEMP2, PSTEMP1, PSTEMP0};
1978*eb6b10e6SToomas Soome 	uint8_t noval = 0;
197929949e86Sstevel 	uint8_t fspval;
198029949e86Sstevel 
198129949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
198229949e86Sstevel 
198329949e86Sstevel 	fan.fan_num = ENVCTRL_CPU_FANS;
198429949e86Sstevel 	fan.chip_num = ENVCTRL_TDA8444T_DEV7;
198529949e86Sstevel 
198629949e86Sstevel 	tmptemp = 0;	/* Right init value ?? */
198729949e86Sstevel 
198829949e86Sstevel 	/*
198929949e86Sstevel 	 * THis routine is caled once every minute
199029949e86Sstevel 	 * we wil re-se the watchdog timer each time
199129949e86Sstevel 	 * we poll the temps. The watchdog timer is
199229949e86Sstevel 	 * set up for 3 minutes. Should the kernel thread
199329949e86Sstevel 	 * wedge, for some reason the watchdog will go off
199429949e86Sstevel 	 * and blast the fans.
199529949e86Sstevel 	 */
199629949e86Sstevel 
199729949e86Sstevel 	if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
199829949e86Sstevel 		unitp->current_mode = ENVCTRL_NORMAL_MODE;
199929949e86Sstevel 		envctrl_reset_watchdog(unitp, &noval);
200029949e86Sstevel 		unitp->current_mode = ENVCTRL_DIAG_MODE;
200129949e86Sstevel 	} else {
200229949e86Sstevel 		envctrl_reset_watchdog(unitp, &noval);
200329949e86Sstevel 	}
200429949e86Sstevel 
200529949e86Sstevel 	/*
200629949e86Sstevel 	 * we need to reset the dflop to allow the fans to be
200729949e86Sstevel 	 * set if the watchdog goes of and the kernel resumes
200829949e86Sstevel 	 * resetting the dflop alos resets the device interrupts
200929949e86Sstevel 	 * we need to reenable them also.
201029949e86Sstevel 	 */
201129949e86Sstevel 	envctrl_reset_dflop(unitp);
201229949e86Sstevel 
201329949e86Sstevel 	envctrl_enable_devintrs(unitp);
201429949e86Sstevel 
201529949e86Sstevel 	/*
201629949e86Sstevel 	 * If we are in diag mode we allow the system to be
201729949e86Sstevel 	 * faked out as to what the temperature is
201829949e86Sstevel 	 * to see if the fans speed up.
201929949e86Sstevel 	 */
202029949e86Sstevel 	if (unitp->current_mode == ENVCTRL_DIAG_MODE && diag_tempr != NULL) {
202129949e86Sstevel 		if (unitp->timeout_id != 0) {
202219397407SSherry Moore 			(void) untimeout(unitp->timeout_id);
202329949e86Sstevel 		}
202429949e86Sstevel 
202529949e86Sstevel 		ambtemp = *diag_tempr;
202629949e86Sstevel 		unitp->timeout_id = (timeout(envctrl_tempr_poll,
202729949e86Sstevel 		    (caddr_t)unitp, overtemp_timeout_hz));
202829949e86Sstevel 	} else {
202929949e86Sstevel 		ambtemp = envctrl_get_lm75_temp(unitp);
203029949e86Sstevel 		/*
203129949e86Sstevel 		 * Sometimes when we read the temp it comes back bogus
203229949e86Sstevel 		 * to fix this we just need to reset the envctrl bus
203329949e86Sstevel 		 */
203429949e86Sstevel 		if (ambtemp == -100) {
203529949e86Sstevel 			mutex_exit(&unitp->umutex);
203629949e86Sstevel 			envctrl_init_bus(unitp);
203729949e86Sstevel 			mutex_enter(&unitp->umutex);
203829949e86Sstevel 			ambtemp = envctrl_get_lm75_temp(unitp);
203929949e86Sstevel 		}
204029949e86Sstevel 	}
204129949e86Sstevel 
204229949e86Sstevel 	envctrl_mod_encl_kstats(unitp, ENVCTRL_ENCL_AMBTEMPR, INSTANCE_0,
204329949e86Sstevel 	    ambtemp);
204429949e86Sstevel 
204529949e86Sstevel 	fspval = envctrl_get_fpm_status(unitp);
204629949e86Sstevel 
204729949e86Sstevel 	if (ambtemp > MAX_AMB_TEMP) {
204829949e86Sstevel 		fspval |= (ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_GEN_ERR);
204929949e86Sstevel 		if (!(envctrl_power_off_overide) &&
205029949e86Sstevel 		    unitp->current_mode == ENVCTRL_NORMAL_MODE) {
205129949e86Sstevel 			unitp->shutdown = B_TRUE;
205229949e86Sstevel 		}
2053*eb6b10e6SToomas Soome 		if (unitp->current_mode == ENVCTRL_NORMAL_MODE) {
2054*eb6b10e6SToomas Soome 			cmn_err(CE_WARN,
2055*eb6b10e6SToomas Soome 			    "Ambient Temperature is %d C, shutdown now\n",
2056*eb6b10e6SToomas Soome 			    ambtemp);
2057*eb6b10e6SToomas Soome 		}
205829949e86Sstevel 	} else {
205929949e86Sstevel 		if (envctrl_isother_fault_led(unitp, fspval,
206019397407SSherry Moore 		    ENVCTRL_FSP_TEMP_ERR)) {
206129949e86Sstevel 			fspval &= ~(ENVCTRL_FSP_TEMP_ERR);
206229949e86Sstevel 		} else {
206329949e86Sstevel 			fspval &= ~(ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_GEN_ERR);
206429949e86Sstevel 		}
206529949e86Sstevel 	}
206629949e86Sstevel 
206729949e86Sstevel 	envctrl_set_fsp(unitp, &fspval);
206829949e86Sstevel 
206929949e86Sstevel 	cputemp = hicputemp = 0;
207029949e86Sstevel #ifndef TESTBED
207129949e86Sstevel 	for (i = 0; i < ENVCTRL_MAX_CPUS; i++) {
207229949e86Sstevel 		if (unitp->cpu_pr_location[i] == B_TRUE) {
207329949e86Sstevel 			cputemp = envctrl_get_cpu_temp(unitp, i);
207429949e86Sstevel 			envctrl_mod_encl_kstats(unitp, ENVCTRL_ENCL_CPUTEMPR,
207529949e86Sstevel 			    i, cputemp);
207629949e86Sstevel 			if (cputemp >= MAX_CPU_TEMP) {
207729949e86Sstevel 				if (!(envctrl_power_off_overide)) {
207829949e86Sstevel 					unitp->shutdown = B_TRUE;
207929949e86Sstevel 				}
208029949e86Sstevel 				cmn_err(CE_WARN,
208129949e86Sstevel 				    "CPU %d OVERHEATING!!!", i);
208229949e86Sstevel 			}
208329949e86Sstevel 
208429949e86Sstevel 			if (cputemp > hicputemp) {
208529949e86Sstevel 				hicputemp = cputemp;
208629949e86Sstevel 			}
208729949e86Sstevel 		}
208829949e86Sstevel 	}
208929949e86Sstevel #else
209029949e86Sstevel 	cputemp = envctrl_get_cpu_temp(unitp, 0);
209129949e86Sstevel 	envctrl_mod_encl_kstats(unitp, ENVCTRL_ENCL_CPUTEMPR, 0, cputemp);
209229949e86Sstevel #endif
209329949e86Sstevel 
209429949e86Sstevel 	fspval = envctrl_get_fpm_status(unitp);
209529949e86Sstevel 
209629949e86Sstevel 	/*
209729949e86Sstevel 	 * We first look at the ambient temp. If the system is at idle
209829949e86Sstevel 	 * the cpu temps will be approx 20 degrees above ambient.
209929949e86Sstevel 	 * If the cpu's rise above 20, then the CPU fans are set
210029949e86Sstevel 	 * according to the cpu temp minus 20 degrees C.
210129949e86Sstevel 	 */
210229949e86Sstevel 	if (unitp->current_mode == ENVCTRL_DIAG_MODE && diag_tempr != NULL) {
210329949e86Sstevel 		temperature = ambtemp;
210429949e86Sstevel 	} else {
210529949e86Sstevel 		temperature = hicputemp - CPU_AMB_RISE;
210629949e86Sstevel 	}
210729949e86Sstevel 
210829949e86Sstevel 	if (temperature < 0) {
2109*eb6b10e6SToomas Soome 		fan.val = MAX_FAN_SPEED;	/* blast it is out of range */
211029949e86Sstevel 	} else if (temperature > MAX_AMB_TEMP) {
211129949e86Sstevel 		fan.val = MAX_FAN_SPEED;
211229949e86Sstevel 		fspval |= (ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_GEN_ERR);
211329949e86Sstevel 
2114*eb6b10e6SToomas Soome 		if (unitp->current_mode == ENVCTRL_NORMAL_MODE) {
2115*eb6b10e6SToomas Soome 			cmn_err(CE_WARN,
2116*eb6b10e6SToomas Soome 			    "CPU Fans set to MAX. CPU Temp is %d C\n",
2117*eb6b10e6SToomas Soome 			    hicputemp);
2118*eb6b10e6SToomas Soome 		}
211929949e86Sstevel 	} else if (ambtemp < MAX_AMB_TEMP) {
212029949e86Sstevel 		if (!envctrl_p0_enclosure) {
212129949e86Sstevel 			fan.val = acme_cpu_fanspd[temperature];
212229949e86Sstevel 		} else {
212329949e86Sstevel 			fan.val = fan_speed[temperature];
212429949e86Sstevel 		}
212529949e86Sstevel 		if (envctrl_isother_fault_led(unitp, fspval,
212619397407SSherry Moore 		    ENVCTRL_FSP_TEMP_ERR)) {
212729949e86Sstevel 			fspval &= ~(ENVCTRL_FSP_TEMP_ERR);
212829949e86Sstevel 		} else {
212929949e86Sstevel 			fspval &= ~(ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_GEN_ERR);
213029949e86Sstevel 		}
213129949e86Sstevel 	}
213229949e86Sstevel 
213329949e86Sstevel 	envctrl_set_fsp(unitp, &fspval);
213429949e86Sstevel 
213529949e86Sstevel 	/*
213629949e86Sstevel 	 * Update temperature kstats. FSP kstats are updated in the
213729949e86Sstevel 	 * set and get routine.
213829949e86Sstevel 	 */
213929949e86Sstevel 
214029949e86Sstevel 	unitp->fan_kstats[ENVCTRL_FAN_TYPE_CPU].fanspeed = fan.val;
214129949e86Sstevel 
214229949e86Sstevel 	/* CPU FANS */
214329949e86Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)&fan, TDA8444T);
214429949e86Sstevel 
214529949e86Sstevel 	/* The afb Fan is always at max */
214629949e86Sstevel 	if (unitp->AFB_present == B_TRUE) {
214729949e86Sstevel 		fan.val = AFB_MAX;
214829949e86Sstevel 		/* AFB FANS */
214929949e86Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].fanspeed = fan.val;
215029949e86Sstevel 		fan.fan_num = ENVCTRL_AFB_FANS;
215129949e86Sstevel 		(void) envctrl_xmit(unitp, (caddr_t *)(void *)&fan, TDA8444T);
215229949e86Sstevel 	}
215329949e86Sstevel 
215429949e86Sstevel 	/*
215529949e86Sstevel 	 * Now set the Powersupply fans
215629949e86Sstevel 	 */
215729949e86Sstevel 
215829949e86Sstevel 	tmptemp = temperature = 0;
215929949e86Sstevel 	for (i = 0; i <= MAXPS; i++) {
216029949e86Sstevel 		if (unitp->ps_present[i]) {
216129949e86Sstevel 			tmptemp = envctrl_get_ps_temp(unitp, psaddr[i]);
216229949e86Sstevel 			unitp->ps_kstats[i].ps_tempr = tmptemp & 0xFFFF;
216329949e86Sstevel 			if (tmptemp > temperature) {
216429949e86Sstevel 				temperature = tmptemp;
216529949e86Sstevel 			}
216629949e86Sstevel 			if (temperature >= MAX_PS_TEMP) {
216729949e86Sstevel 				if (!(envctrl_power_off_overide)) {
216829949e86Sstevel 					unitp->shutdown = B_TRUE;
216929949e86Sstevel 				}
217029949e86Sstevel 				cmn_err(CE_WARN,
217129949e86Sstevel 				    "Power Supply %d OVERHEATING!!!\
217229949e86Sstevel 				    Temp is %d C", i, temperature);
217329949e86Sstevel 			}
217429949e86Sstevel 		}
217529949e86Sstevel 	}
217629949e86Sstevel 
217729949e86Sstevel 
217829949e86Sstevel 	fan.fan_num = ENVCTRL_PS_FANS;
217929949e86Sstevel 	if (temperature > PS_TEMP_WARN) {
218029949e86Sstevel 		fspval = envctrl_get_fpm_status(unitp);
218129949e86Sstevel 		fspval |= (ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_GEN_ERR);
218229949e86Sstevel 		envctrl_set_fsp(unitp, &fspval);
218329949e86Sstevel 		fan.val = MAX_FAN_SPEED;
218429949e86Sstevel 		cmn_err(CE_WARN, "A Power Supply is close to  OVERHEATING!!!");
218529949e86Sstevel 	} else {
218629949e86Sstevel 		if (temperature - ambtemp > PS_AMB_RISE) {
218729949e86Sstevel 			ambtemp = temperature - PS_AMB_RISE;
218829949e86Sstevel 		}
218929949e86Sstevel 		if (!envctrl_p0_enclosure) {
219029949e86Sstevel 			fan.val = acme_ps_fanspd[ambtemp];
219129949e86Sstevel 		} else {
219229949e86Sstevel 			fan.val = ps_fans[ambtemp];
219329949e86Sstevel 		}
219429949e86Sstevel 	}
219529949e86Sstevel 
219629949e86Sstevel 	/*
219729949e86Sstevel 	 * XXX add in error condition for ps overtemp
219829949e86Sstevel 	 */
219929949e86Sstevel 
220029949e86Sstevel 	unitp->fan_kstats[ENVCTRL_FAN_TYPE_PS].fanspeed = fan.val;
220129949e86Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)&fan, TDA8444T);
220229949e86Sstevel }
220329949e86Sstevel 
220429949e86Sstevel /* called with mutex held */
220529949e86Sstevel static void
envctrl_fan_fail_service(struct envctrlunit * unitp)220629949e86Sstevel envctrl_fan_fail_service(struct envctrlunit *unitp)
220729949e86Sstevel {
220829949e86Sstevel 	uint8_t recv_data, fpmstat;
220929949e86Sstevel 	int fantype;
221029949e86Sstevel 	int psfanflt, cpufanflt, afbfanflt;
221129949e86Sstevel 	int retries = 0, max_retry_count;
221229949e86Sstevel 	int status;
221329949e86Sstevel 
221429949e86Sstevel 	psfanflt = cpufanflt = afbfanflt = 0;
221529949e86Sstevel 	/*
221629949e86Sstevel 	 * The fan fail sensor is located at address 0x70
221729949e86Sstevel 	 * on the envctrl bus.
221829949e86Sstevel 	 */
221929949e86Sstevel 
222029949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
222129949e86Sstevel 
222229949e86Sstevel retry:
222329949e86Sstevel 	status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
222419397407SSherry Moore 	    PCF8574A_BASE_ADDR | ENVCTRL_PCF8574_DEV4, &recv_data, 1);
222529949e86Sstevel 	if (status != DDI_SUCCESS)
222629949e86Sstevel 		cmn_err(CE_WARN, "fan_fail_service: status = %d, data = %x\n",
222719397407SSherry Moore 		    status, recv_data);
222829949e86Sstevel 
222929949e86Sstevel 	/*
223029949e86Sstevel 	 * If all fan ports are high (0xff) then we don't have any
223129949e86Sstevel 	 * fan faults. Reset the kstats
223229949e86Sstevel 	 */
223329949e86Sstevel 	if (recv_data == 0xff) {
223429949e86Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_PS].fans_ok = B_TRUE;
223529949e86Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_CPU].fans_ok = B_TRUE;
223629949e86Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].fans_ok = B_TRUE;
223729949e86Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_PS].fanflt_num = 0;
223829949e86Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_CPU].fanflt_num = 0;
223929949e86Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].fanflt_num = 0;
224029949e86Sstevel 		unitp->num_fans_failed = 0;
224129949e86Sstevel 		fpmstat = envctrl_get_fpm_status(unitp);
224229949e86Sstevel 		if (!(envctrl_isother_fault_led(unitp, fpmstat, 0))) {
224329949e86Sstevel 			fpmstat &= ~(ENVCTRL_FSP_GEN_ERR);
224429949e86Sstevel 		}
224529949e86Sstevel 		if (unitp->shutdown != B_TRUE) {
224629949e86Sstevel 			envctrl_set_fsp(unitp, &fpmstat);
224729949e86Sstevel 		}
224829949e86Sstevel 		return;
224929949e86Sstevel 	}
225029949e86Sstevel 
225129949e86Sstevel 	fantype = ENVCTRL_FAN_TYPE_PS;
225229949e86Sstevel 
225329949e86Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT0)) {
225429949e86Sstevel 		psfanflt = PS_FAN_3;
225529949e86Sstevel 	}
225629949e86Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT1)) {
225729949e86Sstevel 		psfanflt = PS_FAN_2;
225829949e86Sstevel 	}
225929949e86Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT2)) {
226029949e86Sstevel 		psfanflt = PS_FAN_1;
226129949e86Sstevel 	}
226229949e86Sstevel 
226329949e86Sstevel 	if (psfanflt != 0) {
226429949e86Sstevel 		unitp->fan_kstats[fantype].fans_ok = B_FALSE;
226529949e86Sstevel 		unitp->fan_kstats[fantype].fanflt_num = psfanflt - 1;
226629949e86Sstevel 		if (retries == MAX_FAN_FAIL_RETRY && status == DDI_SUCCESS &&
226729949e86Sstevel 		    unitp->current_mode == ENVCTRL_NORMAL_MODE) {
226829949e86Sstevel 			cmn_err(CE_WARN, "PS Fan Number %d Failed",
226929949e86Sstevel 			    psfanflt - 1);
227029949e86Sstevel 		}
227129949e86Sstevel 	} else {
227229949e86Sstevel 		unitp->fan_kstats[fantype].fans_ok = B_TRUE;
227329949e86Sstevel 		unitp->fan_kstats[fantype].fanflt_num = 0;
227429949e86Sstevel 	}
227529949e86Sstevel 
227629949e86Sstevel 	fantype = ENVCTRL_FAN_TYPE_CPU;
227729949e86Sstevel 
227829949e86Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT3)) {
227929949e86Sstevel 		cpufanflt = CPU_FAN_1;
228029949e86Sstevel 	}
228129949e86Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT4)) {
228229949e86Sstevel 		cpufanflt = CPU_FAN_2;
228329949e86Sstevel 	}
228429949e86Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT5)) {
228529949e86Sstevel 		cpufanflt = CPU_FAN_3;
228629949e86Sstevel 	}
228729949e86Sstevel 
228829949e86Sstevel 	if (cpufanflt != 0) {
228929949e86Sstevel 		unitp->fan_kstats[fantype].fans_ok = B_FALSE;
229029949e86Sstevel 		unitp->fan_kstats[fantype].fanflt_num = cpufanflt - 1;
229129949e86Sstevel 		if (retries == MAX_FAN_FAIL_RETRY && status == DDI_SUCCESS &&
229229949e86Sstevel 		    unitp->current_mode == ENVCTRL_NORMAL_MODE) {
229329949e86Sstevel 			cmn_err(CE_WARN, "CPU Fan Number %d Failed",
229429949e86Sstevel 			    cpufanflt - 1);
229529949e86Sstevel 		}
229629949e86Sstevel 	} else {
229729949e86Sstevel 		unitp->fan_kstats[fantype].fans_ok = B_TRUE;
229829949e86Sstevel 		unitp->fan_kstats[fantype].fanflt_num = 0;
229929949e86Sstevel 	}
230029949e86Sstevel 
230129949e86Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT6) &&
230219397407SSherry Moore 	    (unitp->AFB_present == B_TRUE)) {
230329949e86Sstevel 		/*
230429949e86Sstevel 		 * If the afb is present and the afb fan fails,
230529949e86Sstevel 		 * we need to power off or else it will melt!
230629949e86Sstevel 		 * If it isn't present just log the error.
230729949e86Sstevel 		 * We make the decision off of the afbfanflt
230829949e86Sstevel 		 * flag later on in an if statement.
230929949e86Sstevel 		 */
231029949e86Sstevel 		afbfanflt++;
231129949e86Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].fans_ok
231229949e86Sstevel 		    = B_FALSE;
231329949e86Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].fanflt_num =
231429949e86Sstevel 		    AFB_FAN_1;
231529949e86Sstevel 		if (unitp->current_mode == ENVCTRL_NORMAL_MODE) {
231629949e86Sstevel 			cmn_err(CE_WARN, "AFB Fan Failed");
231729949e86Sstevel 		}
231829949e86Sstevel 
231929949e86Sstevel 	}
232029949e86Sstevel 
232129949e86Sstevel 	/*
232229949e86Sstevel 	 * If we have no Fan Faults Clear the LED's
232329949e86Sstevel 	 * If we have fan faults set the Gen Fault LED.
232429949e86Sstevel 	 */
232529949e86Sstevel 	if (psfanflt == 0 && cpufanflt == 0 && afbfanflt == 0 &&
232629949e86Sstevel 	    unitp->num_fans_failed != 0) {
232729949e86Sstevel 		fpmstat = envctrl_get_fpm_status(unitp);
232829949e86Sstevel 		if (!(envctrl_isother_fault_led(unitp,
232929949e86Sstevel 		    fpmstat, 0))) {
233029949e86Sstevel 			fpmstat &= ~(ENVCTRL_FSP_GEN_ERR);
233129949e86Sstevel 		}
233229949e86Sstevel 		envctrl_set_fsp(unitp, &fpmstat);
233329949e86Sstevel 	} else if (psfanflt != 0 || cpufanflt != 0 || afbfanflt != 0) {
233429949e86Sstevel 		fpmstat = envctrl_get_fpm_status(unitp);
233529949e86Sstevel 		fpmstat |= ENVCTRL_FSP_GEN_ERR;
233629949e86Sstevel 		envctrl_set_fsp(unitp, &fpmstat);
233729949e86Sstevel 	}
233829949e86Sstevel 
233929949e86Sstevel 	if (unitp->AFB_present == B_FALSE) {
234029949e86Sstevel 		afbfanflt = 0;
234129949e86Sstevel 	}
234229949e86Sstevel 
234329949e86Sstevel 	if ((cpufanflt > 0 || psfanflt > 0 || afbfanflt > 0 ||
234419397407SSherry Moore 	    (status != DDI_SUCCESS)) && !unitp->initting &&
234519397407SSherry Moore 	    unitp->current_mode == ENVCTRL_NORMAL_MODE) {
234629949e86Sstevel 		if (status != DDI_SUCCESS)
234729949e86Sstevel 			max_retry_count = envctrl_max_retries;
234829949e86Sstevel 		else
234929949e86Sstevel 			max_retry_count = MAX_FAN_FAIL_RETRY;
235029949e86Sstevel 		if (retries <= max_retry_count) {
235129949e86Sstevel 			retries++;
235229949e86Sstevel 			drv_usecwait(1000);
235329949e86Sstevel 			if (retries == max_retry_count) {
235429949e86Sstevel 				cmn_err(CE_WARN,
235529949e86Sstevel 				    "Fan Fail is 0x%x, retries = %d\n",
235629949e86Sstevel 				    recv_data, retries);
235729949e86Sstevel 			}
235829949e86Sstevel 			envctrl_get_sys_temperatures(unitp,
235929949e86Sstevel 			    (uint8_t *)NULL);
236029949e86Sstevel 			goto retry;
236129949e86Sstevel 		}
236229949e86Sstevel 		if (!(envctrl_power_off_overide)) {
236329949e86Sstevel 			unitp->shutdown = B_TRUE;
236429949e86Sstevel 		}
236529949e86Sstevel 		cmn_err(CE_WARN, "Fan Failure(s), System Shutdown");
236629949e86Sstevel 	}
236729949e86Sstevel 
236829949e86Sstevel 	unitp->num_fans_failed = (psfanflt + cpufanflt + afbfanflt);
236929949e86Sstevel 
237029949e86Sstevel }
237129949e86Sstevel 
237229949e86Sstevel /*
237329949e86Sstevel  * Check for power supply insertion and failure.
237429949e86Sstevel  * This is a bit tricky, because a power supply insertion will
237529949e86Sstevel  * trigger a load share interrupt as well as PS present in the
237629949e86Sstevel  * new supply. if we detect an insertion clear
237729949e86Sstevel  * interrupts, disable interrupts, wait for a couple of seconds
237829949e86Sstevel  * come back and see if the PSOK bit is set, PS_PRESENT is set
237929949e86Sstevel  * and the share fail interrupts are gone. If not this is a
238029949e86Sstevel  * real load share fail event.
238129949e86Sstevel  * Called with mutex held
238229949e86Sstevel  */
238329949e86Sstevel 
238429949e86Sstevel static void
envctrl_PS_intr_service(struct envctrlunit * unitp,uint8_t psaddr)238529949e86Sstevel envctrl_PS_intr_service(struct envctrlunit *unitp, uint8_t psaddr)
238629949e86Sstevel {
238729949e86Sstevel 	uint8_t recv_data;
238829949e86Sstevel 	int status, retrys = 0;
238929949e86Sstevel 
239029949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
239129949e86Sstevel 
239229949e86Sstevel 	if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
239329949e86Sstevel 		return;
239429949e86Sstevel 	}
239529949e86Sstevel 
239629949e86Sstevel retry:
239729949e86Sstevel 	status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
239819397407SSherry Moore 	    PCF8574A_BASE_ADDR | psaddr & 0xF, &recv_data, 1);
239929949e86Sstevel 	if (status != DDI_SUCCESS) {
240029949e86Sstevel 		drv_usecwait(1000);
240129949e86Sstevel 		if (retrys < envctrl_max_retries) {
240229949e86Sstevel 			retrys++;
240329949e86Sstevel 			goto retry;
240429949e86Sstevel 		} else {
240529949e86Sstevel 			mutex_exit(&unitp->umutex);
240629949e86Sstevel 			envctrl_init_bus(unitp);
240729949e86Sstevel 			mutex_enter(&unitp->umutex);
240829949e86Sstevel 			if (envctrl_debug_flags)
240929949e86Sstevel 				cmn_err(CE_WARN,
241019397407SSherry Moore 				    "PS_intr_service: Read from 8574A " \
241119397407SSherry Moore 				"failed\n");
241229949e86Sstevel 		}
241329949e86Sstevel 	}
241429949e86Sstevel 
241529949e86Sstevel 	/*
241629949e86Sstevel 	 * setup a timeout thread to poll the ps after a
241729949e86Sstevel 	 * couple of seconds. This allows for the PS to settle
241829949e86Sstevel 	 * and doesn't report false errors on a hotplug
241929949e86Sstevel 	 */
242029949e86Sstevel 
242129949e86Sstevel 	unitp->pshotplug_id = (timeout(envctrl_pshotplug_poll,
242229949e86Sstevel 	    (caddr_t)unitp, pshotplug_timeout_hz));
242329949e86Sstevel 
242429949e86Sstevel }
242529949e86Sstevel 
242629949e86Sstevel /* called with mutex held */
242729949e86Sstevel static void
envctrl_reset_dflop(struct envctrlunit * unitp)242829949e86Sstevel envctrl_reset_dflop(struct envctrlunit *unitp)
242929949e86Sstevel {
243029949e86Sstevel 	struct envctrl_pcf8574_chip initval;
243129949e86Sstevel 
243229949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
243329949e86Sstevel 
243429949e86Sstevel 	/*
243529949e86Sstevel 	 * This initialization sequence allows a
243629949e86Sstevel 	 * to change state to stop the fans from
243729949e86Sstevel 	 * blastion upon poweron. If this isn't
243829949e86Sstevel 	 * done the writes to the 8444 will not complete
243929949e86Sstevel 	 * to the hardware because the dflop will
244029949e86Sstevel 	 * be closed
244129949e86Sstevel 	 */
244229949e86Sstevel 	initval.chip_num = ENVCTRL_PCF8574_DEV0; /* 0x01 port 1 */
244329949e86Sstevel 	initval.type = PCF8574A;
244429949e86Sstevel 
244529949e86Sstevel 	initval.val = ENVCTRL_DFLOP_INIT0;
244629949e86Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)&initval, PCF8574);
244729949e86Sstevel 
244829949e86Sstevel 	initval.val = ENVCTRL_DFLOP_INIT1;
244929949e86Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)&initval, PCF8574);
245029949e86Sstevel }
245129949e86Sstevel 
245229949e86Sstevel static void
envctrl_add_encl_kstats(struct envctrlunit * unitp,int type,int instance,uint8_t val)245329949e86Sstevel envctrl_add_encl_kstats(struct envctrlunit *unitp, int type,
245429949e86Sstevel     int instance, uint8_t val)
245529949e86Sstevel {
245629949e86Sstevel 	int i = 0;
245729949e86Sstevel 	boolean_t inserted = B_FALSE;
245829949e86Sstevel 
245929949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
246029949e86Sstevel 
246129949e86Sstevel 	while (i < MAX_DEVS && inserted == B_FALSE) {
246229949e86Sstevel 		if (unitp->encl_kstats[i].instance == I2C_NODEV) {
246329949e86Sstevel 			unitp->encl_kstats[i].instance = instance;
246429949e86Sstevel 			unitp->encl_kstats[i].type = type;
246529949e86Sstevel 			unitp->encl_kstats[i].value = val;
246629949e86Sstevel 			inserted = B_TRUE;
246729949e86Sstevel 		}
246829949e86Sstevel 		i++;
246929949e86Sstevel 	}
247029949e86Sstevel 	unitp->num_encl_present++;
247129949e86Sstevel }
247229949e86Sstevel 
247329949e86Sstevel /* called with mutex held */
247429949e86Sstevel static void
envctrl_enable_devintrs(struct envctrlunit * unitp)247529949e86Sstevel envctrl_enable_devintrs(struct envctrlunit *unitp)
247629949e86Sstevel {
247729949e86Sstevel 	struct envctrl_pcf8574_chip initval;
247829949e86Sstevel 
247929949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
248029949e86Sstevel 
248129949e86Sstevel 	/*
248229949e86Sstevel 	 * This initialization sequence allows a
248329949e86Sstevel 	 * to change state to stop the fans from
248429949e86Sstevel 	 * blastion upon poweron. If this isn't
248529949e86Sstevel 	 * done the writes to the 8444 will not complete
248629949e86Sstevel 	 * to the hardware because the dflop will
248729949e86Sstevel 	 * be closed
248829949e86Sstevel 	 */
248929949e86Sstevel 	initval.chip_num = ENVCTRL_PCF8574_DEV0; /* 0x01 port 1 */
249029949e86Sstevel 	initval.type = PCF8574A;
249129949e86Sstevel 
249229949e86Sstevel 	initval.val = ENVCTRL_DEVINTR_INTI0;
249329949e86Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)&initval, PCF8574);
249429949e86Sstevel 
249529949e86Sstevel 	/*
249629949e86Sstevel 	 * set lowerbits all high p0 = PS1, p1 = PS2
249729949e86Sstevel 	 * p2 = PS3 p4 = envctrl intr_ctrl
249829949e86Sstevel 	 */
249929949e86Sstevel 	initval.val = ENVCTRL_DEVINTR_INTI1;
250029949e86Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)&initval, PCF8574);
250129949e86Sstevel }
250229949e86Sstevel 
250329949e86Sstevel /* called with mutex held */
250429949e86Sstevel static void
envctrl_stop_clock(struct envctrlunit * unitp)250529949e86Sstevel envctrl_stop_clock(struct envctrlunit *unitp)
250629949e86Sstevel {
250729949e86Sstevel 	int status;
250829949e86Sstevel 	uint8_t buf[2];
250929949e86Sstevel 
251029949e86Sstevel 	/*
251129949e86Sstevel 	 * This routine talks to the PCF8583 which
251229949e86Sstevel 	 * is a clock calendar chip on the envctrl bus.
251329949e86Sstevel 	 * We use this chip as a watchdog timer for the
251429949e86Sstevel 	 * fan control. At reset this chip pulses the interrupt
251529949e86Sstevel 	 * line every 1 second. We need to be able to shut
251629949e86Sstevel 	 * this off.
251729949e86Sstevel 	 */
251829949e86Sstevel 
251929949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
252029949e86Sstevel 
252129949e86Sstevel 	buf[0] = CLOCK_CSR_REG;
252229949e86Sstevel 	buf[1] = CLOCK_DISABLE;
252329949e86Sstevel 
252429949e86Sstevel 	status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
252519397407SSherry Moore 	    PCF8583_BASE_ADDR | 0, buf, 2);
252629949e86Sstevel 	if (status != DDI_SUCCESS)
252729949e86Sstevel 		cmn_err(CE_WARN, "write to PCF8583 failed\n");
252829949e86Sstevel }
252929949e86Sstevel 
253029949e86Sstevel static void
envctrl_reset_watchdog(struct envctrlunit * unitp,uint8_t * wdval)253129949e86Sstevel envctrl_reset_watchdog(struct envctrlunit *unitp, uint8_t *wdval)
253229949e86Sstevel {
253329949e86Sstevel 
253429949e86Sstevel 	uint8_t w, r;
253529949e86Sstevel 	uint8_t res = 0;
253629949e86Sstevel 	int status;
253729949e86Sstevel 	uint8_t buf[3];
253829949e86Sstevel 
253929949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
254029949e86Sstevel 
254129949e86Sstevel 	/* the clock MUST be stopped before we re-set it */
254229949e86Sstevel 	envctrl_stop_clock(unitp);
254329949e86Sstevel 
254429949e86Sstevel 	/*
254529949e86Sstevel 	 * Reset the minutes counter to 0.
254629949e86Sstevel 	 */
254729949e86Sstevel 	buf[0] = ALARM_CTR_REG_MINS;
254829949e86Sstevel 	buf[1] = 0x0;
254929949e86Sstevel 	status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
255019397407SSherry Moore 	    PCF8583_BASE_ADDR | 0, buf, 2);
255129949e86Sstevel 	if (status != DDI_SUCCESS)
255229949e86Sstevel 		cmn_err(CE_WARN, "write to PCF8583 failed\n");
255329949e86Sstevel 
255429949e86Sstevel 	/*
255529949e86Sstevel 	 * set up the alarm timer for 3 minutes
255629949e86Sstevel 	 * start by setting reg 8 ALARM_CTRL_REG
255729949e86Sstevel 	 * If we are in diag mode, we set the timer in
255829949e86Sstevel 	 * seconds. Valid values are 40-99. The timer
255929949e86Sstevel 	 * counts up to 99. 40 would be 59 seconds
256029949e86Sstevel 	 */
256129949e86Sstevel 	buf[0] = CLOCK_ALARM_REG_A;
256229949e86Sstevel 	if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
256329949e86Sstevel 		if (unitp->timeout_id != 0) {
256429949e86Sstevel 			(void) untimeout(unitp->timeout_id);
256529949e86Sstevel 			unitp->timeout_id = 0;
256629949e86Sstevel 			unitp->timeout_id = (timeout(envctrl_tempr_poll,
256729949e86Sstevel 			    (caddr_t)unitp, overtemp_timeout_hz));
256829949e86Sstevel 		}
256929949e86Sstevel 		buf[1] = CLOCK_ENABLE_TIMER_S;
257029949e86Sstevel 	} else {
257129949e86Sstevel 		buf[1] = CLOCK_ENABLE_TIMER;
257229949e86Sstevel 	}
257329949e86Sstevel 
257429949e86Sstevel 	/* STEP 10: End Transmission */
257529949e86Sstevel 	status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
257619397407SSherry Moore 	    PCF8583_BASE_ADDR | 0, buf, 2);
257729949e86Sstevel 	if (status != DDI_SUCCESS)
257829949e86Sstevel 		cmn_err(CE_WARN, "Reset envctrl watchdog failed\n");
257929949e86Sstevel 
258029949e86Sstevel 	/*
258129949e86Sstevel 	 * Now set up the alarm timer register it
258229949e86Sstevel 	 * counts from 0-99 with an intr triggered
258329949e86Sstevel 	 * when it gets to overflow.. or 99. It will
258429949e86Sstevel 	 * also count from a pre-set value which is
258529949e86Sstevel 	 * where we are seting from. We want a 3 minute fail
258629949e86Sstevel 	 * safe so our value is 99-3 or 96.
258729949e86Sstevel 	 * we are programming register 7 in the 8583.
258829949e86Sstevel 	 */
258929949e86Sstevel 
259029949e86Sstevel 	buf[0] = ALARM_CTRL_REG;
259129949e86Sstevel 	/*
259229949e86Sstevel 	 * Allow the diagnostic to set the egg timer val.
259329949e86Sstevel 	 * never allow it to be set greater than the default.
259429949e86Sstevel 	 */
259529949e86Sstevel 	if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
259629949e86Sstevel 		if (*wdval > MAX_CL_VAL) {
259729949e86Sstevel 			buf[1] = EGG_TIMER_VAL;
259829949e86Sstevel 		} else {
259929949e86Sstevel 
260029949e86Sstevel 			w = *wdval/10;
260129949e86Sstevel 			r = *wdval%10;
260229949e86Sstevel 
260329949e86Sstevel 			res = res | r;
260429949e86Sstevel 			res = (0x99 - (res | (w << 4)));
260529949e86Sstevel 			buf[1] = res;
260629949e86Sstevel 		}
260729949e86Sstevel 	} else {
260829949e86Sstevel 		buf[1] = EGG_TIMER_VAL;
260929949e86Sstevel 	}
261029949e86Sstevel 
261129949e86Sstevel 	status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
261219397407SSherry Moore 	    PCF8583_BASE_ADDR | 0, buf, 2);
261329949e86Sstevel 	if (status != DDI_SUCCESS)
261429949e86Sstevel 		cmn_err(CE_WARN, "Reset envctrl watchdog failed\n");
261529949e86Sstevel 
261629949e86Sstevel 
261729949e86Sstevel 	/*
261829949e86Sstevel 	 * Now that we have set up.. it is time
261929949e86Sstevel 	 * to re-start the clock in the CSR.
262029949e86Sstevel 	 */
262129949e86Sstevel 
262229949e86Sstevel 	buf[0] = CLOCK_CSR_REG;
262329949e86Sstevel 	buf[1] = CLOCK_ENABLE;
262429949e86Sstevel 	status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
262519397407SSherry Moore 	    PCF8583_BASE_ADDR | 0, buf, 2);
262629949e86Sstevel 	if (status != DDI_SUCCESS)
262729949e86Sstevel 		cmn_err(CE_WARN, "Reset envctrl watchdog failed\n");
262829949e86Sstevel 
262929949e86Sstevel }
263029949e86Sstevel 
263129949e86Sstevel /* Called with unip mutex held */
263229949e86Sstevel static void
envctrl_ps_probe(struct envctrlunit * unitp)263329949e86Sstevel envctrl_ps_probe(struct envctrlunit *unitp)
263429949e86Sstevel {
263529949e86Sstevel 
263629949e86Sstevel 	uint8_t recv_data, fpmstat;
263729949e86Sstevel 	uint8_t psaddr[] = {PS1, PS2, PS3, PSTEMP0};
263829949e86Sstevel 	int i;
263929949e86Sstevel 	int ps_error = 0, retrys = 0;
264029949e86Sstevel 	int devaddr;
264129949e86Sstevel 	int status;
264229949e86Sstevel 	int twotimes = 0;
264329949e86Sstevel 
264429949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
264529949e86Sstevel 
264629949e86Sstevel 	unitp->num_ps_present = 0;
264729949e86Sstevel 
264829949e86Sstevel 	for (i = 0; i <= MAXPS; i++) {
264929949e86Sstevel 		unitp->ps_present[i] = B_FALSE;
265029949e86Sstevel 		unitp->ps_kstats[i].ps_rating = 0;
265129949e86Sstevel 		unitp->ps_kstats[i].ps_tempr = 0;
265229949e86Sstevel 
265329949e86Sstevel 		switch (psaddr[i]) {
265429949e86Sstevel 		case PS1:
265529949e86Sstevel 			devaddr = ENVCTRL_PCF8574_DEV3;
265629949e86Sstevel 			break;
265729949e86Sstevel 		case PS2:
265829949e86Sstevel 			devaddr = ENVCTRL_PCF8574_DEV2;
265929949e86Sstevel 			break;
266029949e86Sstevel 		case PS3:
266129949e86Sstevel 			devaddr = ENVCTRL_PCF8574_DEV1;
266229949e86Sstevel 			break;
266329949e86Sstevel 		case PSTEMP0:
266429949e86Sstevel 			devaddr = 0;
266529949e86Sstevel 			break;
266629949e86Sstevel 		}
266729949e86Sstevel 		retrys = 0;
266829949e86Sstevel retry:
266929949e86Sstevel 		status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
267019397407SSherry Moore 		    PCF8574A_BASE_ADDR | devaddr, &recv_data, 1);
267129949e86Sstevel 		if (status != DDI_SUCCESS) {
267229949e86Sstevel 			drv_usecwait(1000);
267329949e86Sstevel 			if (retrys < envctrl_max_retries) {
267429949e86Sstevel 				retrys++;
267529949e86Sstevel 				goto retry;
267629949e86Sstevel 			} else {
267729949e86Sstevel 				mutex_exit(&unitp->umutex);
267829949e86Sstevel 				envctrl_init_bus(unitp);
267929949e86Sstevel 				mutex_enter(&unitp->umutex);
268029949e86Sstevel 				/*
268129949e86Sstevel 				 * If we just reset the bus we need to reread
268229949e86Sstevel 				 * the status.  If a second attempt still fails
268329949e86Sstevel 				 * then report the read failure.
268429949e86Sstevel 				 */
268529949e86Sstevel 				if (twotimes == 0) {
268629949e86Sstevel 					twotimes++;
268729949e86Sstevel 					retrys = 0;
268829949e86Sstevel 					goto retry;
268929949e86Sstevel 				} else {
269029949e86Sstevel 					cmn_err(CE_WARN,
269129949e86Sstevel 					"PS_probe: Read from 8574A failed\n");
269229949e86Sstevel 				}
269329949e86Sstevel 			}
269429949e86Sstevel 		}
269529949e86Sstevel 
269629949e86Sstevel 		/*
269729949e86Sstevel 		 * Port 0 = PS Present
269829949e86Sstevel 		 * Port 1 = PS Type
269929949e86Sstevel 		 * Port 2 = PS Type
270029949e86Sstevel 		 * Port 3 = PS TYpe
270129949e86Sstevel 		 * Port 4 = DC Status
270229949e86Sstevel 		 * Port 5 = Current Limit
270329949e86Sstevel 		 * Port 6 = Current Share
270429949e86Sstevel 		 * Port 7 = SPARE
270529949e86Sstevel 		 */
270629949e86Sstevel 
270729949e86Sstevel 		/*
270829949e86Sstevel 		 * Port 0 = PS Present
270929949e86Sstevel 		 * Port is pulled LOW "0" to indicate
271029949e86Sstevel 		 * present.
271129949e86Sstevel 		 */
271229949e86Sstevel 
271329949e86Sstevel 		if (!(recv_data & ENVCTRL_PCF8574_PORT0)) {
271429949e86Sstevel 			unitp->ps_present[i] = B_TRUE;
271529949e86Sstevel 			/* update unit kstat array */
271629949e86Sstevel 			unitp->ps_kstats[i].instance = i;
271729949e86Sstevel 			unitp->ps_kstats[i].ps_tempr = ENVCTRL_INIT_TEMPR;
271829949e86Sstevel 			++unitp->num_ps_present;
271929949e86Sstevel 
272029949e86Sstevel 			if (power_supply_previous_state[i] == 0) {
272129949e86Sstevel 				cmn_err(CE_NOTE,
272229949e86Sstevel 				    "Power Supply %d inserted\n", i);
272329949e86Sstevel 			}
272429949e86Sstevel 			power_supply_previous_state[i] = 1;
272529949e86Sstevel 
272629949e86Sstevel 			if (!(recv_data & ENVCTRL_PCF8574_PORT1)) {
272729949e86Sstevel 				unitp->ps_kstats[i].ps_rating = ENVCTRL_PS_550;
272829949e86Sstevel 			}
272929949e86Sstevel 			if (!(recv_data & ENVCTRL_PCF8574_PORT2)) {
273029949e86Sstevel 				unitp->ps_kstats[i].ps_rating = ENVCTRL_PS_650;
273129949e86Sstevel 			}
273229949e86Sstevel 			if (!(recv_data & ENVCTRL_PCF8574_PORT3)) {
273329949e86Sstevel 				cmn_err(CE_WARN,
273429949e86Sstevel 				    "Power Supply %d NOT okay\n", i);
273529949e86Sstevel 				unitp->ps_kstats[i].ps_ok = B_FALSE;
273629949e86Sstevel 				ps_error++;
273729949e86Sstevel 			} else {
273829949e86Sstevel 				unitp->ps_kstats[i].ps_ok = B_TRUE;
273929949e86Sstevel 			}
274029949e86Sstevel 			if (!(recv_data & ENVCTRL_PCF8574_PORT4)) {
274129949e86Sstevel 				cmn_err(CE_WARN,
274229949e86Sstevel 				    "Power Supply %d Overloaded\n", i);
274329949e86Sstevel 				unitp->ps_kstats[i].limit_ok = B_FALSE;
274429949e86Sstevel 				ps_error++;
274529949e86Sstevel 			} else {
274629949e86Sstevel 				unitp->ps_kstats[i].limit_ok = B_TRUE;
274729949e86Sstevel 			}
274829949e86Sstevel 			if (!(recv_data & ENVCTRL_PCF8574_PORT5)) {
274929949e86Sstevel 				cmn_err(CE_WARN,
275029949e86Sstevel 				    "Power Supply %d load share err\n", i);
275129949e86Sstevel 				unitp->ps_kstats[i].curr_share_ok = B_FALSE;
275229949e86Sstevel 				ps_error++;
275329949e86Sstevel 			} else {
275429949e86Sstevel 				unitp->ps_kstats[i].curr_share_ok = B_TRUE;
275529949e86Sstevel 			}
275629949e86Sstevel 
275729949e86Sstevel 			if (!(recv_data & ENVCTRL_PCF8574_PORT6)) {
275829949e86Sstevel 				cmn_err(CE_WARN,
275929949e86Sstevel 				    "PS %d Shouln't interrupt\n", i);
276029949e86Sstevel 				ps_error++;
276129949e86Sstevel 			}
276229949e86Sstevel 
276329949e86Sstevel 			if (!(recv_data & ENVCTRL_PCF8574_PORT7)) {
276429949e86Sstevel 				cmn_err(CE_WARN,
276529949e86Sstevel 				    "PS %d Shouln't interrupt\n", i);
276629949e86Sstevel 				ps_error++;
276729949e86Sstevel 			}
276829949e86Sstevel 		} else {
276929949e86Sstevel 			/* No power supply present */
277029949e86Sstevel 			if (power_supply_previous_state[i] == 1) {
277129949e86Sstevel 				cmn_err(CE_NOTE,
277229949e86Sstevel 				    "Power Supply %d removed\n", i);
277329949e86Sstevel 			}
277429949e86Sstevel 			power_supply_previous_state[i] = 0;
277529949e86Sstevel 		}
277629949e86Sstevel 	}
277729949e86Sstevel 
277829949e86Sstevel 	fpmstat = envctrl_get_fpm_status(unitp);
277929949e86Sstevel 	if (ps_error) {
278029949e86Sstevel 		fpmstat |= (ENVCTRL_FSP_PS_ERR | ENVCTRL_FSP_GEN_ERR);
278129949e86Sstevel 	} else {
278229949e86Sstevel 		if (envctrl_isother_fault_led(unitp, fpmstat,
278319397407SSherry Moore 		    ENVCTRL_FSP_PS_ERR)) {
278429949e86Sstevel 			fpmstat &= ~(ENVCTRL_FSP_PS_ERR);
278529949e86Sstevel 		} else {
278629949e86Sstevel 			fpmstat &= ~(ENVCTRL_FSP_PS_ERR |
278729949e86Sstevel 			    ENVCTRL_FSP_GEN_ERR);
278829949e86Sstevel 		}
278929949e86Sstevel 
279029949e86Sstevel 	}
279129949e86Sstevel 	envctrl_set_fsp(unitp, &fpmstat);
279229949e86Sstevel 
279329949e86Sstevel 	/*
279429949e86Sstevel 	 * We need to reset all of the fans etc when a supply is
279529949e86Sstevel 	 * interrupted and added, but we don't want to reset the
279629949e86Sstevel 	 * fans if we are in DIAG mode. This will mess up SUNVTS.
279729949e86Sstevel 	 */
279829949e86Sstevel 	if (unitp->current_mode == ENVCTRL_NORMAL_MODE) {
279929949e86Sstevel 		envctrl_get_sys_temperatures(unitp, (uint8_t *)NULL);
280029949e86Sstevel 	}
280129949e86Sstevel }
280229949e86Sstevel 
280329949e86Sstevel /*
280429949e86Sstevel  * consider key switch position when handling an abort sequence
280529949e86Sstevel  */
280629949e86Sstevel static void
envctrl_abort_seq_handler(char * msg)280729949e86Sstevel envctrl_abort_seq_handler(char *msg)
280829949e86Sstevel {
280929949e86Sstevel 	struct envctrlunit *unitp;
281029949e86Sstevel 	int i;
281129949e86Sstevel 	uint8_t secure = 0;
281229949e86Sstevel 
281329949e86Sstevel 	/*
281429949e86Sstevel 	 * Find the instance of the device available on this host.
281529949e86Sstevel 	 * Note that there may be only one, but the instance may
281629949e86Sstevel 	 * not be zero.
281729949e86Sstevel 	 */
281829949e86Sstevel 	for (i = 0; i < MAX_DEVS; i++) {
281929949e86Sstevel 		if (unitp = (struct envctrlunit *)
282019397407SSherry Moore 		    ddi_get_soft_state(envctrlsoft_statep, i))
282129949e86Sstevel 			break;
282229949e86Sstevel 	}
282329949e86Sstevel 
282429949e86Sstevel 	ASSERT(unitp);
282529949e86Sstevel 
282629949e86Sstevel 	for (i = 0; i < MAX_DEVS; i++) {
282729949e86Sstevel 		if ((unitp->encl_kstats[i].type == ENVCTRL_ENCL_FSP) &&
282819397407SSherry Moore 		    (unitp->encl_kstats[i].instance != I2C_NODEV)) {
282929949e86Sstevel 			secure = unitp->encl_kstats[i].value;
283029949e86Sstevel 			break;
283129949e86Sstevel 		}
283229949e86Sstevel 	}
283329949e86Sstevel 
283429949e86Sstevel 	/*
283529949e86Sstevel 	 * take the logical not because we are in hardware mode only
283629949e86Sstevel 	 */
283729949e86Sstevel 
283829949e86Sstevel 	if ((secure & ENVCTRL_FSP_KEYMASK) == ENVCTRL_FSP_KEYLOCKED) {
283929949e86Sstevel 			cmn_err(CE_CONT,
284029949e86Sstevel 			    "!envctrl: ignoring debug enter sequence\n");
284129949e86Sstevel 	} else {
284229949e86Sstevel 		if (envctrl_debug_flags) {
284329949e86Sstevel 			cmn_err(CE_CONT, "!envctrl: allowing debug enter\n");
284429949e86Sstevel 		}
284529949e86Sstevel 		debug_enter(msg);
284629949e86Sstevel 	}
284729949e86Sstevel }
284829949e86Sstevel 
284929949e86Sstevel /*
285029949e86Sstevel  * get the front Panel module LED and keyswitch status.
285129949e86Sstevel  * this part is addressed at 0x7C on the i2c bus.
285229949e86Sstevel  * called with mutex held
285329949e86Sstevel  */
285429949e86Sstevel static uint8_t
envctrl_get_fpm_status(struct envctrlunit * unitp)285529949e86Sstevel envctrl_get_fpm_status(struct envctrlunit *unitp)
285629949e86Sstevel {
285729949e86Sstevel 	uint8_t recv_data;
285829949e86Sstevel 	int status, retrys = 0;
285929949e86Sstevel 
286029949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
286129949e86Sstevel 
286229949e86Sstevel retry:
286329949e86Sstevel 	status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
286419397407SSherry Moore 	    PCF8574A_BASE_ADDR | ENVCTRL_PCF8574_DEV6, &recv_data, 1);
286529949e86Sstevel 
286629949e86Sstevel 	/*
286729949e86Sstevel 	 * yet another place where a read can cause the
286829949e86Sstevel 	 * the SDA line of the i2c bus to get stuck low.
286929949e86Sstevel 	 * this funky sequence frees the SDA line.
287029949e86Sstevel 	 */
287129949e86Sstevel 	if (status != DDI_SUCCESS) {
287229949e86Sstevel 		drv_usecwait(1000);
287329949e86Sstevel 		if (retrys < envctrl_max_retries) {
287429949e86Sstevel 			retrys++;
287529949e86Sstevel 			goto retry;
287629949e86Sstevel 		} else {
287729949e86Sstevel 			mutex_exit(&unitp->umutex);
287829949e86Sstevel 			envctrl_init_bus(unitp);
287929949e86Sstevel 			mutex_enter(&unitp->umutex);
288029949e86Sstevel 			if (envctrl_debug_flags)
288129949e86Sstevel 				cmn_err(CE_WARN, "Read from PCF8574 (FPM) "\
288229949e86Sstevel 				    "failed\n");
288329949e86Sstevel 		}
288429949e86Sstevel 	}
288529949e86Sstevel 	recv_data = ~recv_data;
288629949e86Sstevel 	envctrl_mod_encl_kstats(unitp, ENVCTRL_ENCL_FSP,
288729949e86Sstevel 	    INSTANCE_0, recv_data);
288829949e86Sstevel 
288929949e86Sstevel 	return (recv_data);
289029949e86Sstevel }
289129949e86Sstevel 
289229949e86Sstevel static void
envctrl_set_fsp(struct envctrlunit * unitp,uint8_t * val)289329949e86Sstevel envctrl_set_fsp(struct envctrlunit *unitp, uint8_t *val)
289429949e86Sstevel {
289529949e86Sstevel 	struct envctrl_pcf8574_chip chip;
289629949e86Sstevel 
289729949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
289829949e86Sstevel 
289929949e86Sstevel 	chip.val = ENVCTRL_FSP_OFF; /* init all values to off */
290029949e86Sstevel 	chip.chip_num = ENVCTRL_PCF8574_DEV6; /* 0x01 port 1 */
290129949e86Sstevel 	chip.type = PCF8574A;
290229949e86Sstevel 
290329949e86Sstevel 	/*
290429949e86Sstevel 	 * strip off bits that are R/O
290529949e86Sstevel 	 */
290629949e86Sstevel 	chip.val = (~(ENVCTRL_FSP_KEYMASK | ENVCTRL_FSP_POMASK) & (*val));
290729949e86Sstevel 
290829949e86Sstevel 	chip.val = ~chip.val;
290929949e86Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)&chip, PCF8574);
291029949e86Sstevel 
291129949e86Sstevel }
291229949e86Sstevel 
291329949e86Sstevel static int
envctrl_get_dskled(struct envctrlunit * unitp,struct envctrl_pcf8574_chip * chip)291429949e86Sstevel envctrl_get_dskled(struct envctrlunit *unitp, struct envctrl_pcf8574_chip *chip)
291529949e86Sstevel {
291629949e86Sstevel 	uint_t oldtype;
291729949e86Sstevel 
291829949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
291929949e86Sstevel 
292029949e86Sstevel 	if (chip->chip_num > ENVCTRL_PCF8574_DEV2 ||
292119397407SSherry Moore 	    chip->type != ENVCTRL_ENCL_BACKPLANE4 &&
292219397407SSherry Moore 	    chip->type != ENVCTRL_ENCL_BACKPLANE8) {
292329949e86Sstevel 		return (DDI_FAILURE);
292429949e86Sstevel 	}
292529949e86Sstevel 	oldtype = chip->type;
292629949e86Sstevel 	chip->type = PCF8574;
292729949e86Sstevel 	envctrl_recv(unitp, (caddr_t *)(void *)chip, PCF8574);
292829949e86Sstevel 	chip->type = oldtype;
292929949e86Sstevel 	chip->val = ~chip->val;
293029949e86Sstevel 
293129949e86Sstevel 	return (DDI_SUCCESS);
293229949e86Sstevel }
293329949e86Sstevel static int
envctrl_set_dskled(struct envctrlunit * unitp,struct envctrl_pcf8574_chip * chip)293429949e86Sstevel envctrl_set_dskled(struct envctrlunit *unitp, struct envctrl_pcf8574_chip *chip)
293529949e86Sstevel {
293629949e86Sstevel 
293729949e86Sstevel 	struct envctrl_pcf8574_chip fspchip;
293829949e86Sstevel 	struct envctrl_pcf8574_chip backchip;
293929949e86Sstevel 	int i, instance;
294029949e86Sstevel 	int diskfault = 0;
294129949e86Sstevel 	uint8_t controller_addr[] = {ENVCTRL_PCF8574_DEV0, ENVCTRL_PCF8574_DEV1,
294229949e86Sstevel 	    ENVCTRL_PCF8574_DEV2};
294329949e86Sstevel 
294429949e86Sstevel 	/*
294529949e86Sstevel 	 * We need to check the type of disk led being set. If it
294629949e86Sstevel 	 * is a 4 slot backplane then the upper 4 bits (7, 6, 5, 4) are
294729949e86Sstevel 	 * invalid.
294829949e86Sstevel 	 */
294929949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
295029949e86Sstevel 
295129949e86Sstevel 
295229949e86Sstevel 	if (chip->chip_num > ENVCTRL_PCF8574_DEV2 ||
295319397407SSherry Moore 	    chip->val > ENVCTRL_DISK8LED_ALLOFF ||
295419397407SSherry Moore 	    chip->val < ENVCTRL_CHAR_ZERO) {
295529949e86Sstevel 		return (DDI_FAILURE);
295629949e86Sstevel 	}
295729949e86Sstevel 
295829949e86Sstevel 	if (chip->type != ENVCTRL_ENCL_BACKPLANE4 &&
295929949e86Sstevel 	    chip->type != ENVCTRL_ENCL_BACKPLANE8) {
296029949e86Sstevel 		return (DDI_FAILURE);
296129949e86Sstevel 	}
296229949e86Sstevel 
296329949e86Sstevel 	/*
296429949e86Sstevel 	 * Check all of the other controllwes LED states to make sure
296529949e86Sstevel 	 * that there are no disk faults. If so then if the user is
296629949e86Sstevel 	 * clearing the disk faults on this contoller, turn off
296729949e86Sstevel 	 * the mass storage fault led.
296829949e86Sstevel 	 */
296929949e86Sstevel 
297029949e86Sstevel 	backchip.type = PCF8574;
297129949e86Sstevel 	for (i = 0; i <= MAX_TAZ_CONTROLLERS; i++) {
297229949e86Sstevel 		if (controller_present[i] == -1)
297329949e86Sstevel 			continue;
297429949e86Sstevel 		backchip.chip_num = controller_addr[i];
297529949e86Sstevel 		envctrl_recv(unitp, (caddr_t *)(void *)&backchip, PCF8574);
297629949e86Sstevel 		if (chip->chip_num == controller_addr[i]) {
297729949e86Sstevel 			if (chip->val != ENVCTRL_CHAR_ZERO)
297829949e86Sstevel 				diskfault++;
297929949e86Sstevel 		} else if ((~backchip.val & 0xFF) != ENVCTRL_CHAR_ZERO) {
298029949e86Sstevel 			diskfault++;
298129949e86Sstevel 		}
298229949e86Sstevel 	}
298329949e86Sstevel 
298429949e86Sstevel 	fspchip.type = PCF8574A;
298529949e86Sstevel 	fspchip.chip_num = ENVCTRL_PCF8574_DEV6; /* 0x01 port 1 */
298629949e86Sstevel 	envctrl_recv(unitp, (caddr_t *)(void *)&fspchip, PCF8574);
298729949e86Sstevel 
298829949e86Sstevel 	if (diskfault) {
298929949e86Sstevel 		if (!(envctrl_isother_fault_led(unitp, fspchip.val & 0xFF,
299019397407SSherry Moore 		    ENVCTRL_FSP_DISK_ERR))) {
299129949e86Sstevel 			fspchip.val &= ~(ENVCTRL_FSP_DISK_ERR);
299229949e86Sstevel 		} else {
299329949e86Sstevel 			fspchip.val &= ~(ENVCTRL_FSP_DISK_ERR |
299429949e86Sstevel 			    ENVCTRL_FSP_GEN_ERR);
299529949e86Sstevel 		}
299629949e86Sstevel 		fspchip.val = (fspchip.val &
299729949e86Sstevel 		    ~(ENVCTRL_FSP_DISK_ERR | ENVCTRL_FSP_GEN_ERR));
299829949e86Sstevel 	} else {
299929949e86Sstevel 		fspchip.val = (fspchip.val |
300029949e86Sstevel 		    (ENVCTRL_FSP_DISK_ERR | ENVCTRL_FSP_GEN_ERR));
300129949e86Sstevel 	}
300229949e86Sstevel 	fspchip.type = PCF8574A;
300329949e86Sstevel 	fspchip.chip_num = ENVCTRL_PCF8574_DEV6; /* 0x01 port 1 */
300429949e86Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)&fspchip, PCF8574);
300529949e86Sstevel 
300629949e86Sstevel 	for (i = 0; i < (sizeof (backaddrs) / sizeof (uint8_t)); i++) {
300729949e86Sstevel 		if (chip->chip_num == backaddrs[i]) {
300829949e86Sstevel 			instance =  i;
300929949e86Sstevel 		}
301029949e86Sstevel 	}
301129949e86Sstevel 
301229949e86Sstevel 	switch (chip->type) {
301329949e86Sstevel 	case ENVCTRL_ENCL_BACKPLANE4:
301429949e86Sstevel 		envctrl_mod_encl_kstats(unitp, ENVCTRL_ENCL_BACKPLANE4,
301529949e86Sstevel 		    instance, chip->val);
301629949e86Sstevel 		break;
301729949e86Sstevel 	case ENVCTRL_ENCL_BACKPLANE8:
301829949e86Sstevel 		envctrl_mod_encl_kstats(unitp, ENVCTRL_ENCL_BACKPLANE8,
301929949e86Sstevel 		    instance, chip->val);
302029949e86Sstevel 		break;
302129949e86Sstevel 	default:
302229949e86Sstevel 		break;
302329949e86Sstevel 	}
302429949e86Sstevel 	chip->type = PCF8574;
302529949e86Sstevel 	/*
302629949e86Sstevel 	 * we take the ones compliment of the val passed in
302729949e86Sstevel 	 * because the hardware thinks that a "low" or "0"
302829949e86Sstevel 	 * is the way to indicate a fault. of course software
302929949e86Sstevel 	 * knows that a 1 is a TRUE state or fault. ;-)
303029949e86Sstevel 	 */
303129949e86Sstevel 	chip->val = ~(chip->val);
303229949e86Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)chip, PCF8574);
303329949e86Sstevel 	return (DDI_SUCCESS);
303429949e86Sstevel }
303529949e86Sstevel 
303629949e86Sstevel void
envctrl_add_kstats(struct envctrlunit * unitp)303729949e86Sstevel envctrl_add_kstats(struct envctrlunit *unitp)
303829949e86Sstevel {
303929949e86Sstevel 
304029949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
304129949e86Sstevel 
304229949e86Sstevel 	if ((unitp->enclksp = kstat_create(ENVCTRL_MODULE_NAME, unitp->instance,
304329949e86Sstevel 	    ENVCTRL_KSTAT_ENCL, "misc", KSTAT_TYPE_RAW,
304429949e86Sstevel 	    sizeof (unitp->encl_kstats),
304529949e86Sstevel 	    KSTAT_FLAG_PERSISTENT)) == NULL) {
304629949e86Sstevel 		cmn_err(CE_WARN, "envctrl%d: encl raw kstat_create failed",
304719397407SSherry Moore 		    unitp->instance);
304829949e86Sstevel 		return;
304929949e86Sstevel 	}
305029949e86Sstevel 
305129949e86Sstevel 	unitp->enclksp->ks_update = envctrl_encl_kstat_update;
305229949e86Sstevel 	unitp->enclksp->ks_private = (void *)unitp;
305329949e86Sstevel 	kstat_install(unitp->enclksp);
305429949e86Sstevel 
305529949e86Sstevel 
305629949e86Sstevel 	if ((unitp->fanksp = kstat_create(ENVCTRL_MODULE_NAME, unitp->instance,
305729949e86Sstevel 	    ENVCTRL_KSTAT_FANSTAT, "misc", KSTAT_TYPE_RAW,
305829949e86Sstevel 	    sizeof (unitp->fan_kstats),
305929949e86Sstevel 	    KSTAT_FLAG_PERSISTENT)) == NULL) {
306029949e86Sstevel 		cmn_err(CE_WARN, "envctrl%d: fans kstat_create failed",
306119397407SSherry Moore 		    unitp->instance);
306229949e86Sstevel 		return;
306329949e86Sstevel 	}
306429949e86Sstevel 
306529949e86Sstevel 	unitp->fanksp->ks_update = envctrl_fanstat_kstat_update;
306629949e86Sstevel 	unitp->fanksp->ks_private = (void *)unitp;
306729949e86Sstevel 	kstat_install(unitp->fanksp);
306829949e86Sstevel 
306929949e86Sstevel 	if ((unitp->psksp = kstat_create(ENVCTRL_MODULE_NAME, unitp->instance,
307029949e86Sstevel 	    ENVCTRL_KSTAT_PSNAME, "misc", KSTAT_TYPE_RAW,
307129949e86Sstevel 	    sizeof (unitp->ps_kstats),
307229949e86Sstevel 	    KSTAT_FLAG_PERSISTENT)) == NULL) {
307329949e86Sstevel 		cmn_err(CE_WARN, "envctrl%d: ps name kstat_create failed",
307419397407SSherry Moore 		    unitp->instance);
307529949e86Sstevel 		return;
307629949e86Sstevel 	}
307729949e86Sstevel 
307829949e86Sstevel 	unitp->psksp->ks_update = envctrl_ps_kstat_update;
307929949e86Sstevel 	unitp->psksp->ks_private = (void *)unitp;
308029949e86Sstevel 	kstat_install(unitp->psksp);
308129949e86Sstevel 
308229949e86Sstevel }
308329949e86Sstevel 
308429949e86Sstevel int
envctrl_ps_kstat_update(kstat_t * ksp,int rw)308529949e86Sstevel envctrl_ps_kstat_update(kstat_t *ksp, int rw)
308629949e86Sstevel {
308729949e86Sstevel 	struct envctrlunit *unitp;
308829949e86Sstevel 	char *kstatp;
308929949e86Sstevel 
309029949e86Sstevel 
309129949e86Sstevel 
309229949e86Sstevel 	unitp = (struct envctrlunit *)ksp->ks_private;
309329949e86Sstevel 
309429949e86Sstevel 	mutex_enter(&unitp->umutex);
309529949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
309629949e86Sstevel 
309729949e86Sstevel 	kstatp = (char *)ksp->ks_data;
309829949e86Sstevel 
309929949e86Sstevel 	if (rw == KSTAT_WRITE) {
310029949e86Sstevel 		return (EACCES);
310129949e86Sstevel 	} else {
310229949e86Sstevel 
310329949e86Sstevel 		unitp->psksp->ks_ndata = unitp->num_ps_present;
310429949e86Sstevel 		bcopy(&unitp->ps_kstats, kstatp, sizeof (unitp->ps_kstats));
310529949e86Sstevel 	}
310629949e86Sstevel 	mutex_exit(&unitp->umutex);
310729949e86Sstevel 	return (DDI_SUCCESS);
310829949e86Sstevel }
310929949e86Sstevel int
envctrl_fanstat_kstat_update(kstat_t * ksp,int rw)311029949e86Sstevel envctrl_fanstat_kstat_update(kstat_t *ksp, int rw)
311129949e86Sstevel {
311229949e86Sstevel 	struct envctrlunit *unitp;
311329949e86Sstevel 	char *kstatp;
311429949e86Sstevel 
311529949e86Sstevel 	kstatp = (char *)ksp->ks_data;
311629949e86Sstevel 	unitp = (struct envctrlunit *)ksp->ks_private;
311729949e86Sstevel 
311829949e86Sstevel 	mutex_enter(&unitp->umutex);
311929949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
312029949e86Sstevel 
312129949e86Sstevel 	if (rw == KSTAT_WRITE) {
312229949e86Sstevel 		return (EACCES);
312329949e86Sstevel 	} else {
312429949e86Sstevel 		unitp->fanksp->ks_ndata = unitp->num_fans_present;
312529949e86Sstevel 		bcopy(unitp->fan_kstats, kstatp, sizeof (unitp->fan_kstats));
312629949e86Sstevel 	}
312729949e86Sstevel 	mutex_exit(&unitp->umutex);
312829949e86Sstevel 	return (DDI_SUCCESS);
312929949e86Sstevel }
313029949e86Sstevel 
313129949e86Sstevel int
envctrl_encl_kstat_update(kstat_t * ksp,int rw)313229949e86Sstevel envctrl_encl_kstat_update(kstat_t *ksp, int rw)
313329949e86Sstevel {
313429949e86Sstevel 	struct envctrlunit *unitp;
313529949e86Sstevel 	char *kstatp;
313629949e86Sstevel 
313729949e86Sstevel 
313829949e86Sstevel 	kstatp = (char *)ksp->ks_data;
313929949e86Sstevel 	unitp = (struct envctrlunit *)ksp->ks_private;
314029949e86Sstevel 
314129949e86Sstevel 	mutex_enter(&unitp->umutex);
314229949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
314329949e86Sstevel 
314429949e86Sstevel 	if (rw == KSTAT_WRITE) {
314529949e86Sstevel 		return (EACCES);
314629949e86Sstevel 	} else {
314729949e86Sstevel 
314829949e86Sstevel 		unitp->enclksp->ks_ndata = unitp->num_encl_present;
314929949e86Sstevel 		(void) envctrl_get_fpm_status(unitp);
315029949e86Sstevel 		/* XXX Need to ad disk updates too ??? */
315129949e86Sstevel 		bcopy(unitp->encl_kstats, kstatp, sizeof (unitp->encl_kstats));
315229949e86Sstevel 	}
315329949e86Sstevel 	mutex_exit(&unitp->umutex);
315429949e86Sstevel 	return (DDI_SUCCESS);
315529949e86Sstevel }
315629949e86Sstevel 
315729949e86Sstevel /*
315829949e86Sstevel  * called with unitp lock held
315929949e86Sstevel  * type, fanspeed and fanflt will be set by the service routines
316029949e86Sstevel  */
316129949e86Sstevel static void
envctrl_init_fan_kstats(struct envctrlunit * unitp)316229949e86Sstevel envctrl_init_fan_kstats(struct envctrlunit *unitp)
316329949e86Sstevel {
316429949e86Sstevel 	int i;
316529949e86Sstevel 
316629949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
316729949e86Sstevel 
316829949e86Sstevel 	for (i = 0; i < unitp->num_fans_present; i++) {
316929949e86Sstevel 		unitp->fan_kstats[i].instance = 0;
317029949e86Sstevel 		unitp->fan_kstats[i].type = 0;
317129949e86Sstevel 		unitp->fan_kstats[i].fans_ok = B_TRUE;
317229949e86Sstevel 		unitp->fan_kstats[i].fanflt_num = B_FALSE;
317329949e86Sstevel 		unitp->fan_kstats[i].fanspeed = B_FALSE;
317429949e86Sstevel 	}
317529949e86Sstevel 
317629949e86Sstevel 	unitp->fan_kstats[ENVCTRL_FAN_TYPE_PS].type = ENVCTRL_FAN_TYPE_PS;
317729949e86Sstevel 	unitp->fan_kstats[ENVCTRL_FAN_TYPE_CPU].type = ENVCTRL_FAN_TYPE_CPU;
317829949e86Sstevel 	if (unitp->AFB_present == B_TRUE)
317929949e86Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].type =
318019397407SSherry Moore 		    ENVCTRL_FAN_TYPE_AFB;
318129949e86Sstevel }
318229949e86Sstevel 
318329949e86Sstevel static void
envctrl_init_encl_kstats(struct envctrlunit * unitp)318429949e86Sstevel envctrl_init_encl_kstats(struct envctrlunit *unitp)
318529949e86Sstevel {
318629949e86Sstevel 
318729949e86Sstevel 	int i;
318829949e86Sstevel 	uint8_t val;
318929949e86Sstevel 	struct envctrl_pcf8574_chip chip;
319029949e86Sstevel 	int *reg_prop;
319129949e86Sstevel 	uint_t len = 0;
319229949e86Sstevel 
319329949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
319429949e86Sstevel 
319529949e86Sstevel 	for (i = 0; i < MAX_DEVS; i++) {
319629949e86Sstevel 		unitp->encl_kstats[i].instance = I2C_NODEV;
319729949e86Sstevel 	}
319829949e86Sstevel 
319929949e86Sstevel 	/*
320029949e86Sstevel 	 * add in kstats now
320129949e86Sstevel 	 * We ALWAYS HAVE THE FOLLOWING
320229949e86Sstevel 	 * 1. FSP
320329949e86Sstevel 	 * 2. AMB TEMPR
320429949e86Sstevel 	 * 3. (1) CPU TEMPR
320529949e86Sstevel 	 * 4. (1) 4 slot disk backplane
320629949e86Sstevel 	 * OPTIONAL
320729949e86Sstevel 	 * 8 slot backplane
320829949e86Sstevel 	 * more cpu's
320929949e86Sstevel 	 */
321029949e86Sstevel 
321129949e86Sstevel 	chip.type = PCF8574A;
321229949e86Sstevel 	chip.chip_num = ENVCTRL_PCF8574_DEV6; /* 0x01 port 1 */
321329949e86Sstevel 	envctrl_recv(unitp, (caddr_t *)(void *)&chip, PCF8574);
321429949e86Sstevel 
321529949e86Sstevel 	envctrl_add_encl_kstats(unitp, ENVCTRL_ENCL_FSP, INSTANCE_0,
321619397407SSherry Moore 	    chip.val & 0xFF);
321729949e86Sstevel 
321829949e86Sstevel 	val = envctrl_get_lm75_temp(unitp) & 0xFF;
321929949e86Sstevel 	envctrl_add_encl_kstats(unitp, ENVCTRL_ENCL_AMBTEMPR, INSTANCE_0, val);
322029949e86Sstevel 
322129949e86Sstevel 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, unitp->dip,
322219397407SSherry Moore 	    DDI_PROP_DONTPASS, ENVCTRL_DISK_LEDS_PR,
322319397407SSherry Moore 	    &reg_prop, &len) != DDI_PROP_SUCCESS) {
322429949e86Sstevel 		cmn_err(CE_WARN, "prop lookup of %s failed\n",
322519397407SSherry Moore 		    ENVCTRL_DISK_LEDS_PR);
322629949e86Sstevel 		return;
322729949e86Sstevel 	}
322829949e86Sstevel 
322929949e86Sstevel 	ASSERT(len != 0);
323029949e86Sstevel 
323129949e86Sstevel 	chip.type = PCF8574;
323229949e86Sstevel 
323329949e86Sstevel 	for (i = 0; i < len; i++) {
323429949e86Sstevel 		chip.chip_num = backaddrs[i];
323529949e86Sstevel 		if (reg_prop[i] == ENVCTRL_4SLOT_BACKPLANE) {
323629949e86Sstevel 			envctrl_recv(unitp, (caddr_t *)(void *)&chip, PCF8574);
323729949e86Sstevel 			envctrl_add_encl_kstats(unitp, ENVCTRL_ENCL_BACKPLANE4,
323829949e86Sstevel 			    i, ~chip.val);
323929949e86Sstevel 			controller_present[i] = 1;
324029949e86Sstevel 		}
324129949e86Sstevel 		if (reg_prop[i] == ENVCTRL_8SLOT_BACKPLANE) {
324229949e86Sstevel 			envctrl_recv(unitp, (caddr_t *)(void *)&chip, PCF8574);
324329949e86Sstevel 			envctrl_add_encl_kstats(unitp, ENVCTRL_ENCL_BACKPLANE8,
324429949e86Sstevel 			    i, ~chip.val);
324529949e86Sstevel 			controller_present[i] = 1;
324629949e86Sstevel 		}
324729949e86Sstevel 	}
324829949e86Sstevel 	ddi_prop_free((void *)reg_prop);
324929949e86Sstevel 
325029949e86Sstevel }
325129949e86Sstevel 
325229949e86Sstevel static void
envctrl_mod_encl_kstats(struct envctrlunit * unitp,int type,int instance,uint8_t val)325329949e86Sstevel envctrl_mod_encl_kstats(struct envctrlunit *unitp, int type,
325429949e86Sstevel     int instance, uint8_t val)
325529949e86Sstevel {
325629949e86Sstevel 	int i = 0;
325729949e86Sstevel 	boolean_t inserted = B_FALSE;
325829949e86Sstevel 
325929949e86Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
326029949e86Sstevel 
326129949e86Sstevel 	while (i < MAX_DEVS && inserted == B_FALSE) {
326229949e86Sstevel 		if (unitp->encl_kstats[i].instance == instance &&
326329949e86Sstevel 		    unitp->encl_kstats[i].type == type) {
326429949e86Sstevel 			unitp->encl_kstats[i].value = val;
326529949e86Sstevel 			inserted = B_TRUE;
326629949e86Sstevel 		}
326729949e86Sstevel 		i++;
326829949e86Sstevel 	}
326929949e86Sstevel }
327029949e86Sstevel 
327129949e86Sstevel static void
envctrl_probe_cpus(struct envctrlunit * unitp)327229949e86Sstevel envctrl_probe_cpus(struct envctrlunit *unitp)
327329949e86Sstevel {
327429949e86Sstevel 	int instance;
327529949e86Sstevel 
327629949e86Sstevel 	/*
327729949e86Sstevel 	 * The cpu search is as follows:
327829949e86Sstevel 	 * If there is only 1 CPU module it is named as
327929949e86Sstevel 	 * SUNW,UltraSPARC. If this is a match we still don't
328029949e86Sstevel 	 * know what slot the cpu module is in therefore
328129949e86Sstevel 	 * we need to check the "upa-portid" property.
328229949e86Sstevel 	 * If we have more than 1 cpu, then they are appended by
328329949e86Sstevel 	 * instance numbers and slot locations. e.g.
328429949e86Sstevel 	 * SUNW,UltraSPARC@1,0 (slot 1). it would have been
328529949e86Sstevel 	 * nice to have the naming consistent for one CPU e.g.
328629949e86Sstevel 	 * SUNW,UltraSPARC@0,0...sigh
328729949e86Sstevel 	 */
328829949e86Sstevel 
328929949e86Sstevel 	for (instance = 0; instance < ENVCTRL_MAX_CPUS; instance++) {
329029949e86Sstevel 		unitp->cpu_pr_location[instance] = B_FALSE;
329129949e86Sstevel 	}
329229949e86Sstevel 
329329949e86Sstevel 	ddi_walk_devs(ddi_root_node(), envctrl_match_cpu, unitp);
329429949e86Sstevel }
329529949e86Sstevel 
329629949e86Sstevel static int
envctrl_match_cpu(dev_info_t * dip,void * arg)329729949e86Sstevel envctrl_match_cpu(dev_info_t *dip, void *arg)
329829949e86Sstevel {
329929949e86Sstevel 
330029949e86Sstevel 	int cpu_slot;
330129949e86Sstevel 	char name[32];
330229949e86Sstevel 	char name1[32];
330329949e86Sstevel 	struct envctrlunit *unitp = (struct envctrlunit *)arg;
330429949e86Sstevel 
330529949e86Sstevel 	(void) sprintf(name, "%s", ENVCTRL_TAZCPU_STRING);
330629949e86Sstevel 	(void) sprintf(name1, "%s", ENVCTRL_TAZBLKBRDCPU_STRING);
330729949e86Sstevel 
330829949e86Sstevel 	if ((strcmp(ddi_node_name(dip), name) == 0) ||
330919397407SSherry Moore 	    (strcmp(ddi_node_name(dip), name1) == 0)) {
331029949e86Sstevel 		if ((cpu_slot = (int)ddi_getprop(DDI_DEV_T_ANY, dip,
331119397407SSherry Moore 		    DDI_PROP_DONTPASS, "upa-portid", -1)) == -1) {
331229949e86Sstevel 			cmn_err(CE_WARN, "envctrl no cpu upa-portid");
331329949e86Sstevel 		} else {
331429949e86Sstevel 			unitp->cpu_pr_location[cpu_slot] = B_TRUE;
331529949e86Sstevel 			unitp->num_cpus_present++;
331629949e86Sstevel 		}
331729949e86Sstevel 	}
331829949e86Sstevel 
331929949e86Sstevel 	return (DDI_WALK_CONTINUE);
332029949e86Sstevel }
332129949e86Sstevel 
332229949e86Sstevel /*
332329949e86Sstevel  * This routine returns TRUE if some other error condition
332429949e86Sstevel  * has set the GEN_ERR FAULT LED. Tp further complicate this
332529949e86Sstevel  * LED panel we have overloaded the GEN_ERR LED to indicate
332629949e86Sstevel  * that a fan fault has occurred without having a fan fault
332729949e86Sstevel  * LED as does all other error conditions. So we just take the
332829949e86Sstevel  * software state and return true. The whole purpose of this functon
332929949e86Sstevel  * is to tell us wehther or not we can shut off the GEN_FAULT LED.
333029949e86Sstevel  * NOTE: this ledval is usually one of the following FSP vals
333129949e86Sstevel  * EXCEPT in the case of the fan fail.. we pass in a "0".
333229949e86Sstevel  */
333329949e86Sstevel 
333429949e86Sstevel static int
envctrl_isother_fault_led(struct envctrlunit * unitp,uint8_t fspval,uint8_t thisled)333529949e86Sstevel envctrl_isother_fault_led(struct envctrlunit *unitp, uint8_t fspval,
333629949e86Sstevel     uint8_t thisled)
333729949e86Sstevel {
333829949e86Sstevel 	int status = B_FALSE;
333929949e86Sstevel 
334029949e86Sstevel 	if (fspval != 0) {
334129949e86Sstevel 		fspval = (fspval & ~(thisled));
334229949e86Sstevel 	}
334329949e86Sstevel 	if (unitp->num_fans_failed > 0 && thisled != 0) {
334429949e86Sstevel 		status = B_TRUE;
334529949e86Sstevel 	} else if (fspval & ENVCTRL_FSP_DISK_ERR) {
334629949e86Sstevel 		status = B_TRUE;
334729949e86Sstevel 	} else if (fspval & ENVCTRL_FSP_PS_ERR) {
334829949e86Sstevel 		status = B_TRUE;
334929949e86Sstevel 	} else if (fspval & ENVCTRL_FSP_TEMP_ERR) {
335029949e86Sstevel 		status = B_TRUE;
335129949e86Sstevel 	}
335229949e86Sstevel 	return (status);
335329949e86Sstevel }
335429949e86Sstevel 
335529949e86Sstevel static void
envctrl_pshotplug_poll(void * arg)335629949e86Sstevel envctrl_pshotplug_poll(void *arg)
335729949e86Sstevel {
335829949e86Sstevel 	struct envctrlunit *unitp = (struct envctrlunit *)arg;
335929949e86Sstevel 
336029949e86Sstevel 	mutex_enter(&unitp->umutex);
336129949e86Sstevel 
336229949e86Sstevel 	envctrl_ps_probe(unitp);
336329949e86Sstevel 
336429949e86Sstevel 	mutex_exit(&unitp->umutex);
336529949e86Sstevel }
336629949e86Sstevel 
336729949e86Sstevel /*
336829949e86Sstevel  * The following routines implement the i2c protocol.
336929949e86Sstevel  * They should be removed once the envctrl_targets.c file is included.
337029949e86Sstevel  */
337129949e86Sstevel 
337229949e86Sstevel /*
337329949e86Sstevel  * put host interface into master mode
337429949e86Sstevel  */
337529949e86Sstevel static int
eHc_start_pcf8584(struct eHc_envcunit * ehcp,uint8_t byteaddress)337629949e86Sstevel eHc_start_pcf8584(struct eHc_envcunit *ehcp, uint8_t byteaddress)
337729949e86Sstevel {
337829949e86Sstevel 	uint8_t poll_status;
337929949e86Sstevel 	uint8_t discard;
338029949e86Sstevel 	int i;
338129949e86Sstevel 
338229949e86Sstevel 	/* wait if bus is busy */
338329949e86Sstevel 
338429949e86Sstevel 	i = 0;
338529949e86Sstevel 	do {
338629949e86Sstevel 		drv_usecwait(1000);
338729949e86Sstevel 		poll_status =
338819397407SSherry Moore 		    ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
338929949e86Sstevel 		i++;
339029949e86Sstevel 	} while (((poll_status & EHC_S1_NBB) == 0) && i < EHC_MAX_WAIT);
339129949e86Sstevel 
339229949e86Sstevel 	if (i == EHC_MAX_WAIT) {
339329949e86Sstevel 		DCMNERR(CE_WARN, "eHc_start_pcf8584: I2C bus busy");
339429949e86Sstevel 		return (EHC_FAILURE);
339529949e86Sstevel 	}
339629949e86Sstevel 
339729949e86Sstevel 	if (poll_status & EHC_S1_BER) {
339829949e86Sstevel 		DCMN2ERR(CE_WARN, "eHc_start_pcf8584: I2C bus error");
339929949e86Sstevel 		return (EHC_FAILURE);
340029949e86Sstevel 	}
340129949e86Sstevel 
340229949e86Sstevel 	if (poll_status & EHC_S1_LAB) {
340329949e86Sstevel 		DCMN2ERR(CE_WARN, "eHc_start_pcf8584: Lost arbitration");
340429949e86Sstevel 		return (EHC_FAILURE);
340529949e86Sstevel 	}
340629949e86Sstevel 
340729949e86Sstevel 	/* load the slave address */
340829949e86Sstevel 	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0, byteaddress);
340929949e86Sstevel 
341029949e86Sstevel 	/* generate the "start condition" and clock out the slave address */
341129949e86Sstevel 	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1,
341219397407SSherry Moore 	    EHC_S1_PIN | EHC_S1_ES0 | EHC_S1_STA | EHC_S1_ACK);
341329949e86Sstevel 
341429949e86Sstevel 	/* wait for completion of transmission */
341529949e86Sstevel 	i = 0;
341629949e86Sstevel 	do {
341729949e86Sstevel 		drv_usecwait(1000);
341829949e86Sstevel 		poll_status =
341919397407SSherry Moore 		    ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
342029949e86Sstevel 		i++;
342129949e86Sstevel 	} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);
342229949e86Sstevel 
342329949e86Sstevel 	if (i == EHC_MAX_WAIT) {
342429949e86Sstevel 		DCMNERR(CE_WARN, "eHc_start_pcf8584: I2C bus busy");
342529949e86Sstevel 		return (EHC_FAILURE);
342629949e86Sstevel 	}
342729949e86Sstevel 
342829949e86Sstevel 	if (poll_status & EHC_S1_BER) {
342929949e86Sstevel 		DCMN2ERR(CE_WARN, "eHc_start_pcf8584: I2C bus error");
343029949e86Sstevel 		return (EHC_FAILURE);
343129949e86Sstevel 	}
343229949e86Sstevel 
343329949e86Sstevel 	if (poll_status & EHC_S1_LAB) {
343429949e86Sstevel 		DCMN2ERR(CE_WARN, "eHc_start_pcf8584: Lost arbitration");
343529949e86Sstevel 		return (EHC_FAILURE);
343629949e86Sstevel 	}
343729949e86Sstevel 
343829949e86Sstevel 	if (poll_status & EHC_S1_LRB) {
343929949e86Sstevel 		DCMNERR(CE_WARN, "eHc_start_pcf8584: No slave ACK");
344029949e86Sstevel 		return (EHC_NO_SLAVE_ACK);
344129949e86Sstevel 	}
344229949e86Sstevel 
344329949e86Sstevel 	/*
344429949e86Sstevel 	 * If this is a read we are setting up for (as indicated by
344529949e86Sstevel 	 * the least significant byte being set), read
344629949e86Sstevel 	 * and discard the first byte off the bus - this
344729949e86Sstevel 	 * is the slave address.
344829949e86Sstevel 	 */
344929949e86Sstevel 
345029949e86Sstevel 	i = 0;
345129949e86Sstevel 	if (byteaddress & EHC_BYTE_READ) {
345229949e86Sstevel 		discard = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
345329949e86Sstevel #ifdef lint
345429949e86Sstevel 		discard = discard;
345529949e86Sstevel #endif
345629949e86Sstevel 
345729949e86Sstevel 		/* wait for completion of transmission */
345829949e86Sstevel 		do {
345929949e86Sstevel 			drv_usecwait(1000);
346019397407SSherry Moore 			poll_status = ddi_get8(ehcp->ctlr_handle,
346119397407SSherry Moore 			    &ehcp->bus_ctl_regs->s1);
346229949e86Sstevel 			i++;
346329949e86Sstevel 		} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);
346429949e86Sstevel 
346529949e86Sstevel 		if (i == EHC_MAX_WAIT) {
346629949e86Sstevel 			DCMNERR(CE_WARN, "eHc_start_pcf8584: I2C bus busy");
346729949e86Sstevel 			return (EHC_FAILURE);
346829949e86Sstevel 		}
346929949e86Sstevel 
347029949e86Sstevel 		if (poll_status & EHC_S1_BER) {
347129949e86Sstevel 			DCMN2ERR(CE_WARN,
347219397407SSherry Moore 			    "eHc_start_pcf8584: I2C bus error");
347329949e86Sstevel 			return (EHC_FAILURE);
347429949e86Sstevel 		}
347529949e86Sstevel 
347629949e86Sstevel 		if (poll_status & EHC_S1_LAB) {
347729949e86Sstevel 			DCMN2ERR(CE_WARN,
347819397407SSherry Moore 			    "eHc_start_pcf8584: Lost arbitration");
347929949e86Sstevel 			return (EHC_FAILURE);
348029949e86Sstevel 		}
348129949e86Sstevel 	}
348229949e86Sstevel 
348329949e86Sstevel 	return (EHC_SUCCESS);
348429949e86Sstevel }
348529949e86Sstevel 
348629949e86Sstevel /*
348729949e86Sstevel  * put host interface into slave/receiver mode
348829949e86Sstevel  */
348929949e86Sstevel static void
eHc_stop_pcf8584(struct eHc_envcunit * ehcp)349029949e86Sstevel eHc_stop_pcf8584(struct eHc_envcunit *ehcp)
349129949e86Sstevel {
349229949e86Sstevel 	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1,
349319397407SSherry Moore 	    EHC_S1_PIN | EHC_S1_ES0 | EHC_S1_STO | EHC_S1_ACK);
349429949e86Sstevel }
349529949e86Sstevel 
349629949e86Sstevel static int
eHc_read_pcf8584(struct eHc_envcunit * ehcp,uint8_t * data)349729949e86Sstevel eHc_read_pcf8584(struct eHc_envcunit *ehcp, uint8_t *data)
349829949e86Sstevel {
349929949e86Sstevel 	uint8_t poll_status;
350029949e86Sstevel 	int i = 0;
350129949e86Sstevel 
350229949e86Sstevel 	/* Read the byte of interest */
350329949e86Sstevel 	*data = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
350429949e86Sstevel 
350529949e86Sstevel 	/* wait for completion of transmission */
350629949e86Sstevel 	do {
350729949e86Sstevel 		drv_usecwait(1000);
350829949e86Sstevel 		poll_status =
350919397407SSherry Moore 		    ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
351029949e86Sstevel 		i++;
351129949e86Sstevel 	} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);
351229949e86Sstevel 
351329949e86Sstevel 	if (i == EHC_MAX_WAIT) {
351429949e86Sstevel 		DCMNERR(CE_WARN, "eHc_read_pcf8584: I2C bus busy");
351529949e86Sstevel 		return (EHC_FAILURE);
351629949e86Sstevel 	}
351729949e86Sstevel 
351829949e86Sstevel 	if (poll_status & EHC_S1_BER) {
351929949e86Sstevel 		DCMN2ERR(CE_WARN, "eHc_read_pcf8584: I2C bus error");
352029949e86Sstevel 		return (EHC_FAILURE);
352129949e86Sstevel 	}
352229949e86Sstevel 
352329949e86Sstevel 	if (poll_status & EHC_S1_LAB) {
352429949e86Sstevel 		DCMN2ERR(CE_WARN, "eHc_read_pcf8584: Lost arbitration");
352529949e86Sstevel 		return (EHC_FAILURE);
352629949e86Sstevel 	}
352729949e86Sstevel 
352829949e86Sstevel 	return (EHC_SUCCESS);
352929949e86Sstevel }
353029949e86Sstevel 
353129949e86Sstevel /*
353229949e86Sstevel  * host interface is in transmitter state, thus mode is master/transmitter
353329949e86Sstevel  * NOTE to Bill: this check the LRB bit (only done in transmit mode).
353429949e86Sstevel  */
353529949e86Sstevel 
353629949e86Sstevel static int
eHc_write_pcf8584(struct eHc_envcunit * ehcp,uint8_t data)353729949e86Sstevel eHc_write_pcf8584(struct eHc_envcunit *ehcp, uint8_t data)
353829949e86Sstevel {
353929949e86Sstevel 	uint8_t poll_status;
354029949e86Sstevel 	int i = 0;
354129949e86Sstevel 
354229949e86Sstevel 	/* send the data, EHC_S1_PIN should go to "1" immediately */
354329949e86Sstevel 	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0, data);
354429949e86Sstevel 
354529949e86Sstevel 	/* wait for completion of transmission */
354629949e86Sstevel 	do {
354729949e86Sstevel 		drv_usecwait(1000);
354829949e86Sstevel 		poll_status =
354919397407SSherry Moore 		    ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
355029949e86Sstevel 		i++;
355129949e86Sstevel 	} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);
355229949e86Sstevel 
355329949e86Sstevel 	if (i == EHC_MAX_WAIT) {
355429949e86Sstevel 		DCMNERR(CE_WARN, "eHc_write_pcf8584: I2C bus busy");
355529949e86Sstevel 		return (EHC_FAILURE);
355629949e86Sstevel 	}
355729949e86Sstevel 
355829949e86Sstevel 	if (poll_status & EHC_S1_BER) {
355929949e86Sstevel 		DCMN2ERR(CE_WARN, "eHc_write_pcf8584: I2C bus error");
356029949e86Sstevel 		return (EHC_FAILURE);
356129949e86Sstevel 	}
356229949e86Sstevel 
356329949e86Sstevel 	if (poll_status & EHC_S1_LAB) {
356429949e86Sstevel 		DCMN2ERR(CE_WARN, "eHc_write_pcf8584: Lost arbitration");
356529949e86Sstevel 		return (EHC_FAILURE);
356629949e86Sstevel 	}
356729949e86Sstevel 
356829949e86Sstevel 	if (poll_status & EHC_S1_LRB) {
356929949e86Sstevel 		DCMNERR(CE_WARN, "eHc_write_pcf8584: No slave ACK");
357029949e86Sstevel 		return (EHC_NO_SLAVE_ACK);
357129949e86Sstevel 	}
357229949e86Sstevel 
357329949e86Sstevel 	return (EHC_SUCCESS);
357429949e86Sstevel }
357529949e86Sstevel 
357629949e86Sstevel static int
eHc_after_read_pcf8584(struct eHc_envcunit * ehcp,uint8_t * data)357729949e86Sstevel eHc_after_read_pcf8584(struct eHc_envcunit *ehcp, uint8_t *data)
357829949e86Sstevel {
357929949e86Sstevel 	uint8_t discard;
358029949e86Sstevel 	uint8_t poll_status;
358129949e86Sstevel 	int i = 0;
358229949e86Sstevel 
358329949e86Sstevel 	/* set ACK in register S1 to 0 */
358429949e86Sstevel 	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1, EHC_S1_ES0);
358529949e86Sstevel 
358629949e86Sstevel 	/*
358729949e86Sstevel 	 * Read the "byte-before-the-last-byte" - sets PIN bit to '1'
358829949e86Sstevel 	 */
358929949e86Sstevel 
359029949e86Sstevel 	*data = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
359129949e86Sstevel 
359229949e86Sstevel 	/* wait for completion of transmission */
359329949e86Sstevel 	do {
359429949e86Sstevel 		drv_usecwait(1000);
359529949e86Sstevel 		poll_status =
359619397407SSherry Moore 		    ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
359729949e86Sstevel 		i++;
359829949e86Sstevel 	} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);
359929949e86Sstevel 
360029949e86Sstevel 	if (i == EHC_MAX_WAIT) {
360129949e86Sstevel 		DCMNERR(CE_WARN, "eHc_after_read_pcf8584: I2C bus busy");
360229949e86Sstevel 		return (EHC_FAILURE);
360329949e86Sstevel 	}
360429949e86Sstevel 
360529949e86Sstevel 	if (poll_status & EHC_S1_BER) {
360629949e86Sstevel 		DCMN2ERR(CE_WARN,
360719397407SSherry Moore 		    "eHc_after_read_pcf8584: I2C bus error");
360829949e86Sstevel 		return (EHC_FAILURE);
360929949e86Sstevel 	}
361029949e86Sstevel 
361129949e86Sstevel 	if (poll_status & EHC_S1_LAB) {
361229949e86Sstevel 		DCMN2ERR(CE_WARN, "eHc_after_read_pcf8584: Lost arbitration");
361329949e86Sstevel 		return (EHC_FAILURE);
361429949e86Sstevel 	}
361529949e86Sstevel 
361629949e86Sstevel 	/*
361729949e86Sstevel 	 * Generate the "stop" condition.
361829949e86Sstevel 	 */
361929949e86Sstevel 	eHc_stop_pcf8584(ehcp);
362029949e86Sstevel 
362129949e86Sstevel 	/*
362229949e86Sstevel 	 * Read the "last" byte.
362329949e86Sstevel 	 */
362429949e86Sstevel 	discard = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
362529949e86Sstevel #ifdef lint
362629949e86Sstevel 	discard = discard;
362729949e86Sstevel #endif
362829949e86Sstevel 
362929949e86Sstevel 	return (EHC_SUCCESS);
363029949e86Sstevel }
363129949e86Sstevel 
363229949e86Sstevel /*
363329949e86Sstevel  * Write to the TDA8444 chip.
363429949e86Sstevel  * byteaddress = chip type base address | chip offset address.
363529949e86Sstevel  */
363629949e86Sstevel static int
eHc_write_tda8444(struct eHc_envcunit * ehcp,int byteaddress,int instruction,int subaddress,uint8_t * buf,int size)363729949e86Sstevel eHc_write_tda8444(struct eHc_envcunit *ehcp, int byteaddress, int instruction,
3638*eb6b10e6SToomas Soome     int subaddress, uint8_t *buf, int size)
363929949e86Sstevel {
364029949e86Sstevel 	uint8_t control;
364129949e86Sstevel 	int i, status;
364229949e86Sstevel 
364329949e86Sstevel 	ASSERT((byteaddress & 0x1) == 0);
364429949e86Sstevel 	ASSERT(subaddress < 8);
364529949e86Sstevel 	ASSERT(instruction == 0xf || instruction == 0x0);
364629949e86Sstevel 	ASSERT(MUTEX_HELD(&ehcp->umutex));
364729949e86Sstevel 
364829949e86Sstevel 	control = (instruction << 4) | subaddress;
364929949e86Sstevel 
365029949e86Sstevel 	if ((status = eHc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
365129949e86Sstevel 		if (status == EHC_NO_SLAVE_ACK) {
365229949e86Sstevel 			/*
365329949e86Sstevel 			 * Send the "stop" condition.
365429949e86Sstevel 			 */
365529949e86Sstevel 			eHc_stop_pcf8584(ehcp);
365629949e86Sstevel 		}
365729949e86Sstevel 		return (EHC_FAILURE);
365829949e86Sstevel 	}
365929949e86Sstevel 
366029949e86Sstevel 	if ((status = eHc_write_pcf8584(ehcp, control)) != EHC_SUCCESS) {
366129949e86Sstevel 		if (status == EHC_NO_SLAVE_ACK) {
366229949e86Sstevel 		/*
366329949e86Sstevel 		 * Send the "stop" condition.
366429949e86Sstevel 		 */
366529949e86Sstevel 		eHc_stop_pcf8584(ehcp);
366629949e86Sstevel 		}
366729949e86Sstevel 		return (EHC_FAILURE);
366829949e86Sstevel 	}
366929949e86Sstevel 
367029949e86Sstevel 	for (i = 0; i < size; i++) {
367129949e86Sstevel 		if ((status = eHc_write_pcf8584(ehcp, (buf[i] & 0x3f))) !=
367219397407SSherry Moore 		    EHC_SUCCESS) {
367329949e86Sstevel 			if (status == EHC_NO_SLAVE_ACK)
367429949e86Sstevel 				eHc_stop_pcf8584(ehcp);
367529949e86Sstevel 			return (EHC_FAILURE);
367629949e86Sstevel 		}
367729949e86Sstevel 	}
367829949e86Sstevel 
367929949e86Sstevel 	eHc_stop_pcf8584(ehcp);
368029949e86Sstevel 
368129949e86Sstevel 	return (EHC_SUCCESS);
368229949e86Sstevel }
368329949e86Sstevel 
368429949e86Sstevel /*
368529949e86Sstevel  * Read from PCF8574A chip.
368629949e86Sstevel  * byteaddress = chip type base address | chip offset address.
368729949e86Sstevel  */
368829949e86Sstevel static int
eHc_read_pcf8574a(struct eHc_envcunit * ehcp,int byteaddress,uint8_t * buf,int size)368929949e86Sstevel eHc_read_pcf8574a(struct eHc_envcunit *ehcp, int byteaddress, uint8_t *buf,
3690*eb6b10e6SToomas Soome     int size)
369129949e86Sstevel {
369229949e86Sstevel 	int i;
369329949e86Sstevel 	int status;
369429949e86Sstevel 	uint8_t discard;
369529949e86Sstevel 
369629949e86Sstevel 	ASSERT((byteaddress & 0x1) == 0);
369729949e86Sstevel 	ASSERT(MUTEX_HELD(&ehcp->umutex));
369829949e86Sstevel 
369929949e86Sstevel 	/*
370029949e86Sstevel 	 * Put the bus into the start condition
370129949e86Sstevel 	 */
370229949e86Sstevel 	if ((status = eHc_start_pcf8584(ehcp, EHC_BYTE_READ | byteaddress)) !=
370319397407SSherry Moore 	    EHC_SUCCESS) {
370429949e86Sstevel 		if (status == EHC_NO_SLAVE_ACK) {
370529949e86Sstevel 			/*
370629949e86Sstevel 			 * Send the "stop" condition.
370729949e86Sstevel 			 */
370829949e86Sstevel 			eHc_stop_pcf8584(ehcp);
370929949e86Sstevel 			/*
371029949e86Sstevel 			 * Read the last byte - discard it.
371129949e86Sstevel 			 */
371219397407SSherry Moore 			discard = ddi_get8(ehcp->ctlr_handle,
371319397407SSherry Moore 			    &ehcp->bus_ctl_regs->s0);
371429949e86Sstevel #ifdef lint
371529949e86Sstevel 			discard = discard;
371629949e86Sstevel #endif
371729949e86Sstevel 		}
371829949e86Sstevel 		return (EHC_FAILURE);
371929949e86Sstevel 	}
372029949e86Sstevel 
372129949e86Sstevel 	for (i = 0; i < size - 1; i++) {
372229949e86Sstevel 		if ((status = eHc_read_pcf8584(ehcp, &buf[i])) != EHC_SUCCESS) {
372329949e86Sstevel 			return (EHC_FAILURE);
372429949e86Sstevel 		}
372529949e86Sstevel 	}
372629949e86Sstevel 
372729949e86Sstevel 	/*
372829949e86Sstevel 	 * Handle the part of the bus protocol which comes
372929949e86Sstevel 	 * after a read, including reading the last byte.
373029949e86Sstevel 	 */
373129949e86Sstevel 
373229949e86Sstevel 	if (eHc_after_read_pcf8584(ehcp, &buf[i]) != EHC_SUCCESS) {
373329949e86Sstevel 		return (EHC_FAILURE);
373429949e86Sstevel 	}
373529949e86Sstevel 
373629949e86Sstevel 	return (EHC_SUCCESS);
373729949e86Sstevel }
373829949e86Sstevel 
373929949e86Sstevel /*
374029949e86Sstevel  * Write to the PCF8574A chip.
374129949e86Sstevel  * byteaddress = chip type base address | chip offset address.
374229949e86Sstevel  */
374329949e86Sstevel static int
eHc_write_pcf8574a(struct eHc_envcunit * ehcp,int byteaddress,uint8_t * buf,int size)374429949e86Sstevel eHc_write_pcf8574a(struct eHc_envcunit *ehcp, int byteaddress, uint8_t *buf,
3745*eb6b10e6SToomas Soome     int size)
374629949e86Sstevel {
374729949e86Sstevel 	int i;
374829949e86Sstevel 	int status;
374929949e86Sstevel 
375029949e86Sstevel 	ASSERT((byteaddress & 0x1) == 0);
375129949e86Sstevel 	ASSERT(MUTEX_HELD(&ehcp->umutex));
375229949e86Sstevel 
375329949e86Sstevel 	/*
375429949e86Sstevel 	 * Put the bus into the start condition (write)
375529949e86Sstevel 	 */
375629949e86Sstevel 	if ((status = eHc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
375729949e86Sstevel 		if (status == EHC_NO_SLAVE_ACK) {
375829949e86Sstevel 			/*
375929949e86Sstevel 			 * Send the "stop" condition.
376029949e86Sstevel 			 */
376129949e86Sstevel 			eHc_stop_pcf8584(ehcp);
376229949e86Sstevel 		}
376329949e86Sstevel 		return (EHC_FAILURE);
376429949e86Sstevel 	}
376529949e86Sstevel 
376629949e86Sstevel 	/*
376729949e86Sstevel 	 * Send the data - poll as needed.
376829949e86Sstevel 	 */
376929949e86Sstevel 	for (i = 0; i < size; i++) {
377029949e86Sstevel 		if ((status = eHc_write_pcf8584(ehcp, buf[i])) != EHC_SUCCESS) {
377129949e86Sstevel 			if (status == EHC_NO_SLAVE_ACK)
377229949e86Sstevel 				eHc_stop_pcf8584(ehcp);
377329949e86Sstevel 			return (EHC_FAILURE);
377429949e86Sstevel 		}
377529949e86Sstevel 	}
377629949e86Sstevel 
377729949e86Sstevel 	/*
377829949e86Sstevel 	 * Transmission complete - generate stop condition and
377929949e86Sstevel 	 * put device back into slave receiver mode.
378029949e86Sstevel 	 */
378129949e86Sstevel 	eHc_stop_pcf8584(ehcp);
378229949e86Sstevel 
378329949e86Sstevel 	return (EHC_SUCCESS);
378429949e86Sstevel }
378529949e86Sstevel 
378629949e86Sstevel /*
378729949e86Sstevel  * Read from the PCF8574 chip.
378829949e86Sstevel  * byteaddress = chip type base address | chip offset address.
378929949e86Sstevel  */
379029949e86Sstevel static int
eHc_read_pcf8574(struct eHc_envcunit * ehcp,int byteaddress,uint8_t * buf,int size)379129949e86Sstevel eHc_read_pcf8574(struct eHc_envcunit *ehcp, int byteaddress, uint8_t *buf,
3792*eb6b10e6SToomas Soome     int size)
379329949e86Sstevel {
379429949e86Sstevel 	int i;
379529949e86Sstevel 	int status;
379629949e86Sstevel 	uint8_t discard;
379729949e86Sstevel 
379829949e86Sstevel 	ASSERT((byteaddress & 0x1) == 0);
379929949e86Sstevel 	ASSERT(MUTEX_HELD(&ehcp->umutex));
380029949e86Sstevel 
380129949e86Sstevel 	/*
380229949e86Sstevel 	 * Put the bus into the start condition
380329949e86Sstevel 	 */
380429949e86Sstevel 	if ((status = eHc_start_pcf8584(ehcp, EHC_BYTE_READ | byteaddress)) !=
380519397407SSherry Moore 	    EHC_SUCCESS) {
380629949e86Sstevel 		if (status == EHC_NO_SLAVE_ACK) {
380729949e86Sstevel 			/*
380829949e86Sstevel 			 * Send the "stop" condition.
380929949e86Sstevel 			 */
381029949e86Sstevel 			eHc_stop_pcf8584(ehcp);
381129949e86Sstevel 			/*
381229949e86Sstevel 			 * Read the last byte - discard it.
381329949e86Sstevel 			 */
381419397407SSherry Moore 			discard = ddi_get8(ehcp->ctlr_handle,
381519397407SSherry Moore 			    &ehcp->bus_ctl_regs->s0);
381629949e86Sstevel #ifdef lint
381729949e86Sstevel 			discard = discard;
381829949e86Sstevel #endif
381929949e86Sstevel 		}
382029949e86Sstevel 		return (EHC_FAILURE);
382129949e86Sstevel 	}
382229949e86Sstevel 
382329949e86Sstevel 	for (i = 0; i < size - 1; i++) {
382429949e86Sstevel 		if ((status = eHc_read_pcf8584(ehcp, &buf[i])) != EHC_SUCCESS) {
382529949e86Sstevel 		return (EHC_FAILURE);
382629949e86Sstevel 		}
382729949e86Sstevel 	}
382829949e86Sstevel 
382929949e86Sstevel 	/*
383029949e86Sstevel 	 * Handle the part of the bus protocol which comes
383129949e86Sstevel 	 * after a read.
383229949e86Sstevel 	 */
383329949e86Sstevel 
383429949e86Sstevel 	if (eHc_after_read_pcf8584(ehcp, &buf[i]) != EHC_SUCCESS) {
383529949e86Sstevel 		return (EHC_FAILURE);
383629949e86Sstevel 	}
383729949e86Sstevel 
383829949e86Sstevel 	return (EHC_SUCCESS);
383929949e86Sstevel }
384029949e86Sstevel 
384129949e86Sstevel /*
384229949e86Sstevel  * Write to the PCF8574 chip.
384329949e86Sstevel  * byteaddress = chip type base address | chip offset address.
384429949e86Sstevel  */
384529949e86Sstevel static int
eHc_write_pcf8574(struct eHc_envcunit * ehcp,int byteaddress,uint8_t * buf,int size)384629949e86Sstevel eHc_write_pcf8574(struct eHc_envcunit *ehcp, int byteaddress, uint8_t *buf,
3847*eb6b10e6SToomas Soome     int size)
384829949e86Sstevel {
384929949e86Sstevel 	int i;
385029949e86Sstevel 	int status;
385129949e86Sstevel 
385229949e86Sstevel 	ASSERT((byteaddress & 0x1) == 0);
385329949e86Sstevel 	ASSERT(MUTEX_HELD(&ehcp->umutex));
385429949e86Sstevel 
385529949e86Sstevel 	/*
385629949e86Sstevel 	 * Put the bus into the start condition (write)
385729949e86Sstevel 	 */
385829949e86Sstevel 	if ((status = eHc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
385929949e86Sstevel 		if (status == EHC_NO_SLAVE_ACK) {
386029949e86Sstevel 			/*
386129949e86Sstevel 			 * Send the "stop" condition.
386229949e86Sstevel 			 */
386329949e86Sstevel 			eHc_stop_pcf8584(ehcp);
386429949e86Sstevel 		}
386529949e86Sstevel 		return (EHC_FAILURE);
386629949e86Sstevel 	}
386729949e86Sstevel 
386829949e86Sstevel 	/*
386929949e86Sstevel 	 * Send the data - poll as needed.
387029949e86Sstevel 	 */
387129949e86Sstevel 	for (i = 0; i < size; i++) {
387229949e86Sstevel 		if ((status = eHc_write_pcf8584(ehcp, buf[i])) != EHC_SUCCESS) {
387329949e86Sstevel 			if (status == EHC_NO_SLAVE_ACK)
387429949e86Sstevel 				eHc_stop_pcf8584(ehcp);
387529949e86Sstevel 			return (EHC_FAILURE);
387629949e86Sstevel 		}
387729949e86Sstevel 	}
387829949e86Sstevel 	/*
387929949e86Sstevel 	 * Transmission complete - generate stop condition and
388029949e86Sstevel 	 * put device back into slave receiver mode.
388129949e86Sstevel 	 */
388229949e86Sstevel 	eHc_stop_pcf8584(ehcp);
388329949e86Sstevel 
388429949e86Sstevel 	return (EHC_SUCCESS);
388529949e86Sstevel }
388629949e86Sstevel 
388729949e86Sstevel /*
388829949e86Sstevel  * Read from the LM75
388929949e86Sstevel  * byteaddress = chip type base address | chip offset address.
389029949e86Sstevel  */
389129949e86Sstevel static int
eHc_read_lm75(struct eHc_envcunit * ehcp,int byteaddress,uint8_t * buf,int size)389229949e86Sstevel eHc_read_lm75(struct eHc_envcunit *ehcp, int byteaddress, uint8_t *buf,
3893*eb6b10e6SToomas Soome     int size)
389429949e86Sstevel {
389529949e86Sstevel 	int i;
389629949e86Sstevel 	int status;
389729949e86Sstevel 	uint8_t discard;
389829949e86Sstevel 
389929949e86Sstevel 	ASSERT((byteaddress & 0x1) == 0);
390029949e86Sstevel 	ASSERT(MUTEX_HELD(&ehcp->umutex));
390129949e86Sstevel 
390229949e86Sstevel 	/*
390329949e86Sstevel 	 * Put the bus into the start condition
390429949e86Sstevel 	 */
390529949e86Sstevel 	if ((status = eHc_start_pcf8584(ehcp, EHC_BYTE_READ | byteaddress)) !=
390619397407SSherry Moore 	    EHC_SUCCESS) {
390729949e86Sstevel 		if (status == EHC_NO_SLAVE_ACK) {
390829949e86Sstevel 			/*
390929949e86Sstevel 			 * Send the stop condition.
391029949e86Sstevel 			 */
391129949e86Sstevel 			eHc_stop_pcf8584(ehcp);
391229949e86Sstevel 			/*
391329949e86Sstevel 			 * Read the last byte - discard it.
391429949e86Sstevel 			 */
391519397407SSherry Moore 			discard = ddi_get8(ehcp->ctlr_handle,
391619397407SSherry Moore 			    &ehcp->bus_ctl_regs->s0);
391729949e86Sstevel #ifdef lint
391829949e86Sstevel 			discard = discard;
391929949e86Sstevel #endif
392029949e86Sstevel 		}
392129949e86Sstevel 		return (EHC_FAILURE);
392229949e86Sstevel 	}
392329949e86Sstevel 
392429949e86Sstevel 	for (i = 0; i < size - 1; i++) {
392529949e86Sstevel 		if ((status = eHc_read_pcf8584(ehcp, &buf[i])) != EHC_SUCCESS) {
392629949e86Sstevel 		return (EHC_FAILURE);
392729949e86Sstevel 		}
392829949e86Sstevel 	}
392929949e86Sstevel 
393029949e86Sstevel 	/*
393129949e86Sstevel 	 * Handle the part of the bus protocol which comes
393229949e86Sstevel 	 * after a read.
393329949e86Sstevel 	 */
393429949e86Sstevel 	if (eHc_after_read_pcf8584(ehcp, &buf[i]) != EHC_SUCCESS) {
393529949e86Sstevel 		return (EHC_FAILURE);
393629949e86Sstevel 	}
393729949e86Sstevel 
393829949e86Sstevel 	return (EHC_SUCCESS);
393929949e86Sstevel }
394029949e86Sstevel 
394129949e86Sstevel /*
394229949e86Sstevel  * Write to the PCF8583 chip.
394329949e86Sstevel  * byteaddress = chip type base address | chip offset address.
394429949e86Sstevel  */
394529949e86Sstevel static int
eHc_write_pcf8583(struct eHc_envcunit * ehcp,int byteaddress,uint8_t * buf,int size)394629949e86Sstevel eHc_write_pcf8583(struct eHc_envcunit *ehcp, int byteaddress, uint8_t *buf,
3947*eb6b10e6SToomas Soome     int size)
394829949e86Sstevel {
394929949e86Sstevel 	int i;
395029949e86Sstevel 	int status;
395129949e86Sstevel 
395229949e86Sstevel 	ASSERT((byteaddress & 0x1) == 0);
395329949e86Sstevel 	ASSERT(MUTEX_HELD(&ehcp->umutex));
395429949e86Sstevel 
395529949e86Sstevel 	if ((status = eHc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
395629949e86Sstevel 		if (status == EHC_NO_SLAVE_ACK) {
395729949e86Sstevel 			/*
395829949e86Sstevel 			 * Send the "stop" condition.
395929949e86Sstevel 			 */
396029949e86Sstevel 			eHc_stop_pcf8584(ehcp);
396129949e86Sstevel 		}
396229949e86Sstevel 		return (EHC_FAILURE);
396329949e86Sstevel 	}
396429949e86Sstevel 
396529949e86Sstevel 	/*
396629949e86Sstevel 	 * Send the data - poll as needed.
396729949e86Sstevel 	 */
396829949e86Sstevel 	for (i = 0; i < size; i++) {
396929949e86Sstevel 		if ((status = eHc_write_pcf8584(ehcp, buf[i])) != EHC_SUCCESS) {
397029949e86Sstevel 			if (status == EHC_NO_SLAVE_ACK)
397129949e86Sstevel 				eHc_stop_pcf8584(ehcp);
397229949e86Sstevel 			return (EHC_FAILURE);
397329949e86Sstevel 		}
397429949e86Sstevel 	}
397529949e86Sstevel 
397629949e86Sstevel 	/*
397729949e86Sstevel 	 * Transmission complete - generate stop condition and
397829949e86Sstevel 	 * put device back into slave receiver mode.
397929949e86Sstevel 	 */
398029949e86Sstevel 	eHc_stop_pcf8584(ehcp);
398129949e86Sstevel 
398229949e86Sstevel 	return (EHC_SUCCESS);
398329949e86Sstevel }
398429949e86Sstevel 
398529949e86Sstevel /*
398629949e86Sstevel  * Read from the PCF8581 chip.
398729949e86Sstevel  * byteaddress = chip type base address | chip offset address.
398829949e86Sstevel  */
398929949e86Sstevel static int
eHc_read_pcf8591(struct eHc_envcunit * ehcp,int byteaddress,int channel,int autoinc,int amode,int aenable,uint8_t * buf,int size)399029949e86Sstevel eHc_read_pcf8591(struct eHc_envcunit *ehcp, int byteaddress, int channel,
3991*eb6b10e6SToomas Soome     int autoinc, int amode, int aenable, uint8_t *buf, int size)
399229949e86Sstevel {
399329949e86Sstevel 	int i;
399429949e86Sstevel 	int status;
399529949e86Sstevel 	uint8_t control;
399629949e86Sstevel 	uint8_t discard;
399729949e86Sstevel 
399829949e86Sstevel 	ASSERT((byteaddress & 0x1) == 0);
399929949e86Sstevel 	ASSERT(channel < 4);
400029949e86Sstevel 	ASSERT(amode < 4);
400129949e86Sstevel 	ASSERT(MUTEX_HELD(&ehcp->umutex));
400229949e86Sstevel 
400329949e86Sstevel 	/*
400429949e86Sstevel 	 * Write the control word to the PCF8591.
400529949e86Sstevel 	 * Follow the control word with a repeated START byte
400629949e86Sstevel 	 * rather than a STOP so that reads can follow without giving
400729949e86Sstevel 	 * up the bus.
400829949e86Sstevel 	 */
400929949e86Sstevel 
401029949e86Sstevel 	control = ((aenable << 6) | (amode << 4) | (autoinc << 2) | channel);
401129949e86Sstevel 
401229949e86Sstevel 	if ((status = eHc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
401329949e86Sstevel 		if (status == EHC_NO_SLAVE_ACK) {
401429949e86Sstevel 			eHc_stop_pcf8584(ehcp);
401529949e86Sstevel 		}
401629949e86Sstevel 		return (EHC_FAILURE);
401729949e86Sstevel 	}
401829949e86Sstevel 
401929949e86Sstevel 	if ((status = eHc_write_pcf8584(ehcp, control)) != EHC_SUCCESS) {
402029949e86Sstevel 		if (status == EHC_NO_SLAVE_ACK)
402129949e86Sstevel 			eHc_stop_pcf8584(ehcp);
402229949e86Sstevel 		return (EHC_FAILURE);
402329949e86Sstevel 	}
402429949e86Sstevel 
402529949e86Sstevel 	/*
402629949e86Sstevel 	 * The following two operations, 0x45 to S1, and the byteaddress
402729949e86Sstevel 	 * to S0, will result in a repeated START being sent out on the bus.
402829949e86Sstevel 	 * Refer to Fig.8 of Philips Semiconductors PCF8584 product spec.
402929949e86Sstevel 	 */
403029949e86Sstevel 
403129949e86Sstevel 	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1,
403219397407SSherry Moore 	    EHC_S1_ES0 | EHC_S1_STA | EHC_S1_ACK);
403329949e86Sstevel 
403429949e86Sstevel 	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0,
403519397407SSherry Moore 	    EHC_BYTE_READ | byteaddress);
403629949e86Sstevel 
403729949e86Sstevel 	i = 0;
403829949e86Sstevel 
403929949e86Sstevel 	do {
404029949e86Sstevel 		drv_usecwait(1000);
404129949e86Sstevel 		status =
404219397407SSherry Moore 		    ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
404329949e86Sstevel 		i++;
404429949e86Sstevel 	} while ((status & EHC_S1_PIN) && i < EHC_MAX_WAIT);
404529949e86Sstevel 
404629949e86Sstevel 	if (i == EHC_MAX_WAIT) {
404729949e86Sstevel 		DCMNERR(CE_WARN, "eHc_read_pcf8591(): read of S1 failed");
404829949e86Sstevel 		return (EHC_FAILURE);
404929949e86Sstevel 	}
405029949e86Sstevel 
405129949e86Sstevel 	if (status & EHC_S1_LRB) {
405229949e86Sstevel 		DCMNERR(CE_WARN, "eHc_read_pcf8591(): No slave ACK");
405329949e86Sstevel 		/*
405429949e86Sstevel 		 * Send the stop condition.
405529949e86Sstevel 		 */
405629949e86Sstevel 		eHc_stop_pcf8584(ehcp);
405729949e86Sstevel 		/*
405829949e86Sstevel 		 * Read the last byte - discard it.
405929949e86Sstevel 		 */
406029949e86Sstevel 		discard = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
406129949e86Sstevel #ifdef lint
406229949e86Sstevel 		discard = discard;
406329949e86Sstevel #endif
406429949e86Sstevel 		return (EHC_FAILURE);
406529949e86Sstevel 	}
406629949e86Sstevel 
406729949e86Sstevel 	if (status & EHC_S1_BER) {
406829949e86Sstevel 		DCMN2ERR(CE_WARN, "eHc_read_pcf8591(): Bus error");
406929949e86Sstevel 		return (EHC_FAILURE);
407029949e86Sstevel 	}
407129949e86Sstevel 
407229949e86Sstevel 	if (status & EHC_S1_LAB) {
407329949e86Sstevel 		DCMN2ERR(CE_WARN, "eHc_read_pcf8591(): Lost Arbitration");
407429949e86Sstevel 		return (EHC_FAILURE);
407529949e86Sstevel 	}
407629949e86Sstevel 
407729949e86Sstevel 	/*
407829949e86Sstevel 	 * Discard first read as per PCF8584 master receiver protocol.
407929949e86Sstevel 	 * This is normally done in the eHc_start_pcf8584() routine.
408029949e86Sstevel 	 */
408129949e86Sstevel 	if ((status = eHc_read_pcf8584(ehcp, &discard)) != EHC_SUCCESS) {
408229949e86Sstevel 		return (EHC_FAILURE);
408329949e86Sstevel 	}
408429949e86Sstevel 
408529949e86Sstevel 	/* Discard second read as per PCF8591 protocol */
408629949e86Sstevel 	if ((status = eHc_read_pcf8584(ehcp, &discard)) != EHC_SUCCESS) {
408729949e86Sstevel 		return (EHC_FAILURE);
408829949e86Sstevel 	}
408929949e86Sstevel 
409029949e86Sstevel 	for (i = 0; i < size - 1; i++) {
409129949e86Sstevel 		if ((status = eHc_read_pcf8584(ehcp, &buf[i])) != EHC_SUCCESS) {
409229949e86Sstevel 			return (EHC_FAILURE);
409329949e86Sstevel 		}
409429949e86Sstevel 	}
409529949e86Sstevel 
409629949e86Sstevel 	if (eHc_after_read_pcf8584(ehcp, &buf[i]) != EHC_SUCCESS) {
409729949e86Sstevel 		return (EHC_FAILURE);
409829949e86Sstevel 	}
409929949e86Sstevel 
410029949e86Sstevel 	return (EHC_SUCCESS);
410129949e86Sstevel }
4102