Module Name: src Committed By: tnn Date: Wed Sep 18 14:07:38 UTC 2019
Modified Files: src/sys/dev/i2c: rkpmic.c Log Message: rkpmic: add RTC support; register w/ todr(9) To generate a diff of this commit: cvs rdiff -u -r1.3 -r1.4 src/sys/dev/i2c/rkpmic.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/i2c/rkpmic.c diff -u src/sys/dev/i2c/rkpmic.c:1.3 src/sys/dev/i2c/rkpmic.c:1.4 --- src/sys/dev/i2c/rkpmic.c:1.3 Wed Jul 3 10:21:41 2019 +++ src/sys/dev/i2c/rkpmic.c Wed Sep 18 14:07:38 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: rkpmic.c,v 1.3 2019/07/03 10:21:41 jmcneill Exp $ */ +/* $NetBSD: rkpmic.c,v 1.4 2019/09/18 14:07:38 tnn Exp $ */ /*- * Copyright (c) 2018 Jared McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: rkpmic.c,v 1.3 2019/07/03 10:21:41 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: rkpmic.c,v 1.4 2019/09/18 14:07:38 tnn Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -37,10 +37,33 @@ __KERNEL_RCSID(0, "$NetBSD: rkpmic.c,v 1 #include <sys/bus.h> #include <sys/kmem.h> +#include <dev/clock_subr.h> + #include <dev/i2c/i2cvar.h> #include <dev/fdt/fdtvar.h> +#define SECONDS_REG 0x00 +#define MINUTES_REG 0x01 +#define HOURS_REG 0x02 +#define DAYS_REG 0x03 +#define MONTHS_REG 0x04 +#define YEARS_REG 0x05 +#define WEEKS_REG 0x06 + +#define RTC_CTRL_REG 0x10 +#define RTC_CTRL_READSEL __BIT(7) +#define RTC_CTRL_GET_TIME __BIT(6) +#define RTC_CTRL_SET_32_COUNTER __BIT(5) +#define RTC_CTRL_TEST_MODE __BIT(4) +#define RTC_CTRL_AMPM_MODE __BIT(3) +#define RTC_CTRL_AUTO_COMP __BIT(2) +#define RTC_CTRL_ROUND_30S __BIT(1) +#define RTC_CTRL_STOP_RTC __BIT(0) + +#define RTC_INT_REG 0x12 +#define RTC_COMP_LSB_REG 0x13 +#define RTC_COMP_MSB_REG 0x14 #define CHIP_NAME_REG 0x17 #define CHIP_VER_REG 0x18 @@ -169,7 +192,7 @@ struct rkpmic_softc { i2c_tag_t sc_i2c; i2c_addr_t sc_addr; int sc_phandle; - + struct todr_chip_handle sc_todr; struct rkpmic_config *sc_conf; }; @@ -198,7 +221,7 @@ rkpmic_read(struct rkpmic_softc *sc, uin error = iic_smbus_read_byte(sc->sc_i2c, sc->sc_addr, reg, &val, flags); if (error != 0) - aprint_error_dev(sc->sc_dev, "error reading reg %#x: %d\n", reg, error); + device_printf(sc->sc_dev, "error reading reg %#x: %d\n", reg, error); return val; } @@ -210,7 +233,7 @@ rkpmic_write(struct rkpmic_softc *sc, ui error = iic_smbus_write_byte(sc->sc_i2c, sc->sc_addr, reg, val, flags); if (error != 0) - aprint_error_dev(sc->sc_dev, "error writing reg %#x: %d\n", reg, error); + device_printf(sc->sc_dev, "error writing reg %#x: %d\n", reg, error); } #define I2C_READ(sc, reg) rkpmic_read((sc), (reg), I2C_F_POLL) @@ -219,6 +242,82 @@ rkpmic_write(struct rkpmic_softc *sc, ui #define I2C_UNLOCK(sc) iic_release_bus((sc)->sc_i2c, I2C_F_POLL) static int +rkpmic_todr_settime(todr_chip_handle_t ch, struct clock_ymdhms *dt) +{ + struct rkpmic_softc * const sc = ch->cookie; + uint8_t val; + + if (dt->dt_year < 2000 || dt->dt_year >= 2100) { + device_printf(sc->sc_dev, "year out of range\n"); + return EINVAL; + } + + if (I2C_LOCK(sc)) + return EBUSY; + + val = I2C_READ(sc, RTC_CTRL_REG); + I2C_WRITE(sc, RTC_CTRL_REG, val | RTC_CTRL_STOP_RTC); + I2C_WRITE(sc, SECONDS_REG, bintobcd(dt->dt_sec)); + I2C_WRITE(sc, MINUTES_REG, bintobcd(dt->dt_min)); + I2C_WRITE(sc, HOURS_REG, bintobcd(dt->dt_hour)); + I2C_WRITE(sc, DAYS_REG, bintobcd(dt->dt_day)); + I2C_WRITE(sc, MONTHS_REG, bintobcd(dt->dt_mon)); + I2C_WRITE(sc, YEARS_REG, bintobcd(dt->dt_year % 100)); + I2C_WRITE(sc, WEEKS_REG, bintobcd(dt->dt_wday == 0 ? 7 : dt->dt_wday)); + I2C_WRITE(sc, RTC_CTRL_REG, val); + I2C_UNLOCK(sc); + + return 0; +} + +static int +rkpmic_todr_gettime(todr_chip_handle_t ch, struct clock_ymdhms *dt) +{ + struct rkpmic_softc * const sc = ch->cookie; + uint8_t val; + + if (I2C_LOCK(sc)) + return EBUSY; + + val = I2C_READ(sc, RTC_CTRL_REG); + I2C_WRITE(sc, RTC_CTRL_REG, val | RTC_CTRL_GET_TIME | RTC_CTRL_READSEL); + delay(1); /* need to wait 1/32768 seconds for shadow regs to latch */ + I2C_WRITE(sc, RTC_CTRL_REG, val | RTC_CTRL_READSEL); + dt->dt_sec = bcdtobin(I2C_READ(sc, SECONDS_REG)); + dt->dt_min = bcdtobin(I2C_READ(sc, MINUTES_REG)); + dt->dt_hour = bcdtobin(I2C_READ(sc, HOURS_REG)); + dt->dt_day = bcdtobin(I2C_READ(sc, DAYS_REG)); + dt->dt_mon = bcdtobin(I2C_READ(sc, MONTHS_REG)); + dt->dt_year = 2000 + bcdtobin(I2C_READ(sc, YEARS_REG)); + dt->dt_wday = bcdtobin(I2C_READ(sc, WEEKS_REG)); + if (dt->dt_wday == 7) + dt->dt_wday = 0; + I2C_WRITE(sc, RTC_CTRL_REG, val); + I2C_UNLOCK(sc); + + /* + * RK808 has a hw bug which makes the 31st of November a valid day. + * If we detect the 31st of November we skip ahead one day. + * If the system has been turned off during the crossover the clock + * will have lost a day. No easy way to detect this. Oh well. + */ + if (dt->dt_mon == 11 && dt->dt_day == 31) { + dt->dt_day--; + clock_secs_to_ymdhms(clock_ymdhms_to_secs(dt) + 86400, dt); + rkpmic_todr_settime(ch, dt); + } + +#if 0 + device_printf(sc->sc_dev, "%04" PRIu64 "-%02u-%02u (%u) %02u:%02u:%02u\n", + dt->dt_year, dt->dt_mon, dt->dt_day, dt->dt_wday, + dt->dt_hour, dt->dt_min, dt->dt_sec); +#endif + + return 0; +} + + +static int rkpmic_match(device_t parent, cfdata_t match, void *aux) { struct i2c_attach_args *ia = aux; @@ -248,15 +347,26 @@ rkpmic_attach(device_t parent, device_t sc->sc_phandle = ia->ia_cookie; sc->sc_conf = (void *)entry->data; + memset(&sc->sc_todr, 0, sizeof(sc->sc_todr)); + sc->sc_todr.cookie = sc; + sc->sc_todr.todr_gettime_ymdhms = rkpmic_todr_gettime; + sc->sc_todr.todr_settime_ymdhms = rkpmic_todr_settime; + aprint_naive("\n"); - aprint_normal(": %s Power Management IC\n", sc->sc_conf->name); + aprint_normal(": %s Power Management and Real Time Clock IC\n", sc->sc_conf->name); I2C_LOCK(sc); chipid = I2C_READ(sc, CHIP_NAME_REG) << 8; chipid |= I2C_READ(sc, CHIP_VER_REG); aprint_debug_dev(self, "Chip ID 0x%04x\n", chipid); + I2C_WRITE(sc, RTC_CTRL_REG, 0x0); + I2C_WRITE(sc, RTC_INT_REG, 0); + I2C_WRITE(sc, RTC_COMP_LSB_REG, 0); + I2C_WRITE(sc, RTC_COMP_MSB_REG, 0); I2C_UNLOCK(sc); + fdtbus_todr_attach(self, sc->sc_phandle, &sc->sc_todr); + regulators = of_find_firstchild_byname(sc->sc_phandle, "regulators"); if (regulators < 0) return;