1/* $NetBSD$ */ 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$"); 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 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 106static void 107ioexp_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 131static void 132ioexp_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 164static void 165ioexp_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 192static __inline uint8_t 193ioexp_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 */ 202void 203ioexp_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 */ 250void 251ioexp_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 */ 284void 285ioexp_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 */ 316void 317ioexp_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