git: 328077bb8ff1 - main - pmic: rockchip: Split the rtc part in its own file

From: Emmanuel Vadot <manu_at_FreeBSD.org>
Date: Sun, 14 Nov 2021 12:34:23 UTC
The branch main has been updated by manu:

URL: https://cgit.FreeBSD.org/src/commit/?id=328077bb8ff1a55d20ca7f8fdbb898e8c07bb057

commit 328077bb8ff1a55d20ca7f8fdbb898e8c07bb057
Author:     Emmanuel Vadot <manu@FreeBSD.org>
AuthorDate: 2021-11-11 19:41:52 +0000
Commit:     Emmanuel Vadot <manu@FreeBSD.org>
CommitDate: 2021-11-14 11:31:18 +0000

    pmic: rockchip: Split the rtc part in its own file
    
    Even if for now all the RTC-related register are at the same offset don't
    use some hardcoded values for them but set them based on the PMIC type.
    
    No functional changes intended.
---
 sys/conf/files.arm64                     |   1 +
 sys/dev/iicbus/pmic/rockchip/rk805.c     | 204 ++++++-------------------------
 sys/dev/iicbus/pmic/rockchip/rk8xx.h     | 109 +++++++++++++++++
 sys/dev/iicbus/pmic/rockchip/rk8xx_rtc.c | 142 +++++++++++++++++++++
 4 files changed, 289 insertions(+), 167 deletions(-)

diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64
index 1f344d0273b8..a3c325070469 100644
--- a/sys/conf/files.arm64
+++ b/sys/conf/files.arm64
@@ -541,6 +541,7 @@ arm64/rockchip/rk_dwc3.c			optional fdt rk_dwc3 soc_rockchip_rk3399
 arm64/rockchip/rk_i2c.c				optional fdt rk_i2c soc_rockchip_rk3328 | fdt rk_i2c soc_rockchip_rk3399
 arm64/rockchip/rk_i2s.c				optional fdt sound soc_rockchip_rk3328 | fdt sound soc_rockchip_rk3399
 dev/iicbus/pmic/rockchip/rk805.c		optional fdt rk805 soc_rockchip_rk3328 | fdt rk805 soc_rockchip_rk3399
+dev/iicbus/pmic/rockchip/rk8xx_rtc.c		optional fdt rk805 soc_rockchip_rk3328 | fdt rk805 soc_rockchip_rk3399
 arm64/rockchip/rk_grf.c				optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399
 arm64/rockchip/rk_pinctrl.c			optional fdt rk_pinctrl soc_rockchip_rk3328 | fdt rk_pinctrl soc_rockchip_rk3399
 arm64/rockchip/rk_gpio.c			optional fdt rk_gpio soc_rockchip_rk3328 | fdt rk_gpio soc_rockchip_rk3399
diff --git a/sys/dev/iicbus/pmic/rockchip/rk805.c b/sys/dev/iicbus/pmic/rockchip/rk805.c
index 76c8462e694e..ab4006ad63f9 100644
--- a/sys/dev/iicbus/pmic/rockchip/rk805.c
+++ b/sys/dev/iicbus/pmic/rockchip/rk805.c
@@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$");
 
 #include <dev/iicbus/pmic/rockchip/rk805reg.h>
 #include <dev/iicbus/pmic/rockchip/rk808reg.h>
+#include <dev/iicbus/pmic/rockchip/rk8xx.h>
 
 #include "clock_if.h"
 #include "regdev_if.h"
@@ -59,55 +60,12 @@ MALLOC_DEFINE(M_RK805_REG, "RK805 regulator", "RK805 power regulator");
 /* #define	dprintf(sc, format, arg...)	device_printf(sc->base_dev, "%s: " format, __func__, arg) */
 #define	dprintf(sc, format, arg...)
 
-enum rk_pmic_type {
-	RK805 = 1,
-	RK808,
-};
-
 static struct ofw_compat_data compat_data[] = {
 	{"rockchip,rk805", RK805},
 	{"rockchip,rk808", RK808},
 	{NULL,             0}
 };
 
-struct rk8xx_regdef {
-	intptr_t		id;
-	char			*name;
-	uint8_t			enable_reg;
-	uint8_t			enable_mask;
-	uint8_t			voltage_reg;
-	uint8_t			voltage_mask;
-	int			voltage_min;
-	int			voltage_max;
-	int			voltage_step;
-	int			voltage_nstep;
-};
-
-struct rk8xx_reg_sc {
-	struct regnode		*regnode;
-	device_t		base_dev;
-	struct rk8xx_regdef	*def;
-	phandle_t		xref;
-	struct regnode_std_param *param;
-};
-
-struct reg_list {
-	TAILQ_ENTRY(reg_list)	next;
-	struct rk8xx_reg_sc	*reg;
-};
-
-struct rk8xx_softc {
-	device_t		dev;
-	struct mtx		mtx;
-	struct resource *	res[1];
-	void *			intrcookie;
-	struct intr_config_hook	intr_hook;
-	enum rk_pmic_type	type;
-
-	TAILQ_HEAD(, reg_list)		regs;
-	int			nregs;
-};
-
 static int rk8xx_regnode_status(struct regnode *regnode, int *status);
 static int rk8xx_regnode_set_voltage(struct regnode *regnode, int min_uvolt,
     int max_uvolt, int *udelay);
@@ -352,7 +310,7 @@ static struct rk8xx_regdef rk808_regdefs[] = {
 	},
 };
 
-static int
+int
 rk8xx_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size)
 {
 	int err;
@@ -361,7 +319,7 @@ rk8xx_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size)
 	return (err);
 }
 
-static int
+int
 rk8xx_write(device_t dev, uint8_t reg, uint8_t *data, uint8_t size)
 {
 
@@ -748,128 +706,6 @@ rk8xx_start(void *pdev)
 	config_intrhook_disestablish(&sc->intr_hook);
 }
 
-static int
-rk8xx_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 = rk8xx_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 = rk8xx_write(dev, RK805_RTC_CTRL, &ctrl, 1);
-	if (error != 0)
-		return (error);
-	ctrl |= RK805_RTC_GET_TIME;
-	error = rk8xx_write(dev, RK805_RTC_CTRL, &ctrl, 1);
-	if (error != 0)
-		return (error);
-	ctrl &= ~RK805_RTC_GET_TIME;
-	error = rk8xx_write(dev, RK805_RTC_CTRL, &ctrl, 1);
-	if (error != 0)
-		return (error);
-
-	/* This works as long as RK805_RTC_SECS = 0 */
-	error = rk8xx_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
-rk8xx_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 = rk8xx_read(dev, RK805_RTC_CTRL, &ctrl, 1);
-	if (error != 0)
-		return (error);
-
-	ctrl |= RK805_RTC_CTRL_STOP;
-	ctrl &= ~RK805_RTC_AMPM_MODE;
-	error = rk8xx_write(dev, RK805_RTC_CTRL, &ctrl, 1);
-	if (error != 0)
-		return (error);
-
-	error = rk8xx_write(dev, RK805_RTC_SECS, data, 7);
-	ctrl &= ~RK805_RTC_CTRL_STOP;
-	rk8xx_write(dev, RK805_RTC_CTRL, &ctrl, 1);
-
-	return (error);
-}
-
-static void
-rk805_poweroff(void *arg, int howto)
-{
-	device_t dev = arg;
-	int error;
-	uint8_t val;
-
-	if ((howto & RB_POWEROFF) == 0)
-		return;
-
-	device_printf(dev, "Powering off...\n");
-	error = rk805_read(dev, RK805_DEV_CTRL, &val, 1);
-	if (error == 0) {
-		val |= RK805_DEV_CTRL_OFF;
-		error = rk805_write(dev, RK805_DEV_CTRL, &val, 1);
-
-		/* Wait a bit for the command to take effect. */
-		if (error == 0)
-			DELAY(100);
-	}
-	device_printf(dev, "Power off failed\n");
-}
-
 static int
 rk8xx_attach(device_t dev)
 {
@@ -896,10 +732,44 @@ rk8xx_attach(device_t dev)
 	case RK805:
 		regdefs = rk805_regdefs;
 		sc->nregs = nitems(rk805_regdefs);
+		sc->rtc_regs.secs = RK805_RTC_SECS;
+		sc->rtc_regs.secs_mask = RK805_RTC_SECS_MASK;
+		sc->rtc_regs.minutes = RK805_RTC_MINUTES;
+		sc->rtc_regs.minutes_mask = RK805_RTC_MINUTES_MASK;
+		sc->rtc_regs.hours = RK805_RTC_HOURS;
+		sc->rtc_regs.hours_mask = RK805_RTC_HOURS_MASK;
+		sc->rtc_regs.days = RK805_RTC_DAYS;
+		sc->rtc_regs.days_mask = RK805_RTC_DAYS_MASK;
+		sc->rtc_regs.months = RK805_RTC_MONTHS;
+		sc->rtc_regs.months_mask = RK805_RTC_MONTHS_MASK;
+		sc->rtc_regs.years = RK805_RTC_YEARS;
+		sc->rtc_regs.weeks = RK805_RTC_WEEKS_MASK;
+		sc->rtc_regs.ctrl = RK805_RTC_CTRL;
+		sc->rtc_regs.ctrl_stop_mask = RK805_RTC_CTRL_STOP;
+		sc->rtc_regs.ctrl_ampm_mask = RK805_RTC_AMPM_MODE;
+		sc->rtc_regs.ctrl_gettime_mask = RK805_RTC_GET_TIME;
+		sc->rtc_regs.ctrl_readsel_mask = RK805_RTC_READSEL;
 		break;
 	case RK808:
 		regdefs = rk808_regdefs;
 		sc->nregs = nitems(rk808_regdefs);
+		sc->rtc_regs.secs = RK808_RTC_SECS;
+		sc->rtc_regs.secs_mask = RK808_RTC_SECS_MASK;
+		sc->rtc_regs.minutes = RK808_RTC_MINUTES;
+		sc->rtc_regs.minutes_mask = RK808_RTC_MINUTES_MASK;
+		sc->rtc_regs.hours = RK808_RTC_HOURS;
+		sc->rtc_regs.hours_mask = RK808_RTC_HOURS_MASK;
+		sc->rtc_regs.days = RK808_RTC_DAYS;
+		sc->rtc_regs.days_mask = RK808_RTC_DAYS_MASK;
+		sc->rtc_regs.months = RK808_RTC_MONTHS;
+		sc->rtc_regs.months_mask = RK808_RTC_MONTHS_MASK;
+		sc->rtc_regs.years = RK808_RTC_YEARS;
+		sc->rtc_regs.weeks = RK808_RTC_WEEKS_MASK;
+		sc->rtc_regs.ctrl = RK808_RTC_CTRL;
+		sc->rtc_regs.ctrl_stop_mask = RK808_RTC_CTRL_STOP;
+		sc->rtc_regs.ctrl_ampm_mask = RK808_RTC_AMPM_MODE;
+		sc->rtc_regs.ctrl_gettime_mask = RK808_RTC_GET_TIME;
+		sc->rtc_regs.ctrl_readsel_mask = RK808_RTC_READSEL;
 		break;
 	default:
 		device_printf(dev, "Unknown type %d\n", sc->type);
diff --git a/sys/dev/iicbus/pmic/rockchip/rk8xx.h b/sys/dev/iicbus/pmic/rockchip/rk8xx.h
new file mode 100644
index 000000000000..4351f8fe254c
--- /dev/null
+++ b/sys/dev/iicbus/pmic/rockchip/rk8xx.h
@@ -0,0 +1,109 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Emmanuel Vadot <manu@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _RK8XX_H_
+#define	_RK8XX_H_
+
+#include <sys/kernel.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+enum rk_pmic_type {
+	RK805 = 1,
+	RK808,
+};
+
+struct rk8xx_regdef {
+	intptr_t		id;
+	char			*name;
+	uint8_t			enable_reg;
+	uint8_t			enable_mask;
+	uint8_t			voltage_reg;
+	uint8_t			voltage_mask;
+	int			voltage_min;
+	int			voltage_max;
+	int			voltage_step;
+	int			voltage_nstep;
+};
+
+struct rk8xx_reg_sc {
+	struct regnode		*regnode;
+	device_t		base_dev;
+	struct rk8xx_regdef	*def;
+	phandle_t		xref;
+	struct regnode_std_param *param;
+};
+
+struct reg_list {
+	TAILQ_ENTRY(reg_list)	next;
+	struct rk8xx_reg_sc	*reg;
+};
+
+struct rk8xx_rtc_reg {
+	uint8_t	secs;
+	uint8_t	secs_mask;
+	uint8_t	minutes;
+	uint8_t	minutes_mask;
+	uint8_t	hours;
+	uint8_t	hours_mask;
+	uint8_t	days;
+	uint8_t	days_mask;
+	uint8_t	months;
+	uint8_t	months_mask;
+	uint8_t	years;
+	uint8_t	weeks;
+	uint8_t	weeks_mask;
+	uint8_t	ctrl;
+	uint8_t	ctrl_stop_mask;
+	uint8_t	ctrl_ampm_mask;
+	uint8_t	ctrl_gettime_mask;
+	uint8_t	ctrl_readsel_mask;
+};
+
+struct rk8xx_softc {
+	device_t		dev;
+	struct mtx		mtx;
+	struct resource *	res[1];
+	void *			intrcookie;
+	struct intr_config_hook	intr_hook;
+	enum rk_pmic_type	type;
+
+	TAILQ_HEAD(, reg_list)		regs;
+	int			nregs;
+
+	struct rk8xx_rtc_reg	rtc_regs;
+};
+
+int rk8xx_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size);
+int rk8xx_write(device_t dev, uint8_t reg, uint8_t *data, uint8_t size);
+
+/* rk8xx_rtc.c */
+int rk8xx_gettime(device_t dev, struct timespec *ts);
+int rk8xx_settime(device_t dev, struct timespec *ts);
+
+#endif	/* _RK8XX_H_ */
diff --git a/sys/dev/iicbus/pmic/rockchip/rk8xx_rtc.c b/sys/dev/iicbus/pmic/rockchip/rk8xx_rtc.c
new file mode 100644
index 000000000000..2f755d16b164
--- /dev/null
+++ b/sys/dev/iicbus/pmic/rockchip/rk8xx_rtc.c
@@ -0,0 +1,142 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Emmanuel Vadot <manu@FreeBSD.org>
+ * Copyright (c) 2021 Peter Jeremy <peterj@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/systm.h>
+#include <sys/clock.h>
+
+#include <dev/iicbus/pmic/rockchip/rk8xx.h>
+
+int
+rk8xx_gettime(device_t dev, struct timespec *ts)
+{
+	struct rk8xx_softc *sc;
+	struct bcd_clocktime bct;
+	uint8_t data[7];
+	uint8_t ctrl;
+	int error;
+
+	sc = device_get_softc(dev);
+
+	/* Latch the RTC value into the shadow registers and set 24hr mode */
+	error = rk8xx_read(dev, sc->rtc_regs.ctrl, &ctrl, 1);
+	if (error != 0)
+		return (error);
+
+	ctrl |= sc->rtc_regs.ctrl_readsel_mask;
+	ctrl &= ~(sc->rtc_regs.ctrl_ampm_mask | sc->rtc_regs.ctrl_gettime_mask);
+	error = rk8xx_write(dev, sc->rtc_regs.ctrl, &ctrl, 1);
+	if (error != 0)
+		return (error);
+	ctrl |= sc->rtc_regs.ctrl_gettime_mask;
+	error = rk8xx_write(dev, sc->rtc_regs.ctrl, &ctrl, 1);
+	if (error != 0)
+		return (error);
+	ctrl &= ~sc->rtc_regs.ctrl_gettime_mask;
+	error = rk8xx_write(dev, sc->rtc_regs.ctrl, &ctrl, 1);
+	if (error != 0)
+		return (error);
+
+	/* This works as long as sc->rtc_regs.secs = 0 */
+	error = rk8xx_read(dev, sc->rtc_regs.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[sc->rtc_regs.years] < 0x19)
+		return (EINVAL);
+
+	memset(&bct, 0, sizeof(bct));
+	bct.year = data[sc->rtc_regs.years];
+	bct.mon = data[sc->rtc_regs.months] & sc->rtc_regs.months_mask;
+	bct.day = data[sc->rtc_regs.days] & sc->rtc_regs.days_mask;
+	bct.hour = data[sc->rtc_regs.hours] & sc->rtc_regs.hours_mask;
+	bct.min = data[sc->rtc_regs.minutes] & sc->rtc_regs.minutes_mask;
+	bct.sec = data[sc->rtc_regs.secs] & sc->rtc_regs.secs_mask;
+	bct.dow = data[sc->rtc_regs.weeks] & sc->rtc_regs.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));
+}
+
+int
+rk8xx_settime(device_t dev, struct timespec *ts)
+{
+	struct rk8xx_softc *sc;
+	struct bcd_clocktime bct;
+	uint8_t data[7];
+	int error;
+	uint8_t ctrl;
+
+	sc = device_get_softc(dev);
+
+	clock_ts_to_bcd(ts, &bct, false);
+
+	/* This works as long as RK805_RTC_SECS = 0 */
+	data[sc->rtc_regs.years] = bct.year;
+	data[sc->rtc_regs.months] = bct.mon;
+	data[sc->rtc_regs.days] = bct.day;
+	data[sc->rtc_regs.hours] = bct.hour;
+	data[sc->rtc_regs.minutes] = bct.min;
+	data[sc->rtc_regs.secs] = bct.sec;
+	data[sc->rtc_regs.weeks] = bct.dow;
+	/* The day of week is reported as 1-7 with 1 = Monday */
+	if (data[sc->rtc_regs.weeks] == 0)
+		data[sc->rtc_regs.weeks] = 7;
+
+	error = rk8xx_read(dev, sc->rtc_regs.ctrl, &ctrl, 1);
+	if (error != 0)
+		return (error);
+
+	ctrl |= sc->rtc_regs.ctrl_stop_mask;
+	ctrl &= ~sc->rtc_regs.ctrl_ampm_mask;
+	error = rk8xx_write(dev, sc->rtc_regs.ctrl, &ctrl, 1);
+	if (error != 0)
+		return (error);
+
+	error = rk8xx_write(dev, sc->rtc_regs.secs, data, 7);
+	ctrl &= ~sc->rtc_regs.ctrl_stop_mask;
+	rk8xx_write(dev, sc->rtc_regs.ctrl, &ctrl, 1);
+
+	return (error);
+}