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 ®_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 ®_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 ®_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 ®_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