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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)174 _info(struct modinfo *modinfop)
175 {
176 return (mod_info(&adm1026_modlinkage, modinfop));
177 }
178
179 static int
adm1026_open(dev_t * devp,int flags,int otyp,cred_t * credp)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
adm1026_close(dev_t dev,int flags,int otyp,cred_t * credp)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
adm1026_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
adm1026_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
adm1026_do_attach(dev_info_t * dip)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", 0) == 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
adm1026_do_resume(void)340 adm1026_do_resume(void)
341 {
342 int ret = DDI_SUCCESS;
343
344 return (ret);
345 }
346
347 static int
adm1026_do_suspend()348 adm1026_do_suspend()
349 {
350 int ret = DDI_SUCCESS;
351
352 return (ret);
353 }
354
355 static int
adm1026_do_detach(dev_info_t * dip)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
adm1026_get8(adm1026_unit_t * unitp,uint8_t reg,uint8_t * val)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
adm1026_put8(adm1026_unit_t * unitp,uint8_t reg,uint8_t val)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
adm1026_send8(adm1026_unit_t * unitp,uint8_t reg,uint8_t reg_val,uint8_t reg_mask)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
adm1026_set_output(adm1026_unit_t * unitp,uint32_t val,uint32_t mask)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
adm1026_get_output(adm1026_unit_t * unitp,uint32_t mask,uint32_t * val)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, ®_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, ®_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
adm1026_set_config(adm1026_unit_t * unitp,int cmd,uint32_t val,uint32_t mask)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
adm1026_get_config(adm1026_unit_t * unitp,int cmd,uint32_t mask,uint32_t * val)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
adm1026_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)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