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