diff --git a/sys/arm64/rockchip/rk805.c b/sys/arm64/rockchip/rk805.c --- a/sys/arm64/rockchip/rk805.c +++ b/sys/arm64/rockchip/rk805.c @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -46,6 +47,7 @@ #include +#include "clock_if.h" #include "regdev_if.h" MALLOC_DEFINE(M_RK805_REG, "RK805 regulator", "RK805 power regulator"); @@ -356,10 +358,10 @@ } static int -rk805_write(device_t dev, uint8_t reg, uint8_t data) +rk805_write(device_t dev, uint8_t reg, uint8_t *data, uint8_t size) { - return (iicdev_writeto(dev, reg, &data, 1, IIC_INTRWAIT)); + return (iicdev_writeto(dev, reg, data, size, IIC_INTRWAIT)); } static int @@ -415,7 +417,7 @@ val |= sc->def->enable_mask; else val &= ~sc->def->enable_mask; - rk805_write(sc->base_dev, sc->def->enable_reg, val); + rk805_write(sc->base_dev, sc->def->enable_reg, &val, 1); *udelay = 0; @@ -491,7 +493,7 @@ if (rk805_regnode_voltage_to_reg(sc, min_uvolt, max_uvolt, &val) != 0) return (ERANGE); - rk805_write(sc->base_dev, sc->def->voltage_reg, val); + rk805_write(sc->base_dev, sc->def->voltage_reg, &val, 1); rk805_read(sc->base_dev, sc->def->voltage_reg, &val, 1); @@ -624,9 +626,117 @@ device_printf(dev, "Chip Version: %x\n", data[1] & 0xf); } + /* Register this as a 1Hz clock */ + clock_register(dev, 1000000); + config_intrhook_disestablish(&sc->intr_hook); } +static int +rk805_gettime(device_t dev, struct timespec *ts) +{ + struct bcd_clocktime bct; + uint8_t data[7]; + uint8_t ctrl; + int error; + + /* Latch the RTC value into the shadow registers and set 24hr mode */ + error = rk805_read(dev, RK805_RTC_CTRL, &ctrl, 1); + if (error != 0) + return (error); + + ctrl |= RK805_RTC_READSEL; + ctrl &= ~(RK805_RTC_AMPM_MODE | RK805_RTC_GET_TIME); + error = rk805_write(dev, RK805_RTC_CTRL, &ctrl, 1); + if (error != 0) + return (error); + ctrl |= RK805_RTC_GET_TIME; + error = rk805_write(dev, RK805_RTC_CTRL, &ctrl, 1); + if (error != 0) + return (error); + ctrl &= ~RK805_RTC_GET_TIME; + error = rk805_write(dev, RK805_RTC_CTRL, &ctrl, 1); + if (error != 0) + return (error); + + /* This works as long as RK805_RTC_SECS = 0 */ + error = rk805_read(dev, RK805_RTC_SECS, data, 7); + if (error != 0) + return (error); + + /* + * If the reported year is earlier than 2019, assume the clock is unset. + * This is both later than the reset value for the RK805 and RK808 as + * well as being prior to the current time. + */ + if (data[RK805_RTC_YEARS] < 0x19) + return (EINVAL); + + memset(&bct, 0, sizeof(bct)); + bct.year = data[RK805_RTC_YEARS]; + bct.mon = data[RK805_RTC_MONTHS] & RK805_RTC_MONTHS_MASK; + bct.day = data[RK805_RTC_DAYS] & RK805_RTC_DAYS_MASK; + bct.hour = data[RK805_RTC_HOURS] & RK805_RTC_HOURS_MASK; + bct.min = data[RK805_RTC_MINUTES] & RK805_RTC_MINUTES_MASK; + bct.sec = data[RK805_RTC_SECS] & RK805_RTC_SECS_MASK; + bct.dow = data[RK805_RTC_WEEKS] & RK805_RTC_WEEKS_MASK; + /* The day of week is reported as 1-7 with 1 = Monday */ + if (bct.dow == 7) + bct.dow = 0; + bct.ispm = 0; + + if (bootverbose) + device_printf(dev, "Read RTC: %02x-%02x-%02x %02x:%02x:%02x\n", + bct.year, bct.mon, bct.day, bct.hour, bct.min, bct.sec); + + return (clock_bcd_to_ts(&bct, ts, false)); +} + +static int +rk805_settime(device_t dev, struct timespec *ts) +{ + struct bcd_clocktime bct; + uint8_t data[7]; + int error; + uint8_t ctrl; + + clock_ts_to_bcd(ts, &bct, false); + + /* This works as long as RK805_RTC_SECS = 0 */ + data[RK805_RTC_YEARS] = bct.year; + data[RK805_RTC_MONTHS] = bct.mon; + data[RK805_RTC_DAYS] = bct.day; + data[RK805_RTC_HOURS] = bct.hour; + data[RK805_RTC_MINUTES] = bct.min; + data[RK805_RTC_SECS] = bct.sec; + data[RK805_RTC_WEEKS] = bct.dow; + /* The day of week is reported as 1-7 with 1 = Monday */ + if (data[RK805_RTC_WEEKS] == 0) + data[RK805_RTC_WEEKS] = 7; + + error = rk805_read(dev, RK805_RTC_CTRL, &ctrl, 1); + if (error != 0) + return (error); + + ctrl |= RK805_RTC_CTRL_STOP; + ctrl &= ~RK805_RTC_AMPM_MODE; + error = rk805_write(dev, RK805_RTC_CTRL, &ctrl, 1); + if (error != 0) + return (error); + + error = rk805_write(dev, RK805_RTC_SECS, data, 7); + ctrl &= ~RK805_RTC_CTRL_STOP; + rk805_write(dev, RK805_RTC_CTRL, &ctrl, 1); + + if (bootverbose) + device_printf(dev, + "Set RTC at %04x-%02x-%02x %02x:%02x:%02x[.%09ld]\n", + bct.year, bct.mon, bct.day, bct.hour, bct.min, bct.sec, + bct.nsec); + + return (error); +} + static int rk805_attach(device_t dev) { @@ -724,6 +834,11 @@ /* regdev interface */ DEVMETHOD(regdev_map, rk805_map), + + /* Clock interface */ + DEVMETHOD(clock_gettime, rk805_gettime), + DEVMETHOD(clock_settime, rk805_settime), + DEVMETHOD_END }; diff --git a/sys/arm64/rockchip/rk805reg.h b/sys/arm64/rockchip/rk805reg.h --- a/sys/arm64/rockchip/rk805reg.h +++ b/sys/arm64/rockchip/rk805reg.h @@ -30,6 +30,31 @@ #ifndef _RK805REG_H_ #define _RK805REG_H_ +/* + * The RTC registers are the same in both RK805 and RK808. + * Note that the code assumes that RK805_RTC_SECS is 0 + */ +#define RK805_RTC_SECS 0x00 +#define RK805_RTC_SECS_MASK 0x7f +#define RK805_RTC_MINUTES 0x01 +#define RK805_RTC_MINUTES_MASK 0x7f +#define RK805_RTC_HOURS 0x02 +#define RK805_RTC_HOURS_MASK 0x3f +#define RK805_RTC_HOURS_PM 0x80 +#define RK805_RTC_DAYS 0x03 +#define RK805_RTC_DAYS_MASK 0x3f +#define RK805_RTC_MONTHS 0x04 +#define RK805_RTC_MONTHS_MASK 0x1f +#define RK805_RTC_YEARS 0x05 +#define RK805_RTC_WEEKS 0x06 /* day of week */ +#define RK805_RTC_WEEKS_MASK 0x07 + +#define RK805_RTC_CTRL 0x10 +#define RK805_RTC_CTRL_STOP (1 << 0) +#define RK805_RTC_AMPM_MODE (1 << 3) +#define RK805_RTC_GET_TIME (1 << 6) +#define RK805_RTC_READSEL (1 << 7) + #define RK805_CHIP_NAME 0x17 #define RK805_CHIP_VER 0x18 #define RK805_OTP_VER 0x19