Home | History | Annotate | Line # | Download | only in dev
ioexp.c revision 1.1.2.2
      1 /*	$NetBSD: ioexp.c,v 1.1.2.2 2011/06/23 14:19:51 cherry 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.1.2.2 2011/06/23 14:19:51 cherry 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
     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
     86 ioexp_match(device_t parent, cfdata_t cf, void *aux)
     87 {
     88 	struct i2c_attach_args *ia = aux;
     89 
     90 	/* only for SL-C1000 */
     91 	if (!ZAURUS_ISC1000)
     92 		return 0;
     93 
     94 	if (ia->ia_name) {
     95 		/* direct config - check name */
     96 		if (strcmp(ia->ia_name, "ioexp") == 0)
     97 			return 1;
     98 	} else {
     99 		/* indirect config - check typical address */
    100 		if (ia->ia_addr == IOEXP_ADDRESS)
    101 			return 1;
    102 	}
    103 	return 0;
    104 }
    105 
    106 static void
    107 ioexp_attach(device_t parent, device_t self, void *aux)
    108 {
    109 	struct ioexp_softc *sc = device_private(self);
    110 	struct i2c_attach_args *ia = aux;
    111 
    112 	sc->sc_dev = self;
    113 	sc->sc_i2c = ia->ia_tag;
    114 
    115 	aprint_normal(": GPIO controller\n");
    116 	aprint_naive("\n");
    117 
    118 	sc->sc_output = output_init_value;
    119 	sc->sc_direction = direction_init_value;
    120 
    121 	iic_acquire_bus(sc->sc_i2c, 0);
    122 	ioexp_write(sc, IOEXP_POLARITY, 0);
    123 	ioexp_write(sc, IOEXP_OUTPUT, sc->sc_output);
    124 	ioexp_write(sc, IOEXP_DIRECTION, sc->sc_direction);
    125 	iic_release_bus(sc->sc_i2c, 0);
    126 
    127 	sc->sc_inited = 1;
    128 }
    129 
    130 #if 0
    131 static void
    132 ioexp_gpio_pin_ctl(struct ioexp_softc *sc, uint8_t bit, int flags,
    133     bool acquire_bus)
    134 {
    135 	int error;
    136 
    137 	if (acquire_bus) {
    138 		error = iic_acquire_bus(sc->sc_i2c, 0);
    139 		if (error) {
    140 			aprint_error_dev(sc->sc_dev,
    141 			    "unable to acquire bus. error=%d\n", error);
    142 			return;
    143 		}
    144 	}
    145 
    146 	switch (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
    147 	case GPIO_PIN_INPUT:
    148 		sc->sc_direction |= bit;
    149 		break;
    150 	case GPIO_PIN_OUTPUT:
    151 		sc->sc_direction &= ~bit;
    152 		break;
    153 	}
    154 	error = ioexp_write(sc, IOEXP_DIRECTION, sc->sc_direction);
    155 	if (error)
    156 		aprint_error_dev(sc->sc_dev,
    157 		    "direction write failed. error=%d\n", error);
    158 
    159 	if (acquire_bus)
    160 		iic_release_bus(sc->sc_i2c, 0);
    161 }
    162 #endif
    163 
    164 static void
    165 ioexp_gpio_pin_write(struct ioexp_softc *sc, uint8_t bit, int level,
    166     bool acquire_bus)
    167 {
    168 	int error;
    169 
    170 	if (acquire_bus) {
    171 		error = iic_acquire_bus(sc->sc_i2c, 0);
    172 		if (error) {
    173 			aprint_error_dev(sc->sc_dev,
    174 			    "unable to acquire bus. error=%d\n", error);
    175 			return;
    176 		}
    177 	}
    178 
    179 	if (level == GPIO_PIN_LOW)
    180 		sc->sc_output &= ~bit;
    181 	else
    182 		sc->sc_output |= bit;
    183 	error = ioexp_write(sc, IOEXP_OUTPUT, sc->sc_output);
    184 	if (error)
    185 		aprint_error_dev(sc->sc_dev,
    186 		    "output write failed. error=%d\n", error);
    187 
    188 	if (acquire_bus)
    189 		iic_release_bus(sc->sc_i2c, 0);
    190 }
    191 
    192 static __inline uint8_t
    193 ioexp_gpio_pin_get(struct ioexp_softc *sc, uint8_t bit)
    194 {
    195 
    196 	return sc->sc_output & bit;
    197 }
    198 
    199 /*
    200  * Turn the LCD background light and contrast signal on or off.
    201  */
    202 void
    203 ioexp_set_backlight(int onoff, int cont)
    204 {
    205 	struct ioexp_softc *sc = device_lookup_private(&ioexp_cd, 0);
    206 
    207 	if (sc == NULL || !sc->sc_inited) {
    208 #ifdef DEBUG
    209 		aprint_error("ioexp: %s: not attached or not inited\n",
    210 		    __func__);
    211 #endif
    212 		if (onoff)
    213 			output_init_value |= IOEXP_BACKLIGHT_ON;
    214 		else
    215 			output_init_value &= ~IOEXP_BACKLIGHT_ON;
    216 		/* BACKLIGHT_CONT is inverted */
    217 		if (cont)
    218 			output_init_value &= ~IOEXP_BACKLIGHT_CONT;
    219 		else
    220 			output_init_value |= IOEXP_BACKLIGHT_CONT;
    221 		return;
    222 	}
    223 
    224 	if (sc != NULL) {
    225 		uint8_t bkreg = ioexp_gpio_pin_get(sc, IOEXP_BACKLIGHT_ON);
    226 		uint8_t contreg = ioexp_gpio_pin_get(sc, IOEXP_BACKLIGHT_CONT);
    227 
    228 		if (onoff && !bkreg) {
    229 			ioexp_gpio_pin_write(sc, IOEXP_BACKLIGHT_ON,
    230 			    GPIO_PIN_HIGH, true);
    231 		} else if (!onoff && bkreg) {
    232 			ioexp_gpio_pin_write(sc, IOEXP_BACKLIGHT_ON,
    233 			    GPIO_PIN_LOW, true);
    234 		}
    235 
    236 		/* BACKLIGHT_CONT is inverted */
    237 		if (cont && contreg) {
    238 			ioexp_gpio_pin_write(sc, IOEXP_BACKLIGHT_CONT,
    239 			    GPIO_PIN_LOW, true);
    240 		} else if (!cont && !contreg) {
    241 			ioexp_gpio_pin_write(sc, IOEXP_BACKLIGHT_CONT,
    242 			    GPIO_PIN_HIGH, true);
    243 		}
    244 	}
    245 }
    246 
    247 /*
    248  * Turn the infrared LED on or off (must be on while transmitting).
    249  */
    250 void
    251 ioexp_set_irled(int onoff)
    252 {
    253 	struct ioexp_softc *sc = device_lookup_private(&ioexp_cd, 0);
    254 
    255 	if (sc == NULL || !sc->sc_inited) {
    256 #ifdef DEBUG
    257 		aprint_error("ioexp: %s: not attached or not inited\n",
    258 		    __func__);
    259 #endif
    260 		/* IR_ON is inverted */
    261 		if (onoff)
    262 			output_init_value &= ~IOEXP_IR_ON;
    263 		else
    264 			output_init_value |= IOEXP_IR_ON;
    265 		return;
    266 	}
    267 
    268 	if (sc != NULL) {
    269 		/* IR_ON is inverted */
    270 		uint8_t reg = ioexp_gpio_pin_get(sc, IOEXP_IR_ON);
    271 		if (onoff && reg) {
    272 			ioexp_gpio_pin_write(sc, IOEXP_IR_ON, GPIO_PIN_LOW,
    273 			    true);
    274 		} else if (!onoff && !reg) {
    275 			ioexp_gpio_pin_write(sc, IOEXP_IR_ON, GPIO_PIN_HIGH,
    276 			    true);
    277 		}
    278 	}
    279 }
    280 
    281 /*
    282  * Enable or disable the mic bias
    283  */
    284 void
    285 ioexp_set_mic_bias(int onoff)
    286 {
    287 	struct ioexp_softc *sc = device_lookup_private(&ioexp_cd, 0);
    288 
    289 	if (sc == NULL || !sc->sc_inited) {
    290 #ifdef DEBUG
    291 		aprint_error("ioexp: %s: not attached or not inited\n",
    292 		    __func__);
    293 #endif
    294 		if (onoff)
    295 			output_init_value |= IOEXP_MIC_BIAS;
    296 		else
    297 			output_init_value &= ~IOEXP_MIC_BIAS;
    298 		return;
    299 	}
    300 
    301 	if (sc != NULL) {
    302 		uint8_t reg = ioexp_gpio_pin_get(sc, IOEXP_MIC_BIAS);
    303 		if (onoff && !reg) {
    304 			ioexp_gpio_pin_write(sc, IOEXP_MIC_BIAS, GPIO_PIN_HIGH,
    305 			    false);
    306 		} else if (!onoff && reg) {
    307 			ioexp_gpio_pin_write(sc, IOEXP_MIC_BIAS, GPIO_PIN_LOW,
    308 			    false);
    309 		}
    310 	}
    311 }
    312 
    313 /*
    314  * Turn on pullup resistor while not reading the remote control.
    315  */
    316 void
    317 ioexp_akin_pullup(int onoff)
    318 {
    319 	struct ioexp_softc *sc = device_lookup_private(&ioexp_cd, 0);
    320 
    321 	if (sc == NULL || !sc->sc_inited) {
    322 #ifdef DEBUG
    323 		aprint_error("ioexp: %s: not attached or not inited\n",
    324 		    __func__);
    325 #endif
    326 		if (onoff)
    327 			output_init_value |= IOEXP_AKIN_PULLUP;
    328 		else
    329 			output_init_value &= ~IOEXP_AKIN_PULLUP;
    330 		return;
    331 	}
    332 
    333 	if (sc != NULL) {
    334 		uint8_t reg = ioexp_gpio_pin_get(sc, IOEXP_AKIN_PULLUP);
    335 		if (onoff && !reg) {
    336 			ioexp_gpio_pin_write(sc, IOEXP_AKIN_PULLUP,
    337 			    GPIO_PIN_HIGH, true);
    338 		} else if (!onoff && reg) {
    339 			ioexp_gpio_pin_write(sc, IOEXP_AKIN_PULLUP,
    340 			    GPIO_PIN_LOW, true);
    341 		}
    342 	}
    343 }
    344