xref: /titanic_44/usr/src/uts/sun4u/montecarlo/io/pcf8591_nct.c (revision 89b43686db1fe9681d80a7cf5662730cb9378cae)
103831d35Sstevel /*
203831d35Sstevel  * CDDL HEADER START
303831d35Sstevel  *
403831d35Sstevel  * The contents of this file are subject to the terms of the
519397407SSherry Moore  * Common Development and Distribution License (the "License").
619397407SSherry Moore  * You may not use this file except in compliance with the License.
703831d35Sstevel  *
803831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel  * See the License for the specific language governing permissions
1103831d35Sstevel  * and limitations under the License.
1203831d35Sstevel  *
1303831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel  *
1903831d35Sstevel  * CDDL HEADER END
2003831d35Sstevel  */
2103831d35Sstevel /*
2207d06da5SSurya Prakki  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2303831d35Sstevel  * Use is subject to license terms.
24*89b43686SBayard Bell  * Copyright (c) 2011 Bayard G. Bell. All rights reserved.
2503831d35Sstevel  */
2603831d35Sstevel 
2703831d35Sstevel /*
2803831d35Sstevel  * I2C leaf driver for the PCF8591
2903831d35Sstevel  */
3003831d35Sstevel 
3103831d35Sstevel #include <sys/param.h>
3203831d35Sstevel #include <sys/types.h>
3303831d35Sstevel #include <sys/signal.h>
3403831d35Sstevel #include <sys/errno.h>
3503831d35Sstevel #include <sys/file.h>
3603831d35Sstevel #include <sys/termio.h>
3703831d35Sstevel #include <sys/termios.h>
3803831d35Sstevel #include <sys/cmn_err.h>
3903831d35Sstevel #include <sys/stream.h>
4003831d35Sstevel #include <sys/strsun.h>
4103831d35Sstevel #include <sys/stropts.h>
4203831d35Sstevel #include <sys/strtty.h>
4303831d35Sstevel #include <sys/debug.h>
4403831d35Sstevel #include <sys/eucioctl.h>
4503831d35Sstevel #include <sys/cred.h>
4603831d35Sstevel #include <sys/uio.h>
4703831d35Sstevel #include <sys/stat.h>
4803831d35Sstevel #include <sys/kmem.h>
4903831d35Sstevel 
5003831d35Sstevel #include <sys/ddi.h>
5103831d35Sstevel #include <sys/sunddi.h>
5203831d35Sstevel #include <sys/obpdefs.h>
5303831d35Sstevel #include <sys/conf.h>
5403831d35Sstevel #include <sys/modctl.h>
5503831d35Sstevel #include <sys/stat.h>
5603831d35Sstevel #include <sys/open.h>
5703831d35Sstevel #include <sys/uio.h>
5803831d35Sstevel 
5903831d35Sstevel #include <sys/i2c/misc/i2c_svc.h>
6003831d35Sstevel #include <sys/envctrl_gen.h>
6103831d35Sstevel #include <sys/netract_gen.h>
6203831d35Sstevel #include <sys/pcf8591_nct.h>
6303831d35Sstevel 
6403831d35Sstevel 
6503831d35Sstevel /*
6603831d35Sstevel  * 		CONTROL OF CHIP
6703831d35Sstevel  * PCF8591 Temp sensing control register definitions
6803831d35Sstevel  *
6903831d35Sstevel  * ---------------------------------------------
7003831d35Sstevel  * | 0 | AOE | X | X | 0 | AIF | X | X |
7103831d35Sstevel  * ---------------------------------------------
7203831d35Sstevel  * AOE = Analog out enable.. not used on out implementation
7303831d35Sstevel  * 5 & 4 = Analog Input Programming.. see data sheet for bits..
7403831d35Sstevel  *
7503831d35Sstevel  * AIF = Auto increment flag
7603831d35Sstevel  * bits 1 & 0 are for the Chennel number.
7703831d35Sstevel  */
7803831d35Sstevel 
7903831d35Sstevel 
8003831d35Sstevel #define	I2CTRANS_DATA	0
8103831d35Sstevel #define	I2CRAW_DATA	1
8203831d35Sstevel #define	TEMP_TABLE_SIZE	256
8303831d35Sstevel 
8403831d35Sstevel #define	SHUTDOWN_TEMP_MIN	55
8503831d35Sstevel #define	SHUTDOWN_TEMP_MAX	85
8603831d35Sstevel 
8703831d35Sstevel #ifdef DEBUG
8803831d35Sstevel #define	dbg_print(level, str) cmn_err(level, str);
8903831d35Sstevel #else
9003831d35Sstevel #define	dbg_print(level, str) {; }
9103831d35Sstevel #endif
9203831d35Sstevel 
9303831d35Sstevel 
9403831d35Sstevel extern int nct_i2c_transfer(i2c_client_hdl_t i2c_hdl, i2c_transfer_t *i2c_tran);
9503831d35Sstevel static uchar_t _cpu_temps[TEMP_TABLE_SIZE + 4];	/* see attach */
9603831d35Sstevel 
9703831d35Sstevel static void *pcf8591_soft_statep;
9803831d35Sstevel 
9903831d35Sstevel /*
10003831d35Sstevel  * cb ops (only need ioctl)
10103831d35Sstevel  */
10203831d35Sstevel static int pcf8591_open(dev_t *, int, int, cred_t *);
10303831d35Sstevel static int pcf8591_close(dev_t, int, int, cred_t *);
10403831d35Sstevel static int pcf8591_read(dev_t dev, struct uio *uiop, cred_t *cred_p);
10503831d35Sstevel static int pcf8591_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
10603831d35Sstevel 
10703831d35Sstevel static struct cb_ops pcf8591_cbops = {
10803831d35Sstevel 	pcf8591_open,			/* open */
10903831d35Sstevel 	pcf8591_close,			/* close */
11003831d35Sstevel 	nodev,				/* strategy */
11103831d35Sstevel 	nodev,				/* print */
11203831d35Sstevel 	nodev,				/* dump */
11303831d35Sstevel 	pcf8591_read,			/* read */
11403831d35Sstevel 	nodev,				/* write */
11503831d35Sstevel 	pcf8591_ioctl,			/* ioctl */
11603831d35Sstevel 	nodev,				/* devmap */
11703831d35Sstevel 	nodev,				/* mmap */
11803831d35Sstevel 	nodev,				/* segmap */
11903831d35Sstevel 	nochpoll,			/* poll */
12003831d35Sstevel 	ddi_prop_op,			/* cb_prop_op */
12103831d35Sstevel 	NULL,				/* streamtab */
12203831d35Sstevel 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
12303831d35Sstevel 	CB_REV,				/* rev */
12403831d35Sstevel 	nodev,				/* int (*cb_aread)() */
12503831d35Sstevel 	nodev				/* int (*cb_awrite)() */
12603831d35Sstevel };
12703831d35Sstevel 
12803831d35Sstevel /*
12903831d35Sstevel  * dev ops
13003831d35Sstevel  */
13103831d35Sstevel static int pcf8591_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
13203831d35Sstevel 		void **result);
13303831d35Sstevel static int pcf8591_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
13403831d35Sstevel static int pcf8591_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
13503831d35Sstevel 
13603831d35Sstevel /* kstat routines */
13703831d35Sstevel static int pcf8591_add_kstats(struct pcf8591_unit *);
13803831d35Sstevel static void pcf8591_delete_kstats(struct pcf8591_unit *);
13903831d35Sstevel static int pcf8591_temp_kstat_update(kstat_t *, int);
14003831d35Sstevel static int pcf8591_read_chip(struct pcf8591_unit *, uint8_t, int);
14103831d35Sstevel static int pcf8591_read_props(struct pcf8591_unit *unitp);
14203831d35Sstevel 
14303831d35Sstevel static struct dev_ops pcf8591_ops = {
14403831d35Sstevel 	DEVO_REV,
14503831d35Sstevel 	0,
14603831d35Sstevel 	pcf8591_info,
14703831d35Sstevel 	nulldev,
14803831d35Sstevel 	nulldev,
14903831d35Sstevel 	pcf8591_attach,
15003831d35Sstevel 	pcf8591_detach,
15103831d35Sstevel 	nodev,
15203831d35Sstevel 	&pcf8591_cbops,
15319397407SSherry Moore 	NULL,
15419397407SSherry Moore 	NULL,
15519397407SSherry Moore 	ddi_quiesce_not_supported,	/* devo_quiesce */
15603831d35Sstevel };
15703831d35Sstevel 
15803831d35Sstevel extern struct mod_ops mod_driverops;
15903831d35Sstevel 
16003831d35Sstevel static struct modldrv pcf8591_modldrv = {
16103831d35Sstevel 	&mod_driverops,		/* type of module - driver */
16219397407SSherry Moore 	"Netract pcf8591 (adio)",
16303831d35Sstevel 	&pcf8591_ops,
16403831d35Sstevel };
16503831d35Sstevel 
16603831d35Sstevel static struct modlinkage pcf8591_modlinkage = {
16703831d35Sstevel 	MODREV_1,
16803831d35Sstevel 	&pcf8591_modldrv,
16903831d35Sstevel 	0
17003831d35Sstevel };
17103831d35Sstevel 
17203831d35Sstevel int	pcf8591_debug = 0x02;
17303831d35Sstevel static uint8_t translate_cputemp(uint8_t value);
17403831d35Sstevel 
17503831d35Sstevel int
_init(void)17603831d35Sstevel _init(void)
17703831d35Sstevel {
17803831d35Sstevel 	register int    error;
17903831d35Sstevel 
18003831d35Sstevel 	error = mod_install(&pcf8591_modlinkage);
18103831d35Sstevel 	if (error == 0) {
18203831d35Sstevel 		(void) ddi_soft_state_init(&pcf8591_soft_statep,
18303831d35Sstevel 		    sizeof (struct pcf8591_unit), PCF8591_MAX_DEVS);
18403831d35Sstevel 	}
18503831d35Sstevel 
18603831d35Sstevel 	return (error);
18703831d35Sstevel }
18803831d35Sstevel 
18903831d35Sstevel int
_fini(void)19003831d35Sstevel _fini(void)
19103831d35Sstevel {
19203831d35Sstevel 	register int    error;
19303831d35Sstevel 
19403831d35Sstevel 	error = mod_remove(&pcf8591_modlinkage);
19503831d35Sstevel 	if (error == 0) {
19603831d35Sstevel 		ddi_soft_state_fini(&pcf8591_soft_statep);
19703831d35Sstevel 	}
19803831d35Sstevel 
19903831d35Sstevel 	return (error);
20003831d35Sstevel }
20103831d35Sstevel 
20203831d35Sstevel int
_info(struct modinfo * modinfop)20303831d35Sstevel _info(struct modinfo *modinfop)
20403831d35Sstevel {
20503831d35Sstevel 	return (mod_info(&pcf8591_modlinkage, modinfop));
20603831d35Sstevel }
20703831d35Sstevel 
20803831d35Sstevel /*ARGSUSED*/
20903831d35Sstevel static int
pcf8591_open(dev_t * devp,int flags,int otyp,cred_t * credp)21003831d35Sstevel pcf8591_open(dev_t *devp, int flags, int otyp, cred_t *credp)
21103831d35Sstevel {
21203831d35Sstevel 	int err = 0;
21303831d35Sstevel 	struct pcf8591_unit *unitp;
21403831d35Sstevel 	minor_t minor = getminor(*devp);
21503831d35Sstevel 
21603831d35Sstevel 	int instance = PCF8591_MINOR_TO_DEVINST(minor);
21703831d35Sstevel 	int channel = PCF8591_MINOR_TO_CHANNEL(minor);
21803831d35Sstevel 
21903831d35Sstevel 	if (instance < 0) {
22003831d35Sstevel 		return (ENXIO);
22103831d35Sstevel 	}
22203831d35Sstevel 
22303831d35Sstevel 	unitp = (struct pcf8591_unit *)
22403831d35Sstevel 	    ddi_get_soft_state(pcf8591_soft_statep, instance);
22503831d35Sstevel 
22603831d35Sstevel 	if (unitp == NULL) {
22703831d35Sstevel 		return (ENXIO);
22803831d35Sstevel 	}
22903831d35Sstevel 
23003831d35Sstevel 	if (otyp != OTYP_CHR) {
23103831d35Sstevel 		return (EINVAL);
23203831d35Sstevel 	}
23303831d35Sstevel 
23403831d35Sstevel 	mutex_enter(&unitp->umutex);
23503831d35Sstevel 
23603831d35Sstevel 	if (flags & FEXCL) {
23703831d35Sstevel 		if (unitp->pcf8591_oflag[channel] != 0) {
23803831d35Sstevel 			err = EBUSY;
23903831d35Sstevel 		} else {
24003831d35Sstevel 			unitp->pcf8591_oflag[channel] = FEXCL;
24103831d35Sstevel 		}
24203831d35Sstevel 	} else {
24303831d35Sstevel 		if (unitp->pcf8591_oflag[channel] == FEXCL) {
24403831d35Sstevel 			err = EBUSY;
24503831d35Sstevel 		} else {
24603831d35Sstevel 			unitp->pcf8591_oflag[channel] = FOPEN;
24703831d35Sstevel 		}
24803831d35Sstevel 	}
24903831d35Sstevel 
25003831d35Sstevel 	mutex_exit(&unitp->umutex);
25103831d35Sstevel 
25203831d35Sstevel 	return (err);
25303831d35Sstevel }
25403831d35Sstevel 
25503831d35Sstevel /*ARGSUSED*/
25603831d35Sstevel static int
pcf8591_close(dev_t devp,int flags,int otyp,cred_t * credp)25703831d35Sstevel pcf8591_close(dev_t devp, int flags, int otyp, cred_t *credp)
25803831d35Sstevel {
25903831d35Sstevel 	struct pcf8591_unit *unitp;
26003831d35Sstevel 	minor_t minor = getminor(devp);
26103831d35Sstevel 
26203831d35Sstevel 	int instance = PCF8591_MINOR_TO_DEVINST(minor);
26303831d35Sstevel 	int channel = PCF8591_MINOR_TO_CHANNEL(minor);
26403831d35Sstevel 
26503831d35Sstevel #ifdef lint
26603831d35Sstevel 	flags = flags;
26703831d35Sstevel 	otyp = otyp;
26803831d35Sstevel #endif
26903831d35Sstevel 
27003831d35Sstevel 	if (instance < 0) {
27103831d35Sstevel 		return (ENXIO);
27203831d35Sstevel 	}
27303831d35Sstevel 
27403831d35Sstevel 	unitp = (struct pcf8591_unit *)
27503831d35Sstevel 	    ddi_get_soft_state(pcf8591_soft_statep, instance);
27603831d35Sstevel 
27703831d35Sstevel 	if (unitp == NULL) {
27803831d35Sstevel 		return (ENXIO);
27903831d35Sstevel 	}
28003831d35Sstevel 
28103831d35Sstevel 	mutex_enter(&unitp->umutex);
28203831d35Sstevel 
28303831d35Sstevel 	unitp->pcf8591_oflag[channel] = 0;
28403831d35Sstevel 
28503831d35Sstevel 	mutex_exit(&unitp->umutex);
28603831d35Sstevel 
28703831d35Sstevel 	return (DDI_SUCCESS);
28803831d35Sstevel }
28903831d35Sstevel 
29003831d35Sstevel static int
pcf8591_io(dev_t dev,struct uio * uiop,int rw)29103831d35Sstevel pcf8591_io(dev_t dev, struct uio *uiop, int rw)
29203831d35Sstevel {
29303831d35Sstevel 	int err = 0;
29403831d35Sstevel 	struct pcf8591_unit *unitp;
29503831d35Sstevel 	minor_t minor = getminor(dev);
29603831d35Sstevel 
29703831d35Sstevel 	int instance = PCF8591_MINOR_TO_DEVINST(minor);
29803831d35Sstevel 	int channel = PCF8591_MINOR_TO_CHANNEL(minor);
29903831d35Sstevel 
30003831d35Sstevel 	int		bytes_to_rw;
30103831d35Sstevel 	int		translate = 0;
30203831d35Sstevel 
30303831d35Sstevel 	/*
30403831d35Sstevel 	 * At this point we don't have a write operation to pcf8591.
30503831d35Sstevel 	 */
30603831d35Sstevel 	if (rw == B_WRITE) {
30703831d35Sstevel 		return (EACCES);
30803831d35Sstevel 	}
30903831d35Sstevel 
31003831d35Sstevel 	if (instance < 0) {
31103831d35Sstevel 		return (ENXIO);
31203831d35Sstevel 	}
31303831d35Sstevel 
31403831d35Sstevel 	unitp = (struct pcf8591_unit *)
31503831d35Sstevel 	    ddi_get_soft_state(pcf8591_soft_statep, instance);
31603831d35Sstevel 	if (unitp == NULL) {
31703831d35Sstevel 		return (ENXIO);
31803831d35Sstevel 	}
31903831d35Sstevel 
32003831d35Sstevel 	if ((bytes_to_rw = uiop->uio_resid) > PCF8591_TRAN_SIZE) {
32103831d35Sstevel 		return (EINVAL);
32203831d35Sstevel 	}
32303831d35Sstevel 
32403831d35Sstevel 	/*
32503831d35Sstevel 	 * Need to serialize all read operations, since there is a single
32603831d35Sstevel 	 * i2c_transfer_t structure allocated for all read and write ops.
32703831d35Sstevel 	 * We can't share the i2c bus among multiple transactions anyway,
32803831d35Sstevel 	 * so this does not affect performance.
32903831d35Sstevel 	 */
33003831d35Sstevel 	mutex_enter(&unitp->umutex);
33103831d35Sstevel 	while (unitp->pcf8591_flags == PCF8591_BUSY) {
33203831d35Sstevel 		if (cv_wait_sig(&unitp->pcf8591_cv, &unitp->umutex) <= 0) {
33303831d35Sstevel 			mutex_exit(&unitp->umutex);
33403831d35Sstevel 
33503831d35Sstevel 			return (EINTR);
33603831d35Sstevel 		}
33703831d35Sstevel 	}
33803831d35Sstevel 	unitp->pcf8591_flags = PCF8591_BUSY;
33903831d35Sstevel 	mutex_exit(&unitp->umutex);
34003831d35Sstevel 
34103831d35Sstevel 	if (bytes_to_rw == 1)
34203831d35Sstevel 		translate = 1;
34303831d35Sstevel 	/*
34403831d35Sstevel 	 * Event sequence:
34503831d35Sstevel 	 * 1. set up the control register write, for now we'll always read
34603831d35Sstevel 	 *    channel 0, which is the only active 8591 port on the Nordica
34703831d35Sstevel 	 *    TODO: We'll need a minor node for each port that is used.
34803831d35Sstevel 	 * 2. increment read count to read the throw-away byte
34903831d35Sstevel 	 * 3. start the write/read of control/data registers
35003831d35Sstevel 	 * 4. throw the first byte away
35103831d35Sstevel 	 * 5. then return the data
35203831d35Sstevel 	 */
35303831d35Sstevel 
35403831d35Sstevel 	unitp->i2c_tran->i2c_flags = I2C_WR_RD;
35503831d35Sstevel 	unitp->i2c_tran->i2c_wlen = 1;
35603831d35Sstevel 	unitp->i2c_tran->i2c_wbuf[0] = (unitp->pcf8591_inprog |
35703831d35Sstevel 	    channel);
35803831d35Sstevel 	/*
35903831d35Sstevel 	 * read extra byte to throw away the first, (PCF8591 datasheet)
36003831d35Sstevel 	 */
36103831d35Sstevel 	unitp->i2c_tran->i2c_rlen = bytes_to_rw + 1;
36203831d35Sstevel 
36303831d35Sstevel 	if (nct_i2c_transfer(unitp->pcf8591_hdl,
36403831d35Sstevel 	    unitp->i2c_tran) != I2C_SUCCESS) {
36503831d35Sstevel 		err = EIO;
36603831d35Sstevel 	} else {
36703831d35Sstevel 		/*
36803831d35Sstevel 		 * Throw away the first byte according to PCF8591 datasheet
36903831d35Sstevel 		 * If translating, use the second byte.
37003831d35Sstevel 		 */
37103831d35Sstevel 		if (translate) {
37203831d35Sstevel 			unitp->i2c_tran->i2c_rbuf[0] =
37303831d35Sstevel 			    translate_cputemp(unitp->i2c_tran->i2c_rbuf[1]);
37403831d35Sstevel 		} else {
37503831d35Sstevel 			unitp->i2c_tran->i2c_rbuf[0] =
37603831d35Sstevel 			    unitp->i2c_tran->i2c_rbuf[1];
37703831d35Sstevel 			unitp->i2c_tran->i2c_rbuf[1] = 0;
37803831d35Sstevel 		}
37903831d35Sstevel 
38003831d35Sstevel 		err = uiomove(unitp->i2c_tran->i2c_rbuf,
38103831d35Sstevel 		    bytes_to_rw,
38203831d35Sstevel 		    UIO_READ,
38303831d35Sstevel 		    uiop);
38403831d35Sstevel 	}
38503831d35Sstevel 	mutex_enter(&unitp->umutex);
38603831d35Sstevel 	unitp->pcf8591_flags = 0;
38703831d35Sstevel 	cv_signal(&unitp->pcf8591_cv);
38803831d35Sstevel 	mutex_exit(&unitp->umutex);
38903831d35Sstevel 
39003831d35Sstevel 	return (err);
39103831d35Sstevel }
39203831d35Sstevel 
39303831d35Sstevel /*ARGSUSED*/
39403831d35Sstevel static int
pcf8591_read(dev_t dev,struct uio * uiop,cred_t * cred_p)39503831d35Sstevel pcf8591_read(dev_t dev, struct uio *uiop, cred_t *cred_p)
39603831d35Sstevel {
39703831d35Sstevel 	return (pcf8591_io(dev, uiop, B_READ));
39803831d35Sstevel }
39903831d35Sstevel 
40003831d35Sstevel static int
call_copyin(caddr_t arg,struct pcf8591_unit * unitp,int mode)40103831d35Sstevel call_copyin(caddr_t arg, struct pcf8591_unit *unitp, int mode)
40203831d35Sstevel {
40303831d35Sstevel 	uchar_t *wbuf;
40403831d35Sstevel 	uchar_t *rbuf;
40503831d35Sstevel 	i2c_transfer_t i2ct;
40603831d35Sstevel 	i2c_transfer_t *i2ctp = unitp->i2c_tran;
40703831d35Sstevel 
40803831d35Sstevel 
40903831d35Sstevel 	if (ddi_copyin((void *)arg, (caddr_t)&i2ct,
41003831d35Sstevel 	    sizeof (i2c_transfer_t), mode) != DDI_SUCCESS) {
41103831d35Sstevel 		return (I2C_FAILURE);
41203831d35Sstevel 	}
41303831d35Sstevel 
41403831d35Sstevel 	/*
41503831d35Sstevel 	 * Save the read and write buffer pointers in the transfer
41603831d35Sstevel 	 * structure, otherwise these will get overwritten when we
41703831d35Sstevel 	 * do a bcopy. Restore once done.
41803831d35Sstevel 	 */
41903831d35Sstevel 
42003831d35Sstevel 	wbuf = i2ctp->i2c_wbuf;
42103831d35Sstevel 	rbuf = i2ctp->i2c_rbuf;
42203831d35Sstevel 
42303831d35Sstevel 	bcopy(&i2ct, i2ctp, sizeof (i2c_transfer_t));
42403831d35Sstevel 
42503831d35Sstevel 	i2ctp->i2c_wbuf = wbuf;
42603831d35Sstevel 	i2ctp->i2c_rbuf = rbuf;
42703831d35Sstevel 
42803831d35Sstevel 	/*
42903831d35Sstevel 	 * copyin the read and write buffers to the saved buffers.
43003831d35Sstevel 	 */
43103831d35Sstevel 
43203831d35Sstevel 	if (i2ct.i2c_wlen != 0) {
43303831d35Sstevel 		if (ddi_copyin(i2ct.i2c_wbuf, (caddr_t)i2ctp->i2c_wbuf,
43403831d35Sstevel 		    i2ct.i2c_wlen, mode) != DDI_SUCCESS) {
43503831d35Sstevel 				return (I2C_FAILURE);
43603831d35Sstevel 			}
43703831d35Sstevel 		}
43803831d35Sstevel 
43903831d35Sstevel 	return (I2C_SUCCESS);
44003831d35Sstevel }
44103831d35Sstevel 
44203831d35Sstevel static int
call_copyout(caddr_t arg,struct pcf8591_unit * unitp,int mode)44303831d35Sstevel call_copyout(caddr_t arg, struct pcf8591_unit *unitp, int mode)
44403831d35Sstevel {
44503831d35Sstevel 	i2c_transfer_t i2ct;
44603831d35Sstevel 	i2c_transfer_t *i2ctp = unitp->i2c_tran;
44703831d35Sstevel 	uint16_t  i2c_actlen;
44803831d35Sstevel 
44903831d35Sstevel 	/*
45003831d35Sstevel 	 * We will copyout the last three fields only, skipping
45103831d35Sstevel 	 * the remaining ones, before copying the rbuf to the
45203831d35Sstevel 	 * user buffer.
45303831d35Sstevel 	 */
45403831d35Sstevel 
45503831d35Sstevel 	int uskip = sizeof (i2c_transfer_t) - 3*sizeof (int16_t),
45603831d35Sstevel 	    kskip = sizeof (i2c_transfer_t) - 3*sizeof (int16_t);
45703831d35Sstevel 
45803831d35Sstevel 	/*
45903831d35Sstevel 	 * First copyin the user structure to the temporary i2ct,
46003831d35Sstevel 	 * so that we have the wbuf and rbuf addresses in it.
46103831d35Sstevel 	 */
46203831d35Sstevel 
46303831d35Sstevel 	uskip = sizeof (i2c_transfer_t) - 3 * (sizeof (uint16_t));
46403831d35Sstevel 
46503831d35Sstevel 	/*
46603831d35Sstevel 	 * copyout the last three out fields now.
46703831d35Sstevel 	 */
46803831d35Sstevel 
46903831d35Sstevel 	if (ddi_copyout((void *)((intptr_t)i2ctp+kskip), (void *)
47003831d35Sstevel 	    ((intptr_t)arg + uskip), 3*sizeof (uint16_t), mode)
47103831d35Sstevel 	    != DDI_SUCCESS) {
47203831d35Sstevel 		return (I2C_FAILURE);
47303831d35Sstevel 		}
47403831d35Sstevel 
47503831d35Sstevel 	/*
47603831d35Sstevel 	 * In case we have something to write, get the address of the read
47703831d35Sstevel 	 * buffer.
47803831d35Sstevel 	 */
47903831d35Sstevel 
48003831d35Sstevel 	if (i2ctp->i2c_rlen - i2ctp->i2c_r_resid > 0) {
48103831d35Sstevel 
48203831d35Sstevel 	if (ddi_copyin((void *)arg, &i2ct,
48303831d35Sstevel 	    sizeof (i2c_transfer_t), mode) != DDI_SUCCESS) {
48403831d35Sstevel 		return (I2C_FAILURE);
48503831d35Sstevel 	}
48603831d35Sstevel 
48703831d35Sstevel 	/*
48803831d35Sstevel 	 * copyout the read buffer to the saved user buffer in i2ct.
48903831d35Sstevel 	 */
49003831d35Sstevel 
49103831d35Sstevel 		i2c_actlen = i2ctp->i2c_rlen - i2ctp->i2c_r_resid;
49203831d35Sstevel 		if (ddi_copyout(i2ctp->i2c_rbuf, i2ct.i2c_rbuf,
49303831d35Sstevel 		    i2c_actlen, mode) != DDI_SUCCESS) {
49403831d35Sstevel 				return (I2C_FAILURE);
49503831d35Sstevel 			}
49603831d35Sstevel 		}
49703831d35Sstevel 
49803831d35Sstevel 	return (I2C_SUCCESS);
49903831d35Sstevel }
50003831d35Sstevel 
50103831d35Sstevel /*
50203831d35Sstevel  * The ioctls will use the same name as the Javelin ioctls. We
50303831d35Sstevel  * will have a very restricted set for MC, and unlike Javelin
50403831d35Sstevel  * will not have a envctrl_chip structure to return values
50503831d35Sstevel  * from the driver. All we will have is a uint8_t value to
50603831d35Sstevel  * get or set values from the driver. Also, unlike the Javelin,
50703831d35Sstevel  * where 'index' is used to specify the input port from where
50803831d35Sstevel  * temperature is collected, here different minor nodes will be
50903831d35Sstevel  * created by the driver for each port, eliminating the need for
51003831d35Sstevel  * 'index' - leaving us with only the value to pass.
51103831d35Sstevel  */
51203831d35Sstevel 
51303831d35Sstevel /*ARGSUSED*/
51403831d35Sstevel static int
pcf8591_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)51503831d35Sstevel pcf8591_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
51603831d35Sstevel 		cred_t *credp, int *rvalp)
51703831d35Sstevel {
51803831d35Sstevel 	int err = 0;
51903831d35Sstevel 	struct pcf8591_unit *unitp;
52003831d35Sstevel 	minor_t minor = getminor(dev);
52103831d35Sstevel 
52203831d35Sstevel 	int instance = PCF8591_MINOR_TO_DEVINST(minor);
52303831d35Sstevel 	int channel = PCF8591_MINOR_TO_CHANNEL(minor);
52403831d35Sstevel 
52503831d35Sstevel 	unitp = (struct pcf8591_unit *)
52603831d35Sstevel 	    ddi_get_soft_state(pcf8591_soft_statep, instance);
52703831d35Sstevel 
52803831d35Sstevel 	mutex_enter(&unitp->umutex);
52903831d35Sstevel 	while (unitp->pcf8591_flags == PCF8591_BUSY) {
53003831d35Sstevel 		if (cv_wait_sig(&unitp->pcf8591_cv, &unitp->umutex) <= 0) {
53103831d35Sstevel 			mutex_exit(&unitp->umutex);
53203831d35Sstevel 
53303831d35Sstevel 			return (EINTR);
53403831d35Sstevel 		}
53503831d35Sstevel 	}
53603831d35Sstevel 	unitp->pcf8591_flags = PCF8591_BUSY;
53703831d35Sstevel 	mutex_exit(&unitp->umutex);
53803831d35Sstevel 
53903831d35Sstevel 	switch (cmd) {
54003831d35Sstevel 
54103831d35Sstevel 	case ENVC_IOC_GETTEMP: {
54203831d35Sstevel 		/*
54303831d35Sstevel 		 * Read the status byte from pcf8591 chip. The value will
54403831d35Sstevel 		 * be already converted to Celcius by translate_cputemp.
54503831d35Sstevel 		 */
54607d06da5SSurya Prakki 		(void) pcf8591_read_chip(unitp, channel, 1);
54703831d35Sstevel 		if (ddi_copyout(unitp->i2c_tran->i2c_rbuf,
54803831d35Sstevel 		    (caddr_t)arg, sizeof (uint8_t), mode) != DDI_SUCCESS) {
54903831d35Sstevel 			err = EFAULT;
55003831d35Sstevel 		}
55103831d35Sstevel 		break;
55203831d35Sstevel 	}
55303831d35Sstevel 
55403831d35Sstevel 	case ENVC_IOC_GETMODE: {
55503831d35Sstevel 		uint8_t curr_mode = unitp->current_mode;
55603831d35Sstevel 
55703831d35Sstevel 		if (ddi_copyout((caddr_t)&curr_mode, (caddr_t)arg,
55803831d35Sstevel 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
55903831d35Sstevel 			err = EFAULT;
56003831d35Sstevel 		}
56103831d35Sstevel 		break;
56203831d35Sstevel 	}
56303831d35Sstevel 
56403831d35Sstevel 	case ENVC_IOC_SETMODE: {
56503831d35Sstevel 		uint8_t curr_mode;
56603831d35Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)&curr_mode,
56703831d35Sstevel 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
56803831d35Sstevel 				err = EFAULT;
56903831d35Sstevel 				break;
57003831d35Sstevel 		}
57103831d35Sstevel 		if (curr_mode == ENVCTRL_DIAG_MODE ||
57203831d35Sstevel 		    curr_mode == ENVCTRL_NORMAL_MODE) {
57303831d35Sstevel 			unitp->current_mode = curr_mode; /* Don't do anything */
57403831d35Sstevel 		}
57503831d35Sstevel 		break;
57603831d35Sstevel 	}
57703831d35Sstevel 
57803831d35Sstevel 	/* Testing, may be removed */
57903831d35Sstevel 	case I2CDEV_TRAN:
58003831d35Sstevel 		if (call_copyin((caddr_t)arg, unitp, mode) != I2C_SUCCESS) {
58103831d35Sstevel 			err = EFAULT;
58203831d35Sstevel 			break;
58303831d35Sstevel 		}
58403831d35Sstevel 		if (nct_i2c_transfer(unitp->pcf8591_hdl, unitp->i2c_tran)
58503831d35Sstevel 		    != I2C_SUCCESS) {
58603831d35Sstevel 			err = EFAULT;
58703831d35Sstevel 			break;
58803831d35Sstevel 		}
58903831d35Sstevel 		if (call_copyout((caddr_t)arg, unitp, mode) != I2C_SUCCESS) {
59003831d35Sstevel 			err = EFAULT;
59103831d35Sstevel 			break;
59203831d35Sstevel 		}
59303831d35Sstevel 		break;
59403831d35Sstevel 
59503831d35Sstevel 	/*
59603831d35Sstevel 	 * TESTING TRANSLATION from "adc" "table" property
59703831d35Sstevel 	 * translate thermistor index into temp Celcius
59803831d35Sstevel 	 */
59903831d35Sstevel 	case I2CDEV_GETTEMP: {
60003831d35Sstevel 		struct i2c_transfer *tp;
60103831d35Sstevel 		if (call_copyin((caddr_t)arg, unitp, mode) != I2C_SUCCESS) {
60203831d35Sstevel 			err = EFAULT;
60303831d35Sstevel 			break;
60403831d35Sstevel 		}
60503831d35Sstevel 		tp = unitp->i2c_tran;
60603831d35Sstevel 		if (tp->i2c_rlen != 1) {
60703831d35Sstevel 			err = EINVAL;
60803831d35Sstevel 			break;
60903831d35Sstevel 		}
61003831d35Sstevel 		/*
61103831d35Sstevel 		 * Throw away the first byte according to PCF8591 datasheet,
61203831d35Sstevel 		 * so read two bytes
61303831d35Sstevel 		 */
61403831d35Sstevel 		tp->i2c_rlen = 2;
61503831d35Sstevel 		if (nct_i2c_transfer(unitp->pcf8591_hdl, unitp->i2c_tran)
61603831d35Sstevel 		    != I2C_SUCCESS) {
61703831d35Sstevel 			err = EFAULT;
61803831d35Sstevel 			break;
61903831d35Sstevel 		}
62003831d35Sstevel #ifdef DEBUG
62103831d35Sstevel 		if (pcf8591_debug & 0x0010)
62203831d35Sstevel 			cmn_err(CE_NOTE,
62319397407SSherry Moore 			    "pcf8591_ioctl: i2c_rlen=%d; "
62419397407SSherry Moore 			    "i2c_rbuf[0,1]=0x%x,0x%x\n",
62503831d35Sstevel 			    tp->i2c_rlen, tp->i2c_rbuf[0], tp->i2c_rbuf[1]);
62603831d35Sstevel #endif /* DEBUG */
62703831d35Sstevel 		/*
62803831d35Sstevel 		 * Throw away the first byte according to PCF8591 datasheet
62903831d35Sstevel 		 */
63003831d35Sstevel 		if ((tp->i2c_rbuf[0] = translate_cputemp(tp->i2c_rbuf[1]))
63103831d35Sstevel 		    == 0) {
63203831d35Sstevel 			err = EINVAL;
63303831d35Sstevel 			break;
63403831d35Sstevel 		}
63503831d35Sstevel 		tp->i2c_rbuf[1] = 0;
63603831d35Sstevel 
63703831d35Sstevel 		if (call_copyout((caddr_t)arg, unitp, mode) != I2C_SUCCESS) {
63803831d35Sstevel 			err = EFAULT;
63903831d35Sstevel 			break;
64003831d35Sstevel 		}
64103831d35Sstevel 		break;
64203831d35Sstevel 	}
64303831d35Sstevel 
64403831d35Sstevel 	case I2CDEV_GETTABLES: {
64503831d35Sstevel 		break;
64603831d35Sstevel 	}
64703831d35Sstevel 	default:
64803831d35Sstevel 		err = EINVAL;
64903831d35Sstevel 	}
65003831d35Sstevel 
65103831d35Sstevel 	mutex_enter(&unitp->umutex);
65203831d35Sstevel 	unitp->pcf8591_flags = 0;
65303831d35Sstevel 	cv_signal(&unitp->pcf8591_cv);
65403831d35Sstevel 	mutex_exit(&unitp->umutex);
65503831d35Sstevel 
65603831d35Sstevel 	return (err);
65703831d35Sstevel }
65803831d35Sstevel 
65903831d35Sstevel static int
pcf8591_do_detach(dev_info_t * dip)66003831d35Sstevel pcf8591_do_detach(dev_info_t *dip)
66103831d35Sstevel {
66203831d35Sstevel 	register struct pcf8591_unit *unitp;
66303831d35Sstevel 	int instance;
66403831d35Sstevel 	uint_t attach_flag;
66503831d35Sstevel 
66603831d35Sstevel 	instance = ddi_get_instance(dip);
66703831d35Sstevel 	unitp = ddi_get_soft_state(pcf8591_soft_statep, instance);
66803831d35Sstevel 	attach_flag = unitp->attach_flag;
66903831d35Sstevel 
67003831d35Sstevel 	if (attach_flag & PCF8591_KSTAT_INIT) {
67103831d35Sstevel 		pcf8591_delete_kstats(unitp);
67203831d35Sstevel 	}
67303831d35Sstevel 
67403831d35Sstevel 	if (attach_flag & PCF8591_LOCK_INIT) {
67503831d35Sstevel 		mutex_destroy(&unitp->umutex);
67603831d35Sstevel 		cv_destroy(&unitp->pcf8591_cv);
67703831d35Sstevel 	}
67803831d35Sstevel 
67903831d35Sstevel 	/*
68003831d35Sstevel 	 * Restore the lengths of the rbuf and wbuf, which was originally
68103831d35Sstevel 	 * allocated so that the appropriate amount of rbuf and wbuf are
68203831d35Sstevel 	 * freed.
68303831d35Sstevel 	 */
68403831d35Sstevel 	if (attach_flag & PCF8591_ALLOC_TRANSFER) {
68503831d35Sstevel 		unitp->i2c_tran->i2c_wlen = MAX_WLEN;
68603831d35Sstevel 		unitp->i2c_tran->i2c_rlen = MAX_RLEN;
68703831d35Sstevel 		i2c_transfer_free(unitp->pcf8591_hdl, unitp->i2c_tran);
68803831d35Sstevel 	}
68903831d35Sstevel 
69003831d35Sstevel 	if (attach_flag & PCF8591_REGISTER_CLIENT) {
69103831d35Sstevel 		i2c_client_unregister(unitp->pcf8591_hdl);
69203831d35Sstevel 	}
69303831d35Sstevel 
69403831d35Sstevel 	if (attach_flag & PCF8591_MINORS_CREATED) {
69503831d35Sstevel 		ddi_remove_minor_node(dip, NULL);
69603831d35Sstevel 	}
69703831d35Sstevel 
69803831d35Sstevel 	/*
69903831d35Sstevel 	 * Free the memory allocated for the properties.
70003831d35Sstevel 	 */
70103831d35Sstevel 	if (attach_flag & PCF8591_PROPS_READ) {
70203831d35Sstevel 		ddi_prop_free(unitp->props.name);
70303831d35Sstevel 		if (unitp->props.num_chans_used) {
70403831d35Sstevel 			ddi_prop_free(unitp->props.channels_in_use);
70503831d35Sstevel 		}
70603831d35Sstevel 
70703831d35Sstevel 		if (unitp->props.channels_description) {
70803831d35Sstevel 			ddi_prop_free(unitp->props.channels_description);
70903831d35Sstevel 		}
71003831d35Sstevel 	}
71103831d35Sstevel 
71203831d35Sstevel 	if (attach_flag & PCF8591_SOFT_STATE_ALLOC) {
71303831d35Sstevel 		ddi_soft_state_free(pcf8591_soft_statep, instance);
71403831d35Sstevel 	}
71503831d35Sstevel 
71603831d35Sstevel 	return (DDI_SUCCESS);
71703831d35Sstevel }
71803831d35Sstevel 
71903831d35Sstevel static int
pcf8591_do_suspend(dev_info_t * dip)72003831d35Sstevel pcf8591_do_suspend(dev_info_t *dip)
72103831d35Sstevel {
72203831d35Sstevel 	int instance = ddi_get_instance(dip);
72303831d35Sstevel 	struct pcf8591_unit *unitp = (struct pcf8591_unit *)
72403831d35Sstevel 	    ddi_get_soft_state(pcf8591_soft_statep, instance);
72503831d35Sstevel 
72603831d35Sstevel 	if (unitp == NULL) {
72703831d35Sstevel 		return (ENXIO);
72803831d35Sstevel 	}
72903831d35Sstevel 
73003831d35Sstevel 	/*
73103831d35Sstevel 	 * Set the busy flag so that future transactions block
73203831d35Sstevel 	 * until resume.
73303831d35Sstevel 	 */
73403831d35Sstevel 	mutex_enter(&unitp->umutex);
73503831d35Sstevel 	while (unitp->pcf8591_flags == PCF8591_BUSY) {
73603831d35Sstevel 		if (cv_wait_sig(&unitp->pcf8591_cv,
73703831d35Sstevel 		    &unitp->umutex) <= 0) {
73803831d35Sstevel 			mutex_exit(&unitp->umutex);
73903831d35Sstevel 
74003831d35Sstevel 			return (DDI_FAILURE);
74103831d35Sstevel 		}
74203831d35Sstevel 	}
74303831d35Sstevel 	unitp->pcf8591_flags = PCF8591_BUSY;
74403831d35Sstevel 	mutex_exit(&unitp->umutex);
74503831d35Sstevel 
74603831d35Sstevel 	return (DDI_SUCCESS);
74703831d35Sstevel }
74803831d35Sstevel 
74903831d35Sstevel static int
pcf8591_do_resume(dev_info_t * dip)75003831d35Sstevel pcf8591_do_resume(dev_info_t *dip)
75103831d35Sstevel {
75203831d35Sstevel 	int instance = ddi_get_instance(dip);
75303831d35Sstevel 	struct pcf8591_unit *unitp = (struct pcf8591_unit *)
75403831d35Sstevel 	    ddi_get_soft_state(pcf8591_soft_statep, instance);
75503831d35Sstevel 	if (unitp == NULL) {
75603831d35Sstevel 		return (ENXIO);
75703831d35Sstevel 	}
75803831d35Sstevel 
75903831d35Sstevel 	mutex_enter(&unitp->umutex);
76003831d35Sstevel 	unitp->pcf8591_flags = 0;
76103831d35Sstevel 	cv_signal(&unitp->pcf8591_cv);
76203831d35Sstevel 	mutex_exit(&unitp->umutex);
76303831d35Sstevel 
76403831d35Sstevel 	return (DDI_SUCCESS);
76503831d35Sstevel }
76603831d35Sstevel 
76703831d35Sstevel static int
pcf8591_do_attach(dev_info_t * dip)76803831d35Sstevel pcf8591_do_attach(dev_info_t *dip)
76903831d35Sstevel {
77003831d35Sstevel 	register struct pcf8591_unit *unitp;
77103831d35Sstevel 	int i, instance;
77203831d35Sstevel 	char name[MAXNAMELEN];
77303831d35Sstevel 	minor_t minor;
77403831d35Sstevel 
77503831d35Sstevel 	instance = ddi_get_instance(dip);
77603831d35Sstevel 
77703831d35Sstevel 	if (ddi_soft_state_zalloc(pcf8591_soft_statep, instance) != 0) {
77803831d35Sstevel 		return (DDI_FAILURE);
77903831d35Sstevel 	}
78003831d35Sstevel 
78103831d35Sstevel 	unitp = ddi_get_soft_state(pcf8591_soft_statep, instance);
78203831d35Sstevel 
78303831d35Sstevel 	if (unitp == NULL) {
78403831d35Sstevel 		return (DDI_FAILURE);
78503831d35Sstevel 	}
78603831d35Sstevel 
78703831d35Sstevel 	unitp->dip = dip;
78803831d35Sstevel 
78903831d35Sstevel 	unitp->attach_flag = PCF8591_SOFT_STATE_ALLOC;
79003831d35Sstevel 
79103831d35Sstevel 	if (pcf8591_read_props(unitp) != DDI_PROP_SUCCESS) {
79207d06da5SSurya Prakki 		(void) pcf8591_do_detach(dip);
79303831d35Sstevel 		return (DDI_FAILURE);
79403831d35Sstevel 	}
79503831d35Sstevel 
79603831d35Sstevel 	unitp->attach_flag |= PCF8591_PROPS_READ;
79703831d35Sstevel 
79803831d35Sstevel 	/*
79903831d35Sstevel 	 * Set the current operating mode to NORMAL_MODE.
80003831d35Sstevel 	 */
80103831d35Sstevel 	unitp->current_mode = ENVCTRL_NORMAL_MODE; /* normal mode */
80203831d35Sstevel 
80307d06da5SSurya Prakki 	(void) snprintf(unitp->pcf8591_name, PCF8591_NAMELEN,
80403831d35Sstevel 	    "%s%d", ddi_driver_name(dip), instance);
80503831d35Sstevel 
80603831d35Sstevel 	/*
80703831d35Sstevel 	 * Create a minor node corresponding to channel 0 to 3
80803831d35Sstevel 	 */
80903831d35Sstevel 	for (i = 0; i < PCF8591_MAX_CHANS; i++) {
81003831d35Sstevel 	if (i == 0) {
81103831d35Sstevel 		(void) sprintf(name, "cputemp");
81203831d35Sstevel 	} else {
81303831d35Sstevel 		(void) sprintf(name, "%d", i);
81403831d35Sstevel 	}
81503831d35Sstevel 	minor = PCF8591_MINOR_NUM(instance, i);
81603831d35Sstevel 	if (ddi_create_minor_node(dip, name, S_IFCHR, minor,
81703831d35Sstevel 	    PCF8591_NODE_TYPE, NULL) == DDI_FAILURE) {
81803831d35Sstevel 			ddi_remove_minor_node(dip, NULL);
81907d06da5SSurya Prakki 			(void) pcf8591_do_detach(dip);
82003831d35Sstevel 			return (DDI_FAILURE);
82103831d35Sstevel 		}
82203831d35Sstevel 	}
82303831d35Sstevel 
82403831d35Sstevel 	unitp->attach_flag |= PCF8591_MINORS_CREATED;
82503831d35Sstevel 
82603831d35Sstevel 	if (i2c_client_register(dip, &unitp->pcf8591_hdl)
82703831d35Sstevel 	    != I2C_SUCCESS) {
82807d06da5SSurya Prakki 		(void) pcf8591_do_detach(dip);
82903831d35Sstevel 		return (DDI_FAILURE);
83003831d35Sstevel 	}
83103831d35Sstevel 
83203831d35Sstevel 	unitp->attach_flag |= PCF8591_REGISTER_CLIENT;
83303831d35Sstevel 
83403831d35Sstevel 	/*
83503831d35Sstevel 	 * We allocate a single i2c_transfer_t structure for all
83603831d35Sstevel 	 * i2c transactions.
83703831d35Sstevel 	 */
83803831d35Sstevel 	if (i2c_transfer_alloc(unitp->pcf8591_hdl, &unitp->i2c_tran,
83903831d35Sstevel 	    MAX_WLEN, MAX_RLEN, KM_SLEEP) != I2C_SUCCESS) {
84007d06da5SSurya Prakki 		(void) pcf8591_do_detach(dip);
84103831d35Sstevel 		return (DDI_FAILURE);
84203831d35Sstevel 	}
84303831d35Sstevel 
84403831d35Sstevel 	unitp->attach_flag |= PCF8591_ALLOC_TRANSFER;
84503831d35Sstevel 
84603831d35Sstevel 	/*
84703831d35Sstevel 	 * The flags will be set to I2C_WR because for all reads from
84803831d35Sstevel 	 * the 8591 we need to also write the control byte.
84903831d35Sstevel 	 */
85003831d35Sstevel 	unitp->i2c_tran->i2c_flags = I2C_WR;
85103831d35Sstevel 	unitp->i2c_tran->i2c_version = I2C_XFER_REV;
85203831d35Sstevel 
85303831d35Sstevel 
85403831d35Sstevel 	/*
85503831d35Sstevel 	 * Set the analog programming mode to default. Upper nibble
85603831d35Sstevel 	 * in control byte. Four single ended inputs, output not enabled.
85703831d35Sstevel 	 */
85803831d35Sstevel 	unitp->pcf8591_inprog = PCF8591_4SINGLE | PCF8591_ANALOG_INPUT_EN;
85903831d35Sstevel 
86003831d35Sstevel 	/*
86103831d35Sstevel 	 * Set the open flag for each channel to 0.
86203831d35Sstevel 	 */
86303831d35Sstevel 	for (i = 0; i < PCF8591_MAX_CHANS; i++) {
86403831d35Sstevel 		unitp->pcf8591_oflag[i] = 0;
86503831d35Sstevel 	}
86603831d35Sstevel 
86703831d35Sstevel 	/*
86803831d35Sstevel 	 * Set the busy flag to 0.
86903831d35Sstevel 	 */
87003831d35Sstevel 	unitp->pcf8591_flags = 0;
87103831d35Sstevel 
87203831d35Sstevel 	mutex_init(&unitp->umutex, NULL, MUTEX_DRIVER, NULL);
87303831d35Sstevel 	cv_init(&unitp->pcf8591_cv, NULL, CV_DRIVER, NULL);
87403831d35Sstevel 
87503831d35Sstevel 	unitp->attach_flag |= PCF8591_LOCK_INIT;
87603831d35Sstevel 
87703831d35Sstevel 	if (pcf8591_add_kstats(unitp) != DDI_SUCCESS) {
87807d06da5SSurya Prakki 		(void) pcf8591_do_detach(dip);
87903831d35Sstevel 		return (DDI_FAILURE);
88003831d35Sstevel 	}
88103831d35Sstevel 
88203831d35Sstevel 	unitp->attach_flag |= PCF8591_KSTAT_INIT;
88303831d35Sstevel 
88403831d35Sstevel 	ddi_report_dev(dip);
88503831d35Sstevel 
88603831d35Sstevel 	return (DDI_SUCCESS);
88703831d35Sstevel }
88803831d35Sstevel 
88903831d35Sstevel /* ARGSUSED */
89003831d35Sstevel static int
pcf8591_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)89103831d35Sstevel pcf8591_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
89203831d35Sstevel {
89303831d35Sstevel 	dev_t	dev;
89403831d35Sstevel 	int	instance;
89503831d35Sstevel 
89603831d35Sstevel 	if (infocmd == DDI_INFO_DEVT2INSTANCE) {
89703831d35Sstevel 		dev = (dev_t)arg;
89803831d35Sstevel 		instance = PCF8591_MINOR_TO_DEVINST(getminor(dev));
89903831d35Sstevel 		*result = (void *)(uintptr_t)instance;
90003831d35Sstevel 		return (DDI_SUCCESS);
90103831d35Sstevel 	}
90203831d35Sstevel 	return (DDI_FAILURE);
90303831d35Sstevel }
90403831d35Sstevel 
90503831d35Sstevel static int
pcf8591_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)90603831d35Sstevel pcf8591_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
90703831d35Sstevel {
90803831d35Sstevel 	switch (cmd) {
90903831d35Sstevel 	case DDI_ATTACH:
91003831d35Sstevel 		return (pcf8591_do_attach(dip));
91103831d35Sstevel 	case DDI_RESUME:
91203831d35Sstevel 		return (pcf8591_do_resume(dip));
91303831d35Sstevel 	default:
91403831d35Sstevel 		return (DDI_FAILURE);
91503831d35Sstevel 	}
91603831d35Sstevel }
91703831d35Sstevel 
91803831d35Sstevel static int
pcf8591_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)91903831d35Sstevel pcf8591_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
92003831d35Sstevel {
92103831d35Sstevel 	switch (cmd) {
92203831d35Sstevel 	case DDI_DETACH:
92303831d35Sstevel 		return (pcf8591_do_detach(dip));
92403831d35Sstevel 	case DDI_SUSPEND:
92503831d35Sstevel 		return (pcf8591_do_suspend(dip));
92603831d35Sstevel 	default:
92703831d35Sstevel 		return (DDI_FAILURE);
92803831d35Sstevel 	}
92903831d35Sstevel }
93003831d35Sstevel 
93103831d35Sstevel static uint8_t
translate_cputemp(uint8_t value)93203831d35Sstevel translate_cputemp(uint8_t value)
93303831d35Sstevel {
93403831d35Sstevel 	return (_cpu_temps[value]);
93503831d35Sstevel }
93603831d35Sstevel 
93703831d35Sstevel static int
pcf8591_add_kstats(struct pcf8591_unit * unitp)93803831d35Sstevel pcf8591_add_kstats(struct pcf8591_unit *unitp)
93903831d35Sstevel {
94003831d35Sstevel 	if ((unitp->tempksp = kstat_create(I2C_PCF8591_NAME,
94103831d35Sstevel 	    unitp->instance, I2C_KSTAT_CPUTEMP, "misc",
94203831d35Sstevel 	    KSTAT_TYPE_RAW, sizeof (unitp->temp_kstats),
94303831d35Sstevel 	    KSTAT_FLAG_PERSISTENT | KSTAT_FLAG_WRITABLE)) == NULL) {
94403831d35Sstevel 
94503831d35Sstevel 		return (DDI_FAILURE);
94603831d35Sstevel 	}
94703831d35Sstevel 
94803831d35Sstevel 	/*
94903831d35Sstevel 	 * The kstat fields are already initialized in the attach routine..
95003831d35Sstevel 	 */
95103831d35Sstevel 
95203831d35Sstevel 	unitp->tempksp->ks_update = pcf8591_temp_kstat_update;
95303831d35Sstevel 	unitp->tempksp->ks_private = (void *)unitp;
95403831d35Sstevel 
95507d06da5SSurya Prakki 	(void) strcpy(unitp->temp_kstats.label,
95603831d35Sstevel 	    unitp->props.channels_description[0]);
95703831d35Sstevel 	unitp->temp_kstats.type = ENVC_NETRACT_CPU_SENSOR;
95803831d35Sstevel 
95903831d35Sstevel 	kstat_install(unitp->tempksp);
96003831d35Sstevel 
96103831d35Sstevel 	return (DDI_SUCCESS);
96203831d35Sstevel }
96303831d35Sstevel 
96403831d35Sstevel static void
pcf8591_delete_kstats(struct pcf8591_unit * unitp)96503831d35Sstevel pcf8591_delete_kstats(struct pcf8591_unit *unitp)
96603831d35Sstevel {
96703831d35Sstevel 	kstat_delete(unitp->tempksp);
96803831d35Sstevel }
96903831d35Sstevel 
97003831d35Sstevel static int
pcf8591_temp_kstat_update(kstat_t * ksp,int rw)97103831d35Sstevel pcf8591_temp_kstat_update(kstat_t *ksp, int rw)
97203831d35Sstevel {
97303831d35Sstevel 	struct pcf8591_unit *unitp;
97403831d35Sstevel 	char *kstatp;
97503831d35Sstevel 	int err = 0;
97603831d35Sstevel 	int channel = 0;
97703831d35Sstevel 	int warn_temp = 0;
97803831d35Sstevel 	int shutdown_temp = 0;
97903831d35Sstevel 
98003831d35Sstevel 	unitp = (struct pcf8591_unit *)ksp->ks_private;
98103831d35Sstevel 
98203831d35Sstevel 	mutex_enter(&unitp->umutex);
98303831d35Sstevel 	while (unitp->pcf8591_flags == PCF8591_BUSY) {
98403831d35Sstevel 		if (cv_wait_sig(&unitp->pcf8591_cv,
98503831d35Sstevel 		    &unitp->umutex) <= 0) {
98603831d35Sstevel 			mutex_exit(&unitp->umutex);
98703831d35Sstevel 
98803831d35Sstevel 			return (EINTR);
98903831d35Sstevel 		}
99003831d35Sstevel 	}
99103831d35Sstevel 
99203831d35Sstevel 	unitp->pcf8591_flags = PCF8591_BUSY;
99303831d35Sstevel 	mutex_exit(&unitp->umutex);
99403831d35Sstevel 
99503831d35Sstevel 	kstatp = (char *)ksp->ks_data;
99603831d35Sstevel 
99703831d35Sstevel 	if (rw == KSTAT_WRITE) {
99803831d35Sstevel 
99903831d35Sstevel 		/* check for the size of buffer */
100003831d35Sstevel 		if (ksp->ks_data_size != sizeof (unitp->temp_kstats)) {
100103831d35Sstevel 			err = EIO;
100203831d35Sstevel 			goto bail;
100303831d35Sstevel 		}
100403831d35Sstevel 
100503831d35Sstevel 		warn_temp = ((envctrl_temp_t *)kstatp)->warning_threshold;
100603831d35Sstevel 		shutdown_temp = ((envctrl_temp_t *)kstatp)->shutdown_threshold;
100703831d35Sstevel 
100803831d35Sstevel 		if (shutdown_temp < SHUTDOWN_TEMP_MIN || shutdown_temp >
100903831d35Sstevel 		    SHUTDOWN_TEMP_MAX) {
101003831d35Sstevel 			err = EIO;
101103831d35Sstevel 			goto bail;
101203831d35Sstevel 		}
101303831d35Sstevel 
101403831d35Sstevel 		if (warn_temp < 0 || shutdown_temp <= warn_temp) {
101503831d35Sstevel 			err = EIO;
101603831d35Sstevel 			goto bail;
101703831d35Sstevel 		}
101803831d35Sstevel 
101903831d35Sstevel 		/* write into kstat fields */
102003831d35Sstevel 		unitp->temp_kstats.warning_threshold = warn_temp;
102103831d35Sstevel 		unitp->temp_kstats.shutdown_threshold = shutdown_temp;
102203831d35Sstevel 
102303831d35Sstevel 	} else {
102407d06da5SSurya Prakki 		(void) pcf8591_read_chip(unitp, channel, 1);
102503831d35Sstevel 		unitp->temp_kstats.value =
102603831d35Sstevel 		    unitp->i2c_tran->i2c_rbuf[0];
102703831d35Sstevel 		bcopy((caddr_t)&unitp->temp_kstats, kstatp,
102803831d35Sstevel 		    sizeof (unitp->temp_kstats));
102903831d35Sstevel 	}
103003831d35Sstevel 
103103831d35Sstevel bail:
103203831d35Sstevel 
103303831d35Sstevel 	mutex_enter(&unitp->umutex);
103403831d35Sstevel 	unitp->pcf8591_flags = 0;
103503831d35Sstevel 	cv_signal(&unitp->pcf8591_cv);
103603831d35Sstevel 	mutex_exit(&unitp->umutex);
103703831d35Sstevel 
103803831d35Sstevel 	return (err);
103903831d35Sstevel }
104003831d35Sstevel 
104103831d35Sstevel static int
pcf8591_read_chip(struct pcf8591_unit * unitp,uint8_t channel,int size)104203831d35Sstevel pcf8591_read_chip(struct pcf8591_unit *unitp, uint8_t channel,
104303831d35Sstevel int size)
104403831d35Sstevel {
104503831d35Sstevel 	int retval = I2C_SUCCESS;
104603831d35Sstevel 
104703831d35Sstevel 	/*
104803831d35Sstevel 	 * We need to read an extra byte, since as per specification
104903831d35Sstevel 	 * the first byte read should be discarded.
105003831d35Sstevel 	 */
105103831d35Sstevel 	i2c_transfer_t *tp = unitp->i2c_tran;
105203831d35Sstevel 	tp->i2c_flags = I2C_WR_RD;
105303831d35Sstevel 	tp->i2c_rlen = size+1;
105403831d35Sstevel 	tp->i2c_wlen = 1;
105503831d35Sstevel 	tp->i2c_wbuf[0] = (unitp->pcf8591_inprog |
105603831d35Sstevel 	    channel);
105703831d35Sstevel 
105803831d35Sstevel 	retval = nct_i2c_transfer(unitp->pcf8591_hdl, tp);
105903831d35Sstevel 	if (retval == I2C_SUCCESS) {
106003831d35Sstevel 		tp->i2c_rbuf[0] = translate_cputemp(tp->i2c_rbuf[1]);
106103831d35Sstevel 	}
106203831d35Sstevel 
106303831d35Sstevel 	if (tp->i2c_rbuf[0] == 0) {
106403831d35Sstevel 		retval = I2C_FAILURE;
106503831d35Sstevel 	}
106603831d35Sstevel 
106703831d35Sstevel 	return (retval);
106803831d35Sstevel }
106903831d35Sstevel 
107003831d35Sstevel /*
107103831d35Sstevel  * Reads the properties of the pcf8591 device.
107203831d35Sstevel  */
107303831d35Sstevel static int
pcf8591_read_props(struct pcf8591_unit * unitp)107403831d35Sstevel pcf8591_read_props(struct pcf8591_unit *unitp)
107503831d35Sstevel {
107603831d35Sstevel 	dev_info_t *dip = unitp->dip;
107703831d35Sstevel 	int i, retval = 0, prop_len;
107803831d35Sstevel 	int instance = ddi_get_instance(dip);
107903831d35Sstevel 	int warning_temp, shutdown_temp;
108003831d35Sstevel 	uint32_t *prop_value = NULL;
108103831d35Sstevel 	uchar_t *creg_prop;
108203831d35Sstevel 	char *function;
108303831d35Sstevel 	uint_t		tblsz;
108403831d35Sstevel 
108503831d35Sstevel #ifdef lint
108603831d35Sstevel 	instance = instance;
108703831d35Sstevel #endif
108803831d35Sstevel 	/*
108903831d35Sstevel 	 * Check for the pcf8591_function property, and make sure it's
109003831d35Sstevel 	 * cputemp.
109103831d35Sstevel 	 */
109203831d35Sstevel 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
109303831d35Sstevel 	    "pcf8591_function", &function) != DDI_SUCCESS) {
109403831d35Sstevel 		dbg_print(CE_WARN, "Couldn't find pcf8591_function property");
109503831d35Sstevel 
109603831d35Sstevel 		return (DDI_FAILURE);
109703831d35Sstevel 	}
109803831d35Sstevel 
109903831d35Sstevel 	if (strcmp(function, "cputemp") != 0) {
110003831d35Sstevel 		dbg_print(CE_WARN, "pcf8591_function is not cputemp");
110103831d35Sstevel 		ddi_prop_free(function);
110203831d35Sstevel 
110303831d35Sstevel 		return (DDI_FAILURE);
110403831d35Sstevel 	}
110503831d35Sstevel 
110603831d35Sstevel 	ddi_prop_free(function);
110703831d35Sstevel 
110803831d35Sstevel 	retval = ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
110903831d35Sstevel 	    "name", &unitp->props.name);
111003831d35Sstevel 	if (retval != DDI_PROP_SUCCESS) {
111103831d35Sstevel 
111203831d35Sstevel 		return (retval);
111303831d35Sstevel 	}
111403831d35Sstevel #ifdef DEBUG
111503831d35Sstevel 	else if (pcf8591_debug & 0x02)
111603831d35Sstevel 		cmn_err(CE_NOTE,
111703831d35Sstevel 		    "pcf8591_read_props:ddi_prop_lookup_string(%s): \
111803831d35Sstevel 			found  %s ", "name", unitp->props.name);
111903831d35Sstevel #endif /* DEBUG */
112003831d35Sstevel 
112103831d35Sstevel 	retval = ddi_getlongprop(DDI_DEV_T_ANY, dip,
112203831d35Sstevel 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP,
112303831d35Sstevel 	    "reg", (caddr_t)&prop_value, &prop_len);
112403831d35Sstevel 	if (retval == DDI_PROP_SUCCESS) {
112503831d35Sstevel 		unitp->props.i2c_bus		= (uint16_t)prop_value[0];
112603831d35Sstevel 		unitp->props.slave_address	= (uint16_t)prop_value[1];
112703831d35Sstevel 		kmem_free(prop_value, prop_len);
112803831d35Sstevel #ifdef DEBUG
112903831d35Sstevel 		if (pcf8591_debug & 0x02)
113003831d35Sstevel 			cmn_err(CE_NOTE,
113103831d35Sstevel 			    "pcf8591:ddi_getlongprop(%s) returns %d,"
113203831d35Sstevel 			    " i2c_bus,slave=0x%x,0x%x",
113303831d35Sstevel 			    "reg", retval,  unitp->props.i2c_bus,
113403831d35Sstevel 			    unitp->props.slave_address);
113503831d35Sstevel #endif /* DEBUG */
113603831d35Sstevel 	} else {
113703831d35Sstevel 		unitp->props.i2c_bus		= (uint16_t)-1;
113803831d35Sstevel 		unitp->props.slave_address	= (uint16_t)-1;
113903831d35Sstevel #ifdef DEBUG
114003831d35Sstevel 		cmn_err(CE_WARN,
114103831d35Sstevel 		    "pcf8591_read_props:ddi_getlongprop(%s) returns %d,"
114203831d35Sstevel 		    " default it to 0x%x:0x%X",
114303831d35Sstevel 		    "reg", retval,  unitp->props.i2c_bus,
114403831d35Sstevel 		    unitp->props.slave_address);
114503831d35Sstevel #endif /* DEBUG */
114603831d35Sstevel 	}
114707d06da5SSurya Prakki 	(void) ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
114803831d35Sstevel 	    "channels-in-use", &prop_len);
114903831d35Sstevel 	retval = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY,
115003831d35Sstevel 	    dip, DDI_PROP_DONTPASS,
115103831d35Sstevel 	    "channels-in-use",
115203831d35Sstevel 	    (uchar_t **)&unitp->props.channels_in_use,
115303831d35Sstevel 	    &unitp->props.num_chans_used);
115403831d35Sstevel 	if (retval == DDI_PROP_SUCCESS) {
115503831d35Sstevel 		unitp->props.num_chans_used /= sizeof (pcf8591_channel_t);
115603831d35Sstevel 	} else {
115703831d35Sstevel 		unitp->props.num_chans_used = 0;
115803831d35Sstevel 	}
115903831d35Sstevel 
116003831d35Sstevel #ifdef DEBUG
116103831d35Sstevel 	if (pcf8591_debug & 0x0002)
116203831d35Sstevel 		cmn_err(CE_NOTE,
116303831d35Sstevel 		    "pcf8591_read_props:ddi_prop_lookup_byte_array(%s)"
116403831d35Sstevel 		    "returns %d\n"
116503831d35Sstevel 		    "\t\tlength=%d, #elements=%d",
116603831d35Sstevel 		    "channels-in-use", retval,
116703831d35Sstevel 		    prop_len, unitp->props.num_chans_used);
116803831d35Sstevel #endif /* DEBUG */
116903831d35Sstevel 
117003831d35Sstevel 	retval = ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip,
117103831d35Sstevel 	    DDI_PROP_DONTPASS, "channels-description",
117203831d35Sstevel 	    (char ***)&unitp->props.channels_description,
117303831d35Sstevel 	    (uint_t *)&prop_len);
117403831d35Sstevel 
117503831d35Sstevel 	if (retval != DDI_PROP_SUCCESS) {
117603831d35Sstevel 		prop_len = 0;
117703831d35Sstevel 		unitp->props.channels_description = NULL;
117803831d35Sstevel 	}
117903831d35Sstevel 
118003831d35Sstevel #ifdef DEBUG
118103831d35Sstevel 	if (pcf8591_debug & 0x0002) {
118203831d35Sstevel 		cmn_err(CE_NOTE,
118303831d35Sstevel 		    "pcf8591_read_props:ddi_prop_lookup_string_array(%s)"
118403831d35Sstevel 		    "returns %d, length=%d",
118503831d35Sstevel 		    "channels-description", retval, prop_len);
118603831d35Sstevel 		for (i = 0; i < prop_len; ++i) {
118703831d35Sstevel 			cmn_err(CE_NOTE, "channels-description[%d]=<%s>",
118803831d35Sstevel 			    i, unitp->props.channels_description[i]);
118903831d35Sstevel 		}
119003831d35Sstevel 	}
119103831d35Sstevel #endif /* DEBUG */
119203831d35Sstevel 
119303831d35Sstevel 	/*
119403831d35Sstevel 	 * The following code was borrowed from envctrltwo.c
119503831d35Sstevel 	 * I haven't yet investigated why the copy target is index + 2
119603831d35Sstevel 	 */
119703831d35Sstevel 	retval = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
119803831d35Sstevel 	    DDI_PROP_DONTPASS, "tables", &creg_prop, (uint_t *)&prop_len);
119903831d35Sstevel 
120003831d35Sstevel 	if (retval != DDI_PROP_SUCCESS) {
120103831d35Sstevel #ifdef DEBUG
120203831d35Sstevel 		cmn_err(CE_WARN, "%s%d: Unable to read pcf8591 tables property",
120303831d35Sstevel 		    ddi_get_name(dip), instance);
120403831d35Sstevel #endif /* DEBUG */
120503831d35Sstevel 
120603831d35Sstevel 		return (DDI_NOT_WELL_FORMED);
120703831d35Sstevel 	}
120803831d35Sstevel 
120903831d35Sstevel 	tblsz = (sizeof (_cpu_temps) / sizeof (uchar_t));
121003831d35Sstevel 	if (prop_len <= tblsz) {
121103831d35Sstevel 		for (i = 0; i < prop_len; i++) {
121203831d35Sstevel 			_cpu_temps[i] = creg_prop[i];
121303831d35Sstevel 		}
121403831d35Sstevel 	}
121503831d35Sstevel #ifdef DEBUG
121603831d35Sstevel 	if (pcf8591_debug & 0x0002)
121703831d35Sstevel 		cmn_err(CE_NOTE, "pcf8591_read_props: _cpu_temps size=%d; "
121803831d35Sstevel 		    "tables prop_len=%d\n", tblsz, prop_len);
121903831d35Sstevel #endif /* DEBUG */
122003831d35Sstevel 
122103831d35Sstevel 	ddi_prop_free(creg_prop);
122203831d35Sstevel 
122303831d35Sstevel 	/*
122403831d35Sstevel 	 * Read shutdown temp and warning temp properties.
122503831d35Sstevel 	 */
122603831d35Sstevel 	warning_temp = (int)ddi_getprop(DDI_DEV_T_ANY, dip,
122703831d35Sstevel 	    DDI_PROP_DONTPASS, "warning-temp", PCF8591_WARNING_TEMP);
122803831d35Sstevel 
122903831d35Sstevel 	shutdown_temp = (int)ddi_getprop(DDI_DEV_T_ANY, dip,
123003831d35Sstevel 	    DDI_PROP_DONTPASS, "shutdown-temp", PCF8591_SHUTDOWN_TEMP);
123103831d35Sstevel 
123203831d35Sstevel 	/*
123303831d35Sstevel 	 * Fill up the warning and shutdown temp values in kstat structure.
123403831d35Sstevel 	 */
123503831d35Sstevel 	unitp->temp_kstats.warning_threshold = warning_temp;
123603831d35Sstevel 	unitp->temp_kstats.shutdown_threshold = shutdown_temp;
123703831d35Sstevel 
123803831d35Sstevel 	return (DDI_PROP_SUCCESS);
123903831d35Sstevel }
1240