xref: /onnv-gate/usr/src/uts/sun4u/montecarlo/io/pcf8591_nct.c (revision 11311:639e7bc0b42f)
11708Sstevel /*
21708Sstevel  * CDDL HEADER START
31708Sstevel  *
41708Sstevel  * The contents of this file are subject to the terms of the
57656SSherry.Moore@Sun.COM  * Common Development and Distribution License (the "License").
67656SSherry.Moore@Sun.COM  * You may not use this file except in compliance with the License.
71708Sstevel  *
81708Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel  * or http://www.opensolaris.org/os/licensing.
101708Sstevel  * See the License for the specific language governing permissions
111708Sstevel  * and limitations under the License.
121708Sstevel  *
131708Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel  * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel  *
191708Sstevel  * CDDL HEADER END
201708Sstevel  */
211708Sstevel /*
22*11311SSurya.Prakki@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
231708Sstevel  * Use is subject to license terms.
241708Sstevel  */
251708Sstevel 
261708Sstevel /*
271708Sstevel  * I2C leaf driver for the PCF8591
281708Sstevel  */
291708Sstevel 
301708Sstevel #include <sys/param.h>
311708Sstevel #include <sys/types.h>
321708Sstevel #include <sys/signal.h>
331708Sstevel #include <sys/errno.h>
341708Sstevel #include <sys/file.h>
351708Sstevel #include <sys/termio.h>
361708Sstevel #include <sys/termios.h>
371708Sstevel #include <sys/cmn_err.h>
381708Sstevel #include <sys/stream.h>
391708Sstevel #include <sys/strsun.h>
401708Sstevel #include <sys/stropts.h>
411708Sstevel #include <sys/strtty.h>
421708Sstevel #include <sys/debug.h>
431708Sstevel #include <sys/eucioctl.h>
441708Sstevel #include <sys/cred.h>
451708Sstevel #include <sys/uio.h>
461708Sstevel #include <sys/stat.h>
471708Sstevel #include <sys/kmem.h>
481708Sstevel 
491708Sstevel #include <sys/ddi.h>
501708Sstevel #include <sys/sunddi.h>
511708Sstevel #include <sys/obpdefs.h>
521708Sstevel #include <sys/conf.h>
531708Sstevel #include <sys/modctl.h>
541708Sstevel #include <sys/stat.h>
551708Sstevel #include <sys/open.h>
561708Sstevel #include <sys/uio.h>
571708Sstevel 
581708Sstevel #include <sys/i2c/misc/i2c_svc.h>
591708Sstevel #include <sys/envctrl_gen.h>
601708Sstevel #include <sys/netract_gen.h>
611708Sstevel #include <sys/pcf8591_nct.h>
621708Sstevel 
631708Sstevel 
641708Sstevel /*
651708Sstevel  * 		CONTROL OF CHIP
661708Sstevel  * PCF8591 Temp sensing control register definitions
671708Sstevel  *
681708Sstevel  * ---------------------------------------------
691708Sstevel  * | 0 | AOE | X | X | 0 | AIF | X | X |
701708Sstevel  * ---------------------------------------------
711708Sstevel  * AOE = Analog out enable.. not used on out implementation
721708Sstevel  * 5 & 4 = Analog Input Programming.. see data sheet for bits..
731708Sstevel  *
741708Sstevel  * AIF = Auto increment flag
751708Sstevel  * bits 1 & 0 are for the Chennel number.
761708Sstevel  */
771708Sstevel 
781708Sstevel 
791708Sstevel #define	I2CTRANS_DATA	0
801708Sstevel #define	I2CRAW_DATA	1
811708Sstevel #define	TEMP_TABLE_SIZE	256
821708Sstevel 
831708Sstevel #define	SHUTDOWN_TEMP_MIN	55
841708Sstevel #define	SHUTDOWN_TEMP_MAX	85
851708Sstevel 
861708Sstevel #ifdef DEBUG
871708Sstevel #define	dbg_print(level, str) cmn_err(level, str);
881708Sstevel #else
891708Sstevel #define	dbg_print(level, str) {; }
901708Sstevel #endif
911708Sstevel 
921708Sstevel 
931708Sstevel extern int nct_i2c_transfer(i2c_client_hdl_t i2c_hdl, i2c_transfer_t *i2c_tran);
941708Sstevel static uchar_t _cpu_temps[TEMP_TABLE_SIZE + 4];	/* see attach */
951708Sstevel 
961708Sstevel static void *pcf8591_soft_statep;
971708Sstevel 
981708Sstevel /*
991708Sstevel  * cb ops (only need ioctl)
1001708Sstevel  */
1011708Sstevel static int pcf8591_open(dev_t *, int, int, cred_t *);
1021708Sstevel static int pcf8591_close(dev_t, int, int, cred_t *);
1031708Sstevel static int pcf8591_read(dev_t dev, struct uio *uiop, cred_t *cred_p);
1041708Sstevel static int pcf8591_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1051708Sstevel 
1061708Sstevel static struct cb_ops pcf8591_cbops = {
1071708Sstevel 	pcf8591_open,			/* open */
1081708Sstevel 	pcf8591_close,			/* close */
1091708Sstevel 	nodev,				/* strategy */
1101708Sstevel 	nodev,				/* print */
1111708Sstevel 	nodev,				/* dump */
1121708Sstevel 	pcf8591_read,			/* read */
1131708Sstevel 	nodev,				/* write */
1141708Sstevel 	pcf8591_ioctl,			/* ioctl */
1151708Sstevel 	nodev,				/* devmap */
1161708Sstevel 	nodev,				/* mmap */
1171708Sstevel 	nodev,				/* segmap */
1181708Sstevel 	nochpoll,			/* poll */
1191708Sstevel 	ddi_prop_op,			/* cb_prop_op */
1201708Sstevel 	NULL,				/* streamtab */
1211708Sstevel 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
1221708Sstevel 	CB_REV,				/* rev */
1231708Sstevel 	nodev,				/* int (*cb_aread)() */
1241708Sstevel 	nodev				/* int (*cb_awrite)() */
1251708Sstevel };
1261708Sstevel 
1271708Sstevel /*
1281708Sstevel  * dev ops
1291708Sstevel  */
1301708Sstevel static int pcf8591_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
1311708Sstevel 		void **result);
1321708Sstevel static int pcf8591_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
1331708Sstevel static int pcf8591_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
1341708Sstevel 
1351708Sstevel /* kstat routines */
1361708Sstevel static int pcf8591_add_kstats(struct pcf8591_unit *);
1371708Sstevel static void pcf8591_delete_kstats(struct pcf8591_unit *);
1381708Sstevel static int pcf8591_temp_kstat_update(kstat_t *, int);
1391708Sstevel static int pcf8591_read_chip(struct pcf8591_unit *, uint8_t, int);
1401708Sstevel static int pcf8591_read_props(struct pcf8591_unit *unitp);
1411708Sstevel 
1421708Sstevel static struct dev_ops pcf8591_ops = {
1431708Sstevel 	DEVO_REV,
1441708Sstevel 	0,
1451708Sstevel 	pcf8591_info,
1461708Sstevel 	nulldev,
1471708Sstevel 	nulldev,
1481708Sstevel 	pcf8591_attach,
1491708Sstevel 	pcf8591_detach,
1501708Sstevel 	nodev,
1511708Sstevel 	&pcf8591_cbops,
1527656SSherry.Moore@Sun.COM 	NULL,
1537656SSherry.Moore@Sun.COM 	NULL,
1547656SSherry.Moore@Sun.COM 	ddi_quiesce_not_supported,	/* devo_quiesce */
1551708Sstevel };
1561708Sstevel 
1571708Sstevel extern struct mod_ops mod_driverops;
1581708Sstevel 
1591708Sstevel static struct modldrv pcf8591_modldrv = {
1601708Sstevel 	&mod_driverops,		/* type of module - driver */
1617656SSherry.Moore@Sun.COM 	"Netract pcf8591 (adio)",
1621708Sstevel 	&pcf8591_ops,
1631708Sstevel };
1641708Sstevel 
1651708Sstevel static struct modlinkage pcf8591_modlinkage = {
1661708Sstevel 	MODREV_1,
1671708Sstevel 	&pcf8591_modldrv,
1681708Sstevel 	0
1691708Sstevel };
1701708Sstevel 
1711708Sstevel char	_depends_on[] = "misc/i2c_svc";
1721708Sstevel 
1731708Sstevel int	pcf8591_debug = 0x02;
1741708Sstevel static uint8_t translate_cputemp(uint8_t value);
1751708Sstevel 
1761708Sstevel int
_init(void)1771708Sstevel _init(void)
1781708Sstevel {
1791708Sstevel 	register int    error;
1801708Sstevel 
1811708Sstevel 	error = mod_install(&pcf8591_modlinkage);
1821708Sstevel 	if (error == 0) {
1831708Sstevel 		(void) ddi_soft_state_init(&pcf8591_soft_statep,
1847656SSherry.Moore@Sun.COM 		    sizeof (struct pcf8591_unit), PCF8591_MAX_DEVS);
1851708Sstevel 	}
1861708Sstevel 
1871708Sstevel 	return (error);
1881708Sstevel }
1891708Sstevel 
1901708Sstevel int
_fini(void)1911708Sstevel _fini(void)
1921708Sstevel {
1931708Sstevel 	register int    error;
1941708Sstevel 
1951708Sstevel 	error = mod_remove(&pcf8591_modlinkage);
1961708Sstevel 	if (error == 0) {
1971708Sstevel 		ddi_soft_state_fini(&pcf8591_soft_statep);
1981708Sstevel 	}
1991708Sstevel 
2001708Sstevel 	return (error);
2011708Sstevel }
2021708Sstevel 
2031708Sstevel int
_info(struct modinfo * modinfop)2041708Sstevel _info(struct modinfo *modinfop)
2051708Sstevel {
2061708Sstevel 	return (mod_info(&pcf8591_modlinkage, modinfop));
2071708Sstevel }
2081708Sstevel 
2091708Sstevel /*ARGSUSED*/
2101708Sstevel static int
pcf8591_open(dev_t * devp,int flags,int otyp,cred_t * credp)2111708Sstevel pcf8591_open(dev_t *devp, int flags, int otyp, cred_t *credp)
2121708Sstevel {
2131708Sstevel 	int err = 0;
2141708Sstevel 	struct pcf8591_unit *unitp;
2151708Sstevel 	minor_t minor = getminor(*devp);
2161708Sstevel 
2171708Sstevel 	int instance = PCF8591_MINOR_TO_DEVINST(minor);
2181708Sstevel 	int channel = PCF8591_MINOR_TO_CHANNEL(minor);
2191708Sstevel 
2201708Sstevel 	if (instance < 0) {
2211708Sstevel 		return (ENXIO);
2221708Sstevel 	}
2231708Sstevel 
2241708Sstevel 	unitp = (struct pcf8591_unit *)
2257656SSherry.Moore@Sun.COM 	    ddi_get_soft_state(pcf8591_soft_statep, instance);
2261708Sstevel 
2271708Sstevel 	if (unitp == NULL) {
2281708Sstevel 		return (ENXIO);
2291708Sstevel 	}
2301708Sstevel 
2311708Sstevel 	if (otyp != OTYP_CHR) {
2321708Sstevel 		return (EINVAL);
2331708Sstevel 	}
2341708Sstevel 
2351708Sstevel 	mutex_enter(&unitp->umutex);
2361708Sstevel 
2371708Sstevel 	if (flags & FEXCL) {
2381708Sstevel 		if (unitp->pcf8591_oflag[channel] != 0) {
2391708Sstevel 			err = EBUSY;
2401708Sstevel 		} else {
2411708Sstevel 			unitp->pcf8591_oflag[channel] = FEXCL;
2421708Sstevel 		}
2431708Sstevel 	} else {
2441708Sstevel 		if (unitp->pcf8591_oflag[channel] == FEXCL) {
2451708Sstevel 			err = EBUSY;
2461708Sstevel 		} else {
2471708Sstevel 			unitp->pcf8591_oflag[channel] = FOPEN;
2481708Sstevel 		}
2491708Sstevel 	}
2501708Sstevel 
2511708Sstevel 	mutex_exit(&unitp->umutex);
2521708Sstevel 
2531708Sstevel 	return (err);
2541708Sstevel }
2551708Sstevel 
2561708Sstevel /*ARGSUSED*/
2571708Sstevel static int
pcf8591_close(dev_t devp,int flags,int otyp,cred_t * credp)2581708Sstevel pcf8591_close(dev_t devp, int flags, int otyp, cred_t *credp)
2591708Sstevel {
2601708Sstevel 	struct pcf8591_unit *unitp;
2611708Sstevel 	minor_t minor = getminor(devp);
2621708Sstevel 
2631708Sstevel 	int instance = PCF8591_MINOR_TO_DEVINST(minor);
2641708Sstevel 	int channel = PCF8591_MINOR_TO_CHANNEL(minor);
2651708Sstevel 
2661708Sstevel #ifdef lint
2671708Sstevel 	flags = flags;
2681708Sstevel 	otyp = otyp;
2691708Sstevel #endif
2701708Sstevel 
2711708Sstevel 	if (instance < 0) {
2721708Sstevel 		return (ENXIO);
2731708Sstevel 	}
2741708Sstevel 
2751708Sstevel 	unitp = (struct pcf8591_unit *)
2767656SSherry.Moore@Sun.COM 	    ddi_get_soft_state(pcf8591_soft_statep, instance);
2771708Sstevel 
2781708Sstevel 	if (unitp == NULL) {
2791708Sstevel 		return (ENXIO);
2801708Sstevel 	}
2811708Sstevel 
2821708Sstevel 	mutex_enter(&unitp->umutex);
2831708Sstevel 
2841708Sstevel 	unitp->pcf8591_oflag[channel] = 0;
2851708Sstevel 
2861708Sstevel 	mutex_exit(&unitp->umutex);
2871708Sstevel 
2881708Sstevel 	return (DDI_SUCCESS);
2891708Sstevel }
2901708Sstevel 
2911708Sstevel static int
pcf8591_io(dev_t dev,struct uio * uiop,int rw)2921708Sstevel pcf8591_io(dev_t dev, struct uio *uiop, int rw)
2931708Sstevel {
2941708Sstevel 	int err = 0;
2951708Sstevel 	struct pcf8591_unit *unitp;
2961708Sstevel 	minor_t minor = getminor(dev);
2971708Sstevel 
2981708Sstevel 	int instance = PCF8591_MINOR_TO_DEVINST(minor);
2991708Sstevel 	int channel = PCF8591_MINOR_TO_CHANNEL(minor);
3001708Sstevel 
3011708Sstevel 	int		bytes_to_rw;
3021708Sstevel 	int		translate = 0;
3031708Sstevel 
3041708Sstevel 	/*
3051708Sstevel 	 * At this point we don't have a write operation to pcf8591.
3061708Sstevel 	 */
3071708Sstevel 	if (rw == B_WRITE) {
3081708Sstevel 		return (EACCES);
3091708Sstevel 	}
3101708Sstevel 
3111708Sstevel 	if (instance < 0) {
3121708Sstevel 		return (ENXIO);
3131708Sstevel 	}
3141708Sstevel 
3151708Sstevel 	unitp = (struct pcf8591_unit *)
3167656SSherry.Moore@Sun.COM 	    ddi_get_soft_state(pcf8591_soft_statep, instance);
3171708Sstevel 	if (unitp == NULL) {
3181708Sstevel 		return (ENXIO);
3191708Sstevel 	}
3201708Sstevel 
3211708Sstevel 	if ((bytes_to_rw = uiop->uio_resid) > PCF8591_TRAN_SIZE) {
3221708Sstevel 		return (EINVAL);
3231708Sstevel 	}
3241708Sstevel 
3251708Sstevel 	/*
3261708Sstevel 	 * Need to serialize all read operations, since there is a single
3271708Sstevel 	 * i2c_transfer_t structure allocated for all read and write ops.
3281708Sstevel 	 * We can't share the i2c bus among multiple transactions anyway,
3291708Sstevel 	 * so this does not affect performance.
3301708Sstevel 	 */
3311708Sstevel 	mutex_enter(&unitp->umutex);
3321708Sstevel 	while (unitp->pcf8591_flags == PCF8591_BUSY) {
3331708Sstevel 		if (cv_wait_sig(&unitp->pcf8591_cv, &unitp->umutex) <= 0) {
3341708Sstevel 			mutex_exit(&unitp->umutex);
3351708Sstevel 
3361708Sstevel 			return (EINTR);
3371708Sstevel 		}
3381708Sstevel 	}
3391708Sstevel 	unitp->pcf8591_flags = PCF8591_BUSY;
3401708Sstevel 	mutex_exit(&unitp->umutex);
3411708Sstevel 
3421708Sstevel 	if (bytes_to_rw == 1)
3431708Sstevel 		translate = 1;
3441708Sstevel 	/*
3451708Sstevel 	 * Event sequence:
3461708Sstevel 	 * 1. set up the control register write, for now we'll always read
3471708Sstevel 	 *    channel 0, which is the only active 8591 port on the Nordica
3481708Sstevel 	 *    TODO: We'll need a minor node for each port that is used.
3491708Sstevel 	 * 2. increment read count to read the throw-away byte
3501708Sstevel 	 * 3. start the write/read of control/data registers
3511708Sstevel 	 * 4. throw the first byte away
3521708Sstevel 	 * 5. then return the data
3531708Sstevel 	 */
3541708Sstevel 
3551708Sstevel 	unitp->i2c_tran->i2c_flags = I2C_WR_RD;
3561708Sstevel 	unitp->i2c_tran->i2c_wlen = 1;
3571708Sstevel 	unitp->i2c_tran->i2c_wbuf[0] = (unitp->pcf8591_inprog |
3587656SSherry.Moore@Sun.COM 	    channel);
3591708Sstevel 	/*
3601708Sstevel 	 * read extra byte to throw away the first, (PCF8591 datasheet)
3611708Sstevel 	 */
3621708Sstevel 	unitp->i2c_tran->i2c_rlen = bytes_to_rw + 1;
3631708Sstevel 
3641708Sstevel 	if (nct_i2c_transfer(unitp->pcf8591_hdl,
3657656SSherry.Moore@Sun.COM 	    unitp->i2c_tran) != I2C_SUCCESS) {
3661708Sstevel 		err = EIO;
3671708Sstevel 	} else {
3681708Sstevel 		/*
3691708Sstevel 		 * Throw away the first byte according to PCF8591 datasheet
3701708Sstevel 		 * If translating, use the second byte.
3711708Sstevel 		 */
3721708Sstevel 		if (translate) {
3731708Sstevel 			unitp->i2c_tran->i2c_rbuf[0] =
3747656SSherry.Moore@Sun.COM 			    translate_cputemp(unitp->i2c_tran->i2c_rbuf[1]);
3751708Sstevel 		} else {
3761708Sstevel 			unitp->i2c_tran->i2c_rbuf[0] =
3777656SSherry.Moore@Sun.COM 			    unitp->i2c_tran->i2c_rbuf[1];
3781708Sstevel 			unitp->i2c_tran->i2c_rbuf[1] = 0;
3791708Sstevel 		}
3801708Sstevel 
3811708Sstevel 		err = uiomove(unitp->i2c_tran->i2c_rbuf,
3827656SSherry.Moore@Sun.COM 		    bytes_to_rw,
3837656SSherry.Moore@Sun.COM 		    UIO_READ,
3847656SSherry.Moore@Sun.COM 		    uiop);
3851708Sstevel 	}
3861708Sstevel 	mutex_enter(&unitp->umutex);
3871708Sstevel 	unitp->pcf8591_flags = 0;
3881708Sstevel 	cv_signal(&unitp->pcf8591_cv);
3891708Sstevel 	mutex_exit(&unitp->umutex);
3901708Sstevel 
3911708Sstevel 	return (err);
3921708Sstevel }
3931708Sstevel 
3941708Sstevel /*ARGSUSED*/
3951708Sstevel static int
pcf8591_read(dev_t dev,struct uio * uiop,cred_t * cred_p)3961708Sstevel pcf8591_read(dev_t dev, struct uio *uiop, cred_t *cred_p)
3971708Sstevel {
3981708Sstevel 	return (pcf8591_io(dev, uiop, B_READ));
3991708Sstevel }
4001708Sstevel 
4011708Sstevel static int
call_copyin(caddr_t arg,struct pcf8591_unit * unitp,int mode)4021708Sstevel call_copyin(caddr_t arg, struct pcf8591_unit *unitp, int mode)
4031708Sstevel {
4041708Sstevel 	uchar_t *wbuf;
4051708Sstevel 	uchar_t *rbuf;
4061708Sstevel 	i2c_transfer_t i2ct;
4071708Sstevel 	i2c_transfer_t *i2ctp = unitp->i2c_tran;
4081708Sstevel 
4091708Sstevel 
4101708Sstevel 	if (ddi_copyin((void *)arg, (caddr_t)&i2ct,
4111708Sstevel 	    sizeof (i2c_transfer_t), mode) != DDI_SUCCESS) {
4127656SSherry.Moore@Sun.COM 		return (I2C_FAILURE);
4131708Sstevel 	}
4141708Sstevel 
4151708Sstevel 	/*
4161708Sstevel 	 * Save the read and write buffer pointers in the transfer
4171708Sstevel 	 * structure, otherwise these will get overwritten when we
4181708Sstevel 	 * do a bcopy. Restore once done.
4191708Sstevel 	 */
4201708Sstevel 
4211708Sstevel 	wbuf = i2ctp->i2c_wbuf;
4221708Sstevel 	rbuf = i2ctp->i2c_rbuf;
4231708Sstevel 
4241708Sstevel 	bcopy(&i2ct, i2ctp, sizeof (i2c_transfer_t));
4251708Sstevel 
4261708Sstevel 	i2ctp->i2c_wbuf = wbuf;
4271708Sstevel 	i2ctp->i2c_rbuf = rbuf;
4281708Sstevel 
4291708Sstevel 	/*
4301708Sstevel 	 * copyin the read and write buffers to the saved buffers.
4311708Sstevel 	 */
4321708Sstevel 
4331708Sstevel 	if (i2ct.i2c_wlen != 0) {
4341708Sstevel 		if (ddi_copyin(i2ct.i2c_wbuf, (caddr_t)i2ctp->i2c_wbuf,
4357656SSherry.Moore@Sun.COM 		    i2ct.i2c_wlen, mode) != DDI_SUCCESS) {
4361708Sstevel 				return (I2C_FAILURE);
4371708Sstevel 			}
4381708Sstevel 		}
4391708Sstevel 
4401708Sstevel 	return (I2C_SUCCESS);
4411708Sstevel }
4421708Sstevel 
4431708Sstevel static int
call_copyout(caddr_t arg,struct pcf8591_unit * unitp,int mode)4441708Sstevel call_copyout(caddr_t arg, struct pcf8591_unit *unitp, int mode)
4451708Sstevel {
4461708Sstevel 	i2c_transfer_t i2ct;
4471708Sstevel 	i2c_transfer_t *i2ctp = unitp->i2c_tran;
4481708Sstevel 	uint16_t  i2c_actlen;
4491708Sstevel 
4501708Sstevel 	/*
4511708Sstevel 	 * We will copyout the last three fields only, skipping
4521708Sstevel 	 * the remaining ones, before copying the rbuf to the
4531708Sstevel 	 * user buffer.
4541708Sstevel 	 */
4551708Sstevel 
4561708Sstevel 	int uskip = sizeof (i2c_transfer_t) - 3*sizeof (int16_t),
4577656SSherry.Moore@Sun.COM 	    kskip = sizeof (i2c_transfer_t) - 3*sizeof (int16_t);
4581708Sstevel 
4591708Sstevel 	/*
4601708Sstevel 	 * First copyin the user structure to the temporary i2ct,
4611708Sstevel 	 * so that we have the wbuf and rbuf addresses in it.
4621708Sstevel 	 */
4631708Sstevel 
4641708Sstevel 	uskip = sizeof (i2c_transfer_t) - 3 * (sizeof (uint16_t));
4651708Sstevel 
4661708Sstevel 	/*
4671708Sstevel 	 * copyout the last three out fields now.
4681708Sstevel 	 */
4691708Sstevel 
4701708Sstevel 	if (ddi_copyout((void *)((intptr_t)i2ctp+kskip), (void *)
4717656SSherry.Moore@Sun.COM 	    ((intptr_t)arg + uskip), 3*sizeof (uint16_t), mode)
4727656SSherry.Moore@Sun.COM 	    != DDI_SUCCESS) {
4731708Sstevel 		return (I2C_FAILURE);
4741708Sstevel 		}
4751708Sstevel 
4761708Sstevel 	/*
4771708Sstevel 	 * In case we have something to write, get the address of the read
4781708Sstevel 	 * buffer.
4791708Sstevel 	 */
4801708Sstevel 
4811708Sstevel 	if (i2ctp->i2c_rlen - i2ctp->i2c_r_resid > 0) {
4821708Sstevel 
4831708Sstevel 	if (ddi_copyin((void *)arg, &i2ct,
4847656SSherry.Moore@Sun.COM 	    sizeof (i2c_transfer_t), mode) != DDI_SUCCESS) {
4851708Sstevel 		return (I2C_FAILURE);
4861708Sstevel 	}
4871708Sstevel 
4881708Sstevel 	/*
4891708Sstevel 	 * copyout the read buffer to the saved user buffer in i2ct.
4901708Sstevel 	 */
4911708Sstevel 
4921708Sstevel 		i2c_actlen = i2ctp->i2c_rlen - i2ctp->i2c_r_resid;
4931708Sstevel 		if (ddi_copyout(i2ctp->i2c_rbuf, i2ct.i2c_rbuf,
4947656SSherry.Moore@Sun.COM 		    i2c_actlen, mode) != DDI_SUCCESS) {
4951708Sstevel 				return (I2C_FAILURE);
4961708Sstevel 			}
4971708Sstevel 		}
4981708Sstevel 
4991708Sstevel 	return (I2C_SUCCESS);
5001708Sstevel }
5011708Sstevel 
5021708Sstevel /*
5031708Sstevel  * The ioctls will use the same name as the Javelin ioctls. We
5041708Sstevel  * will have a very restricted set for MC, and unlike Javelin
5051708Sstevel  * will not have a envctrl_chip structure to return values
5061708Sstevel  * from the driver. All we will have is a uint8_t value to
5071708Sstevel  * get or set values from the driver. Also, unlike the Javelin,
5081708Sstevel  * where 'index' is used to specify the input port from where
5091708Sstevel  * temperature is collected, here different minor nodes will be
5101708Sstevel  * created by the driver for each port, eliminating the need for
5111708Sstevel  * 'index' - leaving us with only the value to pass.
5121708Sstevel  */
5131708Sstevel 
5141708Sstevel /*ARGSUSED*/
5151708Sstevel static int
pcf8591_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)5161708Sstevel pcf8591_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
5171708Sstevel 		cred_t *credp, int *rvalp)
5181708Sstevel {
5191708Sstevel 	int err = 0;
5201708Sstevel 	struct pcf8591_unit *unitp;
5211708Sstevel 	minor_t minor = getminor(dev);
5221708Sstevel 
5231708Sstevel 	int instance = PCF8591_MINOR_TO_DEVINST(minor);
5241708Sstevel 	int channel = PCF8591_MINOR_TO_CHANNEL(minor);
5251708Sstevel 
5261708Sstevel 	unitp = (struct pcf8591_unit *)
5277656SSherry.Moore@Sun.COM 	    ddi_get_soft_state(pcf8591_soft_statep, instance);
5281708Sstevel 
5291708Sstevel 	mutex_enter(&unitp->umutex);
5301708Sstevel 	while (unitp->pcf8591_flags == PCF8591_BUSY) {
5311708Sstevel 		if (cv_wait_sig(&unitp->pcf8591_cv, &unitp->umutex) <= 0) {
5321708Sstevel 			mutex_exit(&unitp->umutex);
5331708Sstevel 
5341708Sstevel 			return (EINTR);
5351708Sstevel 		}
5361708Sstevel 	}
5371708Sstevel 	unitp->pcf8591_flags = PCF8591_BUSY;
5381708Sstevel 	mutex_exit(&unitp->umutex);
5391708Sstevel 
5401708Sstevel 	switch (cmd) {
5411708Sstevel 
5421708Sstevel 	case ENVC_IOC_GETTEMP: {
5431708Sstevel 		/*
5441708Sstevel 		 * Read the status byte from pcf8591 chip. The value will
5451708Sstevel 		 * be already converted to Celcius by translate_cputemp.
5461708Sstevel 		 */
547*11311SSurya.Prakki@Sun.COM 		(void) pcf8591_read_chip(unitp, channel, 1);
5481708Sstevel 		if (ddi_copyout(unitp->i2c_tran->i2c_rbuf,
5497656SSherry.Moore@Sun.COM 		    (caddr_t)arg, sizeof (uint8_t), mode) != DDI_SUCCESS) {
5501708Sstevel 			err = EFAULT;
5511708Sstevel 		}
5521708Sstevel 		break;
5531708Sstevel 	}
5541708Sstevel 
5551708Sstevel 	case ENVC_IOC_GETMODE: {
5561708Sstevel 		uint8_t curr_mode = unitp->current_mode;
5571708Sstevel 
5581708Sstevel 		if (ddi_copyout((caddr_t)&curr_mode, (caddr_t)arg,
5597656SSherry.Moore@Sun.COM 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
5601708Sstevel 			err = EFAULT;
5611708Sstevel 		}
5621708Sstevel 		break;
5631708Sstevel 	}
5641708Sstevel 
5651708Sstevel 	case ENVC_IOC_SETMODE: {
5661708Sstevel 		uint8_t curr_mode;
5671708Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)&curr_mode,
5687656SSherry.Moore@Sun.COM 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
5691708Sstevel 				err = EFAULT;
5701708Sstevel 				break;
5711708Sstevel 		}
5721708Sstevel 		if (curr_mode == ENVCTRL_DIAG_MODE ||
5737656SSherry.Moore@Sun.COM 		    curr_mode == ENVCTRL_NORMAL_MODE) {
5741708Sstevel 			unitp->current_mode = curr_mode; /* Don't do anything */
5751708Sstevel 		}
5761708Sstevel 		break;
5771708Sstevel 	}
5781708Sstevel 
5791708Sstevel 	/* Testing, may be removed */
5801708Sstevel 	case I2CDEV_TRAN:
5811708Sstevel 		if (call_copyin((caddr_t)arg, unitp, mode) != I2C_SUCCESS) {
5821708Sstevel 			err = EFAULT;
5831708Sstevel 			break;
5841708Sstevel 		}
5851708Sstevel 		if (nct_i2c_transfer(unitp->pcf8591_hdl, unitp->i2c_tran)
5867656SSherry.Moore@Sun.COM 		    != I2C_SUCCESS) {
5871708Sstevel 			err = EFAULT;
5881708Sstevel 			break;
5891708Sstevel 		}
5901708Sstevel 		if (call_copyout((caddr_t)arg, unitp, mode) != I2C_SUCCESS) {
5911708Sstevel 			err = EFAULT;
5921708Sstevel 			break;
5931708Sstevel 		}
5941708Sstevel 		break;
5951708Sstevel 
5961708Sstevel 	/*
5971708Sstevel 	 * TESTING TRANSLATION from "adc" "table" property
5981708Sstevel 	 * translate thermistor index into temp Celcius
5991708Sstevel 	 */
6001708Sstevel 	case I2CDEV_GETTEMP: {
6011708Sstevel 		struct i2c_transfer *tp;
6021708Sstevel 		if (call_copyin((caddr_t)arg, unitp, mode) != I2C_SUCCESS) {
6031708Sstevel 			err = EFAULT;
6041708Sstevel 			break;
6051708Sstevel 		}
6061708Sstevel 		tp = unitp->i2c_tran;
6071708Sstevel 		if (tp->i2c_rlen != 1) {
6081708Sstevel 			err = EINVAL;
6091708Sstevel 			break;
6101708Sstevel 		}
6111708Sstevel 		/*
6121708Sstevel 		 * Throw away the first byte according to PCF8591 datasheet,
6131708Sstevel 		 * so read two bytes
6141708Sstevel 		 */
6151708Sstevel 		tp->i2c_rlen = 2;
6161708Sstevel 		if (nct_i2c_transfer(unitp->pcf8591_hdl, unitp->i2c_tran)
6177656SSherry.Moore@Sun.COM 		    != I2C_SUCCESS) {
6181708Sstevel 			err = EFAULT;
6191708Sstevel 			break;
6201708Sstevel 		}
6211708Sstevel #ifdef DEBUG
6221708Sstevel 		if (pcf8591_debug & 0x0010)
6231708Sstevel 			cmn_err(CE_NOTE,
6247656SSherry.Moore@Sun.COM 			    "pcf8591_ioctl: i2c_rlen=%d; "
6257656SSherry.Moore@Sun.COM 			    "i2c_rbuf[0,1]=0x%x,0x%x\n",
6267656SSherry.Moore@Sun.COM 			    tp->i2c_rlen, tp->i2c_rbuf[0], tp->i2c_rbuf[1]);
6271708Sstevel #endif /* DEBUG */
6281708Sstevel 		/*
6291708Sstevel 		 * Throw away the first byte according to PCF8591 datasheet
6301708Sstevel 		 */
6311708Sstevel 		if ((tp->i2c_rbuf[0] = translate_cputemp(tp->i2c_rbuf[1]))
6327656SSherry.Moore@Sun.COM 		    == 0) {
6331708Sstevel 			err = EINVAL;
6341708Sstevel 			break;
6351708Sstevel 		}
6361708Sstevel 		tp->i2c_rbuf[1] = 0;
6371708Sstevel 
6381708Sstevel 		if (call_copyout((caddr_t)arg, unitp, mode) != I2C_SUCCESS) {
6391708Sstevel 			err = EFAULT;
6401708Sstevel 			break;
6411708Sstevel 		}
6421708Sstevel 		break;
6431708Sstevel 	}
6441708Sstevel 
6451708Sstevel 	case I2CDEV_GETTABLES: {
6461708Sstevel 		break;
6471708Sstevel 	}
6481708Sstevel 	default:
6491708Sstevel 		err = EINVAL;
6501708Sstevel 	}
6511708Sstevel 
6521708Sstevel 	mutex_enter(&unitp->umutex);
6531708Sstevel 	unitp->pcf8591_flags = 0;
6541708Sstevel 	cv_signal(&unitp->pcf8591_cv);
6551708Sstevel 	mutex_exit(&unitp->umutex);
6561708Sstevel 
6571708Sstevel 	return (err);
6581708Sstevel }
6591708Sstevel 
6601708Sstevel static int
pcf8591_do_detach(dev_info_t * dip)6611708Sstevel pcf8591_do_detach(dev_info_t *dip)
6621708Sstevel {
6631708Sstevel 	register struct pcf8591_unit *unitp;
6641708Sstevel 	int instance;
6651708Sstevel 	uint_t attach_flag;
6661708Sstevel 
6671708Sstevel 	instance = ddi_get_instance(dip);
6681708Sstevel 	unitp = ddi_get_soft_state(pcf8591_soft_statep, instance);
6691708Sstevel 	attach_flag = unitp->attach_flag;
6701708Sstevel 
6711708Sstevel 	if (attach_flag & PCF8591_KSTAT_INIT) {
6721708Sstevel 		pcf8591_delete_kstats(unitp);
6731708Sstevel 	}
6741708Sstevel 
6751708Sstevel 	if (attach_flag & PCF8591_LOCK_INIT) {
6761708Sstevel 		mutex_destroy(&unitp->umutex);
6771708Sstevel 		cv_destroy(&unitp->pcf8591_cv);
6781708Sstevel 	}
6791708Sstevel 
6801708Sstevel 	/*
6811708Sstevel 	 * Restore the lengths of the rbuf and wbuf, which was originally
6821708Sstevel 	 * allocated so that the appropriate amount of rbuf and wbuf are
6831708Sstevel 	 * freed.
6841708Sstevel 	 */
6851708Sstevel 	if (attach_flag & PCF8591_ALLOC_TRANSFER) {
6861708Sstevel 		unitp->i2c_tran->i2c_wlen = MAX_WLEN;
6871708Sstevel 		unitp->i2c_tran->i2c_rlen = MAX_RLEN;
6881708Sstevel 		i2c_transfer_free(unitp->pcf8591_hdl, unitp->i2c_tran);
6891708Sstevel 	}
6901708Sstevel 
6911708Sstevel 	if (attach_flag & PCF8591_REGISTER_CLIENT) {
6921708Sstevel 		i2c_client_unregister(unitp->pcf8591_hdl);
6931708Sstevel 	}
6941708Sstevel 
6951708Sstevel 	if (attach_flag & PCF8591_MINORS_CREATED) {
6961708Sstevel 		ddi_remove_minor_node(dip, NULL);
6971708Sstevel 	}
6981708Sstevel 
6991708Sstevel 	/*
7001708Sstevel 	 * Free the memory allocated for the properties.
7011708Sstevel 	 */
7021708Sstevel 	if (attach_flag & PCF8591_PROPS_READ) {
7031708Sstevel 		ddi_prop_free(unitp->props.name);
7041708Sstevel 		if (unitp->props.num_chans_used) {
7051708Sstevel 			ddi_prop_free(unitp->props.channels_in_use);
7061708Sstevel 		}
7071708Sstevel 
7081708Sstevel 		if (unitp->props.channels_description) {
7091708Sstevel 			ddi_prop_free(unitp->props.channels_description);
7101708Sstevel 		}
7111708Sstevel 	}
7121708Sstevel 
7131708Sstevel 	if (attach_flag & PCF8591_SOFT_STATE_ALLOC) {
7141708Sstevel 		ddi_soft_state_free(pcf8591_soft_statep, instance);
7151708Sstevel 	}
7161708Sstevel 
7171708Sstevel 	return (DDI_SUCCESS);
7181708Sstevel }
7191708Sstevel 
7201708Sstevel static int
pcf8591_do_suspend(dev_info_t * dip)7211708Sstevel pcf8591_do_suspend(dev_info_t *dip)
7221708Sstevel {
7231708Sstevel 	int instance = ddi_get_instance(dip);
7241708Sstevel 	struct pcf8591_unit *unitp = (struct pcf8591_unit *)
7257656SSherry.Moore@Sun.COM 	    ddi_get_soft_state(pcf8591_soft_statep, instance);
7261708Sstevel 
7271708Sstevel 	if (unitp == NULL) {
7281708Sstevel 		return (ENXIO);
7291708Sstevel 	}
7301708Sstevel 
7311708Sstevel 	/*
7321708Sstevel 	 * Set the busy flag so that future transactions block
7331708Sstevel 	 * until resume.
7341708Sstevel 	 */
7351708Sstevel 	mutex_enter(&unitp->umutex);
7361708Sstevel 	while (unitp->pcf8591_flags == PCF8591_BUSY) {
7371708Sstevel 		if (cv_wait_sig(&unitp->pcf8591_cv,
7387656SSherry.Moore@Sun.COM 		    &unitp->umutex) <= 0) {
7391708Sstevel 			mutex_exit(&unitp->umutex);
7401708Sstevel 
7411708Sstevel 			return (DDI_FAILURE);
7421708Sstevel 		}
7431708Sstevel 	}
7441708Sstevel 	unitp->pcf8591_flags = PCF8591_BUSY;
7451708Sstevel 	mutex_exit(&unitp->umutex);
7461708Sstevel 
7471708Sstevel 	return (DDI_SUCCESS);
7481708Sstevel }
7491708Sstevel 
7501708Sstevel static int
pcf8591_do_resume(dev_info_t * dip)7511708Sstevel pcf8591_do_resume(dev_info_t *dip)
7521708Sstevel {
7531708Sstevel 	int instance = ddi_get_instance(dip);
7541708Sstevel 	struct pcf8591_unit *unitp = (struct pcf8591_unit *)
7557656SSherry.Moore@Sun.COM 	    ddi_get_soft_state(pcf8591_soft_statep, instance);
7561708Sstevel 	if (unitp == NULL) {
7571708Sstevel 		return (ENXIO);
7581708Sstevel 	}
7591708Sstevel 
7601708Sstevel 	mutex_enter(&unitp->umutex);
7611708Sstevel 	unitp->pcf8591_flags = 0;
7621708Sstevel 	cv_signal(&unitp->pcf8591_cv);
7631708Sstevel 	mutex_exit(&unitp->umutex);
7641708Sstevel 
7651708Sstevel 	return (DDI_SUCCESS);
7661708Sstevel }
7671708Sstevel 
7681708Sstevel static int
pcf8591_do_attach(dev_info_t * dip)7691708Sstevel pcf8591_do_attach(dev_info_t *dip)
7701708Sstevel {
7711708Sstevel 	register struct pcf8591_unit *unitp;
7721708Sstevel 	int i, instance;
7731708Sstevel 	char name[MAXNAMELEN];
7741708Sstevel 	minor_t minor;
7751708Sstevel 
7761708Sstevel 	instance = ddi_get_instance(dip);
7771708Sstevel 
7781708Sstevel 	if (ddi_soft_state_zalloc(pcf8591_soft_statep, instance) != 0) {
7791708Sstevel 		return (DDI_FAILURE);
7801708Sstevel 	}
7811708Sstevel 
7821708Sstevel 	unitp = ddi_get_soft_state(pcf8591_soft_statep, instance);
7831708Sstevel 
7841708Sstevel 	if (unitp == NULL) {
7851708Sstevel 		return (DDI_FAILURE);
7861708Sstevel 	}
7871708Sstevel 
7881708Sstevel 	unitp->dip = dip;
7891708Sstevel 
7901708Sstevel 	unitp->attach_flag = PCF8591_SOFT_STATE_ALLOC;
7911708Sstevel 
7921708Sstevel 	if (pcf8591_read_props(unitp) != DDI_PROP_SUCCESS) {
793*11311SSurya.Prakki@Sun.COM 		(void) pcf8591_do_detach(dip);
7941708Sstevel 		return (DDI_FAILURE);
7951708Sstevel 	}
7961708Sstevel 
7971708Sstevel 	unitp->attach_flag |= PCF8591_PROPS_READ;
7981708Sstevel 
7991708Sstevel 	/*
8001708Sstevel 	 * Set the current operating mode to NORMAL_MODE.
8011708Sstevel 	 */
8021708Sstevel 	unitp->current_mode = ENVCTRL_NORMAL_MODE; /* normal mode */
8031708Sstevel 
804*11311SSurya.Prakki@Sun.COM 	(void) snprintf(unitp->pcf8591_name, PCF8591_NAMELEN,
8057656SSherry.Moore@Sun.COM 	    "%s%d", ddi_driver_name(dip), instance);
8061708Sstevel 
8071708Sstevel 	/*
8081708Sstevel 	 * Create a minor node corresponding to channel 0 to 3
8091708Sstevel 	 */
8101708Sstevel 	for (i = 0; i < PCF8591_MAX_CHANS; i++) {
8111708Sstevel 	if (i == 0) {
8121708Sstevel 		(void) sprintf(name, "cputemp");
8131708Sstevel 	} else {
8141708Sstevel 		(void) sprintf(name, "%d", i);
8151708Sstevel 	}
8161708Sstevel 	minor = PCF8591_MINOR_NUM(instance, i);
8171708Sstevel 	if (ddi_create_minor_node(dip, name, S_IFCHR, minor,
8187656SSherry.Moore@Sun.COM 	    PCF8591_NODE_TYPE, NULL) == DDI_FAILURE) {
8191708Sstevel 			ddi_remove_minor_node(dip, NULL);
820*11311SSurya.Prakki@Sun.COM 			(void) pcf8591_do_detach(dip);
8211708Sstevel 			return (DDI_FAILURE);
8221708Sstevel 		}
8231708Sstevel 	}
8241708Sstevel 
8251708Sstevel 	unitp->attach_flag |= PCF8591_MINORS_CREATED;
8261708Sstevel 
8271708Sstevel 	if (i2c_client_register(dip, &unitp->pcf8591_hdl)
8287656SSherry.Moore@Sun.COM 	    != I2C_SUCCESS) {
829*11311SSurya.Prakki@Sun.COM 		(void) pcf8591_do_detach(dip);
8301708Sstevel 		return (DDI_FAILURE);
8311708Sstevel 	}
8321708Sstevel 
8331708Sstevel 	unitp->attach_flag |= PCF8591_REGISTER_CLIENT;
8341708Sstevel 
8351708Sstevel 	/*
8361708Sstevel 	 * We allocate a single i2c_transfer_t structure for all
8371708Sstevel 	 * i2c transactions.
8381708Sstevel 	 */
8391708Sstevel 	if (i2c_transfer_alloc(unitp->pcf8591_hdl, &unitp->i2c_tran,
8407656SSherry.Moore@Sun.COM 	    MAX_WLEN, MAX_RLEN, KM_SLEEP) != I2C_SUCCESS) {
841*11311SSurya.Prakki@Sun.COM 		(void) pcf8591_do_detach(dip);
8421708Sstevel 		return (DDI_FAILURE);
8431708Sstevel 	}
8441708Sstevel 
8451708Sstevel 	unitp->attach_flag |= PCF8591_ALLOC_TRANSFER;
8461708Sstevel 
8471708Sstevel 	/*
8481708Sstevel 	 * The flags will be set to I2C_WR because for all reads from
8491708Sstevel 	 * the 8591 we need to also write the control byte.
8501708Sstevel 	 */
8511708Sstevel 	unitp->i2c_tran->i2c_flags = I2C_WR;
8521708Sstevel 	unitp->i2c_tran->i2c_version = I2C_XFER_REV;
8531708Sstevel 
8541708Sstevel 
8551708Sstevel 	/*
8561708Sstevel 	 * Set the analog programming mode to default. Upper nibble
8571708Sstevel 	 * in control byte. Four single ended inputs, output not enabled.
8581708Sstevel 	 */
8591708Sstevel 	unitp->pcf8591_inprog = PCF8591_4SINGLE | PCF8591_ANALOG_INPUT_EN;
8601708Sstevel 
8611708Sstevel 	/*
8621708Sstevel 	 * Set the open flag for each channel to 0.
8631708Sstevel 	 */
8641708Sstevel 	for (i = 0; i < PCF8591_MAX_CHANS; i++) {
8651708Sstevel 		unitp->pcf8591_oflag[i] = 0;
8661708Sstevel 	}
8671708Sstevel 
8681708Sstevel 	/*
8691708Sstevel 	 * Set the busy flag to 0.
8701708Sstevel 	 */
8711708Sstevel 	unitp->pcf8591_flags = 0;
8721708Sstevel 
8731708Sstevel 	mutex_init(&unitp->umutex, NULL, MUTEX_DRIVER, NULL);
8741708Sstevel 	cv_init(&unitp->pcf8591_cv, NULL, CV_DRIVER, NULL);
8751708Sstevel 
8761708Sstevel 	unitp->attach_flag |= PCF8591_LOCK_INIT;
8771708Sstevel 
8781708Sstevel 	if (pcf8591_add_kstats(unitp) != DDI_SUCCESS) {
879*11311SSurya.Prakki@Sun.COM 		(void) pcf8591_do_detach(dip);
8801708Sstevel 		return (DDI_FAILURE);
8811708Sstevel 	}
8821708Sstevel 
8831708Sstevel 	unitp->attach_flag |= PCF8591_KSTAT_INIT;
8841708Sstevel 
8851708Sstevel 	ddi_report_dev(dip);
8861708Sstevel 
8871708Sstevel 	return (DDI_SUCCESS);
8881708Sstevel }
8891708Sstevel 
8901708Sstevel /* ARGSUSED */
8911708Sstevel static int
pcf8591_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)8921708Sstevel pcf8591_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
8931708Sstevel {
8941708Sstevel 	dev_t	dev;
8951708Sstevel 	int	instance;
8961708Sstevel 
8971708Sstevel 	if (infocmd == DDI_INFO_DEVT2INSTANCE) {
8981708Sstevel 		dev = (dev_t)arg;
8991708Sstevel 		instance = PCF8591_MINOR_TO_DEVINST(getminor(dev));
9001708Sstevel 		*result = (void *)(uintptr_t)instance;
9011708Sstevel 		return (DDI_SUCCESS);
9021708Sstevel 	}
9031708Sstevel 	return (DDI_FAILURE);
9041708Sstevel }
9051708Sstevel 
9061708Sstevel static int
pcf8591_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)9071708Sstevel pcf8591_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
9081708Sstevel {
9091708Sstevel 	switch (cmd) {
9101708Sstevel 	case DDI_ATTACH:
9111708Sstevel 		return (pcf8591_do_attach(dip));
9121708Sstevel 	case DDI_RESUME:
9131708Sstevel 		return (pcf8591_do_resume(dip));
9141708Sstevel 	default:
9151708Sstevel 		return (DDI_FAILURE);
9161708Sstevel 	}
9171708Sstevel }
9181708Sstevel 
9191708Sstevel static int
pcf8591_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)9201708Sstevel pcf8591_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
9211708Sstevel {
9221708Sstevel 	switch (cmd) {
9231708Sstevel 	case DDI_DETACH:
9241708Sstevel 		return (pcf8591_do_detach(dip));
9251708Sstevel 	case DDI_SUSPEND:
9261708Sstevel 		return (pcf8591_do_suspend(dip));
9271708Sstevel 	default:
9281708Sstevel 		return (DDI_FAILURE);
9291708Sstevel 	}
9301708Sstevel }
9311708Sstevel 
9321708Sstevel static uint8_t
translate_cputemp(uint8_t value)9331708Sstevel translate_cputemp(uint8_t value)
9341708Sstevel {
9351708Sstevel 	return (_cpu_temps[value]);
9361708Sstevel }
9371708Sstevel 
9381708Sstevel static int
pcf8591_add_kstats(struct pcf8591_unit * unitp)9391708Sstevel pcf8591_add_kstats(struct pcf8591_unit *unitp)
9401708Sstevel {
9411708Sstevel 	if ((unitp->tempksp = kstat_create(I2C_PCF8591_NAME,
9427656SSherry.Moore@Sun.COM 	    unitp->instance, I2C_KSTAT_CPUTEMP, "misc",
9437656SSherry.Moore@Sun.COM 	    KSTAT_TYPE_RAW, sizeof (unitp->temp_kstats),
9447656SSherry.Moore@Sun.COM 	    KSTAT_FLAG_PERSISTENT | KSTAT_FLAG_WRITABLE)) == NULL) {
9451708Sstevel 
9461708Sstevel 		return (DDI_FAILURE);
9471708Sstevel 	}
9481708Sstevel 
9491708Sstevel 	/*
9501708Sstevel 	 * The kstat fields are already initialized in the attach routine..
9511708Sstevel 	 */
9521708Sstevel 
9531708Sstevel 	unitp->tempksp->ks_update = pcf8591_temp_kstat_update;
9541708Sstevel 	unitp->tempksp->ks_private = (void *)unitp;
9551708Sstevel 
956*11311SSurya.Prakki@Sun.COM 	(void) strcpy(unitp->temp_kstats.label,
9577656SSherry.Moore@Sun.COM 	    unitp->props.channels_description[0]);
9581708Sstevel 	unitp->temp_kstats.type = ENVC_NETRACT_CPU_SENSOR;
9591708Sstevel 
9601708Sstevel 	kstat_install(unitp->tempksp);
9611708Sstevel 
9621708Sstevel 	return (DDI_SUCCESS);
9631708Sstevel }
9641708Sstevel 
9651708Sstevel static void
pcf8591_delete_kstats(struct pcf8591_unit * unitp)9661708Sstevel pcf8591_delete_kstats(struct pcf8591_unit *unitp)
9671708Sstevel {
9681708Sstevel 	kstat_delete(unitp->tempksp);
9691708Sstevel }
9701708Sstevel 
9711708Sstevel static int
pcf8591_temp_kstat_update(kstat_t * ksp,int rw)9721708Sstevel pcf8591_temp_kstat_update(kstat_t *ksp, int rw)
9731708Sstevel {
9741708Sstevel 	struct pcf8591_unit *unitp;
9751708Sstevel 	char *kstatp;
9761708Sstevel 	int err = 0;
9771708Sstevel 	int channel = 0;
9781708Sstevel 	int warn_temp = 0;
9791708Sstevel 	int shutdown_temp = 0;
9801708Sstevel 
9811708Sstevel 	unitp = (struct pcf8591_unit *)ksp->ks_private;
9821708Sstevel 
9831708Sstevel 	mutex_enter(&unitp->umutex);
9841708Sstevel 	while (unitp->pcf8591_flags == PCF8591_BUSY) {
9851708Sstevel 		if (cv_wait_sig(&unitp->pcf8591_cv,
9867656SSherry.Moore@Sun.COM 		    &unitp->umutex) <= 0) {
9871708Sstevel 			mutex_exit(&unitp->umutex);
9881708Sstevel 
9891708Sstevel 			return (EINTR);
9901708Sstevel 		}
9911708Sstevel 	}
9921708Sstevel 
9931708Sstevel 	unitp->pcf8591_flags = PCF8591_BUSY;
9941708Sstevel 	mutex_exit(&unitp->umutex);
9951708Sstevel 
9961708Sstevel 	kstatp = (char *)ksp->ks_data;
9971708Sstevel 
9981708Sstevel 	if (rw == KSTAT_WRITE) {
9991708Sstevel 
10001708Sstevel 		/* check for the size of buffer */
10011708Sstevel 		if (ksp->ks_data_size != sizeof (unitp->temp_kstats)) {
10021708Sstevel 			err = EIO;
10031708Sstevel 			goto bail;
10041708Sstevel 		}
10051708Sstevel 
10061708Sstevel 		warn_temp = ((envctrl_temp_t *)kstatp)->warning_threshold;
10071708Sstevel 		shutdown_temp = ((envctrl_temp_t *)kstatp)->shutdown_threshold;
10081708Sstevel 
10091708Sstevel 		if (shutdown_temp < SHUTDOWN_TEMP_MIN || shutdown_temp >
10107656SSherry.Moore@Sun.COM 		    SHUTDOWN_TEMP_MAX) {
10111708Sstevel 			err = EIO;
10121708Sstevel 			goto bail;
10131708Sstevel 		}
10141708Sstevel 
10151708Sstevel 		if (warn_temp < 0 || shutdown_temp <= warn_temp) {
10161708Sstevel 			err = EIO;
10171708Sstevel 			goto bail;
10181708Sstevel 		}
10191708Sstevel 
10201708Sstevel 		/* write into kstat fields */
10211708Sstevel 		unitp->temp_kstats.warning_threshold = warn_temp;
10221708Sstevel 		unitp->temp_kstats.shutdown_threshold = shutdown_temp;
10231708Sstevel 
10241708Sstevel 	} else {
1025*11311SSurya.Prakki@Sun.COM 		(void) pcf8591_read_chip(unitp, channel, 1);
10261708Sstevel 		unitp->temp_kstats.value =
10277656SSherry.Moore@Sun.COM 		    unitp->i2c_tran->i2c_rbuf[0];
10281708Sstevel 		bcopy((caddr_t)&unitp->temp_kstats, kstatp,
10297656SSherry.Moore@Sun.COM 		    sizeof (unitp->temp_kstats));
10301708Sstevel 	}
10311708Sstevel 
10321708Sstevel bail:
10331708Sstevel 
10341708Sstevel 	mutex_enter(&unitp->umutex);
10351708Sstevel 	unitp->pcf8591_flags = 0;
10361708Sstevel 	cv_signal(&unitp->pcf8591_cv);
10371708Sstevel 	mutex_exit(&unitp->umutex);
10381708Sstevel 
10391708Sstevel 	return (err);
10401708Sstevel }
10411708Sstevel 
10421708Sstevel static int
pcf8591_read_chip(struct pcf8591_unit * unitp,uint8_t channel,int size)10431708Sstevel pcf8591_read_chip(struct pcf8591_unit *unitp, uint8_t channel,
10441708Sstevel int size)
10451708Sstevel {
10461708Sstevel 	int retval = I2C_SUCCESS;
10471708Sstevel 
10481708Sstevel 	/*
10491708Sstevel 	 * We need to read an extra byte, since as per specification
10501708Sstevel 	 * the first byte read should be discarded.
10511708Sstevel 	 */
10521708Sstevel 	i2c_transfer_t *tp = unitp->i2c_tran;
10531708Sstevel 	tp->i2c_flags = I2C_WR_RD;
10541708Sstevel 	tp->i2c_rlen = size+1;
10551708Sstevel 	tp->i2c_wlen = 1;
10561708Sstevel 	tp->i2c_wbuf[0] = (unitp->pcf8591_inprog |
10577656SSherry.Moore@Sun.COM 	    channel);
10581708Sstevel 
10591708Sstevel 	retval = nct_i2c_transfer(unitp->pcf8591_hdl, tp);
10601708Sstevel 	if (retval == I2C_SUCCESS) {
10611708Sstevel 		tp->i2c_rbuf[0] = translate_cputemp(tp->i2c_rbuf[1]);
10621708Sstevel 	}
10631708Sstevel 
10641708Sstevel 	if (tp->i2c_rbuf[0] == 0) {
10651708Sstevel 		retval = I2C_FAILURE;
10661708Sstevel 	}
10671708Sstevel 
10681708Sstevel 	return (retval);
10691708Sstevel }
10701708Sstevel 
10711708Sstevel /*
10721708Sstevel  * Reads the properties of the pcf8591 device.
10731708Sstevel  */
10741708Sstevel static int
pcf8591_read_props(struct pcf8591_unit * unitp)10751708Sstevel pcf8591_read_props(struct pcf8591_unit *unitp)
10761708Sstevel {
10771708Sstevel 	dev_info_t *dip = unitp->dip;
10781708Sstevel 	int i, retval = 0, prop_len;
10791708Sstevel 	int instance = ddi_get_instance(dip);
10801708Sstevel 	int warning_temp, shutdown_temp;
10811708Sstevel 	uint32_t *prop_value = NULL;
10821708Sstevel 	uchar_t *creg_prop;
10831708Sstevel 	char *function;
10841708Sstevel 	uint_t		tblsz;
10851708Sstevel 
10861708Sstevel #ifdef lint
10871708Sstevel 	instance = instance;
10881708Sstevel #endif
10891708Sstevel 	/*
10901708Sstevel 	 * Check for the pcf8591_function property, and make sure it's
10911708Sstevel 	 * cputemp.
10921708Sstevel 	 */
10931708Sstevel 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
10947656SSherry.Moore@Sun.COM 	    "pcf8591_function", &function) != DDI_SUCCESS) {
10951708Sstevel 		dbg_print(CE_WARN, "Couldn't find pcf8591_function property");
10961708Sstevel 
10971708Sstevel 		return (DDI_FAILURE);
10981708Sstevel 	}
10991708Sstevel 
11001708Sstevel 	if (strcmp(function, "cputemp") != 0) {
11011708Sstevel 		dbg_print(CE_WARN, "pcf8591_function is not cputemp");
11021708Sstevel 		ddi_prop_free(function);
11031708Sstevel 
11041708Sstevel 		return (DDI_FAILURE);
11051708Sstevel 	}
11061708Sstevel 
11071708Sstevel 	ddi_prop_free(function);
11081708Sstevel 
11091708Sstevel 	retval = ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
11107656SSherry.Moore@Sun.COM 	    "name", &unitp->props.name);
11111708Sstevel 	if (retval != DDI_PROP_SUCCESS) {
11121708Sstevel 
11131708Sstevel 		return (retval);
11141708Sstevel 	}
11151708Sstevel #ifdef DEBUG
11161708Sstevel 	else if (pcf8591_debug & 0x02)
11171708Sstevel 		cmn_err(CE_NOTE,
11187656SSherry.Moore@Sun.COM 		    "pcf8591_read_props:ddi_prop_lookup_string(%s): \
11191708Sstevel 			found  %s ", "name", unitp->props.name);
11201708Sstevel #endif /* DEBUG */
11211708Sstevel 
11221708Sstevel 	retval = ddi_getlongprop(DDI_DEV_T_ANY, dip,
11237656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP,
11247656SSherry.Moore@Sun.COM 	    "reg", (caddr_t)&prop_value, &prop_len);
11251708Sstevel 	if (retval == DDI_PROP_SUCCESS) {
11261708Sstevel 		unitp->props.i2c_bus		= (uint16_t)prop_value[0];
11271708Sstevel 		unitp->props.slave_address	= (uint16_t)prop_value[1];
11281708Sstevel 		kmem_free(prop_value, prop_len);
11291708Sstevel #ifdef DEBUG
11301708Sstevel 		if (pcf8591_debug & 0x02)
11311708Sstevel 			cmn_err(CE_NOTE,
11327656SSherry.Moore@Sun.COM 			    "pcf8591:ddi_getlongprop(%s) returns %d,"
11337656SSherry.Moore@Sun.COM 			    " i2c_bus,slave=0x%x,0x%x",
11347656SSherry.Moore@Sun.COM 			    "reg", retval,  unitp->props.i2c_bus,
11357656SSherry.Moore@Sun.COM 			    unitp->props.slave_address);
11361708Sstevel #endif /* DEBUG */
11371708Sstevel 	} else {
11381708Sstevel 		unitp->props.i2c_bus		= (uint16_t)-1;
11391708Sstevel 		unitp->props.slave_address	= (uint16_t)-1;
11401708Sstevel #ifdef DEBUG
11411708Sstevel 		cmn_err(CE_WARN,
11427656SSherry.Moore@Sun.COM 		    "pcf8591_read_props:ddi_getlongprop(%s) returns %d,"
11437656SSherry.Moore@Sun.COM 		    " default it to 0x%x:0x%X",
11447656SSherry.Moore@Sun.COM 		    "reg", retval,  unitp->props.i2c_bus,
11457656SSherry.Moore@Sun.COM 		    unitp->props.slave_address);
11461708Sstevel #endif /* DEBUG */
11471708Sstevel 	}
1148*11311SSurya.Prakki@Sun.COM 	(void) ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
11497656SSherry.Moore@Sun.COM 	    "channels-in-use", &prop_len);
11501708Sstevel 	retval = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY,
11517656SSherry.Moore@Sun.COM 	    dip, DDI_PROP_DONTPASS,
11527656SSherry.Moore@Sun.COM 	    "channels-in-use",
11537656SSherry.Moore@Sun.COM 	    (uchar_t **)&unitp->props.channels_in_use,
11547656SSherry.Moore@Sun.COM 	    &unitp->props.num_chans_used);
11551708Sstevel 	if (retval == DDI_PROP_SUCCESS) {
11561708Sstevel 		unitp->props.num_chans_used /= sizeof (pcf8591_channel_t);
11571708Sstevel 	} else {
11581708Sstevel 		unitp->props.num_chans_used = 0;
11591708Sstevel 	}
11601708Sstevel 
11611708Sstevel #ifdef DEBUG
11621708Sstevel 	if (pcf8591_debug & 0x0002)
11631708Sstevel 		cmn_err(CE_NOTE,
11647656SSherry.Moore@Sun.COM 		    "pcf8591_read_props:ddi_prop_lookup_byte_array(%s)"
11657656SSherry.Moore@Sun.COM 		    "returns %d\n"
11667656SSherry.Moore@Sun.COM 		    "\t\tlength=%d, #elements=%d",
11677656SSherry.Moore@Sun.COM 		    "channels-in-use", retval,
11687656SSherry.Moore@Sun.COM 		    prop_len, unitp->props.num_chans_used);
11691708Sstevel #endif /* DEBUG */
11701708Sstevel 
11711708Sstevel 	retval = ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip,
11727656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, "channels-description",
11737656SSherry.Moore@Sun.COM 	    (char ***)&unitp->props.channels_description,
11747656SSherry.Moore@Sun.COM 	    (uint_t *)&prop_len);
11751708Sstevel 
11761708Sstevel 	if (retval != DDI_PROP_SUCCESS) {
11771708Sstevel 		prop_len = 0;
11781708Sstevel 		unitp->props.channels_description = NULL;
11791708Sstevel 	}
11801708Sstevel 
11811708Sstevel #ifdef DEBUG
11821708Sstevel 	if (pcf8591_debug & 0x0002) {
11831708Sstevel 		cmn_err(CE_NOTE,
11847656SSherry.Moore@Sun.COM 		    "pcf8591_read_props:ddi_prop_lookup_string_array(%s)"
11857656SSherry.Moore@Sun.COM 		    "returns %d, length=%d",
11867656SSherry.Moore@Sun.COM 		    "channels-description", retval, prop_len);
11871708Sstevel 		for (i = 0; i < prop_len; ++i) {
11881708Sstevel 			cmn_err(CE_NOTE, "channels-description[%d]=<%s>",
11897656SSherry.Moore@Sun.COM 			    i, unitp->props.channels_description[i]);
11901708Sstevel 		}
11911708Sstevel 	}
11921708Sstevel #endif /* DEBUG */
11931708Sstevel 
11941708Sstevel 	/*
11951708Sstevel 	 * The following code was borrowed from envctrltwo.c
11961708Sstevel 	 * I haven't yet investigated why the copy target is index + 2
11971708Sstevel 	 */
11981708Sstevel 	retval = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
11997656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, "tables", &creg_prop, (uint_t *)&prop_len);
12001708Sstevel 
12011708Sstevel 	if (retval != DDI_PROP_SUCCESS) {
12021708Sstevel #ifdef DEBUG
12031708Sstevel 		cmn_err(CE_WARN, "%s%d: Unable to read pcf8591 tables property",
12047656SSherry.Moore@Sun.COM 		    ddi_get_name(dip), instance);
12051708Sstevel #endif /* DEBUG */
12061708Sstevel 
12071708Sstevel 		return (DDI_NOT_WELL_FORMED);
12081708Sstevel 	}
12091708Sstevel 
12101708Sstevel 	tblsz = (sizeof (_cpu_temps) / sizeof (uchar_t));
12111708Sstevel 	if (prop_len <= tblsz) {
12121708Sstevel 		for (i = 0; i < prop_len; i++) {
12131708Sstevel 			_cpu_temps[i] = creg_prop[i];
12141708Sstevel 		}
12151708Sstevel 	}
12161708Sstevel #ifdef DEBUG
12171708Sstevel 	if (pcf8591_debug & 0x0002)
12181708Sstevel 		cmn_err(CE_NOTE, "pcf8591_read_props: _cpu_temps size=%d; "
12197656SSherry.Moore@Sun.COM 		    "tables prop_len=%d\n", tblsz, prop_len);
12201708Sstevel #endif /* DEBUG */
12211708Sstevel 
12221708Sstevel 	ddi_prop_free(creg_prop);
12231708Sstevel 
12241708Sstevel 	/*
12251708Sstevel 	 * Read shutdown temp and warning temp properties.
12261708Sstevel 	 */
12271708Sstevel 	warning_temp = (int)ddi_getprop(DDI_DEV_T_ANY, dip,
12287656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, "warning-temp", PCF8591_WARNING_TEMP);
12291708Sstevel 
12301708Sstevel 	shutdown_temp = (int)ddi_getprop(DDI_DEV_T_ANY, dip,
12317656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, "shutdown-temp", PCF8591_SHUTDOWN_TEMP);
12321708Sstevel 
12331708Sstevel 	/*
12341708Sstevel 	 * Fill up the warning and shutdown temp values in kstat structure.
12351708Sstevel 	 */
12361708Sstevel 	unitp->temp_kstats.warning_threshold = warning_temp;
12371708Sstevel 	unitp->temp_kstats.shutdown_threshold = shutdown_temp;
12381708Sstevel 
12391708Sstevel 	return (DDI_PROP_SUCCESS);
12401708Sstevel }
1241