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 52struct 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 62static int ioexp_match(device_t, cfdata_t, void *); 63static void ioexp_attach(device_t, device_t, void *); 64 65CFATTACH_DECL_NEW(ioexp, sizeof(struct ioexp_softc), 66 ioexp_match, ioexp_attach, NULL, NULL); 67 68static uint8_t output_init_value = IOEXP_IR_ON | IOEXP_AKIN_PULLUP; 69static uint8_t direction_init_value = 0; 70 71static __inline int 72ioexp_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 85static int 86ioexp_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 105static void 106ioexp_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 130static void 131ioexp_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 163static void 164ioexp_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 191static __inline uint8_t 192ioexp_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 */ 201void 202ioexp_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 */ 249void 250ioexp_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 */ 283void 284ioexp_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 */ 315void 316ioexp_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