xref: /onnv-gate/usr/src/uts/sun4u/io/i2c/clients/pca9556.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include <sys/stat.h>
30*0Sstevel@tonic-gate #include <sys/file.h>
31*0Sstevel@tonic-gate #include <sys/uio.h>
32*0Sstevel@tonic-gate #include <sys/modctl.h>
33*0Sstevel@tonic-gate #include <sys/open.h>
34*0Sstevel@tonic-gate #include <sys/types.h>
35*0Sstevel@tonic-gate #include <sys/kmem.h>
36*0Sstevel@tonic-gate #include <sys/systm.h>
37*0Sstevel@tonic-gate #include <sys/ddi.h>
38*0Sstevel@tonic-gate #include <sys/sunddi.h>
39*0Sstevel@tonic-gate #include <sys/conf.h>
40*0Sstevel@tonic-gate #include <sys/mode.h>
41*0Sstevel@tonic-gate #include <sys/note.h>
42*0Sstevel@tonic-gate #include <sys/i2c/clients/i2c_gpio.h>
43*0Sstevel@tonic-gate #include <sys/i2c/clients/pca9556_impl.h>
44*0Sstevel@tonic-gate 
45*0Sstevel@tonic-gate /*
46*0Sstevel@tonic-gate  * The PCA9556 is a gpio chip with 8 I/O ports.  The ports may be controlled by
47*0Sstevel@tonic-gate  * an 8 bit input  port register, 8 bit output port register, 8 bit polarity
48*0Sstevel@tonic-gate  * inversion register and an 8 bit configuration register.
49*0Sstevel@tonic-gate  *
50*0Sstevel@tonic-gate  * The input port register is a read only port and writes to this register
51*0Sstevel@tonic-gate  * will have no effect regardless of whether the pin is an input or output.
52*0Sstevel@tonic-gate  *
53*0Sstevel@tonic-gate  * The output port register reflects the outgoing logic levels of the pins
54*0Sstevel@tonic-gate  * defined as outputs by the configuration register.  Bit values in this
55*0Sstevel@tonic-gate  * register have no effect on pins defined as inputs.
56*0Sstevel@tonic-gate  *
57*0Sstevel@tonic-gate  * The polarity register enables polarity inversion of pins defined as inputs by
58*0Sstevel@tonic-gate  * the configuration register.  A set bit inverts the corresponding port's
59*0Sstevel@tonic-gate  * polarity.
60*0Sstevel@tonic-gate  *
61*0Sstevel@tonic-gate  * The configuration register configures the directions of the I/O pins.  If a
62*0Sstevel@tonic-gate  * bit is set the corresponding port is enabled as an input and if cleared,
63*0Sstevel@tonic-gate  * as an output.
64*0Sstevel@tonic-gate  *
65*0Sstevel@tonic-gate  * The commands supported in the ioctl routine are:
66*0Sstevel@tonic-gate  * GPIO_GET_INPUT	-- Read bits in the input port register.
67*0Sstevel@tonic-gate  * GPIO_GET_OUTPUT	-- Read bits in the output port register.
68*0Sstevel@tonic-gate  * GPIO_SET_OUPUT	-- Modify bits in the output port register.
69*0Sstevel@tonic-gate  * GPIO_GET_POLARITY    -- Read bits in the polarity register.
70*0Sstevel@tonic-gate  * GPIO_SET_POLARITY    -- Modify bits in the polarity register.
71*0Sstevel@tonic-gate  * GPIO_GET_CONFIG	-- Read bits in the configuration register.
72*0Sstevel@tonic-gate  * GPIO_SET_CONFIG	-- Modify bits in the configuration register.
73*0Sstevel@tonic-gate  *
74*0Sstevel@tonic-gate  * A pointer to the i2c_gpio_t data structure is sent as the third argument
75*0Sstevel@tonic-gate  * in the ioctl call.  The reg_mask member identifies the bits that the user
76*0Sstevel@tonic-gate  * wants to read or modify and reg_val has the actual value of the
77*0Sstevel@tonic-gate  * corresponding bits set in reg_mask.
78*0Sstevel@tonic-gate  *
79*0Sstevel@tonic-gate  * To read a whole register the user has to set all the  bits in reg_mask
80*0Sstevel@tonic-gate  * and the values will be copied into reg_val.
81*0Sstevel@tonic-gate  *
82*0Sstevel@tonic-gate  * In addition the pca9555 device has been added to this driver.  It is similar
83*0Sstevel@tonic-gate  * to the pca9556 except that it has 2 8 bit I/O ports.
84*0Sstevel@tonic-gate  */
85*0Sstevel@tonic-gate 
86*0Sstevel@tonic-gate /*
87*0Sstevel@tonic-gate  * cb ops
88*0Sstevel@tonic-gate  */
89*0Sstevel@tonic-gate static int pca9556_open(dev_t *, int, int, cred_t *);
90*0Sstevel@tonic-gate static int pca9556_close(dev_t, int, int, cred_t *);
91*0Sstevel@tonic-gate static int pca9556_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
92*0Sstevel@tonic-gate 
93*0Sstevel@tonic-gate /*
94*0Sstevel@tonic-gate  * dev ops
95*0Sstevel@tonic-gate  */
96*0Sstevel@tonic-gate static int pca9556_s_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
97*0Sstevel@tonic-gate static int pca9556_s_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
98*0Sstevel@tonic-gate static int pca9556_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
99*0Sstevel@tonic-gate 
100*0Sstevel@tonic-gate static struct cb_ops pca9556_cb_ops = {
101*0Sstevel@tonic-gate 	pca9556_open,			/* open */
102*0Sstevel@tonic-gate 	pca9556_close,			/* close */
103*0Sstevel@tonic-gate 	nodev,				/* strategy */
104*0Sstevel@tonic-gate 	nodev,				/* print */
105*0Sstevel@tonic-gate 	nodev,				/* dump */
106*0Sstevel@tonic-gate 	nodev,				/* read */
107*0Sstevel@tonic-gate 	nodev,				/* write */
108*0Sstevel@tonic-gate 	pca9556_ioctl,			/* ioctl */
109*0Sstevel@tonic-gate 	nodev,				/* devmap */
110*0Sstevel@tonic-gate 	nodev,				/* mmap */
111*0Sstevel@tonic-gate 	nodev,				/* segmap */
112*0Sstevel@tonic-gate 	nochpoll,			/* poll */
113*0Sstevel@tonic-gate 	ddi_prop_op,			/* cb_prop_op */
114*0Sstevel@tonic-gate 	NULL,				/* streamtab */
115*0Sstevel@tonic-gate 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
116*0Sstevel@tonic-gate };
117*0Sstevel@tonic-gate 
118*0Sstevel@tonic-gate static struct dev_ops pca9556_dev_ops = {
119*0Sstevel@tonic-gate 	DEVO_REV,
120*0Sstevel@tonic-gate 	0,
121*0Sstevel@tonic-gate 	pca9556_info,
122*0Sstevel@tonic-gate 	nulldev,
123*0Sstevel@tonic-gate 	nulldev,
124*0Sstevel@tonic-gate 	pca9556_s_attach,
125*0Sstevel@tonic-gate 	pca9556_s_detach,
126*0Sstevel@tonic-gate 	nodev,
127*0Sstevel@tonic-gate 	&pca9556_cb_ops,
128*0Sstevel@tonic-gate 	NULL
129*0Sstevel@tonic-gate };
130*0Sstevel@tonic-gate 
131*0Sstevel@tonic-gate static struct modldrv pca9556_modldrv = {
132*0Sstevel@tonic-gate 	&mod_driverops,		/* type of module - driver */
133*0Sstevel@tonic-gate 	"pca9556 device driver v%I%",
134*0Sstevel@tonic-gate 	&pca9556_dev_ops,
135*0Sstevel@tonic-gate };
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate static struct modlinkage pca9556_modlinkage = {
138*0Sstevel@tonic-gate 	MODREV_1,
139*0Sstevel@tonic-gate 	&pca9556_modldrv,
140*0Sstevel@tonic-gate 	0
141*0Sstevel@tonic-gate };
142*0Sstevel@tonic-gate 
143*0Sstevel@tonic-gate static void *pca9556_soft_statep;
144*0Sstevel@tonic-gate int pca9556_debug;
145*0Sstevel@tonic-gate 
146*0Sstevel@tonic-gate int
147*0Sstevel@tonic-gate _init(void)
148*0Sstevel@tonic-gate {
149*0Sstevel@tonic-gate 	int    err;
150*0Sstevel@tonic-gate 
151*0Sstevel@tonic-gate 	err = mod_install(&pca9556_modlinkage);
152*0Sstevel@tonic-gate 	if (err == 0) {
153*0Sstevel@tonic-gate 		(void) ddi_soft_state_init(&pca9556_soft_statep,
154*0Sstevel@tonic-gate 		    sizeof (pca9556_unit_t), PCA9556_MAX_SIZE);
155*0Sstevel@tonic-gate 	}
156*0Sstevel@tonic-gate 	return (err);
157*0Sstevel@tonic-gate }
158*0Sstevel@tonic-gate 
159*0Sstevel@tonic-gate int
160*0Sstevel@tonic-gate _fini(void)
161*0Sstevel@tonic-gate {
162*0Sstevel@tonic-gate 	int    err;
163*0Sstevel@tonic-gate 
164*0Sstevel@tonic-gate 	err = mod_remove(&pca9556_modlinkage);
165*0Sstevel@tonic-gate 	if (err == 0) {
166*0Sstevel@tonic-gate 		ddi_soft_state_fini(&pca9556_soft_statep);
167*0Sstevel@tonic-gate 	}
168*0Sstevel@tonic-gate 	return (err);
169*0Sstevel@tonic-gate }
170*0Sstevel@tonic-gate 
171*0Sstevel@tonic-gate int
172*0Sstevel@tonic-gate _info(struct modinfo *modinfop)
173*0Sstevel@tonic-gate {
174*0Sstevel@tonic-gate 	return (mod_info(&pca9556_modlinkage, modinfop));
175*0Sstevel@tonic-gate }
176*0Sstevel@tonic-gate 
177*0Sstevel@tonic-gate static int
178*0Sstevel@tonic-gate pca9556_resume(dev_info_t *dip)
179*0Sstevel@tonic-gate {
180*0Sstevel@tonic-gate 	int 		instance = ddi_get_instance(dip);
181*0Sstevel@tonic-gate 	pca9556_unit_t	*pcap;
182*0Sstevel@tonic-gate 	int 		err = DDI_SUCCESS;
183*0Sstevel@tonic-gate 	int		reg_offset, num_of_ports;
184*0Sstevel@tonic-gate 	int		i, j;
185*0Sstevel@tonic-gate 	uint8_t		reg, reg_num = 0;
186*0Sstevel@tonic-gate 
187*0Sstevel@tonic-gate 	pcap = (pca9556_unit_t *)
188*0Sstevel@tonic-gate 	    ddi_get_soft_state(pca9556_soft_statep, instance);
189*0Sstevel@tonic-gate 
190*0Sstevel@tonic-gate 	if (pcap == NULL)
191*0Sstevel@tonic-gate 		return (ENXIO);
192*0Sstevel@tonic-gate 
193*0Sstevel@tonic-gate 	/*
194*0Sstevel@tonic-gate 	 * Restore registers to status existing before cpr
195*0Sstevel@tonic-gate 	 */
196*0Sstevel@tonic-gate 	pcap->pca9556_transfer->i2c_flags = I2C_WR;
197*0Sstevel@tonic-gate 	pcap->pca9556_transfer->i2c_wlen = 2;
198*0Sstevel@tonic-gate 	pcap->pca9556_transfer->i2c_rlen = 0;
199*0Sstevel@tonic-gate 
200*0Sstevel@tonic-gate 	if (pcap->pca9555_device) {
201*0Sstevel@tonic-gate 		reg_offset = 2;
202*0Sstevel@tonic-gate 		num_of_ports = PCA9555_NUM_PORTS;
203*0Sstevel@tonic-gate 	} else {
204*0Sstevel@tonic-gate 		reg_offset = 1;
205*0Sstevel@tonic-gate 		num_of_ports = PCA9556_NUM_PORTS;
206*0Sstevel@tonic-gate 	}
207*0Sstevel@tonic-gate 
208*0Sstevel@tonic-gate 	for (i = 0; i < num_of_ports; i++) {
209*0Sstevel@tonic-gate 		if (pcap->pca9555_device)
210*0Sstevel@tonic-gate 			reg = PCA9555_OUTPUT_REG;
211*0Sstevel@tonic-gate 		else
212*0Sstevel@tonic-gate 			reg = PCA9556_OUTPUT_REG;
213*0Sstevel@tonic-gate 
214*0Sstevel@tonic-gate 		for (j = 0; j < PCA9556_NUM_REG; j++) {
215*0Sstevel@tonic-gate 			pcap->pca9556_transfer->i2c_wbuf[0] = reg + i;
216*0Sstevel@tonic-gate 			pcap->pca9556_transfer->i2c_wbuf[1] =
217*0Sstevel@tonic-gate 			    pcap->pca9556_cpr_state[reg_num++];
218*0Sstevel@tonic-gate 
219*0Sstevel@tonic-gate 			if (i2c_transfer(pcap->pca9556_hdl,
220*0Sstevel@tonic-gate 				pcap->pca9556_transfer) != DDI_SUCCESS) {
221*0Sstevel@tonic-gate 				err = EIO;
222*0Sstevel@tonic-gate 
223*0Sstevel@tonic-gate 				goto done;
224*0Sstevel@tonic-gate 			}
225*0Sstevel@tonic-gate 
226*0Sstevel@tonic-gate 			reg = reg + reg_offset;
227*0Sstevel@tonic-gate 		}
228*0Sstevel@tonic-gate 	}
229*0Sstevel@tonic-gate 
230*0Sstevel@tonic-gate 	/*
231*0Sstevel@tonic-gate 	 * Clear busy flag so that transactions may continue
232*0Sstevel@tonic-gate 	 */
233*0Sstevel@tonic-gate done:
234*0Sstevel@tonic-gate 	if (err != DDI_SUCCESS) {
235*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s Unable to restore registers",
236*0Sstevel@tonic-gate 		    pcap->pca9556_name);
237*0Sstevel@tonic-gate 	}
238*0Sstevel@tonic-gate 	mutex_enter(&pcap->pca9556_mutex);
239*0Sstevel@tonic-gate 	pcap->pca9556_flags = pcap->pca9556_flags & ~PCA9556_BUSYFLAG;
240*0Sstevel@tonic-gate 	cv_broadcast(&pcap->pca9556_cv);
241*0Sstevel@tonic-gate 	mutex_exit(&pcap->pca9556_mutex);
242*0Sstevel@tonic-gate 	return (err);
243*0Sstevel@tonic-gate }
244*0Sstevel@tonic-gate 
245*0Sstevel@tonic-gate static void
246*0Sstevel@tonic-gate pca9556_detach(dev_info_t *dip)
247*0Sstevel@tonic-gate {
248*0Sstevel@tonic-gate 	pca9556_unit_t *pcap;
249*0Sstevel@tonic-gate 	int 		instance = ddi_get_instance(dip);
250*0Sstevel@tonic-gate 
251*0Sstevel@tonic-gate 	pcap = ddi_get_soft_state(pca9556_soft_statep, instance);
252*0Sstevel@tonic-gate 
253*0Sstevel@tonic-gate 	if ((pcap->pca9556_flags & PCA9556_REGFLAG) == PCA9556_REGFLAG) {
254*0Sstevel@tonic-gate 		i2c_client_unregister(pcap->pca9556_hdl);
255*0Sstevel@tonic-gate 	}
256*0Sstevel@tonic-gate 	if ((pcap->pca9556_flags & PCA9556_TBUFFLAG) == PCA9556_TBUFFLAG) {
257*0Sstevel@tonic-gate 		i2c_transfer_free(pcap->pca9556_hdl, pcap->pca9556_transfer);
258*0Sstevel@tonic-gate 	}
259*0Sstevel@tonic-gate 	if ((pcap->pca9556_flags & PCA9556_MINORFLAG) == PCA9556_MINORFLAG) {
260*0Sstevel@tonic-gate 		ddi_remove_minor_node(dip, NULL);
261*0Sstevel@tonic-gate 	}
262*0Sstevel@tonic-gate 	cv_destroy(&pcap->pca9556_cv);
263*0Sstevel@tonic-gate 	mutex_destroy(&pcap->pca9556_mutex);
264*0Sstevel@tonic-gate 	ddi_soft_state_free(pca9556_soft_statep, instance);
265*0Sstevel@tonic-gate 
266*0Sstevel@tonic-gate }
267*0Sstevel@tonic-gate 
268*0Sstevel@tonic-gate static int
269*0Sstevel@tonic-gate pca9556_attach(dev_info_t *dip)
270*0Sstevel@tonic-gate {
271*0Sstevel@tonic-gate 	pca9556_unit_t 		*pcap;
272*0Sstevel@tonic-gate 	int 			instance = ddi_get_instance(dip);
273*0Sstevel@tonic-gate 	char			name[MAXNAMELEN];
274*0Sstevel@tonic-gate 	char *device_name;
275*0Sstevel@tonic-gate 	minor_t 		minor;
276*0Sstevel@tonic-gate 	int			i, num_ports;
277*0Sstevel@tonic-gate 
278*0Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(pca9556_soft_statep, instance) != 0) {
279*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s%d failed to zalloc softstate",
280*0Sstevel@tonic-gate 		    ddi_get_name(dip), instance);
281*0Sstevel@tonic-gate 		return (DDI_FAILURE);
282*0Sstevel@tonic-gate 	}
283*0Sstevel@tonic-gate 
284*0Sstevel@tonic-gate 	pcap = ddi_get_soft_state(pca9556_soft_statep, instance);
285*0Sstevel@tonic-gate 
286*0Sstevel@tonic-gate 	if (pcap == NULL)
287*0Sstevel@tonic-gate 		return (DDI_FAILURE);
288*0Sstevel@tonic-gate 
289*0Sstevel@tonic-gate 	mutex_init(&pcap->pca9556_mutex, NULL, MUTEX_DRIVER, NULL);
290*0Sstevel@tonic-gate 	cv_init(&pcap->pca9556_cv, NULL, CV_DRIVER, NULL);
291*0Sstevel@tonic-gate 
292*0Sstevel@tonic-gate 	(void) snprintf(pcap->pca9556_name, sizeof (pcap->pca9556_name),
293*0Sstevel@tonic-gate 	    "%s_%d", ddi_driver_name(dip), instance);
294*0Sstevel@tonic-gate 
295*0Sstevel@tonic-gate 	device_name = ddi_get_name(dip);
296*0Sstevel@tonic-gate 
297*0Sstevel@tonic-gate 	if (strcmp(device_name, "i2c-pca9555") == 0) {
298*0Sstevel@tonic-gate 		num_ports = PCA9555_NUM_PORTS;
299*0Sstevel@tonic-gate 		pcap->pca9555_device = B_TRUE;
300*0Sstevel@tonic-gate 	} else {
301*0Sstevel@tonic-gate 		num_ports = PCA9556_NUM_PORTS;
302*0Sstevel@tonic-gate 		pcap->pca9555_device = B_FALSE;
303*0Sstevel@tonic-gate 		minor = INST_TO_MINOR(instance);
304*0Sstevel@tonic-gate 	}
305*0Sstevel@tonic-gate 
306*0Sstevel@tonic-gate 	for (i = 0; i < num_ports; i++) {
307*0Sstevel@tonic-gate 		if (!(pcap->pca9555_device)) {
308*0Sstevel@tonic-gate 			(void) snprintf(pcap->pca9556_name,
309*0Sstevel@tonic-gate 			    sizeof (pcap->pca9556_name), "%s_%d",
310*0Sstevel@tonic-gate 			    ddi_driver_name(dip), instance);
311*0Sstevel@tonic-gate 			(void) snprintf(name, sizeof (name), "%s",
312*0Sstevel@tonic-gate 			    pcap->pca9556_name);
313*0Sstevel@tonic-gate 		} else {
314*0Sstevel@tonic-gate 			(void) sprintf(name, "port_%d", i);
315*0Sstevel@tonic-gate 			minor = INST_TO_MINOR(instance) |
316*0Sstevel@tonic-gate 			    PORT_TO_MINOR(I2C_PORT(i));
317*0Sstevel@tonic-gate 		}
318*0Sstevel@tonic-gate 
319*0Sstevel@tonic-gate 		if (ddi_create_minor_node(dip, name, S_IFCHR, minor,
320*0Sstevel@tonic-gate 			PCA9556_NODE_TYPE, NULL) == DDI_FAILURE) {
321*0Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s: failed to create node for %s",
322*0Sstevel@tonic-gate 			    pcap->pca9556_name, name);
323*0Sstevel@tonic-gate 			pca9556_detach(dip);
324*0Sstevel@tonic-gate 			return (DDI_FAILURE);
325*0Sstevel@tonic-gate 		}
326*0Sstevel@tonic-gate 	}
327*0Sstevel@tonic-gate 	pcap->pca9556_flags |= PCA9556_MINORFLAG;
328*0Sstevel@tonic-gate 
329*0Sstevel@tonic-gate 	/*
330*0Sstevel@tonic-gate 	 * Add a zero-length attribute to tell the world we support
331*0Sstevel@tonic-gate 	 * kernel ioctls (for layered drivers)
332*0Sstevel@tonic-gate 	 */
333*0Sstevel@tonic-gate 	(void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
334*0Sstevel@tonic-gate 	    DDI_KERNEL_IOCTL, NULL, 0);
335*0Sstevel@tonic-gate 
336*0Sstevel@tonic-gate 
337*0Sstevel@tonic-gate 	/*
338*0Sstevel@tonic-gate 	 * preallocate a single buffer for all reads and writes
339*0Sstevel@tonic-gate 	 */
340*0Sstevel@tonic-gate 	if (i2c_transfer_alloc(pcap->pca9556_hdl, &pcap->pca9556_transfer,
341*0Sstevel@tonic-gate 	    2, 2, I2C_SLEEP) != I2C_SUCCESS) {
342*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s i2c_transfer_alloc failed",
343*0Sstevel@tonic-gate 		    pcap->pca9556_name);
344*0Sstevel@tonic-gate 		pca9556_detach(dip);
345*0Sstevel@tonic-gate 		return (DDI_FAILURE);
346*0Sstevel@tonic-gate 	}
347*0Sstevel@tonic-gate 	pcap->pca9556_flags |= PCA9556_TBUFFLAG;
348*0Sstevel@tonic-gate 	pcap->pca9556_transfer->i2c_version = I2C_XFER_REV;
349*0Sstevel@tonic-gate 
350*0Sstevel@tonic-gate 	if (i2c_client_register(dip, &pcap->pca9556_hdl) != I2C_SUCCESS) {
351*0Sstevel@tonic-gate 		ddi_remove_minor_node(dip, NULL);
352*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s i2c_client_register failed",
353*0Sstevel@tonic-gate 		    pcap->pca9556_name);
354*0Sstevel@tonic-gate 		pca9556_detach(dip);
355*0Sstevel@tonic-gate 		return (DDI_FAILURE);
356*0Sstevel@tonic-gate 	}
357*0Sstevel@tonic-gate 	pcap->pca9556_flags |= PCA9556_REGFLAG;
358*0Sstevel@tonic-gate 
359*0Sstevel@tonic-gate 	/*
360*0Sstevel@tonic-gate 	 * Store the dip for future dip.
361*0Sstevel@tonic-gate 	 */
362*0Sstevel@tonic-gate 	pcap->pca9556_dip = dip;
363*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
364*0Sstevel@tonic-gate }
365*0Sstevel@tonic-gate 
366*0Sstevel@tonic-gate 
367*0Sstevel@tonic-gate static int
368*0Sstevel@tonic-gate pca9556_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
369*0Sstevel@tonic-gate {
370*0Sstevel@tonic-gate 	_NOTE(ARGUNUSED(dip))
371*0Sstevel@tonic-gate 
372*0Sstevel@tonic-gate 	pca9556_unit_t	*pcap;
373*0Sstevel@tonic-gate 	int		instance = MINOR_TO_INST(getminor((dev_t)arg));
374*0Sstevel@tonic-gate 
375*0Sstevel@tonic-gate 	switch (cmd) {
376*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
377*0Sstevel@tonic-gate 		pcap = ddi_get_soft_state(pca9556_soft_statep, instance);
378*0Sstevel@tonic-gate 		if (pcap == NULL)
379*0Sstevel@tonic-gate 			return (DDI_FAILURE);
380*0Sstevel@tonic-gate 		*result = (void *)pcap->pca9556_dip;
381*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
382*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
383*0Sstevel@tonic-gate 		*result = (void *)instance;
384*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
385*0Sstevel@tonic-gate 	default:
386*0Sstevel@tonic-gate 		return (DDI_FAILURE);
387*0Sstevel@tonic-gate 	}
388*0Sstevel@tonic-gate }
389*0Sstevel@tonic-gate 
390*0Sstevel@tonic-gate static int
391*0Sstevel@tonic-gate pca9556_s_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
392*0Sstevel@tonic-gate {
393*0Sstevel@tonic-gate 	switch (cmd) {
394*0Sstevel@tonic-gate 	case DDI_ATTACH:
395*0Sstevel@tonic-gate 		return (pca9556_attach(dip));
396*0Sstevel@tonic-gate 	case DDI_RESUME:
397*0Sstevel@tonic-gate 		return (pca9556_resume(dip));
398*0Sstevel@tonic-gate 	default:
399*0Sstevel@tonic-gate 		return (DDI_FAILURE);
400*0Sstevel@tonic-gate 	}
401*0Sstevel@tonic-gate }
402*0Sstevel@tonic-gate 
403*0Sstevel@tonic-gate static int
404*0Sstevel@tonic-gate pca9556_suspend(dev_info_t *dip)
405*0Sstevel@tonic-gate {
406*0Sstevel@tonic-gate 	pca9556_unit_t 	*pcap;
407*0Sstevel@tonic-gate 	int 		instance = ddi_get_instance(dip);
408*0Sstevel@tonic-gate 	int		err = DDI_SUCCESS;
409*0Sstevel@tonic-gate 	int		reg_offset, num_of_ports;
410*0Sstevel@tonic-gate 	int		i, j;
411*0Sstevel@tonic-gate 	uint8_t		reg, reg_num = 0;
412*0Sstevel@tonic-gate 
413*0Sstevel@tonic-gate 	pcap = ddi_get_soft_state(pca9556_soft_statep, instance);
414*0Sstevel@tonic-gate 
415*0Sstevel@tonic-gate 	mutex_enter(&pcap->pca9556_mutex);
416*0Sstevel@tonic-gate 	while ((pcap->pca9556_flags & PCA9556_BUSYFLAG) == PCA9556_BUSYFLAG) {
417*0Sstevel@tonic-gate 		if (cv_wait_sig(&pcap->pca9556_cv,
418*0Sstevel@tonic-gate 		    &pcap->pca9556_mutex) <= 0) {
419*0Sstevel@tonic-gate 			mutex_exit(&pcap->pca9556_mutex);
420*0Sstevel@tonic-gate 			return (DDI_FAILURE);
421*0Sstevel@tonic-gate 		}
422*0Sstevel@tonic-gate 	}
423*0Sstevel@tonic-gate 	pcap->pca9556_flags |= PCA9556_BUSYFLAG;
424*0Sstevel@tonic-gate 	mutex_exit(&pcap->pca9556_mutex);
425*0Sstevel@tonic-gate 
426*0Sstevel@tonic-gate 	/*
427*0Sstevel@tonic-gate 	 * A pca9555 devices command registers are offset by 2 and it has 2
428*0Sstevel@tonic-gate 	 * ports to save. A pca9556 devices command registers are offset by 1
429*0Sstevel@tonic-gate 	 * while it only has one "port"
430*0Sstevel@tonic-gate 	 */
431*0Sstevel@tonic-gate 	if (pcap->pca9555_device) {
432*0Sstevel@tonic-gate 		reg_offset = 2;
433*0Sstevel@tonic-gate 		num_of_ports = PCA9555_NUM_PORTS;
434*0Sstevel@tonic-gate 	} else {
435*0Sstevel@tonic-gate 		reg_offset = 1;
436*0Sstevel@tonic-gate 		num_of_ports = PCA9556_NUM_PORTS;
437*0Sstevel@tonic-gate 	}
438*0Sstevel@tonic-gate 	/*
439*0Sstevel@tonic-gate 	 * Save the state of the registers
440*0Sstevel@tonic-gate 	 */
441*0Sstevel@tonic-gate 	pcap->pca9556_transfer->i2c_flags = I2C_WR_RD;
442*0Sstevel@tonic-gate 	pcap->pca9556_transfer->i2c_wlen = 1;
443*0Sstevel@tonic-gate 	pcap->pca9556_transfer->i2c_rlen = 1;
444*0Sstevel@tonic-gate 
445*0Sstevel@tonic-gate 	/*
446*0Sstevel@tonic-gate 	 * The following for loop will run through once for a pca9556 device
447*0Sstevel@tonic-gate 	 * and twice for a pca9555 device. i will represent the port number
448*0Sstevel@tonic-gate 	 * for the pca9555.
449*0Sstevel@tonic-gate 	 */
450*0Sstevel@tonic-gate 	for (i = 0; i < num_of_ports; i++) {
451*0Sstevel@tonic-gate 		/*
452*0Sstevel@tonic-gate 		 * We set the first Register here so it can be reset if we
453*0Sstevel@tonic-gate 		 * loop through (pca9555 device).
454*0Sstevel@tonic-gate 		 */
455*0Sstevel@tonic-gate 		if (pcap->pca9555_device)
456*0Sstevel@tonic-gate 			reg = PCA9555_OUTPUT_REG;
457*0Sstevel@tonic-gate 		else
458*0Sstevel@tonic-gate 			reg = PCA9556_OUTPUT_REG;
459*0Sstevel@tonic-gate 
460*0Sstevel@tonic-gate 		/* We run through this loop 3 times. Once for each register */
461*0Sstevel@tonic-gate 		for (j = 0; j < PCA9556_NUM_REG; j++) {
462*0Sstevel@tonic-gate 
463*0Sstevel@tonic-gate 			/*
464*0Sstevel@tonic-gate 			 * We add the port number (0 for pca9556, 0 or 1 for
465*0Sstevel@tonic-gate 			 * a pca9555) to the register.
466*0Sstevel@tonic-gate 			 */
467*0Sstevel@tonic-gate 			pcap->pca9556_transfer->i2c_wbuf[0] = reg + i;
468*0Sstevel@tonic-gate 			if (i2c_transfer(pcap->pca9556_hdl,
469*0Sstevel@tonic-gate 				pcap->pca9556_transfer) != DDI_SUCCESS) {
470*0Sstevel@tonic-gate 				err = EIO;
471*0Sstevel@tonic-gate 				goto done;
472*0Sstevel@tonic-gate 			}
473*0Sstevel@tonic-gate 
474*0Sstevel@tonic-gate 			pcap->pca9556_cpr_state[reg_num++] =
475*0Sstevel@tonic-gate 			    pcap->pca9556_transfer->i2c_rbuf[0];
476*0Sstevel@tonic-gate 			/*
477*0Sstevel@tonic-gate 			 * The register is then added to the offset and saved
478*0Sstevel@tonic-gate 			 * to go and read the next command register.
479*0Sstevel@tonic-gate 			 */
480*0Sstevel@tonic-gate 			reg = reg + reg_offset;
481*0Sstevel@tonic-gate 		}
482*0Sstevel@tonic-gate 	}
483*0Sstevel@tonic-gate 
484*0Sstevel@tonic-gate done:
485*0Sstevel@tonic-gate 	if (err != DDI_SUCCESS) {
486*0Sstevel@tonic-gate 		mutex_enter(&pcap->pca9556_mutex);
487*0Sstevel@tonic-gate 		pcap->pca9556_flags = pcap->pca9556_flags & ~PCA9556_BUSYFLAG;
488*0Sstevel@tonic-gate 		cv_broadcast(&pcap->pca9556_cv);
489*0Sstevel@tonic-gate 		mutex_exit(&pcap->pca9556_mutex);
490*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s Suspend failed, unable to save registers",
491*0Sstevel@tonic-gate 		    pcap->pca9556_name);
492*0Sstevel@tonic-gate 		return (err);
493*0Sstevel@tonic-gate 	}
494*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
495*0Sstevel@tonic-gate }
496*0Sstevel@tonic-gate 
497*0Sstevel@tonic-gate static int
498*0Sstevel@tonic-gate pca9556_s_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
499*0Sstevel@tonic-gate {
500*0Sstevel@tonic-gate 	switch (cmd) {
501*0Sstevel@tonic-gate 	case DDI_DETACH:
502*0Sstevel@tonic-gate 		pca9556_detach(dip);
503*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
504*0Sstevel@tonic-gate 	case DDI_SUSPEND:
505*0Sstevel@tonic-gate 		return (pca9556_suspend(dip));
506*0Sstevel@tonic-gate 	default:
507*0Sstevel@tonic-gate 		return (DDI_FAILURE);
508*0Sstevel@tonic-gate 	}
509*0Sstevel@tonic-gate }
510*0Sstevel@tonic-gate 
511*0Sstevel@tonic-gate static int
512*0Sstevel@tonic-gate pca9556_open(dev_t *devp, int flags, int otyp, cred_t *credp)
513*0Sstevel@tonic-gate {
514*0Sstevel@tonic-gate 	int			instance;
515*0Sstevel@tonic-gate 	pca9556_unit_t		*pcap;
516*0Sstevel@tonic-gate 	int			err = EBUSY;
517*0Sstevel@tonic-gate 
518*0Sstevel@tonic-gate 	/*
519*0Sstevel@tonic-gate 	 * Make sure the open is for the right file type
520*0Sstevel@tonic-gate 	 */
521*0Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
522*0Sstevel@tonic-gate 		return (EINVAL);
523*0Sstevel@tonic-gate 
524*0Sstevel@tonic-gate 	instance = MINOR_TO_INST(getminor(*devp));
525*0Sstevel@tonic-gate 
526*0Sstevel@tonic-gate 	pcap = (pca9556_unit_t *)
527*0Sstevel@tonic-gate 	    ddi_get_soft_state(pca9556_soft_statep, instance);
528*0Sstevel@tonic-gate 	if (pcap == NULL)
529*0Sstevel@tonic-gate 		return (ENXIO);
530*0Sstevel@tonic-gate 
531*0Sstevel@tonic-gate 	/* must be privileged to access this device */
532*0Sstevel@tonic-gate 	if (drv_priv(credp) != 0)
533*0Sstevel@tonic-gate 		return (EPERM);
534*0Sstevel@tonic-gate 
535*0Sstevel@tonic-gate 	/*
536*0Sstevel@tonic-gate 	 * Enforce exclusive access if required
537*0Sstevel@tonic-gate 	 */
538*0Sstevel@tonic-gate 	mutex_enter(&pcap->pca9556_mutex);
539*0Sstevel@tonic-gate 	if (flags & FEXCL) {
540*0Sstevel@tonic-gate 		if (pcap->pca9556_oflag == 0) {
541*0Sstevel@tonic-gate 			pcap->pca9556_oflag = FEXCL;
542*0Sstevel@tonic-gate 			err = DDI_SUCCESS;
543*0Sstevel@tonic-gate 		}
544*0Sstevel@tonic-gate 	} else if (pcap->pca9556_oflag != FEXCL) {
545*0Sstevel@tonic-gate 		pcap->pca9556_oflag = FOPEN;
546*0Sstevel@tonic-gate 		err = DDI_SUCCESS;
547*0Sstevel@tonic-gate 	}
548*0Sstevel@tonic-gate 	mutex_exit(&pcap->pca9556_mutex);
549*0Sstevel@tonic-gate 	return (err);
550*0Sstevel@tonic-gate }
551*0Sstevel@tonic-gate 
552*0Sstevel@tonic-gate static int
553*0Sstevel@tonic-gate pca9556_close(dev_t dev, int flags, int otyp, cred_t *credp)
554*0Sstevel@tonic-gate {
555*0Sstevel@tonic-gate 	int		instance;
556*0Sstevel@tonic-gate 	pca9556_unit_t 	*pcap;
557*0Sstevel@tonic-gate 
558*0Sstevel@tonic-gate 	_NOTE(ARGUNUSED(flags, credp))
559*0Sstevel@tonic-gate 
560*0Sstevel@tonic-gate 	/*
561*0Sstevel@tonic-gate 	 * Make sure the close is for the right file type
562*0Sstevel@tonic-gate 	 */
563*0Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
564*0Sstevel@tonic-gate 		return (EINVAL);
565*0Sstevel@tonic-gate 
566*0Sstevel@tonic-gate 	instance = MINOR_TO_INST(getminor(dev));
567*0Sstevel@tonic-gate 
568*0Sstevel@tonic-gate 	pcap = (pca9556_unit_t *)
569*0Sstevel@tonic-gate 	    ddi_get_soft_state(pca9556_soft_statep, instance);
570*0Sstevel@tonic-gate 	if (pcap == NULL)
571*0Sstevel@tonic-gate 		return (ENXIO);
572*0Sstevel@tonic-gate 
573*0Sstevel@tonic-gate 	mutex_enter(&pcap->pca9556_mutex);
574*0Sstevel@tonic-gate 	pcap->pca9556_oflag = 0;
575*0Sstevel@tonic-gate 	mutex_exit(&pcap->pca9556_mutex);
576*0Sstevel@tonic-gate 	return (0);
577*0Sstevel@tonic-gate }
578*0Sstevel@tonic-gate 
579*0Sstevel@tonic-gate static int
580*0Sstevel@tonic-gate pca9556_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
581*0Sstevel@tonic-gate 	int *rvalp)
582*0Sstevel@tonic-gate {
583*0Sstevel@tonic-gate 	pca9556_unit_t		*pcap;
584*0Sstevel@tonic-gate 	int			err = 0;
585*0Sstevel@tonic-gate 	int			instance = MINOR_TO_INST(getminor(dev));
586*0Sstevel@tonic-gate 	int			port;
587*0Sstevel@tonic-gate 	i2c_gpio_t		g_buf;
588*0Sstevel@tonic-gate 	uchar_t			temp;
589*0Sstevel@tonic-gate 	boolean_t		write_io = B_FALSE;
590*0Sstevel@tonic-gate 
591*0Sstevel@tonic-gate 	_NOTE(ARGUNUSED(credp, rvalp))
592*0Sstevel@tonic-gate 
593*0Sstevel@tonic-gate 	pcap = (pca9556_unit_t *)
594*0Sstevel@tonic-gate 	    ddi_get_soft_state(pca9556_soft_statep, instance);
595*0Sstevel@tonic-gate 
596*0Sstevel@tonic-gate 	if (pcap->pca9555_device) {
597*0Sstevel@tonic-gate 		port =  MINOR_TO_PORT(getminor(dev));
598*0Sstevel@tonic-gate 	}
599*0Sstevel@tonic-gate 	if (pca9556_debug) {
600*0Sstevel@tonic-gate 		prom_printf("pca9556_ioctl: instance=%d\n", instance);
601*0Sstevel@tonic-gate 	}
602*0Sstevel@tonic-gate 
603*0Sstevel@tonic-gate 	/*
604*0Sstevel@tonic-gate 	 * We serialize here and  block any pending transacations.
605*0Sstevel@tonic-gate 	 */
606*0Sstevel@tonic-gate 	mutex_enter(&pcap->pca9556_mutex);
607*0Sstevel@tonic-gate 	while ((pcap->pca9556_flags & PCA9556_BUSYFLAG) == PCA9556_BUSYFLAG) {
608*0Sstevel@tonic-gate 		if (cv_wait_sig(&pcap->pca9556_cv,
609*0Sstevel@tonic-gate 		    &pcap->pca9556_mutex) <= 0) {
610*0Sstevel@tonic-gate 			mutex_exit(&pcap->pca9556_mutex);
611*0Sstevel@tonic-gate 			return (EINTR);
612*0Sstevel@tonic-gate 		}
613*0Sstevel@tonic-gate 	}
614*0Sstevel@tonic-gate 	pcap->pca9556_flags |= PCA9556_BUSYFLAG;
615*0Sstevel@tonic-gate 	mutex_exit(&pcap->pca9556_mutex);
616*0Sstevel@tonic-gate 	if (ddi_copyin((caddr_t)arg, &g_buf,
617*0Sstevel@tonic-gate 	    sizeof (i2c_gpio_t), mode) != DDI_SUCCESS) {
618*0Sstevel@tonic-gate 
619*0Sstevel@tonic-gate 		err = EFAULT;
620*0Sstevel@tonic-gate 
621*0Sstevel@tonic-gate 		goto cleanup;
622*0Sstevel@tonic-gate 	}
623*0Sstevel@tonic-gate 	pcap->pca9556_transfer->i2c_flags = I2C_WR_RD;
624*0Sstevel@tonic-gate 	pcap->pca9556_transfer->i2c_wlen = 1;
625*0Sstevel@tonic-gate 	pcap->pca9556_transfer->i2c_rlen = 1;
626*0Sstevel@tonic-gate 
627*0Sstevel@tonic-gate 	/*
628*0Sstevel@tonic-gate 	 * Evaluate which register is to be read or modified
629*0Sstevel@tonic-gate 	 */
630*0Sstevel@tonic-gate 
631*0Sstevel@tonic-gate 	switch (cmd) {
632*0Sstevel@tonic-gate 	case GPIO_GET_INPUT:
633*0Sstevel@tonic-gate 		if (pcap->pca9555_device)
634*0Sstevel@tonic-gate 			pcap->pca9556_transfer->i2c_wbuf[0] =
635*0Sstevel@tonic-gate 			    PCA9555_INPUT_REG + port;
636*0Sstevel@tonic-gate 		else
637*0Sstevel@tonic-gate 			pcap->pca9556_transfer->i2c_wbuf[0] =
638*0Sstevel@tonic-gate 			    PCA9556_INPUT_REG;
639*0Sstevel@tonic-gate 		break;
640*0Sstevel@tonic-gate 
641*0Sstevel@tonic-gate 	case GPIO_SET_OUTPUT:
642*0Sstevel@tonic-gate 		write_io = B_TRUE;
643*0Sstevel@tonic-gate 		/*FALLTHROUGH*/
644*0Sstevel@tonic-gate 
645*0Sstevel@tonic-gate 	case GPIO_GET_OUTPUT:
646*0Sstevel@tonic-gate 		if (pcap->pca9555_device)
647*0Sstevel@tonic-gate 			pcap->pca9556_transfer->i2c_wbuf[0] =
648*0Sstevel@tonic-gate 			    PCA9555_OUTPUT_REG + port;
649*0Sstevel@tonic-gate 		else
650*0Sstevel@tonic-gate 			pcap->pca9556_transfer->i2c_wbuf[0] =
651*0Sstevel@tonic-gate 			    PCA9556_OUTPUT_REG;
652*0Sstevel@tonic-gate 		break;
653*0Sstevel@tonic-gate 
654*0Sstevel@tonic-gate 	case GPIO_SET_POLARITY:
655*0Sstevel@tonic-gate 		write_io = B_TRUE;
656*0Sstevel@tonic-gate 		/*FALLTHROUGH*/
657*0Sstevel@tonic-gate 
658*0Sstevel@tonic-gate 	case GPIO_GET_POLARITY:
659*0Sstevel@tonic-gate 		if (pcap->pca9555_device)
660*0Sstevel@tonic-gate 			pcap->pca9556_transfer->i2c_wbuf[0] =
661*0Sstevel@tonic-gate 			    PCA9555_POLARITY_REG + port;
662*0Sstevel@tonic-gate 		else
663*0Sstevel@tonic-gate 			pcap->pca9556_transfer->i2c_wbuf[0] =
664*0Sstevel@tonic-gate 			    PCA9556_POLARITY_REG;
665*0Sstevel@tonic-gate 		break;
666*0Sstevel@tonic-gate 
667*0Sstevel@tonic-gate 	case GPIO_SET_CONFIG:
668*0Sstevel@tonic-gate 		write_io = B_TRUE;
669*0Sstevel@tonic-gate 		/*FALLTHROUGH*/
670*0Sstevel@tonic-gate 
671*0Sstevel@tonic-gate 	case GPIO_GET_CONFIG:
672*0Sstevel@tonic-gate 		if (pcap->pca9555_device)
673*0Sstevel@tonic-gate 			pcap->pca9556_transfer->i2c_wbuf[0] =
674*0Sstevel@tonic-gate 			    PCA9555_CONFIG_REG + port;
675*0Sstevel@tonic-gate 		else
676*0Sstevel@tonic-gate 			pcap->pca9556_transfer->i2c_wbuf[0] =
677*0Sstevel@tonic-gate 			    PCA9556_CONFIG_REG;
678*0Sstevel@tonic-gate 		break;
679*0Sstevel@tonic-gate 	}
680*0Sstevel@tonic-gate 
681*0Sstevel@tonic-gate 	/*
682*0Sstevel@tonic-gate 	 * Read the required register
683*0Sstevel@tonic-gate 	 */
684*0Sstevel@tonic-gate 	if (i2c_transfer(pcap->pca9556_hdl, pcap->pca9556_transfer)
685*0Sstevel@tonic-gate 	    != I2C_SUCCESS) {
686*0Sstevel@tonic-gate 		err = EIO;
687*0Sstevel@tonic-gate 
688*0Sstevel@tonic-gate 		goto cleanup;
689*0Sstevel@tonic-gate 	}
690*0Sstevel@tonic-gate 	/*
691*0Sstevel@tonic-gate 	 * Evaluate whether the register is to be read or modified
692*0Sstevel@tonic-gate 	 */
693*0Sstevel@tonic-gate 	if (!write_io) {
694*0Sstevel@tonic-gate 		g_buf.reg_val = g_buf.reg_mask &
695*0Sstevel@tonic-gate 		    pcap->pca9556_transfer->i2c_rbuf[0];
696*0Sstevel@tonic-gate 		err = ddi_copyout(&g_buf, (caddr_t)arg,
697*0Sstevel@tonic-gate 		    sizeof (i2c_gpio_t), mode);
698*0Sstevel@tonic-gate 	} else {
699*0Sstevel@tonic-gate 		pcap->pca9556_transfer->i2c_flags = I2C_WR;
700*0Sstevel@tonic-gate 		pcap->pca9556_transfer->i2c_wlen = 2;
701*0Sstevel@tonic-gate 		pcap->pca9556_transfer->i2c_rlen = 0;
702*0Sstevel@tonic-gate 
703*0Sstevel@tonic-gate 		/*
704*0Sstevel@tonic-gate 		 * Modify register without overwriting existing contents
705*0Sstevel@tonic-gate 		 */
706*0Sstevel@tonic-gate 
707*0Sstevel@tonic-gate 		temp = pcap->pca9556_transfer->i2c_rbuf[0] & (~g_buf.reg_mask);
708*0Sstevel@tonic-gate 		pcap->pca9556_transfer->i2c_wbuf[1] = temp|
709*0Sstevel@tonic-gate 		    (g_buf.reg_val & g_buf.reg_mask);
710*0Sstevel@tonic-gate 		if (i2c_transfer(pcap->pca9556_hdl, pcap->pca9556_transfer)
711*0Sstevel@tonic-gate 		    != I2C_SUCCESS) {
712*0Sstevel@tonic-gate 				err = EIO;
713*0Sstevel@tonic-gate 		}
714*0Sstevel@tonic-gate 
715*0Sstevel@tonic-gate 	}
716*0Sstevel@tonic-gate cleanup:
717*0Sstevel@tonic-gate 	mutex_enter(&pcap->pca9556_mutex);
718*0Sstevel@tonic-gate 	pcap->pca9556_flags  = pcap->pca9556_flags & ~PCA9556_BUSYFLAG;
719*0Sstevel@tonic-gate 	cv_signal(&pcap->pca9556_cv);
720*0Sstevel@tonic-gate 	mutex_exit(&pcap->pca9556_mutex);
721*0Sstevel@tonic-gate 	return (err);
722*0Sstevel@tonic-gate 	}
723