xref: /onnv-gate/usr/src/uts/sun4u/tazmo/io/envctrl.c (revision 7656:2621e50fdf4a)
11341Sstevel /*
21341Sstevel  * CDDL HEADER START
31341Sstevel  *
41341Sstevel  * The contents of this file are subject to the terms of the
51341Sstevel  * Common Development and Distribution License (the "License").
61341Sstevel  * You may not use this file except in compliance with the License.
71341Sstevel  *
81341Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91341Sstevel  * or http://www.opensolaris.org/os/licensing.
101341Sstevel  * See the License for the specific language governing permissions
111341Sstevel  * and limitations under the License.
121341Sstevel  *
131341Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
141341Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151341Sstevel  * If applicable, add the following below this CDDL HEADER, with the
161341Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
171341Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
181341Sstevel  *
191341Sstevel  * CDDL HEADER END
201341Sstevel  */
211341Sstevel 
221341Sstevel /*
23*7656SSherry.Moore@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
241341Sstevel  * Use is subject to license terms.
251341Sstevel  */
261341Sstevel 
271341Sstevel 
281341Sstevel /*
291341Sstevel  * ENVCTRL_ Environment Monitoring driver for i2c
301341Sstevel  *
311341Sstevel  */
321341Sstevel #include <sys/param.h>
331341Sstevel #include <sys/types.h>
341341Sstevel #include <sys/signal.h>
351341Sstevel #include <sys/errno.h>
361341Sstevel #include <sys/file.h>
371341Sstevel #include <sys/termio.h>
381341Sstevel #include <sys/termios.h>
391341Sstevel #include <sys/cmn_err.h>
401341Sstevel #include <sys/stream.h>
411341Sstevel #include <sys/strsun.h>
421341Sstevel #include <sys/stropts.h>
431341Sstevel #include <sys/strtty.h>
441341Sstevel #include <sys/debug.h>
451341Sstevel #include <sys/eucioctl.h>
461341Sstevel #include <sys/cred.h>
471341Sstevel #include <sys/uio.h>
481341Sstevel #include <sys/stat.h>
491341Sstevel #include <sys/kmem.h>
501341Sstevel 
511341Sstevel #include <sys/ddi.h>
521341Sstevel #include <sys/sunddi.h>
531341Sstevel #include <sys/obpdefs.h>
541341Sstevel #include <sys/conf.h>		/* req. by dev_ops flags MTSAFE etc. */
551341Sstevel #include <sys/modctl.h>		/* for modldrv */
561341Sstevel #include <sys/stat.h>		/* ddi_create_minor_node S_IFCHR */
571341Sstevel #include <sys/open.h>		/* for open params.	 */
581341Sstevel #include <sys/uio.h>		/* for read/write */
591341Sstevel #include <sys/envctrl.h>	/* Environment header */
601341Sstevel 
611341Sstevel /* driver entry point fn definitions */
621341Sstevel static int 	envctrl_open(queue_t *, dev_t *, int, int, cred_t *);
631341Sstevel static int	envctrl_close(queue_t *, int, cred_t *);
641341Sstevel static uint_t 	envctrl_bus_isr(caddr_t);
651341Sstevel static uint_t 	envctrl_dev_isr(caddr_t);
661341Sstevel 
671341Sstevel /* configuration entry point fn definitions */
681341Sstevel static int 	envctrl_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
691341Sstevel static int	envctrl_attach(dev_info_t *, ddi_attach_cmd_t);
701341Sstevel static int	envctrl_detach(dev_info_t *, ddi_detach_cmd_t);
711341Sstevel 
721341Sstevel /* Driver private routines */
731341Sstevel static void	envctrl_init_bus(struct envctrlunit *);
741341Sstevel static int	envctrl_xmit(struct envctrlunit *, caddr_t *, int);
751341Sstevel static void	envctrl_recv(struct envctrlunit *, caddr_t *, int);
761341Sstevel static void	envctrl_get_sys_temperatures(struct envctrlunit *, uint8_t *);
771341Sstevel static int	envctrl_get_lm75_temp(struct envctrlunit *);
781341Sstevel static int	envctrl_get_ps_temp(struct envctrlunit *, uint8_t);
791341Sstevel static int	envctrl_get_cpu_temp(struct envctrlunit *, int);
801341Sstevel static void	envctrl_fan_fail_service(struct envctrlunit *);
811341Sstevel static void	envctrl_PS_intr_service(struct envctrlunit *, uint8_t);
821341Sstevel static void	envctrl_ps_probe(struct envctrlunit *);
831341Sstevel static void	envctrl_tempr_poll(void *);
841341Sstevel static void	envctrl_pshotplug_poll(void *);
851341Sstevel static void	envctrl_led_blink(void *);
861341Sstevel static void	envctrl_reset_dflop(struct envctrlunit *);
871341Sstevel static void	envctrl_enable_devintrs(struct envctrlunit *);
881341Sstevel static void	envctrl_stop_clock(struct envctrlunit *);
891341Sstevel static void	envctrl_reset_watchdog(struct envctrlunit *, uint8_t *);
901341Sstevel static void	envctrl_abort_seq_handler(char *msg);
911341Sstevel static uint8_t	envctrl_get_fpm_status(struct envctrlunit *);
921341Sstevel static void	envctrl_set_fsp(struct envctrlunit *, uint8_t *);
931341Sstevel static int	envctrl_set_dskled(struct envctrlunit *,
941341Sstevel 				struct envctrl_pcf8574_chip *);
951341Sstevel static int	envctrl_get_dskled(struct envctrlunit *,
961341Sstevel 				struct envctrl_pcf8574_chip *);
971341Sstevel static void	envctrl_probe_cpus(struct envctrlunit *);
981341Sstevel static int	envctrl_match_cpu(dev_info_t *, void *);
991341Sstevel static int	envctrl_isother_fault_led(struct envctrlunit *,
1001341Sstevel 		    uint8_t, uint8_t);
1011341Sstevel 
1021341Sstevel /* Kstat routines */
1031341Sstevel static void	envctrl_add_kstats(struct envctrlunit *);
1041341Sstevel static int	envctrl_ps_kstat_update(kstat_t *, int);
1051341Sstevel static int	envctrl_fanstat_kstat_update(kstat_t *, int);
1061341Sstevel static int	envctrl_encl_kstat_update(kstat_t *, int);
1071341Sstevel static void	envctrl_init_fan_kstats(struct envctrlunit *);
1081341Sstevel static void	envctrl_init_encl_kstats(struct envctrlunit *);
1091341Sstevel static void	envctrl_add_encl_kstats(struct envctrlunit *, int, int,
1101341Sstevel 			uint8_t);
1111341Sstevel static void	envctrl_mod_encl_kstats(struct envctrlunit *, int, int,
1121341Sstevel 			uint8_t);
1131341Sstevel 
1141341Sstevel 
1151341Sstevel /* Streams Routines */
1161341Sstevel static int	envctrl_wput(queue_t *, mblk_t *);
1171341Sstevel 
1181341Sstevel /* External routines */
1191341Sstevel extern void power_down(const char *);
1201341Sstevel extern int prom_getprop();
1211341Sstevel extern int prom_getproplen();
1221341Sstevel extern	void	prom_printf(const char *fmt, ...);
1231341Sstevel extern void (*abort_seq_handler)();
1241341Sstevel 
1251341Sstevel static void    *envctrlsoft_statep;
1261341Sstevel 
1271341Sstevel /* Local Variables */
1281341Sstevel /* Indicates whether or not the overtemp thread has been started */
1291341Sstevel static int	envctrl_debug_flags = 0;
1301341Sstevel static int	envctrl_afb_present = 0;
1311341Sstevel static int	envctrl_power_off_overide = 0;
1321341Sstevel static int	envctrl_max_retries = 100;
1331341Sstevel static int	envctrl_allow_detach = 0;
1341341Sstevel static int	envctrl_numcpus = 1;
1351341Sstevel static int	envctrl_p0_enclosure = 0; /* set to 1 if it is a P0 */
1361341Sstevel static int envctrl_handler = 1; /* 1 is the default */
1371341Sstevel static clock_t overtemp_timeout_hz;
1381341Sstevel static clock_t blink_timeout_hz;
1391341Sstevel static clock_t pshotplug_timeout_hz;
1401341Sstevel static int controller_present[] = {-1, -1, -1};
1411341Sstevel #ifdef MULTIFAN
1421341Sstevel static int	envctrl_fan_debug = 0;
1431341Sstevel #endif
1441341Sstevel static int 	eHc_debug = 0;
1451341Sstevel static int	power_supply_previous_state[] = {-1, -1, -1};
1461341Sstevel 
1471341Sstevel extern void	pci_thermal_rem_intr(dev_info_t *, uint_t);
1481341Sstevel 
1491341Sstevel #define	LOOP_TIMEOUT 25
1501341Sstevel #define	INIT_FAN_VAL 35
1511341Sstevel #define	DCMNERR if (eHc_debug & 0x1) cmn_err
1521341Sstevel #define	DCMN2ERR if (eHc_debug & 0x2) cmn_err
1531341Sstevel #define	MAX_FAN_FAIL_RETRY 3
1541341Sstevel 
1551341Sstevel uint8_t backaddrs[] = {ENVCTRL_PCF8574_DEV0, ENVCTRL_PCF8574_DEV1,
1561341Sstevel     ENVCTRL_PCF8574_DEV2};
1571341Sstevel 
1581341Sstevel struct module_info envctrlinfo = {
1591341Sstevel 	/* id, name, min pkt siz, max pkt siz, hi water, low water */
1601341Sstevel 	42, "envctrl", 0, 2048, (1024 * 20), (1024 * 1)
1611341Sstevel };
1621341Sstevel 
1631341Sstevel static struct qinit envctrl_rinit = {
1641341Sstevel 	putq, NULL, envctrl_open, envctrl_close, NULL, &envctrlinfo, NULL
1651341Sstevel };
1661341Sstevel 
1671341Sstevel static struct qinit envctrl_wint = {
1681341Sstevel 	envctrl_wput, NULL, envctrl_open, envctrl_close,
1691341Sstevel 	    NULL, &envctrlinfo, NULL
1701341Sstevel };
1711341Sstevel 
1721341Sstevel struct streamtab envctrl_str_info = {
1731341Sstevel 	&envctrl_rinit, &envctrl_wint, NULL, NULL
1741341Sstevel };
1751341Sstevel 
1761341Sstevel static struct cb_ops envctrl_cb_ops = {
1771341Sstevel 	nodev,			/* cb_open */
1781341Sstevel 	nodev,			/* cb_close */
1791341Sstevel 	nodev,			/* cb_strategy */
1801341Sstevel 	nodev,			/* cb_print */
1811341Sstevel 	nodev,			/* cb_dump */
1821341Sstevel 	nodev,			/* cb_read */
1831341Sstevel 	nodev,			/* cb_write */
1841341Sstevel 	nodev,			/* cb_ioctl */
1851341Sstevel 	nodev,			/* cb_devmap */
1861341Sstevel 	nodev,			/* cb_mmap */
1871341Sstevel 	nodev,			/* cb_segmap */
1881341Sstevel 	nochpoll,		/* cb_chpoll */
1891341Sstevel 	ddi_prop_op,		/* cb_prop_op */
1901341Sstevel 	&envctrl_str_info,	/* cb_stream */
1911341Sstevel 	D_MP			/* cb_flag */
1921341Sstevel };
1931341Sstevel 
1941341Sstevel /*
1951341Sstevel  * Declare ops vectors for auto configuration.
1961341Sstevel  */
1971341Sstevel struct dev_ops  envctrl_ops = {
1981341Sstevel 	DEVO_REV,		/* devo_rev */
1991341Sstevel 	0,			/* devo_refcnt */
2001341Sstevel 	envctrl_getinfo,	/* devo_getinfo */
2011341Sstevel 	nulldev,		/* devo_identify */
2021341Sstevel 	nulldev,		/* devo_probe */
2031341Sstevel 	envctrl_attach,		/* devo_attach */
2041341Sstevel 	envctrl_detach,		/* devo_detach */
2051341Sstevel 	nodev,			/* devo_reset */
2061341Sstevel 	&envctrl_cb_ops,	/* devo_cb_ops */
2071341Sstevel 	(struct bus_ops *)NULL,	/* devo_bus_ops */
208*7656SSherry.Moore@Sun.COM 	nulldev,		/* devo_power */
209*7656SSherry.Moore@Sun.COM 	ddi_quiesce_not_supported,	/* devo_quiesce */
2101341Sstevel };
2111341Sstevel 
2121341Sstevel extern struct mod_ops mod_driverops;
2131341Sstevel 
2141341Sstevel static struct modldrv envctrlmodldrv = {
2151341Sstevel 	&mod_driverops,		/* type of module - driver */
216*7656SSherry.Moore@Sun.COM 	"I2C ENVCTRL_driver",
2171341Sstevel 	&envctrl_ops,
2181341Sstevel };
2191341Sstevel 
2201341Sstevel static struct modlinkage envctrlmodlinkage = {
2211341Sstevel 	MODREV_1,
2221341Sstevel 	&envctrlmodldrv,
2231341Sstevel 	0
2241341Sstevel };
2251341Sstevel 
2261341Sstevel /*
2271341Sstevel  * The following defines are for the i2c protocol routines.
2281341Sstevel  * This section of defines should be removed once the envctrl_targets.c
2291341Sstevel  * file is included.
2301341Sstevel  */
2311341Sstevel 
2321341Sstevel #define	EHC_SUCCESS 0
2331341Sstevel #define	EHC_FAILURE (-1)
2341341Sstevel #define	EHC_NO_SLAVE_ACK 3
2351341Sstevel 
2361341Sstevel #define	EHC_MAX_WAIT 7 /* decimal */
2371341Sstevel 
2381341Sstevel #define	EHC_S1_PIN 0x80
2391341Sstevel #define	EHC_S1_ES1 0x20
2401341Sstevel #define	EHC_S1_ES0 0x40
2411341Sstevel #define	EHC_S1_NBB 0x01
2421341Sstevel #define	EHC_S1_ACK 0x01
2431341Sstevel #define	EHC_S1_STA 0x04
2441341Sstevel #define	EHC_S1_STO 0x02
2451341Sstevel #define	EHC_S1_LRB 0x08
2461341Sstevel #define	EHC_S1_BER 0x10
2471341Sstevel #define	EHC_S1_LAB 0x02
2481341Sstevel 
2491341Sstevel #define	EHC_S0_OWN 0x55
2501341Sstevel #define	EHC_S0_CLK 0x1c
2511341Sstevel 
2521341Sstevel #define	EHC_BYTE_READ 0x01
2531341Sstevel 
2541341Sstevel #define	EHC_LONGEST_MSG 1000 /* decimal */
2551341Sstevel 
2561341Sstevel /*
2571341Sstevel  * PCF8591 Chip Used for temperature sensors
2581341Sstevel  *
2591341Sstevel  * Addressing Register definition.
2601341Sstevel  * A0-A2 valid range is 0-7
2611341Sstevel  *
2621341Sstevel  *  7    6  5   4    3     2     1      0
2631341Sstevel  * ------------------------------------------------
2641341Sstevel  * | 1 | 0 | 0 | 1 | A2 | A1 | A0 | R/W |
2651341Sstevel  * ------------------------------------------------
2661341Sstevel  */
2671341Sstevel 
2681341Sstevel 
2691341Sstevel #define	EHC_PCF8591_MAX_DEVS	0x08
2701341Sstevel 
2711341Sstevel #define	EHC_DEV0	0x00
2721341Sstevel #define	EHC_DEV1	0x02
2731341Sstevel #define	EHC_DEV2	0x04
2741341Sstevel #define	EHC_DEV3	0x06
2751341Sstevel #define	EHC_DEV4	0x08
2761341Sstevel #define	EHC_DEV5	0x0A
2771341Sstevel #define	EHC_DEV6    	0x0C
2781341Sstevel #define	EHC_DEV7	0x0E
2791341Sstevel 
2801341Sstevel 
2811341Sstevel /*
2821341Sstevel  * 		CONTROL OF CHIP
2831341Sstevel  * PCF8591 Temp sensing control register definitions
2841341Sstevel  *
2851341Sstevel  *   7      6     5   4  3   2      1   0
2861341Sstevel  * ---------------------------------------------
2871341Sstevel  * | 0 | AOE | X | X | 0 | AIF | X | X |
2881341Sstevel  * ---------------------------------------------
2891341Sstevel  * AOE = Analog out enable.. not used on out implementation
2901341Sstevel  * 5 & 4 = Analog Input Programming.. see data sheet for bits..
2911341Sstevel  *
2921341Sstevel  * AIF = Auto increment flag
2931341Sstevel  * bits 1 & 0 are for the Chennel number.
2941341Sstevel  */
2951341Sstevel 
2961341Sstevel #define	EHC_PCF8591_ANALOG_OUTPUT_EN	0x40
2971341Sstevel #define	EHC_PCF8591_ANALOG_INPUT_EN	0x00
2981341Sstevel #define	EHC_PCF8591_READ_BIT		0x01
2991341Sstevel 
3001341Sstevel 
3011341Sstevel #define	EHC_PCF8591_AUTO_INCR 0x04
3021341Sstevel #define	EHC_PCF8591_OSCILATOR 0x40
3031341Sstevel 
3041341Sstevel #define	EHC_PCF8591_MAX_PORTS	0x04
3051341Sstevel 
3061341Sstevel #define	EHC_PCF8591_CH_0	0x00
3071341Sstevel #define	EHC_PCF8591_CH_1	0x01
3081341Sstevel #define	EHC_PCF8591_CH_2	0x02
3091341Sstevel #define	EHC_PCF8591_CH_3	0x03
3101341Sstevel 
3111341Sstevel 
3121341Sstevel /*
3131341Sstevel  * PCF8574 Fan Fail, Power Supply Fail Detector
3141341Sstevel  * This device is driven by interrupts. Each time it interrupts
3151341Sstevel  * you must look at the CSR to see which ports caused the interrupt
3161341Sstevel  * they are indicated by a 1.
3171341Sstevel  *
3181341Sstevel  * Address map of this chip
3191341Sstevel  *
3201341Sstevel  * -------------------------------------------
3211341Sstevel  * | 0 | 1 | 1 | 1 | A2 | A1 | A0 | 0 |
3221341Sstevel  * -------------------------------------------
3231341Sstevel  *
3241341Sstevel  */
3251341Sstevel 
3261341Sstevel #define	EHC_PCF8574_PORT0	0x01
3271341Sstevel #define	EHC_PCF8574_PORT1	0x02
3281341Sstevel #define	EHC_PCF8574_PORT2	0x04
3291341Sstevel #define	EHC_PCF8574_PORT3	0x08
3301341Sstevel #define	EHC_PCF8574_PORT4	0x10
3311341Sstevel #define	EHC_PCF8574_PORT5	0x20
3321341Sstevel #define	EHC_PCF8574_PORT6	0x40
3331341Sstevel #define	EHC_PCF8574_PORT7	0x80
3341341Sstevel 
3351341Sstevel /*
3361341Sstevel  * Defines for the PCF8583 Clock Calendar Chip.
3371341Sstevel  */
3381341Sstevel #define	EHC_PCF8583_READ_BIT	0x01
3391341Sstevel #define	ALARM_CTR_REG_MINS	0x03
3401341Sstevel #define	ALARM_REG_MINS		0x0B
3411341Sstevel #define	ALARM_TIMER_REG		0x0F
3421341Sstevel 
3431341Sstevel struct eHc_pcd8584_regs {
3441341Sstevel 	uint8_t s0;		/* Own Address S0' */
3451341Sstevel 	uint8_t s1;		/* Control Status register */
3461341Sstevel 	uint8_t clock_s2;	/* Clock programming register */
3471341Sstevel };
3481341Sstevel 
3491341Sstevel struct eHc_envcunit {
3501341Sstevel 	struct eHc_pcd8584_regs *bus_ctl_regs;
3511341Sstevel 	ddi_acc_handle_t ctlr_handle;
3521341Sstevel 	kmutex_t umutex;
3531341Sstevel };
3541341Sstevel 
3551341Sstevel 
3561341Sstevel /*
3571341Sstevel  * Prototypes for static routines
3581341Sstevel  */
3591341Sstevel 
3601341Sstevel static int eHc_write_tda8444(struct eHc_envcunit *, int, int, int, uint8_t *,
3611341Sstevel 	int);
3621341Sstevel static int eHc_read_pcf8591(struct eHc_envcunit *, int, int, int, int, int,
3631341Sstevel 	uint8_t *, int);
3641341Sstevel static int eHc_read_pcf8574a(struct eHc_envcunit *, int, uint8_t *, int);
3651341Sstevel static int eHc_write_pcf8574a(struct eHc_envcunit *, int, uint8_t *, int);
3661341Sstevel static int eHc_read_pcf8574(struct eHc_envcunit *, int, uint8_t *, int);
3671341Sstevel static int eHc_write_pcf8574(struct eHc_envcunit *, int, uint8_t *, int);
3681341Sstevel static int eHc_read_lm75(struct eHc_envcunit *, int, uint8_t *, int);
3691341Sstevel static int eHc_write_pcf8583(struct eHc_envcunit *, int, uint8_t *, int);
3701341Sstevel 
3711341Sstevel static int eHc_start_pcf8584(struct eHc_envcunit *, uint8_t);
3721341Sstevel static void eHc_stop_pcf8584(struct eHc_envcunit *);
3731341Sstevel static int eHc_read_pcf8584(struct eHc_envcunit *, uint8_t *);
3741341Sstevel static int eHc_write_pcf8584(struct eHc_envcunit *, uint8_t);
3751341Sstevel static int eHc_after_read_pcf8584(struct eHc_envcunit *, uint8_t *);
3761341Sstevel 
3771341Sstevel /*
3781341Sstevel  * End of i2c protocol definitions section
3791341Sstevel  */
3801341Sstevel 
3811341Sstevel int
_init(void)3821341Sstevel _init(void)
3831341Sstevel {
3841341Sstevel 	int    error;
3851341Sstevel 
3861341Sstevel 	if ((error = mod_install(&envctrlmodlinkage)) == 0) {
3871341Sstevel 		(void) ddi_soft_state_init(&envctrlsoft_statep,
388*7656SSherry.Moore@Sun.COM 		    sizeof (struct envctrlunit), 1);
3891341Sstevel 	}
3901341Sstevel 
3911341Sstevel 	return (error);
3921341Sstevel }
3931341Sstevel 
3941341Sstevel int
_fini(void)3951341Sstevel _fini(void)
3961341Sstevel {
3971341Sstevel 	int    error;
3981341Sstevel 
3991341Sstevel 	if ((error = mod_remove(&envctrlmodlinkage)) == 0)
4001341Sstevel 		ddi_soft_state_fini(&envctrlsoft_statep);
4011341Sstevel 
4021341Sstevel 	return (error);
4031341Sstevel }
4041341Sstevel 
4051341Sstevel int
_info(struct modinfo * modinfop)4061341Sstevel _info(struct modinfo *modinfop)
4071341Sstevel {
4081341Sstevel 	return (mod_info(&envctrlmodlinkage, modinfop));
4091341Sstevel }
4101341Sstevel 
4111341Sstevel static int
envctrl_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)4121341Sstevel envctrl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4131341Sstevel {
4141341Sstevel 	int	instance;
4151341Sstevel 	char		name[16];
4161341Sstevel 	uint8_t fspval;
4171341Sstevel 	struct	envctrlunit *unitp;
4181341Sstevel 	struct ddi_device_acc_attr attr;
4191341Sstevel 	int *reg_prop;
4201341Sstevel 	uchar_t *creg_prop;
4211341Sstevel 	uint_t len, tblsz;
4221341Sstevel 	int i, cputemp, status;
4231341Sstevel 	uint8_t buf[3];
4241341Sstevel 
4251341Sstevel 	status = len = tblsz = 0;
4261341Sstevel 
4271341Sstevel 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
4281341Sstevel 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
4291341Sstevel 
4301341Sstevel 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
4311341Sstevel 
4321341Sstevel 	instance = ddi_get_instance(dip);
4331341Sstevel 
4341341Sstevel 	switch (cmd) {
4351341Sstevel 	case DDI_ATTACH:
4361341Sstevel 		break;
4371341Sstevel 	case DDI_RESUME:
4381341Sstevel 		if (!(unitp = ddi_get_soft_state(envctrlsoft_statep, instance)))
4391341Sstevel 			return (DDI_FAILURE);
4401341Sstevel 		mutex_enter(&unitp->umutex);
4411341Sstevel 		if (!unitp->suspended) {
4421341Sstevel 			mutex_exit(&unitp->umutex);
4431341Sstevel 			return (DDI_FAILURE);
4441341Sstevel 		}
4451341Sstevel 		unitp->suspended = 0;
4461341Sstevel 		mutex_exit(&unitp->umutex);
4471341Sstevel 		unitp->initting = B_TRUE;
4481341Sstevel 		envctrl_init_bus(unitp);
4491341Sstevel 		unitp->initting = B_FALSE;
4501341Sstevel 
4511341Sstevel 		mutex_enter(&unitp->umutex);
4521341Sstevel 		envctrl_ps_probe(unitp);
4531341Sstevel 		envctrl_probe_cpus(unitp);
4541341Sstevel 		mutex_exit(&unitp->umutex);
4551341Sstevel 
4561341Sstevel 		return (DDI_SUCCESS);
4571341Sstevel 
4581341Sstevel 	default:
4591341Sstevel 		return (DDI_FAILURE);
4601341Sstevel 	}
4611341Sstevel 
4621341Sstevel 	/* Set up timer values */
4631341Sstevel 	overtemp_timeout_hz = drv_usectohz(OVERTEMP_TIMEOUT_USEC);
4641341Sstevel 	blink_timeout_hz = drv_usectohz(BLINK_TIMEOUT_USEC);
4651341Sstevel 	pshotplug_timeout_hz = drv_usectohz(BLINK_TIMEOUT_USEC * 6);
4661341Sstevel 
4671341Sstevel 	if (ddi_soft_state_zalloc(envctrlsoft_statep, instance) != 0) {
4681341Sstevel 		cmn_err(CE_WARN, "envctrl failed to zalloc softstate\n");
4691341Sstevel 		goto failed;
4701341Sstevel 	}
4711341Sstevel 
4721341Sstevel 	unitp = ddi_get_soft_state(envctrlsoft_statep, instance);
4731341Sstevel 
4741341Sstevel 	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&unitp->bus_ctl_regs, 0,
475*7656SSherry.Moore@Sun.COM 	    sizeof (struct envctrl_pcd8584_regs), &attr,
476*7656SSherry.Moore@Sun.COM 	    &unitp->ctlr_handle) != DDI_SUCCESS) {
4771341Sstevel 		cmn_err(CE_WARN, "I2c failed to map in bus_control regs\n");
4781341Sstevel 		return (DDI_FAILURE);
4791341Sstevel 	}
4801341Sstevel 
4811341Sstevel 	/*
4821341Sstevel 	 * If the PCI nexus has added a thermal interrupt, we first need
4831341Sstevel 	 * to remove that interrupt handler.
4841341Sstevel 	 *
4851341Sstevel 	 * WARNING: Removing another driver's interrupt handler is not
4861341Sstevel 	 * allowed. The pci_thermal_rem_intr() call below is needed to retain
4871341Sstevel 	 * the legacy behavior on Tazmo systems.
4881341Sstevel 	 */
4891341Sstevel 
4901341Sstevel 	pci_thermal_rem_intr(dip, (uint_t)0);
4911341Sstevel 
4921341Sstevel 	/* add interrupts */
4931341Sstevel 
4941341Sstevel 	if (ddi_get_iblock_cookie(dip, 1,
495*7656SSherry.Moore@Sun.COM 	    &unitp->ic_trap_cookie) != DDI_SUCCESS)  {
4961341Sstevel 		cmn_err(CE_WARN, "ddi_get_iblock_cookie FAILED \n");
4971341Sstevel 		goto failed;
4981341Sstevel 	}
4991341Sstevel 
5001341Sstevel 	mutex_init(&unitp->umutex, NULL, MUTEX_DRIVER,
501*7656SSherry.Moore@Sun.COM 	    (void *)unitp->ic_trap_cookie);
5021341Sstevel 
5031341Sstevel 
5041341Sstevel 	if (ddi_add_intr(dip, 0, &unitp->ic_trap_cookie, NULL, envctrl_bus_isr,
505*7656SSherry.Moore@Sun.COM 	    (caddr_t)unitp) != DDI_SUCCESS) {
5061341Sstevel 		cmn_err(CE_WARN, "envctrl_attach failed to add hard intr %d\n",
507*7656SSherry.Moore@Sun.COM 		    instance);
5081341Sstevel 		goto remlock;
5091341Sstevel 	}
5101341Sstevel 
5111341Sstevel 
5121341Sstevel 	if (ddi_add_intr(dip, 1, &unitp->ic_trap_cookie, NULL, envctrl_dev_isr,
513*7656SSherry.Moore@Sun.COM 	    (caddr_t)unitp) != DDI_SUCCESS) {
5141341Sstevel 		cmn_err(CE_WARN, "envctrl_attach failed to add hard intr %d\n",
515*7656SSherry.Moore@Sun.COM 		    instance);
5161341Sstevel 		goto remhardintr;
5171341Sstevel 	}
5181341Sstevel 
5191341Sstevel 
5201341Sstevel 	(void) sprintf(name, "envctrl%d", instance);
5211341Sstevel 
5221341Sstevel 	if (ddi_create_minor_node(dip, name, S_IFCHR, instance, DDI_PSEUDO,
523*7656SSherry.Moore@Sun.COM 	    NULL) == DDI_FAILURE) {
5241341Sstevel 		ddi_remove_minor_node(dip, NULL);
5251341Sstevel 		goto remhardintr1;
5261341Sstevel 	}
5271341Sstevel 
5281341Sstevel 	mutex_enter(&unitp->umutex);
5291341Sstevel 	switch (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
5301341Sstevel 	    ENVCTRL_LED_BLINK, -1)) {
5311341Sstevel 	case 1:
5321341Sstevel 		unitp->activity_led_blink = B_TRUE;
5331341Sstevel 		break;
5341341Sstevel 	case 0:
5351341Sstevel 	default:
5361341Sstevel 		unitp->activity_led_blink = B_FALSE;
5371341Sstevel 		break;
5381341Sstevel 	}
5391341Sstevel 	unitp->shutdown = B_FALSE;
5401341Sstevel 	unitp->num_ps_present = unitp->num_encl_present = 0;
5411341Sstevel 	unitp->num_fans_present = MIN_FAN_BANKS;
5421341Sstevel 	unitp->num_fans_failed = ENVCTRL_CHAR_ZERO;
5431341Sstevel 	unitp->AFB_present = B_TRUE;
5441341Sstevel 	unitp->dip = dip;
5451341Sstevel 
5461341Sstevel #ifdef	DEBUG
5471341Sstevel 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
548*7656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, ENVCTRL_PANEL_LEDS_PR,
549*7656SSherry.Moore@Sun.COM 	    &reg_prop, &len) == DDI_PROP_SUCCESS)
5501341Sstevel 		ddi_prop_free((void *)reg_prop);
5511341Sstevel 	ASSERT(len != 0);
5521341Sstevel 
5531341Sstevel 	len = 0;
5541341Sstevel 
5551341Sstevel 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
556*7656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, ENVCTRL_PANEL_LEDS_STA,
557*7656SSherry.Moore@Sun.COM 	    &reg_prop, &len) == DDI_PROP_SUCCESS)
5581341Sstevel 		ddi_prop_free((void *)reg_prop);
5591341Sstevel 	ASSERT(len != 0);
5601341Sstevel 
5611341Sstevel 	len = 0;
5621341Sstevel 
5631341Sstevel 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
564*7656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, ENVCTRL_DISK_LEDS_STA,
565*7656SSherry.Moore@Sun.COM 	    &reg_prop, &len) == DDI_PROP_SUCCESS)
5661341Sstevel 		ddi_prop_free((void *)reg_prop);
5671341Sstevel 	ASSERT(len != 0);
5681341Sstevel #endif	/* DEBUG */
5691341Sstevel 
5701341Sstevel 	/*
5711341Sstevel 	 * if we have prom fan tables, overide the static tables in
5721341Sstevel 	 * header file.
5731341Sstevel 	 */
5741341Sstevel 
5751341Sstevel 	if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
576*7656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, "cpu-fan-speeds",
577*7656SSherry.Moore@Sun.COM 	    &creg_prop, &len) == DDI_PROP_SUCCESS) {
5781341Sstevel 
5791341Sstevel 		tblsz = (sizeof (acme_cpu_fanspd) / sizeof (short));
5801341Sstevel 
5811341Sstevel 		if (len <= tblsz) {
5821341Sstevel 			for (i = 0; i < len; i++) {
5831341Sstevel 				acme_cpu_fanspd[i] = creg_prop[i];
5841341Sstevel 			}
5851341Sstevel 		}
5861341Sstevel 		ddi_prop_free((void *)creg_prop);
5871341Sstevel 	}
5881341Sstevel 
5891341Sstevel 	len = 0;
5901341Sstevel 
5911341Sstevel 	if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
592*7656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, "ps-fan-speeds",
593*7656SSherry.Moore@Sun.COM 	    &creg_prop, &len) == DDI_PROP_SUCCESS) {
5941341Sstevel 
5951341Sstevel 		tblsz = (sizeof (acme_ps_fanspd) / sizeof (short));
5961341Sstevel 
5971341Sstevel 		if (len <= tblsz) {
5981341Sstevel 			for (i = 0; i < len; i++) {
5991341Sstevel 				acme_ps_fanspd[i] = creg_prop[i];
6001341Sstevel 			}
6011341Sstevel 		}
6021341Sstevel 		ddi_prop_free((void *)creg_prop);
6031341Sstevel 	}
6041341Sstevel 
6051341Sstevel 	switch (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
6061341Sstevel 	    "fan-override", -1)) {
6071341Sstevel 	case 1:
6081341Sstevel 	case 2:
6091341Sstevel 		unitp->AFB_present = B_TRUE;
6101341Sstevel 		break;
6111341Sstevel 	case 0:
6121341Sstevel 	default:
6131341Sstevel 		unitp->AFB_present = B_FALSE;
6141341Sstevel 		break;
6151341Sstevel 	}
6161341Sstevel 
6171341Sstevel 	/* For debug */
6181341Sstevel 	if (envctrl_afb_present) {
6191341Sstevel 		unitp->AFB_present = B_TRUE;
6201341Sstevel 	}
6211341Sstevel 
6221341Sstevel 	if (unitp->AFB_present == B_TRUE)
6231341Sstevel 		unitp->num_fans_present++;
6241341Sstevel 
6251341Sstevel 	/* initialize the envctrl bus controller */
6261341Sstevel 	mutex_exit(&unitp->umutex);
6271341Sstevel 
6281341Sstevel 	unitp->initting = B_TRUE;
6291341Sstevel 	envctrl_init_bus(unitp);
6301341Sstevel 	unitp->initting = B_FALSE;
6311341Sstevel 	drv_usecwait(1000);
6321341Sstevel 
6331341Sstevel 	mutex_enter(&unitp->umutex);
6341341Sstevel 
6351341Sstevel 	/* Initialize the PCF8583 eggtimer registers */
6361341Sstevel 	buf[0] = ALARM_CTR_REG_MINS;
6371341Sstevel 	buf[1] = 0x0;
6381341Sstevel 	status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
639*7656SSherry.Moore@Sun.COM 	    PCF8583_BASE_ADDR | 0, buf, 2);
6401341Sstevel 	if (status != DDI_SUCCESS)
6411341Sstevel 		cmn_err(CE_WARN, "write to PCF8583 failed\n");
6421341Sstevel 
6431341Sstevel 	buf[0] = ALARM_REG_MINS;
6441341Sstevel 	buf[1] = 0x58;
6451341Sstevel 	status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
646*7656SSherry.Moore@Sun.COM 	    PCF8583_BASE_ADDR | 0, buf, 2);
6471341Sstevel 	if (status != DDI_SUCCESS)
6481341Sstevel 		cmn_err(CE_WARN, "write to PCF8583 failed\n");
6491341Sstevel 
6501341Sstevel 	buf[0] = ALARM_TIMER_REG;
6511341Sstevel 	buf[1] = 0x80;
6521341Sstevel 	status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
653*7656SSherry.Moore@Sun.COM 	    PCF8583_BASE_ADDR | 0, buf, 2);
6541341Sstevel 	if (status != DDI_SUCCESS)
6551341Sstevel 		cmn_err(CE_WARN, "write to PCF8583 failed\n");
6561341Sstevel 
6571341Sstevel 	unitp->timeout_id = 0;
6581341Sstevel 	unitp->blink_timeout_id = 0;
6591341Sstevel 
6601341Sstevel 	if (envctrl_numcpus > 1) {
6611341Sstevel 		unitp->num_cpus_present = envctrl_numcpus;
6621341Sstevel 	}
6631341Sstevel 	envctrl_probe_cpus(unitp);
6641341Sstevel 	envctrl_ps_probe(unitp);
6651341Sstevel 	/*
6661341Sstevel 	 * clear the fan failures, if any before we do
6671341Sstevel 	 * real work
6681341Sstevel 	 */
6691341Sstevel 
6701341Sstevel 	unitp->initting = B_TRUE;
6711341Sstevel 	envctrl_fan_fail_service(unitp);
6721341Sstevel 	unitp->initting = B_FALSE;
6731341Sstevel 
6741341Sstevel 	/*
6751341Sstevel 	 * we need to init the fan kstats before the tempr_poll
6761341Sstevel 	 */
6771341Sstevel 	envctrl_add_kstats(unitp);
6781341Sstevel 	envctrl_init_fan_kstats(unitp);
6791341Sstevel 	envctrl_init_encl_kstats(unitp);
6801341Sstevel 	if (unitp->activity_led_blink == B_TRUE) {
6811341Sstevel 		unitp->present_led_state = B_FALSE;
6821341Sstevel 		mutex_exit(&unitp->umutex);
6831341Sstevel 		envctrl_led_blink((void *)unitp);
6841341Sstevel 		mutex_enter(&unitp->umutex);
6851341Sstevel 	} else {
6861341Sstevel 		fspval = ENVCTRL_FSP_ACTIVE;
6871341Sstevel 		envctrl_set_fsp(unitp, &fspval);
6881341Sstevel 	}
6891341Sstevel 
6901341Sstevel #ifndef TESTBED
6911341Sstevel 	for (i = 0; i < ENVCTRL_MAX_CPUS; i++) {
6921341Sstevel 		if (unitp->cpu_pr_location[i] == B_TRUE) {
6931341Sstevel 			cputemp = envctrl_get_cpu_temp(unitp, i);
6941341Sstevel 			envctrl_add_encl_kstats(unitp, ENVCTRL_ENCL_CPUTEMPR,
6951341Sstevel 			    i, cputemp);
6961341Sstevel 			if (cputemp >= MAX_CPU_TEMP) {
6971341Sstevel 				if (!(envctrl_power_off_overide)) {
6981341Sstevel 					cmn_err(CE_WARN,
6991341Sstevel 					    "CPU %d OVERHEATING!!", i);
7001341Sstevel 					unitp->shutdown = B_TRUE;
7011341Sstevel 				} else {
7021341Sstevel 					cmn_err(CE_WARN,
7031341Sstevel 					    "CPU %d OVERHEATING!!", i);
7041341Sstevel 				}
7051341Sstevel 			}
7061341Sstevel 		}
7071341Sstevel 	}
7081341Sstevel #else
7091341Sstevel 	cputemp = envctrl_get_cpu_temp(unitp, 0);
7101341Sstevel 	envctrl_add_encl_kstats(unitp, ENVCTRL_ENCL_CPUTEMPR, INSTANCE_0,
7111341Sstevel 	    cputemp);
7121341Sstevel #endif
7131341Sstevel 	mutex_exit(&unitp->umutex);
7141341Sstevel 
7151341Sstevel 	envctrl_tempr_poll((void *)unitp);
7161341Sstevel 
7171341Sstevel 	/*
7181341Sstevel 	 * interpose envctrl's abort sequence handler
7191341Sstevel 	 */
7201341Sstevel 	if (envctrl_handler) {
7211341Sstevel 		abort_seq_handler = envctrl_abort_seq_handler;
7221341Sstevel 	}
7231341Sstevel 
7241341Sstevel 	ddi_report_dev(dip);
7251341Sstevel 
7261341Sstevel 	return (DDI_SUCCESS);
7271341Sstevel 
7281341Sstevel remhardintr1:
7291341Sstevel 	ddi_remove_intr(dip, (uint_t)1, unitp->ic_trap_cookie);
7301341Sstevel remhardintr:
7311341Sstevel 	ddi_remove_intr(dip, (uint_t)0, unitp->ic_trap_cookie);
7321341Sstevel 
7331341Sstevel remlock:
7341341Sstevel 	mutex_destroy(&unitp->umutex);
7351341Sstevel 
7361341Sstevel failed:
7371341Sstevel 	if (unitp->ctlr_handle)
7381341Sstevel 		ddi_regs_map_free(&unitp->ctlr_handle);
7391341Sstevel 
7401341Sstevel 	cmn_err(CE_WARN, "envctrl_attach:failed.\n");
7411341Sstevel 
7421341Sstevel 	return (DDI_FAILURE);
7431341Sstevel 
7441341Sstevel }
7451341Sstevel 
7461341Sstevel static int
envctrl_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)7471341Sstevel envctrl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
7481341Sstevel {
7491341Sstevel 	int		instance;
7501341Sstevel 	struct envctrlunit *unitp;
7511341Sstevel 
7521341Sstevel 	instance = ddi_get_instance(dip);
7531341Sstevel 	unitp = ddi_get_soft_state(envctrlsoft_statep, instance);
7541341Sstevel 
7551341Sstevel 	switch (cmd) {
7561341Sstevel 	case DDI_DETACH:
7571341Sstevel 		if (envctrl_allow_detach) {
7581341Sstevel 
7591341Sstevel 			if (unitp->psksp != NULL) {
7601341Sstevel 				kstat_delete(unitp->psksp);
7611341Sstevel 			}
7621341Sstevel 			if (unitp->fanksp != NULL) {
7631341Sstevel 				kstat_delete(unitp->fanksp);
7641341Sstevel 			}
7651341Sstevel 			if (unitp->enclksp != NULL) {
7661341Sstevel 				kstat_delete(unitp->enclksp);
7671341Sstevel 			}
7681341Sstevel 
7691341Sstevel 			if (unitp->timeout_id != 0) {
7701341Sstevel 				(void) untimeout(unitp->timeout_id);
7711341Sstevel 				unitp->timeout_id = 0;
7721341Sstevel 			}
7731341Sstevel 			if (unitp->blink_timeout_id != 0) {
7741341Sstevel 				(void) untimeout(unitp->blink_timeout_id);
7751341Sstevel 				unitp->blink_timeout_id = 0;
7761341Sstevel 			}
7771341Sstevel 
7781341Sstevel 			ddi_remove_minor_node(dip, NULL);
7791341Sstevel 
7801341Sstevel 			ddi_remove_intr(dip, (uint_t)0, unitp->ic_trap_cookie);
7811341Sstevel 			ddi_remove_intr(dip, (uint_t)1, unitp->ic_trap_cookie);
7821341Sstevel 
7831341Sstevel 			ddi_regs_map_free(&unitp->ctlr_handle);
7841341Sstevel 
7851341Sstevel 			mutex_destroy(&unitp->umutex);
7861341Sstevel 
7871341Sstevel 			return (DDI_SUCCESS);
7881341Sstevel 		} else {
7891341Sstevel 			return (DDI_FAILURE);
7901341Sstevel 		}
7911341Sstevel 
7921341Sstevel 	case DDI_SUSPEND:
7931341Sstevel 		if (!(unitp = ddi_get_soft_state(envctrlsoft_statep, instance)))
794*7656SSherry.Moore@Sun.COM 			return (DDI_FAILURE);
7951341Sstevel 		mutex_enter(&unitp->umutex);
7961341Sstevel 		if (unitp->suspended) {
7971341Sstevel 			cmn_err(CE_WARN, "envctrl already suspended\n");
7981341Sstevel 			mutex_exit(&unitp->umutex);
7991341Sstevel 			return (DDI_FAILURE);
8001341Sstevel 		}
8011341Sstevel 		unitp->suspended = 1;
8021341Sstevel 		mutex_exit(&unitp->umutex);
8031341Sstevel 		return (DDI_SUCCESS);
8041341Sstevel 
8051341Sstevel 	default:
8061341Sstevel 		cmn_err(CE_WARN, "envctrl suspend general fault\n");
8071341Sstevel 		return (DDI_FAILURE);
8081341Sstevel 	}
8091341Sstevel 
8101341Sstevel 
8111341Sstevel }
8121341Sstevel 
8131341Sstevel /* ARGSUSED */
8141341Sstevel int
envctrl_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)8151341Sstevel envctrl_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
8161341Sstevel     void **result)
8171341Sstevel {
8181341Sstevel 	dev_t	dev = (dev_t)arg;
8191341Sstevel 	struct envctrlunit *unitp;
8201341Sstevel 	int	ret;
8211341Sstevel 	minor_t instance = getminor(dev);
8221341Sstevel 
8231341Sstevel 	switch (infocmd) {
8241341Sstevel 		case DDI_INFO_DEVT2DEVINFO:
8251341Sstevel 			if ((unitp = (struct envctrlunit *)
826*7656SSherry.Moore@Sun.COM 			    ddi_get_soft_state(envctrlsoft_statep,
827*7656SSherry.Moore@Sun.COM 			    instance)) != NULL) {
8281341Sstevel 				*result = unitp->dip;
8291341Sstevel 				ret = DDI_SUCCESS;
8301341Sstevel 			} else {
8311341Sstevel 				*result = NULL;
8321341Sstevel 				ret = DDI_FAILURE;
8331341Sstevel 			}
8341341Sstevel 			break;
8351341Sstevel 		case DDI_INFO_DEVT2INSTANCE:
8361341Sstevel 			*result = (void *)(uintptr_t)instance;
8371341Sstevel 			ret = DDI_SUCCESS;
8381341Sstevel 			break;
8391341Sstevel 		default:
8401341Sstevel 			ret = DDI_FAILURE;
8411341Sstevel 			break;
8421341Sstevel 	}
8431341Sstevel 
8441341Sstevel 	return (ret);
8451341Sstevel }
8461341Sstevel 
8471341Sstevel /* ARGSUSED */
8481341Sstevel static int
envctrl_open(queue_t * q,dev_t * dev,int flag,int sflag,cred_t * credp)8491341Sstevel envctrl_open(queue_t *q, dev_t *dev, int flag, int sflag, cred_t *credp)
8501341Sstevel {
8511341Sstevel 	struct envctrlunit *unitp;
8521341Sstevel 	int status = 0;
8531341Sstevel 	int	instance;
8541341Sstevel 
8551341Sstevel 	instance = getminor(*dev);
8561341Sstevel 	if (instance < 0)
8571341Sstevel 		return (ENXIO);
8581341Sstevel 	unitp = (struct envctrlunit *)
859*7656SSherry.Moore@Sun.COM 	    ddi_get_soft_state(envctrlsoft_statep, instance);
8601341Sstevel 
8611341Sstevel 	if (unitp == NULL)
8621341Sstevel 		return (ENXIO);
8631341Sstevel 
8641341Sstevel 	mutex_enter(&unitp->umutex);
8651341Sstevel 
8661341Sstevel 	if (flag & FWRITE) {
8671341Sstevel 		if ((unitp->oflag & FWRITE)) {
8681341Sstevel 			mutex_exit(&unitp->umutex);
8691341Sstevel 			return (EBUSY);
8701341Sstevel 		} else {
8711341Sstevel 			unitp->oflag |= FWRITE;
8721341Sstevel 		}
8731341Sstevel 	}
8741341Sstevel 
8751341Sstevel 	q->q_ptr = WR(q)->q_ptr = (caddr_t)unitp;
8761341Sstevel 
8771341Sstevel 	/*
8781341Sstevel 	 * if device is open with O_NONBLOCK flag set, let read(2) return 0
8791341Sstevel 	 * if no data waiting to be read.  Writes will block on flow control.
8801341Sstevel 	 */
8811341Sstevel 
8821341Sstevel 	/* enable the stream */
8831341Sstevel 	qprocson(q);
8841341Sstevel 
8851341Sstevel 	unitp->readq = RD(q);
8861341Sstevel 	unitp->writeq = WR(q);
8871341Sstevel 	unitp->msg = (mblk_t *)NULL;
8881341Sstevel 
8891341Sstevel 	mutex_exit(&unitp->umutex);
8901341Sstevel 	return (status);
8911341Sstevel }
8921341Sstevel 
8931341Sstevel /* ARGSUSED */
8941341Sstevel static int
envctrl_close(queue_t * q,int flag,cred_t * cred_p)8951341Sstevel envctrl_close(queue_t *q, int flag, cred_t *cred_p)
8961341Sstevel {
8971341Sstevel 	struct envctrlunit *unitp;
8981341Sstevel 
8991341Sstevel 	unitp = (struct envctrlunit *)q->q_ptr;
9001341Sstevel 
9011341Sstevel 	mutex_enter(&unitp->umutex);
9021341Sstevel 
9031341Sstevel 	unitp->oflag = B_FALSE;
9041341Sstevel 	unitp->current_mode = ENVCTRL_NORMAL_MODE;
9051341Sstevel 
9061341Sstevel 	/* disable the stream */
9071341Sstevel 	q->q_ptr = WR(q)->q_ptr = NULL;
9081341Sstevel 	qprocsoff(q);
9091341Sstevel 
9101341Sstevel 	mutex_exit(&unitp->umutex);
9111341Sstevel 	return (DDI_SUCCESS);
9121341Sstevel }
9131341Sstevel 
9141341Sstevel /*
9151341Sstevel  * standard put procedure for envctrl
9161341Sstevel  */
9171341Sstevel static int
envctrl_wput(queue_t * q,mblk_t * mp)9181341Sstevel envctrl_wput(queue_t *q, mblk_t *mp)
9191341Sstevel {
9201341Sstevel 	struct msgb *mp1;
9211341Sstevel 	struct envctrlunit *unitp;
9221341Sstevel 	struct iocblk *iocp;
9231341Sstevel 	struct copyresp *csp;
9241341Sstevel 	struct envctrl_tda8444t_chip *fanspeed;
9251341Sstevel 	struct envctrl_pcf8574_chip *ledchip;
9261341Sstevel 	struct envctrl_pcf8591_chip *temp, *a_fanspeed;
9271341Sstevel 	struct copyreq *cqp;
9281341Sstevel 	int cmd;
9291341Sstevel 
9301341Sstevel 	unitp = (struct envctrlunit *)q->q_ptr;
9311341Sstevel 
9321341Sstevel 	switch (DB_TYPE(mp)) {
9331341Sstevel 
9341341Sstevel 	case M_DATA:
9351341Sstevel 
9361341Sstevel 		while (mp) {
9371341Sstevel 			DB_TYPE(mp) = M_DATA;
9381341Sstevel 			mp1 = unlinkb(mp);
9391341Sstevel 			mp->b_cont = NULL;
9401341Sstevel 			if ((mp->b_wptr - mp->b_rptr) <= 0) {
9411341Sstevel 				freemsg(mp);
9421341Sstevel 			} else {
9431341Sstevel 				(void) putq(q, mp);
9441341Sstevel 			}
9451341Sstevel 			mp = mp1;
9461341Sstevel 		}
9471341Sstevel 
9481341Sstevel 		break;
9491341Sstevel 
9501341Sstevel 	case M_IOCTL:
9511341Sstevel 	{
9521341Sstevel 		iocp = (struct iocblk *)(void *)mp->b_rptr;
9531341Sstevel 		cmd = iocp->ioc_cmd;
9541341Sstevel 
9551341Sstevel 		switch (cmd) {
9561341Sstevel 		case ENVCTRL_IOC_SETMODE:
9571341Sstevel 		case ENVCTRL_IOC_GETMODE:
9581341Sstevel 			if (iocp->ioc_count == TRANSPARENT) {
9591341Sstevel 				mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
9601341Sstevel 				    sizeof (uchar_t), NULL);
9611341Sstevel 				qreply(q, mp);
9621341Sstevel 			} else {
9631341Sstevel 				miocnak(q, mp, 0, EINVAL);
9641341Sstevel 			}
9651341Sstevel 			break;
9661341Sstevel 		case ENVCTRL_IOC_RESETTMPR:
9671341Sstevel 			/*
9681341Sstevel 			 * For diags, cancel the current temp poll
9691341Sstevel 			 * and reset it for a new one.
9701341Sstevel 			 */
9711341Sstevel 			if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
9721341Sstevel 				if (unitp->timeout_id != 0) {
9731341Sstevel 					(void) untimeout(unitp->timeout_id);
9741341Sstevel 					unitp->timeout_id = 0;
9751341Sstevel 				}
9761341Sstevel 				envctrl_tempr_poll((void *)unitp);
9771341Sstevel 				miocack(q, mp, 0, 0);
9781341Sstevel 			} else {
9791341Sstevel 				miocnak(q, mp, 0, EINVAL);
9801341Sstevel 			}
9811341Sstevel 			break;
9821341Sstevel 		case ENVCTRL_IOC_GETTEMP:
9831341Sstevel 			if (iocp->ioc_count == TRANSPARENT) {
9841341Sstevel 				mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
9851341Sstevel 				    sizeof (struct envctrl_pcf8591_chip), NULL);
9861341Sstevel 				qreply(q, mp);
9871341Sstevel 			} else {
9881341Sstevel 				miocnak(q, mp, 0, EINVAL);
9891341Sstevel 			}
9901341Sstevel 			break;
9911341Sstevel 		case ENVCTRL_IOC_SETTEMP:
9921341Sstevel 			if (unitp->current_mode == ENVCTRL_DIAG_MODE &&
9931341Sstevel 			    iocp->ioc_count == TRANSPARENT) {
9941341Sstevel 				mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
9951341Sstevel 				    sizeof (uint8_t), NULL);
9961341Sstevel 				qreply(q, mp);
9971341Sstevel 			} else {
9981341Sstevel 				miocnak(q, mp, 0, EINVAL);
9991341Sstevel 			}
10001341Sstevel 			break;
10011341Sstevel 		case ENVCTRL_IOC_SETWDT:
10021341Sstevel 			if (unitp->current_mode == ENVCTRL_DIAG_MODE &&
10031341Sstevel 			    iocp->ioc_count == TRANSPARENT) {
10041341Sstevel 				mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
10051341Sstevel 				    sizeof (uint8_t), NULL);
10061341Sstevel 				qreply(q, mp);
10071341Sstevel 			} else {
10081341Sstevel 				miocnak(q, mp, 0, EINVAL);
10091341Sstevel 			}
10101341Sstevel 			break;
10111341Sstevel 		case ENVCTRL_IOC_SETFAN:
10121341Sstevel 			/*
10131341Sstevel 			 * we must be in diag mode before we can
10141341Sstevel 			 * set any fan speeds.
10151341Sstevel 			 */
10161341Sstevel 			if (unitp->current_mode == ENVCTRL_DIAG_MODE &&
10171341Sstevel 			    iocp->ioc_count == TRANSPARENT) {
10181341Sstevel 				mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
10191341Sstevel 				    sizeof (struct envctrl_tda8444t_chip),
10201341Sstevel 				    NULL);
10211341Sstevel 				qreply(q, mp);
10221341Sstevel 			} else {
10231341Sstevel 				miocnak(q, mp, 0, EINVAL);
10241341Sstevel 			}
10251341Sstevel 			break;
10261341Sstevel 		case ENVCTRL_IOC_GETFAN:
10271341Sstevel 			if (iocp->ioc_count == TRANSPARENT) {
10281341Sstevel 				mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
10291341Sstevel 				    sizeof (struct envctrl_pcf8591_chip), NULL);
10301341Sstevel 				qreply(q, mp);
10311341Sstevel 			} else {
10321341Sstevel 				miocnak(q, mp, 0, EINVAL);
10331341Sstevel 			}
10341341Sstevel 			break;
10351341Sstevel 		case ENVCTRL_IOC_SETFSP:
10361341Sstevel 			if (iocp->ioc_count == TRANSPARENT) {
10371341Sstevel 				mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
10381341Sstevel 				    sizeof (uint8_t), NULL);
10391341Sstevel 				qreply(q, mp);
10401341Sstevel 			} else {
10411341Sstevel 				miocnak(q, mp, 0, EINVAL);
10421341Sstevel 			}
10431341Sstevel 			break;
10441341Sstevel 		case ENVCTRL_IOC_SETDSKLED:
10451341Sstevel 		case ENVCTRL_IOC_GETDSKLED:
10461341Sstevel 			if (iocp->ioc_count == TRANSPARENT) {
10471341Sstevel 				mcopyin(mp, *(caddr_t *)mp->b_cont->b_rptr,
10481341Sstevel 				    sizeof (struct envctrl_pcf8574_chip), NULL);
10491341Sstevel 				qreply(q, mp);
10501341Sstevel 			} else {
10511341Sstevel 				miocnak(q, mp, 0, EINVAL);
10521341Sstevel 			}
10531341Sstevel 			break;
10541341Sstevel 		default:
10551341Sstevel 			miocnak(q, mp, 0, EINVAL);
10561341Sstevel 			break;
10571341Sstevel 		}
10581341Sstevel 
10591341Sstevel 		break;
10601341Sstevel 
10611341Sstevel 	}
10621341Sstevel 	case M_IOCDATA:
10631341Sstevel 	{
10641341Sstevel 		uint8_t *tempr, *wdval;
10651341Sstevel 		long state;
10661341Sstevel 
10671341Sstevel 		csp = (struct copyresp *)(void *)mp->b_rptr;
10681341Sstevel 
10691341Sstevel 		/*
10701341Sstevel 		 * If copy request failed, quit now
10711341Sstevel 		 */
10721341Sstevel 		if (csp->cp_rval != 0) {
10731341Sstevel 			miocnak(q, mp, 0, EINVAL);
10741341Sstevel 			return (0);
10751341Sstevel 		}
10761341Sstevel 
10771341Sstevel 		cqp = (struct copyreq *)(void *)mp->b_rptr;
10781341Sstevel 
10791341Sstevel 		cmd = csp->cp_cmd;
10801341Sstevel 		state = (long)cqp->cq_private;
10811341Sstevel 
10821341Sstevel 		switch (cmd) {
10831341Sstevel 		case ENVCTRL_IOC_SETFAN:
10841341Sstevel 			fanspeed = (struct envctrl_tda8444t_chip *)
10851341Sstevel 			    (void *)mp->b_cont->b_rptr;
10861341Sstevel 			mutex_enter(&unitp->umutex);
10871341Sstevel 			if (envctrl_xmit(unitp, (caddr_t *)(void *)fanspeed,
10881341Sstevel 			    fanspeed->type) == DDI_FAILURE) {
10891341Sstevel 				/*
10901341Sstevel 				 * Fix for a ADF bug
10911341Sstevel 				 * move mutex to after fan fail call
10921341Sstevel 				 * bugid 4016121
10931341Sstevel 				 */
10941341Sstevel 				envctrl_fan_fail_service(unitp);
10951341Sstevel 				mutex_exit(&unitp->umutex);
10961341Sstevel 				miocnak(q, mp, 0, EINVAL);
10971341Sstevel 			} else {
10981341Sstevel 				mutex_exit(&unitp->umutex);
10991341Sstevel 				miocack(q, mp, 0, 0);
11001341Sstevel 			}
11011341Sstevel 			break;
11021341Sstevel 		case ENVCTRL_IOC_SETFSP:
11031341Sstevel 			wdval = (uint8_t *)(void *)mp->b_cont->b_rptr;
11041341Sstevel 			mutex_enter(&unitp->umutex);
11051341Sstevel 			/*
11061341Sstevel 			 * If a user is in normal mode and they try
11071341Sstevel 			 * to set anything other than a disk fault or
11081341Sstevel 			 * a gen fault it is an invalid operation.
11091341Sstevel 			 * in diag mode we allow everything to be
11101341Sstevel 			 * twiddled.
11111341Sstevel 			 */
11121341Sstevel 			if (unitp->current_mode == ENVCTRL_NORMAL_MODE) {
11131341Sstevel 				if (*wdval & ~ENVCTRL_FSP_USRMASK) {
11141341Sstevel 					mutex_exit(&unitp->umutex);
11151341Sstevel 					miocnak(q, mp, 0, EINVAL);
11161341Sstevel 					break;
11171341Sstevel 				}
11181341Sstevel 			}
11191341Sstevel 			envctrl_set_fsp(unitp, wdval);
11201341Sstevel 			mutex_exit(&unitp->umutex);
11211341Sstevel 			miocack(q, mp, 0, 0);
11221341Sstevel 			break;
11231341Sstevel 		case ENVCTRL_IOC_SETDSKLED:
11241341Sstevel 			ledchip = (struct envctrl_pcf8574_chip *)
11251341Sstevel 			    (void *)mp->b_cont->b_rptr;
11261341Sstevel 			mutex_enter(&unitp->umutex);
11271341Sstevel 			if (envctrl_set_dskled(unitp, ledchip)) {
11281341Sstevel 				miocnak(q, mp, 0, EINVAL);
11291341Sstevel 			} else {
11301341Sstevel 				miocack(q, mp, 0, 0);
11311341Sstevel 			}
11321341Sstevel 			mutex_exit(&unitp->umutex);
11331341Sstevel 			break;
11341341Sstevel 		case ENVCTRL_IOC_GETDSKLED:
11351341Sstevel 			if (state  == -1) {
11361341Sstevel 				miocack(q, mp, 0, 0);
11371341Sstevel 				break;
11381341Sstevel 			}
11391341Sstevel 			ledchip = (struct envctrl_pcf8574_chip *)
11401341Sstevel 			    (void *)mp->b_cont->b_rptr;
11411341Sstevel 			mutex_enter(&unitp->umutex);
11421341Sstevel 			if (envctrl_get_dskled(unitp, ledchip)) {
11431341Sstevel 				miocnak(q, mp, 0, EINVAL);
11441341Sstevel 			} else {
11451341Sstevel 				mcopyout(mp, (void *)-1,
11461341Sstevel 				    sizeof (struct envctrl_pcf8574_chip),
11471341Sstevel 				    csp->cp_private, NULL);
11481341Sstevel 				qreply(q, mp);
11491341Sstevel 			}
11501341Sstevel 			mutex_exit(&unitp->umutex);
11511341Sstevel 			break;
11521341Sstevel 		case ENVCTRL_IOC_GETTEMP:
11531341Sstevel 			/* Get the user buffer address */
11541341Sstevel 
11551341Sstevel 			if (state  == -1) {
11561341Sstevel 				miocack(q, mp, 0, 0);
11571341Sstevel 				break;
11581341Sstevel 			}
11591341Sstevel 			temp = (struct envctrl_pcf8591_chip *)
11601341Sstevel 			    (void *)mp->b_cont->b_rptr;
11611341Sstevel 			mutex_enter(&unitp->umutex);
11621341Sstevel 			envctrl_recv(unitp, (caddr_t *)(void *)temp, PCF8591);
11631341Sstevel 			mutex_exit(&unitp->umutex);
11641341Sstevel 			mcopyout(mp, (void *)-1,
11651341Sstevel 			    sizeof (struct envctrl_pcf8591_chip),
11661341Sstevel 			    csp->cp_private, NULL);
11671341Sstevel 			qreply(q, mp);
11681341Sstevel 			break;
11691341Sstevel 		case ENVCTRL_IOC_GETFAN:
11701341Sstevel 			/* Get the user buffer address */
11711341Sstevel 
11721341Sstevel 			if (state == -1) {
11731341Sstevel 				miocack(q, mp, 0, 0);
11741341Sstevel 				break;
11751341Sstevel 			}
11761341Sstevel 			a_fanspeed = (struct envctrl_pcf8591_chip *)
11771341Sstevel 			    (void *)mp->b_cont->b_rptr;
11781341Sstevel 			mutex_enter(&unitp->umutex);
11791341Sstevel 			envctrl_recv(unitp, (caddr_t *)(void *)a_fanspeed,
1180*7656SSherry.Moore@Sun.COM 			    PCF8591);
11811341Sstevel 			mutex_exit(&unitp->umutex);
11821341Sstevel 			mcopyout(mp, (void *)-1,
11831341Sstevel 			    sizeof (struct envctrl_pcf8591_chip),
11841341Sstevel 			    csp->cp_private, NULL);
11851341Sstevel 			qreply(q, mp);
11861341Sstevel 			break;
11871341Sstevel 		case ENVCTRL_IOC_SETTEMP:
11881341Sstevel 			tempr = (uint8_t *)(void *)mp->b_cont->b_rptr;
11891341Sstevel 			if (*tempr > MAX_DIAG_TEMPR) {
11901341Sstevel 				miocnak(q, mp, 0, EINVAL);
11911341Sstevel 			} else {
11921341Sstevel 				mutex_enter(&unitp->umutex);
11931341Sstevel 				envctrl_get_sys_temperatures(unitp, tempr);
11941341Sstevel 				mutex_exit(&unitp->umutex);
11951341Sstevel 				miocack(q, mp, 0, 0);
11961341Sstevel 			}
11971341Sstevel 			break;
11981341Sstevel 		case ENVCTRL_IOC_SETWDT:
11991341Sstevel 			/* reset watchdog timeout period */
12001341Sstevel 			wdval = (uint8_t *)(void *)mp->b_cont->b_rptr;
12011341Sstevel 			if (*wdval > MAX_CL_VAL) {
12021341Sstevel 				miocnak(q, mp, 0, EINVAL);
12031341Sstevel 			} else {
12041341Sstevel 				mutex_enter(&unitp->umutex);
12051341Sstevel 				envctrl_reset_watchdog(unitp, wdval);
12061341Sstevel 				mutex_exit(&unitp->umutex);
12071341Sstevel 				miocack(q, mp, 0, 0);
12081341Sstevel 			}
12091341Sstevel 			break;
12101341Sstevel 		case ENVCTRL_IOC_GETMODE:
12111341Sstevel 			/* Get the user buffer address */
12121341Sstevel 
12131341Sstevel 			if (state == -1) {
12141341Sstevel 				miocack(q, mp, 0, 0);
12151341Sstevel 				break;
12161341Sstevel 			}
12171341Sstevel 			tempr = (uchar_t *)(void *)mp->b_cont->b_rptr;
12181341Sstevel 			*tempr = unitp->current_mode;
12191341Sstevel 			mcopyout(mp, (void *)-1, sizeof (uchar_t),
12201341Sstevel 			    csp->cp_private, NULL);
12211341Sstevel 			qreply(q, mp);
12221341Sstevel 			break;
12231341Sstevel 		case ENVCTRL_IOC_SETMODE:
12241341Sstevel 			/* Set mode */
12251341Sstevel 			wdval = (uint8_t *)(void *)mp->b_cont->b_rptr;
12261341Sstevel 			if (*wdval == ENVCTRL_DIAG_MODE || *wdval ==
12271341Sstevel 			    ENVCTRL_NORMAL_MODE) {
12281341Sstevel 				mutex_enter(&unitp->umutex);
12291341Sstevel 				unitp->current_mode = *wdval;
12301341Sstevel 				if (unitp->timeout_id != 0 &&
12311341Sstevel 				    *wdval == ENVCTRL_DIAG_MODE) {
12321341Sstevel 					(void) untimeout(unitp->timeout_id);
12331341Sstevel 					unitp->timeout_id =
12341341Sstevel 					    (timeout(envctrl_tempr_poll,
1235*7656SSherry.Moore@Sun.COM 					    (caddr_t)unitp,
1236*7656SSherry.Moore@Sun.COM 					    overtemp_timeout_hz));
12371341Sstevel 
12381341Sstevel 				}
12391341Sstevel 				if (*wdval == ENVCTRL_NORMAL_MODE) {
12401341Sstevel 					envctrl_get_sys_temperatures(unitp,
12411341Sstevel 					    (uint8_t *)NULL);
12421341Sstevel 					/*
12431341Sstevel 					 * going to normal mode we
12441341Sstevel 					 * need to go to diag mode
12451341Sstevel 					 * just in case we have
12461341Sstevel 					 * injected a fan fault. It
12471341Sstevel 					 * may not be cleared and if
12481341Sstevel 					 * we call fan_failsrvc it will
12491341Sstevel 					 * power off the ystem if we are
12501341Sstevel 					 * in NORMAL_MODE. Also we need
12511341Sstevel 					 * to delay 1 bit of time here
12521341Sstevel 					 * to  allow the fans to rotate
12531341Sstevel 					 * back up and clear the intr
12541341Sstevel 					 * after we get the sys temps.
12551341Sstevel 					 */
12561341Sstevel 					unitp->current_mode =
12571341Sstevel 					    ENVCTRL_DIAG_MODE;
12581341Sstevel 					envctrl_fan_fail_service(unitp);
12591341Sstevel 					unitp->current_mode =
12601341Sstevel 					    ENVCTRL_NORMAL_MODE;
12611341Sstevel 				}
12621341Sstevel 				mutex_exit(&unitp->umutex);
12631341Sstevel 				miocack(q, mp, 0, 0);
12641341Sstevel 			} else {
12651341Sstevel 				miocnak(q, mp, 0, EINVAL);
12661341Sstevel 			}
12671341Sstevel 			break;
12681341Sstevel 		default:
12691341Sstevel 			freemsg(mp);
12701341Sstevel 			break;
12711341Sstevel 		}
12721341Sstevel 
12731341Sstevel 		break;
12741341Sstevel 	}
12751341Sstevel 
12761341Sstevel 	case M_FLUSH:
12771341Sstevel 		if (*mp->b_rptr & FLUSHR) {
12781341Sstevel 			*mp->b_rptr &= ~FLUSHW;
12791341Sstevel 			qreply(q, mp);
12801341Sstevel 		} else {
12811341Sstevel 			freemsg(mp);
12821341Sstevel 		}
12831341Sstevel 		break;
12841341Sstevel 
12851341Sstevel 	default:
12861341Sstevel 		freemsg(mp);
12871341Sstevel 		break;
12881341Sstevel 	}
12891341Sstevel 
12901341Sstevel 	return (0);
12911341Sstevel }
12921341Sstevel 
12931341Sstevel uint_t
envctrl_bus_isr(caddr_t arg)12941341Sstevel envctrl_bus_isr(caddr_t arg)
12951341Sstevel {
12961341Sstevel 	struct envctrlunit *unitp = (struct envctrlunit *)(void *)arg;
12971341Sstevel 	int ic = DDI_INTR_UNCLAIMED;
12981341Sstevel 
12991341Sstevel 	mutex_enter(&unitp->umutex);
13001341Sstevel 
13011341Sstevel 	/*
13021341Sstevel 	 * NOT USED
13031341Sstevel 	 */
13041341Sstevel 
13051341Sstevel 	mutex_exit(&unitp->umutex);
13061341Sstevel 	return (ic);
13071341Sstevel }
13081341Sstevel 
13091341Sstevel uint_t
envctrl_dev_isr(caddr_t arg)13101341Sstevel envctrl_dev_isr(caddr_t arg)
13111341Sstevel {
13121341Sstevel 	struct envctrlunit *unitp = (struct envctrlunit *)(void *)arg;
13131341Sstevel 	uint8_t recv_data;
13141341Sstevel 	int ic;
13151341Sstevel 	int retrys = 0;
13161341Sstevel 	int status;
13171341Sstevel 
13181341Sstevel 	ic = DDI_INTR_UNCLAIMED;
13191341Sstevel 
13201341Sstevel 	mutex_enter(&unitp->umutex);
13211341Sstevel 
13221341Sstevel 	/*
13231341Sstevel 	 * First check to see if it is an interrupt for us by
13241341Sstevel 	 * looking at the "ganged" interrrupt and vector
13251341Sstevel 	 * according to the major type
13261341Sstevel 	 * 0x70 is the addr of the ganged interrupt controller.
13271341Sstevel 	 * Address map for the port byte read is as follows
13281341Sstevel 	 * MSB
13291341Sstevel 	 * -------------------------
13301341Sstevel 	 * |  |  |  |  |  |  |  |  |
13311341Sstevel 	 * -------------------------
13321341Sstevel 	 *  P7 P6 P5 P4 P3 P2 P1 P0
13331341Sstevel 	 * P0 = Power Supply 1 intr
13341341Sstevel 	 * P1 = Power Supply 2 intr
13351341Sstevel 	 * P2 = Power Supply 3 intr
13361341Sstevel 	 * P3 = Dlfop enable for fan sped set
13371341Sstevel 	 * P4 = ENVCTRL_ Fan Fail intr
13381341Sstevel 	 * P5 =	Front Panel Interrupt
13391341Sstevel 	 * P6 = Power Fail Detect Low.
13401341Sstevel 	 * P7 = Enable Interrupts to system
13411341Sstevel 	 */
13421341Sstevel 
13431341Sstevel retry:
13441341Sstevel 
13451341Sstevel 	status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
1346*7656SSherry.Moore@Sun.COM 	    PCF8574A_BASE_ADDR | ENVCTRL_PCF8574_DEV0, &recv_data, 1);
13471341Sstevel 
13481341Sstevel 	/*
13491341Sstevel 	 * This extra read is needed since the first read is discarded
13501341Sstevel 	 * and the second read seems to return 0xFF.
13511341Sstevel 	 */
13521341Sstevel 	if (recv_data == 0xFF) {
13531341Sstevel 		status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
1354*7656SSherry.Moore@Sun.COM 		    PCF8574A_BASE_ADDR | ENVCTRL_PCF8574_DEV0, &recv_data, 1);
13551341Sstevel 	}
13561341Sstevel 	if (envctrl_debug_flags)
13571341Sstevel 		cmn_err(CE_WARN, "envctrl_dev_isr: status= %d, data = %x\n",
1358*7656SSherry.Moore@Sun.COM 		    status, recv_data);
13591341Sstevel 
13601341Sstevel 	/*
13611341Sstevel 	 * if the i2c bus is hung it is imperative that this
13621341Sstevel 	 * be cleared on an interrupt or else it will
13631341Sstevel 	 * hang the system with continuous interrupts
13641341Sstevel 	 */
13651341Sstevel 
13661341Sstevel 	if (status == DDI_FAILURE) {
13671341Sstevel 		drv_usecwait(1000);
13681341Sstevel 		if (retrys < envctrl_max_retries) {
13691341Sstevel 			retrys++;
13701341Sstevel 			goto retry;
13711341Sstevel 		} else {
13721341Sstevel 			if (envctrl_debug_flags)
13731341Sstevel 				cmn_err(CE_WARN,
13741341Sstevel 				    "DEVISR FAILED received 0x%x\n", recv_data);
13751341Sstevel 			mutex_exit(&unitp->umutex);
13761341Sstevel 			envctrl_init_bus(unitp);
13771341Sstevel 			mutex_enter(&unitp->umutex);
13781341Sstevel 			envctrl_ps_probe(unitp);
13791341Sstevel 			mutex_exit(&unitp->umutex);
13801341Sstevel 			ic = DDI_INTR_CLAIMED;
13811341Sstevel 			return (ic);
13821341Sstevel 		}
13831341Sstevel 	}
13841341Sstevel 
13851341Sstevel 	/*
13861341Sstevel 	 * Port 0 = PS1 interrupt
13871341Sstevel 	 * Port 1 = PS2 Interrupt
13881341Sstevel 	 * Port 2 = PS3 Interrupt
13891341Sstevel 	 * Port 3 = SPARE
13901341Sstevel 	 * Port 4 = Fan Fail Intr
13911341Sstevel 	 * Port 5 = Front Panle Module intr
13921341Sstevel 	 * Port 6 = Keyswitch Intr
13931341Sstevel 	 * Port 7 = ESINTR ENABLE ???
13941341Sstevel 	 */
13951341Sstevel 
13961341Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT0)) {
13971341Sstevel 		envctrl_PS_intr_service(unitp, PS1);
13981341Sstevel 		ic = DDI_INTR_CLAIMED;
13991341Sstevel 	}
14001341Sstevel 
14011341Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT1)) {
14021341Sstevel 		envctrl_PS_intr_service(unitp, PS2);
14031341Sstevel 		ic = DDI_INTR_CLAIMED;
14041341Sstevel 	}
14051341Sstevel 
14061341Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT2)) {
14071341Sstevel 		envctrl_PS_intr_service(unitp, PS3);
14081341Sstevel 		ic = DDI_INTR_CLAIMED;
14091341Sstevel 	}
14101341Sstevel 
14111341Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT3)) {
14121341Sstevel 		ic = DDI_INTR_CLAIMED;
14131341Sstevel 	}
14141341Sstevel 
14151341Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT4)) {
14161341Sstevel 		/*
14171341Sstevel 		 * Check for a fan fail
14181341Sstevel 		 * Single fan fail
14191341Sstevel 		 * shutdown system
14201341Sstevel 		 */
14211341Sstevel 		envctrl_fan_fail_service(unitp);
14221341Sstevel 		ic = DDI_INTR_CLAIMED;
14231341Sstevel 	}
14241341Sstevel 
14251341Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT5)) {
14261341Sstevel 		(void) envctrl_get_fpm_status(unitp);
14271341Sstevel 		ic = DDI_INTR_CLAIMED;
14281341Sstevel 	}
14291341Sstevel 
14301341Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT6)) {
14311341Sstevel 		ic = DDI_INTR_CLAIMED;
14321341Sstevel 	}
14331341Sstevel 
14341341Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT7)) {
14351341Sstevel 		ic = DDI_INTR_CLAIMED;
14361341Sstevel 	}
14371341Sstevel 
14381341Sstevel 	if ((recv_data == 0xFF)) {
14391341Sstevel 		ic = DDI_INTR_CLAIMED;
14401341Sstevel 	}
14411341Sstevel 
14421341Sstevel 	mutex_exit(&unitp->umutex);
14431341Sstevel 	return (ic);
14441341Sstevel 
14451341Sstevel }
14461341Sstevel 
14471341Sstevel static void
envctrl_init_bus(struct envctrlunit * unitp)14481341Sstevel envctrl_init_bus(struct envctrlunit *unitp)
14491341Sstevel {
14501341Sstevel 
14511341Sstevel 	int i;
14521341Sstevel 	uint8_t noval = NULL;
14531341Sstevel 	struct envctrl_tda8444t_chip fan;
14541341Sstevel 	int fans[] = {ENVCTRL_CPU_FANS, ENVCTRL_PS_FANS, ENVCTRL_AFB_FANS};
14551341Sstevel 
14561341Sstevel 	mutex_enter(&unitp->umutex);
14571341Sstevel 	/* Sets the Mode to 808x type bus */
14581341Sstevel 	ddi_put8(unitp->ctlr_handle,
14591341Sstevel 	    &unitp->bus_ctl_regs->s0, ENVCTRL_CHAR_ZERO);
14601341Sstevel 
14611341Sstevel 	/* SET UP SLAVE ADDR XXX Required..send 0x80 */
14621341Sstevel 
14631341Sstevel 	ddi_put8(unitp->ctlr_handle, &unitp->bus_ctl_regs->s1,
1464*7656SSherry.Moore@Sun.COM 	    ENVCTRL_BUS_INIT0);
14651341Sstevel 	(void) ddi_put8(unitp->ctlr_handle, &unitp->bus_ctl_regs->s0,
1466*7656SSherry.Moore@Sun.COM 	    ENVCTRL_BUS_INIT1);
14671341Sstevel 
14681341Sstevel 	/* Set the clock now */
14691341Sstevel 	ddi_put8(unitp->ctlr_handle,
14701341Sstevel 	    &unitp->bus_ctl_regs->s1, ENVCTRL_BUS_CLOCK0);
14711341Sstevel 
14721341Sstevel 	/* S0 is now S2  necause of the previous write to S1 */
14731341Sstevel 	/* clock= 12MHz, SCL=90KHz */
14741341Sstevel 	ddi_put8(unitp->ctlr_handle,
14751341Sstevel 	    &unitp->bus_ctl_regs->s0, ENVCTRL_BUS_CLOCK1);
14761341Sstevel 
14771341Sstevel 	/* Enable serial interface */
14781341Sstevel 	ddi_put8(unitp->ctlr_handle,
14791341Sstevel 	    &unitp->bus_ctl_regs->s1, ENVCTRL_BUS_ESI);
14801341Sstevel 
14811341Sstevel 	envctrl_stop_clock(unitp);
14821341Sstevel 
14831341Sstevel 	/*
14841341Sstevel 	 * This has been added here because the DAC is powered
14851341Sstevel 	 * on at "0". When the reset_dflop routine is called
14861341Sstevel 	 * this switched the  fans from blast to DAC control.
14871341Sstevel 	 * if the DAC is at "0", then the fans momentarily lose
14881341Sstevel 	 * power until the temp polling and fan set routine is
14891341Sstevel 	 * first called. If the fans lose power, then there is
14901341Sstevel 	 * a fan fault generated and the system will power off.
14911341Sstevel 	 * We only want to do this IF the bus is first being
14921341Sstevel 	 * initted. This will cause errors in Sunvts if we reset
14931341Sstevel 	 * the fan speed under normal operation. Sometimes we need
14941341Sstevel 	 * to be able to induce fan faults. Init bus is a common
14951341Sstevel 	 * routine to unwedge the i2c bus in some cases.
14961341Sstevel 	 */
14971341Sstevel 
14981341Sstevel 	if (unitp->initting == B_TRUE) {
14991341Sstevel 		fan.chip_num = ENVCTRL_TDA8444T_DEV7;
15001341Sstevel 		fan.val = INIT_FAN_VAL;
15011341Sstevel 
15021341Sstevel 		for (i = 0; i < sizeof (fans)/sizeof (int); i++) {
15031341Sstevel 			fan.fan_num = fans[i];
15041341Sstevel 			if ((fans[i] == ENVCTRL_AFB_FANS) &&
1505*7656SSherry.Moore@Sun.COM 			    (unitp->AFB_present == B_FALSE))
15061341Sstevel 				continue;
15071341Sstevel 			(void) envctrl_xmit(unitp, (caddr_t *)(void *)&fan,
1508*7656SSherry.Moore@Sun.COM 			    TDA8444T);
15091341Sstevel 		}
15101341Sstevel 	}
15111341Sstevel 
15121341Sstevel 	envctrl_reset_dflop(unitp);
15131341Sstevel 
15141341Sstevel 	envctrl_enable_devintrs(unitp);
15151341Sstevel 
15161341Sstevel 	unitp->current_mode = ENVCTRL_NORMAL_MODE;
15171341Sstevel 	envctrl_reset_watchdog(unitp, &noval);
15181341Sstevel 
15191341Sstevel 	mutex_exit(&unitp->umutex);
15201341Sstevel }
15211341Sstevel 
15221341Sstevel static int
envctrl_xmit(struct envctrlunit * unitp,caddr_t * data,int chip_type)15231341Sstevel envctrl_xmit(struct envctrlunit *unitp, caddr_t *data, int chip_type)
15241341Sstevel {
15251341Sstevel 
15261341Sstevel 	struct envctrl_tda8444t_chip *fanspeed;
15271341Sstevel 	struct envctrl_pcf8574_chip *ioport;
15281341Sstevel 	uint8_t slave_addr;
15291341Sstevel 	uint8_t buf[2];
15301341Sstevel 	int retrys = 0;
15311341Sstevel 	int status;
15321341Sstevel 
15331341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
15341341Sstevel 
15351341Sstevel 	switch (chip_type) {
15361341Sstevel 	case TDA8444T:
15371341Sstevel 
15381341Sstevel 		fanspeed = (struct envctrl_tda8444t_chip *)data;
15391341Sstevel 
15401341Sstevel 		if (fanspeed->chip_num > ENVCTRL_FAN_ADDR_MAX) {
15411341Sstevel 			return (DDI_FAILURE);
15421341Sstevel 		}
15431341Sstevel 
15441341Sstevel 		if (fanspeed->fan_num > ENVCTRL_PORT7) {
15451341Sstevel 			return (DDI_FAILURE);
15461341Sstevel 		}
15471341Sstevel 
15481341Sstevel 		if (fanspeed->val > MAX_FAN_VAL) {
15491341Sstevel 			return (DDI_FAILURE);
15501341Sstevel 		}
15511341Sstevel 
15521341Sstevel retry0:
15531341Sstevel 		slave_addr = (TDA8444T_BASE_ADDR | fanspeed->chip_num);
15541341Sstevel 		buf[0] = fanspeed->val;
15551341Sstevel 
15561341Sstevel 		status = eHc_write_tda8444((struct eHc_envcunit *)unitp,
1557*7656SSherry.Moore@Sun.COM 		    TDA8444T_BASE_ADDR | fanspeed->chip_num, 0xF,
1558*7656SSherry.Moore@Sun.COM 		    fanspeed->fan_num, buf, 1);
15591341Sstevel 		if (status != DDI_SUCCESS) {
15601341Sstevel 			drv_usecwait(1000);
15611341Sstevel 			if (retrys < envctrl_max_retries) {
15621341Sstevel 				retrys++;
15631341Sstevel 				goto retry0;
15641341Sstevel 			} else {
15651341Sstevel 				mutex_exit(&unitp->umutex);
15661341Sstevel 				envctrl_init_bus(unitp);
15671341Sstevel 				mutex_enter(&unitp->umutex);
15681341Sstevel 				if (envctrl_debug_flags)
15691341Sstevel 					cmn_err(CE_WARN,
15701341Sstevel 					    "envctrl_xmit: Write to TDA8444 " \
15711341Sstevel 					    "failed\n");
15721341Sstevel 				return (DDI_FAILURE);
15731341Sstevel 			}
15741341Sstevel 		}
15751341Sstevel 
15761341Sstevel 		/*
15771341Sstevel 		 * Update the kstats.
15781341Sstevel 		 */
15791341Sstevel 		switch (fanspeed->fan_num) {
15801341Sstevel 		case ENVCTRL_CPU_FANS:
15811341Sstevel 			unitp->fan_kstats[ENVCTRL_FAN_TYPE_CPU].fanspeed =
15821341Sstevel 			    fanspeed->val;
15831341Sstevel 			break;
15841341Sstevel 		case ENVCTRL_PS_FANS:
15851341Sstevel 			unitp->fan_kstats[ENVCTRL_FAN_TYPE_PS].fanspeed =
15861341Sstevel 			    fanspeed->val;
15871341Sstevel 			break;
15881341Sstevel 		case ENVCTRL_AFB_FANS:
15891341Sstevel 			unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].fanspeed =
15901341Sstevel 			    fanspeed->val;
15911341Sstevel 			break;
15921341Sstevel 		default:
15931341Sstevel 			break;
15941341Sstevel 		}
15951341Sstevel 		break;
15961341Sstevel 	case PCF8574:
15971341Sstevel 		ioport = (struct envctrl_pcf8574_chip *)data;
15981341Sstevel 		buf[0] = ioport->val;
15991341Sstevel 		if (ioport->chip_num > ENVCTRL_PCF8574_DEV7)
16001341Sstevel 			return (DDI_FAILURE);
16011341Sstevel 
16021341Sstevel retry:
16031341Sstevel 		if (ioport->type == PCF8574A) {
16041341Sstevel 			slave_addr = (PCF8574A_BASE_ADDR | ioport->chip_num);
16051341Sstevel 			status =
1606*7656SSherry.Moore@Sun.COM 			    eHc_write_pcf8574a((struct eHc_envcunit *)unitp,
1607*7656SSherry.Moore@Sun.COM 			    PCF8574A_BASE_ADDR | ioport->chip_num, buf, 1);
16081341Sstevel 		} else {
16091341Sstevel 			slave_addr = (PCF8574_BASE_ADDR | ioport->chip_num);
16101341Sstevel 			status = eHc_write_pcf8574((struct eHc_envcunit *)unitp,
1611*7656SSherry.Moore@Sun.COM 			    PCF8574_BASE_ADDR | ioport->chip_num, buf, 1);
16121341Sstevel 		}
16131341Sstevel 
16141341Sstevel 		if (status != DDI_SUCCESS) {
16151341Sstevel 			drv_usecwait(1000);
16161341Sstevel 			if (retrys < envctrl_max_retries) {
16171341Sstevel 				retrys++;
16181341Sstevel 				goto retry;
16191341Sstevel 			} else {
16201341Sstevel 				mutex_exit(&unitp->umutex);
16211341Sstevel 				envctrl_init_bus(unitp);
16221341Sstevel 				mutex_enter(&unitp->umutex);
16231341Sstevel 				if (envctrl_debug_flags)
16241341Sstevel 					cmn_err(CE_WARN, "Write to PCF8574 " \
16251341Sstevel 					    "failed, addr = %X\n", slave_addr);
16261341Sstevel 				if (envctrl_debug_flags)
16271341Sstevel 					cmn_err(CE_WARN, "envctrl_xmit: PCF8574\
16281341Sstevel 						dev = %d, port = %d\n",
1629*7656SSherry.Moore@Sun.COM 					    ioport->chip_num, ioport->type);
16301341Sstevel 				return (DDI_FAILURE);
16311341Sstevel 			}
16321341Sstevel 		}
16331341Sstevel 		break;
16341341Sstevel 
16351341Sstevel 	default:
16361341Sstevel 		return (DDI_FAILURE);
16371341Sstevel 	}
16381341Sstevel 
16391341Sstevel 	return (DDI_SUCCESS);
16401341Sstevel }
16411341Sstevel 
16421341Sstevel static void
envctrl_recv(struct envctrlunit * unitp,caddr_t * data,int chip_type)16431341Sstevel envctrl_recv(struct envctrlunit *unitp, caddr_t *data, int chip_type)
16441341Sstevel {
16451341Sstevel 
16461341Sstevel 	struct envctrl_pcf8591_chip *temp;
16471341Sstevel 	struct envctrl_pcf8574_chip *ioport;
16481341Sstevel 	uint8_t slave_addr, recv_data;
16491341Sstevel 	int retrys = 0;
16501341Sstevel 	int status;
16511341Sstevel 	uint8_t buf[1];
16521341Sstevel 
16531341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
16541341Sstevel 
16551341Sstevel 	switch (chip_type) {
16561341Sstevel 	case PCF8591:
16571341Sstevel 		temp = (struct envctrl_pcf8591_chip *)data;
16581341Sstevel 		slave_addr = (PCF8591_BASE_ADDR | temp->chip_num);
16591341Sstevel 
16601341Sstevel retry:
16611341Sstevel 		status = eHc_read_pcf8591((struct eHc_envcunit *)unitp,
1662*7656SSherry.Moore@Sun.COM 		    PCF8591_BASE_ADDR | temp->chip_num & 0xF,
1663*7656SSherry.Moore@Sun.COM 		    temp->sensor_num, 0, 0, 1, &recv_data, 1);
16641341Sstevel 
16651341Sstevel 		/*
16661341Sstevel 		 * another place to catch the i2c bus hang on an 8591 read
16671341Sstevel 		 * In this instance we will just return the data that is read
16681341Sstevel 		 * after the max_retry because this could be a valid value.
16691341Sstevel 		 */
16701341Sstevel 		if (status != DDI_SUCCESS) {
16711341Sstevel 			drv_usecwait(1000);
16721341Sstevel 			if (retrys < envctrl_max_retries) {
16731341Sstevel 				retrys++;
16741341Sstevel 				goto retry;
16751341Sstevel 			} else {
16761341Sstevel 				mutex_exit(&unitp->umutex);
16771341Sstevel 				envctrl_init_bus(unitp);
16781341Sstevel 				mutex_enter(&unitp->umutex);
16791341Sstevel 				if (envctrl_debug_flags)
16801341Sstevel 					cmn_err(CE_WARN, "Read from PCF8591 " \
16811341Sstevel 					    "failed, slave_addr = %x\n",
16821341Sstevel 					    slave_addr);
16831341Sstevel 			}
16841341Sstevel 		}
16851341Sstevel 		temp->temp_val = recv_data;
16861341Sstevel 		break;
16871341Sstevel 	case TDA8444T:
16881341Sstevel 		printf("envctrl_recv: attempting to read TDA8444T\n");
16891341Sstevel 		return;
16901341Sstevel 	case PCF8574:
16911341Sstevel 		ioport = (struct envctrl_pcf8574_chip *)data;
16921341Sstevel 
16931341Sstevel retry1:
16941341Sstevel 		if (ioport->chip_num > ENVCTRL_PCF8574_DEV7)
16951341Sstevel 			cmn_err(CE_WARN, "envctrl: dev out of range 0x%x\n",
1696*7656SSherry.Moore@Sun.COM 			    ioport->chip_num);
16971341Sstevel 
16981341Sstevel 		if (ioport->type == PCF8574A) {
16991341Sstevel 			slave_addr = (PCF8574_READ_BIT | PCF8574A_BASE_ADDR |
17001341Sstevel 			    ioport->chip_num);
17011341Sstevel 			status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
1702*7656SSherry.Moore@Sun.COM 			    PCF8574A_BASE_ADDR | ioport->chip_num, buf, 1);
17031341Sstevel 		} else {
17041341Sstevel 			slave_addr = (PCF8574_READ_BIT | PCF8574_BASE_ADDR |
17051341Sstevel 			    ioport->chip_num);
17061341Sstevel 			status = eHc_read_pcf8574((struct eHc_envcunit *)unitp,
1707*7656SSherry.Moore@Sun.COM 			    PCF8574_BASE_ADDR | ioport->chip_num, buf, 1);
17081341Sstevel 		}
17091341Sstevel 
17101341Sstevel 		if (status != DDI_SUCCESS) {
17111341Sstevel 			drv_usecwait(1000);
17121341Sstevel 			if (retrys < envctrl_max_retries) {
17131341Sstevel 				retrys++;
17141341Sstevel 				goto retry1;
17151341Sstevel 			} else {
17161341Sstevel 				mutex_exit(&unitp->umutex);
17171341Sstevel 				envctrl_init_bus(unitp);
17181341Sstevel 				mutex_enter(&unitp->umutex);
17191341Sstevel 				if (envctrl_debug_flags)
17201341Sstevel 					cmn_err(CE_WARN, "Read from PCF8574 "\
17211341Sstevel 					    "failed, addr = %X\n", slave_addr);
17221341Sstevel 				if (envctrl_debug_flags)
17231341Sstevel 					cmn_err(CE_WARN, "envctrl_recv: PCF8574\
17241341Sstevel 						dev = %d, port = %d\n",
1725*7656SSherry.Moore@Sun.COM 					    ioport->chip_num, ioport->type);
17261341Sstevel 			}
17271341Sstevel 		}
17281341Sstevel 		ioport->val = buf[0];
17291341Sstevel 		break;
17301341Sstevel 	default:
17311341Sstevel 		break;
17321341Sstevel 	}
17331341Sstevel }
17341341Sstevel 
17351341Sstevel static int
envctrl_get_ps_temp(struct envctrlunit * unitp,uint8_t psaddr)17361341Sstevel envctrl_get_ps_temp(struct envctrlunit *unitp, uint8_t psaddr)
17371341Sstevel {
17381341Sstevel 	uint8_t tempr;
17391341Sstevel 	int i, retrys;
17401341Sstevel 	int status;
17411341Sstevel 	uint8_t buf[4];
17421341Sstevel 
17431341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
17441341Sstevel 
17451341Sstevel 	tempr = 0;
17461341Sstevel 	retrys = 0;
17471341Sstevel 
17481341Sstevel retry:
17491341Sstevel 	status = eHc_read_pcf8591((struct eHc_envcunit *)unitp,
1750*7656SSherry.Moore@Sun.COM 	    PCF8591_BASE_ADDR | psaddr & 0xF, 0, 1, 0, 1, buf, 4);
17511341Sstevel 
17521341Sstevel 	tempr = 0;
17531341Sstevel 	for (i = 0; i < PCF8591_MAX_PORTS; i++) {
17541341Sstevel 		/*
17551341Sstevel 		 * The pcf8591 will return 0xff if no port
17561341Sstevel 		 * is there.. this is bogus for setting temps.
17571341Sstevel 		 * so just ignore it!
17581341Sstevel 		 */
17591341Sstevel 		if (envctrl_debug_flags) {
17601341Sstevel 			cmn_err(CE_WARN, "PS addr 0x%x recvd 0x%x on port %d\n",
17611341Sstevel 			    psaddr, buf[i], i);
17621341Sstevel 		}
17631341Sstevel 		if (buf[i] > tempr && buf[i] < MAX_PS_ADVAL) {
17641341Sstevel 			tempr = buf[i];
17651341Sstevel 		}
17661341Sstevel 	}
17671341Sstevel 
17681341Sstevel 	/*
17691341Sstevel 	 * This routine is a safeguard to make sure that if the
17701341Sstevel 	 * powersupply temps cannot be read that we do something
17711341Sstevel 	 * to make sure that the system will notify the user and
17721341Sstevel 	 * it will stay running with the fans at 100%. The calling
17731341Sstevel 	 * routine should take care of that.
17741341Sstevel 	 */
17751341Sstevel 	if (status != DDI_SUCCESS) {
17761341Sstevel 		drv_usecwait(1000);
17771341Sstevel 		if (retrys < envctrl_max_retries) {
17781341Sstevel 			retrys++;
17791341Sstevel 			goto retry;
17801341Sstevel 		} else {
17811341Sstevel 			mutex_exit(&unitp->umutex);
17821341Sstevel 			envctrl_init_bus(unitp);
17831341Sstevel 			mutex_enter(&unitp->umutex);
17841341Sstevel 			if (envctrl_debug_flags)
17851341Sstevel 				cmn_err(CE_WARN,
17861341Sstevel 				    "Cannot read Power Supply Temps addr = %X",
17871341Sstevel 				    psaddr);
17881341Sstevel 			return (PS_DEFAULT_VAL);
17891341Sstevel 		}
17901341Sstevel 	}
17911341Sstevel 
17921341Sstevel 	return (ps_temps[tempr]);
17931341Sstevel }
17941341Sstevel 
17951341Sstevel static int
envctrl_get_cpu_temp(struct envctrlunit * unitp,int cpunum)17961341Sstevel envctrl_get_cpu_temp(struct envctrlunit *unitp, int cpunum)
17971341Sstevel {
17981341Sstevel 	uint8_t recv_data;
17991341Sstevel 	int retrys;
18001341Sstevel 	int status;
18011341Sstevel 
18021341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
18031341Sstevel 
18041341Sstevel 	/*
18051341Sstevel 	 * This routine takes in the number of the port that
18061341Sstevel 	 * we want to read in the 8591. This should be the
18071341Sstevel 	 * location of the COU thermistor for one of the 4
18081341Sstevel 	 * cpu's. It will return the temperature in degrees C
18091341Sstevel 	 * to the caller.
18101341Sstevel 	 */
18111341Sstevel 
18121341Sstevel 	retrys = 0;
18131341Sstevel 
18141341Sstevel retry:
18151341Sstevel 	status = eHc_read_pcf8591((struct eHc_envcunit *)unitp,
1816*7656SSherry.Moore@Sun.COM 	    PCF8591_BASE_ADDR | PCF8591_DEV7, cpunum, 0, 0, 0,
1817*7656SSherry.Moore@Sun.COM 	    &recv_data, 1);
18181341Sstevel 
18191341Sstevel 	/*
18201341Sstevel 	 * We need to take a sledge hammer to the bus if we get back
18211341Sstevel 	 * value of the chip. This means that the i2c bus got wedged.
18221341Sstevel 	 * On the 1.4 systems this happens sometimes while running
18231341Sstevel 	 * sunvts. We will return the max cpu temp minus 10 to make
18241341Sstevel 	 * the fans run at full speed so that we don;t cook the
18251341Sstevel 	 * system.
18261341Sstevel 	 * At this point this is a workaround for hardware glitch.
18271341Sstevel 	 */
18281341Sstevel 	if (status == DDI_FAILURE) {
18291341Sstevel 		drv_usecwait(1000);
18301341Sstevel 		if (retrys < envctrl_max_retries) {
18311341Sstevel 			retrys++;
18321341Sstevel 			goto retry;
18331341Sstevel 		} else {
18341341Sstevel 			mutex_exit(&unitp->umutex);
18351341Sstevel 			envctrl_init_bus(unitp);
18361341Sstevel 			mutex_enter(&unitp->umutex);
18371341Sstevel 			if (envctrl_debug_flags)
18381341Sstevel 				cmn_err(CE_WARN, "envctrl CPU TEMP read " \
18391341Sstevel 				    "failed\n");
18401341Sstevel 			/* we don't want to power off the system */
18411341Sstevel 			return (MAX_CPU_TEMP - 10);
18421341Sstevel 		}
18431341Sstevel 	}
18441341Sstevel 
18451341Sstevel 	return (cpu_temps[recv_data]);
18461341Sstevel }
18471341Sstevel 
18481341Sstevel static int
envctrl_get_lm75_temp(struct envctrlunit * unitp)18491341Sstevel envctrl_get_lm75_temp(struct envctrlunit *unitp)
18501341Sstevel {
18511341Sstevel 
18521341Sstevel 	int k;
18531341Sstevel 	ushort_t lmval;
18541341Sstevel 	uint8_t tmp1;
18551341Sstevel 	uint8_t tmp2;
18561341Sstevel 	int status;
18571341Sstevel 	uint8_t buf[2];
18581341Sstevel 
18591341Sstevel 
18601341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
18611341Sstevel 
18621341Sstevel 	status = eHc_read_lm75((struct eHc_envcunit *)unitp,
1863*7656SSherry.Moore@Sun.COM 	    LM75_BASE_ADDR | LM75_CONFIG_ADDRA, buf, 2);
18641341Sstevel 	if (status != DDI_SUCCESS)
18651341Sstevel 		cmn_err(CE_WARN, "read of LM75 failed\n");
18661341Sstevel 
18671341Sstevel 	tmp1 = buf[0];
18681341Sstevel 	tmp2 = buf[1];
18691341Sstevel 
18701341Sstevel 	/*
18711341Sstevel 	 * Store the forst 8 bits in the upper nibble of the
18721341Sstevel 	 * short, then store the lower 8 bits in the lower nibble
18731341Sstevel 	 * of the short, shift 7 to the right to get the 9 bit value
18741341Sstevel 	 * that the lm75 is really sending.
18751341Sstevel 	 */
18761341Sstevel 	lmval = tmp1 << 8;
18771341Sstevel 	lmval = (lmval | tmp2);
18781341Sstevel 	lmval = (lmval >> 7);
18791341Sstevel 	/*
18801341Sstevel 	 * Check the 9th bit to see if it is a negative
18811341Sstevel 	 * temperature. If so change into 2's compliment
18821341Sstevel 	 * and divide by 2 since each value is equal to a
18831341Sstevel 	 * half degree strp in degrees C
18841341Sstevel 	 */
18851341Sstevel 	if (lmval & LM75_COMP_MASK) {
18861341Sstevel 		tmp1 = (lmval & LM75_COMP_MASK_UPPER);
18871341Sstevel 		tmp1 = -tmp1;
18881341Sstevel 		tmp1 = tmp1/2;
18891341Sstevel 		k = 0 - tmp1;
18901341Sstevel 	} else {
18911341Sstevel 		k = lmval /2;
18921341Sstevel 	}
18931341Sstevel 		return (k);
18941341Sstevel }
18951341Sstevel 
18961341Sstevel 
18971341Sstevel static void
envctrl_tempr_poll(void * arg)18981341Sstevel envctrl_tempr_poll(void *arg)
18991341Sstevel {
19001341Sstevel 	int diag_flag = 0;
19011341Sstevel 	struct envctrlunit *unitp = (struct envctrlunit *)arg;
19021341Sstevel 
19031341Sstevel 	mutex_enter(&unitp->umutex);
19041341Sstevel 
19051341Sstevel 	if (unitp->shutdown == B_TRUE) {
19061341Sstevel 		(void) power_down("Fatal System Environmental Control Error");
19071341Sstevel 	}
19081341Sstevel 
19091341Sstevel 	/*
19101341Sstevel 	 * if we are in diag mode and the temp poll thread goes off,
19111341Sstevel 	 * this means that the system is too heavily loaded and the 60 second
19121341Sstevel 	 * window to execute the test is failing. We will change the fanspeed
19131341Sstevel 	 * but will not check for a fanfault. This will cause a system shutdown
19141341Sstevel 	 * if the system has had a fanfault injected.
19151341Sstevel 	 */
19161341Sstevel 	if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
19171341Sstevel 		diag_flag++;
19181341Sstevel 		if (envctrl_debug_flags) {
19191341Sstevel 			cmn_err(CE_WARN,
19201341Sstevel 			    "Tempr poll went off while in DIAG MODE");
19211341Sstevel 		}
19221341Sstevel 	}
19231341Sstevel 	unitp->current_mode = ENVCTRL_NORMAL_MODE;
19241341Sstevel 	envctrl_get_sys_temperatures(unitp, (uint8_t *)NULL);
19251341Sstevel 	if (diag_flag == 0) {
19261341Sstevel 		envctrl_fan_fail_service(unitp);
19271341Sstevel 	}
19281341Sstevel 	/* now have this thread sleep for a while */
19291341Sstevel 	unitp->timeout_id = (timeout(envctrl_tempr_poll,
19301341Sstevel 	    (caddr_t)unitp, overtemp_timeout_hz));
19311341Sstevel 
19321341Sstevel 	mutex_exit(&unitp->umutex);
19331341Sstevel }
19341341Sstevel 
19351341Sstevel static void
envctrl_led_blink(void * arg)19361341Sstevel envctrl_led_blink(void *arg)
19371341Sstevel {
19381341Sstevel 	struct envctrl_pcf8574_chip fspchip;
19391341Sstevel 	struct envctrlunit *unitp = (struct envctrlunit *)arg;
19401341Sstevel 
19411341Sstevel 	mutex_enter(&unitp->umutex);
19421341Sstevel 
19431341Sstevel 	fspchip.type = PCF8574A;
19441341Sstevel 	fspchip.chip_num = ENVCTRL_PCF8574_DEV6; /* 0x01 port 1 */
19451341Sstevel 	envctrl_recv(unitp, (caddr_t *)(void *)&fspchip, PCF8574);
19461341Sstevel 
19471341Sstevel 	if (unitp->present_led_state == B_TRUE) {
19481341Sstevel 		/*
19491341Sstevel 		 * Now we need to "or" in fault bits of the FSP
19501341Sstevel 		 * module for the mass storage fault led.
19511341Sstevel 		 * and set it.
19521341Sstevel 		 */
19531341Sstevel 		fspchip.val = (fspchip.val & ~(ENVCTRL_PCF8574_PORT4) |
1954*7656SSherry.Moore@Sun.COM 		    0xC0);
19551341Sstevel 		unitp->present_led_state = B_FALSE;
19561341Sstevel 	} else {
19571341Sstevel 		fspchip.val = (fspchip.val | ENVCTRL_PCF8574_PORT4 | 0xC0);
19581341Sstevel 		unitp->present_led_state = B_TRUE;
19591341Sstevel 	}
19601341Sstevel 
19611341Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)&fspchip, PCF8574);
19621341Sstevel 
19631341Sstevel 	/* now have this thread sleep for a while */
19641341Sstevel 	unitp->blink_timeout_id = (timeout(envctrl_led_blink,
19651341Sstevel 	    (caddr_t)unitp, blink_timeout_hz));
19661341Sstevel 
19671341Sstevel 	mutex_exit(&unitp->umutex);
19681341Sstevel }
19691341Sstevel 
19701341Sstevel /* called with mutex held */
19711341Sstevel static void
envctrl_get_sys_temperatures(struct envctrlunit * unitp,uint8_t * diag_tempr)19721341Sstevel envctrl_get_sys_temperatures(struct envctrlunit *unitp, uint8_t *diag_tempr)
19731341Sstevel {
19741341Sstevel 	int temperature, tmptemp, cputemp, hicputemp, ambtemp;
19751341Sstevel 	int i;
19761341Sstevel 	struct envctrl_tda8444t_chip fan;
19771341Sstevel 	uint8_t psaddr[] = {PSTEMP3, PSTEMP2, PSTEMP1, PSTEMP0};
19781341Sstevel 	uint8_t noval = NULL;
19791341Sstevel 	uint8_t fspval;
19801341Sstevel 
19811341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
19821341Sstevel 
19831341Sstevel 	fan.fan_num = ENVCTRL_CPU_FANS;
19841341Sstevel 	fan.chip_num = ENVCTRL_TDA8444T_DEV7;
19851341Sstevel 
19861341Sstevel 	tmptemp = 0;	/* Right init value ?? */
19871341Sstevel 
19881341Sstevel 	/*
19891341Sstevel 	 * THis routine is caled once every minute
19901341Sstevel 	 * we wil re-se the watchdog timer each time
19911341Sstevel 	 * we poll the temps. The watchdog timer is
19921341Sstevel 	 * set up for 3 minutes. Should the kernel thread
19931341Sstevel 	 * wedge, for some reason the watchdog will go off
19941341Sstevel 	 * and blast the fans.
19951341Sstevel 	 */
19961341Sstevel 
19971341Sstevel 	if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
19981341Sstevel 		unitp->current_mode = ENVCTRL_NORMAL_MODE;
19991341Sstevel 		envctrl_reset_watchdog(unitp, &noval);
20001341Sstevel 		unitp->current_mode = ENVCTRL_DIAG_MODE;
20011341Sstevel 	} else {
20021341Sstevel 		envctrl_reset_watchdog(unitp, &noval);
20031341Sstevel 	}
20041341Sstevel 
20051341Sstevel 	/*
20061341Sstevel 	 * we need to reset the dflop to allow the fans to be
20071341Sstevel 	 * set if the watchdog goes of and the kernel resumes
20081341Sstevel 	 * resetting the dflop alos resets the device interrupts
20091341Sstevel 	 * we need to reenable them also.
20101341Sstevel 	 */
20111341Sstevel 	envctrl_reset_dflop(unitp);
20121341Sstevel 
20131341Sstevel 	envctrl_enable_devintrs(unitp);
20141341Sstevel 
20151341Sstevel 	/*
20161341Sstevel 	 * If we are in diag mode we allow the system to be
20171341Sstevel 	 * faked out as to what the temperature is
20181341Sstevel 	 * to see if the fans speed up.
20191341Sstevel 	 */
20201341Sstevel 	if (unitp->current_mode == ENVCTRL_DIAG_MODE && diag_tempr != NULL) {
20211341Sstevel 		if (unitp->timeout_id != 0) {
2022*7656SSherry.Moore@Sun.COM 			(void) untimeout(unitp->timeout_id);
20231341Sstevel 		}
20241341Sstevel 
20251341Sstevel 		ambtemp = *diag_tempr;
20261341Sstevel 		unitp->timeout_id = (timeout(envctrl_tempr_poll,
20271341Sstevel 		    (caddr_t)unitp, overtemp_timeout_hz));
20281341Sstevel 	} else {
20291341Sstevel 		ambtemp = envctrl_get_lm75_temp(unitp);
20301341Sstevel 		/*
20311341Sstevel 		 * Sometimes when we read the temp it comes back bogus
20321341Sstevel 		 * to fix this we just need to reset the envctrl bus
20331341Sstevel 		 */
20341341Sstevel 		if (ambtemp == -100) {
20351341Sstevel 			mutex_exit(&unitp->umutex);
20361341Sstevel 			envctrl_init_bus(unitp);
20371341Sstevel 			mutex_enter(&unitp->umutex);
20381341Sstevel 			ambtemp = envctrl_get_lm75_temp(unitp);
20391341Sstevel 		}
20401341Sstevel 	}
20411341Sstevel 
20421341Sstevel 	envctrl_mod_encl_kstats(unitp, ENVCTRL_ENCL_AMBTEMPR, INSTANCE_0,
20431341Sstevel 	    ambtemp);
20441341Sstevel 
20451341Sstevel 	fspval = envctrl_get_fpm_status(unitp);
20461341Sstevel 
20471341Sstevel 	if (ambtemp > MAX_AMB_TEMP) {
20481341Sstevel 		fspval |= (ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_GEN_ERR);
20491341Sstevel 		if (!(envctrl_power_off_overide) &&
20501341Sstevel 		    unitp->current_mode == ENVCTRL_NORMAL_MODE) {
20511341Sstevel 			unitp->shutdown = B_TRUE;
20521341Sstevel 		}
20531341Sstevel 			if (unitp->current_mode == ENVCTRL_NORMAL_MODE) {
20541341Sstevel 				cmn_err(CE_WARN,
20551341Sstevel 			"Ambient Temperature is %d C, shutdown now\n",
20561341Sstevel 				    ambtemp);
20571341Sstevel 			}
20581341Sstevel 	} else {
20591341Sstevel 		if (envctrl_isother_fault_led(unitp, fspval,
2060*7656SSherry.Moore@Sun.COM 		    ENVCTRL_FSP_TEMP_ERR)) {
20611341Sstevel 			fspval &= ~(ENVCTRL_FSP_TEMP_ERR);
20621341Sstevel 		} else {
20631341Sstevel 			fspval &= ~(ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_GEN_ERR);
20641341Sstevel 		}
20651341Sstevel 	}
20661341Sstevel 
20671341Sstevel 	envctrl_set_fsp(unitp, &fspval);
20681341Sstevel 
20691341Sstevel 	cputemp = hicputemp = 0;
20701341Sstevel #ifndef TESTBED
20711341Sstevel 	for (i = 0; i < ENVCTRL_MAX_CPUS; i++) {
20721341Sstevel 		if (unitp->cpu_pr_location[i] == B_TRUE) {
20731341Sstevel 			cputemp = envctrl_get_cpu_temp(unitp, i);
20741341Sstevel 			envctrl_mod_encl_kstats(unitp, ENVCTRL_ENCL_CPUTEMPR,
20751341Sstevel 			    i, cputemp);
20761341Sstevel 			if (cputemp >= MAX_CPU_TEMP) {
20771341Sstevel 				if (!(envctrl_power_off_overide)) {
20781341Sstevel 					unitp->shutdown = B_TRUE;
20791341Sstevel 				}
20801341Sstevel 				cmn_err(CE_WARN,
20811341Sstevel 				    "CPU %d OVERHEATING!!!", i);
20821341Sstevel 			}
20831341Sstevel 
20841341Sstevel 			if (cputemp > hicputemp) {
20851341Sstevel 				hicputemp = cputemp;
20861341Sstevel 			}
20871341Sstevel 		}
20881341Sstevel 	}
20891341Sstevel #else
20901341Sstevel 	cputemp = envctrl_get_cpu_temp(unitp, 0);
20911341Sstevel 	envctrl_mod_encl_kstats(unitp, ENVCTRL_ENCL_CPUTEMPR, 0, cputemp);
20921341Sstevel #endif
20931341Sstevel 
20941341Sstevel 	fspval = envctrl_get_fpm_status(unitp);
20951341Sstevel 
20961341Sstevel 	/*
20971341Sstevel 	 * We first look at the ambient temp. If the system is at idle
20981341Sstevel 	 * the cpu temps will be approx 20 degrees above ambient.
20991341Sstevel 	 * If the cpu's rise above 20, then the CPU fans are set
21001341Sstevel 	 * according to the cpu temp minus 20 degrees C.
21011341Sstevel 	 */
21021341Sstevel 	if (unitp->current_mode == ENVCTRL_DIAG_MODE && diag_tempr != NULL) {
21031341Sstevel 		temperature = ambtemp;
21041341Sstevel 	} else {
21051341Sstevel 		temperature = hicputemp - CPU_AMB_RISE;
21061341Sstevel 	}
21071341Sstevel 
21081341Sstevel 	if (temperature < 0) {
21091341Sstevel 		fan.val = MAX_FAN_SPEED; 	/* blast it is out of range */
21101341Sstevel 	} else if (temperature > MAX_AMB_TEMP) {
21111341Sstevel 		fan.val = MAX_FAN_SPEED;
21121341Sstevel 		fspval |= (ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_GEN_ERR);
21131341Sstevel 
21141341Sstevel 			if (unitp->current_mode == ENVCTRL_NORMAL_MODE) {
21151341Sstevel 				cmn_err(CE_WARN,
21161341Sstevel 				    "CPU Fans set to MAX. CPU Temp is %d C\n",
21171341Sstevel 				    hicputemp);
21181341Sstevel 			}
21191341Sstevel 	} else if (ambtemp < MAX_AMB_TEMP) {
21201341Sstevel 		if (!envctrl_p0_enclosure) {
21211341Sstevel 			fan.val = acme_cpu_fanspd[temperature];
21221341Sstevel 		} else {
21231341Sstevel 			fan.val = fan_speed[temperature];
21241341Sstevel 		}
21251341Sstevel 		if (envctrl_isother_fault_led(unitp, fspval,
2126*7656SSherry.Moore@Sun.COM 		    ENVCTRL_FSP_TEMP_ERR)) {
21271341Sstevel 			fspval &= ~(ENVCTRL_FSP_TEMP_ERR);
21281341Sstevel 		} else {
21291341Sstevel 			fspval &= ~(ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_GEN_ERR);
21301341Sstevel 		}
21311341Sstevel 	}
21321341Sstevel 
21331341Sstevel 	envctrl_set_fsp(unitp, &fspval);
21341341Sstevel 
21351341Sstevel 	/*
21361341Sstevel 	 * Update temperature kstats. FSP kstats are updated in the
21371341Sstevel 	 * set and get routine.
21381341Sstevel 	 */
21391341Sstevel 
21401341Sstevel 	unitp->fan_kstats[ENVCTRL_FAN_TYPE_CPU].fanspeed = fan.val;
21411341Sstevel 
21421341Sstevel 	/* CPU FANS */
21431341Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)&fan, TDA8444T);
21441341Sstevel 
21451341Sstevel 	/* The afb Fan is always at max */
21461341Sstevel 	if (unitp->AFB_present == B_TRUE) {
21471341Sstevel 		fan.val = AFB_MAX;
21481341Sstevel 		/* AFB FANS */
21491341Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].fanspeed = fan.val;
21501341Sstevel 		fan.fan_num = ENVCTRL_AFB_FANS;
21511341Sstevel 		(void) envctrl_xmit(unitp, (caddr_t *)(void *)&fan, TDA8444T);
21521341Sstevel 	}
21531341Sstevel 
21541341Sstevel 	/*
21551341Sstevel 	 * Now set the Powersupply fans
21561341Sstevel 	 */
21571341Sstevel 
21581341Sstevel 	tmptemp = temperature = 0;
21591341Sstevel 	for (i = 0; i <= MAXPS; i++) {
21601341Sstevel 		if (unitp->ps_present[i]) {
21611341Sstevel 			tmptemp = envctrl_get_ps_temp(unitp, psaddr[i]);
21621341Sstevel 			unitp->ps_kstats[i].ps_tempr = tmptemp & 0xFFFF;
21631341Sstevel 			if (tmptemp > temperature) {
21641341Sstevel 				temperature = tmptemp;
21651341Sstevel 			}
21661341Sstevel 			if (temperature >= MAX_PS_TEMP) {
21671341Sstevel 				if (!(envctrl_power_off_overide)) {
21681341Sstevel 					unitp->shutdown = B_TRUE;
21691341Sstevel 				}
21701341Sstevel 				cmn_err(CE_WARN,
21711341Sstevel 				    "Power Supply %d OVERHEATING!!!\
21721341Sstevel 				    Temp is %d C", i, temperature);
21731341Sstevel 			}
21741341Sstevel 		}
21751341Sstevel 	}
21761341Sstevel 
21771341Sstevel 
21781341Sstevel 	fan.fan_num = ENVCTRL_PS_FANS;
21791341Sstevel 	if (temperature > PS_TEMP_WARN) {
21801341Sstevel 		fspval = envctrl_get_fpm_status(unitp);
21811341Sstevel 		fspval |= (ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_GEN_ERR);
21821341Sstevel 		envctrl_set_fsp(unitp, &fspval);
21831341Sstevel 		fan.val = MAX_FAN_SPEED;
21841341Sstevel 		cmn_err(CE_WARN, "A Power Supply is close to  OVERHEATING!!!");
21851341Sstevel 	} else {
21861341Sstevel 		if (temperature - ambtemp > PS_AMB_RISE) {
21871341Sstevel 			ambtemp = temperature - PS_AMB_RISE;
21881341Sstevel 		}
21891341Sstevel 		if (!envctrl_p0_enclosure) {
21901341Sstevel 			fan.val = acme_ps_fanspd[ambtemp];
21911341Sstevel 		} else {
21921341Sstevel 			fan.val = ps_fans[ambtemp];
21931341Sstevel 		}
21941341Sstevel 	}
21951341Sstevel 
21961341Sstevel 	/*
21971341Sstevel 	 * XXX add in error condition for ps overtemp
21981341Sstevel 	 */
21991341Sstevel 
22001341Sstevel 	unitp->fan_kstats[ENVCTRL_FAN_TYPE_PS].fanspeed = fan.val;
22011341Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)&fan, TDA8444T);
22021341Sstevel }
22031341Sstevel 
22041341Sstevel /* called with mutex held */
22051341Sstevel static void
envctrl_fan_fail_service(struct envctrlunit * unitp)22061341Sstevel envctrl_fan_fail_service(struct envctrlunit *unitp)
22071341Sstevel {
22081341Sstevel 	uint8_t recv_data, fpmstat;
22091341Sstevel 	int fantype;
22101341Sstevel 	int psfanflt, cpufanflt, afbfanflt;
22111341Sstevel 	int retries = 0, max_retry_count;
22121341Sstevel 	int status;
22131341Sstevel 
22141341Sstevel 	psfanflt = cpufanflt = afbfanflt = 0;
22151341Sstevel 	/*
22161341Sstevel 	 * The fan fail sensor is located at address 0x70
22171341Sstevel 	 * on the envctrl bus.
22181341Sstevel 	 */
22191341Sstevel 
22201341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
22211341Sstevel 
22221341Sstevel retry:
22231341Sstevel 	status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
2224*7656SSherry.Moore@Sun.COM 	    PCF8574A_BASE_ADDR | ENVCTRL_PCF8574_DEV4, &recv_data, 1);
22251341Sstevel 	if (status != DDI_SUCCESS)
22261341Sstevel 		cmn_err(CE_WARN, "fan_fail_service: status = %d, data = %x\n",
2227*7656SSherry.Moore@Sun.COM 		    status, recv_data);
22281341Sstevel 
22291341Sstevel 	/*
22301341Sstevel 	 * If all fan ports are high (0xff) then we don't have any
22311341Sstevel 	 * fan faults. Reset the kstats
22321341Sstevel 	 */
22331341Sstevel 	if (recv_data == 0xff) {
22341341Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_PS].fans_ok = B_TRUE;
22351341Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_CPU].fans_ok = B_TRUE;
22361341Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].fans_ok = B_TRUE;
22371341Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_PS].fanflt_num = 0;
22381341Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_CPU].fanflt_num = 0;
22391341Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].fanflt_num = 0;
22401341Sstevel 		unitp->num_fans_failed = 0;
22411341Sstevel 		fpmstat = envctrl_get_fpm_status(unitp);
22421341Sstevel 		if (!(envctrl_isother_fault_led(unitp, fpmstat, 0))) {
22431341Sstevel 			fpmstat &= ~(ENVCTRL_FSP_GEN_ERR);
22441341Sstevel 		}
22451341Sstevel 		if (unitp->shutdown != B_TRUE) {
22461341Sstevel 			envctrl_set_fsp(unitp, &fpmstat);
22471341Sstevel 		}
22481341Sstevel 		return;
22491341Sstevel 	}
22501341Sstevel 
22511341Sstevel 	fantype = ENVCTRL_FAN_TYPE_PS;
22521341Sstevel 
22531341Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT0)) {
22541341Sstevel 		psfanflt = PS_FAN_3;
22551341Sstevel 	}
22561341Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT1)) {
22571341Sstevel 		psfanflt = PS_FAN_2;
22581341Sstevel 	}
22591341Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT2)) {
22601341Sstevel 		psfanflt = PS_FAN_1;
22611341Sstevel 	}
22621341Sstevel 
22631341Sstevel 	if (psfanflt != 0) {
22641341Sstevel 		unitp->fan_kstats[fantype].fans_ok = B_FALSE;
22651341Sstevel 		unitp->fan_kstats[fantype].fanflt_num = psfanflt - 1;
22661341Sstevel 		if (retries == MAX_FAN_FAIL_RETRY && status == DDI_SUCCESS &&
22671341Sstevel 		    unitp->current_mode == ENVCTRL_NORMAL_MODE) {
22681341Sstevel 			cmn_err(CE_WARN, "PS Fan Number %d Failed",
22691341Sstevel 			    psfanflt - 1);
22701341Sstevel 		}
22711341Sstevel 	} else {
22721341Sstevel 		unitp->fan_kstats[fantype].fans_ok = B_TRUE;
22731341Sstevel 		unitp->fan_kstats[fantype].fanflt_num = 0;
22741341Sstevel 	}
22751341Sstevel 
22761341Sstevel 	fantype = ENVCTRL_FAN_TYPE_CPU;
22771341Sstevel 
22781341Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT3)) {
22791341Sstevel 		cpufanflt = CPU_FAN_1;
22801341Sstevel 	}
22811341Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT4)) {
22821341Sstevel 		cpufanflt = CPU_FAN_2;
22831341Sstevel 	}
22841341Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT5)) {
22851341Sstevel 		cpufanflt = CPU_FAN_3;
22861341Sstevel 	}
22871341Sstevel 
22881341Sstevel 	if (cpufanflt != 0) {
22891341Sstevel 		unitp->fan_kstats[fantype].fans_ok = B_FALSE;
22901341Sstevel 		unitp->fan_kstats[fantype].fanflt_num = cpufanflt - 1;
22911341Sstevel 		if (retries == MAX_FAN_FAIL_RETRY && status == DDI_SUCCESS &&
22921341Sstevel 		    unitp->current_mode == ENVCTRL_NORMAL_MODE) {
22931341Sstevel 			cmn_err(CE_WARN, "CPU Fan Number %d Failed",
22941341Sstevel 			    cpufanflt - 1);
22951341Sstevel 		}
22961341Sstevel 	} else {
22971341Sstevel 		unitp->fan_kstats[fantype].fans_ok = B_TRUE;
22981341Sstevel 		unitp->fan_kstats[fantype].fanflt_num = 0;
22991341Sstevel 	}
23001341Sstevel 
23011341Sstevel 	if (!(recv_data & ENVCTRL_PCF8574_PORT6) &&
2302*7656SSherry.Moore@Sun.COM 	    (unitp->AFB_present == B_TRUE)) {
23031341Sstevel 		/*
23041341Sstevel 		 * If the afb is present and the afb fan fails,
23051341Sstevel 		 * we need to power off or else it will melt!
23061341Sstevel 		 * If it isn't present just log the error.
23071341Sstevel 		 * We make the decision off of the afbfanflt
23081341Sstevel 		 * flag later on in an if statement.
23091341Sstevel 		 */
23101341Sstevel 		afbfanflt++;
23111341Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].fans_ok
23121341Sstevel 		    = B_FALSE;
23131341Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].fanflt_num =
23141341Sstevel 		    AFB_FAN_1;
23151341Sstevel 		if (unitp->current_mode == ENVCTRL_NORMAL_MODE) {
23161341Sstevel 			cmn_err(CE_WARN, "AFB Fan Failed");
23171341Sstevel 		}
23181341Sstevel 
23191341Sstevel 	}
23201341Sstevel 
23211341Sstevel 	/*
23221341Sstevel 	 * If we have no Fan Faults Clear the LED's
23231341Sstevel 	 * If we have fan faults set the Gen Fault LED.
23241341Sstevel 	 */
23251341Sstevel 	if (psfanflt == 0 && cpufanflt == 0 && afbfanflt == 0 &&
23261341Sstevel 	    unitp->num_fans_failed != 0) {
23271341Sstevel 		fpmstat = envctrl_get_fpm_status(unitp);
23281341Sstevel 		if (!(envctrl_isother_fault_led(unitp,
23291341Sstevel 		    fpmstat, 0))) {
23301341Sstevel 			fpmstat &= ~(ENVCTRL_FSP_GEN_ERR);
23311341Sstevel 		}
23321341Sstevel 		envctrl_set_fsp(unitp, &fpmstat);
23331341Sstevel 	} else if (psfanflt != 0 || cpufanflt != 0 || afbfanflt != 0) {
23341341Sstevel 		fpmstat = envctrl_get_fpm_status(unitp);
23351341Sstevel 		fpmstat |= ENVCTRL_FSP_GEN_ERR;
23361341Sstevel 		envctrl_set_fsp(unitp, &fpmstat);
23371341Sstevel 	}
23381341Sstevel 
23391341Sstevel 	if (unitp->AFB_present == B_FALSE) {
23401341Sstevel 		afbfanflt = 0;
23411341Sstevel 	}
23421341Sstevel 
23431341Sstevel 	if ((cpufanflt > 0 || psfanflt > 0 || afbfanflt > 0 ||
2344*7656SSherry.Moore@Sun.COM 	    (status != DDI_SUCCESS)) && !unitp->initting &&
2345*7656SSherry.Moore@Sun.COM 	    unitp->current_mode == ENVCTRL_NORMAL_MODE) {
23461341Sstevel 		if (status != DDI_SUCCESS)
23471341Sstevel 			max_retry_count = envctrl_max_retries;
23481341Sstevel 		else
23491341Sstevel 			max_retry_count = MAX_FAN_FAIL_RETRY;
23501341Sstevel 		if (retries <= max_retry_count) {
23511341Sstevel 			retries++;
23521341Sstevel 			drv_usecwait(1000);
23531341Sstevel 			if (retries == max_retry_count) {
23541341Sstevel 				cmn_err(CE_WARN,
23551341Sstevel 				    "Fan Fail is 0x%x, retries = %d\n",
23561341Sstevel 				    recv_data, retries);
23571341Sstevel 			}
23581341Sstevel 			envctrl_get_sys_temperatures(unitp,
23591341Sstevel 			    (uint8_t *)NULL);
23601341Sstevel 			goto retry;
23611341Sstevel 		}
23621341Sstevel 		if (!(envctrl_power_off_overide)) {
23631341Sstevel 			unitp->shutdown = B_TRUE;
23641341Sstevel 		}
23651341Sstevel 		cmn_err(CE_WARN, "Fan Failure(s), System Shutdown");
23661341Sstevel 	}
23671341Sstevel 
23681341Sstevel 	unitp->num_fans_failed = (psfanflt + cpufanflt + afbfanflt);
23691341Sstevel 
23701341Sstevel }
23711341Sstevel 
23721341Sstevel /*
23731341Sstevel  * Check for power supply insertion and failure.
23741341Sstevel  * This is a bit tricky, because a power supply insertion will
23751341Sstevel  * trigger a load share interrupt as well as PS present in the
23761341Sstevel  * new supply. if we detect an insertion clear
23771341Sstevel  * interrupts, disable interrupts, wait for a couple of seconds
23781341Sstevel  * come back and see if the PSOK bit is set, PS_PRESENT is set
23791341Sstevel  * and the share fail interrupts are gone. If not this is a
23801341Sstevel  * real load share fail event.
23811341Sstevel  * Called with mutex held
23821341Sstevel  */
23831341Sstevel 
23841341Sstevel static void
envctrl_PS_intr_service(struct envctrlunit * unitp,uint8_t psaddr)23851341Sstevel envctrl_PS_intr_service(struct envctrlunit *unitp, uint8_t psaddr)
23861341Sstevel {
23871341Sstevel 	uint8_t recv_data;
23881341Sstevel 	int status, retrys = 0;
23891341Sstevel 
23901341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
23911341Sstevel 
23921341Sstevel 	if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
23931341Sstevel 		return;
23941341Sstevel 	}
23951341Sstevel 
23961341Sstevel retry:
23971341Sstevel 	status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
2398*7656SSherry.Moore@Sun.COM 	    PCF8574A_BASE_ADDR | psaddr & 0xF, &recv_data, 1);
23991341Sstevel 	if (status != DDI_SUCCESS) {
24001341Sstevel 		drv_usecwait(1000);
24011341Sstevel 		if (retrys < envctrl_max_retries) {
24021341Sstevel 			retrys++;
24031341Sstevel 			goto retry;
24041341Sstevel 		} else {
24051341Sstevel 			mutex_exit(&unitp->umutex);
24061341Sstevel 			envctrl_init_bus(unitp);
24071341Sstevel 			mutex_enter(&unitp->umutex);
24081341Sstevel 			if (envctrl_debug_flags)
24091341Sstevel 				cmn_err(CE_WARN,
2410*7656SSherry.Moore@Sun.COM 				    "PS_intr_service: Read from 8574A " \
2411*7656SSherry.Moore@Sun.COM 				"failed\n");
24121341Sstevel 		}
24131341Sstevel 	}
24141341Sstevel 
24151341Sstevel 	/*
24161341Sstevel 	 * setup a timeout thread to poll the ps after a
24171341Sstevel 	 * couple of seconds. This allows for the PS to settle
24181341Sstevel 	 * and doesn't report false errors on a hotplug
24191341Sstevel 	 */
24201341Sstevel 
24211341Sstevel 	unitp->pshotplug_id = (timeout(envctrl_pshotplug_poll,
24221341Sstevel 	    (caddr_t)unitp, pshotplug_timeout_hz));
24231341Sstevel 
24241341Sstevel }
24251341Sstevel 
24261341Sstevel /* called with mutex held */
24271341Sstevel static void
envctrl_reset_dflop(struct envctrlunit * unitp)24281341Sstevel envctrl_reset_dflop(struct envctrlunit *unitp)
24291341Sstevel {
24301341Sstevel 	struct envctrl_pcf8574_chip initval;
24311341Sstevel 
24321341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
24331341Sstevel 
24341341Sstevel 	/*
24351341Sstevel 	 * This initialization sequence allows a
24361341Sstevel 	 * to change state to stop the fans from
24371341Sstevel 	 * blastion upon poweron. If this isn't
24381341Sstevel 	 * done the writes to the 8444 will not complete
24391341Sstevel 	 * to the hardware because the dflop will
24401341Sstevel 	 * be closed
24411341Sstevel 	 */
24421341Sstevel 	initval.chip_num = ENVCTRL_PCF8574_DEV0; /* 0x01 port 1 */
24431341Sstevel 	initval.type = PCF8574A;
24441341Sstevel 
24451341Sstevel 	initval.val = ENVCTRL_DFLOP_INIT0;
24461341Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)&initval, PCF8574);
24471341Sstevel 
24481341Sstevel 	initval.val = ENVCTRL_DFLOP_INIT1;
24491341Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)&initval, PCF8574);
24501341Sstevel }
24511341Sstevel 
24521341Sstevel static void
envctrl_add_encl_kstats(struct envctrlunit * unitp,int type,int instance,uint8_t val)24531341Sstevel envctrl_add_encl_kstats(struct envctrlunit *unitp, int type,
24541341Sstevel     int instance, uint8_t val)
24551341Sstevel {
24561341Sstevel 	int i = 0;
24571341Sstevel 	boolean_t inserted = B_FALSE;
24581341Sstevel 
24591341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
24601341Sstevel 
24611341Sstevel 	while (i < MAX_DEVS && inserted == B_FALSE) {
24621341Sstevel 		if (unitp->encl_kstats[i].instance == I2C_NODEV) {
24631341Sstevel 			unitp->encl_kstats[i].instance = instance;
24641341Sstevel 			unitp->encl_kstats[i].type = type;
24651341Sstevel 			unitp->encl_kstats[i].value = val;
24661341Sstevel 			inserted = B_TRUE;
24671341Sstevel 		}
24681341Sstevel 		i++;
24691341Sstevel 	}
24701341Sstevel 	unitp->num_encl_present++;
24711341Sstevel }
24721341Sstevel 
24731341Sstevel /* called with mutex held */
24741341Sstevel static void
envctrl_enable_devintrs(struct envctrlunit * unitp)24751341Sstevel envctrl_enable_devintrs(struct envctrlunit *unitp)
24761341Sstevel {
24771341Sstevel 	struct envctrl_pcf8574_chip initval;
24781341Sstevel 
24791341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
24801341Sstevel 
24811341Sstevel 	/*
24821341Sstevel 	 * This initialization sequence allows a
24831341Sstevel 	 * to change state to stop the fans from
24841341Sstevel 	 * blastion upon poweron. If this isn't
24851341Sstevel 	 * done the writes to the 8444 will not complete
24861341Sstevel 	 * to the hardware because the dflop will
24871341Sstevel 	 * be closed
24881341Sstevel 	 */
24891341Sstevel 	initval.chip_num = ENVCTRL_PCF8574_DEV0; /* 0x01 port 1 */
24901341Sstevel 	initval.type = PCF8574A;
24911341Sstevel 
24921341Sstevel 	initval.val = ENVCTRL_DEVINTR_INTI0;
24931341Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)&initval, PCF8574);
24941341Sstevel 
24951341Sstevel 	/*
24961341Sstevel 	 * set lowerbits all high p0 = PS1, p1 = PS2
24971341Sstevel 	 * p2 = PS3 p4 = envctrl intr_ctrl
24981341Sstevel 	 */
24991341Sstevel 	initval.val = ENVCTRL_DEVINTR_INTI1;
25001341Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)&initval, PCF8574);
25011341Sstevel }
25021341Sstevel 
25031341Sstevel /* called with mutex held */
25041341Sstevel static void
envctrl_stop_clock(struct envctrlunit * unitp)25051341Sstevel envctrl_stop_clock(struct envctrlunit *unitp)
25061341Sstevel {
25071341Sstevel 	int status;
25081341Sstevel 	uint8_t buf[2];
25091341Sstevel 
25101341Sstevel 	/*
25111341Sstevel 	 * This routine talks to the PCF8583 which
25121341Sstevel 	 * is a clock calendar chip on the envctrl bus.
25131341Sstevel 	 * We use this chip as a watchdog timer for the
25141341Sstevel 	 * fan control. At reset this chip pulses the interrupt
25151341Sstevel 	 * line every 1 second. We need to be able to shut
25161341Sstevel 	 * this off.
25171341Sstevel 	 */
25181341Sstevel 
25191341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
25201341Sstevel 
25211341Sstevel 	buf[0] = CLOCK_CSR_REG;
25221341Sstevel 	buf[1] = CLOCK_DISABLE;
25231341Sstevel 
25241341Sstevel 	status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
2525*7656SSherry.Moore@Sun.COM 	    PCF8583_BASE_ADDR | 0, buf, 2);
25261341Sstevel 	if (status != DDI_SUCCESS)
25271341Sstevel 		cmn_err(CE_WARN, "write to PCF8583 failed\n");
25281341Sstevel }
25291341Sstevel 
25301341Sstevel static void
envctrl_reset_watchdog(struct envctrlunit * unitp,uint8_t * wdval)25311341Sstevel envctrl_reset_watchdog(struct envctrlunit *unitp, uint8_t *wdval)
25321341Sstevel {
25331341Sstevel 
25341341Sstevel 	uint8_t w, r;
25351341Sstevel 	uint8_t res = 0;
25361341Sstevel 	int status;
25371341Sstevel 	uint8_t buf[3];
25381341Sstevel 
25391341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
25401341Sstevel 
25411341Sstevel 	/* the clock MUST be stopped before we re-set it */
25421341Sstevel 	envctrl_stop_clock(unitp);
25431341Sstevel 
25441341Sstevel 	/*
25451341Sstevel 	 * Reset the minutes counter to 0.
25461341Sstevel 	 */
25471341Sstevel 	buf[0] = ALARM_CTR_REG_MINS;
25481341Sstevel 	buf[1] = 0x0;
25491341Sstevel 	status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
2550*7656SSherry.Moore@Sun.COM 	    PCF8583_BASE_ADDR | 0, buf, 2);
25511341Sstevel 	if (status != DDI_SUCCESS)
25521341Sstevel 		cmn_err(CE_WARN, "write to PCF8583 failed\n");
25531341Sstevel 
25541341Sstevel 	/*
25551341Sstevel 	 * set up the alarm timer for 3 minutes
25561341Sstevel 	 * start by setting reg 8 ALARM_CTRL_REG
25571341Sstevel 	 * If we are in diag mode, we set the timer in
25581341Sstevel 	 * seconds. Valid values are 40-99. The timer
25591341Sstevel 	 * counts up to 99. 40 would be 59 seconds
25601341Sstevel 	 */
25611341Sstevel 	buf[0] = CLOCK_ALARM_REG_A;
25621341Sstevel 	if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
25631341Sstevel 		if (unitp->timeout_id != 0) {
25641341Sstevel 			(void) untimeout(unitp->timeout_id);
25651341Sstevel 			unitp->timeout_id = 0;
25661341Sstevel 			unitp->timeout_id = (timeout(envctrl_tempr_poll,
25671341Sstevel 			    (caddr_t)unitp, overtemp_timeout_hz));
25681341Sstevel 		}
25691341Sstevel 		buf[1] = CLOCK_ENABLE_TIMER_S;
25701341Sstevel 	} else {
25711341Sstevel 		buf[1] = CLOCK_ENABLE_TIMER;
25721341Sstevel 	}
25731341Sstevel 
25741341Sstevel 	/* STEP 10: End Transmission */
25751341Sstevel 	status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
2576*7656SSherry.Moore@Sun.COM 	    PCF8583_BASE_ADDR | 0, buf, 2);
25771341Sstevel 	if (status != DDI_SUCCESS)
25781341Sstevel 		cmn_err(CE_WARN, "Reset envctrl watchdog failed\n");
25791341Sstevel 
25801341Sstevel 	/*
25811341Sstevel 	 * Now set up the alarm timer register it
25821341Sstevel 	 * counts from 0-99 with an intr triggered
25831341Sstevel 	 * when it gets to overflow.. or 99. It will
25841341Sstevel 	 * also count from a pre-set value which is
25851341Sstevel 	 * where we are seting from. We want a 3 minute fail
25861341Sstevel 	 * safe so our value is 99-3 or 96.
25871341Sstevel 	 * we are programming register 7 in the 8583.
25881341Sstevel 	 */
25891341Sstevel 
25901341Sstevel 	buf[0] = ALARM_CTRL_REG;
25911341Sstevel 	/*
25921341Sstevel 	 * Allow the diagnostic to set the egg timer val.
25931341Sstevel 	 * never allow it to be set greater than the default.
25941341Sstevel 	 */
25951341Sstevel 	if (unitp->current_mode == ENVCTRL_DIAG_MODE) {
25961341Sstevel 		if (*wdval > MAX_CL_VAL) {
25971341Sstevel 			buf[1] = EGG_TIMER_VAL;
25981341Sstevel 		} else {
25991341Sstevel 
26001341Sstevel 			w = *wdval/10;
26011341Sstevel 			r = *wdval%10;
26021341Sstevel 
26031341Sstevel 			res = res | r;
26041341Sstevel 			res = (0x99 - (res | (w << 4)));
26051341Sstevel 			buf[1] = res;
26061341Sstevel 		}
26071341Sstevel 	} else {
26081341Sstevel 		buf[1] = EGG_TIMER_VAL;
26091341Sstevel 	}
26101341Sstevel 
26111341Sstevel 	status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
2612*7656SSherry.Moore@Sun.COM 	    PCF8583_BASE_ADDR | 0, buf, 2);
26131341Sstevel 	if (status != DDI_SUCCESS)
26141341Sstevel 		cmn_err(CE_WARN, "Reset envctrl watchdog failed\n");
26151341Sstevel 
26161341Sstevel 
26171341Sstevel 	/*
26181341Sstevel 	 * Now that we have set up.. it is time
26191341Sstevel 	 * to re-start the clock in the CSR.
26201341Sstevel 	 */
26211341Sstevel 
26221341Sstevel 	buf[0] = CLOCK_CSR_REG;
26231341Sstevel 	buf[1] = CLOCK_ENABLE;
26241341Sstevel 	status = eHc_write_pcf8583((struct eHc_envcunit *)unitp,
2625*7656SSherry.Moore@Sun.COM 	    PCF8583_BASE_ADDR | 0, buf, 2);
26261341Sstevel 	if (status != DDI_SUCCESS)
26271341Sstevel 		cmn_err(CE_WARN, "Reset envctrl watchdog failed\n");
26281341Sstevel 
26291341Sstevel }
26301341Sstevel 
26311341Sstevel /* Called with unip mutex held */
26321341Sstevel static void
envctrl_ps_probe(struct envctrlunit * unitp)26331341Sstevel envctrl_ps_probe(struct envctrlunit *unitp)
26341341Sstevel {
26351341Sstevel 
26361341Sstevel 	uint8_t recv_data, fpmstat;
26371341Sstevel 	uint8_t psaddr[] = {PS1, PS2, PS3, PSTEMP0};
26381341Sstevel 	int i;
26391341Sstevel 	int ps_error = 0, retrys = 0;
26401341Sstevel 	int devaddr;
26411341Sstevel 	int status;
26421341Sstevel 	int twotimes = 0;
26431341Sstevel 
26441341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
26451341Sstevel 
26461341Sstevel 	unitp->num_ps_present = 0;
26471341Sstevel 
26481341Sstevel 	for (i = 0; i <= MAXPS; i++) {
26491341Sstevel 		unitp->ps_present[i] = B_FALSE;
26501341Sstevel 		unitp->ps_kstats[i].ps_rating = 0;
26511341Sstevel 		unitp->ps_kstats[i].ps_tempr = 0;
26521341Sstevel 
26531341Sstevel 		switch (psaddr[i]) {
26541341Sstevel 		case PS1:
26551341Sstevel 			devaddr = ENVCTRL_PCF8574_DEV3;
26561341Sstevel 			break;
26571341Sstevel 		case PS2:
26581341Sstevel 			devaddr = ENVCTRL_PCF8574_DEV2;
26591341Sstevel 			break;
26601341Sstevel 		case PS3:
26611341Sstevel 			devaddr = ENVCTRL_PCF8574_DEV1;
26621341Sstevel 			break;
26631341Sstevel 		case PSTEMP0:
26641341Sstevel 			devaddr = 0;
26651341Sstevel 			break;
26661341Sstevel 		}
26671341Sstevel 		retrys = 0;
26681341Sstevel retry:
26691341Sstevel 		status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
2670*7656SSherry.Moore@Sun.COM 		    PCF8574A_BASE_ADDR | devaddr, &recv_data, 1);
26711341Sstevel 		if (status != DDI_SUCCESS) {
26721341Sstevel 			drv_usecwait(1000);
26731341Sstevel 			if (retrys < envctrl_max_retries) {
26741341Sstevel 				retrys++;
26751341Sstevel 				goto retry;
26761341Sstevel 			} else {
26771341Sstevel 				mutex_exit(&unitp->umutex);
26781341Sstevel 				envctrl_init_bus(unitp);
26791341Sstevel 				mutex_enter(&unitp->umutex);
26801341Sstevel 				/*
26811341Sstevel 				 * If we just reset the bus we need to reread
26821341Sstevel 				 * the status.  If a second attempt still fails
26831341Sstevel 				 * then report the read failure.
26841341Sstevel 				 */
26851341Sstevel 				if (twotimes == 0) {
26861341Sstevel 					twotimes++;
26871341Sstevel 					retrys = 0;
26881341Sstevel 					goto retry;
26891341Sstevel 				} else {
26901341Sstevel 					cmn_err(CE_WARN,
26911341Sstevel 					"PS_probe: Read from 8574A failed\n");
26921341Sstevel 				}
26931341Sstevel 			}
26941341Sstevel 		}
26951341Sstevel 
26961341Sstevel 		/*
26971341Sstevel 		 * Port 0 = PS Present
26981341Sstevel 		 * Port 1 = PS Type
26991341Sstevel 		 * Port 2 = PS Type
27001341Sstevel 		 * Port 3 = PS TYpe
27011341Sstevel 		 * Port 4 = DC Status
27021341Sstevel 		 * Port 5 = Current Limit
27031341Sstevel 		 * Port 6 = Current Share
27041341Sstevel 		 * Port 7 = SPARE
27051341Sstevel 		 */
27061341Sstevel 
27071341Sstevel 		/*
27081341Sstevel 		 * Port 0 = PS Present
27091341Sstevel 		 * Port is pulled LOW "0" to indicate
27101341Sstevel 		 * present.
27111341Sstevel 		 */
27121341Sstevel 
27131341Sstevel 		if (!(recv_data & ENVCTRL_PCF8574_PORT0)) {
27141341Sstevel 			unitp->ps_present[i] = B_TRUE;
27151341Sstevel 			/* update unit kstat array */
27161341Sstevel 			unitp->ps_kstats[i].instance = i;
27171341Sstevel 			unitp->ps_kstats[i].ps_tempr = ENVCTRL_INIT_TEMPR;
27181341Sstevel 			++unitp->num_ps_present;
27191341Sstevel 
27201341Sstevel 			if (power_supply_previous_state[i] == 0) {
27211341Sstevel 				cmn_err(CE_NOTE,
27221341Sstevel 				    "Power Supply %d inserted\n", i);
27231341Sstevel 			}
27241341Sstevel 			power_supply_previous_state[i] = 1;
27251341Sstevel 
27261341Sstevel 			if (!(recv_data & ENVCTRL_PCF8574_PORT1)) {
27271341Sstevel 				unitp->ps_kstats[i].ps_rating = ENVCTRL_PS_550;
27281341Sstevel 			}
27291341Sstevel 			if (!(recv_data & ENVCTRL_PCF8574_PORT2)) {
27301341Sstevel 				unitp->ps_kstats[i].ps_rating = ENVCTRL_PS_650;
27311341Sstevel 			}
27321341Sstevel 			if (!(recv_data & ENVCTRL_PCF8574_PORT3)) {
27331341Sstevel 				cmn_err(CE_WARN,
27341341Sstevel 				    "Power Supply %d NOT okay\n", i);
27351341Sstevel 				unitp->ps_kstats[i].ps_ok = B_FALSE;
27361341Sstevel 				ps_error++;
27371341Sstevel 			} else {
27381341Sstevel 				unitp->ps_kstats[i].ps_ok = B_TRUE;
27391341Sstevel 			}
27401341Sstevel 			if (!(recv_data & ENVCTRL_PCF8574_PORT4)) {
27411341Sstevel 				cmn_err(CE_WARN,
27421341Sstevel 				    "Power Supply %d Overloaded\n", i);
27431341Sstevel 				unitp->ps_kstats[i].limit_ok = B_FALSE;
27441341Sstevel 				ps_error++;
27451341Sstevel 			} else {
27461341Sstevel 				unitp->ps_kstats[i].limit_ok = B_TRUE;
27471341Sstevel 			}
27481341Sstevel 			if (!(recv_data & ENVCTRL_PCF8574_PORT5)) {
27491341Sstevel 				cmn_err(CE_WARN,
27501341Sstevel 				    "Power Supply %d load share err\n", i);
27511341Sstevel 				unitp->ps_kstats[i].curr_share_ok = B_FALSE;
27521341Sstevel 				ps_error++;
27531341Sstevel 			} else {
27541341Sstevel 				unitp->ps_kstats[i].curr_share_ok = B_TRUE;
27551341Sstevel 			}
27561341Sstevel 
27571341Sstevel 			if (!(recv_data & ENVCTRL_PCF8574_PORT6)) {
27581341Sstevel 				cmn_err(CE_WARN,
27591341Sstevel 				    "PS %d Shouln't interrupt\n", i);
27601341Sstevel 				ps_error++;
27611341Sstevel 			}
27621341Sstevel 
27631341Sstevel 			if (!(recv_data & ENVCTRL_PCF8574_PORT7)) {
27641341Sstevel 				cmn_err(CE_WARN,
27651341Sstevel 				    "PS %d Shouln't interrupt\n", i);
27661341Sstevel 				ps_error++;
27671341Sstevel 			}
27681341Sstevel 		} else {
27691341Sstevel 			/* No power supply present */
27701341Sstevel 			if (power_supply_previous_state[i] == 1) {
27711341Sstevel 				cmn_err(CE_NOTE,
27721341Sstevel 				    "Power Supply %d removed\n", i);
27731341Sstevel 			}
27741341Sstevel 			power_supply_previous_state[i] = 0;
27751341Sstevel 		}
27761341Sstevel 	}
27771341Sstevel 
27781341Sstevel 	fpmstat = envctrl_get_fpm_status(unitp);
27791341Sstevel 	if (ps_error) {
27801341Sstevel 		fpmstat |= (ENVCTRL_FSP_PS_ERR | ENVCTRL_FSP_GEN_ERR);
27811341Sstevel 	} else {
27821341Sstevel 		if (envctrl_isother_fault_led(unitp, fpmstat,
2783*7656SSherry.Moore@Sun.COM 		    ENVCTRL_FSP_PS_ERR)) {
27841341Sstevel 			fpmstat &= ~(ENVCTRL_FSP_PS_ERR);
27851341Sstevel 		} else {
27861341Sstevel 			fpmstat &= ~(ENVCTRL_FSP_PS_ERR |
27871341Sstevel 			    ENVCTRL_FSP_GEN_ERR);
27881341Sstevel 		}
27891341Sstevel 
27901341Sstevel 	}
27911341Sstevel 	envctrl_set_fsp(unitp, &fpmstat);
27921341Sstevel 
27931341Sstevel 	/*
27941341Sstevel 	 * We need to reset all of the fans etc when a supply is
27951341Sstevel 	 * interrupted and added, but we don't want to reset the
27961341Sstevel 	 * fans if we are in DIAG mode. This will mess up SUNVTS.
27971341Sstevel 	 */
27981341Sstevel 	if (unitp->current_mode == ENVCTRL_NORMAL_MODE) {
27991341Sstevel 		envctrl_get_sys_temperatures(unitp, (uint8_t *)NULL);
28001341Sstevel 	}
28011341Sstevel }
28021341Sstevel 
28031341Sstevel /*
28041341Sstevel  * consider key switch position when handling an abort sequence
28051341Sstevel  */
28061341Sstevel static void
envctrl_abort_seq_handler(char * msg)28071341Sstevel envctrl_abort_seq_handler(char *msg)
28081341Sstevel {
28091341Sstevel 	struct envctrlunit *unitp;
28101341Sstevel 	int i;
28111341Sstevel 	uint8_t secure = 0;
28121341Sstevel 
28131341Sstevel 	/*
28141341Sstevel 	 * Find the instance of the device available on this host.
28151341Sstevel 	 * Note that there may be only one, but the instance may
28161341Sstevel 	 * not be zero.
28171341Sstevel 	 */
28181341Sstevel 	for (i = 0; i < MAX_DEVS; i++) {
28191341Sstevel 		if (unitp = (struct envctrlunit *)
2820*7656SSherry.Moore@Sun.COM 		    ddi_get_soft_state(envctrlsoft_statep, i))
28211341Sstevel 			break;
28221341Sstevel 	}
28231341Sstevel 
28241341Sstevel 	ASSERT(unitp);
28251341Sstevel 
28261341Sstevel 	for (i = 0; i < MAX_DEVS; i++) {
28271341Sstevel 		if ((unitp->encl_kstats[i].type == ENVCTRL_ENCL_FSP) &&
2828*7656SSherry.Moore@Sun.COM 		    (unitp->encl_kstats[i].instance != I2C_NODEV)) {
28291341Sstevel 			secure = unitp->encl_kstats[i].value;
28301341Sstevel 			break;
28311341Sstevel 		}
28321341Sstevel 	}
28331341Sstevel 
28341341Sstevel 	/*
28351341Sstevel 	 * take the logical not because we are in hardware mode only
28361341Sstevel 	 */
28371341Sstevel 
28381341Sstevel 	if ((secure & ENVCTRL_FSP_KEYMASK) == ENVCTRL_FSP_KEYLOCKED) {
28391341Sstevel 			cmn_err(CE_CONT,
28401341Sstevel 			    "!envctrl: ignoring debug enter sequence\n");
28411341Sstevel 	} else {
28421341Sstevel 		if (envctrl_debug_flags) {
28431341Sstevel 			cmn_err(CE_CONT, "!envctrl: allowing debug enter\n");
28441341Sstevel 		}
28451341Sstevel 		debug_enter(msg);
28461341Sstevel 	}
28471341Sstevel }
28481341Sstevel 
28491341Sstevel /*
28501341Sstevel  * get the front Panel module LED and keyswitch status.
28511341Sstevel  * this part is addressed at 0x7C on the i2c bus.
28521341Sstevel  * called with mutex held
28531341Sstevel  */
28541341Sstevel static uint8_t
envctrl_get_fpm_status(struct envctrlunit * unitp)28551341Sstevel envctrl_get_fpm_status(struct envctrlunit *unitp)
28561341Sstevel {
28571341Sstevel 	uint8_t recv_data;
28581341Sstevel 	int status, retrys = 0;
28591341Sstevel 
28601341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
28611341Sstevel 
28621341Sstevel retry:
28631341Sstevel 	status = eHc_read_pcf8574a((struct eHc_envcunit *)unitp,
2864*7656SSherry.Moore@Sun.COM 	    PCF8574A_BASE_ADDR | ENVCTRL_PCF8574_DEV6, &recv_data, 1);
28651341Sstevel 
28661341Sstevel 	/*
28671341Sstevel 	 * yet another place where a read can cause the
28681341Sstevel 	 * the SDA line of the i2c bus to get stuck low.
28691341Sstevel 	 * this funky sequence frees the SDA line.
28701341Sstevel 	 */
28711341Sstevel 	if (status != DDI_SUCCESS) {
28721341Sstevel 		drv_usecwait(1000);
28731341Sstevel 		if (retrys < envctrl_max_retries) {
28741341Sstevel 			retrys++;
28751341Sstevel 			goto retry;
28761341Sstevel 		} else {
28771341Sstevel 			mutex_exit(&unitp->umutex);
28781341Sstevel 			envctrl_init_bus(unitp);
28791341Sstevel 			mutex_enter(&unitp->umutex);
28801341Sstevel 			if (envctrl_debug_flags)
28811341Sstevel 				cmn_err(CE_WARN, "Read from PCF8574 (FPM) "\
28821341Sstevel 				    "failed\n");
28831341Sstevel 		}
28841341Sstevel 	}
28851341Sstevel 	recv_data = ~recv_data;
28861341Sstevel 	envctrl_mod_encl_kstats(unitp, ENVCTRL_ENCL_FSP,
28871341Sstevel 	    INSTANCE_0, recv_data);
28881341Sstevel 
28891341Sstevel 	return (recv_data);
28901341Sstevel }
28911341Sstevel 
28921341Sstevel static void
envctrl_set_fsp(struct envctrlunit * unitp,uint8_t * val)28931341Sstevel envctrl_set_fsp(struct envctrlunit *unitp, uint8_t *val)
28941341Sstevel {
28951341Sstevel 	struct envctrl_pcf8574_chip chip;
28961341Sstevel 
28971341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
28981341Sstevel 
28991341Sstevel 	chip.val = ENVCTRL_FSP_OFF; /* init all values to off */
29001341Sstevel 	chip.chip_num = ENVCTRL_PCF8574_DEV6; /* 0x01 port 1 */
29011341Sstevel 	chip.type = PCF8574A;
29021341Sstevel 
29031341Sstevel 	/*
29041341Sstevel 	 * strip off bits that are R/O
29051341Sstevel 	 */
29061341Sstevel 	chip.val = (~(ENVCTRL_FSP_KEYMASK | ENVCTRL_FSP_POMASK) & (*val));
29071341Sstevel 
29081341Sstevel 	chip.val = ~chip.val;
29091341Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)&chip, PCF8574);
29101341Sstevel 
29111341Sstevel }
29121341Sstevel 
29131341Sstevel static int
envctrl_get_dskled(struct envctrlunit * unitp,struct envctrl_pcf8574_chip * chip)29141341Sstevel envctrl_get_dskled(struct envctrlunit *unitp, struct envctrl_pcf8574_chip *chip)
29151341Sstevel {
29161341Sstevel 	uint_t oldtype;
29171341Sstevel 
29181341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
29191341Sstevel 
29201341Sstevel 	if (chip->chip_num > ENVCTRL_PCF8574_DEV2 ||
2921*7656SSherry.Moore@Sun.COM 	    chip->type != ENVCTRL_ENCL_BACKPLANE4 &&
2922*7656SSherry.Moore@Sun.COM 	    chip->type != ENVCTRL_ENCL_BACKPLANE8) {
29231341Sstevel 		return (DDI_FAILURE);
29241341Sstevel 	}
29251341Sstevel 	oldtype = chip->type;
29261341Sstevel 	chip->type = PCF8574;
29271341Sstevel 	envctrl_recv(unitp, (caddr_t *)(void *)chip, PCF8574);
29281341Sstevel 	chip->type = oldtype;
29291341Sstevel 	chip->val = ~chip->val;
29301341Sstevel 
29311341Sstevel 	return (DDI_SUCCESS);
29321341Sstevel }
29331341Sstevel static int
envctrl_set_dskled(struct envctrlunit * unitp,struct envctrl_pcf8574_chip * chip)29341341Sstevel envctrl_set_dskled(struct envctrlunit *unitp, struct envctrl_pcf8574_chip *chip)
29351341Sstevel {
29361341Sstevel 
29371341Sstevel 	struct envctrl_pcf8574_chip fspchip;
29381341Sstevel 	struct envctrl_pcf8574_chip backchip;
29391341Sstevel 	int i, instance;
29401341Sstevel 	int diskfault = 0;
29411341Sstevel 	uint8_t controller_addr[] = {ENVCTRL_PCF8574_DEV0, ENVCTRL_PCF8574_DEV1,
29421341Sstevel 	    ENVCTRL_PCF8574_DEV2};
29431341Sstevel 
29441341Sstevel 	/*
29451341Sstevel 	 * We need to check the type of disk led being set. If it
29461341Sstevel 	 * is a 4 slot backplane then the upper 4 bits (7, 6, 5, 4) are
29471341Sstevel 	 * invalid.
29481341Sstevel 	 */
29491341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
29501341Sstevel 
29511341Sstevel 
29521341Sstevel 	if (chip->chip_num > ENVCTRL_PCF8574_DEV2 ||
2953*7656SSherry.Moore@Sun.COM 	    chip->val > ENVCTRL_DISK8LED_ALLOFF ||
2954*7656SSherry.Moore@Sun.COM 	    chip->val < ENVCTRL_CHAR_ZERO) {
29551341Sstevel 		return (DDI_FAILURE);
29561341Sstevel 	}
29571341Sstevel 
29581341Sstevel 	if (chip->type != ENVCTRL_ENCL_BACKPLANE4 &&
29591341Sstevel 	    chip->type != ENVCTRL_ENCL_BACKPLANE8) {
29601341Sstevel 		return (DDI_FAILURE);
29611341Sstevel 	}
29621341Sstevel 
29631341Sstevel 	/*
29641341Sstevel 	 * Check all of the other controllwes LED states to make sure
29651341Sstevel 	 * that there are no disk faults. If so then if the user is
29661341Sstevel 	 * clearing the disk faults on this contoller, turn off
29671341Sstevel 	 * the mass storage fault led.
29681341Sstevel 	 */
29691341Sstevel 
29701341Sstevel 	backchip.type = PCF8574;
29711341Sstevel 	for (i = 0; i <= MAX_TAZ_CONTROLLERS; i++) {
29721341Sstevel 		if (controller_present[i] == -1)
29731341Sstevel 			continue;
29741341Sstevel 		backchip.chip_num = controller_addr[i];
29751341Sstevel 		envctrl_recv(unitp, (caddr_t *)(void *)&backchip, PCF8574);
29761341Sstevel 		if (chip->chip_num == controller_addr[i]) {
29771341Sstevel 			if (chip->val != ENVCTRL_CHAR_ZERO)
29781341Sstevel 				diskfault++;
29791341Sstevel 		} else if ((~backchip.val & 0xFF) != ENVCTRL_CHAR_ZERO) {
29801341Sstevel 			diskfault++;
29811341Sstevel 		}
29821341Sstevel 	}
29831341Sstevel 
29841341Sstevel 	fspchip.type = PCF8574A;
29851341Sstevel 	fspchip.chip_num = ENVCTRL_PCF8574_DEV6; /* 0x01 port 1 */
29861341Sstevel 	envctrl_recv(unitp, (caddr_t *)(void *)&fspchip, PCF8574);
29871341Sstevel 
29881341Sstevel 	if (diskfault) {
29891341Sstevel 		if (!(envctrl_isother_fault_led(unitp, fspchip.val & 0xFF,
2990*7656SSherry.Moore@Sun.COM 		    ENVCTRL_FSP_DISK_ERR))) {
29911341Sstevel 			fspchip.val &= ~(ENVCTRL_FSP_DISK_ERR);
29921341Sstevel 		} else {
29931341Sstevel 			fspchip.val &= ~(ENVCTRL_FSP_DISK_ERR |
29941341Sstevel 			    ENVCTRL_FSP_GEN_ERR);
29951341Sstevel 		}
29961341Sstevel 		fspchip.val = (fspchip.val &
29971341Sstevel 		    ~(ENVCTRL_FSP_DISK_ERR | ENVCTRL_FSP_GEN_ERR));
29981341Sstevel 	} else {
29991341Sstevel 		fspchip.val = (fspchip.val |
30001341Sstevel 		    (ENVCTRL_FSP_DISK_ERR | ENVCTRL_FSP_GEN_ERR));
30011341Sstevel 	}
30021341Sstevel 	fspchip.type = PCF8574A;
30031341Sstevel 	fspchip.chip_num = ENVCTRL_PCF8574_DEV6; /* 0x01 port 1 */
30041341Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)&fspchip, PCF8574);
30051341Sstevel 
30061341Sstevel 	for (i = 0; i < (sizeof (backaddrs) / sizeof (uint8_t)); i++) {
30071341Sstevel 		if (chip->chip_num == backaddrs[i]) {
30081341Sstevel 			instance =  i;
30091341Sstevel 		}
30101341Sstevel 	}
30111341Sstevel 
30121341Sstevel 	switch (chip->type) {
30131341Sstevel 	case ENVCTRL_ENCL_BACKPLANE4:
30141341Sstevel 		envctrl_mod_encl_kstats(unitp, ENVCTRL_ENCL_BACKPLANE4,
30151341Sstevel 		    instance, chip->val);
30161341Sstevel 		break;
30171341Sstevel 	case ENVCTRL_ENCL_BACKPLANE8:
30181341Sstevel 		envctrl_mod_encl_kstats(unitp, ENVCTRL_ENCL_BACKPLANE8,
30191341Sstevel 		    instance, chip->val);
30201341Sstevel 		break;
30211341Sstevel 	default:
30221341Sstevel 		break;
30231341Sstevel 	}
30241341Sstevel 	chip->type = PCF8574;
30251341Sstevel 	/*
30261341Sstevel 	 * we take the ones compliment of the val passed in
30271341Sstevel 	 * because the hardware thinks that a "low" or "0"
30281341Sstevel 	 * is the way to indicate a fault. of course software
30291341Sstevel 	 * knows that a 1 is a TRUE state or fault. ;-)
30301341Sstevel 	 */
30311341Sstevel 	chip->val = ~(chip->val);
30321341Sstevel 	(void) envctrl_xmit(unitp, (caddr_t *)(void *)chip, PCF8574);
30331341Sstevel 	return (DDI_SUCCESS);
30341341Sstevel }
30351341Sstevel 
30361341Sstevel void
envctrl_add_kstats(struct envctrlunit * unitp)30371341Sstevel envctrl_add_kstats(struct envctrlunit *unitp)
30381341Sstevel {
30391341Sstevel 
30401341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
30411341Sstevel 
30421341Sstevel 	if ((unitp->enclksp = kstat_create(ENVCTRL_MODULE_NAME, unitp->instance,
30431341Sstevel 	    ENVCTRL_KSTAT_ENCL, "misc", KSTAT_TYPE_RAW,
30441341Sstevel 	    sizeof (unitp->encl_kstats),
30451341Sstevel 	    KSTAT_FLAG_PERSISTENT)) == NULL) {
30461341Sstevel 		cmn_err(CE_WARN, "envctrl%d: encl raw kstat_create failed",
3047*7656SSherry.Moore@Sun.COM 		    unitp->instance);
30481341Sstevel 		return;
30491341Sstevel 	}
30501341Sstevel 
30511341Sstevel 	unitp->enclksp->ks_update = envctrl_encl_kstat_update;
30521341Sstevel 	unitp->enclksp->ks_private = (void *)unitp;
30531341Sstevel 	kstat_install(unitp->enclksp);
30541341Sstevel 
30551341Sstevel 
30561341Sstevel 	if ((unitp->fanksp = kstat_create(ENVCTRL_MODULE_NAME, unitp->instance,
30571341Sstevel 	    ENVCTRL_KSTAT_FANSTAT, "misc", KSTAT_TYPE_RAW,
30581341Sstevel 	    sizeof (unitp->fan_kstats),
30591341Sstevel 	    KSTAT_FLAG_PERSISTENT)) == NULL) {
30601341Sstevel 		cmn_err(CE_WARN, "envctrl%d: fans kstat_create failed",
3061*7656SSherry.Moore@Sun.COM 		    unitp->instance);
30621341Sstevel 		return;
30631341Sstevel 	}
30641341Sstevel 
30651341Sstevel 	unitp->fanksp->ks_update = envctrl_fanstat_kstat_update;
30661341Sstevel 	unitp->fanksp->ks_private = (void *)unitp;
30671341Sstevel 	kstat_install(unitp->fanksp);
30681341Sstevel 
30691341Sstevel 	if ((unitp->psksp = kstat_create(ENVCTRL_MODULE_NAME, unitp->instance,
30701341Sstevel 	    ENVCTRL_KSTAT_PSNAME, "misc", KSTAT_TYPE_RAW,
30711341Sstevel 	    sizeof (unitp->ps_kstats),
30721341Sstevel 	    KSTAT_FLAG_PERSISTENT)) == NULL) {
30731341Sstevel 		cmn_err(CE_WARN, "envctrl%d: ps name kstat_create failed",
3074*7656SSherry.Moore@Sun.COM 		    unitp->instance);
30751341Sstevel 		return;
30761341Sstevel 	}
30771341Sstevel 
30781341Sstevel 	unitp->psksp->ks_update = envctrl_ps_kstat_update;
30791341Sstevel 	unitp->psksp->ks_private = (void *)unitp;
30801341Sstevel 	kstat_install(unitp->psksp);
30811341Sstevel 
30821341Sstevel }
30831341Sstevel 
30841341Sstevel int
envctrl_ps_kstat_update(kstat_t * ksp,int rw)30851341Sstevel envctrl_ps_kstat_update(kstat_t *ksp, int rw)
30861341Sstevel {
30871341Sstevel 	struct envctrlunit *unitp;
30881341Sstevel 	char *kstatp;
30891341Sstevel 
30901341Sstevel 
30911341Sstevel 
30921341Sstevel 	unitp = (struct envctrlunit *)ksp->ks_private;
30931341Sstevel 
30941341Sstevel 	mutex_enter(&unitp->umutex);
30951341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
30961341Sstevel 
30971341Sstevel 	kstatp = (char *)ksp->ks_data;
30981341Sstevel 
30991341Sstevel 	if (rw == KSTAT_WRITE) {
31001341Sstevel 		return (EACCES);
31011341Sstevel 	} else {
31021341Sstevel 
31031341Sstevel 		unitp->psksp->ks_ndata = unitp->num_ps_present;
31041341Sstevel 		bcopy(&unitp->ps_kstats, kstatp, sizeof (unitp->ps_kstats));
31051341Sstevel 	}
31061341Sstevel 	mutex_exit(&unitp->umutex);
31071341Sstevel 	return (DDI_SUCCESS);
31081341Sstevel }
31091341Sstevel int
envctrl_fanstat_kstat_update(kstat_t * ksp,int rw)31101341Sstevel envctrl_fanstat_kstat_update(kstat_t *ksp, int rw)
31111341Sstevel {
31121341Sstevel 	struct envctrlunit *unitp;
31131341Sstevel 	char *kstatp;
31141341Sstevel 
31151341Sstevel 	kstatp = (char *)ksp->ks_data;
31161341Sstevel 	unitp = (struct envctrlunit *)ksp->ks_private;
31171341Sstevel 
31181341Sstevel 	mutex_enter(&unitp->umutex);
31191341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
31201341Sstevel 
31211341Sstevel 	if (rw == KSTAT_WRITE) {
31221341Sstevel 		return (EACCES);
31231341Sstevel 	} else {
31241341Sstevel 		unitp->fanksp->ks_ndata = unitp->num_fans_present;
31251341Sstevel 		bcopy(unitp->fan_kstats, kstatp, sizeof (unitp->fan_kstats));
31261341Sstevel 	}
31271341Sstevel 	mutex_exit(&unitp->umutex);
31281341Sstevel 	return (DDI_SUCCESS);
31291341Sstevel }
31301341Sstevel 
31311341Sstevel int
envctrl_encl_kstat_update(kstat_t * ksp,int rw)31321341Sstevel envctrl_encl_kstat_update(kstat_t *ksp, int rw)
31331341Sstevel {
31341341Sstevel 	struct envctrlunit *unitp;
31351341Sstevel 	char *kstatp;
31361341Sstevel 
31371341Sstevel 
31381341Sstevel 	kstatp = (char *)ksp->ks_data;
31391341Sstevel 	unitp = (struct envctrlunit *)ksp->ks_private;
31401341Sstevel 
31411341Sstevel 	mutex_enter(&unitp->umutex);
31421341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
31431341Sstevel 
31441341Sstevel 	if (rw == KSTAT_WRITE) {
31451341Sstevel 		return (EACCES);
31461341Sstevel 	} else {
31471341Sstevel 
31481341Sstevel 		unitp->enclksp->ks_ndata = unitp->num_encl_present;
31491341Sstevel 		(void) envctrl_get_fpm_status(unitp);
31501341Sstevel 		/* XXX Need to ad disk updates too ??? */
31511341Sstevel 		bcopy(unitp->encl_kstats, kstatp, sizeof (unitp->encl_kstats));
31521341Sstevel 	}
31531341Sstevel 	mutex_exit(&unitp->umutex);
31541341Sstevel 	return (DDI_SUCCESS);
31551341Sstevel }
31561341Sstevel 
31571341Sstevel /*
31581341Sstevel  * called with unitp lock held
31591341Sstevel  * type, fanspeed and fanflt will be set by the service routines
31601341Sstevel  */
31611341Sstevel static void
envctrl_init_fan_kstats(struct envctrlunit * unitp)31621341Sstevel envctrl_init_fan_kstats(struct envctrlunit *unitp)
31631341Sstevel {
31641341Sstevel 	int i;
31651341Sstevel 
31661341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
31671341Sstevel 
31681341Sstevel 	for (i = 0; i < unitp->num_fans_present; i++) {
31691341Sstevel 		unitp->fan_kstats[i].instance = 0;
31701341Sstevel 		unitp->fan_kstats[i].type = 0;
31711341Sstevel 		unitp->fan_kstats[i].fans_ok = B_TRUE;
31721341Sstevel 		unitp->fan_kstats[i].fanflt_num = B_FALSE;
31731341Sstevel 		unitp->fan_kstats[i].fanspeed = B_FALSE;
31741341Sstevel 	}
31751341Sstevel 
31761341Sstevel 	unitp->fan_kstats[ENVCTRL_FAN_TYPE_PS].type = ENVCTRL_FAN_TYPE_PS;
31771341Sstevel 	unitp->fan_kstats[ENVCTRL_FAN_TYPE_CPU].type = ENVCTRL_FAN_TYPE_CPU;
31781341Sstevel 	if (unitp->AFB_present == B_TRUE)
31791341Sstevel 		unitp->fan_kstats[ENVCTRL_FAN_TYPE_AFB].type =
3180*7656SSherry.Moore@Sun.COM 		    ENVCTRL_FAN_TYPE_AFB;
31811341Sstevel }
31821341Sstevel 
31831341Sstevel static void
envctrl_init_encl_kstats(struct envctrlunit * unitp)31841341Sstevel envctrl_init_encl_kstats(struct envctrlunit *unitp)
31851341Sstevel {
31861341Sstevel 
31871341Sstevel 	int i;
31881341Sstevel 	uint8_t val;
31891341Sstevel 	struct envctrl_pcf8574_chip chip;
31901341Sstevel 	int *reg_prop;
31911341Sstevel 	uint_t len = 0;
31921341Sstevel 
31931341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
31941341Sstevel 
31951341Sstevel 	for (i = 0; i < MAX_DEVS; i++) {
31961341Sstevel 		unitp->encl_kstats[i].instance = I2C_NODEV;
31971341Sstevel 	}
31981341Sstevel 
31991341Sstevel 	/*
32001341Sstevel 	 * add in kstats now
32011341Sstevel 	 * We ALWAYS HAVE THE FOLLOWING
32021341Sstevel 	 * 1. FSP
32031341Sstevel 	 * 2. AMB TEMPR
32041341Sstevel 	 * 3. (1) CPU TEMPR
32051341Sstevel 	 * 4. (1) 4 slot disk backplane
32061341Sstevel 	 * OPTIONAL
32071341Sstevel 	 * 8 slot backplane
32081341Sstevel 	 * more cpu's
32091341Sstevel 	 */
32101341Sstevel 
32111341Sstevel 	chip.type = PCF8574A;
32121341Sstevel 	chip.chip_num = ENVCTRL_PCF8574_DEV6; /* 0x01 port 1 */
32131341Sstevel 	envctrl_recv(unitp, (caddr_t *)(void *)&chip, PCF8574);
32141341Sstevel 
32151341Sstevel 	envctrl_add_encl_kstats(unitp, ENVCTRL_ENCL_FSP, INSTANCE_0,
3216*7656SSherry.Moore@Sun.COM 	    chip.val & 0xFF);
32171341Sstevel 
32181341Sstevel 	val = envctrl_get_lm75_temp(unitp) & 0xFF;
32191341Sstevel 	envctrl_add_encl_kstats(unitp, ENVCTRL_ENCL_AMBTEMPR, INSTANCE_0, val);
32201341Sstevel 
32211341Sstevel 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, unitp->dip,
3222*7656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, ENVCTRL_DISK_LEDS_PR,
3223*7656SSherry.Moore@Sun.COM 	    &reg_prop, &len) != DDI_PROP_SUCCESS) {
32241341Sstevel 		cmn_err(CE_WARN, "prop lookup of %s failed\n",
3225*7656SSherry.Moore@Sun.COM 		    ENVCTRL_DISK_LEDS_PR);
32261341Sstevel 		return;
32271341Sstevel 	}
32281341Sstevel 
32291341Sstevel 	ASSERT(len != 0);
32301341Sstevel 
32311341Sstevel 	chip.type = PCF8574;
32321341Sstevel 
32331341Sstevel 	for (i = 0; i < len; i++) {
32341341Sstevel 		chip.chip_num = backaddrs[i];
32351341Sstevel 		if (reg_prop[i] == ENVCTRL_4SLOT_BACKPLANE) {
32361341Sstevel 			envctrl_recv(unitp, (caddr_t *)(void *)&chip, PCF8574);
32371341Sstevel 			envctrl_add_encl_kstats(unitp, ENVCTRL_ENCL_BACKPLANE4,
32381341Sstevel 			    i, ~chip.val);
32391341Sstevel 			controller_present[i] = 1;
32401341Sstevel 		}
32411341Sstevel 		if (reg_prop[i] == ENVCTRL_8SLOT_BACKPLANE) {
32421341Sstevel 			envctrl_recv(unitp, (caddr_t *)(void *)&chip, PCF8574);
32431341Sstevel 			envctrl_add_encl_kstats(unitp, ENVCTRL_ENCL_BACKPLANE8,
32441341Sstevel 			    i, ~chip.val);
32451341Sstevel 			controller_present[i] = 1;
32461341Sstevel 		}
32471341Sstevel 	}
32481341Sstevel 	ddi_prop_free((void *)reg_prop);
32491341Sstevel 
32501341Sstevel }
32511341Sstevel 
32521341Sstevel static void
envctrl_mod_encl_kstats(struct envctrlunit * unitp,int type,int instance,uint8_t val)32531341Sstevel envctrl_mod_encl_kstats(struct envctrlunit *unitp, int type,
32541341Sstevel     int instance, uint8_t val)
32551341Sstevel {
32561341Sstevel 	int i = 0;
32571341Sstevel 	boolean_t inserted = B_FALSE;
32581341Sstevel 
32591341Sstevel 	ASSERT(MUTEX_HELD(&unitp->umutex));
32601341Sstevel 
32611341Sstevel 	while (i < MAX_DEVS && inserted == B_FALSE) {
32621341Sstevel 		if (unitp->encl_kstats[i].instance == instance &&
32631341Sstevel 		    unitp->encl_kstats[i].type == type) {
32641341Sstevel 			unitp->encl_kstats[i].value = val;
32651341Sstevel 			inserted = B_TRUE;
32661341Sstevel 		}
32671341Sstevel 		i++;
32681341Sstevel 	}
32691341Sstevel }
32701341Sstevel 
32711341Sstevel static void
envctrl_probe_cpus(struct envctrlunit * unitp)32721341Sstevel envctrl_probe_cpus(struct envctrlunit *unitp)
32731341Sstevel {
32741341Sstevel 	int instance;
32751341Sstevel 
32761341Sstevel 	/*
32771341Sstevel 	 * The cpu search is as follows:
32781341Sstevel 	 * If there is only 1 CPU module it is named as
32791341Sstevel 	 * SUNW,UltraSPARC. If this is a match we still don't
32801341Sstevel 	 * know what slot the cpu module is in therefore
32811341Sstevel 	 * we need to check the "upa-portid" property.
32821341Sstevel 	 * If we have more than 1 cpu, then they are appended by
32831341Sstevel 	 * instance numbers and slot locations. e.g.
32841341Sstevel 	 * SUNW,UltraSPARC@1,0 (slot 1). it would have been
32851341Sstevel 	 * nice to have the naming consistent for one CPU e.g.
32861341Sstevel 	 * SUNW,UltraSPARC@0,0...sigh
32871341Sstevel 	 */
32881341Sstevel 
32891341Sstevel 	for (instance = 0; instance < ENVCTRL_MAX_CPUS; instance++) {
32901341Sstevel 		unitp->cpu_pr_location[instance] = B_FALSE;
32911341Sstevel 	}
32921341Sstevel 
32931341Sstevel 	ddi_walk_devs(ddi_root_node(), envctrl_match_cpu, unitp);
32941341Sstevel }
32951341Sstevel 
32961341Sstevel static int
envctrl_match_cpu(dev_info_t * dip,void * arg)32971341Sstevel envctrl_match_cpu(dev_info_t *dip, void *arg)
32981341Sstevel {
32991341Sstevel 
33001341Sstevel 	int cpu_slot;
33011341Sstevel 	char name[32];
33021341Sstevel 	char name1[32];
33031341Sstevel 	struct envctrlunit *unitp = (struct envctrlunit *)arg;
33041341Sstevel 
33051341Sstevel 	(void) sprintf(name, "%s", ENVCTRL_TAZCPU_STRING);
33061341Sstevel 	(void) sprintf(name1, "%s", ENVCTRL_TAZBLKBRDCPU_STRING);
33071341Sstevel 
33081341Sstevel 	if ((strcmp(ddi_node_name(dip), name) == 0) ||
3309*7656SSherry.Moore@Sun.COM 	    (strcmp(ddi_node_name(dip), name1) == 0)) {
33101341Sstevel 		if ((cpu_slot = (int)ddi_getprop(DDI_DEV_T_ANY, dip,
3311*7656SSherry.Moore@Sun.COM 		    DDI_PROP_DONTPASS, "upa-portid", -1)) == -1) {
33121341Sstevel 			cmn_err(CE_WARN, "envctrl no cpu upa-portid");
33131341Sstevel 		} else {
33141341Sstevel 			unitp->cpu_pr_location[cpu_slot] = B_TRUE;
33151341Sstevel 			unitp->num_cpus_present++;
33161341Sstevel 		}
33171341Sstevel 	}
33181341Sstevel 
33191341Sstevel 	return (DDI_WALK_CONTINUE);
33201341Sstevel }
33211341Sstevel 
33221341Sstevel /*
33231341Sstevel  * This routine returns TRUE if some other error condition
33241341Sstevel  * has set the GEN_ERR FAULT LED. Tp further complicate this
33251341Sstevel  * LED panel we have overloaded the GEN_ERR LED to indicate
33261341Sstevel  * that a fan fault has occurred without having a fan fault
33271341Sstevel  * LED as does all other error conditions. So we just take the
33281341Sstevel  * software state and return true. The whole purpose of this functon
33291341Sstevel  * is to tell us wehther or not we can shut off the GEN_FAULT LED.
33301341Sstevel  * NOTE: this ledval is usually one of the following FSP vals
33311341Sstevel  * EXCEPT in the case of the fan fail.. we pass in a "0".
33321341Sstevel  */
33331341Sstevel 
33341341Sstevel static int
envctrl_isother_fault_led(struct envctrlunit * unitp,uint8_t fspval,uint8_t thisled)33351341Sstevel envctrl_isother_fault_led(struct envctrlunit *unitp, uint8_t fspval,
33361341Sstevel     uint8_t thisled)
33371341Sstevel {
33381341Sstevel 	int status = B_FALSE;
33391341Sstevel 
33401341Sstevel 	if (fspval != 0) {
33411341Sstevel 		fspval = (fspval & ~(thisled));
33421341Sstevel 	}
33431341Sstevel 	if (unitp->num_fans_failed > 0 && thisled != 0) {
33441341Sstevel 		status = B_TRUE;
33451341Sstevel 	} else if (fspval & ENVCTRL_FSP_DISK_ERR) {
33461341Sstevel 		status = B_TRUE;
33471341Sstevel 	} else if (fspval & ENVCTRL_FSP_PS_ERR) {
33481341Sstevel 		status = B_TRUE;
33491341Sstevel 	} else if (fspval & ENVCTRL_FSP_TEMP_ERR) {
33501341Sstevel 		status = B_TRUE;
33511341Sstevel 	}
33521341Sstevel 	return (status);
33531341Sstevel }
33541341Sstevel 
33551341Sstevel static void
envctrl_pshotplug_poll(void * arg)33561341Sstevel envctrl_pshotplug_poll(void *arg)
33571341Sstevel {
33581341Sstevel 	struct envctrlunit *unitp = (struct envctrlunit *)arg;
33591341Sstevel 
33601341Sstevel 	mutex_enter(&unitp->umutex);
33611341Sstevel 
33621341Sstevel 	envctrl_ps_probe(unitp);
33631341Sstevel 
33641341Sstevel 	mutex_exit(&unitp->umutex);
33651341Sstevel }
33661341Sstevel 
33671341Sstevel /*
33681341Sstevel  * The following routines implement the i2c protocol.
33691341Sstevel  * They should be removed once the envctrl_targets.c file is included.
33701341Sstevel  */
33711341Sstevel 
33721341Sstevel /*
33731341Sstevel  * put host interface into master mode
33741341Sstevel  */
33751341Sstevel static int
eHc_start_pcf8584(struct eHc_envcunit * ehcp,uint8_t byteaddress)33761341Sstevel eHc_start_pcf8584(struct eHc_envcunit *ehcp, uint8_t byteaddress)
33771341Sstevel {
33781341Sstevel 	uint8_t poll_status;
33791341Sstevel 	uint8_t discard;
33801341Sstevel 	int i;
33811341Sstevel 
33821341Sstevel 	/* wait if bus is busy */
33831341Sstevel 
33841341Sstevel 	i = 0;
33851341Sstevel 	do {
33861341Sstevel 		drv_usecwait(1000);
33871341Sstevel 		poll_status =
3388*7656SSherry.Moore@Sun.COM 		    ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
33891341Sstevel 		i++;
33901341Sstevel 	} while (((poll_status & EHC_S1_NBB) == 0) && i < EHC_MAX_WAIT);
33911341Sstevel 
33921341Sstevel 	if (i == EHC_MAX_WAIT) {
33931341Sstevel 		DCMNERR(CE_WARN, "eHc_start_pcf8584: I2C bus busy");
33941341Sstevel 		return (EHC_FAILURE);
33951341Sstevel 	}
33961341Sstevel 
33971341Sstevel 	if (poll_status & EHC_S1_BER) {
33981341Sstevel 		DCMN2ERR(CE_WARN, "eHc_start_pcf8584: I2C bus error");
33991341Sstevel 		return (EHC_FAILURE);
34001341Sstevel 	}
34011341Sstevel 
34021341Sstevel 	if (poll_status & EHC_S1_LAB) {
34031341Sstevel 		DCMN2ERR(CE_WARN, "eHc_start_pcf8584: Lost arbitration");
34041341Sstevel 		return (EHC_FAILURE);
34051341Sstevel 	}
34061341Sstevel 
34071341Sstevel 	/* load the slave address */
34081341Sstevel 	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0, byteaddress);
34091341Sstevel 
34101341Sstevel 	/* generate the "start condition" and clock out the slave address */
34111341Sstevel 	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1,
3412*7656SSherry.Moore@Sun.COM 	    EHC_S1_PIN | EHC_S1_ES0 | EHC_S1_STA | EHC_S1_ACK);
34131341Sstevel 
34141341Sstevel 	/* wait for completion of transmission */
34151341Sstevel 	i = 0;
34161341Sstevel 	do {
34171341Sstevel 		drv_usecwait(1000);
34181341Sstevel 		poll_status =
3419*7656SSherry.Moore@Sun.COM 		    ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
34201341Sstevel 		i++;
34211341Sstevel 	} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);
34221341Sstevel 
34231341Sstevel 	if (i == EHC_MAX_WAIT) {
34241341Sstevel 		DCMNERR(CE_WARN, "eHc_start_pcf8584: I2C bus busy");
34251341Sstevel 		return (EHC_FAILURE);
34261341Sstevel 	}
34271341Sstevel 
34281341Sstevel 	if (poll_status & EHC_S1_BER) {
34291341Sstevel 		DCMN2ERR(CE_WARN, "eHc_start_pcf8584: I2C bus error");
34301341Sstevel 		return (EHC_FAILURE);
34311341Sstevel 	}
34321341Sstevel 
34331341Sstevel 	if (poll_status & EHC_S1_LAB) {
34341341Sstevel 		DCMN2ERR(CE_WARN, "eHc_start_pcf8584: Lost arbitration");
34351341Sstevel 		return (EHC_FAILURE);
34361341Sstevel 	}
34371341Sstevel 
34381341Sstevel 	if (poll_status & EHC_S1_LRB) {
34391341Sstevel 		DCMNERR(CE_WARN, "eHc_start_pcf8584: No slave ACK");
34401341Sstevel 		return (EHC_NO_SLAVE_ACK);
34411341Sstevel 	}
34421341Sstevel 
34431341Sstevel 	/*
34441341Sstevel 	 * If this is a read we are setting up for (as indicated by
34451341Sstevel 	 * the least significant byte being set), read
34461341Sstevel 	 * and discard the first byte off the bus - this
34471341Sstevel 	 * is the slave address.
34481341Sstevel 	 */
34491341Sstevel 
34501341Sstevel 	i = 0;
34511341Sstevel 	if (byteaddress & EHC_BYTE_READ) {
34521341Sstevel 		discard = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
34531341Sstevel #ifdef lint
34541341Sstevel 		discard = discard;
34551341Sstevel #endif
34561341Sstevel 
34571341Sstevel 		/* wait for completion of transmission */
34581341Sstevel 		do {
34591341Sstevel 			drv_usecwait(1000);
3460*7656SSherry.Moore@Sun.COM 			poll_status = ddi_get8(ehcp->ctlr_handle,
3461*7656SSherry.Moore@Sun.COM 			    &ehcp->bus_ctl_regs->s1);
34621341Sstevel 			i++;
34631341Sstevel 		} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);
34641341Sstevel 
34651341Sstevel 		if (i == EHC_MAX_WAIT) {
34661341Sstevel 			DCMNERR(CE_WARN, "eHc_start_pcf8584: I2C bus busy");
34671341Sstevel 			return (EHC_FAILURE);
34681341Sstevel 		}
34691341Sstevel 
34701341Sstevel 		if (poll_status & EHC_S1_BER) {
34711341Sstevel 			DCMN2ERR(CE_WARN,
3472*7656SSherry.Moore@Sun.COM 			    "eHc_start_pcf8584: I2C bus error");
34731341Sstevel 			return (EHC_FAILURE);
34741341Sstevel 		}
34751341Sstevel 
34761341Sstevel 		if (poll_status & EHC_S1_LAB) {
34771341Sstevel 			DCMN2ERR(CE_WARN,
3478*7656SSherry.Moore@Sun.COM 			    "eHc_start_pcf8584: Lost arbitration");
34791341Sstevel 			return (EHC_FAILURE);
34801341Sstevel 		}
34811341Sstevel 	}
34821341Sstevel 
34831341Sstevel 	return (EHC_SUCCESS);
34841341Sstevel }
34851341Sstevel 
34861341Sstevel /*
34871341Sstevel  * put host interface into slave/receiver mode
34881341Sstevel  */
34891341Sstevel static void
eHc_stop_pcf8584(struct eHc_envcunit * ehcp)34901341Sstevel eHc_stop_pcf8584(struct eHc_envcunit *ehcp)
34911341Sstevel {
34921341Sstevel 	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1,
3493*7656SSherry.Moore@Sun.COM 	    EHC_S1_PIN | EHC_S1_ES0 | EHC_S1_STO | EHC_S1_ACK);
34941341Sstevel }
34951341Sstevel 
34961341Sstevel static int
eHc_read_pcf8584(struct eHc_envcunit * ehcp,uint8_t * data)34971341Sstevel eHc_read_pcf8584(struct eHc_envcunit *ehcp, uint8_t *data)
34981341Sstevel {
34991341Sstevel 	uint8_t poll_status;
35001341Sstevel 	int i = 0;
35011341Sstevel 
35021341Sstevel 	/* Read the byte of interest */
35031341Sstevel 	*data = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
35041341Sstevel 
35051341Sstevel 	/* wait for completion of transmission */
35061341Sstevel 	do {
35071341Sstevel 		drv_usecwait(1000);
35081341Sstevel 		poll_status =
3509*7656SSherry.Moore@Sun.COM 		    ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
35101341Sstevel 		i++;
35111341Sstevel 	} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);
35121341Sstevel 
35131341Sstevel 	if (i == EHC_MAX_WAIT) {
35141341Sstevel 		DCMNERR(CE_WARN, "eHc_read_pcf8584: I2C bus busy");
35151341Sstevel 		return (EHC_FAILURE);
35161341Sstevel 	}
35171341Sstevel 
35181341Sstevel 	if (poll_status & EHC_S1_BER) {
35191341Sstevel 		DCMN2ERR(CE_WARN, "eHc_read_pcf8584: I2C bus error");
35201341Sstevel 		return (EHC_FAILURE);
35211341Sstevel 	}
35221341Sstevel 
35231341Sstevel 	if (poll_status & EHC_S1_LAB) {
35241341Sstevel 		DCMN2ERR(CE_WARN, "eHc_read_pcf8584: Lost arbitration");
35251341Sstevel 		return (EHC_FAILURE);
35261341Sstevel 	}
35271341Sstevel 
35281341Sstevel 	return (EHC_SUCCESS);
35291341Sstevel }
35301341Sstevel 
35311341Sstevel /*
35321341Sstevel  * host interface is in transmitter state, thus mode is master/transmitter
35331341Sstevel  * NOTE to Bill: this check the LRB bit (only done in transmit mode).
35341341Sstevel  */
35351341Sstevel 
35361341Sstevel static int
eHc_write_pcf8584(struct eHc_envcunit * ehcp,uint8_t data)35371341Sstevel eHc_write_pcf8584(struct eHc_envcunit *ehcp, uint8_t data)
35381341Sstevel {
35391341Sstevel 	uint8_t poll_status;
35401341Sstevel 	int i = 0;
35411341Sstevel 
35421341Sstevel 	/* send the data, EHC_S1_PIN should go to "1" immediately */
35431341Sstevel 	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0, data);
35441341Sstevel 
35451341Sstevel 	/* wait for completion of transmission */
35461341Sstevel 	do {
35471341Sstevel 		drv_usecwait(1000);
35481341Sstevel 		poll_status =
3549*7656SSherry.Moore@Sun.COM 		    ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
35501341Sstevel 		i++;
35511341Sstevel 	} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);
35521341Sstevel 
35531341Sstevel 	if (i == EHC_MAX_WAIT) {
35541341Sstevel 		DCMNERR(CE_WARN, "eHc_write_pcf8584: I2C bus busy");
35551341Sstevel 		return (EHC_FAILURE);
35561341Sstevel 	}
35571341Sstevel 
35581341Sstevel 	if (poll_status & EHC_S1_BER) {
35591341Sstevel 		DCMN2ERR(CE_WARN, "eHc_write_pcf8584: I2C bus error");
35601341Sstevel 		return (EHC_FAILURE);
35611341Sstevel 	}
35621341Sstevel 
35631341Sstevel 	if (poll_status & EHC_S1_LAB) {
35641341Sstevel 		DCMN2ERR(CE_WARN, "eHc_write_pcf8584: Lost arbitration");
35651341Sstevel 		return (EHC_FAILURE);
35661341Sstevel 	}
35671341Sstevel 
35681341Sstevel 	if (poll_status & EHC_S1_LRB) {
35691341Sstevel 		DCMNERR(CE_WARN, "eHc_write_pcf8584: No slave ACK");
35701341Sstevel 		return (EHC_NO_SLAVE_ACK);
35711341Sstevel 	}
35721341Sstevel 
35731341Sstevel 	return (EHC_SUCCESS);
35741341Sstevel }
35751341Sstevel 
35761341Sstevel static int
eHc_after_read_pcf8584(struct eHc_envcunit * ehcp,uint8_t * data)35771341Sstevel eHc_after_read_pcf8584(struct eHc_envcunit *ehcp, uint8_t *data)
35781341Sstevel {
35791341Sstevel 	uint8_t discard;
35801341Sstevel 	uint8_t poll_status;
35811341Sstevel 	int i = 0;
35821341Sstevel 
35831341Sstevel 	/* set ACK in register S1 to 0 */
35841341Sstevel 	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1, EHC_S1_ES0);
35851341Sstevel 
35861341Sstevel 	/*
35871341Sstevel 	 * Read the "byte-before-the-last-byte" - sets PIN bit to '1'
35881341Sstevel 	 */
35891341Sstevel 
35901341Sstevel 	*data = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
35911341Sstevel 
35921341Sstevel 	/* wait for completion of transmission */
35931341Sstevel 	do {
35941341Sstevel 		drv_usecwait(1000);
35951341Sstevel 		poll_status =
3596*7656SSherry.Moore@Sun.COM 		    ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
35971341Sstevel 		i++;
35981341Sstevel 	} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);
35991341Sstevel 
36001341Sstevel 	if (i == EHC_MAX_WAIT) {
36011341Sstevel 		DCMNERR(CE_WARN, "eHc_after_read_pcf8584: I2C bus busy");
36021341Sstevel 		return (EHC_FAILURE);
36031341Sstevel 	}
36041341Sstevel 
36051341Sstevel 	if (poll_status & EHC_S1_BER) {
36061341Sstevel 		DCMN2ERR(CE_WARN,
3607*7656SSherry.Moore@Sun.COM 		    "eHc_after_read_pcf8584: I2C bus error");
36081341Sstevel 		return (EHC_FAILURE);
36091341Sstevel 	}
36101341Sstevel 
36111341Sstevel 	if (poll_status & EHC_S1_LAB) {
36121341Sstevel 		DCMN2ERR(CE_WARN, "eHc_after_read_pcf8584: Lost arbitration");
36131341Sstevel 		return (EHC_FAILURE);
36141341Sstevel 	}
36151341Sstevel 
36161341Sstevel 	/*
36171341Sstevel 	 * Generate the "stop" condition.
36181341Sstevel 	 */
36191341Sstevel 	eHc_stop_pcf8584(ehcp);
36201341Sstevel 
36211341Sstevel 	/*
36221341Sstevel 	 * Read the "last" byte.
36231341Sstevel 	 */
36241341Sstevel 	discard = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
36251341Sstevel #ifdef lint
36261341Sstevel 	discard = discard;
36271341Sstevel #endif
36281341Sstevel 
36291341Sstevel 	return (EHC_SUCCESS);
36301341Sstevel }
36311341Sstevel 
36321341Sstevel /*
36331341Sstevel  * Write to the TDA8444 chip.
36341341Sstevel  * byteaddress = chip type base address | chip offset address.
36351341Sstevel  */
36361341Sstevel static int
eHc_write_tda8444(struct eHc_envcunit * ehcp,int byteaddress,int instruction,int subaddress,uint8_t * buf,int size)36371341Sstevel eHc_write_tda8444(struct eHc_envcunit *ehcp, int byteaddress, int instruction,
36381341Sstevel 	int subaddress, uint8_t *buf, int size)
36391341Sstevel {
36401341Sstevel 	uint8_t control;
36411341Sstevel 	int i, status;
36421341Sstevel 
36431341Sstevel 	ASSERT((byteaddress & 0x1) == 0);
36441341Sstevel 	ASSERT(subaddress < 8);
36451341Sstevel 	ASSERT(instruction == 0xf || instruction == 0x0);
36461341Sstevel 	ASSERT(MUTEX_HELD(&ehcp->umutex));
36471341Sstevel 
36481341Sstevel 	control = (instruction << 4) | subaddress;
36491341Sstevel 
36501341Sstevel 	if ((status = eHc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
36511341Sstevel 		if (status == EHC_NO_SLAVE_ACK) {
36521341Sstevel 			/*
36531341Sstevel 			 * Send the "stop" condition.
36541341Sstevel 			 */
36551341Sstevel 			eHc_stop_pcf8584(ehcp);
36561341Sstevel 		}
36571341Sstevel 		return (EHC_FAILURE);
36581341Sstevel 	}
36591341Sstevel 
36601341Sstevel 	if ((status = eHc_write_pcf8584(ehcp, control)) != EHC_SUCCESS) {
36611341Sstevel 		if (status == EHC_NO_SLAVE_ACK) {
36621341Sstevel 		/*
36631341Sstevel 		 * Send the "stop" condition.
36641341Sstevel 		 */
36651341Sstevel 		eHc_stop_pcf8584(ehcp);
36661341Sstevel 		}
36671341Sstevel 		return (EHC_FAILURE);
36681341Sstevel 	}
36691341Sstevel 
36701341Sstevel 	for (i = 0; i < size; i++) {
36711341Sstevel 		if ((status = eHc_write_pcf8584(ehcp, (buf[i] & 0x3f))) !=
3672*7656SSherry.Moore@Sun.COM 		    EHC_SUCCESS) {
36731341Sstevel 			if (status == EHC_NO_SLAVE_ACK)
36741341Sstevel 				eHc_stop_pcf8584(ehcp);
36751341Sstevel 			return (EHC_FAILURE);
36761341Sstevel 		}
36771341Sstevel 	}
36781341Sstevel 
36791341Sstevel 	eHc_stop_pcf8584(ehcp);
36801341Sstevel 
36811341Sstevel 	return (EHC_SUCCESS);
36821341Sstevel }
36831341Sstevel 
36841341Sstevel /*
36851341Sstevel  * Read from PCF8574A chip.
36861341Sstevel  * byteaddress = chip type base address | chip offset address.
36871341Sstevel  */
36881341Sstevel static int
eHc_read_pcf8574a(struct eHc_envcunit * ehcp,int byteaddress,uint8_t * buf,int size)36891341Sstevel eHc_read_pcf8574a(struct eHc_envcunit *ehcp, int byteaddress, uint8_t *buf,
36901341Sstevel 	int size)
36911341Sstevel {
36921341Sstevel 	int i;
36931341Sstevel 	int status;
36941341Sstevel 	uint8_t discard;
36951341Sstevel 
36961341Sstevel 	ASSERT((byteaddress & 0x1) == 0);
36971341Sstevel 	ASSERT(MUTEX_HELD(&ehcp->umutex));
36981341Sstevel 
36991341Sstevel 	/*
37001341Sstevel 	 * Put the bus into the start condition
37011341Sstevel 	 */
37021341Sstevel 	if ((status = eHc_start_pcf8584(ehcp, EHC_BYTE_READ | byteaddress)) !=
3703*7656SSherry.Moore@Sun.COM 	    EHC_SUCCESS) {
37041341Sstevel 		if (status == EHC_NO_SLAVE_ACK) {
37051341Sstevel 			/*
37061341Sstevel 			 * Send the "stop" condition.
37071341Sstevel 			 */
37081341Sstevel 			eHc_stop_pcf8584(ehcp);
37091341Sstevel 			/*
37101341Sstevel 			 * Read the last byte - discard it.
37111341Sstevel 			 */
3712*7656SSherry.Moore@Sun.COM 			discard = ddi_get8(ehcp->ctlr_handle,
3713*7656SSherry.Moore@Sun.COM 			    &ehcp->bus_ctl_regs->s0);
37141341Sstevel #ifdef lint
37151341Sstevel 			discard = discard;
37161341Sstevel #endif
37171341Sstevel 		}
37181341Sstevel 		return (EHC_FAILURE);
37191341Sstevel 	}
37201341Sstevel 
37211341Sstevel 	for (i = 0; i < size - 1; i++) {
37221341Sstevel 		if ((status = eHc_read_pcf8584(ehcp, &buf[i])) != EHC_SUCCESS) {
37231341Sstevel 			return (EHC_FAILURE);
37241341Sstevel 		}
37251341Sstevel 	}
37261341Sstevel 
37271341Sstevel 	/*
37281341Sstevel 	 * Handle the part of the bus protocol which comes
37291341Sstevel 	 * after a read, including reading the last byte.
37301341Sstevel 	 */
37311341Sstevel 
37321341Sstevel 	if (eHc_after_read_pcf8584(ehcp, &buf[i]) != EHC_SUCCESS) {
37331341Sstevel 		return (EHC_FAILURE);
37341341Sstevel 	}
37351341Sstevel 
37361341Sstevel 	return (EHC_SUCCESS);
37371341Sstevel }
37381341Sstevel 
37391341Sstevel /*
37401341Sstevel  * Write to the PCF8574A chip.
37411341Sstevel  * byteaddress = chip type base address | chip offset address.
37421341Sstevel  */
37431341Sstevel static int
eHc_write_pcf8574a(struct eHc_envcunit * ehcp,int byteaddress,uint8_t * buf,int size)37441341Sstevel eHc_write_pcf8574a(struct eHc_envcunit *ehcp, int byteaddress, uint8_t *buf,
37451341Sstevel 	int size)
37461341Sstevel {
37471341Sstevel 	int i;
37481341Sstevel 	int status;
37491341Sstevel 
37501341Sstevel 	ASSERT((byteaddress & 0x1) == 0);
37511341Sstevel 	ASSERT(MUTEX_HELD(&ehcp->umutex));
37521341Sstevel 
37531341Sstevel 	/*
37541341Sstevel 	 * Put the bus into the start condition (write)
37551341Sstevel 	 */
37561341Sstevel 	if ((status = eHc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
37571341Sstevel 		if (status == EHC_NO_SLAVE_ACK) {
37581341Sstevel 			/*
37591341Sstevel 			 * Send the "stop" condition.
37601341Sstevel 			 */
37611341Sstevel 			eHc_stop_pcf8584(ehcp);
37621341Sstevel 		}
37631341Sstevel 		return (EHC_FAILURE);
37641341Sstevel 	}
37651341Sstevel 
37661341Sstevel 	/*
37671341Sstevel 	 * Send the data - poll as needed.
37681341Sstevel 	 */
37691341Sstevel 	for (i = 0; i < size; i++) {
37701341Sstevel 		if ((status = eHc_write_pcf8584(ehcp, buf[i])) != EHC_SUCCESS) {
37711341Sstevel 			if (status == EHC_NO_SLAVE_ACK)
37721341Sstevel 				eHc_stop_pcf8584(ehcp);
37731341Sstevel 			return (EHC_FAILURE);
37741341Sstevel 		}
37751341Sstevel 	}
37761341Sstevel 
37771341Sstevel 	/*
37781341Sstevel 	 * Transmission complete - generate stop condition and
37791341Sstevel 	 * put device back into slave receiver mode.
37801341Sstevel 	 */
37811341Sstevel 	eHc_stop_pcf8584(ehcp);
37821341Sstevel 
37831341Sstevel 	return (EHC_SUCCESS);
37841341Sstevel }
37851341Sstevel 
37861341Sstevel /*
37871341Sstevel  * Read from the PCF8574 chip.
37881341Sstevel  * byteaddress = chip type base address | chip offset address.
37891341Sstevel  */
37901341Sstevel static int
eHc_read_pcf8574(struct eHc_envcunit * ehcp,int byteaddress,uint8_t * buf,int size)37911341Sstevel eHc_read_pcf8574(struct eHc_envcunit *ehcp, int byteaddress, uint8_t *buf,
37921341Sstevel 	int size)
37931341Sstevel {
37941341Sstevel 	int i;
37951341Sstevel 	int status;
37961341Sstevel 	uint8_t discard;
37971341Sstevel 
37981341Sstevel 	ASSERT((byteaddress & 0x1) == 0);
37991341Sstevel 	ASSERT(MUTEX_HELD(&ehcp->umutex));
38001341Sstevel 
38011341Sstevel 	/*
38021341Sstevel 	 * Put the bus into the start condition
38031341Sstevel 	 */
38041341Sstevel 	if ((status = eHc_start_pcf8584(ehcp, EHC_BYTE_READ | byteaddress)) !=
3805*7656SSherry.Moore@Sun.COM 	    EHC_SUCCESS) {
38061341Sstevel 		if (status == EHC_NO_SLAVE_ACK) {
38071341Sstevel 			/*
38081341Sstevel 			 * Send the "stop" condition.
38091341Sstevel 			 */
38101341Sstevel 			eHc_stop_pcf8584(ehcp);
38111341Sstevel 			/*
38121341Sstevel 			 * Read the last byte - discard it.
38131341Sstevel 			 */
3814*7656SSherry.Moore@Sun.COM 			discard = ddi_get8(ehcp->ctlr_handle,
3815*7656SSherry.Moore@Sun.COM 			    &ehcp->bus_ctl_regs->s0);
38161341Sstevel #ifdef lint
38171341Sstevel 			discard = discard;
38181341Sstevel #endif
38191341Sstevel 		}
38201341Sstevel 		return (EHC_FAILURE);
38211341Sstevel 	}
38221341Sstevel 
38231341Sstevel 	for (i = 0; i < size - 1; i++) {
38241341Sstevel 		if ((status = eHc_read_pcf8584(ehcp, &buf[i])) != EHC_SUCCESS) {
38251341Sstevel 		return (EHC_FAILURE);
38261341Sstevel 		}
38271341Sstevel 	}
38281341Sstevel 
38291341Sstevel 	/*
38301341Sstevel 	 * Handle the part of the bus protocol which comes
38311341Sstevel 	 * after a read.
38321341Sstevel 	 */
38331341Sstevel 
38341341Sstevel 	if (eHc_after_read_pcf8584(ehcp, &buf[i]) != EHC_SUCCESS) {
38351341Sstevel 		return (EHC_FAILURE);
38361341Sstevel 	}
38371341Sstevel 
38381341Sstevel 	return (EHC_SUCCESS);
38391341Sstevel }
38401341Sstevel 
38411341Sstevel /*
38421341Sstevel  * Write to the PCF8574 chip.
38431341Sstevel  * byteaddress = chip type base address | chip offset address.
38441341Sstevel  */
38451341Sstevel static int
eHc_write_pcf8574(struct eHc_envcunit * ehcp,int byteaddress,uint8_t * buf,int size)38461341Sstevel eHc_write_pcf8574(struct eHc_envcunit *ehcp, int byteaddress, uint8_t *buf,
38471341Sstevel 	int size)
38481341Sstevel {
38491341Sstevel 	int i;
38501341Sstevel 	int status;
38511341Sstevel 
38521341Sstevel 	ASSERT((byteaddress & 0x1) == 0);
38531341Sstevel 	ASSERT(MUTEX_HELD(&ehcp->umutex));
38541341Sstevel 
38551341Sstevel 	/*
38561341Sstevel 	 * Put the bus into the start condition (write)
38571341Sstevel 	 */
38581341Sstevel 	if ((status = eHc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
38591341Sstevel 		if (status == EHC_NO_SLAVE_ACK) {
38601341Sstevel 			/*
38611341Sstevel 			 * Send the "stop" condition.
38621341Sstevel 			 */
38631341Sstevel 			eHc_stop_pcf8584(ehcp);
38641341Sstevel 		}
38651341Sstevel 		return (EHC_FAILURE);
38661341Sstevel 	}
38671341Sstevel 
38681341Sstevel 	/*
38691341Sstevel 	 * Send the data - poll as needed.
38701341Sstevel 	 */
38711341Sstevel 	for (i = 0; i < size; i++) {
38721341Sstevel 		if ((status = eHc_write_pcf8584(ehcp, buf[i])) != EHC_SUCCESS) {
38731341Sstevel 			if (status == EHC_NO_SLAVE_ACK)
38741341Sstevel 				eHc_stop_pcf8584(ehcp);
38751341Sstevel 			return (EHC_FAILURE);
38761341Sstevel 		}
38771341Sstevel 	}
38781341Sstevel 	/*
38791341Sstevel 	 * Transmission complete - generate stop condition and
38801341Sstevel 	 * put device back into slave receiver mode.
38811341Sstevel 	 */
38821341Sstevel 	eHc_stop_pcf8584(ehcp);
38831341Sstevel 
38841341Sstevel 	return (EHC_SUCCESS);
38851341Sstevel }
38861341Sstevel 
38871341Sstevel /*
38881341Sstevel  * Read from the LM75
38891341Sstevel  * byteaddress = chip type base address | chip offset address.
38901341Sstevel  */
38911341Sstevel static int
eHc_read_lm75(struct eHc_envcunit * ehcp,int byteaddress,uint8_t * buf,int size)38921341Sstevel eHc_read_lm75(struct eHc_envcunit *ehcp, int byteaddress, uint8_t *buf,
38931341Sstevel 	int size)
38941341Sstevel {
38951341Sstevel 	int i;
38961341Sstevel 	int status;
38971341Sstevel 	uint8_t discard;
38981341Sstevel 
38991341Sstevel 	ASSERT((byteaddress & 0x1) == 0);
39001341Sstevel 	ASSERT(MUTEX_HELD(&ehcp->umutex));
39011341Sstevel 
39021341Sstevel 	/*
39031341Sstevel 	 * Put the bus into the start condition
39041341Sstevel 	 */
39051341Sstevel 	if ((status = eHc_start_pcf8584(ehcp, EHC_BYTE_READ | byteaddress)) !=
3906*7656SSherry.Moore@Sun.COM 	    EHC_SUCCESS) {
39071341Sstevel 		if (status == EHC_NO_SLAVE_ACK) {
39081341Sstevel 			/*
39091341Sstevel 			 * Send the stop condition.
39101341Sstevel 			 */
39111341Sstevel 			eHc_stop_pcf8584(ehcp);
39121341Sstevel 			/*
39131341Sstevel 			 * Read the last byte - discard it.
39141341Sstevel 			 */
3915*7656SSherry.Moore@Sun.COM 			discard = ddi_get8(ehcp->ctlr_handle,
3916*7656SSherry.Moore@Sun.COM 			    &ehcp->bus_ctl_regs->s0);
39171341Sstevel #ifdef lint
39181341Sstevel 			discard = discard;
39191341Sstevel #endif
39201341Sstevel 		}
39211341Sstevel 		return (EHC_FAILURE);
39221341Sstevel 	}
39231341Sstevel 
39241341Sstevel 	for (i = 0; i < size - 1; i++) {
39251341Sstevel 		if ((status = eHc_read_pcf8584(ehcp, &buf[i])) != EHC_SUCCESS) {
39261341Sstevel 		return (EHC_FAILURE);
39271341Sstevel 		}
39281341Sstevel 	}
39291341Sstevel 
39301341Sstevel 	/*
39311341Sstevel 	 * Handle the part of the bus protocol which comes
39321341Sstevel 	 * after a read.
39331341Sstevel 	 */
39341341Sstevel 	if (eHc_after_read_pcf8584(ehcp, &buf[i]) != EHC_SUCCESS) {
39351341Sstevel 		return (EHC_FAILURE);
39361341Sstevel 	}
39371341Sstevel 
39381341Sstevel 	return (EHC_SUCCESS);
39391341Sstevel }
39401341Sstevel 
39411341Sstevel /*
39421341Sstevel  * Write to the PCF8583 chip.
39431341Sstevel  * byteaddress = chip type base address | chip offset address.
39441341Sstevel  */
39451341Sstevel static int
eHc_write_pcf8583(struct eHc_envcunit * ehcp,int byteaddress,uint8_t * buf,int size)39461341Sstevel eHc_write_pcf8583(struct eHc_envcunit *ehcp, int byteaddress, uint8_t *buf,
39471341Sstevel 	int size)
39481341Sstevel {
39491341Sstevel 	int i;
39501341Sstevel 	int status;
39511341Sstevel 
39521341Sstevel 	ASSERT((byteaddress & 0x1) == 0);
39531341Sstevel 	ASSERT(MUTEX_HELD(&ehcp->umutex));
39541341Sstevel 
39551341Sstevel 	if ((status = eHc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
39561341Sstevel 		if (status == EHC_NO_SLAVE_ACK) {
39571341Sstevel 			/*
39581341Sstevel 			 * Send the "stop" condition.
39591341Sstevel 			 */
39601341Sstevel 			eHc_stop_pcf8584(ehcp);
39611341Sstevel 		}
39621341Sstevel 		return (EHC_FAILURE);
39631341Sstevel 	}
39641341Sstevel 
39651341Sstevel 	/*
39661341Sstevel 	 * Send the data - poll as needed.
39671341Sstevel 	 */
39681341Sstevel 	for (i = 0; i < size; i++) {
39691341Sstevel 		if ((status = eHc_write_pcf8584(ehcp, buf[i])) != EHC_SUCCESS) {
39701341Sstevel 			if (status == EHC_NO_SLAVE_ACK)
39711341Sstevel 				eHc_stop_pcf8584(ehcp);
39721341Sstevel 			return (EHC_FAILURE);
39731341Sstevel 		}
39741341Sstevel 	}
39751341Sstevel 
39761341Sstevel 	/*
39771341Sstevel 	 * Transmission complete - generate stop condition and
39781341Sstevel 	 * put device back into slave receiver mode.
39791341Sstevel 	 */
39801341Sstevel 	eHc_stop_pcf8584(ehcp);
39811341Sstevel 
39821341Sstevel 	return (EHC_SUCCESS);
39831341Sstevel }
39841341Sstevel 
39851341Sstevel /*
39861341Sstevel  * Read from the PCF8581 chip.
39871341Sstevel  * byteaddress = chip type base address | chip offset address.
39881341Sstevel  */
39891341Sstevel static int
eHc_read_pcf8591(struct eHc_envcunit * ehcp,int byteaddress,int channel,int autoinc,int amode,int aenable,uint8_t * buf,int size)39901341Sstevel eHc_read_pcf8591(struct eHc_envcunit *ehcp, int byteaddress, int channel,
39911341Sstevel 	int autoinc, int amode, int aenable, uint8_t *buf, int size)
39921341Sstevel {
39931341Sstevel 	int i;
39941341Sstevel 	int status;
39951341Sstevel 	uint8_t control;
39961341Sstevel 	uint8_t discard;
39971341Sstevel 
39981341Sstevel 	ASSERT((byteaddress & 0x1) == 0);
39991341Sstevel 	ASSERT(channel < 4);
40001341Sstevel 	ASSERT(amode < 4);
40011341Sstevel 	ASSERT(MUTEX_HELD(&ehcp->umutex));
40021341Sstevel 
40031341Sstevel 	/*
40041341Sstevel 	 * Write the control word to the PCF8591.
40051341Sstevel 	 * Follow the control word with a repeated START byte
40061341Sstevel 	 * rather than a STOP so that reads can follow without giving
40071341Sstevel 	 * up the bus.
40081341Sstevel 	 */
40091341Sstevel 
40101341Sstevel 	control = ((aenable << 6) | (amode << 4) | (autoinc << 2) | channel);
40111341Sstevel 
40121341Sstevel 	if ((status = eHc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
40131341Sstevel 		if (status == EHC_NO_SLAVE_ACK) {
40141341Sstevel 			eHc_stop_pcf8584(ehcp);
40151341Sstevel 		}
40161341Sstevel 		return (EHC_FAILURE);
40171341Sstevel 	}
40181341Sstevel 
40191341Sstevel 	if ((status = eHc_write_pcf8584(ehcp, control)) != EHC_SUCCESS) {
40201341Sstevel 		if (status == EHC_NO_SLAVE_ACK)
40211341Sstevel 			eHc_stop_pcf8584(ehcp);
40221341Sstevel 		return (EHC_FAILURE);
40231341Sstevel 	}
40241341Sstevel 
40251341Sstevel 	/*
40261341Sstevel 	 * The following two operations, 0x45 to S1, and the byteaddress
40271341Sstevel 	 * to S0, will result in a repeated START being sent out on the bus.
40281341Sstevel 	 * Refer to Fig.8 of Philips Semiconductors PCF8584 product spec.
40291341Sstevel 	 */
40301341Sstevel 
40311341Sstevel 	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1,
4032*7656SSherry.Moore@Sun.COM 	    EHC_S1_ES0 | EHC_S1_STA | EHC_S1_ACK);
40331341Sstevel 
40341341Sstevel 	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0,
4035*7656SSherry.Moore@Sun.COM 	    EHC_BYTE_READ | byteaddress);
40361341Sstevel 
40371341Sstevel 	i = 0;
40381341Sstevel 
40391341Sstevel 	do {
40401341Sstevel 		drv_usecwait(1000);
40411341Sstevel 		status =
4042*7656SSherry.Moore@Sun.COM 		    ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
40431341Sstevel 		i++;
40441341Sstevel 	} while ((status & EHC_S1_PIN) && i < EHC_MAX_WAIT);
40451341Sstevel 
40461341Sstevel 	if (i == EHC_MAX_WAIT) {
40471341Sstevel 		DCMNERR(CE_WARN, "eHc_read_pcf8591(): read of S1 failed");
40481341Sstevel 		return (EHC_FAILURE);
40491341Sstevel 	}
40501341Sstevel 
40511341Sstevel 	if (status & EHC_S1_LRB) {
40521341Sstevel 		DCMNERR(CE_WARN, "eHc_read_pcf8591(): No slave ACK");
40531341Sstevel 		/*
40541341Sstevel 		 * Send the stop condition.
40551341Sstevel 		 */
40561341Sstevel 		eHc_stop_pcf8584(ehcp);
40571341Sstevel 		/*
40581341Sstevel 		 * Read the last byte - discard it.
40591341Sstevel 		 */
40601341Sstevel 		discard = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
40611341Sstevel #ifdef lint
40621341Sstevel 		discard = discard;
40631341Sstevel #endif
40641341Sstevel 		return (EHC_FAILURE);
40651341Sstevel 	}
40661341Sstevel 
40671341Sstevel 	if (status & EHC_S1_BER) {
40681341Sstevel 		DCMN2ERR(CE_WARN, "eHc_read_pcf8591(): Bus error");
40691341Sstevel 		return (EHC_FAILURE);
40701341Sstevel 	}
40711341Sstevel 
40721341Sstevel 	if (status & EHC_S1_LAB) {
40731341Sstevel 		DCMN2ERR(CE_WARN, "eHc_read_pcf8591(): Lost Arbitration");
40741341Sstevel 		return (EHC_FAILURE);
40751341Sstevel 	}
40761341Sstevel 
40771341Sstevel 	/*
40781341Sstevel 	 * Discard first read as per PCF8584 master receiver protocol.
40791341Sstevel 	 * This is normally done in the eHc_start_pcf8584() routine.
40801341Sstevel 	 */
40811341Sstevel 	if ((status = eHc_read_pcf8584(ehcp, &discard)) != EHC_SUCCESS) {
40821341Sstevel 		return (EHC_FAILURE);
40831341Sstevel 	}
40841341Sstevel 
40851341Sstevel 	/* Discard second read as per PCF8591 protocol */
40861341Sstevel 	if ((status = eHc_read_pcf8584(ehcp, &discard)) != EHC_SUCCESS) {
40871341Sstevel 		return (EHC_FAILURE);
40881341Sstevel 	}
40891341Sstevel 
40901341Sstevel 	for (i = 0; i < size - 1; i++) {
40911341Sstevel 		if ((status = eHc_read_pcf8584(ehcp, &buf[i])) != EHC_SUCCESS) {
40921341Sstevel 			return (EHC_FAILURE);
40931341Sstevel 		}
40941341Sstevel 	}
40951341Sstevel 
40961341Sstevel 	if (eHc_after_read_pcf8584(ehcp, &buf[i]) != EHC_SUCCESS) {
40971341Sstevel 		return (EHC_FAILURE);
40981341Sstevel 	}
40991341Sstevel 
41001341Sstevel 	return (EHC_SUCCESS);
41011341Sstevel }
4102