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