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