twl4030.c revision 1.2
1/* $NetBSD: twl4030.c,v 1.2 2019/11/01 09:49:55 jmcneill Exp $ */ 2 3/*- 4 * Copyright (c) 2019 Jared McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: twl4030.c,v 1.2 2019/11/01 09:49:55 jmcneill Exp $"); 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/kernel.h> 35#include <sys/device.h> 36#include <sys/conf.h> 37#include <sys/bus.h> 38#include <sys/kmem.h> 39#include <sys/gpio.h> 40 41#include <dev/i2c/i2cvar.h> 42 43#include <dev/fdt/fdtvar.h> 44 45#define TWL_PIN_COUNT 16 46 47/* TWL4030 is a multi-function IC. Each module is at a separate I2C address */ 48#define ADDR_USB 0x00 49#define ADDR_INT 0x01 50#define ADDR_AUX 0x02 51#define ADDR_POWER 0x03 52 53/* INTBR registers */ 54#define IDCODE_7_0 0x85 55#define IDCODE_15_8 0x86 56#define IDCODE_23_16 0x87 57#define IDCODE_31_24 0x88 58 59/* GPIO registers */ 60#define GPIOBASE 0x98 61#define GPIODATAIN(pin) (GPIOBASE + 0x00 + (pin) / 8) 62#define GPIODATADIR(pin) (GPIOBASE + 0x03 + (pin) / 8) 63#define CLEARGPIODATAOUT(pin) (GPIOBASE + 0x09 + (pin) / 8) 64#define SETGPIODATAOUT(pin) (GPIOBASE + 0x0c + (pin) / 8) 65#define PIN_BIT(pin) __BIT((pin) % 8) 66#define GPIOPUPDCTR(pin) (GPIOBASE + 0x13 + (n) / 4) 67#define PUPD_BITS(pin) __BITS((pin) % 4 + 1, (pin) % 4) 68 69/* POWER registers */ 70#define SECONDS_REG 0x1c 71#define MINUTES_REG 0x1d 72#define HOURS_REG 0x1e 73#define DAYS_REG 0x1f 74#define MONTHS_REG 0x20 75#define YEARS_REG 0x21 76#define WEEKS_REG 0x22 77#define RTC_CTRL_REG 0x29 78#define GET_TIME __BIT(6) 79#define STOP_RTC __BIT(0) 80 81struct twl_softc { 82 device_t sc_dev; 83 i2c_tag_t sc_i2c; 84 i2c_addr_t sc_addr; 85 int sc_phandle; 86 87 int sc_npins; 88 89 struct todr_chip_handle sc_todr; 90}; 91 92struct twl_pin { 93 struct twl_softc *pin_sc; 94 int pin_num; 95 int pin_flags; 96 bool pin_actlo; 97}; 98 99static const struct device_compatible_entry compat_data[] = { 100 { "ti,twl4030", 0 }, 101 { NULL, 0 } 102}; 103 104static const char * const rtc_compatible[] = { "ti,twl4030-rtc", NULL }; 105static const char * const gpio_compatible[] = { "ti,twl4030-gpio", NULL }; 106 107static uint8_t 108twl_read(struct twl_softc *sc, uint8_t mod, uint8_t reg, int flags) 109{ 110 uint8_t val = 0; 111 int error; 112 113 error = iic_exec(sc->sc_i2c, I2C_OP_READ_WITH_STOP, sc->sc_addr + mod, 114 ®, 1, &val, 1, flags); 115 if (error != 0) 116 aprint_error_dev(sc->sc_dev, "error reading reg %#x: %d\n", reg, error); 117 118 return val; 119} 120 121static void 122twl_write(struct twl_softc *sc, uint8_t mod, uint8_t reg, uint8_t val, int flags) 123{ 124 uint8_t buf[2]; 125 int error; 126 127 buf[0] = reg; 128 buf[1] = val; 129 130 error = iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP, sc->sc_addr + mod, 131 NULL, 0, buf, 2, flags); 132 if (error != 0) 133 aprint_error_dev(sc->sc_dev, "error writing reg %#x: %d\n", reg, error); 134} 135 136#define I2C_LOCK(sc) iic_acquire_bus((sc)->sc_i2c, I2C_F_POLL) 137#define I2C_UNLOCK(sc) iic_release_bus((sc)->sc_i2c, I2C_F_POLL) 138 139#define INT_READ(sc, reg) twl_read((sc), ADDR_INT, (reg), I2C_F_POLL) 140#define INT_WRITE(sc, reg, val) twl_write((sc), ADDR_INT, (reg), (val), I2C_F_POLL) 141 142#define POWER_READ(sc, reg) twl_read((sc), ADDR_POWER, (reg), I2C_F_POLL) 143#define POWER_WRITE(sc, reg, val) twl_write((sc), ADDR_POWER, (reg), (val), I2C_F_POLL) 144 145static void 146twl_rtc_enable(struct twl_softc *sc, bool onoff) 147{ 148 uint8_t rtc_ctrl; 149 150 rtc_ctrl = POWER_READ(sc, RTC_CTRL_REG); 151 if (onoff) 152 rtc_ctrl |= STOP_RTC; /* 1: RTC is running */ 153 else 154 rtc_ctrl &= ~STOP_RTC; /* 0: RTC is frozen */ 155 POWER_WRITE(sc, RTC_CTRL_REG, rtc_ctrl); 156} 157 158static int 159twl_rtc_gettime(todr_chip_handle_t tch, struct clock_ymdhms *dt) 160{ 161 struct twl_softc *sc = tch->cookie; 162 uint8_t seconds_reg, minutes_reg, hours_reg, 163 days_reg, months_reg, years_reg, weeks_reg; 164 165 iic_acquire_bus(sc->sc_i2c, I2C_F_POLL); 166 seconds_reg = POWER_READ(sc, SECONDS_REG); 167 minutes_reg = POWER_READ(sc, MINUTES_REG); 168 hours_reg = POWER_READ(sc, HOURS_REG); 169 days_reg = POWER_READ(sc, DAYS_REG); 170 months_reg = POWER_READ(sc, MONTHS_REG); 171 years_reg = POWER_READ(sc, YEARS_REG); 172 weeks_reg = POWER_READ(sc, WEEKS_REG); 173 iic_release_bus(sc->sc_i2c, I2C_F_POLL); 174 175 dt->dt_sec = bcdtobin(seconds_reg); 176 dt->dt_min = bcdtobin(minutes_reg); 177 dt->dt_hour = bcdtobin(hours_reg); 178 dt->dt_day = bcdtobin(days_reg); 179 dt->dt_mon = bcdtobin(months_reg); 180 dt->dt_year = bcdtobin(years_reg) + 2000; 181 dt->dt_wday = bcdtobin(weeks_reg); 182 183 return 0; 184} 185 186static int 187twl_rtc_settime(todr_chip_handle_t tch, struct clock_ymdhms *dt) 188{ 189 struct twl_softc *sc = tch->cookie; 190 191 iic_acquire_bus(sc->sc_i2c, I2C_F_POLL); 192 twl_rtc_enable(sc, false); 193 POWER_WRITE(sc, SECONDS_REG, bintobcd(dt->dt_sec)); 194 POWER_WRITE(sc, MINUTES_REG, bintobcd(dt->dt_min)); 195 POWER_WRITE(sc, HOURS_REG, bintobcd(dt->dt_hour)); 196 POWER_WRITE(sc, DAYS_REG, bintobcd(dt->dt_day)); 197 POWER_WRITE(sc, MONTHS_REG, bintobcd(dt->dt_mon)); 198 POWER_WRITE(sc, YEARS_REG, bintobcd(dt->dt_year % 100)); 199 POWER_WRITE(sc, WEEKS_REG, bintobcd(dt->dt_wday)); 200 twl_rtc_enable(sc, true); 201 iic_release_bus(sc->sc_i2c, I2C_F_POLL); 202 203 return 0; 204} 205 206static int 207twl_gpio_config(struct twl_softc *sc, int pin, int flags) 208{ 209 uint8_t dir; 210 211 KASSERT(pin >= 0 && pin < sc->sc_npins); 212 213 dir = INT_READ(sc, GPIODATADIR(pin)); 214 215 switch (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { 216 case GPIO_PIN_INPUT: 217 dir &= ~PIN_BIT(pin); 218 break; 219 case GPIO_PIN_OUTPUT: 220 dir |= PIN_BIT(pin); 221 break; 222 default: 223 return EINVAL; 224 } 225 226 INT_WRITE(sc, GPIODATADIR(pin), dir); 227 228 return 0; 229} 230 231static void * 232twl_gpio_acquire(device_t dev, const void *data, size_t len, int flags) 233{ 234 struct twl_softc * const sc = device_private(dev); 235 struct twl_pin *gpin; 236 const u_int *gpio = data; 237 int error; 238 239 if (len != 12) 240 return NULL; 241 242 const uint8_t pin = be32toh(gpio[1]) & 0xff; 243 const bool actlo = (be32toh(gpio[2]) & __BIT(0)) != 0; 244 245 if (pin >= sc->sc_npins) 246 return NULL; 247 248 I2C_LOCK(sc); 249 error = twl_gpio_config(sc, pin, flags); 250 I2C_UNLOCK(sc); 251 252 if (error != 0) { 253 device_printf(dev, "bad pin %d config %#x\n", pin, flags); 254 return NULL; 255 } 256 257 gpin = kmem_zalloc(sizeof(*gpin), KM_SLEEP); 258 gpin->pin_sc = sc; 259 gpin->pin_num = pin; 260 gpin->pin_flags = flags; 261 gpin->pin_actlo = actlo; 262 263 return gpin; 264} 265 266static void 267twl_gpio_release(device_t dev, void *priv) 268{ 269 struct twl_softc * const sc = device_private(dev); 270 struct twl_pin *gpin = priv; 271 272 I2C_LOCK(sc); 273 twl_gpio_config(sc, gpin->pin_num, GPIO_PIN_INPUT); 274 I2C_UNLOCK(sc); 275 276 kmem_free(gpin, sizeof(*gpin)); 277} 278 279static int 280twl_gpio_read(device_t dev, void *priv, bool raw) 281{ 282 struct twl_softc * const sc = device_private(dev); 283 struct twl_pin *gpin = priv; 284 uint8_t gpio; 285 int val; 286 287 I2C_LOCK(sc); 288 gpio = INT_READ(sc, GPIODATAIN(gpin->pin_num)); 289 I2C_UNLOCK(sc); 290 291 val = __SHIFTOUT(gpio, PIN_BIT(gpin->pin_num)); 292 if (!raw && gpin->pin_actlo) 293 val = !val; 294 295 return val; 296} 297 298static void 299twl_gpio_write(device_t dev, void *priv, int val, bool raw) 300{ 301 struct twl_softc * const sc = device_private(dev); 302 struct twl_pin *gpin = priv; 303 304 if (!raw && gpin->pin_actlo) 305 val = !val; 306 307 I2C_LOCK(sc); 308 if (val) 309 INT_WRITE(sc, SETGPIODATAOUT(gpin->pin_num), PIN_BIT(gpin->pin_num)); 310 else 311 INT_WRITE(sc, CLEARGPIODATAOUT(gpin->pin_num), PIN_BIT(gpin->pin_num)); 312 I2C_UNLOCK(sc); 313} 314 315static struct fdtbus_gpio_controller_func twl_gpio_funcs = { 316 .acquire = twl_gpio_acquire, 317 .release = twl_gpio_release, 318 .read = twl_gpio_read, 319 .write = twl_gpio_write, 320}; 321 322static void 323twl_rtc_attach(struct twl_softc *sc, const int phandle) 324{ 325 iic_acquire_bus(sc->sc_i2c, I2C_F_POLL); 326 twl_rtc_enable(sc, true); 327 iic_release_bus(sc->sc_i2c, I2C_F_POLL); 328 329 sc->sc_todr.todr_gettime_ymdhms = twl_rtc_gettime; 330 sc->sc_todr.todr_settime_ymdhms = twl_rtc_settime; 331 sc->sc_todr.cookie = sc; 332 fdtbus_todr_attach(sc->sc_dev, phandle, &sc->sc_todr); 333} 334 335static void 336twl_gpio_attach(struct twl_softc *sc, const int phandle) 337{ 338 fdtbus_register_gpio_controller(sc->sc_dev, phandle, &twl_gpio_funcs); 339} 340 341static int 342twl_match(device_t parent, cfdata_t match, void *aux) 343{ 344 struct i2c_attach_args *ia = aux; 345 int match_result; 346 347 if (iic_use_direct_match(ia, match, compat_data, &match_result)) 348 return match_result; 349 350 return 0; 351} 352 353static void 354twl_attach(device_t parent, device_t self, void *aux) 355{ 356 struct twl_softc * const sc = device_private(self); 357 struct i2c_attach_args *ia = aux; 358 uint32_t idcode; 359 int child; 360 361 sc->sc_dev = self; 362 sc->sc_i2c = ia->ia_tag; 363 sc->sc_addr = ia->ia_addr; 364 sc->sc_phandle = ia->ia_cookie; 365 sc->sc_npins = TWL_PIN_COUNT; 366 367 aprint_naive("\n"); 368 aprint_normal(": TWL4030"); 369 370 for (child = OF_child(sc->sc_phandle); child; child = OF_peer(child)) { 371 if (of_match_compatible(child, gpio_compatible)) { 372 aprint_normal(", GPIO"); 373 twl_gpio_attach(sc, child); 374 } else if (of_match_compatible(child, rtc_compatible)) { 375 aprint_normal(", RTC"); 376 twl_rtc_attach(sc, child); 377 } 378 } 379 380 I2C_LOCK(sc); 381 idcode = INT_READ(sc, IDCODE_7_0); 382 idcode |= (uint32_t)INT_READ(sc, IDCODE_15_8) << 8; 383 idcode |= (uint32_t)INT_READ(sc, IDCODE_23_16) << 16; 384 idcode |= (uint32_t)INT_READ(sc, IDCODE_31_24) << 24; 385 I2C_UNLOCK(sc); 386 387 aprint_normal(", IDCODE 0x%08x\n", idcode); 388} 389 390CFATTACH_DECL_NEW(twl, sizeof(struct twl_softc), 391 twl_match, twl_attach, NULL, NULL); 392