xref: /onnv-gate/usr/src/uts/sun4u/montecarlo/io/pcf8574_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  */
21*11311SSurya.Prakki@Sun.COM 
221708Sstevel /*
23*11311SSurya.Prakki@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241708Sstevel  * Use is subject to license terms.
251708Sstevel  */
261708Sstevel 
271708Sstevel #include <sys/param.h>
281708Sstevel #include <sys/types.h>
291708Sstevel #include <sys/signal.h>
301708Sstevel #include <sys/errno.h>
311708Sstevel #include <sys/file.h>
321708Sstevel #include <sys/termio.h>
331708Sstevel #include <sys/termios.h>
341708Sstevel #include <sys/cmn_err.h>
351708Sstevel #include <sys/stream.h>
361708Sstevel #include <sys/strsun.h>
371708Sstevel #include <sys/stropts.h>
381708Sstevel #include <sys/strtty.h>
391708Sstevel #include <sys/debug.h>
401708Sstevel #include <sys/eucioctl.h>
411708Sstevel #include <sys/cred.h>
421708Sstevel #include <sys/uio.h>
431708Sstevel #include <sys/stat.h>
441708Sstevel #include <sys/kmem.h>
451708Sstevel 
461708Sstevel #include <sys/ddi.h>
471708Sstevel #include <sys/sunddi.h>
481708Sstevel #include <sys/obpdefs.h>
491708Sstevel #include <sys/conf.h>		/* req. by dev_ops flags MTSAFE etc. */
501708Sstevel #include <sys/modctl.h>		/* for modldrv */
511708Sstevel #include <sys/stat.h>		/* ddi_create_minor_node S_IFCHR */
521708Sstevel #include <sys/open.h>		/* for open params.	 */
531708Sstevel #include <sys/uio.h>		/* for read/write */
541708Sstevel 
551708Sstevel #include <sys/i2c/misc/i2c_svc.h>
561708Sstevel #include <sys/mct_topology.h>
571708Sstevel #include <sys/envctrl_gen.h>	/* must be before netract_gen.h	*/
581708Sstevel #include <sys/netract_gen.h>
591708Sstevel #include <sys/pcf8574_nct.h>
601708Sstevel #include <sys/scsb_cbi.h>
611708Sstevel 
621708Sstevel #ifdef DEBUG
631708Sstevel #define	dbg_print(level, str) cmn_err(level, str);
641708Sstevel 	static int	pcf8574_debug = 0x00000102;
651708Sstevel #else
661708Sstevel #define	dbg_print(level, str) {; }
671708Sstevel #endif
681708Sstevel 
691708Sstevel #define	CV_LOCK(retval)				\
701708Sstevel {									\
711708Sstevel 	mutex_enter(&unitp->umutex);	\
721708Sstevel 	while (unitp->pcf8574_flags == PCF8574_BUSY) {	\
731708Sstevel 		if (cv_wait_sig(&unitp->pcf8574_cv,	\
741708Sstevel 					&unitp->umutex) <= 0) {	\
751708Sstevel 			mutex_exit(&unitp->umutex);		\
761708Sstevel 			return (retval);		\
771708Sstevel 		}							\
781708Sstevel 	}								\
791708Sstevel 	unitp->pcf8574_flags = PCF8574_BUSY;	\
801708Sstevel 	mutex_exit(&unitp->umutex);		\
811708Sstevel }
821708Sstevel 
831708Sstevel #define	CV_UNLOCK					\
841708Sstevel {									\
851708Sstevel 	mutex_enter(&unitp->umutex);	\
861708Sstevel 	unitp->pcf8574_flags = 0;		\
871708Sstevel 	cv_signal(&unitp->pcf8574_cv);	\
881708Sstevel 	mutex_exit(&unitp->umutex);		\
891708Sstevel }
901708Sstevel 
911708Sstevel static int nct_p10fan_patch = 0;	/* Fan patch for P1.0 */
921708Sstevel static void	*pcf8574_soft_statep;
931708Sstevel 
941708Sstevel /*
951708Sstevel  * cb ops (only need open,close,read,write,ioctl)
961708Sstevel  */
971708Sstevel static int	pcf8574_open(dev_t *, int, int, cred_t *);
981708Sstevel static int	pcf8574_close(dev_t, int, int, cred_t *);
991708Sstevel static int	pcf8574_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1001708Sstevel static int	pcf8574_read(dev_t dev, struct uio *uiop, cred_t *cred_p);
1011708Sstevel static int	pcf8574_chpoll(dev_t, short, int, short *, struct pollhead **);
1021708Sstevel static uint_t	pcf8574_intr(caddr_t arg);
1031708Sstevel static int pcf8574_io(dev_t, struct uio *, int);
1041708Sstevel 
1051708Sstevel static struct cb_ops pcf8574_cbops = {
1061708Sstevel 	pcf8574_open,		/* open */
1071708Sstevel 	pcf8574_close,		/* close */
1081708Sstevel 	nodev,				/* strategy */
1091708Sstevel 	nodev,				/* print */
1101708Sstevel 	nodev,				/* dump */
1111708Sstevel 	pcf8574_read,		/* read */
1121708Sstevel 	nodev,				/* write */
1131708Sstevel 	pcf8574_ioctl,		/* ioctl */
1141708Sstevel 	nodev,				/* devmap */
1151708Sstevel 	nodev,				/* mmap */
1161708Sstevel 	nodev,				/* segmap */
1171708Sstevel 	pcf8574_chpoll,		/* poll */
1181708Sstevel 	ddi_prop_op,		/* cb_prop_op */
1191708Sstevel 	NULL,				/* streamtab */
1201708Sstevel 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
1211708Sstevel 	CB_REV,				/* rev */
1221708Sstevel 	nodev,				/* int (*cb_aread)() */
1231708Sstevel 	nodev				/* int (*cb_awrite)() */
1241708Sstevel };
1251708Sstevel 
1261708Sstevel /*
1271708Sstevel  * dev ops
1281708Sstevel  */
1291708Sstevel static int pcf8574_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
1301708Sstevel static int pcf8574_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
1311708Sstevel 
1321708Sstevel /* kstat routines */
1331708Sstevel static int pcf8574_add_kstat(struct pcf8574_unit *, scsb_fru_status_t);
1341708Sstevel static void pcf8574_delete_kstat(struct pcf8574_unit *);
1351708Sstevel static int pcf8574_kstat_update(kstat_t *, int);
1361708Sstevel static int pcf8574_read_chip(struct pcf8574_unit *unitp,
1371708Sstevel 	uint16_t size);
1381708Sstevel static int pcf8574_write_chip(struct pcf8574_unit *unitp,
1391708Sstevel 	uint16_t size, uint8_t bitpattern);
1401708Sstevel static int pcf8574_read_props(struct pcf8574_unit *unitp);
1411708Sstevel static int pcf8574_init_chip(struct pcf8574_unit *unitp, int);
1421708Sstevel /*
1431708Sstevel  * SCSB callback function
1441708Sstevel  */
1451708Sstevel static void pcf8574_callback(void *, scsb_fru_event_t, scsb_fru_status_t);
1461708Sstevel extern int scsb_intr_register(uint_t (*intr_handler)(caddr_t), caddr_t,
1471708Sstevel 		fru_id_t);
1481708Sstevel extern int scsb_intr_unregister(fru_id_t);
1491708Sstevel 
1501708Sstevel extern int nct_i2c_transfer(i2c_client_hdl_t i2c_hdl, i2c_transfer_t *i2c_tran);
1511708Sstevel 
1521708Sstevel static struct dev_ops pcf8574_ops = {
1531708Sstevel 	DEVO_REV,
1541708Sstevel 	0,
1551708Sstevel 	ddi_getinfo_1to1,
1561708Sstevel 	nulldev,
1571708Sstevel 	nulldev,
1581708Sstevel 	pcf8574_attach,
1591708Sstevel 	pcf8574_detach,
1601708Sstevel 	nodev,
1611708Sstevel 	&pcf8574_cbops,
1627656SSherry.Moore@Sun.COM 	NULL,				/* bus_ops */
1637656SSherry.Moore@Sun.COM 	NULL,				/* power */
1647656SSherry.Moore@Sun.COM 	ddi_quiesce_not_supported,	/* devo_quiesce */
1651708Sstevel };
1661708Sstevel 
1671708Sstevel extern struct mod_ops mod_driverops;
1681708Sstevel 
1691708Sstevel static struct modldrv pcf8574_modldrv = {
1701708Sstevel 	&mod_driverops,		/* type of module - driver */
1717656SSherry.Moore@Sun.COM 	"Netract pcf8574 (gpio)",
1721708Sstevel 	&pcf8574_ops,
1731708Sstevel };
1741708Sstevel 
1751708Sstevel static struct modlinkage pcf8574_modlinkage = {
1761708Sstevel 	MODREV_1,
1771708Sstevel 	&pcf8574_modldrv,
1781708Sstevel 	0
1791708Sstevel };
1801708Sstevel 
1811708Sstevel /* char _depends_on[] = "misc/i2c_svc drv/scsb"; */
1821708Sstevel 
1831708Sstevel int
_init(void)1841708Sstevel _init(void)
1851708Sstevel {
1861708Sstevel 	register int    error;
1871708Sstevel 
1881708Sstevel 	error = mod_install(&pcf8574_modlinkage);
1891708Sstevel 	if (!error) {
1901708Sstevel 		(void) ddi_soft_state_init(&pcf8574_soft_statep,
1917656SSherry.Moore@Sun.COM 		    sizeof (struct pcf8574_unit), PCF8574_MAX_DEVS);
1921708Sstevel 	}
1931708Sstevel 
1941708Sstevel 	return (error);
1951708Sstevel }
1961708Sstevel 
1971708Sstevel int
_fini(void)1981708Sstevel _fini(void)
1991708Sstevel {
2001708Sstevel 	register int    error;
2011708Sstevel 
2021708Sstevel 	error = mod_remove(&pcf8574_modlinkage);
2031708Sstevel 	if (!error)
2041708Sstevel 		ddi_soft_state_fini(&pcf8574_soft_statep);
2051708Sstevel 
2061708Sstevel 	return (error);
2071708Sstevel }
2081708Sstevel 
2091708Sstevel int
_info(struct modinfo * modinfop)2101708Sstevel _info(struct modinfo *modinfop)
2111708Sstevel {
2121708Sstevel 	return (mod_info(&pcf8574_modlinkage, modinfop));
2131708Sstevel }
2141708Sstevel 
2151708Sstevel /*ARGSUSED*/
2161708Sstevel static int
pcf8574_open(dev_t * devp,int flags,int otyp,cred_t * credp)2171708Sstevel pcf8574_open(dev_t *devp, int flags, int otyp, cred_t *credp)
2181708Sstevel {
2191708Sstevel 	struct pcf8574_unit *unitp;
2201708Sstevel 	register int    instance;
2211708Sstevel 	int err = DDI_SUCCESS;
2221708Sstevel 
2231708Sstevel 	instance = getminor(*devp);
2241708Sstevel 	if (instance < 0) {
2251708Sstevel 		return (ENXIO);
2261708Sstevel 	}
2271708Sstevel 
2281708Sstevel 	unitp = (struct pcf8574_unit *)
2297656SSherry.Moore@Sun.COM 	    ddi_get_soft_state(pcf8574_soft_statep, instance);
2301708Sstevel 
2311708Sstevel 	if (unitp == NULL) {
2321708Sstevel 		return (ENXIO);
2331708Sstevel 	}
2341708Sstevel 
2351708Sstevel 	if (otyp != OTYP_CHR) {
2361708Sstevel 		return (EINVAL);
2371708Sstevel 	}
2381708Sstevel 
2391708Sstevel 	mutex_enter(&unitp->umutex);
2401708Sstevel 
2411708Sstevel 	if (flags & FEXCL) {
2421708Sstevel 		if (unitp->pcf8574_oflag != 0) {
2431708Sstevel 			err = EBUSY;
2441708Sstevel 		} else {
2451708Sstevel 			unitp->pcf8574_oflag = FEXCL;
2461708Sstevel 		}
2471708Sstevel 	} else {
2481708Sstevel 		if (unitp->pcf8574_oflag == FEXCL) {
2491708Sstevel 			err = EBUSY;
2501708Sstevel 		} else {
2511708Sstevel 			unitp->pcf8574_oflag = FOPEN;
2521708Sstevel 		}
2531708Sstevel 	}
2541708Sstevel 
2551708Sstevel 	mutex_exit(&unitp->umutex);
2561708Sstevel 
2571708Sstevel 	return (err);
2581708Sstevel }
2591708Sstevel 
2601708Sstevel /*ARGSUSED*/
2611708Sstevel static int
pcf8574_close(dev_t dev,int flags,int otyp,cred_t * credp)2621708Sstevel pcf8574_close(dev_t dev, int flags, int otyp, cred_t *credp)
2631708Sstevel {
2641708Sstevel 	struct pcf8574_unit *unitp;
2651708Sstevel 	register int    instance;
2661708Sstevel 
2671708Sstevel #ifdef lint
2681708Sstevel 	flags = flags;
2691708Sstevel 	otyp = otyp;
2701708Sstevel #endif
2711708Sstevel 
2721708Sstevel 	instance = getminor(dev);
2731708Sstevel 
2741708Sstevel 	if (instance < 0) {
2751708Sstevel 		return (ENXIO);
2761708Sstevel 	}
2771708Sstevel 
2781708Sstevel 	unitp = (struct pcf8574_unit *)
2797656SSherry.Moore@Sun.COM 	    ddi_get_soft_state(pcf8574_soft_statep, instance);
2801708Sstevel 
2811708Sstevel 	if (unitp == NULL) {
2821708Sstevel 		return (ENXIO);
2831708Sstevel 	}
2841708Sstevel 
2851708Sstevel 	mutex_enter(&unitp->umutex);
2861708Sstevel 
2871708Sstevel 	unitp->pcf8574_oflag = 0;
2881708Sstevel 
2891708Sstevel 	mutex_exit(&unitp->umutex);
2901708Sstevel 
2911708Sstevel 	return (DDI_SUCCESS);
2921708Sstevel }
2931708Sstevel 
2941708Sstevel 
2951708Sstevel /*ARGSUSED*/
2961708Sstevel static int
pcf8574_read(dev_t dev,struct uio * uiop,cred_t * cred_p)2971708Sstevel pcf8574_read(dev_t dev, struct uio *uiop, cred_t *cred_p)
2981708Sstevel {
2991708Sstevel 	return (pcf8574_io(dev, uiop, B_READ));
3001708Sstevel }
3011708Sstevel 
3021708Sstevel static int
pcf8574_io(dev_t dev,struct uio * uiop,int rw)3031708Sstevel pcf8574_io(dev_t dev, struct uio *uiop, int rw)
3041708Sstevel {
3051708Sstevel 	struct pcf8574_unit *unitp;
3061708Sstevel 	register int    instance;
3071708Sstevel 	uint16_t	bytes_to_rw;
3081708Sstevel 	int	err = DDI_SUCCESS;
3091708Sstevel 
3101708Sstevel 	err = 0;
3111708Sstevel 	instance = getminor(dev);
3121708Sstevel 
3131708Sstevel 	if (instance < 0) {
3141708Sstevel 		return (ENXIO);
3151708Sstevel 	}
3161708Sstevel 
3171708Sstevel 	unitp = (struct pcf8574_unit *)
3187656SSherry.Moore@Sun.COM 	    ddi_get_soft_state(pcf8574_soft_statep, instance);
3191708Sstevel 	if (unitp == NULL) {
3201708Sstevel 		return (ENXIO);
3211708Sstevel 	}
3221708Sstevel 	if ((bytes_to_rw = uiop->uio_resid) > PCF8574_TRAN_SIZE) {
3231708Sstevel 		return (EINVAL);
3241708Sstevel 	}
3251708Sstevel 
3261708Sstevel 	CV_LOCK(EINTR)
3271708Sstevel 
3281708Sstevel 	if (rw == B_WRITE) {
3291708Sstevel 		err = uiomove(unitp->i2c_tran->i2c_wbuf,
3307656SSherry.Moore@Sun.COM 		    bytes_to_rw, UIO_WRITE, uiop);
3311708Sstevel 
3321708Sstevel 		if (!err) {
3331708Sstevel 			err = pcf8574_write_chip(unitp, bytes_to_rw,
3347656SSherry.Moore@Sun.COM 			    unitp->writemask);
3351708Sstevel 		}
3361708Sstevel 
3371708Sstevel 	} else {
3381708Sstevel 			err = pcf8574_read_chip(unitp, bytes_to_rw);
3391708Sstevel 			if (!err) {
3401708Sstevel 				err = uiomove(unitp->i2c_tran->i2c_rbuf,
3417656SSherry.Moore@Sun.COM 				    bytes_to_rw, UIO_READ, uiop);
3421708Sstevel 			}
3431708Sstevel 	}
3441708Sstevel 
3451708Sstevel 	CV_UNLOCK
3461708Sstevel 	if (err)
3471708Sstevel 		err = EIO;
3481708Sstevel 
3491708Sstevel 	return (err);
3501708Sstevel }
3511708Sstevel 
3521708Sstevel static int
pcf8574_do_resume(dev_info_t * dip)3531708Sstevel pcf8574_do_resume(dev_info_t *dip)
3541708Sstevel {
3551708Sstevel 	int instance = ddi_get_instance(dip);
3561708Sstevel 	struct pcf8574_unit *unitp =
3577656SSherry.Moore@Sun.COM 	    ddi_get_soft_state(pcf8574_soft_statep, instance);
3581708Sstevel 
3591708Sstevel 	if (unitp == NULL) {
3601708Sstevel 		return (ENXIO);
3611708Sstevel 	}
3621708Sstevel 
3631708Sstevel 	CV_UNLOCK
3641708Sstevel 
3651708Sstevel 	return (DDI_SUCCESS);
3661708Sstevel }
3671708Sstevel 
3681708Sstevel static int
pcf8574_do_detach(dev_info_t * dip)3691708Sstevel pcf8574_do_detach(dev_info_t *dip)
3701708Sstevel {
3711708Sstevel 	struct pcf8574_unit *unitp;
3721708Sstevel 	int instance;
3731708Sstevel 	uint_t attach_flag;
3741708Sstevel 
3751708Sstevel 	instance = ddi_get_instance(dip);
3761708Sstevel 	unitp = ddi_get_soft_state(pcf8574_soft_statep, instance);
3771708Sstevel 
3781708Sstevel 	attach_flag = unitp->attach_flag;
3791708Sstevel 
3801708Sstevel 	if (attach_flag & PCF8574_INTR_ADDED) {
381*11311SSurya.Prakki@Sun.COM 		(void) scsb_intr_unregister(
382*11311SSurya.Prakki@Sun.COM 		    (fru_id_t)unitp->props.slave_address);
3831708Sstevel 	}
3841708Sstevel 
3851708Sstevel 	if (attach_flag & PCF8574_KSTAT_INIT) {
3861708Sstevel 		pcf8574_delete_kstat(unitp);
3871708Sstevel 	}
3881708Sstevel 
3891708Sstevel 	if (attach_flag & PCF8574_LOCK_INIT) {
3901708Sstevel 		mutex_destroy(&unitp->umutex);
3911708Sstevel 		cv_destroy(&unitp->pcf8574_cv);
3921708Sstevel 	}
3931708Sstevel 
3941708Sstevel 	scsb_fru_unregister((void *)unitp,
3957656SSherry.Moore@Sun.COM 	    (fru_id_t)unitp->props.slave_address);
3961708Sstevel 
3971708Sstevel 	if (attach_flag & PCF8574_ALLOC_TRANSFER) {
3981708Sstevel 		/*
3991708Sstevel 		 * restore the lengths to allocated lengths
4001708Sstevel 		 * before freeing.
4011708Sstevel 		 */
4021708Sstevel 		unitp->i2c_tran->i2c_wlen = MAX_WLEN;
4031708Sstevel 		unitp->i2c_tran->i2c_rlen = MAX_RLEN;
4041708Sstevel 		i2c_transfer_free(unitp->pcf8574_hdl, unitp->i2c_tran);
4051708Sstevel 	}
4061708Sstevel 
4071708Sstevel 	if (attach_flag & PCF8574_REGISTER_CLIENT) {
4081708Sstevel 		i2c_client_unregister(unitp->pcf8574_hdl);
4091708Sstevel 	}
4101708Sstevel 
4111708Sstevel 	if (attach_flag & PCF8574_MINORS_CREATED) {
4121708Sstevel 		ddi_remove_minor_node(dip, NULL);
4131708Sstevel 	}
4141708Sstevel 
4151708Sstevel 	if (attach_flag & PCF8574_PROPS_READ) {
4161708Sstevel 		if (unitp->pcf8574_type == PCF8574_ADR_CPUVOLTAGE &&
4177656SSherry.Moore@Sun.COM 		    unitp->props.num_chans_used != 0) {
4181708Sstevel 			ddi_prop_free(unitp->props.channels_in_use);
4191708Sstevel 		} else {
420*11311SSurya.Prakki@Sun.COM 			(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
4217656SSherry.Moore@Sun.COM 			    "interrupt-priorities");
4221708Sstevel 		}
4231708Sstevel 	}
4241708Sstevel 
4251708Sstevel 	if (attach_flag & PCF8574_SOFT_STATE_ALLOC) {
4261708Sstevel 		ddi_soft_state_free(pcf8574_soft_statep, instance);
4271708Sstevel 	}
4281708Sstevel 
4291708Sstevel 	return (DDI_SUCCESS);
4301708Sstevel }
4311708Sstevel 
4321708Sstevel /*
4331708Sstevel  * NOTE****
4341708Sstevel  * The OBP will create device tree node for all I2C devices which
4351708Sstevel  * may be present in a system. This means, even if the device is
4361708Sstevel  * not physically present, the device tree node exists. We also
4371708Sstevel  * will succeed the attach routine, since currently there is no
4381708Sstevel  * hotplug support in the I2C bus, and the FRUs need to be hot
4391708Sstevel  * swappable. Only during an I2C transaction we figure out whether
4401708Sstevel  * the particular I2C device is actually present in the system
4411708Sstevel  * by looking at the system controller board register. The fantray
4421708Sstevel  * and power-supply devices may be swapped any time after system
4431708Sstevel  * reboot, and the way we can make sure that the device is attached
4441708Sstevel  * to the driver, is by always keeping the driver loaded, and report
4451708Sstevel  * an error during the actual transaction.
4461708Sstevel  */
4471708Sstevel static int
pcf8574_do_attach(dev_info_t * dip)4481708Sstevel pcf8574_do_attach(dev_info_t *dip)
4491708Sstevel {
4501708Sstevel 	register struct pcf8574_unit *unitp;
4511708Sstevel 	int instance;
4521708Sstevel 	char name[MAXNAMELEN];
4531708Sstevel 	int		i;
4541708Sstevel 	pcf8574_channel_t *chp;
4551708Sstevel 	scsb_fru_status_t	dev_presence;
4561708Sstevel 
4571708Sstevel 	instance = ddi_get_instance(dip);
4581708Sstevel #ifdef DEBUG
4591708Sstevel 	if (pcf8574_debug & 0x04)
4601708Sstevel 		cmn_err(CE_NOTE, "pcf8574_attach: instance=%d\n",
4617656SSherry.Moore@Sun.COM 		    instance);
4621708Sstevel #endif /* DEBUG */
4631708Sstevel 
4641708Sstevel 	if (ddi_soft_state_zalloc(pcf8574_soft_statep, instance) !=
4657656SSherry.Moore@Sun.COM 	    DDI_SUCCESS) {
4661708Sstevel 		return (DDI_FAILURE);
4671708Sstevel 	}
4681708Sstevel 	unitp = ddi_get_soft_state(pcf8574_soft_statep, instance);
4691708Sstevel 
4701708Sstevel 	if (unitp == NULL) {
4711708Sstevel 		ddi_soft_state_free(pcf8574_soft_statep, instance);
4721708Sstevel 		return (DDI_FAILURE);
4731708Sstevel 	}
4741708Sstevel 
4751708Sstevel 	unitp->dip = dip;
4761708Sstevel 
4771708Sstevel 	unitp->attach_flag = PCF8574_SOFT_STATE_ALLOC;
4781708Sstevel 
4791708Sstevel 	if (pcf8574_read_props(unitp) != DDI_PROP_SUCCESS) {
4801708Sstevel 		ddi_soft_state_free(pcf8574_soft_statep, instance);
4811708Sstevel 		return (DDI_FAILURE);
4821708Sstevel 	}
4831708Sstevel 
4841708Sstevel 	unitp->attach_flag |= PCF8574_PROPS_READ;
4851708Sstevel 
4861708Sstevel 	/*
4871708Sstevel 	 * Set the current operating mode to NORMAL_MODE.
4881708Sstevel 	 */
4891708Sstevel 	unitp->current_mode = ENVCTRL_NORMAL_MODE;
4901708Sstevel 
491*11311SSurya.Prakki@Sun.COM 	(void) snprintf(unitp->pcf8574_name, PCF8574_NAMELEN,
4927656SSherry.Moore@Sun.COM 	    "%s%d", ddi_driver_name(dip), instance);
4931708Sstevel 
4941708Sstevel 	if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP) {
4951708Sstevel 		(void) sprintf(name, "pwrsuppply");
4961708Sstevel 		if (ddi_create_minor_node(dip, name, S_IFCHR, instance,
4977656SSherry.Moore@Sun.COM 		    PCF8574_NODE_TYPE, NULL) == DDI_FAILURE) {
4981708Sstevel 			ddi_remove_minor_node(dip, NULL);
499*11311SSurya.Prakki@Sun.COM 			(void) pcf8574_do_detach(dip);
5001708Sstevel 
5011708Sstevel 			return (DDI_FAILURE);
5021708Sstevel 		}
5031708Sstevel 	}
5041708Sstevel 	else
5051708Sstevel 	if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY) {
5061708Sstevel 		(void) sprintf(name, "fantray");
5071708Sstevel 		if (ddi_create_minor_node(dip, name, S_IFCHR, instance,
5087656SSherry.Moore@Sun.COM 		    PCF8574_NODE_TYPE, NULL) == DDI_FAILURE) {
5091708Sstevel 			ddi_remove_minor_node(dip, NULL);
510*11311SSurya.Prakki@Sun.COM 			(void) pcf8574_do_detach(dip);
5111708Sstevel 
5121708Sstevel 			return (DDI_FAILURE);
5131708Sstevel 		}
5141708Sstevel 	}
5151708Sstevel 	else
5161708Sstevel 	if (unitp->pcf8574_type == PCF8574_TYPE_CPUVOLTAGE) {
5171708Sstevel 		(void) sprintf(name, "cpuvoltage");
5181708Sstevel 		if (ddi_create_minor_node(dip, name, S_IFCHR, instance,
5197656SSherry.Moore@Sun.COM 		    PCF8574_NODE_TYPE, NULL) == DDI_FAILURE) {
5201708Sstevel 			ddi_remove_minor_node(dip, NULL);
521*11311SSurya.Prakki@Sun.COM 			(void) pcf8574_do_detach(dip);
5221708Sstevel 
5231708Sstevel 			return (DDI_FAILURE);
5241708Sstevel 		}
5251708Sstevel 	} else {
5261708Sstevel 		return (DDI_FAILURE);
5271708Sstevel 	}
5281708Sstevel 
5291708Sstevel 	unitp->attach_flag |= PCF8574_MINORS_CREATED;
5301708Sstevel 
5311708Sstevel 	/*
5321708Sstevel 	 * Now we need read/write masks since all the 8574 bits can be either
5331708Sstevel 	 * read/written, but some ports are intended to be RD/WR only, or RW
5341708Sstevel 	 * If no channels-in-use propoerty, set default values.
5351708Sstevel 	 */
5361708Sstevel 	if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY) {
5371708Sstevel 		unitp->readmask = PCF8574_FAN_READMASK;
5381708Sstevel 		unitp->writemask = PCF8574_FAN_WRITEMASK;
5391708Sstevel 	}
5401708Sstevel 	if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP) {
5411708Sstevel 		unitp->readmask = PCF8574_PS_READMASK;
5421708Sstevel 		unitp->writemask = PCF8574_PS_WRITEMASK;
5431708Sstevel 	}
5441708Sstevel 
5451708Sstevel 	for (i = unitp->props.num_chans_used,
5467656SSherry.Moore@Sun.COM 	    chp = unitp->props.channels_in_use; i; --i, ++chp) {
5471708Sstevel 		unitp->readmask |= (uint8_t)(
5487656SSherry.Moore@Sun.COM 		    (chp->io_dir == I2C_PROP_IODIR_IN ||
5497656SSherry.Moore@Sun.COM 		    chp->io_dir == I2C_PROP_IODIR_INOUT) << chp->port);
5501708Sstevel 		unitp->writemask |= (uint8_t)(
5517656SSherry.Moore@Sun.COM 		    (chp->io_dir == I2C_PROP_IODIR_OUT ||
5527656SSherry.Moore@Sun.COM 		    chp->io_dir == I2C_PROP_IODIR_INOUT) << chp->port);
5531708Sstevel 	}
5541708Sstevel 
5551708Sstevel #ifdef DEBUG
5561708Sstevel 	cmn_err(CE_NOTE, "pcf8574_do_attach: readmask = 0x%x \
5571708Sstevel 		writemask = 0x%x\n", unitp->readmask, unitp->writemask);
5581708Sstevel #endif /* DEBUG */
5591708Sstevel 
5601708Sstevel 	if (i2c_client_register(dip, &unitp->pcf8574_hdl)
5617656SSherry.Moore@Sun.COM 	    != I2C_SUCCESS) {
562*11311SSurya.Prakki@Sun.COM 		(void) pcf8574_do_detach(dip);
5631708Sstevel 		return (DDI_FAILURE);
5641708Sstevel 	}
5651708Sstevel 	unitp->attach_flag |= PCF8574_REGISTER_CLIENT;
5661708Sstevel 
5671708Sstevel 	/*
5681708Sstevel 	 * Allocate the I2C_transfer structure. The same structure
5691708Sstevel 	 * is used throughout the driver.
5701708Sstevel 	 */
5711708Sstevel 	if (i2c_transfer_alloc(unitp->pcf8574_hdl, &unitp->i2c_tran,
572*11311SSurya.Prakki@Sun.COM 	    MAX_WLEN, MAX_RLEN, KM_SLEEP) != I2C_SUCCESS) {
573*11311SSurya.Prakki@Sun.COM 		(void) pcf8574_do_detach(dip);
5741708Sstevel 		return (DDI_FAILURE);
5751708Sstevel 	}
5761708Sstevel 	unitp->attach_flag |= PCF8574_ALLOC_TRANSFER;
5771708Sstevel 
5781708Sstevel 	/*
5791708Sstevel 	 * To begin with we set the mode to I2C_RD.
5801708Sstevel 	 */
5811708Sstevel 	unitp->i2c_tran->i2c_flags = I2C_RD;
5821708Sstevel 	unitp->i2c_tran->i2c_version = I2C_XFER_REV;
5831708Sstevel 
5841708Sstevel 	/*
5851708Sstevel 	 * Set the busy flag and open flag to 0.
5861708Sstevel 	 */
5871708Sstevel 	unitp->pcf8574_flags = 0;
5881708Sstevel 	unitp->pcf8574_oflag = 0;
5891708Sstevel 
5901708Sstevel 	mutex_init(&unitp->umutex, NULL, MUTEX_DRIVER, NULL);
5911708Sstevel 	cv_init(&unitp->pcf8574_cv, NULL, CV_DRIVER, NULL);
5921708Sstevel 
5931708Sstevel 	unitp->attach_flag |= PCF8574_LOCK_INIT;
5941708Sstevel 
5951708Sstevel 	/*
5961708Sstevel 	 * Register out callback function with the SCSB driver, and save
5971708Sstevel 	 * the returned value to check that the device instance exists.
5981708Sstevel 	 */
5991708Sstevel 	dev_presence = scsb_fru_register(pcf8574_callback, (void *)unitp,
6007656SSherry.Moore@Sun.COM 	    (fru_id_t)unitp->props.slave_address);
6011708Sstevel 	if (dev_presence == FRU_NOT_AVAILABLE) {
6021708Sstevel 		scsb_fru_unregister((void *)unitp,
6037656SSherry.Moore@Sun.COM 		    (fru_id_t)unitp->props.slave_address);
6041708Sstevel 	}
6051708Sstevel 
6061708Sstevel 	/*
6071708Sstevel 	 * Add the kstats. First we need to get the property values
6081708Sstevel 	 * depending on the device type. For example, for the fan
6091708Sstevel 	 * tray there will be a different set of properties, and there
6101708Sstevel 	 * will be another for the powersupplies, and another one for
6111708Sstevel 	 * the CPU voltage monitor. Initialize the kstat structures with
6121708Sstevel 	 * these values.
6131708Sstevel 	 */
6141708Sstevel 
6151708Sstevel 	if (pcf8574_add_kstat(unitp, dev_presence) != DDI_SUCCESS) {
616*11311SSurya.Prakki@Sun.COM 		(void) pcf8574_do_detach(dip);
6171708Sstevel 		return (DDI_FAILURE);
6181708Sstevel 	}
6191708Sstevel 
6201708Sstevel 	unitp->attach_flag |= PCF8574_KSTAT_INIT;
6211708Sstevel 
6221708Sstevel 	/*
6231708Sstevel 	 * Due to observed behavior on Solaris 8, the handler must be
6241708Sstevel 	 * registered before any interrupts are enabled,
6251708Sstevel 	 * in spite of what the ddi_get_iblock_cookie() manual says.
6261708Sstevel 	 * As per the HW/SW spec, by default interrupts are disabled.
6271708Sstevel 	 */
6281708Sstevel 
6291708Sstevel 	if (dev_presence == FRU_PRESENT) { /* program the chip */
630*11311SSurya.Prakki@Sun.COM 		(void) pcf8574_init_chip(unitp, 0);   /* Disable intr first */
6311708Sstevel 	}
6321708Sstevel 
6331708Sstevel 	if (unitp->pcf8574_canintr == PCF8574_INTR_ON) {
6341708Sstevel #ifdef DEBUG
6351708Sstevel 		if (pcf8574_debug & 0x0004)
6361708Sstevel 			cmn_err(CE_NOTE, "registering pcf9574 interrupt "
6377656SSherry.Moore@Sun.COM 			    "handler");
6381708Sstevel #endif /* DEBUG */
6391708Sstevel 		if (scsb_intr_register(pcf8574_intr, (void *)unitp,
6407656SSherry.Moore@Sun.COM 		    (fru_id_t)unitp->props.slave_address) == DDI_SUCCESS) {
6411708Sstevel 			unitp->pcf8574_canintr |= PCF8574_INTR_ENABLED;
6421708Sstevel 			unitp->attach_flag |= PCF8574_INTR_ADDED;
6431708Sstevel 		} else {
644*11311SSurya.Prakki@Sun.COM 			(void) pcf8574_do_detach(dip);
6451708Sstevel 			return (DDI_FAILURE);
6461708Sstevel 		}
6471708Sstevel 	}
6481708Sstevel 
6491708Sstevel 	ddi_report_dev(dip);
6501708Sstevel 
6511708Sstevel 	return (DDI_SUCCESS);
6521708Sstevel }
6531708Sstevel 
6541708Sstevel static int
pcf8574_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)6551708Sstevel pcf8574_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
6561708Sstevel {
6571708Sstevel 	switch (cmd) {
6581708Sstevel 	case DDI_ATTACH:
6591708Sstevel 		return (pcf8574_do_attach(dip));
6601708Sstevel 	case DDI_RESUME:
6611708Sstevel 		return (pcf8574_do_resume(dip));
6621708Sstevel 	default:
6631708Sstevel 		return (DDI_FAILURE);
6641708Sstevel 	}
6651708Sstevel }
6661708Sstevel 
6671708Sstevel static int
pcf8574_do_suspend(dev_info_t * dip)6681708Sstevel pcf8574_do_suspend(dev_info_t *dip)
6691708Sstevel {
6701708Sstevel 	int instance = ddi_get_instance(dip);
6711708Sstevel 	struct pcf8574_unit *unitp =
6727656SSherry.Moore@Sun.COM 	    ddi_get_soft_state(pcf8574_soft_statep, instance);
6731708Sstevel 
6741708Sstevel 	if (unitp == NULL) {
6751708Sstevel 		return (ENXIO);
6761708Sstevel 	}
6771708Sstevel 
6781708Sstevel 	/*
6791708Sstevel 	 * Set the busy flag so that future transactions block
6801708Sstevel 	 * until resume.
6811708Sstevel 	 */
6821708Sstevel 	CV_LOCK(ENXIO)
6831708Sstevel 
6841708Sstevel 	return (DDI_SUCCESS);
6851708Sstevel }
6861708Sstevel 
6871708Sstevel static int
pcf8574_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)6881708Sstevel pcf8574_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
6891708Sstevel {
6901708Sstevel 	switch (cmd) {
6911708Sstevel 	case DDI_DETACH:
6921708Sstevel 		return (pcf8574_do_detach(dip));
6931708Sstevel 	case DDI_SUSPEND:
6941708Sstevel 		return (pcf8574_do_suspend(dip));
6951708Sstevel 	default:
6961708Sstevel 		return (DDI_FAILURE);
6971708Sstevel 	}
6981708Sstevel }
6991708Sstevel 
7001708Sstevel static int
pcf8574_chpoll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)7011708Sstevel pcf8574_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
7021708Sstevel     struct pollhead **phpp)
7031708Sstevel {
7041708Sstevel 	struct pcf8574_unit	*unitp;
7051708Sstevel 	int		instance;
7061708Sstevel 
7071708Sstevel 	instance = getminor(dev);
7081708Sstevel 	if ((unitp = (struct pcf8574_unit *)ddi_get_soft_state(
7097656SSherry.Moore@Sun.COM 	    pcf8574_soft_statep, instance)) == NULL) {
7101708Sstevel 		return (ENXIO);
7111708Sstevel 	}
7121708Sstevel 	*reventsp = 0;
7131708Sstevel 	mutex_enter(&unitp->umutex);
7141708Sstevel 	if (unitp->poll_event) {
7151708Sstevel 		*reventsp = unitp->poll_event;
7161708Sstevel 		unitp->poll_event = 0;
7171708Sstevel 	} else if ((events & POLLIN) && !anyyet)
7181708Sstevel 		*phpp = &unitp->poll;
7191708Sstevel 	mutex_exit(&unitp->umutex);
7201708Sstevel 	return (0);
7211708Sstevel }
7221708Sstevel 
7231708Sstevel /*
7241708Sstevel  * In normal scenarios, this function should never get called.
7251708Sstevel  * But, we will still come back and call this function if scsb
7261708Sstevel  * interrupt sources does not indicate an scsb interrupt. We may
7271708Sstevel  * come to this situation when SunVTS env4test is independently
7281708Sstevel  * changing the device registers.
7291708Sstevel  */
7301708Sstevel uint_t
pcf8574_intr(caddr_t arg)7311708Sstevel pcf8574_intr(caddr_t arg)
7321708Sstevel {
7331708Sstevel 	int			ic;
7341708Sstevel 	uint8_t value;
7351708Sstevel 	struct pcf8574_unit	*unitp = (struct pcf8574_unit *)(void *)arg;
7361708Sstevel 	scsb_fru_status_t	dev_presence;
7371708Sstevel 	i2c_transfer_t *tp = unitp->i2c_tran;
7381708Sstevel 
7391708Sstevel 	ic = DDI_INTR_CLAIMED;
7401708Sstevel #ifdef DEBUG
7411708Sstevel 	cmn_err(CE_NOTE, " In the interrupt service routine, %x",
7427656SSherry.Moore@Sun.COM 	    unitp->props.slave_address);
7431708Sstevel #endif
7441708Sstevel 
7451708Sstevel 	/*
7461708Sstevel 	 * Initiate an I2C transaction to find out
7471708Sstevel 	 * whether this is the device which interrupted.
7481708Sstevel 	 */
7491708Sstevel 	mutex_enter(&unitp->umutex);
7501708Sstevel 	while (unitp->pcf8574_flags == PCF8574_BUSY) {
7511708Sstevel 		if (cv_wait_sig(&unitp->pcf8574_cv, &unitp->umutex) <= 0) {
7521708Sstevel 			mutex_exit(&unitp->umutex);
7531708Sstevel 			return (DDI_INTR_UNCLAIMED);
7541708Sstevel 		}
7551708Sstevel 	}
7561708Sstevel 
7571708Sstevel 	unitp->pcf8574_flags = PCF8574_BUSY;
7581708Sstevel 	mutex_exit(&unitp->umutex);
7591708Sstevel 
7601708Sstevel 	switch (unitp->pcf8574_type) {
7611708Sstevel 		case PCF8574_TYPE_CPUVOLTAGE: {
7621708Sstevel 			dev_presence = FRU_PRESENT;
7631708Sstevel 			break;
7641708Sstevel 		}
7651708Sstevel 		case PCF8574_TYPE_PWRSUPP: {
7661708Sstevel 			envctrl_pwrsupp_t *envp =
7677656SSherry.Moore@Sun.COM 			    (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
7681708Sstevel 			dev_presence = envp->ps_present;
7691708Sstevel 			break;
7701708Sstevel 		}
7711708Sstevel 		case PCF8574_TYPE_FANTRAY: {
7721708Sstevel 			envctrl_fantray_t *envp =
7737656SSherry.Moore@Sun.COM 			    (envctrl_fantray_t *)unitp->envctrl_kstat;
7741708Sstevel 			dev_presence = envp->fan_present;
7751708Sstevel 			break;
7761708Sstevel 		}
7771708Sstevel 	}
7781708Sstevel 	if (dev_presence != FRU_PRESENT) {
7791708Sstevel 		ic = DDI_INTR_UNCLAIMED;
7801708Sstevel 		goto intr_exit;
7811708Sstevel 	}
7821708Sstevel 	if (pcf8574_read_chip(unitp, 1) != I2C_SUCCESS) {
7831708Sstevel 		ic = DDI_INTR_UNCLAIMED;
7841708Sstevel 		goto intr_exit;
7851708Sstevel 	}
7861708Sstevel 	value = unitp->i2c_tran->i2c_rbuf[0];
7871708Sstevel 	/*
7881708Sstevel 	 * If interrupt is already masked, return
7891708Sstevel 	 */
7901708Sstevel 	if (value & PCF8574_INTRMASK_BIT) {
7911708Sstevel 		ic = DDI_INTR_UNCLAIMED;
7921708Sstevel 		goto intr_exit;
7931708Sstevel 	}
7941708Sstevel 
7951708Sstevel 	/*
7961708Sstevel 	 * In case a fault bit is set, claim the interrupt.
7971708Sstevel 	 */
7981708Sstevel 	switch (unitp->pcf8574_type) {
7991708Sstevel 	case PCF8574_TYPE_PWRSUPP:
8001708Sstevel 	{
8011708Sstevel 		envctrl_pwrsupp_t *envp =
8027656SSherry.Moore@Sun.COM 		    (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
8031708Sstevel 
8041708Sstevel 		if (PCF8574_PS_FAULT(value) ||
8057656SSherry.Moore@Sun.COM 		    PCF8574_PS_TEMPOK(value) ||
8067656SSherry.Moore@Sun.COM 		    PCF8574_PS_ONOFF(value) ||
8077656SSherry.Moore@Sun.COM 		    PCF8574_PS_FANOK(value)) {
8081708Sstevel 
8091708Sstevel 			envp->ps_ok =		PCF8574_PS_FAULT(value);
8101708Sstevel 			envp->temp_ok =		PCF8574_PS_TEMPOK(value);
8111708Sstevel 			envp->psfan_ok =	PCF8574_PS_FANOK(value);
8121708Sstevel 			envp->on_state =	PCF8574_PS_ONOFF(value);
8131708Sstevel 			envp->ps_ver =		PCF8574_PS_TYPE(value);
8141708Sstevel 
8151708Sstevel 			tp->i2c_wbuf[0] =
8167656SSherry.Moore@Sun.COM 			    PCF8574_PS_DEFAULT | PCF8574_PS_MASKINTR;
8171708Sstevel 			tp->i2c_wlen = 1;
8181708Sstevel 			tp->i2c_rlen = 0;
8191708Sstevel 			tp->i2c_flags = I2C_WR;
8201708Sstevel 
8211708Sstevel 			unitp->i2c_status =
8227656SSherry.Moore@Sun.COM 			    nct_i2c_transfer(unitp->pcf8574_hdl, tp);
8231708Sstevel 
8241708Sstevel 			unitp->poll_event = POLLIN;
8251708Sstevel 			pollwakeup(&unitp->poll, POLLIN);
8261708Sstevel 		} else {
8271708Sstevel 			ic = DDI_INTR_UNCLAIMED;
8281708Sstevel 		}
8291708Sstevel 	}
8301708Sstevel 	break;
8311708Sstevel 
8321708Sstevel 	case PCF8574_TYPE_FANTRAY:
8331708Sstevel 	{
8341708Sstevel 		envctrl_fantray_t *envp =
8357656SSherry.Moore@Sun.COM 		    (envctrl_fantray_t *)unitp->envctrl_kstat;
8361708Sstevel 
8371708Sstevel 		if (!PCF8574_FAN_FAULT(value)) {
8381708Sstevel 
8391708Sstevel 			envp->fan_ver = 	PCF8574_FAN_TYPE(value);
8401708Sstevel 			envp->fan_ok = 		PCF8574_FAN_FAULT(value);
8411708Sstevel 			envp->fanspeed =  	PCF8574_FAN_FANSPD(value);
8421708Sstevel 
8431708Sstevel 			tp->i2c_wbuf[0] =
8447656SSherry.Moore@Sun.COM 			    PCF8574_FAN_DEFAULT | PCF8574_FAN_MASKINTR;
8451708Sstevel 			tp->i2c_wlen = 1;
8461708Sstevel 			tp->i2c_rlen = 0;
8471708Sstevel 			tp->i2c_flags = I2C_WR;
8481708Sstevel 
8491708Sstevel 			unitp->i2c_status =
8507656SSherry.Moore@Sun.COM 			    nct_i2c_transfer(unitp->pcf8574_hdl, tp);
8511708Sstevel 
8521708Sstevel 			unitp->poll_event = POLLIN;
8531708Sstevel 			pollwakeup(&unitp->poll, POLLIN);
8541708Sstevel 
8551708Sstevel 		} else {
8561708Sstevel 			ic = DDI_INTR_UNCLAIMED;
8571708Sstevel 		}
8581708Sstevel 	}
8591708Sstevel 	break;
8601708Sstevel 
8611708Sstevel 	default:
8621708Sstevel 		ic = DDI_INTR_UNCLAIMED;
8631708Sstevel 	} /* switch */
8641708Sstevel 
8651708Sstevel intr_exit:
8661708Sstevel 	mutex_enter(&unitp->umutex);
8671708Sstevel 	unitp->pcf8574_flags = 0;
8681708Sstevel 	cv_signal(&unitp->pcf8574_cv);
8691708Sstevel 	mutex_exit(&unitp->umutex);
8701708Sstevel 
8711708Sstevel 	return (ic);
8721708Sstevel }
8731708Sstevel 
8741708Sstevel static int
call_copyin(caddr_t arg,struct pcf8574_unit * unitp,int mode)8751708Sstevel call_copyin(caddr_t arg, struct pcf8574_unit *unitp, int mode)
8761708Sstevel {
8771708Sstevel 	uchar_t *wbuf;
8781708Sstevel 	uchar_t *rbuf;
8791708Sstevel 	i2c_transfer_t i2ct;
8801708Sstevel 	i2c_transfer_t *i2ctp = unitp->i2c_tran;
8811708Sstevel 
8821708Sstevel 
8831708Sstevel 	if (ddi_copyin((void *)arg, (caddr_t)&i2ct,
8847656SSherry.Moore@Sun.COM 	    sizeof (i2c_transfer_t), mode) != DDI_SUCCESS) {
8851708Sstevel 		return (I2C_FAILURE);
8861708Sstevel 	}
8871708Sstevel 
8881708Sstevel 	/*
8891708Sstevel 	 * Save the read and write buffer pointers in the transfer
8901708Sstevel 	 * structure, otherwise these will get overwritten when we
8911708Sstevel 	 * do a bcopy. Restore once done.
8921708Sstevel 	 */
8931708Sstevel 
8941708Sstevel 	wbuf = i2ctp->i2c_wbuf;
8951708Sstevel 	rbuf = i2ctp->i2c_rbuf;
8961708Sstevel 
8971708Sstevel 	bcopy(&i2ct, i2ctp, sizeof (i2c_transfer_t));
8981708Sstevel 
8991708Sstevel 	i2ctp->i2c_wbuf = wbuf;
9001708Sstevel 	i2ctp->i2c_rbuf = rbuf;
9011708Sstevel 
9021708Sstevel 	/*
9031708Sstevel 	 * copyin the read and write buffers to the saved buffers.
9041708Sstevel 	 */
9051708Sstevel 
9061708Sstevel 	if (i2ct.i2c_wlen != 0) {
9071708Sstevel 		if (ddi_copyin(i2ct.i2c_wbuf, (caddr_t)i2ctp->i2c_wbuf,
9087656SSherry.Moore@Sun.COM 		    i2ct.i2c_wlen, mode) != DDI_SUCCESS) {
9091708Sstevel 				return (I2C_FAILURE);
9101708Sstevel 		}
9111708Sstevel 	}
9121708Sstevel 
9131708Sstevel 	return (I2C_SUCCESS);
9141708Sstevel }
9151708Sstevel 
9161708Sstevel static int
call_copyout(caddr_t arg,struct pcf8574_unit * unitp,int mode)9171708Sstevel call_copyout(caddr_t arg, struct pcf8574_unit *unitp, int mode)
9181708Sstevel {
9191708Sstevel 	i2c_transfer_t i2ct;
9201708Sstevel 	i2c_transfer_t *i2ctp = unitp->i2c_tran;
9211708Sstevel 
9221708Sstevel 	/*
9231708Sstevel 	 * We will copyout the last three fields only, skipping
9241708Sstevel 	 * the remaining ones, before copying the rbuf to the
9251708Sstevel 	 * user buffer.
9261708Sstevel 	 */
9271708Sstevel 
9281708Sstevel 	int uskip = sizeof (i2c_transfer_t) - 3*sizeof (int16_t),
9297656SSherry.Moore@Sun.COM 	    kskip = sizeof (i2c_transfer_t) - 3*sizeof (int16_t);
9301708Sstevel 
9311708Sstevel 	/*
9321708Sstevel 	 * First copyin the user structure to the temporary i2ct,
9331708Sstevel 	 * so that we have the wbuf and rbuf addresses in it.
9341708Sstevel 	 */
9351708Sstevel 
9361708Sstevel 	uskip = sizeof (i2c_transfer_t) - 3 * (sizeof (uint16_t));
9371708Sstevel 
9381708Sstevel 	/*
9391708Sstevel 	 * copyout the last three out fields now.
9401708Sstevel 	 */
9411708Sstevel 
9421708Sstevel 	if (ddi_copyout((void *)((intptr_t)i2ctp+kskip), (void *)
9437656SSherry.Moore@Sun.COM 	    ((intptr_t)arg + uskip), 3*sizeof (uint16_t), mode)
9447656SSherry.Moore@Sun.COM 	    != DDI_SUCCESS) {
9451708Sstevel 		return (I2C_FAILURE);
9461708Sstevel 		}
9471708Sstevel 
9481708Sstevel 	/*
9491708Sstevel 	 * In case we have something to write, get the address of the read
9501708Sstevel 	 * buffer.
9511708Sstevel 	 */
9521708Sstevel 
9531708Sstevel 	if (i2ctp->i2c_rlen > i2ctp->i2c_r_resid) {
9541708Sstevel 
9551708Sstevel 		if (ddi_copyin((void *)arg, &i2ct,
9567656SSherry.Moore@Sun.COM 		    sizeof (i2c_transfer_t), mode) != DDI_SUCCESS) {
9571708Sstevel 			return (I2C_FAILURE);
9581708Sstevel 		}
9591708Sstevel 
9601708Sstevel 		/*
9611708Sstevel 		 * copyout the read buffer to the saved user buffer in i2ct.
9621708Sstevel 		 */
9631708Sstevel 
9641708Sstevel 		if (ddi_copyout(i2ctp->i2c_rbuf, i2ct.i2c_rbuf,
9657656SSherry.Moore@Sun.COM 		    i2ctp->i2c_rlen - i2ctp->i2c_r_resid, mode)
9667656SSherry.Moore@Sun.COM 		    != DDI_SUCCESS) {
9671708Sstevel 			return (I2C_FAILURE);
9681708Sstevel 		}
9691708Sstevel 	}
9701708Sstevel 
9711708Sstevel 	return (I2C_SUCCESS);
9721708Sstevel }
9731708Sstevel 
9741708Sstevel /*ARGSUSED*/
9751708Sstevel static int
pcf8574_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)9761708Sstevel pcf8574_ioctl(dev_t dev, int cmd, intptr_t arg,
9771708Sstevel 		int mode, cred_t *credp, int *rvalp)
9781708Sstevel {
9791708Sstevel 	struct pcf8574_unit *unitp;
9801708Sstevel 	register int    instance;
9811708Sstevel 	int err = 0;
9821708Sstevel 	uint8_t value, inval, outval;
9831708Sstevel 	scsb_fru_status_t dev_presence;
9841708Sstevel 
9851708Sstevel 	instance = getminor(dev);
9861708Sstevel 
9871708Sstevel 	if (instance < 0) {
9881708Sstevel 		return (ENXIO);
9891708Sstevel 	}
9901708Sstevel 	unitp = (struct pcf8574_unit *)
9917656SSherry.Moore@Sun.COM 	    ddi_get_soft_state(pcf8574_soft_statep, instance);
9921708Sstevel 
9931708Sstevel 	if (unitp == NULL) {
9941708Sstevel 		return (ENXIO);
9951708Sstevel 	}
9961708Sstevel 
9971708Sstevel 	dev_presence =
9987656SSherry.Moore@Sun.COM 	    scsb_fru_status((uchar_t)unitp->props.slave_address);
9991708Sstevel 
10001708Sstevel 	CV_LOCK(EINTR)
10011708Sstevel 
10021708Sstevel 	switch (cmd) {
10031708Sstevel 	case ENVC_IOC_INTRMASK:
10041708Sstevel 	if (dev_presence == FRU_NOT_PRESENT) {
10051708Sstevel 		break;
10061708Sstevel 	}
10071708Sstevel 
10081708Sstevel 	if (ddi_copyin((caddr_t)arg, (caddr_t)&inval,
10097656SSherry.Moore@Sun.COM 	    sizeof (uint8_t), mode) != DDI_SUCCESS) {
10101708Sstevel 		err = EFAULT;
10111708Sstevel 		break;
10121708Sstevel 	}
10131708Sstevel 
10141708Sstevel 	if (inval != 0 && inval != 1) {
10151708Sstevel 		err = EINVAL;
10161708Sstevel 	} else {
10171708Sstevel 		unitp->i2c_tran->i2c_wbuf[0] =
10187656SSherry.Moore@Sun.COM 		    PCF8574_INT_MASK(inval);
10191708Sstevel 		if (pcf8574_write_chip(unitp, 1, PCF8574_INTRMASK_BIT)
10207656SSherry.Moore@Sun.COM 		    != I2C_SUCCESS) {
10211708Sstevel 			err = EFAULT;
10221708Sstevel 		}
10231708Sstevel 	}
10241708Sstevel 	break;
10251708Sstevel 
10261708Sstevel 	case ENVC_IOC_SETFAN:
10271708Sstevel 	if (unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) {
10281708Sstevel 		err = EINVAL;
10291708Sstevel 		break;
10301708Sstevel 	}
10311708Sstevel 	if (dev_presence == FRU_NOT_PRESENT) {
10321708Sstevel 		err = EINVAL;
10331708Sstevel 		break;
10341708Sstevel 	}
10351708Sstevel 	if (ddi_copyin((caddr_t)arg, (caddr_t)&inval, sizeof (uint8_t),
10367656SSherry.Moore@Sun.COM 	    mode) != DDI_SUCCESS) {
10371708Sstevel 			err = EFAULT;
10381708Sstevel 			break;
10391708Sstevel 	}
10401708Sstevel 	if (inval != PCF8574_FAN_SPEED_LOW &&
10417656SSherry.Moore@Sun.COM 	    inval != PCF8574_FAN_SPEED_HIGH) {
10421708Sstevel 		err = EINVAL;
10431708Sstevel 		break;
10441708Sstevel 	}
10451708Sstevel 
10461708Sstevel 	unitp->i2c_tran->i2c_wbuf[0] = PCF8574_FAN_SPEED(inval);
10471708Sstevel 
10481708Sstevel 	if (pcf8574_write_chip(unitp, 1, PCF8574_FANSPEED_BIT)
10497656SSherry.Moore@Sun.COM 	    != I2C_SUCCESS) {
10501708Sstevel 		err = EFAULT;
10511708Sstevel 	}
10521708Sstevel 	break;
10531708Sstevel 
10541708Sstevel 	case ENVC_IOC_SETSTATUS:
10551708Sstevel 	/*
10561708Sstevel 	 * Allow this ioctl only in DIAG mode.
10571708Sstevel 	 */
10581708Sstevel 	if (unitp->current_mode != ENVCTRL_DIAG_MODE) {
10591708Sstevel 		err = EINVAL;
10601708Sstevel 	} else {
10611708Sstevel 		if (dev_presence == FRU_NOT_PRESENT) {
10621708Sstevel 			err = EINVAL;
10631708Sstevel 			break;
10641708Sstevel 		}
10651708Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)&inval,
10667656SSherry.Moore@Sun.COM 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
10671708Sstevel 			err = EFAULT;
10681708Sstevel 		} else {
10691708Sstevel 			unitp->i2c_tran->i2c_wbuf[0] = inval & 0xff;
10701708Sstevel 			if (pcf8574_write_chip(unitp, 1, 0xff)
10717656SSherry.Moore@Sun.COM 			    != I2C_SUCCESS) {
10721708Sstevel 				err = EFAULT;
10731708Sstevel 			}
10741708Sstevel 		}
10751708Sstevel 	}
10761708Sstevel 	break;
10771708Sstevel 
10781708Sstevel 	case ENVC_IOC_GETFAN:
10791708Sstevel 	case ENVC_IOC_GETSTATUS:
10801708Sstevel 	case ENVC_IOC_GETTYPE:
10811708Sstevel 	case ENVC_IOC_GETFAULT:
10821708Sstevel 	case ENVC_IOC_PSTEMPOK:
10831708Sstevel 	case ENVC_IOC_PSFANOK:
10841708Sstevel 	case ENVC_IOC_PSONOFF: {
10851708Sstevel 		if (dev_presence == FRU_NOT_PRESENT) {
10861708Sstevel 			err = EINVAL;
10871708Sstevel 			break;
10881708Sstevel 		}
10891708Sstevel 		if (pcf8574_read_chip(unitp, 1)
10907656SSherry.Moore@Sun.COM 		    != I2C_SUCCESS) {
10911708Sstevel 			err = EFAULT;
10921708Sstevel 			break;
10931708Sstevel 		}
10941708Sstevel 		value = unitp->i2c_tran->i2c_rbuf[0];
10951708Sstevel 		if (cmd == ENVC_IOC_GETFAN) {
10961708Sstevel 			if (unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) {
10971708Sstevel 				err = EINVAL;
10981708Sstevel 				break;
10991708Sstevel 			} else {
11001708Sstevel 				outval = PCF8574_FAN_FANSPD(value);
11011708Sstevel 			}
11021708Sstevel 		}
11031708Sstevel 		else
11041708Sstevel 		if (cmd == ENVC_IOC_GETSTATUS) {
11051708Sstevel 			outval = value;
11061708Sstevel 		}
11071708Sstevel 		else
11081708Sstevel 		if (cmd == ENVC_IOC_GETTYPE) {
11091708Sstevel 			if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP)
11101708Sstevel 				outval = PCF8574_PS_TYPE(value);
11111708Sstevel 			if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY)
11121708Sstevel 				outval = PCF8574_FAN_TYPE(value);
11131708Sstevel 		}
11141708Sstevel 		else
11151708Sstevel 		if (cmd == ENVC_IOC_GETFAULT) {
11161708Sstevel 			if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP)
11171708Sstevel 				outval = PCF8574_PS_FAULT(value);
11181708Sstevel 			if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY)
11191708Sstevel 				outval = PCF8574_PS_FAULT(value);
11201708Sstevel 		}
11211708Sstevel 		else
11221708Sstevel 		if (cmd == ENVC_IOC_PSTEMPOK) {
11231708Sstevel 			outval = PCF8574_PS_TEMPOK(value);
11241708Sstevel 		}
11251708Sstevel 		else
11261708Sstevel 		if (cmd == ENVC_IOC_PSFANOK) {
11271708Sstevel 			outval = PCF8574_PS_FANOK(value);
11281708Sstevel 		}
11291708Sstevel 		else
11301708Sstevel 		if (cmd == ENVC_IOC_PSONOFF) {
11311708Sstevel 			outval = PCF8574_PS_ONOFF(value);
11321708Sstevel 		} else {
11331708Sstevel 			outval = 0;
11341708Sstevel 		}
11351708Sstevel 
11361708Sstevel 		if (ddi_copyout((caddr_t)&outval, (caddr_t)arg,
11377656SSherry.Moore@Sun.COM 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
11381708Sstevel 			err = EFAULT;
11391708Sstevel 		}
11401708Sstevel 	}
11411708Sstevel 	break;
11421708Sstevel 
11431708Sstevel 	case ENVC_IOC_GETMODE: {
11441708Sstevel 		uint8_t curr_mode = unitp->current_mode;
11451708Sstevel 
11461708Sstevel 		if (ddi_copyout((caddr_t)&curr_mode, (caddr_t)arg,
11477656SSherry.Moore@Sun.COM 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
11481708Sstevel 			err = EFAULT;
11491708Sstevel 		}
11501708Sstevel 		break;
11511708Sstevel 	}
11521708Sstevel 
11531708Sstevel 	case ENVC_IOC_SETMODE: {
11541708Sstevel 		uint8_t curr_mode;
11551708Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)&curr_mode,
11567656SSherry.Moore@Sun.COM 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
11571708Sstevel 				err = EFAULT;
11581708Sstevel 				break;
11591708Sstevel 		}
11601708Sstevel 		if (curr_mode == ENVCTRL_DIAG_MODE ||
11617656SSherry.Moore@Sun.COM 		    curr_mode == ENVCTRL_NORMAL_MODE) {
11621708Sstevel 			unitp->current_mode = curr_mode; /* Don't do anything */
11631708Sstevel 		}
11641708Sstevel 		break;
11651708Sstevel 	}
11661708Sstevel 
11671708Sstevel 
11681708Sstevel 	case I2CDEV_TRAN:
11691708Sstevel 		if (call_copyin((caddr_t)arg, unitp, mode) != DDI_SUCCESS) {
11701708Sstevel 			err = EFAULT;
11711708Sstevel 			break;
11721708Sstevel 		}
11731708Sstevel 		unitp->i2c_status = err =
11747656SSherry.Moore@Sun.COM 		    nct_i2c_transfer(unitp->pcf8574_hdl, unitp->i2c_tran);
11751708Sstevel 
11761708Sstevel 		if (err != I2C_SUCCESS) {
11771708Sstevel 			err = EIO;
11781708Sstevel 		} else {
11791708Sstevel 			if (call_copyout((caddr_t)arg, unitp, mode)
11807656SSherry.Moore@Sun.COM 			    != DDI_SUCCESS) {
11811708Sstevel 				err = EFAULT;
11821708Sstevel 				break;
11831708Sstevel 			}
11841708Sstevel 		}
11851708Sstevel 		break;
11861708Sstevel 
11871708Sstevel 	default:
11881708Sstevel 		err = EINVAL;
11891708Sstevel 	}
11901708Sstevel 
11911708Sstevel 	CV_UNLOCK
11921708Sstevel 
11931708Sstevel 	return (err);
11941708Sstevel }
11951708Sstevel 
11961708Sstevel static int
pcf8574_add_kstat(struct pcf8574_unit * unitp,scsb_fru_status_t dev_presence)11971708Sstevel pcf8574_add_kstat(struct pcf8574_unit *unitp, scsb_fru_status_t dev_presence)
11981708Sstevel {
11991708Sstevel 	char ksname[50];
12001708Sstevel 	int id;
12011708Sstevel 	uint8_t i2c_address = unitp->props.slave_address;
12021708Sstevel 
12031708Sstevel 	/*
12041708Sstevel 	 * We create the kstat depending on the device function,
12051708Sstevel 	 * allocate the kstat placeholder and initialize the
12061708Sstevel 	 * values.
12071708Sstevel 	 */
12081708Sstevel 	unitp->envctrl_kstat = NULL;
12091708Sstevel 	switch (unitp->pcf8574_type) {
12101708Sstevel 	case PCF8574_TYPE_CPUVOLTAGE:
12111708Sstevel 	{
12121708Sstevel 		if ((unitp->kstatp = kstat_create(I2C_PCF8574_NAME,
12137656SSherry.Moore@Sun.COM 		    unitp->instance, I2C_KSTAT_CPUVOLTAGE, "misc",
12147656SSherry.Moore@Sun.COM 		    KSTAT_TYPE_RAW, sizeof (envctrl_cpuvoltage_t),
12157656SSherry.Moore@Sun.COM 		    KSTAT_FLAG_PERSISTENT)) != NULL) {
12161708Sstevel 
12171708Sstevel 			if ((unitp->envctrl_kstat = kmem_zalloc(
12187656SSherry.Moore@Sun.COM 			    sizeof (envctrl_cpuvoltage_t), KM_NOSLEEP)) ==
12197656SSherry.Moore@Sun.COM 			    NULL) {
12201708Sstevel 				kstat_delete(unitp->kstatp);
12211708Sstevel 				return (DDI_FAILURE);
12221708Sstevel 			}
12231708Sstevel 		} else {
12241708Sstevel 			return (DDI_FAILURE);
12251708Sstevel 		}
12261708Sstevel 
12271708Sstevel 		break;
12281708Sstevel 	}
12291708Sstevel 	case PCF8574_TYPE_PWRSUPP:
12301708Sstevel 	{
12311708Sstevel 		envctrl_pwrsupp_t *envp;
12321708Sstevel 		if (i2c_address == PCF8574_ADR_PWRSUPPLY1) {
12331708Sstevel 			id = 1;
12341708Sstevel 		} else if (i2c_address == PCF8574_ADR_PWRSUPPLY2) {
12351708Sstevel 			id = 2;
12361708Sstevel 		} else  {
12371708Sstevel 			id = i2c_address - PCF8574_ADR_PWRSUPPLY1;
12381708Sstevel 		}
1239*11311SSurya.Prakki@Sun.COM 		(void) sprintf(ksname, "%s%d", I2C_KSTAT_PWRSUPPLY, id);
12401708Sstevel 		if ((unitp->kstatp = kstat_create(I2C_PCF8574_NAME,
12417656SSherry.Moore@Sun.COM 		    unitp->instance, ksname, "misc",
12427656SSherry.Moore@Sun.COM 		    KSTAT_TYPE_RAW, sizeof (envctrl_pwrsupp_t),
12437656SSherry.Moore@Sun.COM 		    KSTAT_FLAG_PERSISTENT)) != NULL) {
12441708Sstevel 
12451708Sstevel 			if ((unitp->envctrl_kstat = kmem_zalloc(
12467656SSherry.Moore@Sun.COM 			    sizeof (envctrl_pwrsupp_t), KM_NOSLEEP)) ==
12477656SSherry.Moore@Sun.COM 			    NULL) {
12481708Sstevel 				kstat_delete(unitp->kstatp);
12491708Sstevel 				return (DDI_FAILURE);
12501708Sstevel 			}
12511708Sstevel 			/*
12521708Sstevel 			 * Initialize the kstat fields. Need to initialize
12531708Sstevel 			 * the present field from SCSB info (dev_presence)
12541708Sstevel 			 */
12551708Sstevel 			envp = (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
12561708Sstevel 
12571708Sstevel 			envp->ps_present = dev_presence;
12581708Sstevel 			envp->ps_ok = 0;
12591708Sstevel 			envp->temp_ok = 0;
12601708Sstevel 			envp->psfan_ok = 0;
12611708Sstevel 			envp->on_state = 0;
12621708Sstevel 			envp->ps_ver = 0;
12631708Sstevel 		} else {
12641708Sstevel 			return (DDI_FAILURE);
12651708Sstevel 		}
12661708Sstevel 
12671708Sstevel 		break;
12681708Sstevel 	}
12691708Sstevel 	case PCF8574_TYPE_FANTRAY:
12701708Sstevel 	{
12711708Sstevel 		envctrl_fantray_t *envp;
12721708Sstevel 		if (i2c_address == PCF8574_ADR_FANTRAY1) {
12731708Sstevel 			id = 1;
12741708Sstevel 		} else if (i2c_address == PCF8574_ADR_FANTRAY2) {
12751708Sstevel 			id = 2;
12761708Sstevel 		} else  {
12771708Sstevel 			id = i2c_address - PCF8574_ADR_FANTRAY1;
12781708Sstevel 		}
1279*11311SSurya.Prakki@Sun.COM 		(void) sprintf(ksname, "%s%d", I2C_KSTAT_FANTRAY, id);
12801708Sstevel 		if ((unitp->kstatp = kstat_create(I2C_PCF8574_NAME,
12817656SSherry.Moore@Sun.COM 		    unitp->instance, ksname, "misc",
12827656SSherry.Moore@Sun.COM 		    KSTAT_TYPE_RAW, sizeof (envctrl_fantray_t),
12837656SSherry.Moore@Sun.COM 		    KSTAT_FLAG_PERSISTENT | KSTAT_FLAG_WRITABLE)) != NULL) {
12841708Sstevel 
12851708Sstevel 			if ((unitp->envctrl_kstat = kmem_zalloc(
12867656SSherry.Moore@Sun.COM 			    sizeof (envctrl_fantray_t), KM_NOSLEEP)) ==
12877656SSherry.Moore@Sun.COM 			    NULL) {
12881708Sstevel 				kstat_delete(unitp->kstatp);
12891708Sstevel 				return (DDI_FAILURE);
12901708Sstevel 			}
12911708Sstevel 
12921708Sstevel 			/*
12931708Sstevel 			 * Initialize the kstat fields. Need to initialize
12941708Sstevel 			 * the present field from SCSB info (dev_presence)
12951708Sstevel 			 */
12961708Sstevel 			envp = (envctrl_fantray_t *)unitp->envctrl_kstat;
12971708Sstevel 
12981708Sstevel 			envp->fan_present = dev_presence;
12991708Sstevel 			envp->fan_ok = 0;
13001708Sstevel 			envp->fanspeed =  PCF8574_FAN_SPEED60;
13011708Sstevel 			envp->fan_ver = 0;
13021708Sstevel 		} else {
13031708Sstevel 			return (DDI_FAILURE);
13041708Sstevel 		}
13051708Sstevel 
13061708Sstevel 		break;
13071708Sstevel 	}
13081708Sstevel 	default:
13091708Sstevel 		return (DDI_FAILURE);
13101708Sstevel 	}
13111708Sstevel 
13121708Sstevel 	unitp->kstatp->ks_private = (void *)unitp;
13131708Sstevel 	unitp->kstatp->ks_update = pcf8574_kstat_update;
13141708Sstevel 
13151708Sstevel 	kstat_install(unitp->kstatp);
13161708Sstevel 
13171708Sstevel 	return (DDI_SUCCESS);
13181708Sstevel }
13191708Sstevel 
13201708Sstevel /*
13211708Sstevel  * This function reads a single byte from the pcf8574 chip, for use by the
13221708Sstevel  * kstat routines. The protocol for read will depend on the function.
13231708Sstevel  */
13241708Sstevel 
13251708Sstevel static int
pcf8574_read_chip(struct pcf8574_unit * unitp,uint16_t size)13261708Sstevel pcf8574_read_chip(struct pcf8574_unit *unitp, uint16_t size)
13271708Sstevel {
13281708Sstevel 	int retval, i;
13291708Sstevel 	i2c_transfer_t *tp = unitp->i2c_tran;
13301708Sstevel 
13311708Sstevel 
13321708Sstevel 	tp->i2c_flags = I2C_RD;
13331708Sstevel 	tp->i2c_rlen = size;
13341708Sstevel 	tp->i2c_wlen = 0;
13351708Sstevel 
13361708Sstevel 	/*
13371708Sstevel 	 * Read the bytes from the pcf8574, mask off the
13381708Sstevel 	 * non-read bits and return the value. Block with
13391708Sstevel 	 * the driverwide lock.
13401708Sstevel 	 */
13411708Sstevel 	unitp->i2c_status = retval =
13427656SSherry.Moore@Sun.COM 	    nct_i2c_transfer(unitp->pcf8574_hdl, unitp->i2c_tran);
13431708Sstevel 
13441708Sstevel 	if (retval != I2C_SUCCESS) {
13451708Sstevel 		return (retval);
13461708Sstevel 	}
13471708Sstevel 
13481708Sstevel 	for (i = 0; i < size; i++) {
13491708Sstevel 		tp->i2c_rbuf[i] &= unitp->readmask;
13501708Sstevel 	}
13511708Sstevel 
13521708Sstevel 	return (I2C_SUCCESS);
13531708Sstevel }
13541708Sstevel 
13551708Sstevel /*
13561708Sstevel  * This function writes a single byte to the pcf8574 chip, for use by the
13571708Sstevel  * ioctl routines. The protocol for write will depend on the function.
13581708Sstevel  * The bitpattern tells which bits are being modified, by setting these
13591708Sstevel  * bits in bitpattern to 1, e.g for fanspeed, bitpattern = 0x08, fanspeed
13601708Sstevel  * and intr 0x0c, only intr 0x04.
13611708Sstevel  */
13621708Sstevel 
13631708Sstevel static int
pcf8574_write_chip(struct pcf8574_unit * unitp,uint16_t size,uint8_t bitpattern)13641708Sstevel pcf8574_write_chip(struct pcf8574_unit *unitp,
13651708Sstevel 		uint16_t size, uint8_t bitpattern)
13661708Sstevel {
13671708Sstevel 	i2c_transfer_t *tp = unitp->i2c_tran;
13681708Sstevel 	int i;
13691708Sstevel 
13701708Sstevel 		/*
13711708Sstevel 		 * pcf8574_write
13721708Sstevel 		 *
13731708Sstevel 		 * First read the byte, modify only the writable
13741708Sstevel 		 * ports, then write back the modified data.
13751708Sstevel 		 */
13761708Sstevel 		tp->i2c_wlen = 0;
13771708Sstevel 		tp->i2c_rlen = size;
13781708Sstevel 		tp->i2c_flags = I2C_RD;
13791708Sstevel 
13801708Sstevel 		unitp->i2c_status = nct_i2c_transfer(unitp->pcf8574_hdl, tp);
13811708Sstevel 
13821708Sstevel 		if (unitp->i2c_status != I2C_SUCCESS) {
13831708Sstevel 			return (I2C_FAILURE);
13841708Sstevel 		}
13851708Sstevel 
13861708Sstevel 		/*
13871708Sstevel 		 * Our concern is when we have to write only a few bits.
13881708Sstevel 		 * We need to make sure we write the same value to those
13891708Sstevel 		 * bit positions which does not appear in bitpattern.
13901708Sstevel 		 */
13911708Sstevel 
13921708Sstevel 		/*
13931708Sstevel 		 * 1) Ignore all bits than the one we are writing
13941708Sstevel 		 * 2) Now 0 the bits we intend to modify in the value
13951708Sstevel 		 * read from the chip, preserving all others.
13961708Sstevel 		 * 3) Now turn all non-writable ( read only/reserved )
13971708Sstevel 		 * bits to 1. The value now should contain:
13981708Sstevel 		 * 1 			in all non-writable bits.
13991708Sstevel 		 * 0 			in the bis(s) we intend to modify.
14001708Sstevel 		 * no change 	in the writable bits we don't modify.
14011708Sstevel 		 * 4) Now OR it with the bits we got before, i.e. after
14021708Sstevel 		 * ignoring all bits other than one we are writing.
14031708Sstevel 		 */
14041708Sstevel 
14051708Sstevel 		for (i = 0; i < size; i++) {
14061708Sstevel 			tp->i2c_rbuf[i] &= ~(bitpattern);
14071708Sstevel 
14081708Sstevel 			tp->i2c_rbuf[i] |= ~(unitp->writemask);
14091708Sstevel 
14101708Sstevel 			tp->i2c_wbuf[i] = tp->i2c_rbuf[i] |
14117656SSherry.Moore@Sun.COM 			    (tp->i2c_wbuf[i] & bitpattern);
14121708Sstevel 		}
14131708Sstevel 
14141708Sstevel 		tp->i2c_rlen = 0;
14151708Sstevel 		tp->i2c_wlen = size;
14161708Sstevel 		tp->i2c_flags = I2C_WR;
14171708Sstevel 
14181708Sstevel 		unitp->i2c_status = nct_i2c_transfer(unitp->pcf8574_hdl, tp);
14191708Sstevel 
14201708Sstevel 		return (unitp->i2c_status);
14211708Sstevel }
14221708Sstevel 
14231708Sstevel static int
pcf8574_kstat_update(kstat_t * ksp,int rw)14241708Sstevel pcf8574_kstat_update(kstat_t *ksp, int rw)
14251708Sstevel {
14261708Sstevel 	struct pcf8574_unit *unitp;
14271708Sstevel 	char *kstatp;
14281708Sstevel 	uint8_t value;
14291708Sstevel 	int err = DDI_SUCCESS;
14301708Sstevel 	scsb_fru_status_t	dev_presence;
14311708Sstevel 
14321708Sstevel 	unitp = (struct pcf8574_unit *)ksp->ks_private;
14331708Sstevel 	if (unitp->envctrl_kstat == NULL) { /* May be detaching */
14341708Sstevel 		return (err);
14351708Sstevel 	}
14361708Sstevel 
14371708Sstevel 	CV_LOCK(EINTR)
14381708Sstevel 
14391708Sstevel 	/*
14401708Sstevel 	 * Need to call scsb to find whether device is present.
14411708Sstevel 	 * For I2C devices, the I2C address is used as a FRU ID.
14421708Sstevel 	 */
14431708Sstevel 	if (unitp->pcf8574_type == PCF8574_TYPE_CPUVOLTAGE) {
14441708Sstevel 		dev_presence = FRU_PRESENT;
14451708Sstevel 	} else {
14461708Sstevel 		dev_presence =
14477656SSherry.Moore@Sun.COM 		    scsb_fru_status((uchar_t)unitp->props.slave_address);
14481708Sstevel 	}
14491708Sstevel 
14501708Sstevel 	kstatp = (char *)ksp->ks_data;
14511708Sstevel 
14521708Sstevel 	/*
14531708Sstevel 	 * We could have write on the power supply and the fantray
14541708Sstevel 	 * pcf8574 chips. For masking the interrupt on both, or
14551708Sstevel 	 * controlling the fan speed on the fantray. But write
14561708Sstevel 	 * will not be allowed through the kstat interface. For
14571708Sstevel 	 * the present field, call SCSB.
14581708Sstevel 	 */
14591708Sstevel 
14601708Sstevel 	if (rw == KSTAT_WRITE) {
14611708Sstevel 		if (unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) {
14621708Sstevel 			err = EACCES;
14631708Sstevel 			goto kstat_exit;
14641708Sstevel 		}
14651708Sstevel 		value = ((envctrl_fantray_t *)kstatp)->fanspeed;
14661708Sstevel 		if (value != PCF8574_FAN_SPEED_LOW &&
14677656SSherry.Moore@Sun.COM 		    value != PCF8574_FAN_SPEED_HIGH) {
14681708Sstevel 			err = EINVAL;
14691708Sstevel 			goto kstat_exit;
14701708Sstevel 		}
14711708Sstevel 
14721708Sstevel 		unitp->i2c_tran->i2c_wbuf[0] = PCF8574_FAN_SPEED(value);
14731708Sstevel 
14741708Sstevel 		if (dev_presence == FRU_PRESENT &&
14757656SSherry.Moore@Sun.COM 		    pcf8574_write_chip(unitp, 1, PCF8574_FANSPEED_BIT)
14767656SSherry.Moore@Sun.COM 		    != I2C_SUCCESS) {
14771708Sstevel 			err = EFAULT;
14781708Sstevel 			goto kstat_exit;
14791708Sstevel 		}
14801708Sstevel 
14811708Sstevel 	} else {
14821708Sstevel 		/*
14831708Sstevel 		 * First make sure that the FRU exists by checking the SCSB
14841708Sstevel 		 * dev_presence info.  If not present, set the change field,
14851708Sstevel 		 * clear the kstat fields and make sure the kstat *_present
14861708Sstevel 		 * field is set to dev_presence from the SCSB driver.
14871708Sstevel 		 */
14881708Sstevel 		if (dev_presence == FRU_PRESENT &&
14897656SSherry.Moore@Sun.COM 		    pcf8574_read_chip(unitp, 1) != I2C_SUCCESS) {
14901708Sstevel 			/*
14911708Sstevel 			 * Looks like a real IO error.
14921708Sstevel 			 */
14931708Sstevel 			err = EIO;
14941708Sstevel 			CV_UNLOCK
14951708Sstevel 
14961708Sstevel 			return (err);
14971708Sstevel 		}
14981708Sstevel 		if (dev_presence == FRU_PRESENT)
14991708Sstevel 			value = unitp->i2c_tran->i2c_rbuf[0];
15001708Sstevel 		else
15011708Sstevel 			value = 0;
15021708Sstevel 
15031708Sstevel 		switch (unitp->pcf8574_type) {
15041708Sstevel 		case PCF8574_TYPE_CPUVOLTAGE: {
15051708Sstevel 			envctrl_cpuvoltage_t *envp =
15067656SSherry.Moore@Sun.COM 			    (envctrl_cpuvoltage_t *)unitp->envctrl_kstat;
15071708Sstevel 			envp->value = value;
15081708Sstevel 			bcopy((caddr_t)envp, kstatp,
15097656SSherry.Moore@Sun.COM 			    sizeof (envctrl_cpuvoltage_t));
15101708Sstevel 
15111708Sstevel 			break;
15121708Sstevel 		}
15131708Sstevel 		case PCF8574_TYPE_PWRSUPP: {
15141708Sstevel 			envctrl_pwrsupp_t *envp =
15157656SSherry.Moore@Sun.COM 			    (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
15161708Sstevel 
15171708Sstevel 			envp->ps_present = 	dev_presence;
15181708Sstevel 			envp->ps_ok =		PCF8574_PS_FAULT(value);
15191708Sstevel 			envp->temp_ok =		PCF8574_PS_TEMPOK(value);
15201708Sstevel 			envp->psfan_ok =	PCF8574_PS_FANOK(value);
15211708Sstevel 			envp->on_state =	PCF8574_PS_ONOFF(value);
15221708Sstevel 			envp->ps_ver =		PCF8574_PS_TYPE(value);
15231708Sstevel 
15241708Sstevel 			bcopy((caddr_t)envp, kstatp,
15257656SSherry.Moore@Sun.COM 			    sizeof (envctrl_pwrsupp_t));
15261708Sstevel 
15271708Sstevel 			break;
15281708Sstevel 		}
15291708Sstevel 		case PCF8574_TYPE_FANTRAY: {
15301708Sstevel 			envctrl_fantray_t *envp =
15317656SSherry.Moore@Sun.COM 			    (envctrl_fantray_t *)unitp->envctrl_kstat;
15321708Sstevel 
15331708Sstevel 			envp->fan_present = dev_presence;
15341708Sstevel 			envp->fan_ver = 	PCF8574_FAN_TYPE(value);
15351708Sstevel 			envp->fan_ok = 		PCF8574_FAN_FAULT(value);
15361708Sstevel 			envp->fanspeed =  	PCF8574_FAN_FANSPD(value);
15371708Sstevel 
15381708Sstevel 			bcopy((caddr_t)unitp->envctrl_kstat, kstatp,
15397656SSherry.Moore@Sun.COM 			    sizeof (envctrl_fantray_t));
15401708Sstevel 
15411708Sstevel 			break;
15421708Sstevel 		}
15431708Sstevel 
15441708Sstevel 		default:
15451708Sstevel 			break;
15461708Sstevel 		}
15471708Sstevel 	}
15481708Sstevel 
15491708Sstevel kstat_exit:
15501708Sstevel 
15511708Sstevel 	CV_UNLOCK
15521708Sstevel 
15531708Sstevel 	return (err);
15541708Sstevel }
15551708Sstevel 
15561708Sstevel static void
pcf8574_delete_kstat(struct pcf8574_unit * unitp)15571708Sstevel pcf8574_delete_kstat(struct pcf8574_unit *unitp)
15581708Sstevel {
15591708Sstevel 	/*
15601708Sstevel 	 * Depending on the function, deallocate the correct
15611708Sstevel 	 * kernel allocated memory.
15621708Sstevel 	 */
15631708Sstevel 	if (unitp->kstatp != NULL) {
15641708Sstevel 		kstat_delete(unitp->kstatp);
15651708Sstevel 	}
15661708Sstevel 
15671708Sstevel 	switch (unitp->pcf8574_type) {
15681708Sstevel 	case PCF8574_TYPE_CPUVOLTAGE: {
15691708Sstevel 		if (unitp->envctrl_kstat != NULL) {
15701708Sstevel 			kmem_free(unitp->envctrl_kstat,
15717656SSherry.Moore@Sun.COM 			    sizeof (envctrl_cpuvoltage_t));
15721708Sstevel 		}
15731708Sstevel 		break;
15741708Sstevel 	}
15751708Sstevel 	case PCF8574_TYPE_PWRSUPP: {
15761708Sstevel 		if (unitp->envctrl_kstat != NULL) {
15771708Sstevel 			kmem_free(unitp->envctrl_kstat,
15787656SSherry.Moore@Sun.COM 			    sizeof (envctrl_pwrsupp_t));
15791708Sstevel 		}
15801708Sstevel 
15811708Sstevel 		break;
15821708Sstevel 	}
15831708Sstevel 	case PCF8574_TYPE_FANTRAY: {
15841708Sstevel 		if (unitp->envctrl_kstat != NULL) {
15851708Sstevel 			kmem_free(unitp->envctrl_kstat,
15867656SSherry.Moore@Sun.COM 			    sizeof (envctrl_fantray_t));
15871708Sstevel 		}
15881708Sstevel 		break;
15891708Sstevel 	}
15901708Sstevel 	default:
15911708Sstevel 		break;
15921708Sstevel 	}
15931708Sstevel 
15941708Sstevel 	unitp->envctrl_kstat = NULL;
15951708Sstevel }
15961708Sstevel 
15971708Sstevel static int
pcf8574_read_props(struct pcf8574_unit * unitp)15981708Sstevel pcf8574_read_props(struct pcf8574_unit *unitp)
15991708Sstevel {
16001708Sstevel 	dev_info_t *dip = unitp->dip;
16011708Sstevel 	int retval = 0, prop_len;
16021708Sstevel 	uint32_t  *prop_value = NULL;
16031708Sstevel 	uint8_t i2c_address;
16041708Sstevel 	char *function;
16051708Sstevel 
16061708Sstevel 	/*
16071708Sstevel 	 * read the pcf8574_function property. If this property is not
16081708Sstevel 	 * found, return ERROR. Else, make sure it's either powersupply
16091708Sstevel 	 * or fantray.
16101708Sstevel 	 */
16111708Sstevel 
16121708Sstevel 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
16137656SSherry.Moore@Sun.COM 	    "pcf8574_function", &function) != DDI_SUCCESS) {
16141708Sstevel 		dbg_print(CE_WARN, "Couldn't find pcf8574_function property");
16151708Sstevel 
16161708Sstevel 		return (DDI_FAILURE);
16171708Sstevel 	}
16181708Sstevel 
16191708Sstevel 	if (strcmp(function, "fantray") == 0) {
16201708Sstevel 		unitp->pcf8574_type = PCF8574_TYPE_FANTRAY;
16211708Sstevel 		/*
16221708Sstevel 		 * Will fail the fantray attach if patch - 1.
16231708Sstevel 		 */
16241708Sstevel 		if (nct_p10fan_patch) {
16251708Sstevel #ifdef DEBUG
16261708Sstevel 		cmn_err(CE_WARN, "nct_p10fan_patch set: will not load "
16277656SSherry.Moore@Sun.COM 		    "fantary:address %x,%x", unitp->props.i2c_bus,
16287656SSherry.Moore@Sun.COM 		    unitp->props.slave_address);
16291708Sstevel #endif
16301708Sstevel 			ddi_prop_free(function);
16311708Sstevel 			return (DDI_FAILURE);
16321708Sstevel 		}
16331708Sstevel 	} else
16341708Sstevel 	if (strcmp(function, "powersupply") == 0) {
16351708Sstevel 		unitp->pcf8574_type = PCF8574_TYPE_PWRSUPP;
16361708Sstevel 	} else {
16371708Sstevel 		dbg_print(CE_WARN, "Neither powersupply nor fantray");
16381708Sstevel 		ddi_prop_free(function);
16391708Sstevel 
16401708Sstevel 		return (DDI_FAILURE);
16411708Sstevel 	}
16421708Sstevel 
16431708Sstevel 	ddi_prop_free(function);
16441708Sstevel 
16451708Sstevel 	retval = ddi_getlongprop(DDI_DEV_T_ANY, dip,
16467656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP,
16477656SSherry.Moore@Sun.COM 	    "reg", (caddr_t)&prop_value, &prop_len);
16481708Sstevel 	if (retval == DDI_PROP_SUCCESS) {
16491708Sstevel 		unitp->props.i2c_bus		= (uint16_t)prop_value[0];
16501708Sstevel 		unitp->props.slave_address	= i2c_address =
16517656SSherry.Moore@Sun.COM 		    (uint8_t)prop_value[1];
16521708Sstevel 		kmem_free(prop_value, prop_len);
16531708Sstevel 
16541708Sstevel 		if (i2c_address>>4 == 7)
16551708Sstevel 			unitp->sensor_type = PCF8574A;
16561708Sstevel 		else if (i2c_address>>4 == 4)
16571708Sstevel 			unitp->sensor_type = PCF8574;
16581708Sstevel 		else {
16591708Sstevel 			unitp->sensor_type = PCF8574A;
16601708Sstevel 			dbg_print(CE_WARN, "Not a pcf8574/a device");
16611708Sstevel 		}
16621708Sstevel 
16631708Sstevel 	} else {
16641708Sstevel 		unitp->props.i2c_bus		= (uint16_t)-1;
16651708Sstevel 		unitp->props.slave_address	= (uint16_t)-1;
16661708Sstevel 	}
16671708Sstevel 
16681708Sstevel 	/*
16691708Sstevel 	 * Get the Property information that the driver will be using
16701708Sstevel 	 * see typedef struct pcf8574_properties_t;
16711708Sstevel 	 */
16721708Sstevel 
16731708Sstevel 	unitp->pcf8574_canintr = 0;
16741708Sstevel 	retval = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
16757656SSherry.Moore@Sun.COM 	    "interrupts", -1);
16761708Sstevel 	if (retval >= 0) {
16771708Sstevel 		int prop_len, intr_pri = 4;
16781708Sstevel 		unitp->pcf8574_canintr |= PCF8574_INTR_ON;
16791708Sstevel 		if (ddi_getproplen(DDI_DEV_T_ANY, dip,
16807656SSherry.Moore@Sun.COM 		    DDI_PROP_DONTPASS, "interrupt-priorities",
16817656SSherry.Moore@Sun.COM 		    &prop_len) == DDI_PROP_NOT_FOUND) {
16821708Sstevel 			retval = ddi_prop_create(DDI_DEV_T_NONE, dip,
16837656SSherry.Moore@Sun.COM 			    DDI_PROP_CANSLEEP, "interrupt-priorities",
16847656SSherry.Moore@Sun.COM 			    (caddr_t)&intr_pri, sizeof (int));
16851708Sstevel #ifdef DEBUG
16861708Sstevel 			if (retval != DDI_PROP_SUCCESS) {
16871708Sstevel 				cmn_err(CE_WARN, "Failed to create interrupt- \
16881708Sstevel 				priorities property, retval %d", retval);
16891708Sstevel 			}
16901708Sstevel #endif /* DEBUG */
16911708Sstevel 		}
16921708Sstevel 	}
16931708Sstevel 
16941708Sstevel 	/*
16951708Sstevel 	 * No channels-in-use property for the fan and powersupplies.
16961708Sstevel 	 */
16971708Sstevel 	unitp->props.num_chans_used = 0;
16981708Sstevel 	if (i2c_address == PCF8574_ADR_CPUVOLTAGE) {
16991708Sstevel 		if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
17007656SSherry.Moore@Sun.COM 		    "channels-in-use", &prop_len) == DDI_PROP_SUCCESS) {
17011708Sstevel 			retval = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY,
17027656SSherry.Moore@Sun.COM 			    dip, DDI_PROP_DONTPASS,
17037656SSherry.Moore@Sun.COM 			    "channels-in-use",
17047656SSherry.Moore@Sun.COM 			    (uchar_t **)&unitp->props.channels_in_use,
17057656SSherry.Moore@Sun.COM 			    &unitp->props.num_chans_used);
17061708Sstevel 			if (retval != DDI_PROP_SUCCESS) {
17071708Sstevel 				unitp->props.num_chans_used = 0;
17081708Sstevel 			} else {
17091708Sstevel 				unitp->props.num_chans_used /=
17107656SSherry.Moore@Sun.COM 				    sizeof (pcf8574_channel_t);
17111708Sstevel 			}
17121708Sstevel 		}
17131708Sstevel 	}
17141708Sstevel 
17151708Sstevel 	return (DDI_PROP_SUCCESS);
17161708Sstevel }
17171708Sstevel 
17181708Sstevel /*
17191708Sstevel  * callback function to register with the SCSB driver in order to be
17201708Sstevel  * informed about changes in device instance presence.
17211708Sstevel  */
17221708Sstevel /*ARGSUSED*/
17231708Sstevel void
pcf8574_callback(void * softstate,scsb_fru_event_t cb_event,scsb_fru_status_t dev_presence)17241708Sstevel pcf8574_callback(void *softstate, scsb_fru_event_t cb_event,
17251708Sstevel 		scsb_fru_status_t dev_presence)
17261708Sstevel {
17271708Sstevel 	struct pcf8574_unit *unitp = (struct pcf8574_unit *)softstate;
17281708Sstevel #ifdef DEBUG
17291708Sstevel 		if (pcf8574_debug & 0x00800001)
17301708Sstevel 			cmn_err(CE_NOTE, "pcf8574_callback(unitp,%d,%d)",
17317656SSherry.Moore@Sun.COM 			    (int)cb_event, (int)dev_presence);
17321708Sstevel #endif /* DEBUG */
17331708Sstevel 
17341708Sstevel 	switch (unitp->pcf8574_type) {
17351708Sstevel 		case PCF8574_TYPE_CPUVOLTAGE: {
17361708Sstevel 			/*
17371708Sstevel 			 * This Unit is not Field Replacable and will not
17381708Sstevel 			 * generate any events at the SCB.
17391708Sstevel 			 */
17401708Sstevel 			break;
17411708Sstevel 		}
17421708Sstevel 		case PCF8574_TYPE_PWRSUPP: {
17431708Sstevel 			envctrl_pwrsupp_t *envp;
17441708Sstevel 
17451708Sstevel 			envp = (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
17461708Sstevel 			if (dev_presence == FRU_NOT_PRESENT) {
17471708Sstevel 				envp->ps_ok = 0;
17481708Sstevel 				envp->temp_ok = 0;
17491708Sstevel 				envp->psfan_ok = 0;
17501708Sstevel 				envp->on_state = 0;
17511708Sstevel 				envp->ps_ver = 0;
17521708Sstevel 			} else
17531708Sstevel 			if (dev_presence == FRU_PRESENT &&
17547656SSherry.Moore@Sun.COM 			    envp->ps_present == FRU_NOT_PRESENT) {
1755*11311SSurya.Prakki@Sun.COM 				(void) pcf8574_init_chip(unitp, 0);
17561708Sstevel 			}
17571708Sstevel 			envp->ps_present = dev_presence;
17581708Sstevel 			unitp->poll_event = POLLIN;
17591708Sstevel 			pollwakeup(&unitp->poll, POLLIN);
17601708Sstevel 			break;
17611708Sstevel 		}
17621708Sstevel 		case PCF8574_TYPE_FANTRAY: {
17631708Sstevel 			envctrl_fantray_t *envp;
17641708Sstevel 
17651708Sstevel 			envp = (envctrl_fantray_t *)unitp->envctrl_kstat;
17661708Sstevel 
17671708Sstevel 			if (dev_presence == FRU_NOT_PRESENT) {
17681708Sstevel 				envp->fan_ok = 0;
17691708Sstevel 				envp->fanspeed =  PCF8574_FAN_SPEED60;
17701708Sstevel 				envp->fan_ver = 0;
17711708Sstevel 			} else
17721708Sstevel 			if (dev_presence == FRU_PRESENT &&
17737656SSherry.Moore@Sun.COM 			    envp->fan_present == FRU_NOT_PRESENT) {
1774*11311SSurya.Prakki@Sun.COM 				(void) pcf8574_init_chip(unitp, 0);
17751708Sstevel 			}
17761708Sstevel 			envp->fan_present = dev_presence;
17771708Sstevel 			unitp->poll_event = POLLIN;
17781708Sstevel 			pollwakeup(&unitp->poll, POLLIN);
17791708Sstevel 			break;
17801708Sstevel 		}
17811708Sstevel 	}
17821708Sstevel }
17831708Sstevel 
17841708Sstevel /*
17851708Sstevel  * Initializes the chip after attach or after being inserted.
17861708Sstevel  * intron = 0 => disable interrupt.
17871708Sstevel  * intron = 1 => read register, enable interrupt if no fault.
17881708Sstevel  */
17891708Sstevel 
17901708Sstevel static int
pcf8574_init_chip(struct pcf8574_unit * unitp,int intron)17911708Sstevel pcf8574_init_chip(struct pcf8574_unit *unitp, int intron)
17921708Sstevel {
17931708Sstevel 	int ret = I2C_SUCCESS;
17941708Sstevel 	i2c_transfer_t *tp = unitp->i2c_tran;
17951708Sstevel 	uint8_t value = 0;
17961708Sstevel 	boolean_t device_faulty = B_FALSE; /* true is faulty */
17971708Sstevel 
17981708Sstevel 	if (unitp->pcf8574_type != PCF8574_TYPE_PWRSUPP &&
17997656SSherry.Moore@Sun.COM 	    unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) {
18001708Sstevel 		return (ret);
18011708Sstevel 	}
18021708Sstevel 	switch (unitp->pcf8574_type) {
18031708Sstevel 	case PCF8574_TYPE_PWRSUPP:
18041708Sstevel 		tp->i2c_wbuf[0] = PCF8574_PS_DEFAULT;
18051708Sstevel 
18061708Sstevel 		break;
18071708Sstevel 	case PCF8574_TYPE_FANTRAY:
18081708Sstevel 			tp->i2c_wbuf[0] = PCF8574_FAN_DEFAULT;
18091708Sstevel 
18101708Sstevel 		break;
18111708Sstevel 	default:
18121708Sstevel 		break;
18131708Sstevel 	}
18141708Sstevel 
18151708Sstevel 	/*
18161708Sstevel 	 * First, read the device. If the device is faulty, it does
18171708Sstevel 	 * not make sense to enable the interrupt, so in this case
18181708Sstevel 	 * keep interrupt maskked inspite of what "intron" says.
18191708Sstevel 	 */
18201708Sstevel 
18211708Sstevel 	tp->i2c_wlen = 0;
18221708Sstevel 	tp->i2c_rlen = 1;
18231708Sstevel 	tp->i2c_flags = I2C_RD;
18241708Sstevel 
18251708Sstevel 	unitp->i2c_status = ret = nct_i2c_transfer(unitp->pcf8574_hdl, tp);
18261708Sstevel 
18271708Sstevel 	if (ret != I2C_SUCCESS) {
18281708Sstevel 		return (ret);
18291708Sstevel 	}
18301708Sstevel 
18311708Sstevel 	value = tp->i2c_rbuf[0];
18321708Sstevel 
18331708Sstevel 	switch (unitp->pcf8574_type) {
18341708Sstevel 	case PCF8574_TYPE_PWRSUPP:
18351708Sstevel 	{
18361708Sstevel 		envctrl_pwrsupp_t *envp =
18377656SSherry.Moore@Sun.COM 		    (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
18381708Sstevel 
18391708Sstevel 		envp->ps_ok    = PCF8574_PS_FAULT(value);
18401708Sstevel 		envp->temp_ok  = PCF8574_PS_TEMPOK(value);
18411708Sstevel 		envp->psfan_ok = PCF8574_PS_FANOK(value);
18421708Sstevel 		envp->on_state = PCF8574_PS_ONOFF(value);
18431708Sstevel 		envp->ps_ver   = PCF8574_PS_TYPE(value);
18441708Sstevel 
18451708Sstevel 		if (envp->ps_ok || envp->temp_ok ||
18467656SSherry.Moore@Sun.COM 		    envp->psfan_ok || envp->on_state)
18471708Sstevel 			device_faulty = B_TRUE;
18481708Sstevel 
18491708Sstevel 		break;
18501708Sstevel 	}
18511708Sstevel 	case PCF8574_TYPE_FANTRAY:
18521708Sstevel 	{
18531708Sstevel 		envctrl_fantray_t *envp =
18547656SSherry.Moore@Sun.COM 		    (envctrl_fantray_t *)unitp->envctrl_kstat;
18551708Sstevel 
18561708Sstevel 		envp->fan_ver  = PCF8574_FAN_TYPE(value);
18571708Sstevel 		envp->fan_ok   = PCF8574_FAN_FAULT(value);
18581708Sstevel 		envp->fanspeed = PCF8574_FAN_FANSPD(value);
18591708Sstevel 
18601708Sstevel 		if (!envp->fan_ok)
18611708Sstevel 			device_faulty = B_TRUE; /* remember, 0 is faulty */
18621708Sstevel 
18631708Sstevel 		break;
18641708Sstevel 	}
18651708Sstevel 	default:
18661708Sstevel 		break;
18671708Sstevel 	}
18681708Sstevel 	/*
18691708Sstevel 	 * Mask interrupt, if intron = 0.
18701708Sstevel 	 */
18711708Sstevel 	if (!intron || device_faulty == B_TRUE) {
18721708Sstevel 		tp->i2c_wbuf[0] |= PCF8574_INTRMASK_BIT;
18731708Sstevel 	}
18741708Sstevel 
18751708Sstevel 	tp->i2c_wlen = 1;
18761708Sstevel 	tp->i2c_rlen = 0;
18771708Sstevel 	tp->i2c_flags = I2C_WR;
18781708Sstevel 
18791708Sstevel 	unitp->i2c_status = nct_i2c_transfer(unitp->pcf8574_hdl, tp);
18801708Sstevel 
18811708Sstevel 	return (unitp->i2c_status);
18821708Sstevel }
1883