xref: /netbsd-src/sys/arch/zaurus/dev/ioexp.c (revision aa41e9922cf39f098b31d90c04e1deda4cbc10cf)
1*aa41e992Sthorpej /*	$NetBSD: ioexp.c,v 1.2 2018/06/16 21:22:13 thorpej Exp $	*/
2003bdf4cSnonaka 
3003bdf4cSnonaka /*-
4003bdf4cSnonaka  * Copyright (c) 2011 The NetBSD Foundation, Inc.
5003bdf4cSnonaka  * All rights reserved.
6003bdf4cSnonaka  *
7003bdf4cSnonaka  * This code is derived from software contributed to The NetBSD Foundation
8003bdf4cSnonaka  * by NONAKA Kimihiro.
9003bdf4cSnonaka  *
10003bdf4cSnonaka  * Redistribution and use in source and binary forms, with or without
11003bdf4cSnonaka  * modification, are permitted provided that the following conditions
12003bdf4cSnonaka  * are met:
13003bdf4cSnonaka  * 1. Redistributions of source code must retain the above copyright
14003bdf4cSnonaka  *    notice, this list of conditions and the following disclaimer.
15003bdf4cSnonaka  * 2. Redistributions in binary form must reproduce the above copyright
16003bdf4cSnonaka  *    notice, this list of conditions and the following disclaimer in the
17003bdf4cSnonaka  *    documentation and/or other materials provided with the distribution.
18003bdf4cSnonaka  *
19003bdf4cSnonaka  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20003bdf4cSnonaka  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21003bdf4cSnonaka  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22003bdf4cSnonaka  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23003bdf4cSnonaka  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24003bdf4cSnonaka  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25003bdf4cSnonaka  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26003bdf4cSnonaka  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27003bdf4cSnonaka  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28003bdf4cSnonaka  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29003bdf4cSnonaka  * POSSIBILITY OF SUCH DAMAGE.
30003bdf4cSnonaka  */
31003bdf4cSnonaka 
32003bdf4cSnonaka #include <sys/cdefs.h>
33*aa41e992Sthorpej __KERNEL_RCSID(0, "$NetBSD: ioexp.c,v 1.2 2018/06/16 21:22:13 thorpej Exp $");
34003bdf4cSnonaka 
35003bdf4cSnonaka #include <sys/param.h>
36003bdf4cSnonaka #include <sys/systm.h>
37003bdf4cSnonaka #include <sys/device.h>
38003bdf4cSnonaka #include <sys/gpio.h>
39003bdf4cSnonaka 
40003bdf4cSnonaka #include <dev/i2c/i2cvar.h>
41003bdf4cSnonaka 
42003bdf4cSnonaka #include <arm/xscale/pxa2x0reg.h>
43003bdf4cSnonaka #include <arm/xscale/pxa2x0var.h>
44003bdf4cSnonaka #include <arm/xscale/pxa2x0_i2c.h>
45003bdf4cSnonaka 
46003bdf4cSnonaka #include <zaurus/zaurus/zaurus_var.h>
47003bdf4cSnonaka #include <zaurus/dev/ioexpreg.h>
48003bdf4cSnonaka #include <zaurus/dev/ioexpvar.h>
49003bdf4cSnonaka 
50003bdf4cSnonaka #include "ioconf.h"
51003bdf4cSnonaka 
52003bdf4cSnonaka struct ioexp_softc {
53003bdf4cSnonaka 	device_t	sc_dev;
54003bdf4cSnonaka 	i2c_tag_t	sc_i2c;
55003bdf4cSnonaka 
56003bdf4cSnonaka 	uint8_t		sc_output;
57003bdf4cSnonaka 	uint8_t		sc_direction;
58003bdf4cSnonaka 
59003bdf4cSnonaka 	int		sc_inited;
60003bdf4cSnonaka };
61003bdf4cSnonaka 
62003bdf4cSnonaka static int ioexp_match(device_t, cfdata_t, void *);
63003bdf4cSnonaka static void ioexp_attach(device_t, device_t, void *);
64003bdf4cSnonaka 
65003bdf4cSnonaka CFATTACH_DECL_NEW(ioexp, sizeof(struct ioexp_softc),
66003bdf4cSnonaka     ioexp_match, ioexp_attach, NULL, NULL);
67003bdf4cSnonaka 
68003bdf4cSnonaka static uint8_t output_init_value = IOEXP_IR_ON | IOEXP_AKIN_PULLUP;
69003bdf4cSnonaka static uint8_t direction_init_value = 0;
70003bdf4cSnonaka 
71003bdf4cSnonaka static __inline int
ioexp_write(struct ioexp_softc * sc,uint8_t reg,uint8_t val)72003bdf4cSnonaka ioexp_write(struct ioexp_softc *sc, uint8_t reg, uint8_t val)
73003bdf4cSnonaka {
74003bdf4cSnonaka 	uint8_t cmd;
75003bdf4cSnonaka 	uint8_t data;
76003bdf4cSnonaka 	int error;
77003bdf4cSnonaka 
78003bdf4cSnonaka 	cmd = reg;
79003bdf4cSnonaka 	data = val;
80003bdf4cSnonaka 	error = iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP, IOEXP_ADDRESS,
81003bdf4cSnonaka 	    &cmd, 1, &data, 1, 0);
82003bdf4cSnonaka 	return error;
83003bdf4cSnonaka }
84003bdf4cSnonaka 
85003bdf4cSnonaka static int
ioexp_match(device_t parent,cfdata_t cf,void * aux)86003bdf4cSnonaka ioexp_match(device_t parent, cfdata_t cf, void *aux)
87003bdf4cSnonaka {
88003bdf4cSnonaka 	struct i2c_attach_args *ia = aux;
89*aa41e992Sthorpej 	int match_result;
90003bdf4cSnonaka 
91003bdf4cSnonaka 	/* only for SL-C1000 */
92003bdf4cSnonaka 	if (!ZAURUS_ISC1000)
93003bdf4cSnonaka 		return 0;
94003bdf4cSnonaka 
95*aa41e992Sthorpej 	if (iic_use_direct_match(ia, cf, NULL, &match_result))
96*aa41e992Sthorpej 		return match_result;
97*aa41e992Sthorpej 
98003bdf4cSnonaka 	/* indirect config - check typical address */
99003bdf4cSnonaka 	if (ia->ia_addr == IOEXP_ADDRESS)
100*aa41e992Sthorpej 		return I2C_MATCH_ADDRESS_ONLY;
101*aa41e992Sthorpej 
102003bdf4cSnonaka 	return 0;
103003bdf4cSnonaka }
104003bdf4cSnonaka 
105003bdf4cSnonaka static void
ioexp_attach(device_t parent,device_t self,void * aux)106003bdf4cSnonaka ioexp_attach(device_t parent, device_t self, void *aux)
107003bdf4cSnonaka {
108003bdf4cSnonaka 	struct ioexp_softc *sc = device_private(self);
109003bdf4cSnonaka 	struct i2c_attach_args *ia = aux;
110003bdf4cSnonaka 
111003bdf4cSnonaka 	sc->sc_dev = self;
112003bdf4cSnonaka 	sc->sc_i2c = ia->ia_tag;
113003bdf4cSnonaka 
114003bdf4cSnonaka 	aprint_normal(": GPIO controller\n");
115003bdf4cSnonaka 	aprint_naive("\n");
116003bdf4cSnonaka 
117003bdf4cSnonaka 	sc->sc_output = output_init_value;
118003bdf4cSnonaka 	sc->sc_direction = direction_init_value;
119003bdf4cSnonaka 
120003bdf4cSnonaka 	iic_acquire_bus(sc->sc_i2c, 0);
121003bdf4cSnonaka 	ioexp_write(sc, IOEXP_POLARITY, 0);
122003bdf4cSnonaka 	ioexp_write(sc, IOEXP_OUTPUT, sc->sc_output);
123003bdf4cSnonaka 	ioexp_write(sc, IOEXP_DIRECTION, sc->sc_direction);
124003bdf4cSnonaka 	iic_release_bus(sc->sc_i2c, 0);
125003bdf4cSnonaka 
126003bdf4cSnonaka 	sc->sc_inited = 1;
127003bdf4cSnonaka }
128003bdf4cSnonaka 
129003bdf4cSnonaka #if 0
130003bdf4cSnonaka static void
131003bdf4cSnonaka ioexp_gpio_pin_ctl(struct ioexp_softc *sc, uint8_t bit, int flags,
132003bdf4cSnonaka     bool acquire_bus)
133003bdf4cSnonaka {
134003bdf4cSnonaka 	int error;
135003bdf4cSnonaka 
136003bdf4cSnonaka 	if (acquire_bus) {
137003bdf4cSnonaka 		error = iic_acquire_bus(sc->sc_i2c, 0);
138003bdf4cSnonaka 		if (error) {
139003bdf4cSnonaka 			aprint_error_dev(sc->sc_dev,
140003bdf4cSnonaka 			    "unable to acquire bus. error=%d\n", error);
141003bdf4cSnonaka 			return;
142003bdf4cSnonaka 		}
143003bdf4cSnonaka 	}
144003bdf4cSnonaka 
145003bdf4cSnonaka 	switch (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
146003bdf4cSnonaka 	case GPIO_PIN_INPUT:
147003bdf4cSnonaka 		sc->sc_direction |= bit;
148003bdf4cSnonaka 		break;
149003bdf4cSnonaka 	case GPIO_PIN_OUTPUT:
150003bdf4cSnonaka 		sc->sc_direction &= ~bit;
151003bdf4cSnonaka 		break;
152003bdf4cSnonaka 	}
153003bdf4cSnonaka 	error = ioexp_write(sc, IOEXP_DIRECTION, sc->sc_direction);
154003bdf4cSnonaka 	if (error)
155003bdf4cSnonaka 		aprint_error_dev(sc->sc_dev,
156003bdf4cSnonaka 		    "direction write failed. error=%d\n", error);
157003bdf4cSnonaka 
158003bdf4cSnonaka 	if (acquire_bus)
159003bdf4cSnonaka 		iic_release_bus(sc->sc_i2c, 0);
160003bdf4cSnonaka }
161003bdf4cSnonaka #endif
162003bdf4cSnonaka 
163003bdf4cSnonaka static void
ioexp_gpio_pin_write(struct ioexp_softc * sc,uint8_t bit,int level,bool acquire_bus)164003bdf4cSnonaka ioexp_gpio_pin_write(struct ioexp_softc *sc, uint8_t bit, int level,
165003bdf4cSnonaka     bool acquire_bus)
166003bdf4cSnonaka {
167003bdf4cSnonaka 	int error;
168003bdf4cSnonaka 
169003bdf4cSnonaka 	if (acquire_bus) {
170003bdf4cSnonaka 		error = iic_acquire_bus(sc->sc_i2c, 0);
171003bdf4cSnonaka 		if (error) {
172003bdf4cSnonaka 			aprint_error_dev(sc->sc_dev,
173003bdf4cSnonaka 			    "unable to acquire bus. error=%d\n", error);
174003bdf4cSnonaka 			return;
175003bdf4cSnonaka 		}
176003bdf4cSnonaka 	}
177003bdf4cSnonaka 
178003bdf4cSnonaka 	if (level == GPIO_PIN_LOW)
179003bdf4cSnonaka 		sc->sc_output &= ~bit;
180003bdf4cSnonaka 	else
181003bdf4cSnonaka 		sc->sc_output |= bit;
182003bdf4cSnonaka 	error = ioexp_write(sc, IOEXP_OUTPUT, sc->sc_output);
183003bdf4cSnonaka 	if (error)
184003bdf4cSnonaka 		aprint_error_dev(sc->sc_dev,
185003bdf4cSnonaka 		    "output write failed. error=%d\n", error);
186003bdf4cSnonaka 
187003bdf4cSnonaka 	if (acquire_bus)
188003bdf4cSnonaka 		iic_release_bus(sc->sc_i2c, 0);
189003bdf4cSnonaka }
190003bdf4cSnonaka 
191003bdf4cSnonaka static __inline uint8_t
ioexp_gpio_pin_get(struct ioexp_softc * sc,uint8_t bit)192003bdf4cSnonaka ioexp_gpio_pin_get(struct ioexp_softc *sc, uint8_t bit)
193003bdf4cSnonaka {
194003bdf4cSnonaka 
195003bdf4cSnonaka 	return sc->sc_output & bit;
196003bdf4cSnonaka }
197003bdf4cSnonaka 
198003bdf4cSnonaka /*
199003bdf4cSnonaka  * Turn the LCD background light and contrast signal on or off.
200003bdf4cSnonaka  */
201003bdf4cSnonaka void
ioexp_set_backlight(int onoff,int cont)202003bdf4cSnonaka ioexp_set_backlight(int onoff, int cont)
203003bdf4cSnonaka {
204003bdf4cSnonaka 	struct ioexp_softc *sc = device_lookup_private(&ioexp_cd, 0);
205003bdf4cSnonaka 
206003bdf4cSnonaka 	if (sc == NULL || !sc->sc_inited) {
207003bdf4cSnonaka #ifdef DEBUG
208003bdf4cSnonaka 		aprint_error("ioexp: %s: not attached or not inited\n",
209003bdf4cSnonaka 		    __func__);
210003bdf4cSnonaka #endif
211003bdf4cSnonaka 		if (onoff)
212003bdf4cSnonaka 			output_init_value |= IOEXP_BACKLIGHT_ON;
213003bdf4cSnonaka 		else
214003bdf4cSnonaka 			output_init_value &= ~IOEXP_BACKLIGHT_ON;
215003bdf4cSnonaka 		/* BACKLIGHT_CONT is inverted */
216003bdf4cSnonaka 		if (cont)
217003bdf4cSnonaka 			output_init_value &= ~IOEXP_BACKLIGHT_CONT;
218003bdf4cSnonaka 		else
219003bdf4cSnonaka 			output_init_value |= IOEXP_BACKLIGHT_CONT;
220003bdf4cSnonaka 		return;
221003bdf4cSnonaka 	}
222003bdf4cSnonaka 
223003bdf4cSnonaka 	if (sc != NULL) {
224003bdf4cSnonaka 		uint8_t bkreg = ioexp_gpio_pin_get(sc, IOEXP_BACKLIGHT_ON);
225003bdf4cSnonaka 		uint8_t contreg = ioexp_gpio_pin_get(sc, IOEXP_BACKLIGHT_CONT);
226003bdf4cSnonaka 
227003bdf4cSnonaka 		if (onoff && !bkreg) {
228003bdf4cSnonaka 			ioexp_gpio_pin_write(sc, IOEXP_BACKLIGHT_ON,
229003bdf4cSnonaka 			    GPIO_PIN_HIGH, true);
230003bdf4cSnonaka 		} else if (!onoff && bkreg) {
231003bdf4cSnonaka 			ioexp_gpio_pin_write(sc, IOEXP_BACKLIGHT_ON,
232003bdf4cSnonaka 			    GPIO_PIN_LOW, true);
233003bdf4cSnonaka 		}
234003bdf4cSnonaka 
235003bdf4cSnonaka 		/* BACKLIGHT_CONT is inverted */
236003bdf4cSnonaka 		if (cont && contreg) {
237003bdf4cSnonaka 			ioexp_gpio_pin_write(sc, IOEXP_BACKLIGHT_CONT,
238003bdf4cSnonaka 			    GPIO_PIN_LOW, true);
239003bdf4cSnonaka 		} else if (!cont && !contreg) {
240003bdf4cSnonaka 			ioexp_gpio_pin_write(sc, IOEXP_BACKLIGHT_CONT,
241003bdf4cSnonaka 			    GPIO_PIN_HIGH, true);
242003bdf4cSnonaka 		}
243003bdf4cSnonaka 	}
244003bdf4cSnonaka }
245003bdf4cSnonaka 
246003bdf4cSnonaka /*
247003bdf4cSnonaka  * Turn the infrared LED on or off (must be on while transmitting).
248003bdf4cSnonaka  */
249003bdf4cSnonaka void
ioexp_set_irled(int onoff)250003bdf4cSnonaka ioexp_set_irled(int onoff)
251003bdf4cSnonaka {
252003bdf4cSnonaka 	struct ioexp_softc *sc = device_lookup_private(&ioexp_cd, 0);
253003bdf4cSnonaka 
254003bdf4cSnonaka 	if (sc == NULL || !sc->sc_inited) {
255003bdf4cSnonaka #ifdef DEBUG
256003bdf4cSnonaka 		aprint_error("ioexp: %s: not attached or not inited\n",
257003bdf4cSnonaka 		    __func__);
258003bdf4cSnonaka #endif
259003bdf4cSnonaka 		/* IR_ON is inverted */
260003bdf4cSnonaka 		if (onoff)
261003bdf4cSnonaka 			output_init_value &= ~IOEXP_IR_ON;
262003bdf4cSnonaka 		else
263003bdf4cSnonaka 			output_init_value |= IOEXP_IR_ON;
264003bdf4cSnonaka 		return;
265003bdf4cSnonaka 	}
266003bdf4cSnonaka 
267003bdf4cSnonaka 	if (sc != NULL) {
268003bdf4cSnonaka 		/* IR_ON is inverted */
269003bdf4cSnonaka 		uint8_t reg = ioexp_gpio_pin_get(sc, IOEXP_IR_ON);
270003bdf4cSnonaka 		if (onoff && reg) {
271003bdf4cSnonaka 			ioexp_gpio_pin_write(sc, IOEXP_IR_ON, GPIO_PIN_LOW,
272003bdf4cSnonaka 			    true);
273003bdf4cSnonaka 		} else if (!onoff && !reg) {
274003bdf4cSnonaka 			ioexp_gpio_pin_write(sc, IOEXP_IR_ON, GPIO_PIN_HIGH,
275003bdf4cSnonaka 			    true);
276003bdf4cSnonaka 		}
277003bdf4cSnonaka 	}
278003bdf4cSnonaka }
279003bdf4cSnonaka 
280003bdf4cSnonaka /*
281003bdf4cSnonaka  * Enable or disable the mic bias
282003bdf4cSnonaka  */
283003bdf4cSnonaka void
ioexp_set_mic_bias(int onoff)284003bdf4cSnonaka ioexp_set_mic_bias(int onoff)
285003bdf4cSnonaka {
286003bdf4cSnonaka 	struct ioexp_softc *sc = device_lookup_private(&ioexp_cd, 0);
287003bdf4cSnonaka 
288003bdf4cSnonaka 	if (sc == NULL || !sc->sc_inited) {
289003bdf4cSnonaka #ifdef DEBUG
290003bdf4cSnonaka 		aprint_error("ioexp: %s: not attached or not inited\n",
291003bdf4cSnonaka 		    __func__);
292003bdf4cSnonaka #endif
293003bdf4cSnonaka 		if (onoff)
294003bdf4cSnonaka 			output_init_value |= IOEXP_MIC_BIAS;
295003bdf4cSnonaka 		else
296003bdf4cSnonaka 			output_init_value &= ~IOEXP_MIC_BIAS;
297003bdf4cSnonaka 		return;
298003bdf4cSnonaka 	}
299003bdf4cSnonaka 
300003bdf4cSnonaka 	if (sc != NULL) {
301003bdf4cSnonaka 		uint8_t reg = ioexp_gpio_pin_get(sc, IOEXP_MIC_BIAS);
302003bdf4cSnonaka 		if (onoff && !reg) {
303003bdf4cSnonaka 			ioexp_gpio_pin_write(sc, IOEXP_MIC_BIAS, GPIO_PIN_HIGH,
304003bdf4cSnonaka 			    false);
305003bdf4cSnonaka 		} else if (!onoff && reg) {
306003bdf4cSnonaka 			ioexp_gpio_pin_write(sc, IOEXP_MIC_BIAS, GPIO_PIN_LOW,
307003bdf4cSnonaka 			    false);
308003bdf4cSnonaka 		}
309003bdf4cSnonaka 	}
310003bdf4cSnonaka }
311003bdf4cSnonaka 
312003bdf4cSnonaka /*
313003bdf4cSnonaka  * Turn on pullup resistor while not reading the remote control.
314003bdf4cSnonaka  */
315003bdf4cSnonaka void
ioexp_akin_pullup(int onoff)316003bdf4cSnonaka ioexp_akin_pullup(int onoff)
317003bdf4cSnonaka {
318003bdf4cSnonaka 	struct ioexp_softc *sc = device_lookup_private(&ioexp_cd, 0);
319003bdf4cSnonaka 
320003bdf4cSnonaka 	if (sc == NULL || !sc->sc_inited) {
321003bdf4cSnonaka #ifdef DEBUG
322003bdf4cSnonaka 		aprint_error("ioexp: %s: not attached or not inited\n",
323003bdf4cSnonaka 		    __func__);
324003bdf4cSnonaka #endif
325003bdf4cSnonaka 		if (onoff)
326003bdf4cSnonaka 			output_init_value |= IOEXP_AKIN_PULLUP;
327003bdf4cSnonaka 		else
328003bdf4cSnonaka 			output_init_value &= ~IOEXP_AKIN_PULLUP;
329003bdf4cSnonaka 		return;
330003bdf4cSnonaka 	}
331003bdf4cSnonaka 
332003bdf4cSnonaka 	if (sc != NULL) {
333003bdf4cSnonaka 		uint8_t reg = ioexp_gpio_pin_get(sc, IOEXP_AKIN_PULLUP);
334003bdf4cSnonaka 		if (onoff && !reg) {
335003bdf4cSnonaka 			ioexp_gpio_pin_write(sc, IOEXP_AKIN_PULLUP,
336003bdf4cSnonaka 			    GPIO_PIN_HIGH, true);
337003bdf4cSnonaka 		} else if (!onoff && reg) {
338003bdf4cSnonaka 			ioexp_gpio_pin_write(sc, IOEXP_AKIN_PULLUP,
339003bdf4cSnonaka 			    GPIO_PIN_LOW, true);
340003bdf4cSnonaka 		}
341003bdf4cSnonaka 	}
342003bdf4cSnonaka }
343