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