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