xref: /onnv-gate/usr/src/uts/sun4u/io/i2c/clients/adm1026.c (revision 7696:21f5c73c0c15)
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, &reg_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, &reg_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