The upstream devicetrees describe the CV1800 RTC subsystem with the
"sophgo,cv1800b-rtc" compatible string. The RTC seconds counter and the
system-reset controls share the same register range.

Add an RTC driver which directly matches this node, enables its clock,
and implements reading and setting the 32-bit seconds counter. Bind the
CV1800B sysreset driver as a child from the RTC driver's bind callback so
both functions can be instantiated from the same devicetree node.

Initialize the RTC and sysreset register maps from their shared devicetree
node instead of using hard-coded MMIO addresses. Remove the board-level
sysreset binding from Milk-V Duo. Boards using the upstream RTC node can
now instantiate both functions without board-specific binding code.

Signed-off-by: Zhuxu Ran <[email protected]>
---
 board/sophgo/milkv_duo/board.c         |   5 --
 configs/milkv_duo_256m_defconfig       |   2 +
 configs/milkv_duo_defconfig            |   2 +
 configs/sipeed_licheerv_nano_defconfig |   2 +
 drivers/rtc/Kconfig                    |   9 +++
 drivers/rtc/Makefile                   |   1 +
 drivers/rtc/rtc-cv1800.c               | 122 +++++++++++++++++++++++++++++++++
 drivers/sysreset/Kconfig               |   6 +-
 drivers/sysreset/sysreset_cv1800b.c    |  86 ++++++++++++++---------
 9 files changed, 194 insertions(+), 41 deletions(-)

diff --git a/board/sophgo/milkv_duo/board.c b/board/sophgo/milkv_duo/board.c
index 34339c42925..3423f54012e 100644
--- a/board/sophgo/milkv_duo/board.c
+++ b/board/sophgo/milkv_duo/board.c
@@ -3,15 +3,10 @@
  * Copyright (c) 2024, Kongyang Liu <[email protected]>
  */
 
-#include <dm/lists.h>
-
 #include "../common/ethernet.h"
 
 int board_init(void)
 {
-       if (IS_ENABLED(CONFIG_SYSRESET_CV1800B))
-               device_bind_driver(gd->dm_root, "cv1800b_sysreset", "sysreset", 
NULL);
-
        if (IS_ENABLED(CONFIG_NET_LEGACY))
                cv1800b_ephy_init();
 
diff --git a/configs/milkv_duo_256m_defconfig b/configs/milkv_duo_256m_defconfig
index 436bdb1549a..ea31b053672 100644
--- a/configs/milkv_duo_256m_defconfig
+++ b/configs/milkv_duo_256m_defconfig
@@ -36,6 +36,8 @@ CONFIG_MMC_SDHCI=y
 CONFIG_MMC_SDHCI_ADMA=y
 CONFIG_MMC_SDHCI_CV1800B=y
 CONFIG_ETH_DESIGNWARE=y
+CONFIG_DM_RTC=y
+CONFIG_RTC_CV1800B=y
 CONFIG_SYS_NS16550=y
 CONFIG_SYS_NS16550_MEM32=y
 CONFIG_SPI=y
diff --git a/configs/milkv_duo_defconfig b/configs/milkv_duo_defconfig
index 9d6b80362d5..77c8d89701d 100644
--- a/configs/milkv_duo_defconfig
+++ b/configs/milkv_duo_defconfig
@@ -35,6 +35,8 @@ CONFIG_MMC_SDHCI_ADMA=y
 CONFIG_MMC_SDHCI_CV1800B=y
 CONFIG_SPI_FLASH_MACRONIX=y
 CONFIG_ETH_DESIGNWARE=y
+CONFIG_DM_RTC=y
+CONFIG_RTC_CV1800B=y
 CONFIG_SYS_NS16550=y
 CONFIG_SYS_NS16550_MEM32=y
 CONFIG_SPI=y
diff --git a/configs/sipeed_licheerv_nano_defconfig 
b/configs/sipeed_licheerv_nano_defconfig
index 21b8fece201..75229787d2f 100644
--- a/configs/sipeed_licheerv_nano_defconfig
+++ b/configs/sipeed_licheerv_nano_defconfig
@@ -40,6 +40,8 @@ CONFIG_MMC_SDHCI_ADMA=y
 CONFIG_MMC_SDHCI_CV1800B=y
 CONFIG_SPI_FLASH_MACRONIX=y
 CONFIG_ETH_DESIGNWARE=y
+CONFIG_DM_RTC=y
+CONFIG_RTC_CV1800B=y
 CONFIG_SYS_NS16550=y
 CONFIG_SYS_NS16550_MEM32=y
 CONFIG_SPI=y
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 5b44cc99c97..f200d978937 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -54,6 +54,15 @@ config RTC_ARMADA38X
          This adds support for the in-chip RTC that can be found in the
          Armada 38x Marvell's SoC devices.
 
+config RTC_CV1800B
+       bool "Enable Sophgo CV1800B RTC driver"
+       depends on DM_RTC
+       select REGMAP
+       help
+         Enable access to the RTC seconds counter found on Sophgo CV1800
+         series SoCs. The driver also binds the system-reset function provided
+         by the RTC subsystem when CONFIG_SYSRESET_CV1800B is enabled.
+
 config RTC_PCF2127
        bool "Enable PCF2127 driver"
        depends on DM_RTC
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 742481ce185..961fe5409f0 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -7,6 +7,7 @@
 obj-$(CONFIG_$(PHASE_)DM_RTC) += rtc-uclass.o
 
 obj-$(CONFIG_RTC_ARMADA38X) += armada38x.o
+obj-$(CONFIG_RTC_CV1800B) += rtc-cv1800.o
 obj-$(CONFIG_RTC_DAVINCI) += davinci.o
 obj-$(CONFIG_RTC_DS1307) += ds1307.o
 obj-$(CONFIG_RTC_DS1337) += ds1337.o
diff --git a/drivers/rtc/rtc-cv1800.c b/drivers/rtc/rtc-cv1800.c
new file mode 100644
index 00000000000..4a958e44de1
--- /dev/null
+++ b/drivers/rtc/rtc-cv1800.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Zhuxu Ran <[email protected]>
+ *
+ * Based on the Linux driver by Jingbao Qiu <[email protected]>.
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <dm/lists.h>
+#include <regmap.h>
+#include <rtc.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+
+#define SEC_PULSE_GEN          0x1004
+#define SET_SEC_CNTR_VAL       0x1010
+#define SET_SEC_CNTR_TRIG      0x1014
+#define SEC_CNTR_VAL           0x1018
+#define MACRO_RG_SET_T         0x1498
+
+#define SEL_SEC_PULSE          BIT(31)
+
+struct cv1800_rtc_priv {
+       struct regmap *regmap;
+       struct clk clk;
+};
+
+static int cv1800_rtc_get(struct udevice *dev, struct rtc_time *time)
+{
+       struct cv1800_rtc_priv *priv = dev_get_priv(dev);
+       u32 value;
+       int ret;
+
+       ret = regmap_read(priv->regmap, SEC_PULSE_GEN, &value);
+       if (ret)
+               return ret;
+       if (value & SEL_SEC_PULSE)
+               return -EINVAL;
+
+       ret = regmap_read(priv->regmap, SEC_CNTR_VAL, &value);
+       if (ret)
+               return ret;
+
+       rtc_to_tm(value, time);
+
+       return 0;
+}
+
+static int cv1800_rtc_set(struct udevice *dev, const struct rtc_time *time)
+{
+       struct cv1800_rtc_priv *priv = dev_get_priv(dev);
+       time64_t seconds;
+       int ret;
+
+       if (time->tm_year < 1970)
+               return -EINVAL;
+
+       seconds = rtc_mktime(time);
+       if (seconds > U32_MAX)
+               return -ERANGE;
+
+       ret = regmap_write(priv->regmap, SET_SEC_CNTR_VAL, seconds);
+       if (ret)
+               return ret;
+
+       ret = regmap_write(priv->regmap, SET_SEC_CNTR_TRIG, 1);
+       if (ret)
+               return ret;
+
+       ret = regmap_write(priv->regmap, MACRO_RG_SET_T, seconds);
+       if (ret)
+               return ret;
+
+       return regmap_update_bits(priv->regmap, SEC_PULSE_GEN,
+                                 SEL_SEC_PULSE, 0);
+}
+
+static int cv1800_rtc_bind(struct udevice *dev)
+{
+       if (!CONFIG_IS_ENABLED(SYSRESET_CV1800B))
+               return 0;
+
+       return device_bind_driver_to_node(dev, "cv1800b_sysreset",
+                                         "sysreset", dev_ofnode(dev), NULL);
+}
+
+static int cv1800_rtc_probe(struct udevice *dev)
+{
+       struct cv1800_rtc_priv *priv = dev_get_priv(dev);
+       int ret;
+
+       priv->regmap = devm_regmap_init(dev, NULL, NULL, NULL);
+       if (IS_ERR(priv->regmap))
+               return PTR_ERR(priv->regmap);
+
+       ret = clk_get_by_name(dev, "rtc", &priv->clk);
+       if (ret)
+               return ret;
+
+       return clk_enable(&priv->clk);
+}
+
+static const struct rtc_ops cv1800_rtc_ops = {
+       .get = cv1800_rtc_get,
+       .set = cv1800_rtc_set,
+};
+
+static const struct udevice_id cv1800_rtc_ids[] = {
+       { .compatible = "sophgo,cv1800b-rtc" },
+       { }
+};
+
+U_BOOT_DRIVER(cv1800b_rtc) = {
+       .name = "cv1800b_rtc",
+       .id = UCLASS_RTC,
+       .of_match = cv1800_rtc_ids,
+       .bind = cv1800_rtc_bind,
+       .probe = cv1800_rtc_probe,
+       .ops = &cv1800_rtc_ops,
+       .priv_auto = sizeof(struct cv1800_rtc_priv),
+};
diff --git a/drivers/sysreset/Kconfig b/drivers/sysreset/Kconfig
index 16ef434a8d9..c3125d1921a 100644
--- a/drivers/sysreset/Kconfig
+++ b/drivers/sysreset/Kconfig
@@ -60,9 +60,11 @@ config SYSRESET_CMD_POWEROFF
 endif
 
 config SYSRESET_CV1800B
-       bool "Enable support for Sophgo cv1800b System Reset"
+       bool "Enable support for Sophgo CV1800B system reset"
+       depends on RTC_CV1800B
        help
-         Enable system reset support for Sophgo cv1800b SoC.
+         Enable system reset and power control through the RTC subsystem found
+         on Sophgo CV1800 series SoCs.
 
 config POWEROFF_GPIO
        bool "Enable support for GPIO poweroff driver"
diff --git a/drivers/sysreset/sysreset_cv1800b.c 
b/drivers/sysreset/sysreset_cv1800b.c
index 9cd62772ef4..0836b018956 100644
--- a/drivers/sysreset/sysreset_cv1800b.c
+++ b/drivers/sysreset/sysreset_cv1800b.c
@@ -4,61 +4,79 @@
  */
 
 #include <dm.h>
-#include <stdbool.h>
+#include <regmap.h>
 #include <sysreset.h>
-#include <wait_bit.h>
-#include <linux/io.h>
+#include <linux/bitops.h>
 #include <linux/errno.h>
+#include <linux/err.h>
 
-#define REG_RTC_BASE             (void *)0x05026000
-#define REG_RTC_CTRL_BASE        (void *)0x05025000
-#define REG_RTC_EN_SHDN_REQ      (REG_RTC_BASE + 0xc0)
-#define REG_RTC_EN_PWR_CYC_REQ   (REG_RTC_BASE + 0xc8)
-#define REG_RTC_EN_WARM_RST_REQ  (REG_RTC_BASE + 0xcc)
-#define REG_RTC_CTRL_UNLOCKKEY   (REG_RTC_CTRL_BASE + 0x4)
-#define REG_RTC_CTRL             (REG_RTC_CTRL_BASE + 0x8)
+#define RTC_CTRL_UNLOCKKEY     0x0004
+#define RTC_CTRL               0x0008
+#define RTC_EN_SHDN_REQ                0x10c0
+#define RTC_EN_PWR_CYC_REQ     0x10c8
+#define RTC_EN_WARM_RST_REQ    0x10cc
 
-#define CTRL_UNLOCKKEY_MAGIC     0xAB18
+#define CTRL_UNLOCKKEY_MAGIC   0xab18
 
-/* REG_RTC_CTRL */
-#define BIT_REQ_SHDN       BIT(0)
-#define BIT_REQ_PWR_CYC    BIT(3)
-#define BIT_REQ_WARM_RST   BIT(4)
+#define BIT_REQ_SHDN           BIT(0)
+#define BIT_REQ_PWR_CYC                BIT(3)
+#define BIT_REQ_WARM_RST       BIT(4)
 
-static struct {
-       void *pre_req_reg;
+struct cv1800b_sysreset_priv {
+       struct regmap *regmap;
+};
+
+static const struct {
+       u32 pre_req_reg;
        u32 req_bit;
 } reset_info[SYSRESET_COUNT] = {
-       [SYSRESET_WARM]      = { REG_RTC_EN_WARM_RST_REQ, BIT_REQ_WARM_RST },
-       [SYSRESET_COLD]      = { REG_RTC_EN_WARM_RST_REQ, BIT_REQ_WARM_RST },
-       [SYSRESET_POWER]     = { REG_RTC_EN_PWR_CYC_REQ, BIT_REQ_PWR_CYC },
-       [SYSRESET_POWER_OFF] = { REG_RTC_EN_SHDN_REQ, BIT_REQ_SHDN },
+       [SYSRESET_WARM]      = { RTC_EN_WARM_RST_REQ, BIT_REQ_WARM_RST },
+       [SYSRESET_COLD]      = { RTC_EN_WARM_RST_REQ, BIT_REQ_WARM_RST },
+       [SYSRESET_POWER]     = { RTC_EN_PWR_CYC_REQ, BIT_REQ_PWR_CYC },
+       [SYSRESET_POWER_OFF] = { RTC_EN_SHDN_REQ, BIT_REQ_SHDN },
 };
 
 static int cv1800b_sysreset_request(struct udevice *dev, enum sysreset_t type)
 {
-       u32 reg;
+       struct cv1800b_sysreset_priv *priv = dev_get_priv(dev);
+       int ret;
+
+       ret = regmap_write(priv->regmap, reset_info[type].pre_req_reg, 1);
+       if (ret)
+               return ret;
 
-       writel(1, reset_info[type].pre_req_reg);
-       writel(CTRL_UNLOCKKEY_MAGIC, REG_RTC_CTRL_UNLOCKKEY);
-       reg = readl(REG_RTC_CTRL);
-       writel(0xFFFF0800 | reset_info[type].req_bit, REG_RTC_CTRL);
+       ret = regmap_write(priv->regmap, RTC_CTRL_UNLOCKKEY,
+                          CTRL_UNLOCKKEY_MAGIC);
+       if (ret)
+               return ret;
+
+       ret = regmap_write(priv->regmap, RTC_CTRL,
+                          0xffff0800 | reset_info[type].req_bit);
+       if (ret)
+               return ret;
 
        return -EINPROGRESS;
 }
 
+static int cv1800b_sysreset_probe(struct udevice *dev)
+{
+       struct cv1800b_sysreset_priv *priv = dev_get_priv(dev);
+
+       priv->regmap = devm_regmap_init(dev, NULL, NULL, NULL);
+       if (IS_ERR(priv->regmap))
+               return PTR_ERR(priv->regmap);
+
+       return 0;
+}
+
 static struct sysreset_ops cv1800b_sysreset = {
        .request = cv1800b_sysreset_request,
 };
 
-static const struct udevice_id cv1800b_sysreset_ids[] = {
-       { .compatible = "sophgo,cv1800b-sysreset", },
-       {},
-};
-
 U_BOOT_DRIVER(sysreset_cv1800b) = {
        .name = "cv1800b_sysreset",
-       .id       = UCLASS_SYSRESET,
-       .ops  = &cv1800b_sysreset,
-       .of_match = cv1800b_sysreset_ids
+       .id = UCLASS_SYSRESET,
+       .probe = cv1800b_sysreset_probe,
+       .ops = &cv1800b_sysreset,
+       .priv_auto = sizeof(struct cv1800b_sysreset_priv),
 };

-- 
2.55.0

Reply via email to