1920Sjbeloro /*
2920Sjbeloro * CDDL HEADER START
3920Sjbeloro *
4920Sjbeloro * The contents of this file are subject to the terms of the
57656SSherry.Moore@Sun.COM * Common Development and Distribution License (the "License").
67656SSherry.Moore@Sun.COM * You may not use this file except in compliance with the License.
7920Sjbeloro *
8920Sjbeloro * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9920Sjbeloro * or http://www.opensolaris.org/os/licensing.
10920Sjbeloro * See the License for the specific language governing permissions
11920Sjbeloro * and limitations under the License.
12920Sjbeloro *
13920Sjbeloro * When distributing Covered Code, include this CDDL HEADER in each
14920Sjbeloro * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15920Sjbeloro * If applicable, add the following below this CDDL HEADER, with the
16920Sjbeloro * fields enclosed by brackets "[]" replaced with your own identifying
17920Sjbeloro * information: Portions Copyright [yyyy] [name of copyright owner]
18920Sjbeloro *
19920Sjbeloro * CDDL HEADER END
20920Sjbeloro */
21920Sjbeloro /*
227656SSherry.Moore@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23920Sjbeloro * Use is subject to license terms.
24920Sjbeloro */
25920Sjbeloro
26920Sjbeloro #include <sys/stat.h> /* ddi_create_minor_node S_IFCHR */
27920Sjbeloro #include <sys/modctl.h> /* for modldrv */
28920Sjbeloro #include <sys/open.h> /* for open params. */
29920Sjbeloro #include <sys/types.h>
30920Sjbeloro #include <sys/kmem.h>
31920Sjbeloro #include <sys/sunddi.h>
32920Sjbeloro #include <sys/conf.h> /* req. by dev_ops flags MTSAFE etc. */
33920Sjbeloro #include <sys/ddi.h>
34920Sjbeloro #include <sys/file.h>
35920Sjbeloro #include <sys/note.h>
36920Sjbeloro #include <sys/i2c/clients/i2c_gpio.h>
37920Sjbeloro #include <sys/i2c/clients/adm1026_impl.h>
38920Sjbeloro
39920Sjbeloro /*
40920Sjbeloro * This driver supports the GPIO subset of the full ADM1026 register set.
41920Sjbeloro * The driver is designed to allow modifying and reading the Polarity and
42920Sjbeloro * Direction bits of the ADM1026's 16 GPIO pins via the 4 GPIO Config
43920Sjbeloro * registers. In addition, the driver supports modifying and reading
44920Sjbeloro * the 16 GPIO pins via the 2 GPIO input/output registers.
45920Sjbeloro *
46920Sjbeloro * The 4 GPIO Config registers configure the direction and polarity of
47920Sjbeloro * the 16 GPIO pins. When a Polarity bit is set to 0, the GPIO pin is
48920Sjbeloro * active low, otherwise, it is active high. When a Direction bit is set
49920Sjbeloro * to 0, the GPIO pin configured as an input; otherwise, it is an output.
50920Sjbeloro *
51920Sjbeloro * The 2 GPIO input/output registers (Status Register 5 & 6 ) behave as follows.
52920Sjbeloro * When a GPIO pin is configured as an input, the bit is set when its GPIO
53920Sjbeloro * pin is asserted. When a GPIO pin is configured as an output, the bit
54920Sjbeloro * asserts the GPIO pin.
55920Sjbeloro *
56920Sjbeloro * The commands supported in the ioctl routine are:
57920Sjbeloro * GPIO_GET_OUTPUT -- Read GPIO0-GPIO15 bits in Status Register 5 & 6
58920Sjbeloro * GPIO_SET_OUTPUT -- Modify GPIO0-GPIO15 bits in Status Register 5 & 6
59920Sjbeloro * GPIO_GET_POLARITY -- Read GPIO0-GPIO15 Polarity bits in GPIO Config 1-4
60920Sjbeloro * GPIO_SET_POLARITY -- Modify GPIO0-GPIO15 Polarity bits in GPIO Config 1-4
61920Sjbeloro * GPIO_GET_CONFIG -- Read GPIO0-GPIO15 Direction bits in GPIO Config 1-4
62920Sjbeloro * GPIO_SET_CONFIG -- Modify GPIO0-GPIO15 Direction bits in GPIO Config 1-4
63920Sjbeloro *
64920Sjbeloro * A pointer to the i2c_gpio_t data structure is sent as the third argument
65920Sjbeloro * in the ioctl call. The reg_mask and reg_val members of i2c_gpio_t are
66920Sjbeloro * used to logically represent the 16 GPIO pins, thus only the lower 16 bits
67920Sjbeloro * of each member is used. The reg_mask member identifies the GPIO pin(s)
68920Sjbeloro * that the user wants to read or modify and reg_val has the actual value of
69920Sjbeloro * what the corresponding GPIO pin should be set to.
70920Sjbeloro *
71920Sjbeloro * For example, a reg_mask of 0x8001 indicates that the ioctl should only
72920Sjbeloro * access GPIO15 and GPIO0.
73920Sjbeloro */
74920Sjbeloro
75920Sjbeloro static void *adm1026soft_statep;
76920Sjbeloro
77920Sjbeloro static int adm1026_do_attach(dev_info_t *);
78920Sjbeloro static int adm1026_do_detach(dev_info_t *);
79920Sjbeloro static int adm1026_do_resume(void);
80920Sjbeloro static int adm1026_do_suspend(void);
81920Sjbeloro static int adm1026_get8(adm1026_unit_t *unitp, uint8_t reg, uint8_t *val);
82920Sjbeloro static int adm1026_put8(adm1026_unit_t *unitp, uint8_t reg, uint8_t val);
83920Sjbeloro
84920Sjbeloro /*
85920Sjbeloro * cb ops (only need ioctl)
86920Sjbeloro */
87920Sjbeloro static int adm1026_open(dev_t *, int, int, cred_t *);
88920Sjbeloro static int adm1026_close(dev_t, int, int, cred_t *);
89920Sjbeloro static int adm1026_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
90920Sjbeloro
91920Sjbeloro static struct cb_ops adm1026_cbops = {
92920Sjbeloro adm1026_open, /* open */
93920Sjbeloro adm1026_close, /* close */
94920Sjbeloro nodev, /* strategy */
95920Sjbeloro nodev, /* print */
96920Sjbeloro nodev, /* dump */
97920Sjbeloro nodev, /* read */
98920Sjbeloro nodev, /* write */
99920Sjbeloro adm1026_ioctl, /* ioctl */
100920Sjbeloro nodev, /* devmap */
101920Sjbeloro nodev, /* mmap */
102920Sjbeloro nodev, /* segmap */
103920Sjbeloro nochpoll, /* poll */
104920Sjbeloro ddi_prop_op, /* cb_prop_op */
105920Sjbeloro NULL, /* streamtab */
106920Sjbeloro D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
107920Sjbeloro CB_REV, /* rev */
108920Sjbeloro nodev, /* int (*cb_aread)() */
109920Sjbeloro nodev /* int (*cb_awrite)() */
110920Sjbeloro };
111920Sjbeloro
112920Sjbeloro /*
113920Sjbeloro * dev ops
114920Sjbeloro */
115920Sjbeloro static int adm1026_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
116920Sjbeloro static int adm1026_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
117920Sjbeloro
118920Sjbeloro static struct dev_ops adm1026_ops = {
119920Sjbeloro DEVO_REV,
120920Sjbeloro 0,
121920Sjbeloro ddi_getinfo_1to1,
122920Sjbeloro nulldev,
123920Sjbeloro nulldev,
124920Sjbeloro adm1026_attach,
125920Sjbeloro adm1026_detach,
126920Sjbeloro nodev,
127920Sjbeloro &adm1026_cbops,
1287656SSherry.Moore@Sun.COM NULL,
1297656SSherry.Moore@Sun.COM NULL,
1307656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* quiesce */
131920Sjbeloro };
132920Sjbeloro
133920Sjbeloro extern struct mod_ops mod_driverops;
134920Sjbeloro
135920Sjbeloro static struct modldrv adm1026_modldrv = {
136920Sjbeloro &mod_driverops, /* type of module - driver */
137*7696SRichard.Bean@Sun.COM "ADM1026 i2c device driver",
138920Sjbeloro &adm1026_ops
139920Sjbeloro };
140920Sjbeloro
141920Sjbeloro static struct modlinkage adm1026_modlinkage = {
142920Sjbeloro MODREV_1,
143920Sjbeloro &adm1026_modldrv,
144920Sjbeloro 0
145920Sjbeloro };
146920Sjbeloro
147920Sjbeloro
148920Sjbeloro int
_init(void)149920Sjbeloro _init(void)
150920Sjbeloro {
151920Sjbeloro int error;
152920Sjbeloro
153920Sjbeloro error = mod_install(&adm1026_modlinkage);
154920Sjbeloro
155920Sjbeloro if (!error)
156920Sjbeloro (void) ddi_soft_state_init(&adm1026soft_statep,
157920Sjbeloro sizeof (struct adm1026_unit), 1);
158920Sjbeloro return (error);
159920Sjbeloro }
160920Sjbeloro
161920Sjbeloro int
_fini(void)162920Sjbeloro _fini(void)
163920Sjbeloro {
164920Sjbeloro int error;
165920Sjbeloro
166920Sjbeloro error = mod_remove(&adm1026_modlinkage);
167920Sjbeloro if (!error)
168920Sjbeloro ddi_soft_state_fini(&adm1026soft_statep);
169920Sjbeloro
170920Sjbeloro return (error);
171920Sjbeloro }
172920Sjbeloro
173920Sjbeloro int
_info(struct modinfo * modinfop)174920Sjbeloro _info(struct modinfo *modinfop)
175920Sjbeloro {
176920Sjbeloro return (mod_info(&adm1026_modlinkage, modinfop));
177920Sjbeloro }
178920Sjbeloro
179920Sjbeloro static int
adm1026_open(dev_t * devp,int flags,int otyp,cred_t * credp)180920Sjbeloro adm1026_open(dev_t *devp, int flags, int otyp, cred_t *credp)
181920Sjbeloro {
182920Sjbeloro _NOTE(ARGUNUSED(credp))
183920Sjbeloro
184920Sjbeloro adm1026_unit_t *unitp;
185920Sjbeloro int instance;
186920Sjbeloro int error = 0;
187920Sjbeloro
188920Sjbeloro instance = getminor(*devp);
189920Sjbeloro
190920Sjbeloro D2CMN_ERR((CE_WARN, "adm1026_open: instance=%d\n", instance));
191920Sjbeloro
192920Sjbeloro if (instance < 0) {
193920Sjbeloro return (ENXIO);
194920Sjbeloro }
195920Sjbeloro
196920Sjbeloro unitp = (struct adm1026_unit *)
197920Sjbeloro ddi_get_soft_state(adm1026soft_statep, instance);
198920Sjbeloro
199920Sjbeloro if (unitp == NULL) {
200920Sjbeloro return (ENXIO);
201920Sjbeloro }
202920Sjbeloro
203920Sjbeloro if (otyp != OTYP_CHR) {
204920Sjbeloro return (EINVAL);
205920Sjbeloro }
206920Sjbeloro
207920Sjbeloro mutex_enter(&unitp->adm1026_mutex);
208920Sjbeloro
209920Sjbeloro if (flags & FEXCL) {
210920Sjbeloro if (unitp->adm1026_oflag != 0) {
211920Sjbeloro error = EBUSY;
212920Sjbeloro } else {
213920Sjbeloro unitp->adm1026_oflag = FEXCL;
214920Sjbeloro }
215920Sjbeloro } else {
216920Sjbeloro if (unitp->adm1026_oflag == FEXCL) {
217920Sjbeloro error = EBUSY;
218920Sjbeloro } else {
219920Sjbeloro unitp->adm1026_oflag = FOPEN;
220920Sjbeloro }
221920Sjbeloro }
222920Sjbeloro
223920Sjbeloro mutex_exit(&unitp->adm1026_mutex);
224920Sjbeloro
225920Sjbeloro return (error);
226920Sjbeloro }
227920Sjbeloro
228920Sjbeloro static int
adm1026_close(dev_t dev,int flags,int otyp,cred_t * credp)229920Sjbeloro adm1026_close(dev_t dev, int flags, int otyp, cred_t *credp)
230920Sjbeloro {
231920Sjbeloro _NOTE(ARGUNUSED(flags, otyp, credp))
232920Sjbeloro
233920Sjbeloro adm1026_unit_t *unitp;
234920Sjbeloro int instance;
235920Sjbeloro
236920Sjbeloro instance = getminor(dev);
237920Sjbeloro
238920Sjbeloro D2CMN_ERR((CE_WARN, "adm1026_close: instance=%d\n", instance));
239920Sjbeloro
240920Sjbeloro if (instance < 0) {
241920Sjbeloro return (ENXIO);
242920Sjbeloro }
243920Sjbeloro
244920Sjbeloro unitp = (struct adm1026_unit *)
245920Sjbeloro ddi_get_soft_state(adm1026soft_statep, instance);
246920Sjbeloro
247920Sjbeloro if (unitp == NULL) {
248920Sjbeloro return (ENXIO);
249920Sjbeloro }
250920Sjbeloro
251920Sjbeloro mutex_enter(&unitp->adm1026_mutex);
252920Sjbeloro
253920Sjbeloro unitp->adm1026_oflag = 0;
254920Sjbeloro
255920Sjbeloro mutex_exit(&unitp->adm1026_mutex);
256920Sjbeloro return (DDI_SUCCESS);
257920Sjbeloro }
258920Sjbeloro
259920Sjbeloro static int
adm1026_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)260920Sjbeloro adm1026_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
261920Sjbeloro {
262920Sjbeloro D2CMN_ERR((CE_WARN, "adm1026_attach: cmd=%x\n", cmd));
263920Sjbeloro
264920Sjbeloro switch (cmd) {
265920Sjbeloro case DDI_ATTACH:
266920Sjbeloro return (adm1026_do_attach(dip));
267920Sjbeloro case DDI_RESUME:
268920Sjbeloro return (adm1026_do_resume());
269920Sjbeloro default:
270920Sjbeloro return (DDI_FAILURE);
271920Sjbeloro }
272920Sjbeloro }
273920Sjbeloro
274920Sjbeloro static int
adm1026_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)275920Sjbeloro adm1026_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
276920Sjbeloro {
277920Sjbeloro D2CMN_ERR((CE_WARN, "adm1026_detach: cmd=%x\n", cmd));
278920Sjbeloro switch (cmd) {
279920Sjbeloro case DDI_DETACH:
280920Sjbeloro return (adm1026_do_detach(dip));
281920Sjbeloro case DDI_SUSPEND:
282920Sjbeloro return (adm1026_do_suspend());
283920Sjbeloro default:
284920Sjbeloro return (DDI_FAILURE);
285920Sjbeloro }
286920Sjbeloro }
287920Sjbeloro
288920Sjbeloro static int
adm1026_do_attach(dev_info_t * dip)289920Sjbeloro adm1026_do_attach(dev_info_t *dip)
290920Sjbeloro {
291920Sjbeloro adm1026_unit_t *unitp;
292920Sjbeloro int instance;
293920Sjbeloro
294920Sjbeloro instance = ddi_get_instance(dip);
295920Sjbeloro
296920Sjbeloro D2CMN_ERR((CE_WARN, "adm1026_do_attach: instance=%d, dip=%p",
297920Sjbeloro instance, (void *)dip));
298920Sjbeloro
299920Sjbeloro if (ddi_soft_state_zalloc(adm1026soft_statep, instance) != 0) {
300920Sjbeloro cmn_err(CE_WARN, "%s%d: ddi_soft_state_zalloc() failed",
301920Sjbeloro ddi_get_name(dip), instance);
302920Sjbeloro return (DDI_FAILURE);
303920Sjbeloro }
304920Sjbeloro
305920Sjbeloro unitp = ddi_get_soft_state(adm1026soft_statep, instance);
306920Sjbeloro
307920Sjbeloro if (unitp == NULL) {
308920Sjbeloro cmn_err(CE_WARN, "%s%d: ddi_get_soft_state(), no memory",
309920Sjbeloro ddi_get_name(dip), instance);
310920Sjbeloro return (ENOMEM);
311920Sjbeloro }
312920Sjbeloro
313920Sjbeloro D2CMN_ERR((CE_WARN, "adm1026_do_attach: ddi_create_minor_node"));
314920Sjbeloro if (ddi_create_minor_node(dip, "adm1026", S_IFCHR, instance,
3157656SSherry.Moore@Sun.COM "ddi_i2c:led_control", NULL) == DDI_FAILURE) {
316920Sjbeloro cmn_err(CE_WARN,
317920Sjbeloro "adm1026_do_attach: ddi_create_minor_node failed");
318920Sjbeloro ddi_soft_state_free(adm1026soft_statep, instance);
319920Sjbeloro
320920Sjbeloro return (DDI_FAILURE);
321920Sjbeloro }
322920Sjbeloro
323920Sjbeloro D2CMN_ERR((CE_WARN, "adm1026_do_attach: i2c_client_register"));
324920Sjbeloro if (i2c_client_register(dip, &unitp->adm1026_hdl) != I2C_SUCCESS) {
325920Sjbeloro ddi_remove_minor_node(dip, NULL);
326920Sjbeloro ddi_soft_state_free(adm1026soft_statep, instance);
327920Sjbeloro cmn_err(CE_WARN,
328920Sjbeloro "adm1026_do_attach: i2c_client_register failed");
329920Sjbeloro
330920Sjbeloro return (DDI_FAILURE);
331920Sjbeloro }
332920Sjbeloro
333920Sjbeloro mutex_init(&unitp->adm1026_mutex, NULL, MUTEX_DRIVER, NULL);
334920Sjbeloro
335920Sjbeloro D2CMN_ERR((CE_WARN, "adm1026_do_attach: DDI_SUCCESS"));
336920Sjbeloro return (DDI_SUCCESS);
337920Sjbeloro }
338920Sjbeloro
339920Sjbeloro static int
adm1026_do_resume(void)340920Sjbeloro adm1026_do_resume(void)
341920Sjbeloro {
342920Sjbeloro int ret = DDI_SUCCESS;
343920Sjbeloro
344920Sjbeloro return (ret);
345920Sjbeloro }
346920Sjbeloro
347920Sjbeloro static int
adm1026_do_suspend()348920Sjbeloro adm1026_do_suspend()
349920Sjbeloro {
350920Sjbeloro int ret = DDI_SUCCESS;
351920Sjbeloro
352920Sjbeloro return (ret);
353920Sjbeloro }
354920Sjbeloro
355920Sjbeloro static int
adm1026_do_detach(dev_info_t * dip)356920Sjbeloro adm1026_do_detach(dev_info_t *dip)
357920Sjbeloro {
358920Sjbeloro adm1026_unit_t *unitp;
359920Sjbeloro int instance;
360920Sjbeloro
361920Sjbeloro instance = ddi_get_instance(dip);
362920Sjbeloro
363920Sjbeloro unitp = ddi_get_soft_state(adm1026soft_statep, instance);
364920Sjbeloro
365920Sjbeloro if (unitp == NULL) {
366920Sjbeloro cmn_err(CE_WARN,
367920Sjbeloro "adm1026_do_detach: ddi_get_soft_state failed");
368920Sjbeloro return (ENOMEM);
369920Sjbeloro }
370920Sjbeloro
371920Sjbeloro i2c_client_unregister(unitp->adm1026_hdl);
372920Sjbeloro
373920Sjbeloro ddi_remove_minor_node(dip, NULL);
374920Sjbeloro
375920Sjbeloro mutex_destroy(&unitp->adm1026_mutex);
376920Sjbeloro ddi_soft_state_free(adm1026soft_statep, instance);
377920Sjbeloro
378920Sjbeloro return (DDI_SUCCESS);
379920Sjbeloro }
380920Sjbeloro
381920Sjbeloro static int
adm1026_get8(adm1026_unit_t * unitp,uint8_t reg,uint8_t * val)382920Sjbeloro adm1026_get8(adm1026_unit_t *unitp, uint8_t reg, uint8_t *val)
383920Sjbeloro {
384920Sjbeloro i2c_transfer_t *i2c_tran_pointer = NULL;
385920Sjbeloro int err = DDI_SUCCESS;
386920Sjbeloro
387920Sjbeloro (void) i2c_transfer_alloc(unitp->adm1026_hdl, &i2c_tran_pointer,
388920Sjbeloro 1, 1, I2C_SLEEP);
389920Sjbeloro if (i2c_tran_pointer == NULL)
390920Sjbeloro return (ENOMEM);
391920Sjbeloro
392920Sjbeloro i2c_tran_pointer->i2c_flags = I2C_WR_RD;
393920Sjbeloro i2c_tran_pointer->i2c_wbuf[0] = (uchar_t)reg;
394920Sjbeloro err = i2c_transfer(unitp->adm1026_hdl, i2c_tran_pointer);
395920Sjbeloro if (err) {
396920Sjbeloro D1CMN_ERR((CE_WARN,
397920Sjbeloro "adm1026_get8: I2C_WR_RD reg=0x%x failed", reg));
398920Sjbeloro } else {
399920Sjbeloro *val = i2c_tran_pointer->i2c_rbuf[0];
400920Sjbeloro D1CMN_ERR((CE_WARN, "adm1026_get8: reg=%02x, val=%02x",
401920Sjbeloro reg, *val));
402920Sjbeloro }
403920Sjbeloro i2c_transfer_free(unitp->adm1026_hdl, i2c_tran_pointer);
404920Sjbeloro
405920Sjbeloro return (err);
406920Sjbeloro }
407920Sjbeloro
408920Sjbeloro static int
adm1026_put8(adm1026_unit_t * unitp,uint8_t reg,uint8_t val)409920Sjbeloro adm1026_put8(adm1026_unit_t *unitp, uint8_t reg, uint8_t val)
410920Sjbeloro {
411920Sjbeloro i2c_transfer_t *i2c_tran_pointer = NULL;
412920Sjbeloro int err = DDI_SUCCESS;
413920Sjbeloro
414920Sjbeloro D1CMN_ERR((CE_WARN, "adm1026_put8: reg=%02x, val=%02x\n", reg, val));
415920Sjbeloro
416920Sjbeloro (void) i2c_transfer_alloc(unitp->adm1026_hdl, &i2c_tran_pointer,
417920Sjbeloro 2, 0, I2C_SLEEP);
418920Sjbeloro if (i2c_tran_pointer == NULL)
419920Sjbeloro return (ENOMEM);
420920Sjbeloro
421920Sjbeloro i2c_tran_pointer->i2c_flags = I2C_WR;
422920Sjbeloro i2c_tran_pointer->i2c_wbuf[0] = reg;
423920Sjbeloro i2c_tran_pointer->i2c_wbuf[1] = val;
424920Sjbeloro
425920Sjbeloro err = i2c_transfer(unitp->adm1026_hdl, i2c_tran_pointer);
426920Sjbeloro if (err)
427920Sjbeloro D2CMN_ERR((CE_WARN, "adm1026_put8: return=%x", err));
428920Sjbeloro
429920Sjbeloro i2c_transfer_free(unitp->adm1026_hdl, i2c_tran_pointer);
430920Sjbeloro
431920Sjbeloro return (err);
432920Sjbeloro }
433920Sjbeloro
434920Sjbeloro /*
435920Sjbeloro * adm1026_send8:
436920Sjbeloro * Read the i2c register, apply the mask to contents so that only
437920Sjbeloro * bits in mask affected. Or in value and write it back to the i2c register.
438920Sjbeloro */
439920Sjbeloro static int
adm1026_send8(adm1026_unit_t * unitp,uint8_t reg,uint8_t reg_val,uint8_t reg_mask)440920Sjbeloro adm1026_send8(adm1026_unit_t *unitp, uint8_t reg, uint8_t reg_val,
441920Sjbeloro uint8_t reg_mask)
442920Sjbeloro {
443920Sjbeloro uint8_t val = 0;
444920Sjbeloro int err;
445920Sjbeloro
446920Sjbeloro if ((err = adm1026_get8(unitp, reg, &val)) != I2C_SUCCESS)
447920Sjbeloro return (err);
448920Sjbeloro val &= ~reg_mask;
449920Sjbeloro val |= (reg_val & reg_mask);
450920Sjbeloro
451920Sjbeloro return (adm1026_put8(unitp, reg, val));
452920Sjbeloro }
453920Sjbeloro
454920Sjbeloro /*
455920Sjbeloro * adm1026_set_output:
456920Sjbeloro * The low 16 bits of the mask is a 1:1 mask indicating which of the
457920Sjbeloro * 16 GPIO pin(s) to set.
458920Sjbeloro */
459920Sjbeloro static int
adm1026_set_output(adm1026_unit_t * unitp,uint32_t val,uint32_t mask)460920Sjbeloro adm1026_set_output(adm1026_unit_t *unitp, uint32_t val, uint32_t mask)
461920Sjbeloro {
462920Sjbeloro int err = I2C_SUCCESS;
463920Sjbeloro
464920Sjbeloro if (mask & 0xff)
465920Sjbeloro err = adm1026_send8(unitp, ADM1026_STS_REG5, (uint8_t)val,
466920Sjbeloro (uint8_t)mask);
467920Sjbeloro
468920Sjbeloro if ((err == I2C_SUCCESS) && (mask & 0xff00))
469920Sjbeloro err = adm1026_send8(unitp, ADM1026_STS_REG6,
470920Sjbeloro (uint8_t)(val >> OUTPUT_SHIFT),
471920Sjbeloro (uint8_t)(mask >> OUTPUT_SHIFT));
472920Sjbeloro
473920Sjbeloro return (err);
474920Sjbeloro }
475920Sjbeloro
476920Sjbeloro /*
477920Sjbeloro * adm1026_get_output:
478920Sjbeloro * The low 16 bits of the mask is a 1:1 mask indicating which of the
479920Sjbeloro * 16 GPIO pin(s) to get.
480920Sjbeloro */
481920Sjbeloro static int
adm1026_get_output(adm1026_unit_t * unitp,uint32_t mask,uint32_t * val)482920Sjbeloro adm1026_get_output(adm1026_unit_t *unitp, uint32_t mask, uint32_t *val)
483920Sjbeloro {
484920Sjbeloro uint8_t reg_val = 0;
485920Sjbeloro int err = I2C_SUCCESS;
486920Sjbeloro
487920Sjbeloro if (mask & 0xff) {
488920Sjbeloro err = adm1026_get8(unitp, ADM1026_STS_REG5, ®_val);
489920Sjbeloro if (err != I2C_SUCCESS)
490920Sjbeloro return (err);
491920Sjbeloro
492920Sjbeloro *val = reg_val;
493920Sjbeloro }
494920Sjbeloro
495920Sjbeloro if (mask & 0xff00) {
496920Sjbeloro err = adm1026_get8(unitp, ADM1026_STS_REG6, ®_val);
497920Sjbeloro if (err != I2C_SUCCESS)
498920Sjbeloro return (err);
499920Sjbeloro
500920Sjbeloro *val |= ((reg_val << OUTPUT_SHIFT) & (mask & 0xff00));
501920Sjbeloro }
502920Sjbeloro
503920Sjbeloro return (err);
504920Sjbeloro }
505920Sjbeloro
506920Sjbeloro /*
507920Sjbeloro * adm1026_set_config:
508920Sjbeloro * The low 16 bits of the mask is a 1:1 mask indicating which of the
509920Sjbeloro * 16 GPIO pin(s) to set the polarity or direction configuration for.
510920Sjbeloro * Each GPIO pin has 2 bits of configuration - 1 polarity bit and 1
511920Sjbeloro * direction bit. Traverse the mask 4 bits at a time to determine
512920Sjbeloro * which of the 4 GPIO Config registers to access and apply the value
513920Sjbeloro * based on whether cmd is GPIO_SET_CONFIG (set Direction) or
514920Sjbeloro * GPIO_SET_POLARITY.
515920Sjbeloro */
516920Sjbeloro static int
adm1026_set_config(adm1026_unit_t * unitp,int cmd,uint32_t val,uint32_t mask)517920Sjbeloro adm1026_set_config(adm1026_unit_t *unitp, int cmd, uint32_t val, uint32_t mask)
518920Sjbeloro {
519920Sjbeloro int i;
520920Sjbeloro uint8_t r;
521920Sjbeloro uint32_t m = mask, v = val;
522920Sjbeloro int err = I2C_SUCCESS;
523920Sjbeloro
524920Sjbeloro for (i = 0, r = ADM1026_GPIO_CFG1; i < BYTES_PER_CONFIG; i++, r++) {
525920Sjbeloro if (m & GPIO_CFG_MASK) {
526920Sjbeloro int j;
527920Sjbeloro uint8_t mm = 0, vv = 0;
528920Sjbeloro uint8_t bit = (cmd == GPIO_SET_CONFIG) ?
529920Sjbeloro DIR_BIT : POLARITY_BIT;
530920Sjbeloro
531920Sjbeloro for (j = 0; j < GPIOS_PER_CFG_BYTE; j++) {
532920Sjbeloro if (m & (1 << j)) {
533920Sjbeloro mm |= (bit << (j * BITSPERCFG));
534920Sjbeloro }
535920Sjbeloro if (v & (1 << j)) {
536920Sjbeloro vv |= (bit << (j * BITSPERCFG));
537920Sjbeloro }
538920Sjbeloro }
539920Sjbeloro D2CMN_ERR((CE_WARN, "adm1026_set_config: r=%02x, "
540920Sjbeloro "vv=%02x, mm=%02x, m=%02x", r, vv, mm, m));
541920Sjbeloro err = adm1026_send8(unitp, r, vv, mm);
542920Sjbeloro if (err != I2C_SUCCESS)
543920Sjbeloro return (err);
544920Sjbeloro }
545920Sjbeloro m >>= GPIOS_PER_CFG_BYTE;
546920Sjbeloro v >>= GPIOS_PER_CFG_BYTE;
547920Sjbeloro }
548920Sjbeloro return (err);
549920Sjbeloro }
550920Sjbeloro
551920Sjbeloro /*
552920Sjbeloro * adm1026_get_config:
553920Sjbeloro * The low 16 bits of the mask is a 1:1 mask indicating which of the
554920Sjbeloro * 16 GPIO pin(s) to get the polarity or direction configuration for.
555920Sjbeloro * Each GPIO pin has 2 bits of configuration - 1 polarity bit and 1
556920Sjbeloro * direction bit. Traverse the mask 4 bits at a time to determine
557920Sjbeloro * which of the 4 GPIO Config registers to access and build the return
558920Sjbeloro * value based on whether cmd is GPIO_GET_CONFIG (get Direction) or
559920Sjbeloro * GPIO_GET_POLARITY.
560920Sjbeloro */
561920Sjbeloro static int
adm1026_get_config(adm1026_unit_t * unitp,int cmd,uint32_t mask,uint32_t * val)562920Sjbeloro adm1026_get_config(adm1026_unit_t *unitp, int cmd, uint32_t mask, uint32_t *val)
563920Sjbeloro {
564920Sjbeloro int i, j;
565920Sjbeloro uint8_t r;
566920Sjbeloro int err = I2C_SUCCESS;
567920Sjbeloro
568920Sjbeloro *val = 0;
569920Sjbeloro
570920Sjbeloro for (i = 0, r = ADM1026_GPIO_CFG1; i < BYTES_PER_CONFIG; i++, r++) {
571920Sjbeloro if (mask & GPIO_CFG_MASK) {
572920Sjbeloro uint8_t newval = 0, x;
573920Sjbeloro uint8_t bit = (cmd == GPIO_GET_CONFIG) ?
574920Sjbeloro DIR_BIT : POLARITY_BIT;
575920Sjbeloro
576920Sjbeloro err = adm1026_get8(unitp, r, &x);
577920Sjbeloro if (err != I2C_SUCCESS)
578920Sjbeloro return (err);
579920Sjbeloro for (j = 0; j < GPIOS_PER_CFG_BYTE; j++) {
580920Sjbeloro if (mask & (1 << j)) {
581920Sjbeloro if (x & (bit << (j * BITSPERCFG)))
582920Sjbeloro newval |= (1 << j);
583920Sjbeloro }
584920Sjbeloro }
585920Sjbeloro *val |= (newval << (i * GPIOS_PER_CFG_BYTE));
586920Sjbeloro } else
587920Sjbeloro *val <<= GPIOS_PER_CFG_BYTE;
588920Sjbeloro
589920Sjbeloro mask >>= GPIOS_PER_CFG_BYTE;
590920Sjbeloro }
591920Sjbeloro return (err);
592920Sjbeloro }
593920Sjbeloro
594920Sjbeloro static int
adm1026_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)595920Sjbeloro adm1026_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
596920Sjbeloro int *rvalp)
597920Sjbeloro {
598920Sjbeloro _NOTE(ARGUNUSED(credp, rvalp))
599920Sjbeloro
600920Sjbeloro adm1026_unit_t *unitp;
601920Sjbeloro int instance;
602920Sjbeloro int err = DDI_SUCCESS;
603920Sjbeloro i2c_gpio_t g_buf;
604920Sjbeloro
605920Sjbeloro instance = getminor(dev);
606920Sjbeloro
607920Sjbeloro D2CMN_ERR((CE_WARN, "adm1026_ioctl: instance=%d, cmd=%x\n",
608920Sjbeloro instance, cmd));
609920Sjbeloro
610920Sjbeloro unitp = (struct adm1026_unit *)
6117656SSherry.Moore@Sun.COM ddi_get_soft_state(adm1026soft_statep, instance);
612920Sjbeloro
613920Sjbeloro if (unitp == NULL) {
614920Sjbeloro cmn_err(CE_WARN, "adm1026_ioctl: ddi_get_soft_state failed");
615920Sjbeloro err = ENOMEM;
616920Sjbeloro return (err);
617920Sjbeloro }
618920Sjbeloro
619920Sjbeloro mutex_enter(&unitp->adm1026_mutex);
620920Sjbeloro
621920Sjbeloro if (ddi_copyin((caddr_t)arg, &g_buf,
622920Sjbeloro sizeof (i2c_gpio_t), mode) != DDI_SUCCESS) {
623920Sjbeloro
624920Sjbeloro mutex_exit(&unitp->adm1026_mutex);
625920Sjbeloro return (EFAULT);
626920Sjbeloro }
627920Sjbeloro if (g_buf.reg_mask & 0xffff0000) {
628920Sjbeloro cmn_err(CE_WARN,
629920Sjbeloro "adm1026_ioctl: reg_mask too large. "
630920Sjbeloro "Only bits 15-0 supported");
631920Sjbeloro mutex_exit(&unitp->adm1026_mutex);
632920Sjbeloro return (EINVAL);
633920Sjbeloro }
634920Sjbeloro switch (cmd) {
635920Sjbeloro case GPIO_SET_OUTPUT:
636920Sjbeloro err = adm1026_set_output(unitp, g_buf.reg_val, g_buf.reg_mask);
637920Sjbeloro break;
638920Sjbeloro
639920Sjbeloro case GPIO_GET_OUTPUT:
640920Sjbeloro err = adm1026_get_output(unitp, g_buf.reg_mask, &g_buf.reg_val);
641920Sjbeloro if (err == DDI_SUCCESS)
642920Sjbeloro err = ddi_copyout(&g_buf, (caddr_t)arg,
643920Sjbeloro sizeof (i2c_gpio_t), mode);
644920Sjbeloro break;
645920Sjbeloro
646920Sjbeloro case GPIO_SET_CONFIG:
647920Sjbeloro case GPIO_SET_POLARITY:
648920Sjbeloro err = adm1026_set_config(unitp, cmd, g_buf.reg_val,
649920Sjbeloro g_buf.reg_mask);
650920Sjbeloro break;
651920Sjbeloro
652920Sjbeloro case GPIO_GET_CONFIG:
653920Sjbeloro case GPIO_GET_POLARITY:
654920Sjbeloro err = adm1026_get_config(unitp, cmd, g_buf.reg_mask,
655920Sjbeloro &g_buf.reg_val);
656920Sjbeloro if (err == DDI_SUCCESS)
657920Sjbeloro err = ddi_copyout(&g_buf, (caddr_t)arg,
658920Sjbeloro sizeof (i2c_gpio_t), mode);
659920Sjbeloro break;
660920Sjbeloro default:
661920Sjbeloro D2CMN_ERR((CE_WARN,
662920Sjbeloro "adm1026_ioctl: Invalid ioctl cmd %x\n", cmd));
663920Sjbeloro err = EINVAL;
664920Sjbeloro }
665920Sjbeloro mutex_exit(&unitp->adm1026_mutex);
666920Sjbeloro
667920Sjbeloro if (err) {
668920Sjbeloro D2CMN_ERR((CE_WARN,
669920Sjbeloro "adm1026_ioctl: failed, err=%x\n", err));
670920Sjbeloro if (err == DDI_FAILURE)
671920Sjbeloro err = EIO;
672920Sjbeloro }
673920Sjbeloro
674920Sjbeloro return (err);
675920Sjbeloro }
676