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