git: 07564e176201 - main - arm64: Add support for the RK805/RK808 RTC

Peter Jeremy peterj at FreeBSD.org
Fri Mar 12 22:07:12 UTC 2021


The branch main has been updated by peterj:

URL: https://cgit.FreeBSD.org/src/commit/?id=07564e1762010ba7e8ef5a7574bf9ceee811e95c

commit 07564e1762010ba7e8ef5a7574bf9ceee811e95c
Author:     Peter Jeremy <peterj at FreeBSD.org>
AuthorDate: 2021-03-12 22:06:04 +0000
Commit:     Peter Jeremy <peterj at FreeBSD.org>
CommitDate: 2021-03-12 22:06:04 +0000

    arm64: Add support for the RK805/RK808 RTC
    
    Implement a driver for the RTC embedded in the RK805/RK808 power
    management system used for RK3328 and RK3399 SoCs.
    
    Based on experiments on my RK808, setting the time doesn't alter the
    internal/inaccessible sub-second counter, therefore there's no point
    in calling clock_schedule().
    
    Based on an earlier revision by andrew.
    
    Reviewed by:    manu
    Differential Revision:  https://reviews.freebsd.org/D22692
    Sponsored by:   Google
    MFC after:      1 week
---
 sys/arm64/rockchip/rk805.c    | 123 ++++++++++++++++++++++++++++++++++++++++--
 sys/arm64/rockchip/rk805reg.h |  25 +++++++++
 2 files changed, 144 insertions(+), 4 deletions(-)

diff --git a/sys/arm64/rockchip/rk805.c b/sys/arm64/rockchip/rk805.c
index 19397627a8b0..d3e04081aeb2 100644
--- a/sys/arm64/rockchip/rk805.c
+++ b/sys/arm64/rockchip/rk805.c
@@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/bus.h>
+#include <sys/clock.h>
 #include <sys/kernel.h>
 #include <sys/module.h>
 #include <sys/mutex.h>
@@ -46,6 +47,7 @@ __FBSDID("$FreeBSD$");
 
 #include <arm64/rockchip/rk805reg.h>
 
+#include "clock_if.h"
 #include "regdev_if.h"
 
 MALLOC_DEFINE(M_RK805_REG, "RK805 regulator", "RK805 power regulator");
@@ -356,10 +358,10 @@ rk805_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size)
 }
 
 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 @@ rk805_regnode_enable(struct regnode *regnode, bool enable, int *udelay)
 		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 @@ rk805_regnode_set_voltage(struct regnode *regnode, int min_uvolt,
 	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 @@ rk805_start(void *pdev)
 		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 @@ static device_method_t rk805_methods[] = {
 
 	/* 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
index db489d77c26e..b1f4481a5b68 100644
--- 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


More information about the dev-commits-src-main mailing list