xref: /onnv-gate/usr/src/uts/sun4u/io/i2c/clients/adm1031.c (revision 7656:2621e50fdf4a)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7656SSherry.Moore@Sun.COM  * Common Development and Distribution License (the "License").
6*7656SSherry.Moore@Sun.COM  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*7656SSherry.Moore@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #include <sys/stat.h>
280Sstevel@tonic-gate #include <sys/file.h>
290Sstevel@tonic-gate #include <sys/uio.h>
300Sstevel@tonic-gate #include <sys/modctl.h>
310Sstevel@tonic-gate #include <sys/open.h>
320Sstevel@tonic-gate #include <sys/types.h>
330Sstevel@tonic-gate #include <sys/kmem.h>
340Sstevel@tonic-gate #include <sys/systm.h>
350Sstevel@tonic-gate #include <sys/ddi.h>
360Sstevel@tonic-gate #include <sys/sunddi.h>
370Sstevel@tonic-gate #include <sys/conf.h>
380Sstevel@tonic-gate #include <sys/mode.h>
390Sstevel@tonic-gate #include <sys/promif.h>
400Sstevel@tonic-gate #include <sys/note.h>
410Sstevel@tonic-gate #include <sys/i2c/misc/i2c_svc.h>
420Sstevel@tonic-gate #include <sys/i2c/clients/i2c_client.h>
430Sstevel@tonic-gate #include <sys/i2c/clients/adm1031.h>
440Sstevel@tonic-gate #include <sys/i2c/clients/adm1031_impl.h>
450Sstevel@tonic-gate 
460Sstevel@tonic-gate /*
470Sstevel@tonic-gate  * ADM1031 is an Intelligent Temperature Monitor and Dual PWM Fan Controller.
480Sstevel@tonic-gate  * The functions supported by the driver are:
490Sstevel@tonic-gate  * 	Reading sensed temperatures.
500Sstevel@tonic-gate  * 	Setting temperature limits which control fan speeds.
510Sstevel@tonic-gate  * 	Reading fan speeds.
520Sstevel@tonic-gate  * 	Setting fan outputs.
530Sstevel@tonic-gate  *	Reading internal registers.
540Sstevel@tonic-gate  *	Setting internal registers.
550Sstevel@tonic-gate  */
560Sstevel@tonic-gate 
570Sstevel@tonic-gate /*
580Sstevel@tonic-gate  * A pointer to an int16_t is expected as an ioctl argument for all temperature
590Sstevel@tonic-gate  * related commands and a pointer to a uint8_t is expected for all other
600Sstevel@tonic-gate  * commands.  If the  parameter is to be read the value is copied into it and
610Sstevel@tonic-gate  * if it is to be written, the integer referred to should have the appropriate
620Sstevel@tonic-gate  * value.
630Sstevel@tonic-gate  *
640Sstevel@tonic-gate  * For all temperature related commands, a temperature minor node should be
650Sstevel@tonic-gate  * passed as the argument to open(2) and correspondingly, a fan minor node
660Sstevel@tonic-gate  * should be used for all fan related commands. Commands which do not fall in
670Sstevel@tonic-gate  * either of the two categories are control commands and involve
680Sstevel@tonic-gate  * reading/writing to the internal registers of the device or switching from
690Sstevel@tonic-gate  * automatic monitoring mode to manual mode and vice-versa. A control minor
700Sstevel@tonic-gate  * node is created by the driver which has to be used for control commands.
710Sstevel@tonic-gate  *
720Sstevel@tonic-gate  * Fan Speed in RPM = (frequency * 60)/Count * N, where Count is the value
730Sstevel@tonic-gate  * received in the fan speed register and N is Speed Range.
740Sstevel@tonic-gate  */
750Sstevel@tonic-gate 
760Sstevel@tonic-gate /*
770Sstevel@tonic-gate  * cb ops
780Sstevel@tonic-gate  */
790Sstevel@tonic-gate static int adm1031_open(dev_t *, int, int, cred_t *);
800Sstevel@tonic-gate static int adm1031_close(dev_t, int, int, cred_t *);
810Sstevel@tonic-gate static int adm1031_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
820Sstevel@tonic-gate 
830Sstevel@tonic-gate /*
840Sstevel@tonic-gate  * dev ops
850Sstevel@tonic-gate  */
860Sstevel@tonic-gate static int adm1031_s_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
870Sstevel@tonic-gate static int adm1031_s_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
880Sstevel@tonic-gate 
890Sstevel@tonic-gate static struct cb_ops adm1031_cb_ops = {
900Sstevel@tonic-gate 	adm1031_open,			/* open */
910Sstevel@tonic-gate 	adm1031_close,			/* close */
920Sstevel@tonic-gate 	nodev,				/* strategy */
930Sstevel@tonic-gate 	nodev,				/* print */
940Sstevel@tonic-gate 	nodev,				/* dump */
950Sstevel@tonic-gate 	nodev,				/* read */
960Sstevel@tonic-gate 	nodev,				/* write */
970Sstevel@tonic-gate 	adm1031_ioctl,			/* ioctl */
980Sstevel@tonic-gate 	nodev,				/* devmap */
990Sstevel@tonic-gate 	nodev,				/* mmap */
1000Sstevel@tonic-gate 	nodev,				/* segmap */
1010Sstevel@tonic-gate 	nochpoll,			/* poll */
1020Sstevel@tonic-gate 	ddi_prop_op,			/* cb_prop_op */
1030Sstevel@tonic-gate 	NULL,				/* streamtab */
1040Sstevel@tonic-gate 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
1050Sstevel@tonic-gate };
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate static struct dev_ops adm1031_dev_ops = {
1080Sstevel@tonic-gate 	DEVO_REV,
1090Sstevel@tonic-gate 	0,
1100Sstevel@tonic-gate 	ddi_no_info,
1110Sstevel@tonic-gate 	nulldev,
1120Sstevel@tonic-gate 	nulldev,
1130Sstevel@tonic-gate 	adm1031_s_attach,
1140Sstevel@tonic-gate 	adm1031_s_detach,
1150Sstevel@tonic-gate 	nodev,
1160Sstevel@tonic-gate 	&adm1031_cb_ops,
117*7656SSherry.Moore@Sun.COM 	NULL,
118*7656SSherry.Moore@Sun.COM 	NULL,
119*7656SSherry.Moore@Sun.COM 	ddi_quiesce_not_supported,	/* devo_quiesce */
1200Sstevel@tonic-gate };
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate static uint8_t adm1031_control_regs[] = {
1230Sstevel@tonic-gate 	0x00,
1240Sstevel@tonic-gate 	ADM1031_STAT_1_REG,
1250Sstevel@tonic-gate 	ADM1031_STAT_2_REG,
1260Sstevel@tonic-gate 	ADM1031_DEVICE_ID_REG,
1270Sstevel@tonic-gate 	ADM1031_CONFIG_REG_1,
1280Sstevel@tonic-gate 	ADM1031_CONFIG_REG_2,
1290Sstevel@tonic-gate 	ADM1031_FAN_CHAR_1_REG,
1300Sstevel@tonic-gate 	ADM1031_FAN_CHAR_2_REG,
1310Sstevel@tonic-gate 	ADM1031_FAN_SPEED_CONFIG_REG,
1320Sstevel@tonic-gate 	ADM1031_FAN_HIGH_LIMIT_1_REG,
1330Sstevel@tonic-gate 	ADM1031_FAN_HIGH_LIMIT_2_REG,
1340Sstevel@tonic-gate 	ADM1031_LOCAL_TEMP_RANGE_REG,
1350Sstevel@tonic-gate 	ADM1031_REMOTE_TEMP_RANGE_1_REG,
1360Sstevel@tonic-gate 	ADM1031_REMOTE_TEMP_RANGE_2_REG,
1370Sstevel@tonic-gate 	ADM1031_EXTD_TEMP_RESL_REG,
1380Sstevel@tonic-gate 	ADM1031_LOCAL_TEMP_OFFSET_REG,
1390Sstevel@tonic-gate 	ADM1031_REMOTE_TEMP_OFFSET_1_REG,
1400Sstevel@tonic-gate 	ADM1031_REMOTE_TEMP_OFFSET_2_REG,
1410Sstevel@tonic-gate 	ADM1031_LOCAL_TEMP_HIGH_LIMIT_REG,
1420Sstevel@tonic-gate 	ADM1031_REMOTE_TEMP_HIGH_LIMIT_1_REG,
1430Sstevel@tonic-gate 	ADM1031_REMOTE_TEMP_HIGH_LIMIT_2_REG,
1440Sstevel@tonic-gate 	ADM1031_LOCAL_TEMP_LOW_LIMIT_REG,
1450Sstevel@tonic-gate 	ADM1031_REMOTE_TEMP_LOW_LIMIT_1_REG,
1460Sstevel@tonic-gate 	ADM1031_REMOTE_TEMP_LOW_LIMIT_2_REG,
1470Sstevel@tonic-gate 	ADM1031_LOCAL_TEMP_THERM_LIMIT_REG,
1480Sstevel@tonic-gate 	ADM1031_REMOTE_TEMP_THERM_LIMIT_1_REG,
1490Sstevel@tonic-gate 	ADM1031_REMOTE_TEMP_THERM_LIMIT_2_REG
1500Sstevel@tonic-gate };
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate static  minor_info	temperatures[ADM1031_TEMP_CHANS] = {
1530Sstevel@tonic-gate 	{"local", ADM1031_LOCAL_TEMP_INST_REG }, /* Local Temperature */
1540Sstevel@tonic-gate 	{"remote_1", ADM1031_REMOTE_TEMP_INST_REG_1 }, /* Remote 1 */
1550Sstevel@tonic-gate 	{"remote_2", ADM1031_REMOTE_TEMP_INST_REG_2 }  /* Remote 2 */
1560Sstevel@tonic-gate };
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate static	minor_info	fans[ADM1031_FAN_SPEED_CHANS] = {
1590Sstevel@tonic-gate 	{"fan_1", ADM1031_FAN_SPEED_INST_REG_1},
1600Sstevel@tonic-gate 	{"fan_2", ADM1031_FAN_SPEED_INST_REG_2}
1610Sstevel@tonic-gate };
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate static struct modldrv adm1031_modldrv = {
1640Sstevel@tonic-gate 	&mod_driverops,		/* type of module - driver */
165*7656SSherry.Moore@Sun.COM 	"adm1031 device driver",
1660Sstevel@tonic-gate 	&adm1031_dev_ops,
1670Sstevel@tonic-gate };
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate static struct modlinkage adm1031_modlinkage = {
1700Sstevel@tonic-gate 	MODREV_1,
1710Sstevel@tonic-gate 	&adm1031_modldrv,
1720Sstevel@tonic-gate 	0
1730Sstevel@tonic-gate };
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate static void *adm1031_soft_statep;
1760Sstevel@tonic-gate int	adm1031_pil = ADM1031_PIL;
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate int
_init(void)1800Sstevel@tonic-gate _init(void)
1810Sstevel@tonic-gate {
1820Sstevel@tonic-gate 	int    err;
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate 	err = mod_install(&adm1031_modlinkage);
1850Sstevel@tonic-gate 	if (err == 0) {
1860Sstevel@tonic-gate 		(void) ddi_soft_state_init(&adm1031_soft_statep,
1870Sstevel@tonic-gate 		    sizeof (adm1031_unit_t), 1);
1880Sstevel@tonic-gate 	}
1890Sstevel@tonic-gate 	return (err);
1900Sstevel@tonic-gate }
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate int
_fini(void)1930Sstevel@tonic-gate _fini(void)
1940Sstevel@tonic-gate {
1950Sstevel@tonic-gate 	int    err;
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate 	err = mod_remove(&adm1031_modlinkage);
1980Sstevel@tonic-gate 	if (err == 0) {
1990Sstevel@tonic-gate 		ddi_soft_state_fini(&adm1031_soft_statep);
2000Sstevel@tonic-gate 	}
2010Sstevel@tonic-gate 	return (err);
2020Sstevel@tonic-gate }
2030Sstevel@tonic-gate 
2040Sstevel@tonic-gate int
_info(struct modinfo * modinfop)2050Sstevel@tonic-gate _info(struct modinfo *modinfop)
2060Sstevel@tonic-gate {
2070Sstevel@tonic-gate 	return (mod_info(&adm1031_modlinkage, modinfop));
2080Sstevel@tonic-gate }
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate static int
adm1031_resume(dev_info_t * dip)2110Sstevel@tonic-gate adm1031_resume(dev_info_t *dip)
2120Sstevel@tonic-gate {
2130Sstevel@tonic-gate 	int 		instance = ddi_get_instance(dip);
2140Sstevel@tonic-gate 	adm1031_unit_t	*admp;
2150Sstevel@tonic-gate 	int 		err = DDI_SUCCESS;
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 	admp = (adm1031_unit_t *)
2180Sstevel@tonic-gate 	    ddi_get_soft_state(adm1031_soft_statep, instance);
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	if (admp == NULL) {
2210Sstevel@tonic-gate 		return (DDI_FAILURE);
2220Sstevel@tonic-gate 	}
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate 	/*
2250Sstevel@tonic-gate 	 * Restore registers to state existing before cpr
2260Sstevel@tonic-gate 	 */
2270Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_flags = I2C_WR;
2280Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wlen = 2;
2290Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_rlen = 0;
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_CONFIG_REG_1;
2320Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wbuf[1] =
2330Sstevel@tonic-gate 	    admp->adm1031_cpr_state.config_reg_1;
2340Sstevel@tonic-gate 	if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
2350Sstevel@tonic-gate 	    DDI_SUCCESS) {
2360Sstevel@tonic-gate 		err = DDI_FAILURE;
2370Sstevel@tonic-gate 		goto done;
2380Sstevel@tonic-gate 	}
2390Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_CONFIG_REG_2;
2400Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wbuf[1] =
2410Sstevel@tonic-gate 	    admp->adm1031_cpr_state.config_reg_2;
2420Sstevel@tonic-gate 	if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
2430Sstevel@tonic-gate 	    DDI_SUCCESS) {
2440Sstevel@tonic-gate 		err = DDI_FAILURE;
2450Sstevel@tonic-gate 		goto done;
2460Sstevel@tonic-gate 	}
2470Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_FAN_SPEED_CONFIG_REG;
2480Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wbuf[1] =
2490Sstevel@tonic-gate 	    admp->adm1031_cpr_state.fan_speed_reg;
2500Sstevel@tonic-gate 	if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
2510Sstevel@tonic-gate 	    DDI_SUCCESS) {
2520Sstevel@tonic-gate 		err = DDI_FAILURE;
2530Sstevel@tonic-gate 		goto done;
2540Sstevel@tonic-gate 	}
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 	/*
2570Sstevel@tonic-gate 	 * Clear busy flag so that transactions may continue
2580Sstevel@tonic-gate 	 */
2590Sstevel@tonic-gate 	mutex_enter(&admp->adm1031_mutex);
2600Sstevel@tonic-gate 	admp->adm1031_flags = admp->adm1031_flags & ~ADM1031_BUSYFLAG;
2610Sstevel@tonic-gate 	cv_signal(&admp->adm1031_cv);
2620Sstevel@tonic-gate 	mutex_exit(&admp->adm1031_mutex);
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate done:
2650Sstevel@tonic-gate 	if (err != DDI_SUCCESS) {
2660Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s:%d Registers not restored correctly",
2670Sstevel@tonic-gate 		    admp->adm1031_name, instance);
2680Sstevel@tonic-gate 	}
2690Sstevel@tonic-gate 	return (err);
2700Sstevel@tonic-gate }
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate static void
adm1031_detach(dev_info_t * dip)2730Sstevel@tonic-gate adm1031_detach(dev_info_t *dip)
2740Sstevel@tonic-gate {
2750Sstevel@tonic-gate 	adm1031_unit_t 	*admp;
2760Sstevel@tonic-gate 	int 		instance = ddi_get_instance(dip);
2770Sstevel@tonic-gate 
2780Sstevel@tonic-gate 	admp = ddi_get_soft_state(adm1031_soft_statep, instance);
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate 	if (admp->adm1031_flags & ADM1031_REGFLAG) {
2810Sstevel@tonic-gate 		i2c_client_unregister(admp->adm1031_hdl);
2820Sstevel@tonic-gate 	}
2830Sstevel@tonic-gate 	if (admp->adm1031_flags & ADM1031_TBUFFLAG) {
2840Sstevel@tonic-gate 		i2c_transfer_free(admp->adm1031_hdl, admp->adm1031_transfer);
2850Sstevel@tonic-gate 	}
2860Sstevel@tonic-gate 	if (admp->adm1031_flags & ADM1031_INTRFLAG) {
2870Sstevel@tonic-gate 		ddi_remove_intr(dip, 0, admp->adm1031_icookie);
2880Sstevel@tonic-gate 		cv_destroy(&admp->adm1031_icv);
2890Sstevel@tonic-gate 		mutex_destroy(&admp->adm1031_imutex);
2900Sstevel@tonic-gate 	}
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 	(void) ddi_prop_remove_all(dip);
2930Sstevel@tonic-gate 	ddi_remove_minor_node(dip, NULL);
2940Sstevel@tonic-gate 	cv_destroy(&admp->adm1031_cv);
2950Sstevel@tonic-gate 	mutex_destroy(&admp->adm1031_mutex);
2960Sstevel@tonic-gate 	ddi_soft_state_free(adm1031_soft_statep, instance);
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate }
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate static uint_t
adm1031_intr(caddr_t arg)3010Sstevel@tonic-gate adm1031_intr(caddr_t arg)
3020Sstevel@tonic-gate {
3030Sstevel@tonic-gate 	adm1031_unit_t	*admp = (adm1031_unit_t *)arg;
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate 	if (admp->adm1031_cvwaiting == 0)
3070Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 	mutex_enter(&admp->adm1031_imutex);
3100Sstevel@tonic-gate 	cv_broadcast(&admp->adm1031_icv);
3110Sstevel@tonic-gate 	admp->adm1031_cvwaiting = 0;
3120Sstevel@tonic-gate 	mutex_exit(&admp->adm1031_imutex);
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
3150Sstevel@tonic-gate }
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate static int
adm1031_attach(dev_info_t * dip)3180Sstevel@tonic-gate adm1031_attach(dev_info_t *dip)
3190Sstevel@tonic-gate {
3200Sstevel@tonic-gate 	adm1031_unit_t 		*admp;
3210Sstevel@tonic-gate 	int 			instance = ddi_get_instance(dip);
3220Sstevel@tonic-gate 	minor_t 		minor;
3230Sstevel@tonic-gate 	int 			i;
3240Sstevel@tonic-gate 	char			*minor_name;
3250Sstevel@tonic-gate 	int			err = 0;
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(adm1031_soft_statep, instance) != 0) {
3280Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s:%d failed to zalloc softstate",
3290Sstevel@tonic-gate 		    ddi_get_name(dip), instance);
3300Sstevel@tonic-gate 		return (DDI_FAILURE);
3310Sstevel@tonic-gate 	}
3320Sstevel@tonic-gate 	admp = ddi_get_soft_state(adm1031_soft_statep, instance);
3330Sstevel@tonic-gate 	if (admp == NULL) {
3340Sstevel@tonic-gate 		return (DDI_FAILURE);
3350Sstevel@tonic-gate 	}
3360Sstevel@tonic-gate 	admp->adm1031_dip = dip;
3370Sstevel@tonic-gate 	mutex_init(&admp->adm1031_mutex, NULL, MUTEX_DRIVER, NULL);
3380Sstevel@tonic-gate 	cv_init(&admp->adm1031_cv, NULL, CV_DRIVER, NULL);
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 	(void) snprintf(admp->adm1031_name, sizeof (admp->adm1031_name),
3410Sstevel@tonic-gate 	    "%s_%d", ddi_driver_name(dip), instance);
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 	/*
3440Sstevel@tonic-gate 	 * Create minor node for all temperature functions.
3450Sstevel@tonic-gate 	 */
3460Sstevel@tonic-gate 	for (i = 0; i < ADM1031_TEMP_CHANS; i++) {
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate 		minor_name = temperatures[i].minor_name;
3490Sstevel@tonic-gate 		minor = ADM1031_INST_TO_MINOR(instance) |
3500Sstevel@tonic-gate 		    ADM1031_FCN_TO_MINOR(ADM1031_TEMPERATURES) |
3510Sstevel@tonic-gate 		    ADM1031_FCNINST_TO_MINOR(i);
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 		if (ddi_create_minor_node(dip, minor_name, S_IFCHR, minor,
3540Sstevel@tonic-gate 		    ADM1031_NODE_TYPE, NULL) == DDI_FAILURE) {
3550Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s:%d ddi_create_minor_node failed",
3560Sstevel@tonic-gate 			    admp->adm1031_name, instance);
3570Sstevel@tonic-gate 			adm1031_detach(dip);
3580Sstevel@tonic-gate 			return (DDI_FAILURE);
3590Sstevel@tonic-gate 		}
3600Sstevel@tonic-gate 	}
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 	/*
3630Sstevel@tonic-gate 	 * Create minor node for all fan functions.
3640Sstevel@tonic-gate 	 */
3650Sstevel@tonic-gate 	for (i = 0; i < ADM1031_FAN_SPEED_CHANS; i++) {
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate 		minor_name = fans[i].minor_name;
3680Sstevel@tonic-gate 		minor = ADM1031_INST_TO_MINOR(instance) |
3690Sstevel@tonic-gate 		    ADM1031_FCN_TO_MINOR(ADM1031_FANS) |
3700Sstevel@tonic-gate 		    ADM1031_FCNINST_TO_MINOR(i);
3710Sstevel@tonic-gate 
3720Sstevel@tonic-gate 		if (ddi_create_minor_node(dip, minor_name, S_IFCHR, minor,
3730Sstevel@tonic-gate 		    ADM1031_NODE_TYPE, NULL) == DDI_FAILURE) {
3740Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s:%d ddi_create_minor_node failed",
3750Sstevel@tonic-gate 			    admp->adm1031_name, instance);
3760Sstevel@tonic-gate 			adm1031_detach(dip);
3770Sstevel@tonic-gate 			return (DDI_FAILURE);
3780Sstevel@tonic-gate 		}
3790Sstevel@tonic-gate 	}
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate 	/*
3820Sstevel@tonic-gate 	 * Create minor node for all control functions.
3830Sstevel@tonic-gate 	 */
3840Sstevel@tonic-gate 	minor = ADM1031_INST_TO_MINOR(instance) |
3850Sstevel@tonic-gate 	    ADM1031_FCN_TO_MINOR(ADM1031_CONTROL) |
3860Sstevel@tonic-gate 	    ADM1031_FCNINST_TO_MINOR(0);
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 	if (ddi_create_minor_node(dip, "control", S_IFCHR, minor,
3890Sstevel@tonic-gate 	    ADM1031_NODE_TYPE, NULL) == DDI_FAILURE) {
3900Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s:%d ddi_create_minor_node failed",
3910Sstevel@tonic-gate 		    admp->adm1031_name, instance);
3920Sstevel@tonic-gate 		adm1031_detach(dip);
3930Sstevel@tonic-gate 		return (DDI_FAILURE);
3940Sstevel@tonic-gate 	}
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate 	/*
3970Sstevel@tonic-gate 	 * preallocate a single buffer for all reads and writes
3980Sstevel@tonic-gate 	 */
3990Sstevel@tonic-gate 	if (i2c_transfer_alloc(admp->adm1031_hdl, &admp->adm1031_transfer,
4000Sstevel@tonic-gate 	    ADM1031_MAX_XFER, ADM1031_MAX_XFER, I2C_SLEEP) != I2C_SUCCESS) {
4010Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s:%d i2c_transfer_alloc failed",
4020Sstevel@tonic-gate 		    admp->adm1031_name, instance);
4030Sstevel@tonic-gate 		adm1031_detach(dip);
4040Sstevel@tonic-gate 		return (DDI_FAILURE);
4050Sstevel@tonic-gate 	}
4060Sstevel@tonic-gate 	admp->adm1031_flags |= ADM1031_TBUFFLAG;
4070Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_version = I2C_XFER_REV;
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate 	if (i2c_client_register(dip, &admp->adm1031_hdl) != I2C_SUCCESS) {
4100Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s:%d i2c_client_register failed",
4110Sstevel@tonic-gate 		    admp->adm1031_name, instance);
4120Sstevel@tonic-gate 		adm1031_detach(dip);
4130Sstevel@tonic-gate 		return (DDI_FAILURE);
4140Sstevel@tonic-gate 	}
4150Sstevel@tonic-gate 	admp->adm1031_flags |= ADM1031_REGFLAG;
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip,
4180Sstevel@tonic-gate 	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
4190Sstevel@tonic-gate 	    "interrupt-priorities") != 1) {
4200Sstevel@tonic-gate 		(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
4210Sstevel@tonic-gate 		    DDI_PROP_CANSLEEP, "interrupt-priorities",
4220Sstevel@tonic-gate 		    (void *)&adm1031_pil, sizeof (adm1031_pil));
4230Sstevel@tonic-gate 	}
4240Sstevel@tonic-gate 	err = ddi_get_iblock_cookie(dip, 0, &admp->adm1031_icookie);
4250Sstevel@tonic-gate 	if (err == DDI_SUCCESS) {
4260Sstevel@tonic-gate 		mutex_init(&admp->adm1031_imutex, NULL, MUTEX_DRIVER,
4270Sstevel@tonic-gate 		    (void *)admp->adm1031_icookie);
4280Sstevel@tonic-gate 		cv_init(&admp->adm1031_icv, NULL, CV_DRIVER, NULL);
4290Sstevel@tonic-gate 		if (ddi_add_intr(dip, 0, NULL, NULL, adm1031_intr,
4300Sstevel@tonic-gate 		    (caddr_t)admp) == DDI_SUCCESS) {
4310Sstevel@tonic-gate 			admp->adm1031_flags |= ADM1031_INTRFLAG;
4320Sstevel@tonic-gate 		} else {
4330Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s:%d failed to add interrupt",
4340Sstevel@tonic-gate 			    admp->adm1031_name, instance);
4350Sstevel@tonic-gate 		}
4360Sstevel@tonic-gate 	}
4370Sstevel@tonic-gate 
4380Sstevel@tonic-gate 	/*
4390Sstevel@tonic-gate 	 * The system comes up in Automatic Monitor Mode.
4400Sstevel@tonic-gate 	 */
4410Sstevel@tonic-gate 	admp->adm1031_flags |= ADM1031_AUTOFLAG;
4420Sstevel@tonic-gate 
4430Sstevel@tonic-gate 	return (DDI_SUCCESS);
4440Sstevel@tonic-gate }
4450Sstevel@tonic-gate 
4460Sstevel@tonic-gate static int
adm1031_s_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)4470Sstevel@tonic-gate adm1031_s_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4480Sstevel@tonic-gate {
4490Sstevel@tonic-gate 	switch (cmd) {
4500Sstevel@tonic-gate 	case DDI_ATTACH:
4510Sstevel@tonic-gate 		return (adm1031_attach(dip));
4520Sstevel@tonic-gate 	case DDI_RESUME:
4530Sstevel@tonic-gate 		return (adm1031_resume(dip));
4540Sstevel@tonic-gate 	default:
4550Sstevel@tonic-gate 		return (DDI_FAILURE);
4560Sstevel@tonic-gate 	}
4570Sstevel@tonic-gate }
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate static int
adm1031_suspend(dev_info_t * dip)4600Sstevel@tonic-gate adm1031_suspend(dev_info_t *dip)
4610Sstevel@tonic-gate {
4620Sstevel@tonic-gate 	adm1031_unit_t 	*admp;
4630Sstevel@tonic-gate 	int 		instance = ddi_get_instance(dip);
4640Sstevel@tonic-gate 	int		err = DDI_SUCCESS;
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate 	admp = ddi_get_soft_state(adm1031_soft_statep, instance);
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate 	/*
4690Sstevel@tonic-gate 	 * Set the busy flag so that future transactions block
4700Sstevel@tonic-gate 	 * until resume.
4710Sstevel@tonic-gate 	 */
4720Sstevel@tonic-gate 	mutex_enter(&admp->adm1031_mutex);
4730Sstevel@tonic-gate 	while (admp->adm1031_flags & ADM1031_BUSYFLAG) {
4740Sstevel@tonic-gate 		if (cv_wait_sig(&admp->adm1031_cv,
475*7656SSherry.Moore@Sun.COM 		    &admp->adm1031_mutex) <= 0) {
4760Sstevel@tonic-gate 			mutex_exit(&admp->adm1031_mutex);
4770Sstevel@tonic-gate 			return (DDI_FAILURE);
4780Sstevel@tonic-gate 		}
4790Sstevel@tonic-gate 	}
4800Sstevel@tonic-gate 	admp->adm1031_flags |= ADM1031_BUSYFLAG;
4810Sstevel@tonic-gate 	mutex_exit(&admp->adm1031_mutex);
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 	/*
4840Sstevel@tonic-gate 	 * Save the state of the threshold registers.
4850Sstevel@tonic-gate 	 */
4860Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_flags = I2C_WR_RD;
4870Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wlen = 1;
4880Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_rlen = 1;
4890Sstevel@tonic-gate 
4900Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_CONFIG_REG_1;
4910Sstevel@tonic-gate 	if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
4920Sstevel@tonic-gate 	    DDI_SUCCESS) {
4930Sstevel@tonic-gate 		err = DDI_FAILURE;
4940Sstevel@tonic-gate 		goto done;
4950Sstevel@tonic-gate 	}
4960Sstevel@tonic-gate 	admp->adm1031_cpr_state.config_reg_1 =
4970Sstevel@tonic-gate 	    admp->adm1031_transfer->i2c_rbuf[0];
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_CONFIG_REG_2;
5000Sstevel@tonic-gate 	if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
5010Sstevel@tonic-gate 	    DDI_SUCCESS) {
5020Sstevel@tonic-gate 		err = DDI_FAILURE;
5030Sstevel@tonic-gate 		goto done;
5040Sstevel@tonic-gate 	}
5050Sstevel@tonic-gate 	admp->adm1031_cpr_state.config_reg_2 =
5060Sstevel@tonic-gate 	    admp->adm1031_transfer->i2c_rbuf[0];
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_FAN_SPEED_CONFIG_REG;
5090Sstevel@tonic-gate 	if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
5100Sstevel@tonic-gate 	    DDI_SUCCESS) {
5110Sstevel@tonic-gate 		err = DDI_FAILURE;
5120Sstevel@tonic-gate 		goto done;
5130Sstevel@tonic-gate 	}
5140Sstevel@tonic-gate 	admp->adm1031_cpr_state.fan_speed_reg =
5150Sstevel@tonic-gate 	    admp->adm1031_transfer->i2c_rbuf[0];
5160Sstevel@tonic-gate done:
5170Sstevel@tonic-gate 	if (err != DDI_SUCCESS) {
5180Sstevel@tonic-gate 		mutex_enter(&admp->adm1031_mutex);
5190Sstevel@tonic-gate 		admp->adm1031_flags = admp->adm1031_flags & ~ADM1031_BUSYFLAG;
5200Sstevel@tonic-gate 		cv_broadcast(&admp->adm1031_cv);
5210Sstevel@tonic-gate 		mutex_exit(&admp->adm1031_mutex);
5220Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s:%d Suspend failed,\
5230Sstevel@tonic-gate 		    unable to save registers", admp->adm1031_name, instance);
5240Sstevel@tonic-gate 	}
5250Sstevel@tonic-gate 	return (err);
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate }
5280Sstevel@tonic-gate 
5290Sstevel@tonic-gate static int
adm1031_s_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)5300Sstevel@tonic-gate adm1031_s_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
5310Sstevel@tonic-gate {
5320Sstevel@tonic-gate 	switch (cmd) {
5330Sstevel@tonic-gate 	case DDI_DETACH:
5340Sstevel@tonic-gate 		adm1031_detach(dip);
5350Sstevel@tonic-gate 		return (DDI_SUCCESS);
5360Sstevel@tonic-gate 	case DDI_SUSPEND:
5370Sstevel@tonic-gate 		return (adm1031_suspend(dip));
5380Sstevel@tonic-gate 	default:
5390Sstevel@tonic-gate 		return (DDI_FAILURE);
5400Sstevel@tonic-gate 	}
5410Sstevel@tonic-gate }
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate static int
adm1031_open(dev_t * devp,int flags,int otyp,cred_t * credp)5440Sstevel@tonic-gate adm1031_open(dev_t *devp, int flags, int otyp, cred_t *credp)
5450Sstevel@tonic-gate {
5460Sstevel@tonic-gate 	int			instance;
5470Sstevel@tonic-gate 	adm1031_unit_t		*admp;
5480Sstevel@tonic-gate 	int			err = EBUSY;
5490Sstevel@tonic-gate 
5500Sstevel@tonic-gate 	/* must be root to access this device */
5510Sstevel@tonic-gate 	if (drv_priv(credp) != 0) {
5520Sstevel@tonic-gate 		return (EPERM);
5530Sstevel@tonic-gate 	}
5540Sstevel@tonic-gate 
5550Sstevel@tonic-gate 	/*
5560Sstevel@tonic-gate 	 * Make sure the open is for the right file type
5570Sstevel@tonic-gate 	 */
5580Sstevel@tonic-gate 	if (otyp != OTYP_CHR) {
5590Sstevel@tonic-gate 		return (EINVAL);
5600Sstevel@tonic-gate 	}
5610Sstevel@tonic-gate 	instance = ADM1031_MINOR_TO_INST(getminor(*devp));
5620Sstevel@tonic-gate 	admp = (adm1031_unit_t *)
5630Sstevel@tonic-gate 	    ddi_get_soft_state(adm1031_soft_statep, instance);
5640Sstevel@tonic-gate 	if (admp == NULL) {
5650Sstevel@tonic-gate 		return (ENXIO);
5660Sstevel@tonic-gate 	}
5670Sstevel@tonic-gate 
5680Sstevel@tonic-gate 	/*
5690Sstevel@tonic-gate 	 * Enforce exclusive access if required.
5700Sstevel@tonic-gate 	 */
5710Sstevel@tonic-gate 	mutex_enter(&admp->adm1031_mutex);
5720Sstevel@tonic-gate 	if (flags & FEXCL) {
5730Sstevel@tonic-gate 		if (admp->adm1031_oflag == 0) {
5740Sstevel@tonic-gate 			admp->adm1031_oflag = FEXCL;
5750Sstevel@tonic-gate 			err = 0;
5760Sstevel@tonic-gate 		}
5770Sstevel@tonic-gate 	} else if (admp->adm1031_oflag != FEXCL) {
5780Sstevel@tonic-gate 		admp->adm1031_oflag = FOPEN;
5790Sstevel@tonic-gate 		err = 0;
5800Sstevel@tonic-gate 	}
5810Sstevel@tonic-gate 	mutex_exit(&admp->adm1031_mutex);
5820Sstevel@tonic-gate 	return (err);
5830Sstevel@tonic-gate }
5840Sstevel@tonic-gate 
5850Sstevel@tonic-gate static int
adm1031_close(dev_t dev,int flags,int otyp,cred_t * credp)5860Sstevel@tonic-gate adm1031_close(dev_t dev, int flags, int otyp, cred_t *credp)
5870Sstevel@tonic-gate {
5880Sstevel@tonic-gate 	int		instance;
5890Sstevel@tonic-gate 	adm1031_unit_t 	*admp;
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate 	_NOTE(ARGUNUSED(flags, otyp, credp))
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate 	instance = ADM1031_MINOR_TO_INST(getminor(dev));
5940Sstevel@tonic-gate 	admp = (adm1031_unit_t *)
5950Sstevel@tonic-gate 	    ddi_get_soft_state(adm1031_soft_statep, instance);
5960Sstevel@tonic-gate 	if (admp == NULL) {
5970Sstevel@tonic-gate 		return (ENXIO);
5980Sstevel@tonic-gate 	}
5990Sstevel@tonic-gate 
6000Sstevel@tonic-gate 	mutex_enter(&admp->adm1031_mutex);
6010Sstevel@tonic-gate 	admp->adm1031_oflag = 0;
6020Sstevel@tonic-gate 	mutex_exit(&admp->adm1031_mutex);
6030Sstevel@tonic-gate 	return (0);
6040Sstevel@tonic-gate }
6050Sstevel@tonic-gate 
6060Sstevel@tonic-gate static int
adm1031_s_ioctl(dev_t dev,int cmd,intptr_t arg,int mode)6070Sstevel@tonic-gate adm1031_s_ioctl(dev_t dev, int cmd, intptr_t arg, int mode)
6080Sstevel@tonic-gate {
6090Sstevel@tonic-gate 	adm1031_unit_t	*admp;
6100Sstevel@tonic-gate 	int		err = 0, cmd_c = 0;
6110Sstevel@tonic-gate 	uint8_t		speed = 0, f_set = 0, temp = 0, write_value = 0;
6120Sstevel@tonic-gate 	int16_t		temp16 = 0, write_value16 = 0;
6130Sstevel@tonic-gate 	minor_t		minor = getminor(dev);
6140Sstevel@tonic-gate 	int		instance = ADM1031_MINOR_TO_INST(minor);
6150Sstevel@tonic-gate 	int		fcn = ADM1031_MINOR_TO_FCN(minor);
6160Sstevel@tonic-gate 	int		fcn_inst = ADM1031_MINOR_TO_FCNINST(minor);
6170Sstevel@tonic-gate 
6180Sstevel@tonic-gate 	admp = (adm1031_unit_t *)
6190Sstevel@tonic-gate 	    ddi_get_soft_state(adm1031_soft_statep, instance);
6200Sstevel@tonic-gate 
6210Sstevel@tonic-gate 	/*
6220Sstevel@tonic-gate 	 * We serialize here and block pending transactions.
6230Sstevel@tonic-gate 	 */
6240Sstevel@tonic-gate 	mutex_enter(&admp->adm1031_mutex);
6250Sstevel@tonic-gate 	while (admp->adm1031_flags & ADM1031_BUSYFLAG) {
6260Sstevel@tonic-gate 		if (cv_wait_sig(&admp->adm1031_cv,
6270Sstevel@tonic-gate 		    &admp->adm1031_mutex) <= 0) {
6280Sstevel@tonic-gate 			mutex_exit(&admp->adm1031_mutex);
6290Sstevel@tonic-gate 			return (EINTR);
6300Sstevel@tonic-gate 		}
6310Sstevel@tonic-gate 	}
6320Sstevel@tonic-gate 	admp->adm1031_flags |= ADM1031_BUSYFLAG;
6330Sstevel@tonic-gate 	mutex_exit(&admp->adm1031_mutex);
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 	switch (fcn) {
6360Sstevel@tonic-gate 	case ADM1031_TEMPERATURES:
6370Sstevel@tonic-gate 		if (cmd == I2C_GET_TEMPERATURE) {
6380Sstevel@tonic-gate 			admp->adm1031_transfer->i2c_wbuf[0] =
6390Sstevel@tonic-gate 			    temperatures[fcn_inst].reg;
6400Sstevel@tonic-gate 			goto copyout;
6410Sstevel@tonic-gate 		} else {
6420Sstevel@tonic-gate 			cmd = cmd - ADM1031_PVT_BASE_IOCTL;
6430Sstevel@tonic-gate 			cmd_c = ADM1031_CHECK_FOR_WRITES(cmd) ?
6440Sstevel@tonic-gate 			    (cmd - ADM1031_WRITE_COMMAND_BASE) + fcn_inst :
6450Sstevel@tonic-gate 			    cmd + fcn_inst;
6460Sstevel@tonic-gate 			if (!ADM1031_CHECK_TEMPERATURE_CMD(cmd_c)) {
6470Sstevel@tonic-gate 				err = EINVAL;
6480Sstevel@tonic-gate 				goto done;
6490Sstevel@tonic-gate 			}
6500Sstevel@tonic-gate 			admp->adm1031_transfer->i2c_wbuf[0] =
6510Sstevel@tonic-gate 			    adm1031_control_regs[cmd_c];
6520Sstevel@tonic-gate 			if (ADM1031_CHECK_FOR_WRITES(cmd))
6530Sstevel@tonic-gate 				goto writes;
6540Sstevel@tonic-gate 			else
6550Sstevel@tonic-gate 				goto copyout;
6560Sstevel@tonic-gate 		}
6570Sstevel@tonic-gate 	case ADM1031_FANS:
6580Sstevel@tonic-gate 		if (cmd == I2C_GET_FAN_SPEED) {
6590Sstevel@tonic-gate 			admp->adm1031_transfer->i2c_wbuf[0] =
6600Sstevel@tonic-gate 			    fans[fcn_inst].reg;
6610Sstevel@tonic-gate 			goto copyout;
6620Sstevel@tonic-gate 		} else if (cmd == ADM1031_GET_FAN_CONFIG) {
6630Sstevel@tonic-gate 			admp->adm1031_transfer->i2c_wbuf[0] =
6640Sstevel@tonic-gate 			    ADM1031_FAN_SPEED_CONFIG_REG;
6650Sstevel@tonic-gate 			goto copyout;
6660Sstevel@tonic-gate 		} else if (cmd == I2C_SET_FAN_SPEED) {
6670Sstevel@tonic-gate 			if (ddi_copyin((void *)arg, &write_value,
6680Sstevel@tonic-gate 			    sizeof (write_value), mode) != DDI_SUCCESS) {
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate 				err = EFAULT;
6710Sstevel@tonic-gate 				goto done;
6720Sstevel@tonic-gate 			}
6730Sstevel@tonic-gate 			speed = write_value;
6740Sstevel@tonic-gate 			if ((admp->adm1031_flags & ADM1031_AUTOFLAG)) {
6750Sstevel@tonic-gate 				err = EBUSY;
6760Sstevel@tonic-gate 				goto done;
6770Sstevel@tonic-gate 			}
6780Sstevel@tonic-gate 			if (ADM1031_CHECK_INVALID_SPEED(speed)) {
6790Sstevel@tonic-gate 				err = EINVAL;
6800Sstevel@tonic-gate 				goto done;
6810Sstevel@tonic-gate 			}
6820Sstevel@tonic-gate 			admp->adm1031_transfer->i2c_wbuf[0] =
6830Sstevel@tonic-gate 			    ADM1031_FAN_SPEED_CONFIG_REG;
6840Sstevel@tonic-gate 			admp->adm1031_transfer->i2c_flags = I2C_WR_RD;
6850Sstevel@tonic-gate 			admp->adm1031_transfer->i2c_wlen = 1;
6860Sstevel@tonic-gate 			admp->adm1031_transfer->i2c_rlen = 1;
6870Sstevel@tonic-gate 			if (i2c_transfer(admp->adm1031_hdl,
6880Sstevel@tonic-gate 			    admp->adm1031_transfer) != I2C_SUCCESS) {
6890Sstevel@tonic-gate 				err = EIO;
6900Sstevel@tonic-gate 				goto done;
6910Sstevel@tonic-gate 			}
6920Sstevel@tonic-gate 			f_set = admp->adm1031_transfer->i2c_rbuf[0];
6930Sstevel@tonic-gate 			f_set = (fcn_inst == 0) ? (MLSN(f_set) | speed):
6940Sstevel@tonic-gate 			    (MMSN(f_set) | (speed << 4));
6950Sstevel@tonic-gate 
6960Sstevel@tonic-gate 			admp->adm1031_transfer->i2c_wbuf[1] = f_set;
6970Sstevel@tonic-gate 			admp->adm1031_transfer->i2c_flags = I2C_WR;
6980Sstevel@tonic-gate 			admp->adm1031_transfer->i2c_wlen = 2;
6990Sstevel@tonic-gate 			if (i2c_transfer(admp->adm1031_hdl,
7000Sstevel@tonic-gate 			    admp->adm1031_transfer) != I2C_SUCCESS) {
7010Sstevel@tonic-gate 				err = EIO;
7020Sstevel@tonic-gate 			}
7030Sstevel@tonic-gate 			goto done;
7040Sstevel@tonic-gate 		}
7050Sstevel@tonic-gate 		cmd = cmd - ADM1031_PVT_BASE_IOCTL;
7060Sstevel@tonic-gate 		cmd_c = ADM1031_CHECK_FOR_WRITES(cmd) ?
7070Sstevel@tonic-gate 		    (cmd - ADM1031_WRITE_COMMAND_BASE) + fcn_inst :
7080Sstevel@tonic-gate 		    cmd + fcn_inst;
7090Sstevel@tonic-gate 		if (!ADM1031_CHECK_FAN_CMD(cmd_c)) {
7100Sstevel@tonic-gate 			err = EINVAL;
7110Sstevel@tonic-gate 			goto done;
7120Sstevel@tonic-gate 		}
7130Sstevel@tonic-gate 		admp->adm1031_transfer->i2c_wbuf[0] =
7140Sstevel@tonic-gate 		    adm1031_control_regs[cmd_c];
7150Sstevel@tonic-gate 		if (ADM1031_CHECK_FOR_WRITES(cmd))
7160Sstevel@tonic-gate 			goto writes;
7170Sstevel@tonic-gate 		else
7180Sstevel@tonic-gate 			goto copyout;
7190Sstevel@tonic-gate 	case ADM1031_CONTROL:
7200Sstevel@tonic-gate 
7210Sstevel@tonic-gate 		/*
7220Sstevel@tonic-gate 		 * Read the primary configuration register in advance.
7230Sstevel@tonic-gate 		 */
7240Sstevel@tonic-gate 		admp->adm1031_transfer->i2c_wbuf[0] =
7250Sstevel@tonic-gate 		    ADM1031_CONFIG_REG_1;
7260Sstevel@tonic-gate 		admp->adm1031_transfer->i2c_flags = I2C_WR_RD;
7270Sstevel@tonic-gate 		admp->adm1031_transfer->i2c_wlen = 1;
7280Sstevel@tonic-gate 		admp->adm1031_transfer->i2c_rlen = 1;
7290Sstevel@tonic-gate 		if (i2c_transfer(admp->adm1031_hdl,
7300Sstevel@tonic-gate 		    admp->adm1031_transfer) != I2C_SUCCESS) {
7310Sstevel@tonic-gate 			err = EIO;
7320Sstevel@tonic-gate 			goto done;
7330Sstevel@tonic-gate 		}
7340Sstevel@tonic-gate 		switch (cmd) {
7350Sstevel@tonic-gate 		case ADM1031_GET_MONITOR_MODE:
7360Sstevel@tonic-gate 			temp = ADM1031_AUTOFLAG &
7370Sstevel@tonic-gate 			    admp->adm1031_transfer->i2c_rbuf[0];
7380Sstevel@tonic-gate 			temp = temp >> 7;
7390Sstevel@tonic-gate 			if (ddi_copyout((void *)&temp, (void *)arg,
7400Sstevel@tonic-gate 			    sizeof (temp), mode) != DDI_SUCCESS) {
7410Sstevel@tonic-gate 				err = EFAULT;
7420Sstevel@tonic-gate 			}
7430Sstevel@tonic-gate 			goto done;
7440Sstevel@tonic-gate 		case ADM1031_SET_MONITOR_MODE:
7450Sstevel@tonic-gate 			if (ddi_copyin((void *)arg, &write_value,
7460Sstevel@tonic-gate 			    sizeof (write_value), mode) != DDI_SUCCESS) {
7470Sstevel@tonic-gate 				err = EFAULT;
7480Sstevel@tonic-gate 				goto done;
7490Sstevel@tonic-gate 			}
7500Sstevel@tonic-gate 			if (write_value == ADM1031_AUTO_MODE) {
7510Sstevel@tonic-gate 				temp = ADM1031_AUTOFLAG |
7520Sstevel@tonic-gate 				    admp->adm1031_transfer->i2c_rbuf[0];
7530Sstevel@tonic-gate 				admp->adm1031_flags |= ADM1031_AUTOFLAG;
7540Sstevel@tonic-gate 			} else if (write_value == ADM1031_MANUAL_MODE) {
7550Sstevel@tonic-gate 				temp = admp->adm1031_transfer->i2c_rbuf[0] &
756*7656SSherry.Moore@Sun.COM 				    (~ADM1031_AUTOFLAG);
7570Sstevel@tonic-gate 				admp->adm1031_flags &= ~ADM1031_AUTOFLAG;
7580Sstevel@tonic-gate 			} else {
7590Sstevel@tonic-gate 				err = EINVAL;
7600Sstevel@tonic-gate 				goto done;
7610Sstevel@tonic-gate 			}
7620Sstevel@tonic-gate 			admp->adm1031_transfer->i2c_wbuf[1] = temp;
7630Sstevel@tonic-gate 			admp->adm1031_transfer->i2c_flags = I2C_WR;
7640Sstevel@tonic-gate 			admp->adm1031_transfer->i2c_wlen = 2;
7650Sstevel@tonic-gate 			if (i2c_transfer(admp->adm1031_hdl,
7660Sstevel@tonic-gate 			    admp->adm1031_transfer) != I2C_SUCCESS) {
7670Sstevel@tonic-gate 				err = EIO;
7680Sstevel@tonic-gate 			}
7690Sstevel@tonic-gate 			goto done;
7700Sstevel@tonic-gate 		default:
7710Sstevel@tonic-gate 			goto control;
7720Sstevel@tonic-gate 		}
7730Sstevel@tonic-gate 	default:
7740Sstevel@tonic-gate 		err = EINVAL;
7750Sstevel@tonic-gate 		goto done;
7760Sstevel@tonic-gate 	}
7770Sstevel@tonic-gate 
7780Sstevel@tonic-gate control:
7790Sstevel@tonic-gate 	cmd = cmd - ADM1031_PVT_BASE_IOCTL;
7800Sstevel@tonic-gate 
7810Sstevel@tonic-gate 	if (ADM1031_CHECK_FOR_WRITES(cmd)) {
7820Sstevel@tonic-gate 		cmd_c = (cmd - ADM1031_WRITE_COMMAND_BASE) + fcn_inst;
7830Sstevel@tonic-gate 		admp->adm1031_transfer->i2c_wbuf[0] =
7840Sstevel@tonic-gate 		    adm1031_control_regs[cmd_c];
7850Sstevel@tonic-gate 
7860Sstevel@tonic-gate 		goto writes;
7870Sstevel@tonic-gate 	}
7880Sstevel@tonic-gate 	cmd_c = cmd  + fcn_inst;
7890Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wbuf[0] = adm1031_control_regs[cmd_c];
7900Sstevel@tonic-gate 	goto copyout;
7910Sstevel@tonic-gate 
7920Sstevel@tonic-gate writes:
7930Sstevel@tonic-gate 	if (fcn == ADM1031_TEMPERATURES) {
7940Sstevel@tonic-gate 		if (ddi_copyin((void *)arg, &write_value16,
7950Sstevel@tonic-gate 		    sizeof (write_value16), mode) != DDI_SUCCESS) {
7960Sstevel@tonic-gate 
7970Sstevel@tonic-gate 			err = EFAULT;
7980Sstevel@tonic-gate 			goto done;
7990Sstevel@tonic-gate 		}
8000Sstevel@tonic-gate 		write_value = (uint8_t)((int8_t)(write_value16));
8010Sstevel@tonic-gate 	} else {
8020Sstevel@tonic-gate 		if (ddi_copyin((void *)arg, &write_value,
8030Sstevel@tonic-gate 		    sizeof (write_value), mode) != DDI_SUCCESS) {
8040Sstevel@tonic-gate 
8050Sstevel@tonic-gate 			err = EFAULT;
8060Sstevel@tonic-gate 			goto done;
8070Sstevel@tonic-gate 		}
8080Sstevel@tonic-gate 	}
8090Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_flags = I2C_WR;
8100Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wlen = 2;
8110Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_rlen = 0;
8120Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wbuf[1] = write_value;
8130Sstevel@tonic-gate 	if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
8140Sstevel@tonic-gate 	    I2C_SUCCESS) {
8150Sstevel@tonic-gate 
8160Sstevel@tonic-gate 		err = EIO;
8170Sstevel@tonic-gate 	}
8180Sstevel@tonic-gate 	goto done;
8190Sstevel@tonic-gate 
8200Sstevel@tonic-gate copyout:
8210Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_flags = I2C_WR_RD;
8220Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wlen = 1;
8230Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_rlen = 1;
8240Sstevel@tonic-gate 	if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
8250Sstevel@tonic-gate 	    I2C_SUCCESS) {
8260Sstevel@tonic-gate 
8270Sstevel@tonic-gate 		err = EIO;
8280Sstevel@tonic-gate 		goto done;
8290Sstevel@tonic-gate 	}
8300Sstevel@tonic-gate 	temp = admp->adm1031_transfer->i2c_rbuf[0];
8310Sstevel@tonic-gate 	if (fcn == ADM1031_TEMPERATURES) {
8320Sstevel@tonic-gate 		/*
8330Sstevel@tonic-gate 		 * Workaround for bug in ADM1031 which reports -128 (0x80)
8340Sstevel@tonic-gate 		 * when the temperature transitions from 0C to -1C.
8350Sstevel@tonic-gate 		 * All other -ve temperatures are not affected. We map
8360Sstevel@tonic-gate 		 * 0x80 to 0xFF(-1) since we don't ever expect to see -128C on a
8370Sstevel@tonic-gate 		 * sensor.
8380Sstevel@tonic-gate 		 */
8390Sstevel@tonic-gate 		if (temp == 0x80) {
8400Sstevel@tonic-gate 			temp = 0xFF;
8410Sstevel@tonic-gate 		}
8420Sstevel@tonic-gate 		temp16 = (int16_t)((int8_t)temp);
8430Sstevel@tonic-gate 		if (ddi_copyout((void *)&temp16, (void *)arg, sizeof (temp16),
8440Sstevel@tonic-gate 		    mode) != DDI_SUCCESS)
8450Sstevel@tonic-gate 			err = EFAULT;
8460Sstevel@tonic-gate 	} else {
8470Sstevel@tonic-gate 		if (ddi_copyout((void *)&temp, (void *)arg, sizeof (temp),
8480Sstevel@tonic-gate 		    mode) != DDI_SUCCESS)
8490Sstevel@tonic-gate 			err = EFAULT;
8500Sstevel@tonic-gate 	}
8510Sstevel@tonic-gate 
8520Sstevel@tonic-gate done:
8530Sstevel@tonic-gate 	mutex_enter(&admp->adm1031_mutex);
8540Sstevel@tonic-gate 	admp->adm1031_flags = admp->adm1031_flags & (~ADM1031_BUSYFLAG);
8550Sstevel@tonic-gate 	cv_signal(&admp->adm1031_cv);
8560Sstevel@tonic-gate 	mutex_exit(&admp->adm1031_mutex);
8570Sstevel@tonic-gate 	return (err);
8580Sstevel@tonic-gate }
8590Sstevel@tonic-gate 
8600Sstevel@tonic-gate /*
8610Sstevel@tonic-gate  * The interrupt ioctl is a private handshake between the user and the driver
8620Sstevel@tonic-gate  * and is a mechanism to asynchronously inform the user of a system event such
8630Sstevel@tonic-gate  * as a fan fault or a temperature limit being exceeded.
8640Sstevel@tonic-gate  *
8650Sstevel@tonic-gate  * Step 1):
8660Sstevel@tonic-gate  *	User(or environmental monitoring software) calls the ioctl routine
8670Sstevel@tonic-gate  *	which blocks as it waits on a condition. The open(2) call has to be
8680Sstevel@tonic-gate  *	called with the _control minor node. The ioctl routine requires
8690Sstevel@tonic-gate  *	ADM1031_INTERRUPT_WAIT as the command and a pointer to an array of
8700Sstevel@tonic-gate  *	uint8_t as the third argument.
8710Sstevel@tonic-gate  * Step 2):
8720Sstevel@tonic-gate  *	A system event occurs which unblocks the ioctl and returns the call
8730Sstevel@tonic-gate  *	to the user.
8740Sstevel@tonic-gate  * Step 3):
8750Sstevel@tonic-gate  *	User reads the contents of the array (which actually contains the values
8760Sstevel@tonic-gate  *	of the devices' status registers) to determine the exact nature of the
8770Sstevel@tonic-gate  * 	event.
8780Sstevel@tonic-gate  */
8790Sstevel@tonic-gate static int
adm1031_i_ioctl(dev_t dev,int cmd,intptr_t arg,int mode)8800Sstevel@tonic-gate adm1031_i_ioctl(dev_t dev, int cmd, intptr_t arg, int mode)
8810Sstevel@tonic-gate {
8820Sstevel@tonic-gate 	_NOTE(ARGUNUSED(cmd))
8830Sstevel@tonic-gate 	adm1031_unit_t	*admp;
8840Sstevel@tonic-gate 	uint8_t		i = 0;
8850Sstevel@tonic-gate 	minor_t		minor = getminor(dev);
8860Sstevel@tonic-gate 	int		fcn = ADM1031_MINOR_TO_FCN(minor);
8870Sstevel@tonic-gate 	int		instance = ADM1031_MINOR_TO_INST(minor);
8880Sstevel@tonic-gate 	int		err = 0;
8890Sstevel@tonic-gate 	uint8_t		temp[2];
8900Sstevel@tonic-gate 	uint8_t		temp1;
8910Sstevel@tonic-gate 
8920Sstevel@tonic-gate 
8930Sstevel@tonic-gate 	if (fcn != ADM1031_CONTROL)
8940Sstevel@tonic-gate 		return (EINVAL);
8950Sstevel@tonic-gate 
8960Sstevel@tonic-gate 	admp = (adm1031_unit_t *)
8970Sstevel@tonic-gate 	    ddi_get_soft_state(adm1031_soft_statep, instance);
8980Sstevel@tonic-gate 
8990Sstevel@tonic-gate 	if (!(admp->adm1031_flags & ADM1031_INTRFLAG)) {
9000Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s:%d No interrupt handler registered\n",
9010Sstevel@tonic-gate 		    admp->adm1031_name, instance);
9020Sstevel@tonic-gate 		return (EBUSY);
9030Sstevel@tonic-gate 	}
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_flags = I2C_WR_RD;
9060Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wlen = 1;
9070Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_rlen = 1;
9080Sstevel@tonic-gate 
9090Sstevel@tonic-gate 	/*
9100Sstevel@tonic-gate 	 * The register has to be read to clear the previous status.
9110Sstevel@tonic-gate 	 */
9120Sstevel@tonic-gate 
9130Sstevel@tonic-gate 	for (i = 0; i < 2; i++) {
9140Sstevel@tonic-gate 		admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_STAT_1_REG;
9150Sstevel@tonic-gate 		if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer)
9160Sstevel@tonic-gate 		    != I2C_SUCCESS) {
9170Sstevel@tonic-gate 			return (EIO);
9180Sstevel@tonic-gate 		}
9190Sstevel@tonic-gate 		temp[0] = admp->adm1031_transfer->i2c_rbuf[0];
9200Sstevel@tonic-gate 		admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_STAT_2_REG;
9210Sstevel@tonic-gate 		if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer)
9220Sstevel@tonic-gate 		    != I2C_SUCCESS) {
9230Sstevel@tonic-gate 			return (EIO);
9240Sstevel@tonic-gate 		}
9250Sstevel@tonic-gate 	}
9260Sstevel@tonic-gate 	temp[1] = admp->adm1031_transfer->i2c_rbuf[0];
9270Sstevel@tonic-gate 
9280Sstevel@tonic-gate 	if ((temp[0] != 0) || (temp[1] != 0)) {
9290Sstevel@tonic-gate 		goto copyout;
9300Sstevel@tonic-gate 	}
9310Sstevel@tonic-gate 
9320Sstevel@tonic-gate 	/*
9330Sstevel@tonic-gate 	 * Enable the interrupt and fan fault alert.
9340Sstevel@tonic-gate 	 */
9350Sstevel@tonic-gate 	mutex_enter(&admp->adm1031_mutex);
9360Sstevel@tonic-gate 	while (admp->adm1031_flags & ADM1031_BUSYFLAG) {
9370Sstevel@tonic-gate 		if (cv_wait_sig(&admp->adm1031_cv,
9380Sstevel@tonic-gate 		    &admp->adm1031_mutex) <= 0) {
9390Sstevel@tonic-gate 			mutex_exit(&admp->adm1031_mutex);
9400Sstevel@tonic-gate 			return (EINTR);
9410Sstevel@tonic-gate 		}
9420Sstevel@tonic-gate 	}
9430Sstevel@tonic-gate 	admp->adm1031_flags |= ADM1031_BUSYFLAG;
9440Sstevel@tonic-gate 
9450Sstevel@tonic-gate 	mutex_exit(&admp->adm1031_mutex);
9460Sstevel@tonic-gate 
9470Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_flags = I2C_WR_RD;
9480Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wlen = 1;
9490Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_rlen = 1;
9500Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_CONFIG_REG_1;
9510Sstevel@tonic-gate 	if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
9520Sstevel@tonic-gate 	    I2C_SUCCESS) {
9530Sstevel@tonic-gate 		err = EIO;
9540Sstevel@tonic-gate 		goto err;
9550Sstevel@tonic-gate 	}
9560Sstevel@tonic-gate 
9570Sstevel@tonic-gate 	temp1 = admp->adm1031_transfer->i2c_rbuf[0];
9580Sstevel@tonic-gate 
9590Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_flags = I2C_WR;
9600Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wlen = 2;
9610Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wbuf[1] = (temp1 | 0x12);
9620Sstevel@tonic-gate 
9630Sstevel@tonic-gate 	if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
9640Sstevel@tonic-gate 	    I2C_SUCCESS) {
9650Sstevel@tonic-gate 		err = EIO;
9660Sstevel@tonic-gate 		goto err;
9670Sstevel@tonic-gate 	}
9680Sstevel@tonic-gate 
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate 	mutex_enter(&admp->adm1031_mutex);
9710Sstevel@tonic-gate 	admp->adm1031_flags = admp->adm1031_flags & (~ADM1031_BUSYFLAG);
9720Sstevel@tonic-gate 	cv_signal(&admp->adm1031_cv);
9730Sstevel@tonic-gate 	mutex_exit(&admp->adm1031_mutex);
9740Sstevel@tonic-gate 
9750Sstevel@tonic-gate 
9760Sstevel@tonic-gate 
9770Sstevel@tonic-gate 	mutex_enter(&admp->adm1031_imutex);
9780Sstevel@tonic-gate 	admp->adm1031_cvwaiting = 1;
9790Sstevel@tonic-gate 	(void) cv_wait_sig(&admp->adm1031_icv, &admp->adm1031_imutex);
9800Sstevel@tonic-gate 	mutex_exit(&admp->adm1031_imutex);
9810Sstevel@tonic-gate 
9820Sstevel@tonic-gate 
9830Sstevel@tonic-gate 	/*
9840Sstevel@tonic-gate 	 * Disable the interrupt and fan fault alert.
9850Sstevel@tonic-gate 	 */
9860Sstevel@tonic-gate 	mutex_enter(&admp->adm1031_mutex);
9870Sstevel@tonic-gate 
9880Sstevel@tonic-gate 	while (admp->adm1031_flags & ADM1031_BUSYFLAG) {
9890Sstevel@tonic-gate 		if (cv_wait_sig(&admp->adm1031_cv,
9900Sstevel@tonic-gate 		    &admp->adm1031_mutex) <= 0) {
9910Sstevel@tonic-gate 			mutex_exit(&admp->adm1031_mutex);
9920Sstevel@tonic-gate 			return (EINTR);
9930Sstevel@tonic-gate 		}
9940Sstevel@tonic-gate 	}
9950Sstevel@tonic-gate 	admp->adm1031_flags |= ADM1031_BUSYFLAG;
9960Sstevel@tonic-gate 
9970Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_flags = I2C_WR_RD;
9980Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wlen = 1;
9990Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_rlen = 1;
10000Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_CONFIG_REG_1;
10010Sstevel@tonic-gate 
10020Sstevel@tonic-gate 	if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
10030Sstevel@tonic-gate 	    I2C_SUCCESS) {
10040Sstevel@tonic-gate 		err = EIO;
10050Sstevel@tonic-gate 		goto err;
10060Sstevel@tonic-gate 	}
10070Sstevel@tonic-gate 
10080Sstevel@tonic-gate 
10090Sstevel@tonic-gate 	temp1 = admp->adm1031_transfer->i2c_rbuf[0];
10100Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_flags = I2C_WR;
10110Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wlen = 2;
10120Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wbuf[1] = (temp1 & (~0x12));
10130Sstevel@tonic-gate 
10140Sstevel@tonic-gate 	if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
10150Sstevel@tonic-gate 	    I2C_SUCCESS) {
10160Sstevel@tonic-gate 		err = (EIO);
10170Sstevel@tonic-gate 		goto err;
10180Sstevel@tonic-gate 	}
10190Sstevel@tonic-gate 
10200Sstevel@tonic-gate 	admp->adm1031_flags = admp->adm1031_flags & (~ADM1031_BUSYFLAG);
10210Sstevel@tonic-gate 	cv_signal(&admp->adm1031_cv);
10220Sstevel@tonic-gate 	mutex_exit(&admp->adm1031_mutex);
10230Sstevel@tonic-gate 
10240Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_flags = I2C_WR_RD;
10250Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wlen = 1;
10260Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_rlen = 1;
10270Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_STAT_1_REG;
10280Sstevel@tonic-gate 	if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
10290Sstevel@tonic-gate 	    I2C_SUCCESS) {
10300Sstevel@tonic-gate 
10310Sstevel@tonic-gate 		return (EIO);
10320Sstevel@tonic-gate 	}
10330Sstevel@tonic-gate 	temp[0] = admp->adm1031_transfer->i2c_rbuf[0];
10340Sstevel@tonic-gate 
10350Sstevel@tonic-gate 	admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_STAT_2_REG;
10360Sstevel@tonic-gate 	if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
10370Sstevel@tonic-gate 	    I2C_SUCCESS) {
10380Sstevel@tonic-gate 
10390Sstevel@tonic-gate 		return (EIO);
10400Sstevel@tonic-gate 	}
10410Sstevel@tonic-gate 	temp[1] = admp->adm1031_transfer->i2c_rbuf[0];
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate copyout:
10440Sstevel@tonic-gate 	if (ddi_copyout((void *)&temp, (void *)arg, sizeof (temp),
10450Sstevel@tonic-gate 	    mode) != DDI_SUCCESS) {
10460Sstevel@tonic-gate 
10470Sstevel@tonic-gate 		return (EFAULT);
10480Sstevel@tonic-gate 	}
10490Sstevel@tonic-gate 
10500Sstevel@tonic-gate 	return (0);
10510Sstevel@tonic-gate 
10520Sstevel@tonic-gate err:
10530Sstevel@tonic-gate 	mutex_enter(&admp->adm1031_mutex);
10540Sstevel@tonic-gate 	admp->adm1031_flags = admp->adm1031_flags & (~ADM1031_BUSYFLAG);
10550Sstevel@tonic-gate 	cv_signal(&admp->adm1031_cv);
10560Sstevel@tonic-gate 	mutex_exit(&admp->adm1031_mutex);
10570Sstevel@tonic-gate 
10580Sstevel@tonic-gate 	return (err);
10590Sstevel@tonic-gate }
10600Sstevel@tonic-gate 
10610Sstevel@tonic-gate static int
adm1031_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)10620Sstevel@tonic-gate adm1031_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
10630Sstevel@tonic-gate 	int *rvalp)
10640Sstevel@tonic-gate {
10650Sstevel@tonic-gate 	_NOTE(ARGUNUSED(credp, rvalp))
10660Sstevel@tonic-gate 
10670Sstevel@tonic-gate 	if (cmd == ADM1031_INTERRUPT_WAIT) {
10680Sstevel@tonic-gate 
10690Sstevel@tonic-gate 		return (adm1031_i_ioctl(dev, cmd, arg, mode));
10700Sstevel@tonic-gate 	} else {
10710Sstevel@tonic-gate 		return (adm1031_s_ioctl(dev, cmd, arg, mode));
10720Sstevel@tonic-gate 	}
10730Sstevel@tonic-gate }
1074