xref: /titanic_51/usr/src/uts/sun4u/montecarlo/io/pcf8574_nct.c (revision 89b43686db1fe9681d80a7cf5662730cb9378cae)
103831d35Sstevel /*
203831d35Sstevel  * CDDL HEADER START
303831d35Sstevel  *
403831d35Sstevel  * The contents of this file are subject to the terms of the
519397407SSherry Moore  * Common Development and Distribution License (the "License").
619397407SSherry Moore  * You may not use this file except in compliance with the License.
703831d35Sstevel  *
803831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel  * See the License for the specific language governing permissions
1103831d35Sstevel  * and limitations under the License.
1203831d35Sstevel  *
1303831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel  *
1903831d35Sstevel  * CDDL HEADER END
2003831d35Sstevel  */
2107d06da5SSurya Prakki 
2203831d35Sstevel /*
2307d06da5SSurya Prakki  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2403831d35Sstevel  * Use is subject to license terms.
25*89b43686SBayard Bell  * Copyright (c) 2011 Bayard G. Bell. All rights reserved.
2603831d35Sstevel  */
2703831d35Sstevel 
2803831d35Sstevel #include <sys/param.h>
2903831d35Sstevel #include <sys/types.h>
3003831d35Sstevel #include <sys/signal.h>
3103831d35Sstevel #include <sys/errno.h>
3203831d35Sstevel #include <sys/file.h>
3303831d35Sstevel #include <sys/termio.h>
3403831d35Sstevel #include <sys/termios.h>
3503831d35Sstevel #include <sys/cmn_err.h>
3603831d35Sstevel #include <sys/stream.h>
3703831d35Sstevel #include <sys/strsun.h>
3803831d35Sstevel #include <sys/stropts.h>
3903831d35Sstevel #include <sys/strtty.h>
4003831d35Sstevel #include <sys/debug.h>
4103831d35Sstevel #include <sys/eucioctl.h>
4203831d35Sstevel #include <sys/cred.h>
4303831d35Sstevel #include <sys/uio.h>
4403831d35Sstevel #include <sys/stat.h>
4503831d35Sstevel #include <sys/kmem.h>
4603831d35Sstevel 
4703831d35Sstevel #include <sys/ddi.h>
4803831d35Sstevel #include <sys/sunddi.h>
4903831d35Sstevel #include <sys/obpdefs.h>
5003831d35Sstevel #include <sys/conf.h>		/* req. by dev_ops flags MTSAFE etc. */
5103831d35Sstevel #include <sys/modctl.h>		/* for modldrv */
5203831d35Sstevel #include <sys/stat.h>		/* ddi_create_minor_node S_IFCHR */
5303831d35Sstevel #include <sys/open.h>		/* for open params.	 */
5403831d35Sstevel #include <sys/uio.h>		/* for read/write */
5503831d35Sstevel 
5603831d35Sstevel #include <sys/i2c/misc/i2c_svc.h>
5703831d35Sstevel #include <sys/mct_topology.h>
5803831d35Sstevel #include <sys/envctrl_gen.h>	/* must be before netract_gen.h	*/
5903831d35Sstevel #include <sys/netract_gen.h>
6003831d35Sstevel #include <sys/pcf8574_nct.h>
6103831d35Sstevel #include <sys/scsb_cbi.h>
6203831d35Sstevel 
6303831d35Sstevel #ifdef DEBUG
6403831d35Sstevel #define	dbg_print(level, str) cmn_err(level, str);
6503831d35Sstevel 	static int	pcf8574_debug = 0x00000102;
6603831d35Sstevel #else
6703831d35Sstevel #define	dbg_print(level, str) {; }
6803831d35Sstevel #endif
6903831d35Sstevel 
7003831d35Sstevel #define	CV_LOCK(retval)				\
7103831d35Sstevel {									\
7203831d35Sstevel 	mutex_enter(&unitp->umutex);	\
7303831d35Sstevel 	while (unitp->pcf8574_flags == PCF8574_BUSY) {	\
7403831d35Sstevel 		if (cv_wait_sig(&unitp->pcf8574_cv,	\
7503831d35Sstevel 					&unitp->umutex) <= 0) {	\
7603831d35Sstevel 			mutex_exit(&unitp->umutex);		\
7703831d35Sstevel 			return (retval);		\
7803831d35Sstevel 		}							\
7903831d35Sstevel 	}								\
8003831d35Sstevel 	unitp->pcf8574_flags = PCF8574_BUSY;	\
8103831d35Sstevel 	mutex_exit(&unitp->umutex);		\
8203831d35Sstevel }
8303831d35Sstevel 
8403831d35Sstevel #define	CV_UNLOCK					\
8503831d35Sstevel {									\
8603831d35Sstevel 	mutex_enter(&unitp->umutex);	\
8703831d35Sstevel 	unitp->pcf8574_flags = 0;		\
8803831d35Sstevel 	cv_signal(&unitp->pcf8574_cv);	\
8903831d35Sstevel 	mutex_exit(&unitp->umutex);		\
9003831d35Sstevel }
9103831d35Sstevel 
9203831d35Sstevel static int nct_p10fan_patch = 0;	/* Fan patch for P1.0 */
9303831d35Sstevel static void	*pcf8574_soft_statep;
9403831d35Sstevel 
9503831d35Sstevel /*
9603831d35Sstevel  * cb ops (only need open,close,read,write,ioctl)
9703831d35Sstevel  */
9803831d35Sstevel static int	pcf8574_open(dev_t *, int, int, cred_t *);
9903831d35Sstevel static int	pcf8574_close(dev_t, int, int, cred_t *);
10003831d35Sstevel static int	pcf8574_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
10103831d35Sstevel static int	pcf8574_read(dev_t dev, struct uio *uiop, cred_t *cred_p);
10203831d35Sstevel static int	pcf8574_chpoll(dev_t, short, int, short *, struct pollhead **);
10303831d35Sstevel static uint_t	pcf8574_intr(caddr_t arg);
10403831d35Sstevel static int pcf8574_io(dev_t, struct uio *, int);
10503831d35Sstevel 
10603831d35Sstevel static struct cb_ops pcf8574_cbops = {
10703831d35Sstevel 	pcf8574_open,		/* open */
10803831d35Sstevel 	pcf8574_close,		/* close */
10903831d35Sstevel 	nodev,				/* strategy */
11003831d35Sstevel 	nodev,				/* print */
11103831d35Sstevel 	nodev,				/* dump */
11203831d35Sstevel 	pcf8574_read,		/* read */
11303831d35Sstevel 	nodev,				/* write */
11403831d35Sstevel 	pcf8574_ioctl,		/* ioctl */
11503831d35Sstevel 	nodev,				/* devmap */
11603831d35Sstevel 	nodev,				/* mmap */
11703831d35Sstevel 	nodev,				/* segmap */
11803831d35Sstevel 	pcf8574_chpoll,		/* poll */
11903831d35Sstevel 	ddi_prop_op,		/* cb_prop_op */
12003831d35Sstevel 	NULL,				/* streamtab */
12103831d35Sstevel 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
12203831d35Sstevel 	CB_REV,				/* rev */
12303831d35Sstevel 	nodev,				/* int (*cb_aread)() */
12403831d35Sstevel 	nodev				/* int (*cb_awrite)() */
12503831d35Sstevel };
12603831d35Sstevel 
12703831d35Sstevel /*
12803831d35Sstevel  * dev ops
12903831d35Sstevel  */
13003831d35Sstevel static int pcf8574_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
13103831d35Sstevel static int pcf8574_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
13203831d35Sstevel 
13303831d35Sstevel /* kstat routines */
13403831d35Sstevel static int pcf8574_add_kstat(struct pcf8574_unit *, scsb_fru_status_t);
13503831d35Sstevel static void pcf8574_delete_kstat(struct pcf8574_unit *);
13603831d35Sstevel static int pcf8574_kstat_update(kstat_t *, int);
13703831d35Sstevel static int pcf8574_read_chip(struct pcf8574_unit *unitp,
13803831d35Sstevel 	uint16_t size);
13903831d35Sstevel static int pcf8574_write_chip(struct pcf8574_unit *unitp,
14003831d35Sstevel 	uint16_t size, uint8_t bitpattern);
14103831d35Sstevel static int pcf8574_read_props(struct pcf8574_unit *unitp);
14203831d35Sstevel static int pcf8574_init_chip(struct pcf8574_unit *unitp, int);
14303831d35Sstevel /*
14403831d35Sstevel  * SCSB callback function
14503831d35Sstevel  */
14603831d35Sstevel static void pcf8574_callback(void *, scsb_fru_event_t, scsb_fru_status_t);
14703831d35Sstevel extern int scsb_intr_register(uint_t (*intr_handler)(caddr_t), caddr_t,
14803831d35Sstevel 		fru_id_t);
14903831d35Sstevel extern int scsb_intr_unregister(fru_id_t);
15003831d35Sstevel 
15103831d35Sstevel extern int nct_i2c_transfer(i2c_client_hdl_t i2c_hdl, i2c_transfer_t *i2c_tran);
15203831d35Sstevel 
15303831d35Sstevel static struct dev_ops pcf8574_ops = {
15403831d35Sstevel 	DEVO_REV,
15503831d35Sstevel 	0,
15603831d35Sstevel 	ddi_getinfo_1to1,
15703831d35Sstevel 	nulldev,
15803831d35Sstevel 	nulldev,
15903831d35Sstevel 	pcf8574_attach,
16003831d35Sstevel 	pcf8574_detach,
16103831d35Sstevel 	nodev,
16203831d35Sstevel 	&pcf8574_cbops,
16319397407SSherry Moore 	NULL,				/* bus_ops */
16419397407SSherry Moore 	NULL,				/* power */
16519397407SSherry Moore 	ddi_quiesce_not_supported,	/* devo_quiesce */
16603831d35Sstevel };
16703831d35Sstevel 
16803831d35Sstevel extern struct mod_ops mod_driverops;
16903831d35Sstevel 
17003831d35Sstevel static struct modldrv pcf8574_modldrv = {
17103831d35Sstevel 	&mod_driverops,		/* type of module - driver */
17219397407SSherry Moore 	"Netract pcf8574 (gpio)",
17303831d35Sstevel 	&pcf8574_ops,
17403831d35Sstevel };
17503831d35Sstevel 
17603831d35Sstevel static struct modlinkage pcf8574_modlinkage = {
17703831d35Sstevel 	MODREV_1,
17803831d35Sstevel 	&pcf8574_modldrv,
17903831d35Sstevel 	0
18003831d35Sstevel };
18103831d35Sstevel 
18203831d35Sstevel int
18303831d35Sstevel _init(void)
18403831d35Sstevel {
18503831d35Sstevel 	register int    error;
18603831d35Sstevel 
18703831d35Sstevel 	error = mod_install(&pcf8574_modlinkage);
18803831d35Sstevel 	if (!error) {
18903831d35Sstevel 		(void) ddi_soft_state_init(&pcf8574_soft_statep,
19003831d35Sstevel 		    sizeof (struct pcf8574_unit), PCF8574_MAX_DEVS);
19103831d35Sstevel 	}
19203831d35Sstevel 
19303831d35Sstevel 	return (error);
19403831d35Sstevel }
19503831d35Sstevel 
19603831d35Sstevel int
19703831d35Sstevel _fini(void)
19803831d35Sstevel {
19903831d35Sstevel 	register int    error;
20003831d35Sstevel 
20103831d35Sstevel 	error = mod_remove(&pcf8574_modlinkage);
20203831d35Sstevel 	if (!error)
20303831d35Sstevel 		ddi_soft_state_fini(&pcf8574_soft_statep);
20403831d35Sstevel 
20503831d35Sstevel 	return (error);
20603831d35Sstevel }
20703831d35Sstevel 
20803831d35Sstevel int
20903831d35Sstevel _info(struct modinfo *modinfop)
21003831d35Sstevel {
21103831d35Sstevel 	return (mod_info(&pcf8574_modlinkage, modinfop));
21203831d35Sstevel }
21303831d35Sstevel 
21403831d35Sstevel /*ARGSUSED*/
21503831d35Sstevel static int
21603831d35Sstevel pcf8574_open(dev_t *devp, int flags, int otyp, cred_t *credp)
21703831d35Sstevel {
21803831d35Sstevel 	struct pcf8574_unit *unitp;
21903831d35Sstevel 	register int    instance;
22003831d35Sstevel 	int err = DDI_SUCCESS;
22103831d35Sstevel 
22203831d35Sstevel 	instance = getminor(*devp);
22303831d35Sstevel 	if (instance < 0) {
22403831d35Sstevel 		return (ENXIO);
22503831d35Sstevel 	}
22603831d35Sstevel 
22703831d35Sstevel 	unitp = (struct pcf8574_unit *)
22803831d35Sstevel 	    ddi_get_soft_state(pcf8574_soft_statep, instance);
22903831d35Sstevel 
23003831d35Sstevel 	if (unitp == NULL) {
23103831d35Sstevel 		return (ENXIO);
23203831d35Sstevel 	}
23303831d35Sstevel 
23403831d35Sstevel 	if (otyp != OTYP_CHR) {
23503831d35Sstevel 		return (EINVAL);
23603831d35Sstevel 	}
23703831d35Sstevel 
23803831d35Sstevel 	mutex_enter(&unitp->umutex);
23903831d35Sstevel 
24003831d35Sstevel 	if (flags & FEXCL) {
24103831d35Sstevel 		if (unitp->pcf8574_oflag != 0) {
24203831d35Sstevel 			err = EBUSY;
24303831d35Sstevel 		} else {
24403831d35Sstevel 			unitp->pcf8574_oflag = FEXCL;
24503831d35Sstevel 		}
24603831d35Sstevel 	} else {
24703831d35Sstevel 		if (unitp->pcf8574_oflag == FEXCL) {
24803831d35Sstevel 			err = EBUSY;
24903831d35Sstevel 		} else {
25003831d35Sstevel 			unitp->pcf8574_oflag = FOPEN;
25103831d35Sstevel 		}
25203831d35Sstevel 	}
25303831d35Sstevel 
25403831d35Sstevel 	mutex_exit(&unitp->umutex);
25503831d35Sstevel 
25603831d35Sstevel 	return (err);
25703831d35Sstevel }
25803831d35Sstevel 
25903831d35Sstevel /*ARGSUSED*/
26003831d35Sstevel static int
26103831d35Sstevel pcf8574_close(dev_t dev, int flags, int otyp, cred_t *credp)
26203831d35Sstevel {
26303831d35Sstevel 	struct pcf8574_unit *unitp;
26403831d35Sstevel 	register int    instance;
26503831d35Sstevel 
26603831d35Sstevel #ifdef lint
26703831d35Sstevel 	flags = flags;
26803831d35Sstevel 	otyp = otyp;
26903831d35Sstevel #endif
27003831d35Sstevel 
27103831d35Sstevel 	instance = getminor(dev);
27203831d35Sstevel 
27303831d35Sstevel 	if (instance < 0) {
27403831d35Sstevel 		return (ENXIO);
27503831d35Sstevel 	}
27603831d35Sstevel 
27703831d35Sstevel 	unitp = (struct pcf8574_unit *)
27803831d35Sstevel 	    ddi_get_soft_state(pcf8574_soft_statep, instance);
27903831d35Sstevel 
28003831d35Sstevel 	if (unitp == NULL) {
28103831d35Sstevel 		return (ENXIO);
28203831d35Sstevel 	}
28303831d35Sstevel 
28403831d35Sstevel 	mutex_enter(&unitp->umutex);
28503831d35Sstevel 
28603831d35Sstevel 	unitp->pcf8574_oflag = 0;
28703831d35Sstevel 
28803831d35Sstevel 	mutex_exit(&unitp->umutex);
28903831d35Sstevel 
29003831d35Sstevel 	return (DDI_SUCCESS);
29103831d35Sstevel }
29203831d35Sstevel 
29303831d35Sstevel 
29403831d35Sstevel /*ARGSUSED*/
29503831d35Sstevel static int
29603831d35Sstevel pcf8574_read(dev_t dev, struct uio *uiop, cred_t *cred_p)
29703831d35Sstevel {
29803831d35Sstevel 	return (pcf8574_io(dev, uiop, B_READ));
29903831d35Sstevel }
30003831d35Sstevel 
30103831d35Sstevel static int
30203831d35Sstevel pcf8574_io(dev_t dev, struct uio *uiop, int rw)
30303831d35Sstevel {
30403831d35Sstevel 	struct pcf8574_unit *unitp;
30503831d35Sstevel 	register int    instance;
30603831d35Sstevel 	uint16_t	bytes_to_rw;
30703831d35Sstevel 	int	err = DDI_SUCCESS;
30803831d35Sstevel 
30903831d35Sstevel 	err = 0;
31003831d35Sstevel 	instance = getminor(dev);
31103831d35Sstevel 
31203831d35Sstevel 	if (instance < 0) {
31303831d35Sstevel 		return (ENXIO);
31403831d35Sstevel 	}
31503831d35Sstevel 
31603831d35Sstevel 	unitp = (struct pcf8574_unit *)
31703831d35Sstevel 	    ddi_get_soft_state(pcf8574_soft_statep, instance);
31803831d35Sstevel 	if (unitp == NULL) {
31903831d35Sstevel 		return (ENXIO);
32003831d35Sstevel 	}
32103831d35Sstevel 	if ((bytes_to_rw = uiop->uio_resid) > PCF8574_TRAN_SIZE) {
32203831d35Sstevel 		return (EINVAL);
32303831d35Sstevel 	}
32403831d35Sstevel 
32503831d35Sstevel 	CV_LOCK(EINTR)
32603831d35Sstevel 
32703831d35Sstevel 	if (rw == B_WRITE) {
32803831d35Sstevel 		err = uiomove(unitp->i2c_tran->i2c_wbuf,
32903831d35Sstevel 		    bytes_to_rw, UIO_WRITE, uiop);
33003831d35Sstevel 
33103831d35Sstevel 		if (!err) {
33203831d35Sstevel 			err = pcf8574_write_chip(unitp, bytes_to_rw,
33303831d35Sstevel 			    unitp->writemask);
33403831d35Sstevel 		}
33503831d35Sstevel 
33603831d35Sstevel 	} else {
33703831d35Sstevel 			err = pcf8574_read_chip(unitp, bytes_to_rw);
33803831d35Sstevel 			if (!err) {
33903831d35Sstevel 				err = uiomove(unitp->i2c_tran->i2c_rbuf,
34003831d35Sstevel 				    bytes_to_rw, UIO_READ, uiop);
34103831d35Sstevel 			}
34203831d35Sstevel 	}
34303831d35Sstevel 
34403831d35Sstevel 	CV_UNLOCK
34503831d35Sstevel 	if (err)
34603831d35Sstevel 		err = EIO;
34703831d35Sstevel 
34803831d35Sstevel 	return (err);
34903831d35Sstevel }
35003831d35Sstevel 
35103831d35Sstevel static int
35203831d35Sstevel pcf8574_do_resume(dev_info_t *dip)
35303831d35Sstevel {
35403831d35Sstevel 	int instance = ddi_get_instance(dip);
35503831d35Sstevel 	struct pcf8574_unit *unitp =
35603831d35Sstevel 	    ddi_get_soft_state(pcf8574_soft_statep, instance);
35703831d35Sstevel 
35803831d35Sstevel 	if (unitp == NULL) {
35903831d35Sstevel 		return (ENXIO);
36003831d35Sstevel 	}
36103831d35Sstevel 
36203831d35Sstevel 	CV_UNLOCK
36303831d35Sstevel 
36403831d35Sstevel 	return (DDI_SUCCESS);
36503831d35Sstevel }
36603831d35Sstevel 
36703831d35Sstevel static int
36803831d35Sstevel pcf8574_do_detach(dev_info_t *dip)
36903831d35Sstevel {
37003831d35Sstevel 	struct pcf8574_unit *unitp;
37103831d35Sstevel 	int instance;
37203831d35Sstevel 	uint_t attach_flag;
37303831d35Sstevel 
37403831d35Sstevel 	instance = ddi_get_instance(dip);
37503831d35Sstevel 	unitp = ddi_get_soft_state(pcf8574_soft_statep, instance);
37603831d35Sstevel 
37703831d35Sstevel 	attach_flag = unitp->attach_flag;
37803831d35Sstevel 
37903831d35Sstevel 	if (attach_flag & PCF8574_INTR_ADDED) {
38007d06da5SSurya Prakki 		(void) scsb_intr_unregister(
38107d06da5SSurya Prakki 		    (fru_id_t)unitp->props.slave_address);
38203831d35Sstevel 	}
38303831d35Sstevel 
38403831d35Sstevel 	if (attach_flag & PCF8574_KSTAT_INIT) {
38503831d35Sstevel 		pcf8574_delete_kstat(unitp);
38603831d35Sstevel 	}
38703831d35Sstevel 
38803831d35Sstevel 	if (attach_flag & PCF8574_LOCK_INIT) {
38903831d35Sstevel 		mutex_destroy(&unitp->umutex);
39003831d35Sstevel 		cv_destroy(&unitp->pcf8574_cv);
39103831d35Sstevel 	}
39203831d35Sstevel 
39303831d35Sstevel 	scsb_fru_unregister((void *)unitp,
39403831d35Sstevel 	    (fru_id_t)unitp->props.slave_address);
39503831d35Sstevel 
39603831d35Sstevel 	if (attach_flag & PCF8574_ALLOC_TRANSFER) {
39703831d35Sstevel 		/*
39803831d35Sstevel 		 * restore the lengths to allocated lengths
39903831d35Sstevel 		 * before freeing.
40003831d35Sstevel 		 */
40103831d35Sstevel 		unitp->i2c_tran->i2c_wlen = MAX_WLEN;
40203831d35Sstevel 		unitp->i2c_tran->i2c_rlen = MAX_RLEN;
40303831d35Sstevel 		i2c_transfer_free(unitp->pcf8574_hdl, unitp->i2c_tran);
40403831d35Sstevel 	}
40503831d35Sstevel 
40603831d35Sstevel 	if (attach_flag & PCF8574_REGISTER_CLIENT) {
40703831d35Sstevel 		i2c_client_unregister(unitp->pcf8574_hdl);
40803831d35Sstevel 	}
40903831d35Sstevel 
41003831d35Sstevel 	if (attach_flag & PCF8574_MINORS_CREATED) {
41103831d35Sstevel 		ddi_remove_minor_node(dip, NULL);
41203831d35Sstevel 	}
41303831d35Sstevel 
41403831d35Sstevel 	if (attach_flag & PCF8574_PROPS_READ) {
41503831d35Sstevel 		if (unitp->pcf8574_type == PCF8574_ADR_CPUVOLTAGE &&
41603831d35Sstevel 		    unitp->props.num_chans_used != 0) {
41703831d35Sstevel 			ddi_prop_free(unitp->props.channels_in_use);
41803831d35Sstevel 		} else {
41907d06da5SSurya Prakki 			(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
42003831d35Sstevel 			    "interrupt-priorities");
42103831d35Sstevel 		}
42203831d35Sstevel 	}
42303831d35Sstevel 
42403831d35Sstevel 	if (attach_flag & PCF8574_SOFT_STATE_ALLOC) {
42503831d35Sstevel 		ddi_soft_state_free(pcf8574_soft_statep, instance);
42603831d35Sstevel 	}
42703831d35Sstevel 
42803831d35Sstevel 	return (DDI_SUCCESS);
42903831d35Sstevel }
43003831d35Sstevel 
43103831d35Sstevel /*
43203831d35Sstevel  * NOTE****
43303831d35Sstevel  * The OBP will create device tree node for all I2C devices which
43403831d35Sstevel  * may be present in a system. This means, even if the device is
43503831d35Sstevel  * not physically present, the device tree node exists. We also
43603831d35Sstevel  * will succeed the attach routine, since currently there is no
43703831d35Sstevel  * hotplug support in the I2C bus, and the FRUs need to be hot
43803831d35Sstevel  * swappable. Only during an I2C transaction we figure out whether
43903831d35Sstevel  * the particular I2C device is actually present in the system
44003831d35Sstevel  * by looking at the system controller board register. The fantray
44103831d35Sstevel  * and power-supply devices may be swapped any time after system
44203831d35Sstevel  * reboot, and the way we can make sure that the device is attached
44303831d35Sstevel  * to the driver, is by always keeping the driver loaded, and report
44403831d35Sstevel  * an error during the actual transaction.
44503831d35Sstevel  */
44603831d35Sstevel static int
44703831d35Sstevel pcf8574_do_attach(dev_info_t *dip)
44803831d35Sstevel {
44903831d35Sstevel 	register struct pcf8574_unit *unitp;
45003831d35Sstevel 	int instance;
45103831d35Sstevel 	char name[MAXNAMELEN];
45203831d35Sstevel 	int		i;
45303831d35Sstevel 	pcf8574_channel_t *chp;
45403831d35Sstevel 	scsb_fru_status_t	dev_presence;
45503831d35Sstevel 
45603831d35Sstevel 	instance = ddi_get_instance(dip);
45703831d35Sstevel #ifdef DEBUG
45803831d35Sstevel 	if (pcf8574_debug & 0x04)
45903831d35Sstevel 		cmn_err(CE_NOTE, "pcf8574_attach: instance=%d\n",
46003831d35Sstevel 		    instance);
46103831d35Sstevel #endif /* DEBUG */
46203831d35Sstevel 
46303831d35Sstevel 	if (ddi_soft_state_zalloc(pcf8574_soft_statep, instance) !=
46403831d35Sstevel 	    DDI_SUCCESS) {
46503831d35Sstevel 		return (DDI_FAILURE);
46603831d35Sstevel 	}
46703831d35Sstevel 	unitp = ddi_get_soft_state(pcf8574_soft_statep, instance);
46803831d35Sstevel 
46903831d35Sstevel 	if (unitp == NULL) {
47003831d35Sstevel 		ddi_soft_state_free(pcf8574_soft_statep, instance);
47103831d35Sstevel 		return (DDI_FAILURE);
47203831d35Sstevel 	}
47303831d35Sstevel 
47403831d35Sstevel 	unitp->dip = dip;
47503831d35Sstevel 
47603831d35Sstevel 	unitp->attach_flag = PCF8574_SOFT_STATE_ALLOC;
47703831d35Sstevel 
47803831d35Sstevel 	if (pcf8574_read_props(unitp) != DDI_PROP_SUCCESS) {
47903831d35Sstevel 		ddi_soft_state_free(pcf8574_soft_statep, instance);
48003831d35Sstevel 		return (DDI_FAILURE);
48103831d35Sstevel 	}
48203831d35Sstevel 
48303831d35Sstevel 	unitp->attach_flag |= PCF8574_PROPS_READ;
48403831d35Sstevel 
48503831d35Sstevel 	/*
48603831d35Sstevel 	 * Set the current operating mode to NORMAL_MODE.
48703831d35Sstevel 	 */
48803831d35Sstevel 	unitp->current_mode = ENVCTRL_NORMAL_MODE;
48903831d35Sstevel 
49007d06da5SSurya Prakki 	(void) snprintf(unitp->pcf8574_name, PCF8574_NAMELEN,
49103831d35Sstevel 	    "%s%d", ddi_driver_name(dip), instance);
49203831d35Sstevel 
49303831d35Sstevel 	if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP) {
49403831d35Sstevel 		(void) sprintf(name, "pwrsuppply");
49503831d35Sstevel 		if (ddi_create_minor_node(dip, name, S_IFCHR, instance,
49603831d35Sstevel 		    PCF8574_NODE_TYPE, NULL) == DDI_FAILURE) {
49703831d35Sstevel 			ddi_remove_minor_node(dip, NULL);
49807d06da5SSurya Prakki 			(void) pcf8574_do_detach(dip);
49903831d35Sstevel 
50003831d35Sstevel 			return (DDI_FAILURE);
50103831d35Sstevel 		}
50203831d35Sstevel 	}
50303831d35Sstevel 	else
50403831d35Sstevel 	if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY) {
50503831d35Sstevel 		(void) sprintf(name, "fantray");
50603831d35Sstevel 		if (ddi_create_minor_node(dip, name, S_IFCHR, instance,
50703831d35Sstevel 		    PCF8574_NODE_TYPE, NULL) == DDI_FAILURE) {
50803831d35Sstevel 			ddi_remove_minor_node(dip, NULL);
50907d06da5SSurya Prakki 			(void) pcf8574_do_detach(dip);
51003831d35Sstevel 
51103831d35Sstevel 			return (DDI_FAILURE);
51203831d35Sstevel 		}
51303831d35Sstevel 	}
51403831d35Sstevel 	else
51503831d35Sstevel 	if (unitp->pcf8574_type == PCF8574_TYPE_CPUVOLTAGE) {
51603831d35Sstevel 		(void) sprintf(name, "cpuvoltage");
51703831d35Sstevel 		if (ddi_create_minor_node(dip, name, S_IFCHR, instance,
51803831d35Sstevel 		    PCF8574_NODE_TYPE, NULL) == DDI_FAILURE) {
51903831d35Sstevel 			ddi_remove_minor_node(dip, NULL);
52007d06da5SSurya Prakki 			(void) pcf8574_do_detach(dip);
52103831d35Sstevel 
52203831d35Sstevel 			return (DDI_FAILURE);
52303831d35Sstevel 		}
52403831d35Sstevel 	} else {
52503831d35Sstevel 		return (DDI_FAILURE);
52603831d35Sstevel 	}
52703831d35Sstevel 
52803831d35Sstevel 	unitp->attach_flag |= PCF8574_MINORS_CREATED;
52903831d35Sstevel 
53003831d35Sstevel 	/*
53103831d35Sstevel 	 * Now we need read/write masks since all the 8574 bits can be either
53203831d35Sstevel 	 * read/written, but some ports are intended to be RD/WR only, or RW
53303831d35Sstevel 	 * If no channels-in-use propoerty, set default values.
53403831d35Sstevel 	 */
53503831d35Sstevel 	if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY) {
53603831d35Sstevel 		unitp->readmask = PCF8574_FAN_READMASK;
53703831d35Sstevel 		unitp->writemask = PCF8574_FAN_WRITEMASK;
53803831d35Sstevel 	}
53903831d35Sstevel 	if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP) {
54003831d35Sstevel 		unitp->readmask = PCF8574_PS_READMASK;
54103831d35Sstevel 		unitp->writemask = PCF8574_PS_WRITEMASK;
54203831d35Sstevel 	}
54303831d35Sstevel 
54403831d35Sstevel 	for (i = unitp->props.num_chans_used,
54503831d35Sstevel 	    chp = unitp->props.channels_in_use; i; --i, ++chp) {
54603831d35Sstevel 		unitp->readmask |= (uint8_t)(
54703831d35Sstevel 		    (chp->io_dir == I2C_PROP_IODIR_IN ||
54803831d35Sstevel 		    chp->io_dir == I2C_PROP_IODIR_INOUT) << chp->port);
54903831d35Sstevel 		unitp->writemask |= (uint8_t)(
55003831d35Sstevel 		    (chp->io_dir == I2C_PROP_IODIR_OUT ||
55103831d35Sstevel 		    chp->io_dir == I2C_PROP_IODIR_INOUT) << chp->port);
55203831d35Sstevel 	}
55303831d35Sstevel 
55403831d35Sstevel #ifdef DEBUG
55503831d35Sstevel 	cmn_err(CE_NOTE, "pcf8574_do_attach: readmask = 0x%x \
55603831d35Sstevel 		writemask = 0x%x\n", unitp->readmask, unitp->writemask);
55703831d35Sstevel #endif /* DEBUG */
55803831d35Sstevel 
55903831d35Sstevel 	if (i2c_client_register(dip, &unitp->pcf8574_hdl)
56003831d35Sstevel 	    != I2C_SUCCESS) {
56107d06da5SSurya Prakki 		(void) pcf8574_do_detach(dip);
56203831d35Sstevel 		return (DDI_FAILURE);
56303831d35Sstevel 	}
56403831d35Sstevel 	unitp->attach_flag |= PCF8574_REGISTER_CLIENT;
56503831d35Sstevel 
56603831d35Sstevel 	/*
56703831d35Sstevel 	 * Allocate the I2C_transfer structure. The same structure
56803831d35Sstevel 	 * is used throughout the driver.
56903831d35Sstevel 	 */
57003831d35Sstevel 	if (i2c_transfer_alloc(unitp->pcf8574_hdl, &unitp->i2c_tran,
57107d06da5SSurya Prakki 	    MAX_WLEN, MAX_RLEN, KM_SLEEP) != I2C_SUCCESS) {
57207d06da5SSurya Prakki 		(void) pcf8574_do_detach(dip);
57303831d35Sstevel 		return (DDI_FAILURE);
57403831d35Sstevel 	}
57503831d35Sstevel 	unitp->attach_flag |= PCF8574_ALLOC_TRANSFER;
57603831d35Sstevel 
57703831d35Sstevel 	/*
57803831d35Sstevel 	 * To begin with we set the mode to I2C_RD.
57903831d35Sstevel 	 */
58003831d35Sstevel 	unitp->i2c_tran->i2c_flags = I2C_RD;
58103831d35Sstevel 	unitp->i2c_tran->i2c_version = I2C_XFER_REV;
58203831d35Sstevel 
58303831d35Sstevel 	/*
58403831d35Sstevel 	 * Set the busy flag and open flag to 0.
58503831d35Sstevel 	 */
58603831d35Sstevel 	unitp->pcf8574_flags = 0;
58703831d35Sstevel 	unitp->pcf8574_oflag = 0;
58803831d35Sstevel 
58903831d35Sstevel 	mutex_init(&unitp->umutex, NULL, MUTEX_DRIVER, NULL);
59003831d35Sstevel 	cv_init(&unitp->pcf8574_cv, NULL, CV_DRIVER, NULL);
59103831d35Sstevel 
59203831d35Sstevel 	unitp->attach_flag |= PCF8574_LOCK_INIT;
59303831d35Sstevel 
59403831d35Sstevel 	/*
59503831d35Sstevel 	 * Register out callback function with the SCSB driver, and save
59603831d35Sstevel 	 * the returned value to check that the device instance exists.
59703831d35Sstevel 	 */
59803831d35Sstevel 	dev_presence = scsb_fru_register(pcf8574_callback, (void *)unitp,
59903831d35Sstevel 	    (fru_id_t)unitp->props.slave_address);
60003831d35Sstevel 	if (dev_presence == FRU_NOT_AVAILABLE) {
60103831d35Sstevel 		scsb_fru_unregister((void *)unitp,
60203831d35Sstevel 		    (fru_id_t)unitp->props.slave_address);
60303831d35Sstevel 	}
60403831d35Sstevel 
60503831d35Sstevel 	/*
60603831d35Sstevel 	 * Add the kstats. First we need to get the property values
60703831d35Sstevel 	 * depending on the device type. For example, for the fan
60803831d35Sstevel 	 * tray there will be a different set of properties, and there
60903831d35Sstevel 	 * will be another for the powersupplies, and another one for
61003831d35Sstevel 	 * the CPU voltage monitor. Initialize the kstat structures with
61103831d35Sstevel 	 * these values.
61203831d35Sstevel 	 */
61303831d35Sstevel 
61403831d35Sstevel 	if (pcf8574_add_kstat(unitp, dev_presence) != DDI_SUCCESS) {
61507d06da5SSurya Prakki 		(void) pcf8574_do_detach(dip);
61603831d35Sstevel 		return (DDI_FAILURE);
61703831d35Sstevel 	}
61803831d35Sstevel 
61903831d35Sstevel 	unitp->attach_flag |= PCF8574_KSTAT_INIT;
62003831d35Sstevel 
62103831d35Sstevel 	/*
62203831d35Sstevel 	 * Due to observed behavior on Solaris 8, the handler must be
62303831d35Sstevel 	 * registered before any interrupts are enabled,
62403831d35Sstevel 	 * in spite of what the ddi_get_iblock_cookie() manual says.
62503831d35Sstevel 	 * As per the HW/SW spec, by default interrupts are disabled.
62603831d35Sstevel 	 */
62703831d35Sstevel 
62803831d35Sstevel 	if (dev_presence == FRU_PRESENT) { /* program the chip */
62907d06da5SSurya Prakki 		(void) pcf8574_init_chip(unitp, 0);   /* Disable intr first */
63003831d35Sstevel 	}
63103831d35Sstevel 
63203831d35Sstevel 	if (unitp->pcf8574_canintr == PCF8574_INTR_ON) {
63303831d35Sstevel #ifdef DEBUG
63403831d35Sstevel 		if (pcf8574_debug & 0x0004)
63503831d35Sstevel 			cmn_err(CE_NOTE, "registering pcf9574 interrupt "
63603831d35Sstevel 			    "handler");
63703831d35Sstevel #endif /* DEBUG */
63803831d35Sstevel 		if (scsb_intr_register(pcf8574_intr, (void *)unitp,
63903831d35Sstevel 		    (fru_id_t)unitp->props.slave_address) == DDI_SUCCESS) {
64003831d35Sstevel 			unitp->pcf8574_canintr |= PCF8574_INTR_ENABLED;
64103831d35Sstevel 			unitp->attach_flag |= PCF8574_INTR_ADDED;
64203831d35Sstevel 		} else {
64307d06da5SSurya Prakki 			(void) pcf8574_do_detach(dip);
64403831d35Sstevel 			return (DDI_FAILURE);
64503831d35Sstevel 		}
64603831d35Sstevel 	}
64703831d35Sstevel 
64803831d35Sstevel 	ddi_report_dev(dip);
64903831d35Sstevel 
65003831d35Sstevel 	return (DDI_SUCCESS);
65103831d35Sstevel }
65203831d35Sstevel 
65303831d35Sstevel static int
65403831d35Sstevel pcf8574_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
65503831d35Sstevel {
65603831d35Sstevel 	switch (cmd) {
65703831d35Sstevel 	case DDI_ATTACH:
65803831d35Sstevel 		return (pcf8574_do_attach(dip));
65903831d35Sstevel 	case DDI_RESUME:
66003831d35Sstevel 		return (pcf8574_do_resume(dip));
66103831d35Sstevel 	default:
66203831d35Sstevel 		return (DDI_FAILURE);
66303831d35Sstevel 	}
66403831d35Sstevel }
66503831d35Sstevel 
66603831d35Sstevel static int
66703831d35Sstevel pcf8574_do_suspend(dev_info_t *dip)
66803831d35Sstevel {
66903831d35Sstevel 	int instance = ddi_get_instance(dip);
67003831d35Sstevel 	struct pcf8574_unit *unitp =
67103831d35Sstevel 	    ddi_get_soft_state(pcf8574_soft_statep, instance);
67203831d35Sstevel 
67303831d35Sstevel 	if (unitp == NULL) {
67403831d35Sstevel 		return (ENXIO);
67503831d35Sstevel 	}
67603831d35Sstevel 
67703831d35Sstevel 	/*
67803831d35Sstevel 	 * Set the busy flag so that future transactions block
67903831d35Sstevel 	 * until resume.
68003831d35Sstevel 	 */
68103831d35Sstevel 	CV_LOCK(ENXIO)
68203831d35Sstevel 
68303831d35Sstevel 	return (DDI_SUCCESS);
68403831d35Sstevel }
68503831d35Sstevel 
68603831d35Sstevel static int
68703831d35Sstevel pcf8574_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
68803831d35Sstevel {
68903831d35Sstevel 	switch (cmd) {
69003831d35Sstevel 	case DDI_DETACH:
69103831d35Sstevel 		return (pcf8574_do_detach(dip));
69203831d35Sstevel 	case DDI_SUSPEND:
69303831d35Sstevel 		return (pcf8574_do_suspend(dip));
69403831d35Sstevel 	default:
69503831d35Sstevel 		return (DDI_FAILURE);
69603831d35Sstevel 	}
69703831d35Sstevel }
69803831d35Sstevel 
69903831d35Sstevel static int
70003831d35Sstevel pcf8574_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
70103831d35Sstevel     struct pollhead **phpp)
70203831d35Sstevel {
70303831d35Sstevel 	struct pcf8574_unit	*unitp;
70403831d35Sstevel 	int		instance;
70503831d35Sstevel 
70603831d35Sstevel 	instance = getminor(dev);
70703831d35Sstevel 	if ((unitp = (struct pcf8574_unit *)ddi_get_soft_state(
70803831d35Sstevel 	    pcf8574_soft_statep, instance)) == NULL) {
70903831d35Sstevel 		return (ENXIO);
71003831d35Sstevel 	}
71103831d35Sstevel 	*reventsp = 0;
71203831d35Sstevel 	mutex_enter(&unitp->umutex);
71303831d35Sstevel 	if (unitp->poll_event) {
71403831d35Sstevel 		*reventsp = unitp->poll_event;
71503831d35Sstevel 		unitp->poll_event = 0;
71603831d35Sstevel 	} else if ((events & POLLIN) && !anyyet)
71703831d35Sstevel 		*phpp = &unitp->poll;
71803831d35Sstevel 	mutex_exit(&unitp->umutex);
71903831d35Sstevel 	return (0);
72003831d35Sstevel }
72103831d35Sstevel 
72203831d35Sstevel /*
72303831d35Sstevel  * In normal scenarios, this function should never get called.
72403831d35Sstevel  * But, we will still come back and call this function if scsb
72503831d35Sstevel  * interrupt sources does not indicate an scsb interrupt. We may
72603831d35Sstevel  * come to this situation when SunVTS env4test is independently
72703831d35Sstevel  * changing the device registers.
72803831d35Sstevel  */
72903831d35Sstevel uint_t
73003831d35Sstevel pcf8574_intr(caddr_t arg)
73103831d35Sstevel {
73203831d35Sstevel 	int			ic;
73303831d35Sstevel 	uint8_t value;
73403831d35Sstevel 	struct pcf8574_unit	*unitp = (struct pcf8574_unit *)(void *)arg;
73503831d35Sstevel 	scsb_fru_status_t	dev_presence;
73603831d35Sstevel 	i2c_transfer_t *tp = unitp->i2c_tran;
73703831d35Sstevel 
73803831d35Sstevel 	ic = DDI_INTR_CLAIMED;
73903831d35Sstevel #ifdef DEBUG
74003831d35Sstevel 	cmn_err(CE_NOTE, " In the interrupt service routine, %x",
74103831d35Sstevel 	    unitp->props.slave_address);
74203831d35Sstevel #endif
74303831d35Sstevel 
74403831d35Sstevel 	/*
74503831d35Sstevel 	 * Initiate an I2C transaction to find out
74603831d35Sstevel 	 * whether this is the device which interrupted.
74703831d35Sstevel 	 */
74803831d35Sstevel 	mutex_enter(&unitp->umutex);
74903831d35Sstevel 	while (unitp->pcf8574_flags == PCF8574_BUSY) {
75003831d35Sstevel 		if (cv_wait_sig(&unitp->pcf8574_cv, &unitp->umutex) <= 0) {
75103831d35Sstevel 			mutex_exit(&unitp->umutex);
75203831d35Sstevel 			return (DDI_INTR_UNCLAIMED);
75303831d35Sstevel 		}
75403831d35Sstevel 	}
75503831d35Sstevel 
75603831d35Sstevel 	unitp->pcf8574_flags = PCF8574_BUSY;
75703831d35Sstevel 	mutex_exit(&unitp->umutex);
75803831d35Sstevel 
75903831d35Sstevel 	switch (unitp->pcf8574_type) {
76003831d35Sstevel 		case PCF8574_TYPE_CPUVOLTAGE: {
76103831d35Sstevel 			dev_presence = FRU_PRESENT;
76203831d35Sstevel 			break;
76303831d35Sstevel 		}
76403831d35Sstevel 		case PCF8574_TYPE_PWRSUPP: {
76503831d35Sstevel 			envctrl_pwrsupp_t *envp =
76603831d35Sstevel 			    (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
76703831d35Sstevel 			dev_presence = envp->ps_present;
76803831d35Sstevel 			break;
76903831d35Sstevel 		}
77003831d35Sstevel 		case PCF8574_TYPE_FANTRAY: {
77103831d35Sstevel 			envctrl_fantray_t *envp =
77203831d35Sstevel 			    (envctrl_fantray_t *)unitp->envctrl_kstat;
77303831d35Sstevel 			dev_presence = envp->fan_present;
77403831d35Sstevel 			break;
77503831d35Sstevel 		}
77603831d35Sstevel 	}
77703831d35Sstevel 	if (dev_presence != FRU_PRESENT) {
77803831d35Sstevel 		ic = DDI_INTR_UNCLAIMED;
77903831d35Sstevel 		goto intr_exit;
78003831d35Sstevel 	}
78103831d35Sstevel 	if (pcf8574_read_chip(unitp, 1) != I2C_SUCCESS) {
78203831d35Sstevel 		ic = DDI_INTR_UNCLAIMED;
78303831d35Sstevel 		goto intr_exit;
78403831d35Sstevel 	}
78503831d35Sstevel 	value = unitp->i2c_tran->i2c_rbuf[0];
78603831d35Sstevel 	/*
78703831d35Sstevel 	 * If interrupt is already masked, return
78803831d35Sstevel 	 */
78903831d35Sstevel 	if (value & PCF8574_INTRMASK_BIT) {
79003831d35Sstevel 		ic = DDI_INTR_UNCLAIMED;
79103831d35Sstevel 		goto intr_exit;
79203831d35Sstevel 	}
79303831d35Sstevel 
79403831d35Sstevel 	/*
79503831d35Sstevel 	 * In case a fault bit is set, claim the interrupt.
79603831d35Sstevel 	 */
79703831d35Sstevel 	switch (unitp->pcf8574_type) {
79803831d35Sstevel 	case PCF8574_TYPE_PWRSUPP:
79903831d35Sstevel 	{
80003831d35Sstevel 		envctrl_pwrsupp_t *envp =
80103831d35Sstevel 		    (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
80203831d35Sstevel 
80303831d35Sstevel 		if (PCF8574_PS_FAULT(value) ||
80403831d35Sstevel 		    PCF8574_PS_TEMPOK(value) ||
80503831d35Sstevel 		    PCF8574_PS_ONOFF(value) ||
80603831d35Sstevel 		    PCF8574_PS_FANOK(value)) {
80703831d35Sstevel 
80803831d35Sstevel 			envp->ps_ok =		PCF8574_PS_FAULT(value);
80903831d35Sstevel 			envp->temp_ok =		PCF8574_PS_TEMPOK(value);
81003831d35Sstevel 			envp->psfan_ok =	PCF8574_PS_FANOK(value);
81103831d35Sstevel 			envp->on_state =	PCF8574_PS_ONOFF(value);
81203831d35Sstevel 			envp->ps_ver =		PCF8574_PS_TYPE(value);
81303831d35Sstevel 
81403831d35Sstevel 			tp->i2c_wbuf[0] =
81503831d35Sstevel 			    PCF8574_PS_DEFAULT | PCF8574_PS_MASKINTR;
81603831d35Sstevel 			tp->i2c_wlen = 1;
81703831d35Sstevel 			tp->i2c_rlen = 0;
81803831d35Sstevel 			tp->i2c_flags = I2C_WR;
81903831d35Sstevel 
82003831d35Sstevel 			unitp->i2c_status =
82103831d35Sstevel 			    nct_i2c_transfer(unitp->pcf8574_hdl, tp);
82203831d35Sstevel 
82303831d35Sstevel 			unitp->poll_event = POLLIN;
82403831d35Sstevel 			pollwakeup(&unitp->poll, POLLIN);
82503831d35Sstevel 		} else {
82603831d35Sstevel 			ic = DDI_INTR_UNCLAIMED;
82703831d35Sstevel 		}
82803831d35Sstevel 	}
82903831d35Sstevel 	break;
83003831d35Sstevel 
83103831d35Sstevel 	case PCF8574_TYPE_FANTRAY:
83203831d35Sstevel 	{
83303831d35Sstevel 		envctrl_fantray_t *envp =
83403831d35Sstevel 		    (envctrl_fantray_t *)unitp->envctrl_kstat;
83503831d35Sstevel 
83603831d35Sstevel 		if (!PCF8574_FAN_FAULT(value)) {
83703831d35Sstevel 
83803831d35Sstevel 			envp->fan_ver = 	PCF8574_FAN_TYPE(value);
83903831d35Sstevel 			envp->fan_ok = 		PCF8574_FAN_FAULT(value);
84003831d35Sstevel 			envp->fanspeed =  	PCF8574_FAN_FANSPD(value);
84103831d35Sstevel 
84203831d35Sstevel 			tp->i2c_wbuf[0] =
84303831d35Sstevel 			    PCF8574_FAN_DEFAULT | PCF8574_FAN_MASKINTR;
84403831d35Sstevel 			tp->i2c_wlen = 1;
84503831d35Sstevel 			tp->i2c_rlen = 0;
84603831d35Sstevel 			tp->i2c_flags = I2C_WR;
84703831d35Sstevel 
84803831d35Sstevel 			unitp->i2c_status =
84903831d35Sstevel 			    nct_i2c_transfer(unitp->pcf8574_hdl, tp);
85003831d35Sstevel 
85103831d35Sstevel 			unitp->poll_event = POLLIN;
85203831d35Sstevel 			pollwakeup(&unitp->poll, POLLIN);
85303831d35Sstevel 
85403831d35Sstevel 		} else {
85503831d35Sstevel 			ic = DDI_INTR_UNCLAIMED;
85603831d35Sstevel 		}
85703831d35Sstevel 	}
85803831d35Sstevel 	break;
85903831d35Sstevel 
86003831d35Sstevel 	default:
86103831d35Sstevel 		ic = DDI_INTR_UNCLAIMED;
86203831d35Sstevel 	} /* switch */
86303831d35Sstevel 
86403831d35Sstevel intr_exit:
86503831d35Sstevel 	mutex_enter(&unitp->umutex);
86603831d35Sstevel 	unitp->pcf8574_flags = 0;
86703831d35Sstevel 	cv_signal(&unitp->pcf8574_cv);
86803831d35Sstevel 	mutex_exit(&unitp->umutex);
86903831d35Sstevel 
87003831d35Sstevel 	return (ic);
87103831d35Sstevel }
87203831d35Sstevel 
87303831d35Sstevel static int
87403831d35Sstevel call_copyin(caddr_t arg, struct pcf8574_unit *unitp, int mode)
87503831d35Sstevel {
87603831d35Sstevel 	uchar_t *wbuf;
87703831d35Sstevel 	uchar_t *rbuf;
87803831d35Sstevel 	i2c_transfer_t i2ct;
87903831d35Sstevel 	i2c_transfer_t *i2ctp = unitp->i2c_tran;
88003831d35Sstevel 
88103831d35Sstevel 
88203831d35Sstevel 	if (ddi_copyin((void *)arg, (caddr_t)&i2ct,
88303831d35Sstevel 	    sizeof (i2c_transfer_t), mode) != DDI_SUCCESS) {
88403831d35Sstevel 		return (I2C_FAILURE);
88503831d35Sstevel 	}
88603831d35Sstevel 
88703831d35Sstevel 	/*
88803831d35Sstevel 	 * Save the read and write buffer pointers in the transfer
88903831d35Sstevel 	 * structure, otherwise these will get overwritten when we
89003831d35Sstevel 	 * do a bcopy. Restore once done.
89103831d35Sstevel 	 */
89203831d35Sstevel 
89303831d35Sstevel 	wbuf = i2ctp->i2c_wbuf;
89403831d35Sstevel 	rbuf = i2ctp->i2c_rbuf;
89503831d35Sstevel 
89603831d35Sstevel 	bcopy(&i2ct, i2ctp, sizeof (i2c_transfer_t));
89703831d35Sstevel 
89803831d35Sstevel 	i2ctp->i2c_wbuf = wbuf;
89903831d35Sstevel 	i2ctp->i2c_rbuf = rbuf;
90003831d35Sstevel 
90103831d35Sstevel 	/*
90203831d35Sstevel 	 * copyin the read and write buffers to the saved buffers.
90303831d35Sstevel 	 */
90403831d35Sstevel 
90503831d35Sstevel 	if (i2ct.i2c_wlen != 0) {
90603831d35Sstevel 		if (ddi_copyin(i2ct.i2c_wbuf, (caddr_t)i2ctp->i2c_wbuf,
90703831d35Sstevel 		    i2ct.i2c_wlen, mode) != DDI_SUCCESS) {
90803831d35Sstevel 				return (I2C_FAILURE);
90903831d35Sstevel 		}
91003831d35Sstevel 	}
91103831d35Sstevel 
91203831d35Sstevel 	return (I2C_SUCCESS);
91303831d35Sstevel }
91403831d35Sstevel 
91503831d35Sstevel static int
91603831d35Sstevel call_copyout(caddr_t arg, struct pcf8574_unit *unitp, int mode)
91703831d35Sstevel {
91803831d35Sstevel 	i2c_transfer_t i2ct;
91903831d35Sstevel 	i2c_transfer_t *i2ctp = unitp->i2c_tran;
92003831d35Sstevel 
92103831d35Sstevel 	/*
92203831d35Sstevel 	 * We will copyout the last three fields only, skipping
92303831d35Sstevel 	 * the remaining ones, before copying the rbuf to the
92403831d35Sstevel 	 * user buffer.
92503831d35Sstevel 	 */
92603831d35Sstevel 
92703831d35Sstevel 	int uskip = sizeof (i2c_transfer_t) - 3*sizeof (int16_t),
92803831d35Sstevel 	    kskip = sizeof (i2c_transfer_t) - 3*sizeof (int16_t);
92903831d35Sstevel 
93003831d35Sstevel 	/*
93103831d35Sstevel 	 * First copyin the user structure to the temporary i2ct,
93203831d35Sstevel 	 * so that we have the wbuf and rbuf addresses in it.
93303831d35Sstevel 	 */
93403831d35Sstevel 
93503831d35Sstevel 	uskip = sizeof (i2c_transfer_t) - 3 * (sizeof (uint16_t));
93603831d35Sstevel 
93703831d35Sstevel 	/*
93803831d35Sstevel 	 * copyout the last three out fields now.
93903831d35Sstevel 	 */
94003831d35Sstevel 
94103831d35Sstevel 	if (ddi_copyout((void *)((intptr_t)i2ctp+kskip), (void *)
94203831d35Sstevel 	    ((intptr_t)arg + uskip), 3*sizeof (uint16_t), mode)
94303831d35Sstevel 	    != DDI_SUCCESS) {
94403831d35Sstevel 		return (I2C_FAILURE);
94503831d35Sstevel 		}
94603831d35Sstevel 
94703831d35Sstevel 	/*
94803831d35Sstevel 	 * In case we have something to write, get the address of the read
94903831d35Sstevel 	 * buffer.
95003831d35Sstevel 	 */
95103831d35Sstevel 
95203831d35Sstevel 	if (i2ctp->i2c_rlen > i2ctp->i2c_r_resid) {
95303831d35Sstevel 
95403831d35Sstevel 		if (ddi_copyin((void *)arg, &i2ct,
95503831d35Sstevel 		    sizeof (i2c_transfer_t), mode) != DDI_SUCCESS) {
95603831d35Sstevel 			return (I2C_FAILURE);
95703831d35Sstevel 		}
95803831d35Sstevel 
95903831d35Sstevel 		/*
96003831d35Sstevel 		 * copyout the read buffer to the saved user buffer in i2ct.
96103831d35Sstevel 		 */
96203831d35Sstevel 
96303831d35Sstevel 		if (ddi_copyout(i2ctp->i2c_rbuf, i2ct.i2c_rbuf,
96403831d35Sstevel 		    i2ctp->i2c_rlen - i2ctp->i2c_r_resid, mode)
96503831d35Sstevel 		    != DDI_SUCCESS) {
96603831d35Sstevel 			return (I2C_FAILURE);
96703831d35Sstevel 		}
96803831d35Sstevel 	}
96903831d35Sstevel 
97003831d35Sstevel 	return (I2C_SUCCESS);
97103831d35Sstevel }
97203831d35Sstevel 
97303831d35Sstevel /*ARGSUSED*/
97403831d35Sstevel static int
97503831d35Sstevel pcf8574_ioctl(dev_t dev, int cmd, intptr_t arg,
97603831d35Sstevel 		int mode, cred_t *credp, int *rvalp)
97703831d35Sstevel {
97803831d35Sstevel 	struct pcf8574_unit *unitp;
97903831d35Sstevel 	register int    instance;
98003831d35Sstevel 	int err = 0;
98103831d35Sstevel 	uint8_t value, inval, outval;
98203831d35Sstevel 	scsb_fru_status_t dev_presence;
98303831d35Sstevel 
98403831d35Sstevel 	instance = getminor(dev);
98503831d35Sstevel 
98603831d35Sstevel 	if (instance < 0) {
98703831d35Sstevel 		return (ENXIO);
98803831d35Sstevel 	}
98903831d35Sstevel 	unitp = (struct pcf8574_unit *)
99003831d35Sstevel 	    ddi_get_soft_state(pcf8574_soft_statep, instance);
99103831d35Sstevel 
99203831d35Sstevel 	if (unitp == NULL) {
99303831d35Sstevel 		return (ENXIO);
99403831d35Sstevel 	}
99503831d35Sstevel 
99603831d35Sstevel 	dev_presence =
99703831d35Sstevel 	    scsb_fru_status((uchar_t)unitp->props.slave_address);
99803831d35Sstevel 
99903831d35Sstevel 	CV_LOCK(EINTR)
100003831d35Sstevel 
100103831d35Sstevel 	switch (cmd) {
100203831d35Sstevel 	case ENVC_IOC_INTRMASK:
100303831d35Sstevel 	if (dev_presence == FRU_NOT_PRESENT) {
100403831d35Sstevel 		break;
100503831d35Sstevel 	}
100603831d35Sstevel 
100703831d35Sstevel 	if (ddi_copyin((caddr_t)arg, (caddr_t)&inval,
100803831d35Sstevel 	    sizeof (uint8_t), mode) != DDI_SUCCESS) {
100903831d35Sstevel 		err = EFAULT;
101003831d35Sstevel 		break;
101103831d35Sstevel 	}
101203831d35Sstevel 
101303831d35Sstevel 	if (inval != 0 && inval != 1) {
101403831d35Sstevel 		err = EINVAL;
101503831d35Sstevel 	} else {
101603831d35Sstevel 		unitp->i2c_tran->i2c_wbuf[0] =
101703831d35Sstevel 		    PCF8574_INT_MASK(inval);
101803831d35Sstevel 		if (pcf8574_write_chip(unitp, 1, PCF8574_INTRMASK_BIT)
101903831d35Sstevel 		    != I2C_SUCCESS) {
102003831d35Sstevel 			err = EFAULT;
102103831d35Sstevel 		}
102203831d35Sstevel 	}
102303831d35Sstevel 	break;
102403831d35Sstevel 
102503831d35Sstevel 	case ENVC_IOC_SETFAN:
102603831d35Sstevel 	if (unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) {
102703831d35Sstevel 		err = EINVAL;
102803831d35Sstevel 		break;
102903831d35Sstevel 	}
103003831d35Sstevel 	if (dev_presence == FRU_NOT_PRESENT) {
103103831d35Sstevel 		err = EINVAL;
103203831d35Sstevel 		break;
103303831d35Sstevel 	}
103403831d35Sstevel 	if (ddi_copyin((caddr_t)arg, (caddr_t)&inval, sizeof (uint8_t),
103503831d35Sstevel 	    mode) != DDI_SUCCESS) {
103603831d35Sstevel 			err = EFAULT;
103703831d35Sstevel 			break;
103803831d35Sstevel 	}
103903831d35Sstevel 	if (inval != PCF8574_FAN_SPEED_LOW &&
104003831d35Sstevel 	    inval != PCF8574_FAN_SPEED_HIGH) {
104103831d35Sstevel 		err = EINVAL;
104203831d35Sstevel 		break;
104303831d35Sstevel 	}
104403831d35Sstevel 
104503831d35Sstevel 	unitp->i2c_tran->i2c_wbuf[0] = PCF8574_FAN_SPEED(inval);
104603831d35Sstevel 
104703831d35Sstevel 	if (pcf8574_write_chip(unitp, 1, PCF8574_FANSPEED_BIT)
104803831d35Sstevel 	    != I2C_SUCCESS) {
104903831d35Sstevel 		err = EFAULT;
105003831d35Sstevel 	}
105103831d35Sstevel 	break;
105203831d35Sstevel 
105303831d35Sstevel 	case ENVC_IOC_SETSTATUS:
105403831d35Sstevel 	/*
105503831d35Sstevel 	 * Allow this ioctl only in DIAG mode.
105603831d35Sstevel 	 */
105703831d35Sstevel 	if (unitp->current_mode != ENVCTRL_DIAG_MODE) {
105803831d35Sstevel 		err = EINVAL;
105903831d35Sstevel 	} else {
106003831d35Sstevel 		if (dev_presence == FRU_NOT_PRESENT) {
106103831d35Sstevel 			err = EINVAL;
106203831d35Sstevel 			break;
106303831d35Sstevel 		}
106403831d35Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)&inval,
106503831d35Sstevel 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
106603831d35Sstevel 			err = EFAULT;
106703831d35Sstevel 		} else {
106803831d35Sstevel 			unitp->i2c_tran->i2c_wbuf[0] = inval & 0xff;
106903831d35Sstevel 			if (pcf8574_write_chip(unitp, 1, 0xff)
107003831d35Sstevel 			    != I2C_SUCCESS) {
107103831d35Sstevel 				err = EFAULT;
107203831d35Sstevel 			}
107303831d35Sstevel 		}
107403831d35Sstevel 	}
107503831d35Sstevel 	break;
107603831d35Sstevel 
107703831d35Sstevel 	case ENVC_IOC_GETFAN:
107803831d35Sstevel 	case ENVC_IOC_GETSTATUS:
107903831d35Sstevel 	case ENVC_IOC_GETTYPE:
108003831d35Sstevel 	case ENVC_IOC_GETFAULT:
108103831d35Sstevel 	case ENVC_IOC_PSTEMPOK:
108203831d35Sstevel 	case ENVC_IOC_PSFANOK:
108303831d35Sstevel 	case ENVC_IOC_PSONOFF: {
108403831d35Sstevel 		if (dev_presence == FRU_NOT_PRESENT) {
108503831d35Sstevel 			err = EINVAL;
108603831d35Sstevel 			break;
108703831d35Sstevel 		}
108803831d35Sstevel 		if (pcf8574_read_chip(unitp, 1)
108903831d35Sstevel 		    != I2C_SUCCESS) {
109003831d35Sstevel 			err = EFAULT;
109103831d35Sstevel 			break;
109203831d35Sstevel 		}
109303831d35Sstevel 		value = unitp->i2c_tran->i2c_rbuf[0];
109403831d35Sstevel 		if (cmd == ENVC_IOC_GETFAN) {
109503831d35Sstevel 			if (unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) {
109603831d35Sstevel 				err = EINVAL;
109703831d35Sstevel 				break;
109803831d35Sstevel 			} else {
109903831d35Sstevel 				outval = PCF8574_FAN_FANSPD(value);
110003831d35Sstevel 			}
110103831d35Sstevel 		}
110203831d35Sstevel 		else
110303831d35Sstevel 		if (cmd == ENVC_IOC_GETSTATUS) {
110403831d35Sstevel 			outval = value;
110503831d35Sstevel 		}
110603831d35Sstevel 		else
110703831d35Sstevel 		if (cmd == ENVC_IOC_GETTYPE) {
110803831d35Sstevel 			if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP)
110903831d35Sstevel 				outval = PCF8574_PS_TYPE(value);
111003831d35Sstevel 			if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY)
111103831d35Sstevel 				outval = PCF8574_FAN_TYPE(value);
111203831d35Sstevel 		}
111303831d35Sstevel 		else
111403831d35Sstevel 		if (cmd == ENVC_IOC_GETFAULT) {
111503831d35Sstevel 			if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP)
111603831d35Sstevel 				outval = PCF8574_PS_FAULT(value);
111703831d35Sstevel 			if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY)
111803831d35Sstevel 				outval = PCF8574_PS_FAULT(value);
111903831d35Sstevel 		}
112003831d35Sstevel 		else
112103831d35Sstevel 		if (cmd == ENVC_IOC_PSTEMPOK) {
112203831d35Sstevel 			outval = PCF8574_PS_TEMPOK(value);
112303831d35Sstevel 		}
112403831d35Sstevel 		else
112503831d35Sstevel 		if (cmd == ENVC_IOC_PSFANOK) {
112603831d35Sstevel 			outval = PCF8574_PS_FANOK(value);
112703831d35Sstevel 		}
112803831d35Sstevel 		else
112903831d35Sstevel 		if (cmd == ENVC_IOC_PSONOFF) {
113003831d35Sstevel 			outval = PCF8574_PS_ONOFF(value);
113103831d35Sstevel 		} else {
113203831d35Sstevel 			outval = 0;
113303831d35Sstevel 		}
113403831d35Sstevel 
113503831d35Sstevel 		if (ddi_copyout((caddr_t)&outval, (caddr_t)arg,
113603831d35Sstevel 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
113703831d35Sstevel 			err = EFAULT;
113803831d35Sstevel 		}
113903831d35Sstevel 	}
114003831d35Sstevel 	break;
114103831d35Sstevel 
114203831d35Sstevel 	case ENVC_IOC_GETMODE: {
114303831d35Sstevel 		uint8_t curr_mode = unitp->current_mode;
114403831d35Sstevel 
114503831d35Sstevel 		if (ddi_copyout((caddr_t)&curr_mode, (caddr_t)arg,
114603831d35Sstevel 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
114703831d35Sstevel 			err = EFAULT;
114803831d35Sstevel 		}
114903831d35Sstevel 		break;
115003831d35Sstevel 	}
115103831d35Sstevel 
115203831d35Sstevel 	case ENVC_IOC_SETMODE: {
115303831d35Sstevel 		uint8_t curr_mode;
115403831d35Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)&curr_mode,
115503831d35Sstevel 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
115603831d35Sstevel 				err = EFAULT;
115703831d35Sstevel 				break;
115803831d35Sstevel 		}
115903831d35Sstevel 		if (curr_mode == ENVCTRL_DIAG_MODE ||
116003831d35Sstevel 		    curr_mode == ENVCTRL_NORMAL_MODE) {
116103831d35Sstevel 			unitp->current_mode = curr_mode; /* Don't do anything */
116203831d35Sstevel 		}
116303831d35Sstevel 		break;
116403831d35Sstevel 	}
116503831d35Sstevel 
116603831d35Sstevel 
116703831d35Sstevel 	case I2CDEV_TRAN:
116803831d35Sstevel 		if (call_copyin((caddr_t)arg, unitp, mode) != DDI_SUCCESS) {
116903831d35Sstevel 			err = EFAULT;
117003831d35Sstevel 			break;
117103831d35Sstevel 		}
117203831d35Sstevel 		unitp->i2c_status = err =
117303831d35Sstevel 		    nct_i2c_transfer(unitp->pcf8574_hdl, unitp->i2c_tran);
117403831d35Sstevel 
117503831d35Sstevel 		if (err != I2C_SUCCESS) {
117603831d35Sstevel 			err = EIO;
117703831d35Sstevel 		} else {
117803831d35Sstevel 			if (call_copyout((caddr_t)arg, unitp, mode)
117903831d35Sstevel 			    != DDI_SUCCESS) {
118003831d35Sstevel 				err = EFAULT;
118103831d35Sstevel 				break;
118203831d35Sstevel 			}
118303831d35Sstevel 		}
118403831d35Sstevel 		break;
118503831d35Sstevel 
118603831d35Sstevel 	default:
118703831d35Sstevel 		err = EINVAL;
118803831d35Sstevel 	}
118903831d35Sstevel 
119003831d35Sstevel 	CV_UNLOCK
119103831d35Sstevel 
119203831d35Sstevel 	return (err);
119303831d35Sstevel }
119403831d35Sstevel 
119503831d35Sstevel static int
119603831d35Sstevel pcf8574_add_kstat(struct pcf8574_unit *unitp, scsb_fru_status_t dev_presence)
119703831d35Sstevel {
119803831d35Sstevel 	char ksname[50];
119903831d35Sstevel 	int id;
120003831d35Sstevel 	uint8_t i2c_address = unitp->props.slave_address;
120103831d35Sstevel 
120203831d35Sstevel 	/*
120303831d35Sstevel 	 * We create the kstat depending on the device function,
120403831d35Sstevel 	 * allocate the kstat placeholder and initialize the
120503831d35Sstevel 	 * values.
120603831d35Sstevel 	 */
120703831d35Sstevel 	unitp->envctrl_kstat = NULL;
120803831d35Sstevel 	switch (unitp->pcf8574_type) {
120903831d35Sstevel 	case PCF8574_TYPE_CPUVOLTAGE:
121003831d35Sstevel 	{
121103831d35Sstevel 		if ((unitp->kstatp = kstat_create(I2C_PCF8574_NAME,
121203831d35Sstevel 		    unitp->instance, I2C_KSTAT_CPUVOLTAGE, "misc",
121303831d35Sstevel 		    KSTAT_TYPE_RAW, sizeof (envctrl_cpuvoltage_t),
121403831d35Sstevel 		    KSTAT_FLAG_PERSISTENT)) != NULL) {
121503831d35Sstevel 
121603831d35Sstevel 			if ((unitp->envctrl_kstat = kmem_zalloc(
121703831d35Sstevel 			    sizeof (envctrl_cpuvoltage_t), KM_NOSLEEP)) ==
121803831d35Sstevel 			    NULL) {
121903831d35Sstevel 				kstat_delete(unitp->kstatp);
122003831d35Sstevel 				return (DDI_FAILURE);
122103831d35Sstevel 			}
122203831d35Sstevel 		} else {
122303831d35Sstevel 			return (DDI_FAILURE);
122403831d35Sstevel 		}
122503831d35Sstevel 
122603831d35Sstevel 		break;
122703831d35Sstevel 	}
122803831d35Sstevel 	case PCF8574_TYPE_PWRSUPP:
122903831d35Sstevel 	{
123003831d35Sstevel 		envctrl_pwrsupp_t *envp;
123103831d35Sstevel 		if (i2c_address == PCF8574_ADR_PWRSUPPLY1) {
123203831d35Sstevel 			id = 1;
123303831d35Sstevel 		} else if (i2c_address == PCF8574_ADR_PWRSUPPLY2) {
123403831d35Sstevel 			id = 2;
123503831d35Sstevel 		} else  {
123603831d35Sstevel 			id = i2c_address - PCF8574_ADR_PWRSUPPLY1;
123703831d35Sstevel 		}
123807d06da5SSurya Prakki 		(void) sprintf(ksname, "%s%d", I2C_KSTAT_PWRSUPPLY, id);
123903831d35Sstevel 		if ((unitp->kstatp = kstat_create(I2C_PCF8574_NAME,
124003831d35Sstevel 		    unitp->instance, ksname, "misc",
124103831d35Sstevel 		    KSTAT_TYPE_RAW, sizeof (envctrl_pwrsupp_t),
124203831d35Sstevel 		    KSTAT_FLAG_PERSISTENT)) != NULL) {
124303831d35Sstevel 
124403831d35Sstevel 			if ((unitp->envctrl_kstat = kmem_zalloc(
124503831d35Sstevel 			    sizeof (envctrl_pwrsupp_t), KM_NOSLEEP)) ==
124603831d35Sstevel 			    NULL) {
124703831d35Sstevel 				kstat_delete(unitp->kstatp);
124803831d35Sstevel 				return (DDI_FAILURE);
124903831d35Sstevel 			}
125003831d35Sstevel 			/*
125103831d35Sstevel 			 * Initialize the kstat fields. Need to initialize
125203831d35Sstevel 			 * the present field from SCSB info (dev_presence)
125303831d35Sstevel 			 */
125403831d35Sstevel 			envp = (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
125503831d35Sstevel 
125603831d35Sstevel 			envp->ps_present = dev_presence;
125703831d35Sstevel 			envp->ps_ok = 0;
125803831d35Sstevel 			envp->temp_ok = 0;
125903831d35Sstevel 			envp->psfan_ok = 0;
126003831d35Sstevel 			envp->on_state = 0;
126103831d35Sstevel 			envp->ps_ver = 0;
126203831d35Sstevel 		} else {
126303831d35Sstevel 			return (DDI_FAILURE);
126403831d35Sstevel 		}
126503831d35Sstevel 
126603831d35Sstevel 		break;
126703831d35Sstevel 	}
126803831d35Sstevel 	case PCF8574_TYPE_FANTRAY:
126903831d35Sstevel 	{
127003831d35Sstevel 		envctrl_fantray_t *envp;
127103831d35Sstevel 		if (i2c_address == PCF8574_ADR_FANTRAY1) {
127203831d35Sstevel 			id = 1;
127303831d35Sstevel 		} else if (i2c_address == PCF8574_ADR_FANTRAY2) {
127403831d35Sstevel 			id = 2;
127503831d35Sstevel 		} else  {
127603831d35Sstevel 			id = i2c_address - PCF8574_ADR_FANTRAY1;
127703831d35Sstevel 		}
127807d06da5SSurya Prakki 		(void) sprintf(ksname, "%s%d", I2C_KSTAT_FANTRAY, id);
127903831d35Sstevel 		if ((unitp->kstatp = kstat_create(I2C_PCF8574_NAME,
128003831d35Sstevel 		    unitp->instance, ksname, "misc",
128103831d35Sstevel 		    KSTAT_TYPE_RAW, sizeof (envctrl_fantray_t),
128203831d35Sstevel 		    KSTAT_FLAG_PERSISTENT | KSTAT_FLAG_WRITABLE)) != NULL) {
128303831d35Sstevel 
128403831d35Sstevel 			if ((unitp->envctrl_kstat = kmem_zalloc(
128503831d35Sstevel 			    sizeof (envctrl_fantray_t), KM_NOSLEEP)) ==
128603831d35Sstevel 			    NULL) {
128703831d35Sstevel 				kstat_delete(unitp->kstatp);
128803831d35Sstevel 				return (DDI_FAILURE);
128903831d35Sstevel 			}
129003831d35Sstevel 
129103831d35Sstevel 			/*
129203831d35Sstevel 			 * Initialize the kstat fields. Need to initialize
129303831d35Sstevel 			 * the present field from SCSB info (dev_presence)
129403831d35Sstevel 			 */
129503831d35Sstevel 			envp = (envctrl_fantray_t *)unitp->envctrl_kstat;
129603831d35Sstevel 
129703831d35Sstevel 			envp->fan_present = dev_presence;
129803831d35Sstevel 			envp->fan_ok = 0;
129903831d35Sstevel 			envp->fanspeed =  PCF8574_FAN_SPEED60;
130003831d35Sstevel 			envp->fan_ver = 0;
130103831d35Sstevel 		} else {
130203831d35Sstevel 			return (DDI_FAILURE);
130303831d35Sstevel 		}
130403831d35Sstevel 
130503831d35Sstevel 		break;
130603831d35Sstevel 	}
130703831d35Sstevel 	default:
130803831d35Sstevel 		return (DDI_FAILURE);
130903831d35Sstevel 	}
131003831d35Sstevel 
131103831d35Sstevel 	unitp->kstatp->ks_private = (void *)unitp;
131203831d35Sstevel 	unitp->kstatp->ks_update = pcf8574_kstat_update;
131303831d35Sstevel 
131403831d35Sstevel 	kstat_install(unitp->kstatp);
131503831d35Sstevel 
131603831d35Sstevel 	return (DDI_SUCCESS);
131703831d35Sstevel }
131803831d35Sstevel 
131903831d35Sstevel /*
132003831d35Sstevel  * This function reads a single byte from the pcf8574 chip, for use by the
132103831d35Sstevel  * kstat routines. The protocol for read will depend on the function.
132203831d35Sstevel  */
132303831d35Sstevel 
132403831d35Sstevel static int
132503831d35Sstevel pcf8574_read_chip(struct pcf8574_unit *unitp, uint16_t size)
132603831d35Sstevel {
132703831d35Sstevel 	int retval, i;
132803831d35Sstevel 	i2c_transfer_t *tp = unitp->i2c_tran;
132903831d35Sstevel 
133003831d35Sstevel 
133103831d35Sstevel 	tp->i2c_flags = I2C_RD;
133203831d35Sstevel 	tp->i2c_rlen = size;
133303831d35Sstevel 	tp->i2c_wlen = 0;
133403831d35Sstevel 
133503831d35Sstevel 	/*
133603831d35Sstevel 	 * Read the bytes from the pcf8574, mask off the
133703831d35Sstevel 	 * non-read bits and return the value. Block with
133803831d35Sstevel 	 * the driverwide lock.
133903831d35Sstevel 	 */
134003831d35Sstevel 	unitp->i2c_status = retval =
134103831d35Sstevel 	    nct_i2c_transfer(unitp->pcf8574_hdl, unitp->i2c_tran);
134203831d35Sstevel 
134303831d35Sstevel 	if (retval != I2C_SUCCESS) {
134403831d35Sstevel 		return (retval);
134503831d35Sstevel 	}
134603831d35Sstevel 
134703831d35Sstevel 	for (i = 0; i < size; i++) {
134803831d35Sstevel 		tp->i2c_rbuf[i] &= unitp->readmask;
134903831d35Sstevel 	}
135003831d35Sstevel 
135103831d35Sstevel 	return (I2C_SUCCESS);
135203831d35Sstevel }
135303831d35Sstevel 
135403831d35Sstevel /*
135503831d35Sstevel  * This function writes a single byte to the pcf8574 chip, for use by the
135603831d35Sstevel  * ioctl routines. The protocol for write will depend on the function.
135703831d35Sstevel  * The bitpattern tells which bits are being modified, by setting these
135803831d35Sstevel  * bits in bitpattern to 1, e.g for fanspeed, bitpattern = 0x08, fanspeed
135903831d35Sstevel  * and intr 0x0c, only intr 0x04.
136003831d35Sstevel  */
136103831d35Sstevel 
136203831d35Sstevel static int
136303831d35Sstevel pcf8574_write_chip(struct pcf8574_unit *unitp,
136403831d35Sstevel 		uint16_t size, uint8_t bitpattern)
136503831d35Sstevel {
136603831d35Sstevel 	i2c_transfer_t *tp = unitp->i2c_tran;
136703831d35Sstevel 	int i;
136803831d35Sstevel 
136903831d35Sstevel 		/*
137003831d35Sstevel 		 * pcf8574_write
137103831d35Sstevel 		 *
137203831d35Sstevel 		 * First read the byte, modify only the writable
137303831d35Sstevel 		 * ports, then write back the modified data.
137403831d35Sstevel 		 */
137503831d35Sstevel 		tp->i2c_wlen = 0;
137603831d35Sstevel 		tp->i2c_rlen = size;
137703831d35Sstevel 		tp->i2c_flags = I2C_RD;
137803831d35Sstevel 
137903831d35Sstevel 		unitp->i2c_status = nct_i2c_transfer(unitp->pcf8574_hdl, tp);
138003831d35Sstevel 
138103831d35Sstevel 		if (unitp->i2c_status != I2C_SUCCESS) {
138203831d35Sstevel 			return (I2C_FAILURE);
138303831d35Sstevel 		}
138403831d35Sstevel 
138503831d35Sstevel 		/*
138603831d35Sstevel 		 * Our concern is when we have to write only a few bits.
138703831d35Sstevel 		 * We need to make sure we write the same value to those
138803831d35Sstevel 		 * bit positions which does not appear in bitpattern.
138903831d35Sstevel 		 */
139003831d35Sstevel 
139103831d35Sstevel 		/*
139203831d35Sstevel 		 * 1) Ignore all bits than the one we are writing
139303831d35Sstevel 		 * 2) Now 0 the bits we intend to modify in the value
139403831d35Sstevel 		 * read from the chip, preserving all others.
139503831d35Sstevel 		 * 3) Now turn all non-writable ( read only/reserved )
139603831d35Sstevel 		 * bits to 1. The value now should contain:
139703831d35Sstevel 		 * 1 			in all non-writable bits.
139803831d35Sstevel 		 * 0 			in the bis(s) we intend to modify.
139903831d35Sstevel 		 * no change 	in the writable bits we don't modify.
140003831d35Sstevel 		 * 4) Now OR it with the bits we got before, i.e. after
140103831d35Sstevel 		 * ignoring all bits other than one we are writing.
140203831d35Sstevel 		 */
140303831d35Sstevel 
140403831d35Sstevel 		for (i = 0; i < size; i++) {
140503831d35Sstevel 			tp->i2c_rbuf[i] &= ~(bitpattern);
140603831d35Sstevel 
140703831d35Sstevel 			tp->i2c_rbuf[i] |= ~(unitp->writemask);
140803831d35Sstevel 
140903831d35Sstevel 			tp->i2c_wbuf[i] = tp->i2c_rbuf[i] |
141003831d35Sstevel 			    (tp->i2c_wbuf[i] & bitpattern);
141103831d35Sstevel 		}
141203831d35Sstevel 
141303831d35Sstevel 		tp->i2c_rlen = 0;
141403831d35Sstevel 		tp->i2c_wlen = size;
141503831d35Sstevel 		tp->i2c_flags = I2C_WR;
141603831d35Sstevel 
141703831d35Sstevel 		unitp->i2c_status = nct_i2c_transfer(unitp->pcf8574_hdl, tp);
141803831d35Sstevel 
141903831d35Sstevel 		return (unitp->i2c_status);
142003831d35Sstevel }
142103831d35Sstevel 
142203831d35Sstevel static int
142303831d35Sstevel pcf8574_kstat_update(kstat_t *ksp, int rw)
142403831d35Sstevel {
142503831d35Sstevel 	struct pcf8574_unit *unitp;
142603831d35Sstevel 	char *kstatp;
142703831d35Sstevel 	uint8_t value;
142803831d35Sstevel 	int err = DDI_SUCCESS;
142903831d35Sstevel 	scsb_fru_status_t	dev_presence;
143003831d35Sstevel 
143103831d35Sstevel 	unitp = (struct pcf8574_unit *)ksp->ks_private;
143203831d35Sstevel 	if (unitp->envctrl_kstat == NULL) { /* May be detaching */
143303831d35Sstevel 		return (err);
143403831d35Sstevel 	}
143503831d35Sstevel 
143603831d35Sstevel 	CV_LOCK(EINTR)
143703831d35Sstevel 
143803831d35Sstevel 	/*
143903831d35Sstevel 	 * Need to call scsb to find whether device is present.
144003831d35Sstevel 	 * For I2C devices, the I2C address is used as a FRU ID.
144103831d35Sstevel 	 */
144203831d35Sstevel 	if (unitp->pcf8574_type == PCF8574_TYPE_CPUVOLTAGE) {
144303831d35Sstevel 		dev_presence = FRU_PRESENT;
144403831d35Sstevel 	} else {
144503831d35Sstevel 		dev_presence =
144603831d35Sstevel 		    scsb_fru_status((uchar_t)unitp->props.slave_address);
144703831d35Sstevel 	}
144803831d35Sstevel 
144903831d35Sstevel 	kstatp = (char *)ksp->ks_data;
145003831d35Sstevel 
145103831d35Sstevel 	/*
145203831d35Sstevel 	 * We could have write on the power supply and the fantray
145303831d35Sstevel 	 * pcf8574 chips. For masking the interrupt on both, or
145403831d35Sstevel 	 * controlling the fan speed on the fantray. But write
145503831d35Sstevel 	 * will not be allowed through the kstat interface. For
145603831d35Sstevel 	 * the present field, call SCSB.
145703831d35Sstevel 	 */
145803831d35Sstevel 
145903831d35Sstevel 	if (rw == KSTAT_WRITE) {
146003831d35Sstevel 		if (unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) {
146103831d35Sstevel 			err = EACCES;
146203831d35Sstevel 			goto kstat_exit;
146303831d35Sstevel 		}
146403831d35Sstevel 		value = ((envctrl_fantray_t *)kstatp)->fanspeed;
146503831d35Sstevel 		if (value != PCF8574_FAN_SPEED_LOW &&
146603831d35Sstevel 		    value != PCF8574_FAN_SPEED_HIGH) {
146703831d35Sstevel 			err = EINVAL;
146803831d35Sstevel 			goto kstat_exit;
146903831d35Sstevel 		}
147003831d35Sstevel 
147103831d35Sstevel 		unitp->i2c_tran->i2c_wbuf[0] = PCF8574_FAN_SPEED(value);
147203831d35Sstevel 
147303831d35Sstevel 		if (dev_presence == FRU_PRESENT &&
147403831d35Sstevel 		    pcf8574_write_chip(unitp, 1, PCF8574_FANSPEED_BIT)
147503831d35Sstevel 		    != I2C_SUCCESS) {
147603831d35Sstevel 			err = EFAULT;
147703831d35Sstevel 			goto kstat_exit;
147803831d35Sstevel 		}
147903831d35Sstevel 
148003831d35Sstevel 	} else {
148103831d35Sstevel 		/*
148203831d35Sstevel 		 * First make sure that the FRU exists by checking the SCSB
148303831d35Sstevel 		 * dev_presence info.  If not present, set the change field,
148403831d35Sstevel 		 * clear the kstat fields and make sure the kstat *_present
148503831d35Sstevel 		 * field is set to dev_presence from the SCSB driver.
148603831d35Sstevel 		 */
148703831d35Sstevel 		if (dev_presence == FRU_PRESENT &&
148803831d35Sstevel 		    pcf8574_read_chip(unitp, 1) != I2C_SUCCESS) {
148903831d35Sstevel 			/*
149003831d35Sstevel 			 * Looks like a real IO error.
149103831d35Sstevel 			 */
149203831d35Sstevel 			err = EIO;
149303831d35Sstevel 			CV_UNLOCK
149403831d35Sstevel 
149503831d35Sstevel 			return (err);
149603831d35Sstevel 		}
149703831d35Sstevel 		if (dev_presence == FRU_PRESENT)
149803831d35Sstevel 			value = unitp->i2c_tran->i2c_rbuf[0];
149903831d35Sstevel 		else
150003831d35Sstevel 			value = 0;
150103831d35Sstevel 
150203831d35Sstevel 		switch (unitp->pcf8574_type) {
150303831d35Sstevel 		case PCF8574_TYPE_CPUVOLTAGE: {
150403831d35Sstevel 			envctrl_cpuvoltage_t *envp =
150503831d35Sstevel 			    (envctrl_cpuvoltage_t *)unitp->envctrl_kstat;
150603831d35Sstevel 			envp->value = value;
150703831d35Sstevel 			bcopy((caddr_t)envp, kstatp,
150803831d35Sstevel 			    sizeof (envctrl_cpuvoltage_t));
150903831d35Sstevel 
151003831d35Sstevel 			break;
151103831d35Sstevel 		}
151203831d35Sstevel 		case PCF8574_TYPE_PWRSUPP: {
151303831d35Sstevel 			envctrl_pwrsupp_t *envp =
151403831d35Sstevel 			    (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
151503831d35Sstevel 
151603831d35Sstevel 			envp->ps_present = 	dev_presence;
151703831d35Sstevel 			envp->ps_ok =		PCF8574_PS_FAULT(value);
151803831d35Sstevel 			envp->temp_ok =		PCF8574_PS_TEMPOK(value);
151903831d35Sstevel 			envp->psfan_ok =	PCF8574_PS_FANOK(value);
152003831d35Sstevel 			envp->on_state =	PCF8574_PS_ONOFF(value);
152103831d35Sstevel 			envp->ps_ver =		PCF8574_PS_TYPE(value);
152203831d35Sstevel 
152303831d35Sstevel 			bcopy((caddr_t)envp, kstatp,
152403831d35Sstevel 			    sizeof (envctrl_pwrsupp_t));
152503831d35Sstevel 
152603831d35Sstevel 			break;
152703831d35Sstevel 		}
152803831d35Sstevel 		case PCF8574_TYPE_FANTRAY: {
152903831d35Sstevel 			envctrl_fantray_t *envp =
153003831d35Sstevel 			    (envctrl_fantray_t *)unitp->envctrl_kstat;
153103831d35Sstevel 
153203831d35Sstevel 			envp->fan_present = dev_presence;
153303831d35Sstevel 			envp->fan_ver = 	PCF8574_FAN_TYPE(value);
153403831d35Sstevel 			envp->fan_ok = 		PCF8574_FAN_FAULT(value);
153503831d35Sstevel 			envp->fanspeed =  	PCF8574_FAN_FANSPD(value);
153603831d35Sstevel 
153703831d35Sstevel 			bcopy((caddr_t)unitp->envctrl_kstat, kstatp,
153803831d35Sstevel 			    sizeof (envctrl_fantray_t));
153903831d35Sstevel 
154003831d35Sstevel 			break;
154103831d35Sstevel 		}
154203831d35Sstevel 
154303831d35Sstevel 		default:
154403831d35Sstevel 			break;
154503831d35Sstevel 		}
154603831d35Sstevel 	}
154703831d35Sstevel 
154803831d35Sstevel kstat_exit:
154903831d35Sstevel 
155003831d35Sstevel 	CV_UNLOCK
155103831d35Sstevel 
155203831d35Sstevel 	return (err);
155303831d35Sstevel }
155403831d35Sstevel 
155503831d35Sstevel static void
155603831d35Sstevel pcf8574_delete_kstat(struct pcf8574_unit *unitp)
155703831d35Sstevel {
155803831d35Sstevel 	/*
155903831d35Sstevel 	 * Depending on the function, deallocate the correct
156003831d35Sstevel 	 * kernel allocated memory.
156103831d35Sstevel 	 */
156203831d35Sstevel 	if (unitp->kstatp != NULL) {
156303831d35Sstevel 		kstat_delete(unitp->kstatp);
156403831d35Sstevel 	}
156503831d35Sstevel 
156603831d35Sstevel 	switch (unitp->pcf8574_type) {
156703831d35Sstevel 	case PCF8574_TYPE_CPUVOLTAGE: {
156803831d35Sstevel 		if (unitp->envctrl_kstat != NULL) {
156903831d35Sstevel 			kmem_free(unitp->envctrl_kstat,
157003831d35Sstevel 			    sizeof (envctrl_cpuvoltage_t));
157103831d35Sstevel 		}
157203831d35Sstevel 		break;
157303831d35Sstevel 	}
157403831d35Sstevel 	case PCF8574_TYPE_PWRSUPP: {
157503831d35Sstevel 		if (unitp->envctrl_kstat != NULL) {
157603831d35Sstevel 			kmem_free(unitp->envctrl_kstat,
157703831d35Sstevel 			    sizeof (envctrl_pwrsupp_t));
157803831d35Sstevel 		}
157903831d35Sstevel 
158003831d35Sstevel 		break;
158103831d35Sstevel 	}
158203831d35Sstevel 	case PCF8574_TYPE_FANTRAY: {
158303831d35Sstevel 		if (unitp->envctrl_kstat != NULL) {
158403831d35Sstevel 			kmem_free(unitp->envctrl_kstat,
158503831d35Sstevel 			    sizeof (envctrl_fantray_t));
158603831d35Sstevel 		}
158703831d35Sstevel 		break;
158803831d35Sstevel 	}
158903831d35Sstevel 	default:
159003831d35Sstevel 		break;
159103831d35Sstevel 	}
159203831d35Sstevel 
159303831d35Sstevel 	unitp->envctrl_kstat = NULL;
159403831d35Sstevel }
159503831d35Sstevel 
159603831d35Sstevel static int
159703831d35Sstevel pcf8574_read_props(struct pcf8574_unit *unitp)
159803831d35Sstevel {
159903831d35Sstevel 	dev_info_t *dip = unitp->dip;
160003831d35Sstevel 	int retval = 0, prop_len;
160103831d35Sstevel 	uint32_t  *prop_value = NULL;
160203831d35Sstevel 	uint8_t i2c_address;
160303831d35Sstevel 	char *function;
160403831d35Sstevel 
160503831d35Sstevel 	/*
160603831d35Sstevel 	 * read the pcf8574_function property. If this property is not
160703831d35Sstevel 	 * found, return ERROR. Else, make sure it's either powersupply
160803831d35Sstevel 	 * or fantray.
160903831d35Sstevel 	 */
161003831d35Sstevel 
161103831d35Sstevel 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
161203831d35Sstevel 	    "pcf8574_function", &function) != DDI_SUCCESS) {
161303831d35Sstevel 		dbg_print(CE_WARN, "Couldn't find pcf8574_function property");
161403831d35Sstevel 
161503831d35Sstevel 		return (DDI_FAILURE);
161603831d35Sstevel 	}
161703831d35Sstevel 
161803831d35Sstevel 	if (strcmp(function, "fantray") == 0) {
161903831d35Sstevel 		unitp->pcf8574_type = PCF8574_TYPE_FANTRAY;
162003831d35Sstevel 		/*
162103831d35Sstevel 		 * Will fail the fantray attach if patch - 1.
162203831d35Sstevel 		 */
162303831d35Sstevel 		if (nct_p10fan_patch) {
162403831d35Sstevel #ifdef DEBUG
162503831d35Sstevel 		cmn_err(CE_WARN, "nct_p10fan_patch set: will not load "
162603831d35Sstevel 		    "fantary:address %x,%x", unitp->props.i2c_bus,
162703831d35Sstevel 		    unitp->props.slave_address);
162803831d35Sstevel #endif
162903831d35Sstevel 			ddi_prop_free(function);
163003831d35Sstevel 			return (DDI_FAILURE);
163103831d35Sstevel 		}
163203831d35Sstevel 	} else
163303831d35Sstevel 	if (strcmp(function, "powersupply") == 0) {
163403831d35Sstevel 		unitp->pcf8574_type = PCF8574_TYPE_PWRSUPP;
163503831d35Sstevel 	} else {
163603831d35Sstevel 		dbg_print(CE_WARN, "Neither powersupply nor fantray");
163703831d35Sstevel 		ddi_prop_free(function);
163803831d35Sstevel 
163903831d35Sstevel 		return (DDI_FAILURE);
164003831d35Sstevel 	}
164103831d35Sstevel 
164203831d35Sstevel 	ddi_prop_free(function);
164303831d35Sstevel 
164403831d35Sstevel 	retval = ddi_getlongprop(DDI_DEV_T_ANY, dip,
164503831d35Sstevel 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP,
164603831d35Sstevel 	    "reg", (caddr_t)&prop_value, &prop_len);
164703831d35Sstevel 	if (retval == DDI_PROP_SUCCESS) {
164803831d35Sstevel 		unitp->props.i2c_bus		= (uint16_t)prop_value[0];
164903831d35Sstevel 		unitp->props.slave_address	= i2c_address =
165003831d35Sstevel 		    (uint8_t)prop_value[1];
165103831d35Sstevel 		kmem_free(prop_value, prop_len);
165203831d35Sstevel 
165303831d35Sstevel 		if (i2c_address>>4 == 7)
165403831d35Sstevel 			unitp->sensor_type = PCF8574A;
165503831d35Sstevel 		else if (i2c_address>>4 == 4)
165603831d35Sstevel 			unitp->sensor_type = PCF8574;
165703831d35Sstevel 		else {
165803831d35Sstevel 			unitp->sensor_type = PCF8574A;
165903831d35Sstevel 			dbg_print(CE_WARN, "Not a pcf8574/a device");
166003831d35Sstevel 		}
166103831d35Sstevel 
166203831d35Sstevel 	} else {
166303831d35Sstevel 		unitp->props.i2c_bus		= (uint16_t)-1;
166403831d35Sstevel 		unitp->props.slave_address	= (uint16_t)-1;
166503831d35Sstevel 	}
166603831d35Sstevel 
166703831d35Sstevel 	/*
166803831d35Sstevel 	 * Get the Property information that the driver will be using
166903831d35Sstevel 	 * see typedef struct pcf8574_properties_t;
167003831d35Sstevel 	 */
167103831d35Sstevel 
167203831d35Sstevel 	unitp->pcf8574_canintr = 0;
167303831d35Sstevel 	retval = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
167403831d35Sstevel 	    "interrupts", -1);
167503831d35Sstevel 	if (retval >= 0) {
167603831d35Sstevel 		int prop_len, intr_pri = 4;
167703831d35Sstevel 		unitp->pcf8574_canintr |= PCF8574_INTR_ON;
167803831d35Sstevel 		if (ddi_getproplen(DDI_DEV_T_ANY, dip,
167903831d35Sstevel 		    DDI_PROP_DONTPASS, "interrupt-priorities",
168003831d35Sstevel 		    &prop_len) == DDI_PROP_NOT_FOUND) {
168103831d35Sstevel 			retval = ddi_prop_create(DDI_DEV_T_NONE, dip,
168203831d35Sstevel 			    DDI_PROP_CANSLEEP, "interrupt-priorities",
168303831d35Sstevel 			    (caddr_t)&intr_pri, sizeof (int));
168403831d35Sstevel #ifdef DEBUG
168503831d35Sstevel 			if (retval != DDI_PROP_SUCCESS) {
168603831d35Sstevel 				cmn_err(CE_WARN, "Failed to create interrupt- \
168703831d35Sstevel 				priorities property, retval %d", retval);
168803831d35Sstevel 			}
168903831d35Sstevel #endif /* DEBUG */
169003831d35Sstevel 		}
169103831d35Sstevel 	}
169203831d35Sstevel 
169303831d35Sstevel 	/*
169403831d35Sstevel 	 * No channels-in-use property for the fan and powersupplies.
169503831d35Sstevel 	 */
169603831d35Sstevel 	unitp->props.num_chans_used = 0;
169703831d35Sstevel 	if (i2c_address == PCF8574_ADR_CPUVOLTAGE) {
169803831d35Sstevel 		if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
169903831d35Sstevel 		    "channels-in-use", &prop_len) == DDI_PROP_SUCCESS) {
170003831d35Sstevel 			retval = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY,
170103831d35Sstevel 			    dip, DDI_PROP_DONTPASS,
170203831d35Sstevel 			    "channels-in-use",
170303831d35Sstevel 			    (uchar_t **)&unitp->props.channels_in_use,
170403831d35Sstevel 			    &unitp->props.num_chans_used);
170503831d35Sstevel 			if (retval != DDI_PROP_SUCCESS) {
170603831d35Sstevel 				unitp->props.num_chans_used = 0;
170703831d35Sstevel 			} else {
170803831d35Sstevel 				unitp->props.num_chans_used /=
170903831d35Sstevel 				    sizeof (pcf8574_channel_t);
171003831d35Sstevel 			}
171103831d35Sstevel 		}
171203831d35Sstevel 	}
171303831d35Sstevel 
171403831d35Sstevel 	return (DDI_PROP_SUCCESS);
171503831d35Sstevel }
171603831d35Sstevel 
171703831d35Sstevel /*
171803831d35Sstevel  * callback function to register with the SCSB driver in order to be
171903831d35Sstevel  * informed about changes in device instance presence.
172003831d35Sstevel  */
172103831d35Sstevel /*ARGSUSED*/
172203831d35Sstevel void
172303831d35Sstevel pcf8574_callback(void *softstate, scsb_fru_event_t cb_event,
172403831d35Sstevel 		scsb_fru_status_t dev_presence)
172503831d35Sstevel {
172603831d35Sstevel 	struct pcf8574_unit *unitp = (struct pcf8574_unit *)softstate;
172703831d35Sstevel #ifdef DEBUG
172803831d35Sstevel 		if (pcf8574_debug & 0x00800001)
172903831d35Sstevel 			cmn_err(CE_NOTE, "pcf8574_callback(unitp,%d,%d)",
173003831d35Sstevel 			    (int)cb_event, (int)dev_presence);
173103831d35Sstevel #endif /* DEBUG */
173203831d35Sstevel 
173303831d35Sstevel 	switch (unitp->pcf8574_type) {
173403831d35Sstevel 		case PCF8574_TYPE_CPUVOLTAGE: {
173503831d35Sstevel 			/*
173603831d35Sstevel 			 * This Unit is not Field Replacable and will not
173703831d35Sstevel 			 * generate any events at the SCB.
173803831d35Sstevel 			 */
173903831d35Sstevel 			break;
174003831d35Sstevel 		}
174103831d35Sstevel 		case PCF8574_TYPE_PWRSUPP: {
174203831d35Sstevel 			envctrl_pwrsupp_t *envp;
174303831d35Sstevel 
174403831d35Sstevel 			envp = (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
174503831d35Sstevel 			if (dev_presence == FRU_NOT_PRESENT) {
174603831d35Sstevel 				envp->ps_ok = 0;
174703831d35Sstevel 				envp->temp_ok = 0;
174803831d35Sstevel 				envp->psfan_ok = 0;
174903831d35Sstevel 				envp->on_state = 0;
175003831d35Sstevel 				envp->ps_ver = 0;
175103831d35Sstevel 			} else
175203831d35Sstevel 			if (dev_presence == FRU_PRESENT &&
175303831d35Sstevel 			    envp->ps_present == FRU_NOT_PRESENT) {
175407d06da5SSurya Prakki 				(void) pcf8574_init_chip(unitp, 0);
175503831d35Sstevel 			}
175603831d35Sstevel 			envp->ps_present = dev_presence;
175703831d35Sstevel 			unitp->poll_event = POLLIN;
175803831d35Sstevel 			pollwakeup(&unitp->poll, POLLIN);
175903831d35Sstevel 			break;
176003831d35Sstevel 		}
176103831d35Sstevel 		case PCF8574_TYPE_FANTRAY: {
176203831d35Sstevel 			envctrl_fantray_t *envp;
176303831d35Sstevel 
176403831d35Sstevel 			envp = (envctrl_fantray_t *)unitp->envctrl_kstat;
176503831d35Sstevel 
176603831d35Sstevel 			if (dev_presence == FRU_NOT_PRESENT) {
176703831d35Sstevel 				envp->fan_ok = 0;
176803831d35Sstevel 				envp->fanspeed =  PCF8574_FAN_SPEED60;
176903831d35Sstevel 				envp->fan_ver = 0;
177003831d35Sstevel 			} else
177103831d35Sstevel 			if (dev_presence == FRU_PRESENT &&
177203831d35Sstevel 			    envp->fan_present == FRU_NOT_PRESENT) {
177307d06da5SSurya Prakki 				(void) pcf8574_init_chip(unitp, 0);
177403831d35Sstevel 			}
177503831d35Sstevel 			envp->fan_present = dev_presence;
177603831d35Sstevel 			unitp->poll_event = POLLIN;
177703831d35Sstevel 			pollwakeup(&unitp->poll, POLLIN);
177803831d35Sstevel 			break;
177903831d35Sstevel 		}
178003831d35Sstevel 	}
178103831d35Sstevel }
178203831d35Sstevel 
178303831d35Sstevel /*
178403831d35Sstevel  * Initializes the chip after attach or after being inserted.
178503831d35Sstevel  * intron = 0 => disable interrupt.
178603831d35Sstevel  * intron = 1 => read register, enable interrupt if no fault.
178703831d35Sstevel  */
178803831d35Sstevel 
178903831d35Sstevel static int
179003831d35Sstevel pcf8574_init_chip(struct pcf8574_unit *unitp, int intron)
179103831d35Sstevel {
179203831d35Sstevel 	int ret = I2C_SUCCESS;
179303831d35Sstevel 	i2c_transfer_t *tp = unitp->i2c_tran;
179403831d35Sstevel 	uint8_t value = 0;
179503831d35Sstevel 	boolean_t device_faulty = B_FALSE; /* true is faulty */
179603831d35Sstevel 
179703831d35Sstevel 	if (unitp->pcf8574_type != PCF8574_TYPE_PWRSUPP &&
179803831d35Sstevel 	    unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) {
179903831d35Sstevel 		return (ret);
180003831d35Sstevel 	}
180103831d35Sstevel 	switch (unitp->pcf8574_type) {
180203831d35Sstevel 	case PCF8574_TYPE_PWRSUPP:
180303831d35Sstevel 		tp->i2c_wbuf[0] = PCF8574_PS_DEFAULT;
180403831d35Sstevel 
180503831d35Sstevel 		break;
180603831d35Sstevel 	case PCF8574_TYPE_FANTRAY:
180703831d35Sstevel 			tp->i2c_wbuf[0] = PCF8574_FAN_DEFAULT;
180803831d35Sstevel 
180903831d35Sstevel 		break;
181003831d35Sstevel 	default:
181103831d35Sstevel 		break;
181203831d35Sstevel 	}
181303831d35Sstevel 
181403831d35Sstevel 	/*
181503831d35Sstevel 	 * First, read the device. If the device is faulty, it does
181603831d35Sstevel 	 * not make sense to enable the interrupt, so in this case
181703831d35Sstevel 	 * keep interrupt maskked inspite of what "intron" says.
181803831d35Sstevel 	 */
181903831d35Sstevel 
182003831d35Sstevel 	tp->i2c_wlen = 0;
182103831d35Sstevel 	tp->i2c_rlen = 1;
182203831d35Sstevel 	tp->i2c_flags = I2C_RD;
182303831d35Sstevel 
182403831d35Sstevel 	unitp->i2c_status = ret = nct_i2c_transfer(unitp->pcf8574_hdl, tp);
182503831d35Sstevel 
182603831d35Sstevel 	if (ret != I2C_SUCCESS) {
182703831d35Sstevel 		return (ret);
182803831d35Sstevel 	}
182903831d35Sstevel 
183003831d35Sstevel 	value = tp->i2c_rbuf[0];
183103831d35Sstevel 
183203831d35Sstevel 	switch (unitp->pcf8574_type) {
183303831d35Sstevel 	case PCF8574_TYPE_PWRSUPP:
183403831d35Sstevel 	{
183503831d35Sstevel 		envctrl_pwrsupp_t *envp =
183603831d35Sstevel 		    (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
183703831d35Sstevel 
183803831d35Sstevel 		envp->ps_ok    = PCF8574_PS_FAULT(value);
183903831d35Sstevel 		envp->temp_ok  = PCF8574_PS_TEMPOK(value);
184003831d35Sstevel 		envp->psfan_ok = PCF8574_PS_FANOK(value);
184103831d35Sstevel 		envp->on_state = PCF8574_PS_ONOFF(value);
184203831d35Sstevel 		envp->ps_ver   = PCF8574_PS_TYPE(value);
184303831d35Sstevel 
184403831d35Sstevel 		if (envp->ps_ok || envp->temp_ok ||
184503831d35Sstevel 		    envp->psfan_ok || envp->on_state)
184603831d35Sstevel 			device_faulty = B_TRUE;
184703831d35Sstevel 
184803831d35Sstevel 		break;
184903831d35Sstevel 	}
185003831d35Sstevel 	case PCF8574_TYPE_FANTRAY:
185103831d35Sstevel 	{
185203831d35Sstevel 		envctrl_fantray_t *envp =
185303831d35Sstevel 		    (envctrl_fantray_t *)unitp->envctrl_kstat;
185403831d35Sstevel 
185503831d35Sstevel 		envp->fan_ver  = PCF8574_FAN_TYPE(value);
185603831d35Sstevel 		envp->fan_ok   = PCF8574_FAN_FAULT(value);
185703831d35Sstevel 		envp->fanspeed = PCF8574_FAN_FANSPD(value);
185803831d35Sstevel 
185903831d35Sstevel 		if (!envp->fan_ok)
186003831d35Sstevel 			device_faulty = B_TRUE; /* remember, 0 is faulty */
186103831d35Sstevel 
186203831d35Sstevel 		break;
186303831d35Sstevel 	}
186403831d35Sstevel 	default:
186503831d35Sstevel 		break;
186603831d35Sstevel 	}
186703831d35Sstevel 	/*
186803831d35Sstevel 	 * Mask interrupt, if intron = 0.
186903831d35Sstevel 	 */
187003831d35Sstevel 	if (!intron || device_faulty == B_TRUE) {
187103831d35Sstevel 		tp->i2c_wbuf[0] |= PCF8574_INTRMASK_BIT;
187203831d35Sstevel 	}
187303831d35Sstevel 
187403831d35Sstevel 	tp->i2c_wlen = 1;
187503831d35Sstevel 	tp->i2c_rlen = 0;
187603831d35Sstevel 	tp->i2c_flags = I2C_WR;
187703831d35Sstevel 
187803831d35Sstevel 	unitp->i2c_status = nct_i2c_transfer(unitp->pcf8574_hdl, tp);
187903831d35Sstevel 
188003831d35Sstevel 	return (unitp->i2c_status);
188103831d35Sstevel }
1882