[Differential] D29140: Add support for the RK805/RK808 RTC

"peterj (Peter Jeremy)" <[email protected]>
Newsgroups gmane.os.freebsd.devel.arm
Message-ID <[email protected]>
peterj created this revision.
peterj added a reviewer: arm64.
Herald added subscribers: emaste, andrew.
Herald added a reviewer: manu.
peterj requested review of this revision.

REVISION SUMMARY
  Implement driver for the RTC embedded in the RK805/RK808 power management system.
  
  This is a replacement for D22692 <https://reviews.freebsd.org/D22692>, taking into account comments on than revision.  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().

TEST PLAN
  Tested using the RK808-D on my RockPro64 (RK3399)

REVISION DETAIL
  https://reviews.freebsd.org/D29140

AFFECTED FILES
  sys/arm64/rockchip/rk805.c
  sys/arm64/rockchip/rk805reg.h

EMAIL PREFERENCES
  https://reviews.freebsd.org/settings/panel/emailpreferences/

To: peterj, #arm64, manu
Cc: andrew, freebsd-arm-list, emaste

_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/freebsd-arm
To unsubscribe, send any mail to "[email protected]"
D29140.85371.patch (text/x-patch, 5.8 KB)
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
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 <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 @@
 
 #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 @@
 }
 
 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
 };
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.