xref: /netbsd-src/sys/arch/zaurus/dev/ioexp.c (revision aa41e9922cf39f098b31d90c04e1deda4cbc10cf)
1 /*	$NetBSD: ioexp.c,v 1.2 2018/06/16 21:22:13 thorpej Exp $	*/
2 
3 /*-
4  * Copyright (c) 2011 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by NONAKA Kimihiro.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: ioexp.c,v 1.2 2018/06/16 21:22:13 thorpej Exp $");
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/device.h>
38 #include <sys/gpio.h>
39 
40 #include <dev/i2c/i2cvar.h>
41 
42 #include <arm/xscale/pxa2x0reg.h>
43 #include <arm/xscale/pxa2x0var.h>
44 #include <arm/xscale/pxa2x0_i2c.h>
45 
46 #include <zaurus/zaurus/zaurus_var.h>
47 #include <zaurus/dev/ioexpreg.h>
48 #include <zaurus/dev/ioexpvar.h>
49 
50 #include "ioconf.h"
51 
52 struct ioexp_softc {
53 	device_t	sc_dev;
54 	i2c_tag_t	sc_i2c;
55 
56 	uint8_t		sc_output;
57 	uint8_t		sc_direction;
58 
59 	int		sc_inited;
60 };
61 
62 static int ioexp_match(device_t, cfdata_t, void *);
63 static void ioexp_attach(device_t, device_t, void *);
64 
65 CFATTACH_DECL_NEW(ioexp, sizeof(struct ioexp_softc),
66     ioexp_match, ioexp_attach, NULL, NULL);
67 
68 static uint8_t output_init_value = IOEXP_IR_ON | IOEXP_AKIN_PULLUP;
69 static uint8_t direction_init_value = 0;
70 
71 static __inline int
ioexp_write(struct ioexp_softc * sc,uint8_t reg,uint8_t val)72 ioexp_write(struct ioexp_softc *sc, uint8_t reg, uint8_t val)
73 {
74 	uint8_t cmd;
75 	uint8_t data;
76 	int error;
77 
78 	cmd = reg;
79 	data = val;
80 	error = iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP, IOEXP_ADDRESS,
81 	    &cmd, 1, &data, 1, 0);
82 	return error;
83 }
84 
85 static int
ioexp_match(device_t parent,cfdata_t cf,void * aux)86 ioexp_match(device_t parent, cfdata_t cf, void *aux)
87 {
88 	struct i2c_attach_args *ia = aux;
89 	int match_result;
90 
91 	/* only for SL-C1000 */
92 	if (!ZAURUS_ISC1000)
93 		return 0;
94 
95 	if (iic_use_direct_match(ia, cf, NULL, &match_result))
96 		return match_result;
97 
98 	/* indirect config - check typical address */
99 	if (ia->ia_addr == IOEXP_ADDRESS)
100 		return I2C_MATCH_ADDRESS_ONLY;
101 
102 	return 0;
103 }
104 
105 static void
ioexp_attach(device_t parent,device_t self,void * aux)106 ioexp_attach(device_t parent, device_t self, void *aux)
107 {
108 	struct ioexp_softc *sc = device_private(self);
109 	struct i2c_attach_args *ia = aux;
110 
111 	sc->sc_dev = self;
112 	sc->sc_i2c = ia->ia_tag;
113 
114 	aprint_normal(": GPIO controller\n");
115 	aprint_naive("\n");
116 
117 	sc->sc_output = output_init_value;
118 	sc->sc_direction = direction_init_value;
119 
120 	iic_acquire_bus(sc->sc_i2c, 0);
121 	ioexp_write(sc, IOEXP_POLARITY, 0);
122 	ioexp_write(sc, IOEXP_OUTPUT, sc->sc_output);
123 	ioexp_write(sc, IOEXP_DIRECTION, sc->sc_direction);
124 	iic_release_bus(sc->sc_i2c, 0);
125 
126 	sc->sc_inited = 1;
127 }
128 
129 #if 0
130 static void
131 ioexp_gpio_pin_ctl(struct ioexp_softc *sc, uint8_t bit, int flags,
132     bool acquire_bus)
133 {
134 	int error;
135 
136 	if (acquire_bus) {
137 		error = iic_acquire_bus(sc->sc_i2c, 0);
138 		if (error) {
139 			aprint_error_dev(sc->sc_dev,
140 			    "unable to acquire bus. error=%d\n", error);
141 			return;
142 		}
143 	}
144 
145 	switch (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
146 	case GPIO_PIN_INPUT:
147 		sc->sc_direction |= bit;
148 		break;
149 	case GPIO_PIN_OUTPUT:
150 		sc->sc_direction &= ~bit;
151 		break;
152 	}
153 	error = ioexp_write(sc, IOEXP_DIRECTION, sc->sc_direction);
154 	if (error)
155 		aprint_error_dev(sc->sc_dev,
156 		    "direction write failed. error=%d\n", error);
157 
158 	if (acquire_bus)
159 		iic_release_bus(sc->sc_i2c, 0);
160 }
161 #endif
162 
163 static void
ioexp_gpio_pin_write(struct ioexp_softc * sc,uint8_t bit,int level,bool acquire_bus)164 ioexp_gpio_pin_write(struct ioexp_softc *sc, uint8_t bit, int level,
165     bool acquire_bus)
166 {
167 	int error;
168 
169 	if (acquire_bus) {
170 		error = iic_acquire_bus(sc->sc_i2c, 0);
171 		if (error) {
172 			aprint_error_dev(sc->sc_dev,
173 			    "unable to acquire bus. error=%d\n", error);
174 			return;
175 		}
176 	}
177 
178 	if (level == GPIO_PIN_LOW)
179 		sc->sc_output &= ~bit;
180 	else
181 		sc->sc_output |= bit;
182 	error = ioexp_write(sc, IOEXP_OUTPUT, sc->sc_output);
183 	if (error)
184 		aprint_error_dev(sc->sc_dev,
185 		    "output write failed. error=%d\n", error);
186 
187 	if (acquire_bus)
188 		iic_release_bus(sc->sc_i2c, 0);
189 }
190 
191 static __inline uint8_t
ioexp_gpio_pin_get(struct ioexp_softc * sc,uint8_t bit)192 ioexp_gpio_pin_get(struct ioexp_softc *sc, uint8_t bit)
193 {
194 
195 	return sc->sc_output & bit;
196 }
197 
198 /*
199  * Turn the LCD background light and contrast signal on or off.
200  */
201 void
ioexp_set_backlight(int onoff,int cont)202 ioexp_set_backlight(int onoff, int cont)
203 {
204 	struct ioexp_softc *sc = device_lookup_private(&ioexp_cd, 0);
205 
206 	if (sc == NULL || !sc->sc_inited) {
207 #ifdef DEBUG
208 		aprint_error("ioexp: %s: not attached or not inited\n",
209 		    __func__);
210 #endif
211 		if (onoff)
212 			output_init_value |= IOEXP_BACKLIGHT_ON;
213 		else
214 			output_init_value &= ~IOEXP_BACKLIGHT_ON;
215 		/* BACKLIGHT_CONT is inverted */
216 		if (cont)
217 			output_init_value &= ~IOEXP_BACKLIGHT_CONT;
218 		else
219 			output_init_value |= IOEXP_BACKLIGHT_CONT;
220 		return;
221 	}
222 
223 	if (sc != NULL) {
224 		uint8_t bkreg = ioexp_gpio_pin_get(sc, IOEXP_BACKLIGHT_ON);
225 		uint8_t contreg = ioexp_gpio_pin_get(sc, IOEXP_BACKLIGHT_CONT);
226 
227 		if (onoff && !bkreg) {
228 			ioexp_gpio_pin_write(sc, IOEXP_BACKLIGHT_ON,
229 			    GPIO_PIN_HIGH, true);
230 		} else if (!onoff && bkreg) {
231 			ioexp_gpio_pin_write(sc, IOEXP_BACKLIGHT_ON,
232 			    GPIO_PIN_LOW, true);
233 		}
234 
235 		/* BACKLIGHT_CONT is inverted */
236 		if (cont && contreg) {
237 			ioexp_gpio_pin_write(sc, IOEXP_BACKLIGHT_CONT,
238 			    GPIO_PIN_LOW, true);
239 		} else if (!cont && !contreg) {
240 			ioexp_gpio_pin_write(sc, IOEXP_BACKLIGHT_CONT,
241 			    GPIO_PIN_HIGH, true);
242 		}
243 	}
244 }
245 
246 /*
247  * Turn the infrared LED on or off (must be on while transmitting).
248  */
249 void
ioexp_set_irled(int onoff)250 ioexp_set_irled(int onoff)
251 {
252 	struct ioexp_softc *sc = device_lookup_private(&ioexp_cd, 0);
253 
254 	if (sc == NULL || !sc->sc_inited) {
255 #ifdef DEBUG
256 		aprint_error("ioexp: %s: not attached or not inited\n",
257 		    __func__);
258 #endif
259 		/* IR_ON is inverted */
260 		if (onoff)
261 			output_init_value &= ~IOEXP_IR_ON;
262 		else
263 			output_init_value |= IOEXP_IR_ON;
264 		return;
265 	}
266 
267 	if (sc != NULL) {
268 		/* IR_ON is inverted */
269 		uint8_t reg = ioexp_gpio_pin_get(sc, IOEXP_IR_ON);
270 		if (onoff && reg) {
271 			ioexp_gpio_pin_write(sc, IOEXP_IR_ON, GPIO_PIN_LOW,
272 			    true);
273 		} else if (!onoff && !reg) {
274 			ioexp_gpio_pin_write(sc, IOEXP_IR_ON, GPIO_PIN_HIGH,
275 			    true);
276 		}
277 	}
278 }
279 
280 /*
281  * Enable or disable the mic bias
282  */
283 void
ioexp_set_mic_bias(int onoff)284 ioexp_set_mic_bias(int onoff)
285 {
286 	struct ioexp_softc *sc = device_lookup_private(&ioexp_cd, 0);
287 
288 	if (sc == NULL || !sc->sc_inited) {
289 #ifdef DEBUG
290 		aprint_error("ioexp: %s: not attached or not inited\n",
291 		    __func__);
292 #endif
293 		if (onoff)
294 			output_init_value |= IOEXP_MIC_BIAS;
295 		else
296 			output_init_value &= ~IOEXP_MIC_BIAS;
297 		return;
298 	}
299 
300 	if (sc != NULL) {
301 		uint8_t reg = ioexp_gpio_pin_get(sc, IOEXP_MIC_BIAS);
302 		if (onoff && !reg) {
303 			ioexp_gpio_pin_write(sc, IOEXP_MIC_BIAS, GPIO_PIN_HIGH,
304 			    false);
305 		} else if (!onoff && reg) {
306 			ioexp_gpio_pin_write(sc, IOEXP_MIC_BIAS, GPIO_PIN_LOW,
307 			    false);
308 		}
309 	}
310 }
311 
312 /*
313  * Turn on pullup resistor while not reading the remote control.
314  */
315 void
ioexp_akin_pullup(int onoff)316 ioexp_akin_pullup(int onoff)
317 {
318 	struct ioexp_softc *sc = device_lookup_private(&ioexp_cd, 0);
319 
320 	if (sc == NULL || !sc->sc_inited) {
321 #ifdef DEBUG
322 		aprint_error("ioexp: %s: not attached or not inited\n",
323 		    __func__);
324 #endif
325 		if (onoff)
326 			output_init_value |= IOEXP_AKIN_PULLUP;
327 		else
328 			output_init_value &= ~IOEXP_AKIN_PULLUP;
329 		return;
330 	}
331 
332 	if (sc != NULL) {
333 		uint8_t reg = ioexp_gpio_pin_get(sc, IOEXP_AKIN_PULLUP);
334 		if (onoff && !reg) {
335 			ioexp_gpio_pin_write(sc, IOEXP_AKIN_PULLUP,
336 			    GPIO_PIN_HIGH, true);
337 		} else if (!onoff && reg) {
338 			ioexp_gpio_pin_write(sc, IOEXP_AKIN_PULLUP,
339 			    GPIO_PIN_LOW, true);
340 		}
341 	}
342 }
343