Home | History | Annotate | Line # | Download | only in dev
ioexp.c revision 1.2
      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
     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 	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
    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
    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
    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
    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
    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
    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
    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