xref: /illumos-gate/usr/src/uts/sun4u/io/i2c/clients/adm1026.c (revision 88294e09b5c27cbb12b6735e2fb247a86b76666d)
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 #include <sys/stat.h>		/* ddi_create_minor_node S_IFCHR */
27 #include <sys/modctl.h>		/* for modldrv */
28 #include <sys/open.h>		/* for open params.	 */
29 #include <sys/types.h>
30 #include <sys/kmem.h>
31 #include <sys/sunddi.h>
32 #include <sys/conf.h>		/* req. by dev_ops flags MTSAFE etc. */
33 #include <sys/ddi.h>
34 #include <sys/file.h>
35 #include <sys/note.h>
36 #include <sys/i2c/clients/i2c_gpio.h>
37 #include <sys/i2c/clients/adm1026_impl.h>
38 
39 /*
40  * This driver supports the GPIO subset of the full ADM1026 register set.
41  * The driver is designed to allow modifying and reading the Polarity and
42  * Direction bits of the ADM1026's 16 GPIO pins via the 4 GPIO Config
43  * registers.  In addition, the driver supports modifying and reading
44  * the 16 GPIO pins via the 2 GPIO input/output registers.
45  *
46  * The 4 GPIO Config registers configure the direction and polarity of
47  * the 16 GPIO pins.  When a Polarity bit is set to 0, the GPIO pin is
48  * active low, otherwise, it is active high.  When a Direction bit is set
49  * to 0, the GPIO pin configured as an input; otherwise, it is an output.
50  *
51  * The 2 GPIO input/output registers (Status Register 5 & 6 ) behave as follows.
52  * When a GPIO pin is configured as an input, the bit is set when its GPIO
53  * pin is asserted.  When a GPIO pin is configured as an output, the bit
54  * asserts the GPIO pin.
55  *
56  * The commands supported in the ioctl routine are:
57  * GPIO_GET_OUTPUT   -- Read GPIO0-GPIO15 bits in Status Register 5 & 6
58  * GPIO_SET_OUTPUT   -- Modify GPIO0-GPIO15 bits in Status Register 5 & 6
59  * GPIO_GET_POLARITY -- Read GPIO0-GPIO15 Polarity bits in GPIO Config 1-4
60  * GPIO_SET_POLARITY -- Modify GPIO0-GPIO15 Polarity bits in GPIO Config 1-4
61  * GPIO_GET_CONFIG   -- Read GPIO0-GPIO15 Direction bits in GPIO Config 1-4
62  * GPIO_SET_CONFIG   -- Modify GPIO0-GPIO15 Direction bits in GPIO Config 1-4
63  *
64  * A pointer to the i2c_gpio_t data structure is sent as the third argument
65  * in the ioctl call.  The reg_mask and reg_val members of i2c_gpio_t are
66  * used to logically represent the 16 GPIO pins, thus only the lower 16 bits
67  * of each member is used.  The reg_mask member identifies the GPIO pin(s)
68  * that the user wants to read or modify and reg_val has the actual value of
69  * what the corresponding GPIO pin should be set to.
70  *
71  * For example, a reg_mask of 0x8001 indicates that the ioctl should only
72  * access GPIO15 and GPIO0.
73  */
74 
75 static void *adm1026soft_statep;
76 
77 static int adm1026_do_attach(dev_info_t *);
78 static int adm1026_do_detach(dev_info_t *);
79 static int adm1026_do_resume(void);
80 static int adm1026_do_suspend(void);
81 static int adm1026_get8(adm1026_unit_t *unitp, uint8_t reg, uint8_t *val);
82 static int adm1026_put8(adm1026_unit_t *unitp, uint8_t reg, uint8_t val);
83 
84 /*
85  * cb ops (only need ioctl)
86  */
87 static int adm1026_open(dev_t *, int, int, cred_t *);
88 static int adm1026_close(dev_t, int, int, cred_t *);
89 static int adm1026_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
90 
91 static struct cb_ops adm1026_cbops = {
92 	adm1026_open,			/* open  */
93 	adm1026_close,			/* close */
94 	nodev,				/* strategy */
95 	nodev,				/* print */
96 	nodev,				/* dump */
97 	nodev,				/* read */
98 	nodev,				/* write */
99 	adm1026_ioctl,			/* ioctl */
100 	nodev,				/* devmap */
101 	nodev,				/* mmap */
102 	nodev,				/* segmap */
103 	nochpoll,			/* poll */
104 	ddi_prop_op,			/* cb_prop_op */
105 	NULL,				/* streamtab */
106 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
107 	CB_REV,				/* rev */
108 	nodev,				/* int (*cb_aread)() */
109 	nodev				/* int (*cb_awrite)() */
110 };
111 
112 /*
113  * dev ops
114  */
115 static int adm1026_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
116 static int adm1026_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
117 
118 static struct dev_ops adm1026_ops = {
119 	DEVO_REV,
120 	0,
121 	ddi_getinfo_1to1,
122 	nulldev,
123 	nulldev,
124 	adm1026_attach,
125 	adm1026_detach,
126 	nodev,
127 	&adm1026_cbops,
128 	NULL,
129 	NULL,
130 	ddi_quiesce_not_needed,		/* quiesce */
131 };
132 
133 extern struct mod_ops mod_driverops;
134 
135 static struct modldrv adm1026_modldrv = {
136 	&mod_driverops,			/* type of module - driver */
137 	"ADM1026 i2c device driver",
138 	&adm1026_ops
139 };
140 
141 static struct modlinkage adm1026_modlinkage = {
142 	MODREV_1,
143 	&adm1026_modldrv,
144 	0
145 };
146 
147 
148 int
149 _init(void)
150 {
151 	int error;
152 
153 	error = mod_install(&adm1026_modlinkage);
154 
155 	if (!error)
156 		(void) ddi_soft_state_init(&adm1026soft_statep,
157 		    sizeof (struct adm1026_unit), 1);
158 	return (error);
159 }
160 
161 int
162 _fini(void)
163 {
164 	int error;
165 
166 	error = mod_remove(&adm1026_modlinkage);
167 	if (!error)
168 		ddi_soft_state_fini(&adm1026soft_statep);
169 
170 	return (error);
171 }
172 
173 int
174 _info(struct modinfo *modinfop)
175 {
176 	return (mod_info(&adm1026_modlinkage, modinfop));
177 }
178 
179 static int
180 adm1026_open(dev_t *devp, int flags, int otyp, cred_t *credp)
181 {
182 	_NOTE(ARGUNUSED(credp))
183 
184 	adm1026_unit_t *unitp;
185 	int instance;
186 	int error = 0;
187 
188 	instance = getminor(*devp);
189 
190 	D2CMN_ERR((CE_WARN, "adm1026_open: instance=%d\n", instance));
191 
192 	if (instance < 0) {
193 		return (ENXIO);
194 	}
195 
196 	unitp = (struct adm1026_unit *)
197 	    ddi_get_soft_state(adm1026soft_statep, instance);
198 
199 	if (unitp == NULL) {
200 		return (ENXIO);
201 	}
202 
203 	if (otyp != OTYP_CHR) {
204 		return (EINVAL);
205 	}
206 
207 	mutex_enter(&unitp->adm1026_mutex);
208 
209 	if (flags & FEXCL) {
210 		if (unitp->adm1026_oflag != 0) {
211 			error = EBUSY;
212 		} else {
213 			unitp->adm1026_oflag = FEXCL;
214 		}
215 	} else {
216 		if (unitp->adm1026_oflag == FEXCL) {
217 			error = EBUSY;
218 		} else {
219 			unitp->adm1026_oflag = FOPEN;
220 		}
221 	}
222 
223 	mutex_exit(&unitp->adm1026_mutex);
224 
225 	return (error);
226 }
227 
228 static int
229 adm1026_close(dev_t dev, int flags, int otyp, cred_t *credp)
230 {
231 	_NOTE(ARGUNUSED(flags, otyp, credp))
232 
233 	adm1026_unit_t *unitp;
234 	int instance;
235 
236 	instance = getminor(dev);
237 
238 	D2CMN_ERR((CE_WARN, "adm1026_close: instance=%d\n", instance));
239 
240 	if (instance < 0) {
241 		return (ENXIO);
242 	}
243 
244 	unitp = (struct adm1026_unit *)
245 	    ddi_get_soft_state(adm1026soft_statep, instance);
246 
247 	if (unitp == NULL) {
248 		return (ENXIO);
249 	}
250 
251 	mutex_enter(&unitp->adm1026_mutex);
252 
253 	unitp->adm1026_oflag = 0;
254 
255 	mutex_exit(&unitp->adm1026_mutex);
256 	return (DDI_SUCCESS);
257 }
258 
259 static int
260 adm1026_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
261 {
262 	D2CMN_ERR((CE_WARN, "adm1026_attach: cmd=%x\n", cmd));
263 
264 	switch (cmd) {
265 	case DDI_ATTACH:
266 		return (adm1026_do_attach(dip));
267 	case DDI_RESUME:
268 		return (adm1026_do_resume());
269 	default:
270 		return (DDI_FAILURE);
271 	}
272 }
273 
274 static int
275 adm1026_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
276 {
277 	D2CMN_ERR((CE_WARN, "adm1026_detach: cmd=%x\n", cmd));
278 	switch (cmd) {
279 	case DDI_DETACH:
280 		return (adm1026_do_detach(dip));
281 	case DDI_SUSPEND:
282 		return (adm1026_do_suspend());
283 	default:
284 		return (DDI_FAILURE);
285 	}
286 }
287 
288 static int
289 adm1026_do_attach(dev_info_t *dip)
290 {
291 	adm1026_unit_t *unitp;
292 	int instance;
293 
294 	instance = ddi_get_instance(dip);
295 
296 	D2CMN_ERR((CE_WARN, "adm1026_do_attach: instance=%d, dip=%p",
297 	    instance, (void *)dip));
298 
299 	if (ddi_soft_state_zalloc(adm1026soft_statep, instance) != 0) {
300 		cmn_err(CE_WARN, "%s%d: ddi_soft_state_zalloc() failed",
301 		    ddi_get_name(dip), instance);
302 		return (DDI_FAILURE);
303 	}
304 
305 	unitp = ddi_get_soft_state(adm1026soft_statep, instance);
306 
307 	if (unitp == NULL) {
308 		cmn_err(CE_WARN, "%s%d: ddi_get_soft_state(), no memory",
309 		    ddi_get_name(dip), instance);
310 		return (ENOMEM);
311 	}
312 
313 	D2CMN_ERR((CE_WARN, "adm1026_do_attach: ddi_create_minor_node"));
314 	if (ddi_create_minor_node(dip, "adm1026", S_IFCHR, instance,
315 	"ddi_i2c:led_control", NULL) == DDI_FAILURE) {
316 		cmn_err(CE_WARN,
317 		    "adm1026_do_attach: ddi_create_minor_node failed");
318 		ddi_soft_state_free(adm1026soft_statep, instance);
319 
320 		return (DDI_FAILURE);
321 	}
322 
323 	D2CMN_ERR((CE_WARN, "adm1026_do_attach: i2c_client_register"));
324 	if (i2c_client_register(dip, &unitp->adm1026_hdl) != I2C_SUCCESS) {
325 		ddi_remove_minor_node(dip, NULL);
326 		ddi_soft_state_free(adm1026soft_statep, instance);
327 		cmn_err(CE_WARN,
328 		    "adm1026_do_attach: i2c_client_register failed");
329 
330 		return (DDI_FAILURE);
331 	}
332 
333 	mutex_init(&unitp->adm1026_mutex, NULL, MUTEX_DRIVER, NULL);
334 
335 	D2CMN_ERR((CE_WARN, "adm1026_do_attach: DDI_SUCCESS"));
336 	return (DDI_SUCCESS);
337 }
338 
339 static int
340 adm1026_do_resume(void)
341 {
342 	int ret = DDI_SUCCESS;
343 
344 	return (ret);
345 }
346 
347 static int
348 adm1026_do_suspend()
349 {
350 	int ret = DDI_SUCCESS;
351 
352 	return (ret);
353 }
354 
355 static int
356 adm1026_do_detach(dev_info_t *dip)
357 {
358 	adm1026_unit_t *unitp;
359 	int instance;
360 
361 	instance = ddi_get_instance(dip);
362 
363 	unitp = ddi_get_soft_state(adm1026soft_statep, instance);
364 
365 	if (unitp == NULL) {
366 		cmn_err(CE_WARN,
367 		    "adm1026_do_detach: ddi_get_soft_state failed");
368 		return (ENOMEM);
369 	}
370 
371 	i2c_client_unregister(unitp->adm1026_hdl);
372 
373 	ddi_remove_minor_node(dip, NULL);
374 
375 	mutex_destroy(&unitp->adm1026_mutex);
376 	ddi_soft_state_free(adm1026soft_statep, instance);
377 
378 	return (DDI_SUCCESS);
379 }
380 
381 static int
382 adm1026_get8(adm1026_unit_t *unitp, uint8_t reg, uint8_t *val)
383 {
384 	i2c_transfer_t		*i2c_tran_pointer = NULL;
385 	int			err = DDI_SUCCESS;
386 
387 	(void) i2c_transfer_alloc(unitp->adm1026_hdl, &i2c_tran_pointer,
388 	    1, 1, I2C_SLEEP);
389 	if (i2c_tran_pointer == NULL)
390 		return (ENOMEM);
391 
392 	i2c_tran_pointer->i2c_flags = I2C_WR_RD;
393 	i2c_tran_pointer->i2c_wbuf[0] = (uchar_t)reg;
394 	err = i2c_transfer(unitp->adm1026_hdl, i2c_tran_pointer);
395 	if (err) {
396 		D1CMN_ERR((CE_WARN,
397 		    "adm1026_get8: I2C_WR_RD reg=0x%x failed", reg));
398 	} else {
399 		*val = i2c_tran_pointer->i2c_rbuf[0];
400 		D1CMN_ERR((CE_WARN, "adm1026_get8: reg=%02x, val=%02x",
401 		    reg, *val));
402 	}
403 	i2c_transfer_free(unitp->adm1026_hdl, i2c_tran_pointer);
404 
405 	return (err);
406 }
407 
408 static int
409 adm1026_put8(adm1026_unit_t *unitp, uint8_t reg, uint8_t val)
410 {
411 	i2c_transfer_t		*i2c_tran_pointer = NULL;
412 	int			err = DDI_SUCCESS;
413 
414 	D1CMN_ERR((CE_WARN, "adm1026_put8: reg=%02x, val=%02x\n", reg, val));
415 
416 	(void) i2c_transfer_alloc(unitp->adm1026_hdl, &i2c_tran_pointer,
417 	    2, 0, I2C_SLEEP);
418 	if (i2c_tran_pointer == NULL)
419 		return (ENOMEM);
420 
421 	i2c_tran_pointer->i2c_flags = I2C_WR;
422 	i2c_tran_pointer->i2c_wbuf[0] = reg;
423 	i2c_tran_pointer->i2c_wbuf[1] = val;
424 
425 	err = i2c_transfer(unitp->adm1026_hdl, i2c_tran_pointer);
426 	if (err)
427 		D2CMN_ERR((CE_WARN, "adm1026_put8: return=%x", err));
428 
429 	i2c_transfer_free(unitp->adm1026_hdl, i2c_tran_pointer);
430 
431 	return (err);
432 }
433 
434 /*
435  * adm1026_send8:
436  * Read the i2c register, apply the mask to contents so that only
437  * bits in mask affected. Or in value and write it back to the i2c register.
438  */
439 static int
440 adm1026_send8(adm1026_unit_t *unitp, uint8_t reg, uint8_t reg_val,
441 			uint8_t reg_mask)
442 {
443 	uint8_t val = 0;
444 	int err;
445 
446 	if ((err = adm1026_get8(unitp, reg, &val)) != I2C_SUCCESS)
447 		return (err);
448 	val &= ~reg_mask;
449 	val |= (reg_val & reg_mask);
450 
451 	return (adm1026_put8(unitp, reg, val));
452 }
453 
454 /*
455  * adm1026_set_output:
456  * The low 16 bits of the mask is a 1:1 mask indicating which of the
457  * 16 GPIO pin(s) to set.
458  */
459 static int
460 adm1026_set_output(adm1026_unit_t *unitp, uint32_t val, uint32_t mask)
461 {
462 	int err = I2C_SUCCESS;
463 
464 	if (mask & 0xff)
465 		err = adm1026_send8(unitp, ADM1026_STS_REG5, (uint8_t)val,
466 		    (uint8_t)mask);
467 
468 	if ((err == I2C_SUCCESS) && (mask & 0xff00))
469 		err = adm1026_send8(unitp, ADM1026_STS_REG6,
470 		    (uint8_t)(val >> OUTPUT_SHIFT),
471 		    (uint8_t)(mask >> OUTPUT_SHIFT));
472 
473 	return (err);
474 }
475 
476 /*
477  * adm1026_get_output:
478  * The low 16 bits of the mask is a 1:1 mask indicating which of the
479  * 16 GPIO pin(s) to get.
480  */
481 static int
482 adm1026_get_output(adm1026_unit_t *unitp, uint32_t mask, uint32_t *val)
483 {
484 	uint8_t reg_val = 0;
485 	int err = I2C_SUCCESS;
486 
487 	if (mask & 0xff) {
488 		err = adm1026_get8(unitp, ADM1026_STS_REG5, &reg_val);
489 		if (err != I2C_SUCCESS)
490 			return (err);
491 
492 		*val = reg_val;
493 	}
494 
495 	if (mask & 0xff00) {
496 		err = adm1026_get8(unitp, ADM1026_STS_REG6, &reg_val);
497 		if (err != I2C_SUCCESS)
498 			return (err);
499 
500 		*val |= ((reg_val << OUTPUT_SHIFT) & (mask & 0xff00));
501 	}
502 
503 	return (err);
504 }
505 
506 /*
507  * adm1026_set_config:
508  * The low 16 bits of the mask is a 1:1 mask indicating which of the
509  * 16 GPIO pin(s) to set the polarity or direction configuration for.
510  * Each GPIO pin has 2 bits of configuration - 1 polarity bit and 1
511  * direction bit.  Traverse the mask 4 bits at a time to determine
512  * which of the 4 GPIO Config registers to access and apply the value
513  * based on whether cmd is GPIO_SET_CONFIG (set Direction) or
514  * GPIO_SET_POLARITY.
515  */
516 static int
517 adm1026_set_config(adm1026_unit_t *unitp, int cmd, uint32_t val, uint32_t mask)
518 {
519 	int i;
520 	uint8_t r;
521 	uint32_t m = mask, v = val;
522 	int err = I2C_SUCCESS;
523 
524 	for (i = 0, r = ADM1026_GPIO_CFG1; i < BYTES_PER_CONFIG; i++, r++) {
525 		if (m & GPIO_CFG_MASK) {
526 			int j;
527 			uint8_t mm = 0, vv = 0;
528 			uint8_t bit = (cmd == GPIO_SET_CONFIG) ?
529 			    DIR_BIT : POLARITY_BIT;
530 
531 			for (j = 0; j < GPIOS_PER_CFG_BYTE; j++) {
532 				if (m & (1 << j)) {
533 					mm |= (bit << (j * BITSPERCFG));
534 				}
535 				if (v & (1 << j)) {
536 					vv |= (bit << (j * BITSPERCFG));
537 				}
538 			}
539 			D2CMN_ERR((CE_WARN, "adm1026_set_config: r=%02x, "
540 			    "vv=%02x, mm=%02x, m=%02x", r, vv, mm, m));
541 			err = adm1026_send8(unitp, r, vv, mm);
542 			if (err != I2C_SUCCESS)
543 				return (err);
544 		}
545 		m >>= GPIOS_PER_CFG_BYTE;
546 		v >>= GPIOS_PER_CFG_BYTE;
547 	}
548 	return (err);
549 }
550 
551 /*
552  * adm1026_get_config:
553  * The low 16 bits of the mask is a 1:1 mask indicating which of the
554  * 16 GPIO pin(s) to get the polarity or direction configuration for.
555  * Each GPIO pin has 2 bits of configuration - 1 polarity bit and 1
556  * direction bit.  Traverse the mask 4 bits at a time to determine
557  * which of the 4 GPIO Config registers to access and build the return
558  * value based on whether cmd is GPIO_GET_CONFIG (get Direction) or
559  * GPIO_GET_POLARITY.
560  */
561 static int
562 adm1026_get_config(adm1026_unit_t *unitp, int cmd, uint32_t mask, uint32_t *val)
563 {
564 	int i, j;
565 	uint8_t r;
566 	int err = I2C_SUCCESS;
567 
568 	*val = 0;
569 
570 	for (i = 0, r = ADM1026_GPIO_CFG1; i < BYTES_PER_CONFIG; i++, r++) {
571 		if (mask & GPIO_CFG_MASK) {
572 			uint8_t newval = 0, x;
573 			uint8_t bit = (cmd == GPIO_GET_CONFIG) ?
574 			    DIR_BIT : POLARITY_BIT;
575 
576 			err = adm1026_get8(unitp, r, &x);
577 			if (err != I2C_SUCCESS)
578 				return (err);
579 			for (j = 0; j < GPIOS_PER_CFG_BYTE; j++) {
580 				if (mask & (1 << j)) {
581 					if (x & (bit << (j * BITSPERCFG)))
582 						newval |= (1 << j);
583 				}
584 			}
585 			*val |= (newval << (i * GPIOS_PER_CFG_BYTE));
586 		} else
587 			*val <<= GPIOS_PER_CFG_BYTE;
588 
589 		mask >>= GPIOS_PER_CFG_BYTE;
590 	}
591 	return (err);
592 }
593 
594 static int
595 adm1026_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
596 		int *rvalp)
597 {
598 	_NOTE(ARGUNUSED(credp, rvalp))
599 
600 	adm1026_unit_t		*unitp;
601 	int			instance;
602 	int			err = DDI_SUCCESS;
603 	i2c_gpio_t		g_buf;
604 
605 	instance = getminor(dev);
606 
607 	D2CMN_ERR((CE_WARN, "adm1026_ioctl: instance=%d, cmd=%x\n",
608 	    instance, cmd));
609 
610 	unitp = (struct adm1026_unit *)
611 	    ddi_get_soft_state(adm1026soft_statep, instance);
612 
613 	if (unitp == NULL) {
614 		cmn_err(CE_WARN, "adm1026_ioctl: ddi_get_soft_state failed");
615 		err = ENOMEM;
616 		return (err);
617 	}
618 
619 	mutex_enter(&unitp->adm1026_mutex);
620 
621 	if (ddi_copyin((caddr_t)arg, &g_buf,
622 	    sizeof (i2c_gpio_t), mode) != DDI_SUCCESS) {
623 
624 		mutex_exit(&unitp->adm1026_mutex);
625 		return (EFAULT);
626 	}
627 	if (g_buf.reg_mask & 0xffff0000) {
628 		cmn_err(CE_WARN,
629 		    "adm1026_ioctl: reg_mask too large. "
630 		    "Only bits 15-0 supported");
631 		mutex_exit(&unitp->adm1026_mutex);
632 		return (EINVAL);
633 	}
634 	switch (cmd) {
635 	case GPIO_SET_OUTPUT:
636 		err = adm1026_set_output(unitp, g_buf.reg_val, g_buf.reg_mask);
637 		break;
638 
639 	case GPIO_GET_OUTPUT:
640 		err = adm1026_get_output(unitp, g_buf.reg_mask, &g_buf.reg_val);
641 		if (err == DDI_SUCCESS)
642 			err = ddi_copyout(&g_buf, (caddr_t)arg,
643 			    sizeof (i2c_gpio_t), mode);
644 		break;
645 
646 	case GPIO_SET_CONFIG:
647 	case GPIO_SET_POLARITY:
648 		err = adm1026_set_config(unitp, cmd, g_buf.reg_val,
649 		    g_buf.reg_mask);
650 		break;
651 
652 	case GPIO_GET_CONFIG:
653 	case GPIO_GET_POLARITY:
654 		err = adm1026_get_config(unitp, cmd, g_buf.reg_mask,
655 		    &g_buf.reg_val);
656 		if (err == DDI_SUCCESS)
657 			err = ddi_copyout(&g_buf, (caddr_t)arg,
658 			    sizeof (i2c_gpio_t), mode);
659 		break;
660 	default:
661 		D2CMN_ERR((CE_WARN,
662 		    "adm1026_ioctl: Invalid ioctl cmd %x\n", cmd));
663 		err = EINVAL;
664 	}
665 	mutex_exit(&unitp->adm1026_mutex);
666 
667 	if (err) {
668 		D2CMN_ERR((CE_WARN,
669 		    "adm1026_ioctl: failed, err=%x\n", err));
670 		if (err == DDI_FAILURE)
671 			err = EIO;
672 	}
673 
674 	return (err);
675 }
676