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 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 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 40629949e86Sstevel _info(struct modinfo *modinfop) 40729949e86Sstevel { 40829949e86Sstevel return (mod_info(&envctrlmodlinkage, modinfop)); 40929949e86Sstevel } 41029949e86Sstevel 41129949e86Sstevel static int 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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