Просмотр исходного кода

[dm][rtc] update rtc and new drivers (#11033)

* [dd][rtc] set the RTC alarm thread stack size default.

Signed-off-by: GuEe-GUI <2991707448@qq.com>

* [dm][rtc] make Kconfig import for DM

Signed-off-by: GuEe-GUI <2991707448@qq.com>

* [dm][rtc] support DM API for RTC

1. rtc_dev_set_name for RTC device init the name auto.
2. rtc_wkalarm_to_timestamp and rtc_timestamp_to_wkalarm for
   rt_rtc_wkalarm/time_t convert.

Signed-off-by: GuEe-GUI <2991707448@qq.com>

* [dm][rtc] add new drivers

1. Dallas/Maxim DS1302
2. Dallas/Maxim DS1307/37/38/39/40, ST M41T11
3. Goldfish Real Time Clock
4. Haoyu Microelectronics HYM8563
5. NXP PCF8523
6. Philips PCF8563/Epson RTC8564
7. ARM PL031
8. Epson RX8010SJ

Signed-off-by: GuEe-GUI <2991707448@qq.com>

---------

Signed-off-by: GuEe-GUI <2991707448@qq.com>
GUI 2 месяцев назад
Родитель
Сommit
e465ec567d

+ 60 - 2
components/drivers/rtc/Kconfig

@@ -1,4 +1,4 @@
-config RT_USING_RTC
+menuconfig RT_USING_RTC
     bool "Using RTC device drivers"
     default n
 
@@ -10,7 +10,7 @@ config RT_USING_RTC
         if RT_USING_ALARM
             config RT_ALARM_STACK_SIZE
                 int "stack size for alarm thread"
-                default 2048
+                default IDLE_THREAD_STACK_SIZE
 
             config RT_ALARM_TIMESLICE
                 int "timeslice for alarm thread"
@@ -30,3 +30,61 @@ config RT_USING_RTC
             bool "Using software simulation RTC device"
             default n
     endif
+
+config RT_RTC_DS1302
+    bool "Dallas/Maxim DS1302"
+    depends on RT_USING_DM
+    depends on RT_USING_RTC
+    depends on RT_USING_SPI
+    default n
+
+config RT_RTC_DS1307
+    bool "Dallas/Maxim DS1307/37/38/39/40, ST M41T11"
+    depends on RT_USING_DM
+    depends on RT_USING_RTC
+    depends on RT_USING_I2C
+    default n
+
+config RT_RTC_GOLDFISH
+    bool "Goldfish Real Time Clock"
+    depends on RT_USING_DM
+    depends on RT_USING_RTC
+    default n
+
+config RT_RTC_HYM8563
+    bool "Haoyu Microelectronics HYM8563"
+    depends on RT_USING_DM
+    depends on RT_USING_RTC
+    depends on RT_USING_I2C
+    default n
+
+config RT_RTC_PCF8523
+    bool "NXP PCF8523"
+    depends on RT_USING_DM
+    depends on RT_USING_RTC
+    depends on RT_USING_I2C
+    default n
+
+config RT_RTC_PCF8563
+    bool "Philips PCF8563/Epson RTC8564"
+    depends on RT_USING_DM
+    depends on RT_USING_RTC
+    depends on RT_USING_I2C
+    default n
+
+config RT_RTC_PL031
+    bool "ARM PL031"
+    depends on RT_USING_DM
+    depends on RT_USING_RTC
+    default n
+
+config RT_RTC_RX8010
+    bool "Epson RX8010SJ"
+    depends on RT_USING_DM
+    depends on RT_USING_RTC
+    depends on RT_USING_I2C
+    default n
+
+if RT_USING_DM && RT_USING_RTC
+    osource "$(SOC_DM_RTC_DIR)/Kconfig"
+endif

+ 27 - 0
components/drivers/rtc/SConscript

@@ -13,6 +13,33 @@ if GetDepend(['RT_USING_RTC']):
     if GetDepend(['RT_USING_SOFT_RTC']):
         src = src + ['dev_soft_rtc.c']
 
+if GetDepend(['RT_USING_DM']):
+    src += ['rtc_dm.c']
+
+if GetDepend(['RT_RTC_DS1302']):
+    src += ['rtc-ds1302.c']
+
+if GetDepend(['RT_RTC_DS1307']):
+    src += ['rtc-ds1307.c']
+
+if GetDepend(['RT_RTC_GOLDFISH']):
+    src += ['rtc-goldfish.c']
+
+if GetDepend(['RT_RTC_HYM8563']):
+    src += ['rtc-hym8563.c']
+
+if GetDepend(['RT_RTC_PCF8523']):
+    src += ['rtc-pcf8523.c']
+
+if GetDepend(['RT_RTC_PCF8563']):
+    src += ['rtc-pcf8563.c']
+
+if GetDepend(['RT_RTC_PL031']):
+    src += ['rtc-pl031.c']
+
+if GetDepend(['RT_RTC_RX8010']):
+    src += ['rtc-rx8010.c']
+
 group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_RTC'], CPPPATH = CPPPATH)
 
 Return('group')

+ 256 - 0
components/drivers/rtc/rtc-ds1302.c

@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-09-23     GuEe-GUI     first version
+ */
+
+#include "rtc_dm.h"
+
+#define DBG_TAG "rtc.ds1302"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#define RTC_CMD_READ            0x81    /* Read command */
+#define RTC_CMD_WRITE           0x80    /* Write command */
+
+#define RTC_CMD_WRITE_ENABLE    0x00    /* Write enable */
+#define RTC_CMD_WRITE_DISABLE   0x80    /* Write disable */
+
+#define RTC_ADDR_RAM0           0x20    /* Address of RAM0 */
+#define RTC_ADDR_TCR            0x08    /* Address of trickle charge register */
+#define RTC_CLCK_BURST          0x1F    /* Address of clock burst */
+#define RTC_CLCK_LEN            0x08    /* Size of clock burst */
+#define RTC_ADDR_CTRL           0x07    /* Address of control register */
+#define RTC_ADDR_YEAR           0x06    /* Address of year register */
+#define RTC_ADDR_DAY            0x05    /* Address of day of week register */
+#define RTC_ADDR_MON            0x04    /* Address of month register */
+#define RTC_ADDR_DATE           0x03    /* Address of day of month register */
+#define RTC_ADDR_HOUR           0x02    /* Address of hour register */
+#define RTC_ADDR_MIN            0x01    /* Address of minute register */
+#define RTC_ADDR_SEC            0x00    /* Address of second register */
+
+static rt_err_t ds1302_rtc_get_time(struct rt_spi_device *spi_dev, time_t *sec)
+{
+    struct tm tm;
+    rt_err_t err;
+    rt_uint8_t addr = RTC_CLCK_BURST << 1 | RTC_CMD_READ, buf[RTC_CLCK_LEN - 1];
+
+    err = rt_spi_send_then_recv(spi_dev, &addr, sizeof(addr), buf, sizeof(buf));
+
+    if (err)
+    {
+        return err;
+    }
+
+    /* Decode the registers */
+    tm.tm_sec = rt_bcd2bin(buf[RTC_ADDR_SEC]);
+    tm.tm_min = rt_bcd2bin(buf[RTC_ADDR_MIN]);
+    tm.tm_hour = rt_bcd2bin(buf[RTC_ADDR_HOUR]);
+    tm.tm_wday = buf[RTC_ADDR_DAY] - 1;
+    tm.tm_mday = rt_bcd2bin(buf[RTC_ADDR_DATE]);
+    tm.tm_mon = rt_bcd2bin(buf[RTC_ADDR_MON]) - 1;
+    tm.tm_year = rt_bcd2bin(buf[RTC_ADDR_YEAR]) + 100;
+
+    *sec = timegm(&tm);
+
+    return RT_EOK;
+}
+
+static rt_err_t ds1302_rtc_set_time(struct rt_spi_device *spi_dev, time_t *sec)
+{
+    rt_err_t err;
+    struct tm *tm;
+    rt_uint8_t buf[1 + RTC_CLCK_LEN], *bp;
+
+    tm = localtime(sec);
+
+    /* Enable writing */
+    bp = buf;
+    *bp++ = RTC_ADDR_CTRL << 1 | RTC_CMD_WRITE;
+    *bp++ = RTC_CMD_WRITE_ENABLE;
+
+    err = rt_spi_send_then_recv(spi_dev, buf, 2, RT_NULL, 0);
+
+    if (err)
+    {
+        return err;
+    }
+
+    /* Write registers starting at the first time/date address. */
+    bp = buf;
+    *bp++ = RTC_CLCK_BURST << 1 | RTC_CMD_WRITE;
+
+    *bp++ = rt_bin2bcd(tm->tm_sec);
+    *bp++ = rt_bin2bcd(tm->tm_min);
+    *bp++ = rt_bin2bcd(tm->tm_hour);
+    *bp++ = rt_bin2bcd(tm->tm_mday);
+    *bp++ = rt_bin2bcd(tm->tm_mon + 1);
+    *bp++ = tm->tm_wday + 1;
+    *bp++ = rt_bin2bcd(tm->tm_year % 100);
+    *bp++ = RTC_CMD_WRITE_DISABLE;
+
+    return rt_spi_send_then_recv(spi_dev, buf, sizeof(buf), RT_NULL, 0);
+}
+
+static rt_err_t ds1302_rtc_control(rt_device_t dev, int cmd, void *args)
+{
+    rt_err_t err = RT_EOK;
+    struct rt_spi_device *spi_dev = dev->user_data;
+
+    if (!args)
+    {
+        return -RT_EINVAL;
+    }
+
+    switch (cmd)
+    {
+    case RT_DEVICE_CTRL_RTC_GET_TIME:
+        err = ds1302_rtc_get_time(spi_dev, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_TIME:
+        err = ds1302_rtc_set_time(spi_dev, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_GET_TIMEVAL:
+        err = ds1302_rtc_get_time(spi_dev, (time_t *)&((struct timeval *)args)->tv_sec);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_TIMEVAL:
+        err = ds1302_rtc_set_time(spi_dev, (time_t *)&((struct timeval *)args)->tv_sec);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_GET_ALARM:
+    case RT_DEVICE_CTRL_RTC_SET_ALARM:
+        err = -RT_ENOSYS;
+        break;
+
+    default:
+        err = -RT_EINVAL;
+        break;
+    }
+
+    return err;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops ds1302_rtc_ops =
+{
+    .control = ds1302_rtc_control,
+};
+#endif
+
+static rt_err_t ds1302_rtc_probe(struct rt_spi_device *spi_dev)
+{
+    rt_err_t err = RT_EOK;
+    const char *dev_name;
+    rt_uint8_t addr, buf[4];
+
+    if (spi_dev->config.max_hz > 2000000)
+    {
+        LOG_E("Speed is too high");
+        return -RT_EINVAL;
+    }
+    else if (spi_dev->config.mode & RT_SPI_CPHA)
+    {
+        LOG_E("Bad mode");
+        return -RT_EINVAL;
+    }
+
+    addr = RTC_ADDR_CTRL << 1 | RTC_CMD_READ;
+
+    if ((err = rt_spi_send_then_recv(spi_dev, &addr, sizeof(addr), buf, 1)))
+    {
+        LOG_E("Control register read error = %s", rt_strerror(err));
+        return err;
+    }
+
+    if ((buf[0] & ~RTC_CMD_WRITE_DISABLE) != 0)
+    {
+        if ((err = rt_spi_send_then_recv(spi_dev, &addr, sizeof(addr), buf, 1)))
+        {
+            LOG_E("Control register read error = %s", rt_strerror(err));
+            return err;
+        }
+
+        if ((buf[0] & ~RTC_CMD_WRITE_DISABLE) != 0)
+        {
+            LOG_E("Junk in control register");
+            return -RT_EIO;
+        }
+    }
+
+    if (buf[0] == 0)
+    {
+        buf[0] = RTC_ADDR_CTRL << 1 | RTC_CMD_WRITE;
+        buf[1] = RTC_CMD_WRITE_DISABLE;
+
+        if ((err = rt_spi_send_then_recv(spi_dev, buf, 2, RT_NULL, 0)))
+        {
+            LOG_E("Control register write error = %s", rt_strerror(err));
+            return err;
+        }
+
+        addr = RTC_ADDR_CTRL << 1 | RTC_CMD_READ;
+
+        if ((err = rt_spi_send_then_recv(spi_dev, &addr, sizeof(addr), buf, 1)))
+        {
+            LOG_E("Reading control register error = %s", rt_strerror(err));
+            return err;
+        }
+
+        if (buf[0] != RTC_CMD_WRITE_DISABLE)
+        {
+            LOG_E("Failed to detect chip");
+            return -RT_EIO;
+        }
+    }
+
+    spi_dev->parent.user_data = spi_dev;
+
+    spi_dev->parent.type = RT_Device_Class_RTC;
+#ifdef RT_USING_DEVICE_OPS
+    spi_dev->parent.ops = &ds1302_rtc_ops;
+#else
+    spi_dev->parent.control = ds1302_rtc_control;
+#endif
+
+    rtc_dev_set_name(&spi_dev->parent);
+    dev_name = rt_dm_dev_get_name(&spi_dev->parent);
+    err = rt_device_register(&spi_dev->parent, dev_name, RT_DEVICE_FLAG_RDWR);
+
+    return err;
+}
+
+static rt_err_t ds1302_rtc_remove(struct rt_spi_device *spi_dev)
+{
+    rt_device_unregister(&spi_dev->parent);
+
+    return RT_EOK;
+}
+
+static const struct rt_spi_device_id ds1302_rtc_ids[] =
+{
+    { .name = "ds1302" },
+    { /* sentinel */ },
+};
+
+static const struct rt_ofw_node_id ds1302_rtc_ofw_ids[] =
+{
+    { .compatible = "maxim,ds1302" },
+    { /* sentinel */ },
+};
+
+static struct rt_spi_driver ds1302_rtc_driver =
+{
+    .ids = ds1302_rtc_ids,
+    .ofw_ids = ds1302_rtc_ofw_ids,
+
+    .probe = ds1302_rtc_probe,
+    .remove = ds1302_rtc_remove,
+};
+RT_SPI_DRIVER_EXPORT(ds1302_rtc_driver);

+ 643 - 0
components/drivers/rtc/rtc-ds1307.c

@@ -0,0 +1,643 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-09-23     GuEe-GUI     first version
+ */
+
+#include "rtc_dm.h"
+
+#define DBG_TAG "rtc.ds1307"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#define RTC_SEC_REG_ADDR            0x00
+#define RTC_MIN_REG_ADDR            0x01
+#define RTC_HR_REG_ADDR             0x02
+#define RTC_DAY_REG_ADDR            0x03
+#define RTC_DATE_REG_ADDR           0x04
+#define RTC_MON_REG_ADDR            0x05
+#define RTC_YR_REG_ADDR             0x06
+#define RTC_CTL_REG_ADDR            0x07
+
+#define DS1337_CTL_REG_ADDR         0x0e
+#define DS1337_STAT_REG_ADDR        0x0f
+#define DS1340_STAT_REG_ADDR        0x09
+
+#define RTC_STAT_BIT_A1I            0x01
+#define RTC_STAT_BIT_A2I            0x02
+#define RTC_STAT_BIT_OSF            0x80
+
+#define RTC_SEC_BIT_CH              0x80    /* Clock Halt (in Register 0) */
+
+/* DS1307-specific bits */
+#define RTC_CTL_BIT_RS0             0x01    /* Rate select 0 */
+#define RTC_CTL_BIT_RS1             0x02    /* Rate select 1 */
+#define RTC_CTL_BIT_SQWE            0x10    /* Square Wave Enable */
+#define RTC_CTL_BIT_OUT             0x80    /* Output Control */
+
+/* DS1337-specific bits */
+#define DS1337_CTL_BIT_A1IE         0x01
+#define DS1337_CTL_BIT_A2IE         0x02
+#define DS1337_CTL_BIT_RS1          0x08    /* Rate select 1 */
+#define DS1337_CTL_BIT_RS2          0x10    /* Rate select 2 */
+#define DS1337_CTL_BIT_EOSC         0x80    /* Enable Oscillator */
+
+/* DS1339-specific bits */
+#define DS1339_ALARM1_REG_SECS      0x07
+
+/* DS1340-specific bits */
+#define DS1340_SEC_BIT_EOSC         0x80    /* Enable Oscillator */
+#define DS1340_CTL_BIT_OUT          0x80    /* Output Control */
+
+/* MCP7941X-specific bits */
+#define MCP794XX_ALARM0_REG_ADDR    0x0d
+#define MCP794XX_BIT_ALMX_IF        RT_BIT(3)
+#define MCP794XX_BIT_ALMX_C0        RT_BIT(4)
+#define MCP794XX_BIT_ALMX_C1        RT_BIT(5)
+#define MCP794XX_BIT_ALMX_C2        RT_BIT(6)
+#define MCP794XX_BIT_ALMX_POL       RT_BIT(7)
+#define MCP794XX_MSK_ALMX_MATCH     (MCP794XX_BIT_ALMX_C0 | MCP794XX_BIT_ALMX_C1 | MCP794XX_BIT_ALMX_C2)
+#define MCP794XX_BIT_ALM0_EN        0x10
+#define MCP7941X_BIT_ST             0x80
+#define MCP7941X_BIT_VBATEN         0x08
+
+enum ds_type
+{
+    ds1307,
+    ds1337,
+    ds1338,
+    ds1339,
+    ds1340,
+    m41t11,
+    mcp794xx,
+};
+
+struct ds1307_rtc
+{
+    struct rt_device parent;
+
+    int irq;
+    enum ds_type type;
+    struct rt_i2c_client *client;
+    struct rt_thread *irq_thread;
+
+    struct rt_rtc_wkalarm wkalarm;
+};
+
+#define raw_to_ds1307_rtc(raw) rt_container_of(raw, struct ds1307_rtc, parent)
+
+static rt_int32_t i2c_read_byte(struct rt_i2c_client *client,
+        rt_uint8_t command)
+{
+    rt_int32_t res;
+    rt_uint8_t ret = 0;
+    struct rt_i2c_msg msg[2];
+
+    msg[0].buf = &command;
+    msg[0].addr = client->client_addr;
+    msg[0].len = 1;
+    msg[0].flags = RT_I2C_WR;
+
+    msg[1].buf = &ret;
+    msg[1].addr = client->client_addr;
+    msg[1].len = 1;
+    msg[1].flags = RT_I2C_RD;
+
+    res = rt_i2c_transfer(client->bus, msg, 2);
+
+    return res == 2 ? ret : res;
+}
+
+static rt_int32_t i2c_write_byte(struct rt_i2c_client *client,
+        rt_uint8_t command, rt_uint8_t value)
+{
+    rt_int32_t res;
+    struct rt_i2c_msg msg[1];
+    rt_uint8_t data[2] = { command, value };
+
+    msg[0].buf = data;
+    msg[0].addr = client->client_addr;
+    msg[0].len = 2;
+    msg[0].flags = RT_I2C_WR;
+
+    res = rt_i2c_transfer(client->bus, msg, 1);
+
+    return res == 1 ? 0 : res;
+}
+
+static rt_int32_t i2c_update_byte_bits(struct rt_i2c_client *client,
+        rt_uint8_t command, rt_uint8_t mask, rt_uint8_t value)
+{
+    rt_int32_t res = i2c_read_byte(client, command);
+
+    if (res < 0)
+    {
+        return res;
+    }
+
+    res &= ~mask;
+    res |= value;
+
+    return i2c_write_byte(client, command, res);
+}
+
+/* Returns the number of read bytes */
+static rt_int32_t i2c_read_block(struct rt_i2c_client *client,
+        rt_uint8_t command, rt_uint8_t length, rt_uint8_t *values)
+{
+    struct rt_i2c_msg msg[2];
+
+    msg[0].buf = &command;
+    msg[0].addr = client->client_addr;
+    msg[0].len = 1;
+    msg[0].flags = RT_I2C_WR;
+
+    msg[1].buf = values;
+    msg[1].addr = client->client_addr;
+    msg[1].len = length;
+    msg[1].flags = RT_I2C_RD;
+
+    return rt_i2c_transfer(client->bus, msg, 2);
+}
+
+static rt_int32_t i2c_write_block(struct rt_i2c_client *client,
+        rt_uint8_t command, rt_uint8_t length, const rt_uint8_t *values)
+{
+    rt_uint8_t data[32];
+    struct rt_i2c_msg msg[1];
+
+    length = rt_min_t(rt_uint8_t, length, RT_ARRAY_SIZE(data) - 1);
+
+    data[0] = command;
+    rt_memcpy(&data[1], values, length);
+
+    msg[0].buf = data;
+    msg[0].addr = client->client_addr;
+    msg[0].len = length + 1;
+    msg[0].flags = RT_I2C_WR;
+
+    return rt_i2c_transfer(client->bus, msg, 1);
+}
+
+static void ds1307_rtc_read_time(struct ds1307_rtc *ds1307, time_t *sec)
+{
+    rt_uint8_t data[7];
+    struct tm tm;
+    struct rt_i2c_client *client = ds1307->client;
+
+    if (i2c_read_block(client, 0, sizeof(data), data) > 0)
+    {
+        int reg = -1;
+
+        if (ds1307->type == ds1337)
+        {
+            reg = DS1337_STAT_REG_ADDR;
+        }
+        if (ds1307->type == ds1340)
+        {
+            reg = DS1340_STAT_REG_ADDR;
+        }
+
+        if (reg >= 0)
+        {
+            rt_int32_t status = i2c_read_byte(client, reg);
+
+            if (status >= 0 && (status & RTC_STAT_BIT_OSF))
+            {
+                status &= ~RTC_STAT_BIT_OSF;
+
+                i2c_write_byte(client, reg, status);
+            }
+        }
+
+        tm.tm_sec  = rt_bcd2bin(data[RTC_SEC_REG_ADDR] & 0x7f);
+        tm.tm_min  = rt_bcd2bin(data[RTC_MIN_REG_ADDR] & 0x7f);
+        tm.tm_hour = rt_bcd2bin(data[RTC_HR_REG_ADDR] & 0x3f);
+        tm.tm_mday = rt_bcd2bin(data[RTC_DATE_REG_ADDR] & 0x3f);
+        tm.tm_mon  = rt_bcd2bin(data[RTC_MON_REG_ADDR] & 0x1f) - 1;
+        tm.tm_year = rt_bcd2bin(data[RTC_YR_REG_ADDR]) + 100;
+        tm.tm_wday = rt_bcd2bin(data[RTC_DAY_REG_ADDR] & 0x07) - 1;
+        tm.tm_yday = 0;
+        tm.tm_isdst = 0;
+
+        *sec = timegm(&tm);
+    }
+}
+
+static void ds1307_rtc_set_time(struct ds1307_rtc *ds1307, time_t *sec)
+{
+    rt_uint8_t buf[7];
+    struct tm *tm;
+    struct rt_i2c_client *client = ds1307->client;
+
+    tm = localtime(sec);
+
+    buf[RTC_YR_REG_ADDR] = rt_bin2bcd(tm->tm_year - 100);
+    buf[RTC_MON_REG_ADDR] = rt_bin2bcd(tm->tm_mon + 1);
+    buf[RTC_DAY_REG_ADDR] = rt_bin2bcd(tm->tm_wday + 1);
+    buf[RTC_DATE_REG_ADDR] = rt_bin2bcd(tm->tm_mday);
+    buf[RTC_HR_REG_ADDR] = rt_bin2bcd(tm->tm_hour);
+    buf[RTC_MIN_REG_ADDR] = rt_bin2bcd(tm->tm_min);
+    buf[RTC_SEC_REG_ADDR] = rt_bin2bcd(tm->tm_sec);
+
+    if (ds1307->type == mcp794xx)
+    {
+        buf[RTC_DAY_REG_ADDR] |= MCP7941X_BIT_VBATEN;
+        buf[RTC_SEC_REG_ADDR] |= MCP7941X_BIT_ST;
+    }
+
+    if (i2c_write_block(client, 0, sizeof(buf), buf) >= 0)
+    {
+        if (ds1307->type == ds1337)
+        {
+            i2c_write_byte(client, DS1337_CTL_REG_ADDR, 0);
+        }
+    }
+}
+
+static int ds1307_rtc_alarm_irq_enable(struct ds1307_rtc *ds1307, rt_bool_t enabled)
+{
+    struct rt_i2c_client *client = ds1307->client;
+
+    if (ds1307->type == mcp794xx)
+    {
+        return i2c_update_byte_bits(client, RTC_CTL_REG_ADDR,
+                MCP794XX_BIT_ALM0_EN, enabled ? MCP794XX_BIT_ALM0_EN : 0);
+    }
+
+    return i2c_update_byte_bits(client, DS1337_CTL_REG_ADDR,
+            DS1337_CTL_BIT_A1IE, enabled ? DS1337_CTL_BIT_A1IE : 0);
+}
+
+static int ds1307_rtc_read_alarm(struct ds1307_rtc *ds1307,
+        struct rt_rtc_wkalarm *alarm)
+{
+    int res;
+    struct rt_i2c_client *client = ds1307->client;
+
+    if (ds1307->type == mcp794xx)
+    {
+        rt_uint8_t regs[10];
+
+        res = i2c_read_block(client, RTC_CTL_REG_ADDR, sizeof(regs), regs);
+
+        if (res < 0)
+        {
+            return res;
+        }
+
+        alarm->enable = !!(regs[0] & MCP794XX_BIT_ALM0_EN);
+
+        /* Report alarm 0 time assuming 24-hour and day-of-month modes. */
+        alarm->tm_sec = rt_bcd2bin(regs[3] & 0x7f);
+        alarm->tm_min = rt_bcd2bin(regs[4] & 0x7f);
+        alarm->tm_hour = rt_bcd2bin(regs[5] & 0x3f);
+        /* alarm->tm_wday = rt_bcd2bin(regs[6] & 0x7) - 1; */
+        alarm->tm_mday = rt_bcd2bin(regs[7] & 0x3f);
+        alarm->tm_mon = rt_bcd2bin(regs[8] & 0x1f) - 1;
+        alarm->tm_year = -1;
+    }
+    else
+    {
+        rt_uint8_t regs[9];
+
+        res = i2c_read_block(client, DS1339_ALARM1_REG_SECS, sizeof(regs), regs);
+
+        if (res < 0)
+        {
+            return res;
+        }
+
+        alarm->tm_sec = rt_bcd2bin(regs[0] & 0x7f);
+        alarm->tm_min = rt_bcd2bin(regs[1] & 0x7f);
+        alarm->tm_hour = rt_bcd2bin(regs[2] & 0x3f);
+        alarm->tm_mday = rt_bcd2bin(regs[3] & 0x3f);
+        alarm->enable = !!(regs[7] & DS1337_CTL_BIT_A1IE);
+    }
+
+    return RT_EOK;
+}
+
+static int ds1307_rtc_set_alarm(struct ds1307_rtc *ds1307,
+        struct rt_rtc_wkalarm *alarm)
+{
+    int res;
+    struct rt_i2c_client *client = ds1307->client;
+    struct rt_rtc_wkalarm *wkalarm = &ds1307->wkalarm;
+
+    if (ds1307->type == mcp794xx)
+    {
+        rt_uint8_t regs[7];
+
+        res = i2c_read_block(client, RTC_CTL_REG_ADDR, sizeof(regs), regs);
+
+        if (res < 0)
+        {
+            return res;
+        }
+
+        /* Set alarm 0, using 24-hour and day-of-month modes. */
+        regs[3] = rt_bin2bcd(alarm->tm_sec);
+        regs[4] = rt_bin2bcd(alarm->tm_min);
+        regs[5] = rt_bin2bcd(alarm->tm_hour);
+        regs[6] = MCP7941X_BIT_VBATEN;
+        regs[7] = rt_bin2bcd(alarm->tm_mday);
+        regs[8] = rt_bin2bcd(alarm->tm_mon + 1);
+
+        /* Clear the alarm 0 interrupt flag. */
+        regs[6] &= ~MCP794XX_BIT_ALMX_IF;
+        /* Set alarm match: second, minute, hour, day, date, month. */
+        regs[6] |= MCP794XX_MSK_ALMX_MATCH;
+        /* Disable interrupt. We will not enable until completely programmed */
+        regs[0] &= ~MCP794XX_BIT_ALM0_EN;
+
+        res = i2c_write_block(client, RTC_CTL_REG_ADDR, sizeof(regs), regs);
+
+        if (res < 0)
+        {
+            return res;
+        }
+    }
+    else
+    {
+        rt_uint8_t regs[9], control, status;
+
+        res = i2c_read_block(client, DS1339_ALARM1_REG_SECS, sizeof(regs), regs);
+
+        if (res < 0)
+        {
+            return res;
+        }
+
+        control = regs[7];
+        status = regs[8];
+
+        /* Set ALARM1, using 24 hour and day-of-month modes */
+        regs[0] = rt_bin2bcd(alarm->tm_sec);
+        regs[1] = rt_bin2bcd(alarm->tm_min);
+        regs[2] = rt_bin2bcd(alarm->tm_hour);
+        regs[3] = rt_bin2bcd(alarm->tm_mday);
+
+        /* Set ALARM2 to non-garbage */
+        regs[4] = 0;
+        regs[5] = 0;
+        regs[6] = 0;
+
+        /* Disable alarms */
+        regs[7] = control & ~(DS1337_CTL_BIT_A1IE | DS1337_CTL_BIT_A2IE);
+        regs[8] = status & ~(RTC_STAT_BIT_A1I | RTC_STAT_BIT_A2I);
+
+        res = i2c_write_block(client, DS1339_ALARM1_REG_SECS, sizeof(regs), regs);
+
+        if (res < 0)
+        {
+            return res;
+        }
+    }
+
+    res = ds1307_rtc_alarm_irq_enable(ds1307, wkalarm->enable);
+
+    if (!(res < 0))
+    {
+        wkalarm->enable = alarm->enable;
+        wkalarm->tm_hour = alarm->tm_hour;
+        wkalarm->tm_min = alarm->tm_min;
+        wkalarm->tm_sec = alarm->tm_sec;
+    }
+
+    return res;
+}
+
+static rt_err_t ds1307_rtc_control(rt_device_t dev, int cmd, void *args)
+{
+    rt_err_t err = RT_EOK;
+    struct ds1307_rtc *ds1307 = raw_to_ds1307_rtc(dev);
+
+    if (!args)
+    {
+        return -RT_EINVAL;
+    }
+
+    switch (cmd)
+    {
+    case RT_DEVICE_CTRL_RTC_GET_TIME:
+        ds1307_rtc_read_time(ds1307, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_TIME:
+        ds1307_rtc_set_time(ds1307, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_GET_TIMEVAL:
+        ds1307_rtc_read_time(ds1307, (time_t *)&((struct timeval *)args)->tv_sec);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_TIMEVAL:
+        ds1307_rtc_set_time(ds1307, (time_t *)&((struct timeval *)args)->tv_sec);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_GET_ALARM:
+        err = ds1307_rtc_read_alarm(ds1307, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_ALARM:
+        err = ds1307_rtc_set_alarm(ds1307, args);
+        break;
+
+    default:
+        err = -RT_EINVAL;
+        break;
+    }
+
+    return err;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops ds1307_rtc_ops =
+{
+    .control = ds1307_rtc_control,
+};
+#endif
+
+static void ds1307_rtc_thread_isr(void *param)
+{
+    struct ds1307_rtc *ds1307 = param;
+    struct rt_i2c_client *client = ds1307->client;
+
+    while (RT_TRUE)
+    {
+        rt_thread_suspend(ds1307->irq_thread);
+        rt_schedule();
+
+        if (ds1307->type == mcp794xx)
+        {
+            rt_int32_t reg = i2c_read_byte(client, MCP794XX_ALARM0_REG_ADDR);
+
+            if (reg < 0 || !(reg & MCP794XX_BIT_ALMX_IF))
+            {
+                continue;
+            }
+
+            reg &= ~MCP794XX_BIT_ALMX_IF;
+
+            if (i2c_write_byte(client, MCP794XX_ALARM0_REG_ADDR, reg) < 0)
+            {
+                continue;
+            }
+
+            if (i2c_update_byte_bits(client, RTC_CTL_REG_ADDR, MCP794XX_BIT_ALM0_EN, 0) < 0)
+            {
+                continue;
+            }
+        }
+        else
+        {
+            rt_int32_t reg = i2c_read_byte(client, DS1337_STAT_REG_ADDR);
+
+            if (reg < 0 || !(reg & RTC_STAT_BIT_A1I))
+            {
+                continue;
+            }
+
+            reg &= ~RTC_STAT_BIT_A1I;
+            i2c_write_byte(client, DS1337_STAT_REG_ADDR, reg);
+
+            if (i2c_update_byte_bits(client, DS1337_CTL_REG_ADDR, DS1337_CTL_BIT_A1IE, 0) < 0)
+            {
+                continue;
+            }
+        }
+
+        rt_alarm_update(&ds1307->parent, 1);
+    }
+}
+
+static void ds1307_rtc_isr(int irqno, void *param)
+{
+    struct ds1307_rtc *ds1307 = param;
+
+    rt_thread_resume(ds1307->irq_thread);
+}
+
+static rt_err_t ds1307_rtc_probe(struct rt_i2c_client *client)
+{
+    rt_err_t err;
+    const char *dev_name;
+    struct rt_device *dev = &client->parent;
+    struct ds1307_rtc *ds1307 = rt_calloc(1, sizeof(*ds1307));
+
+    if (!ds1307)
+    {
+        return -RT_ENOMEM;
+    }
+
+    ds1307->type = (rt_ubase_t)rt_i2c_client_id_data(client);
+    ds1307->irq = rt_dm_dev_get_irq(dev, 0);
+    ds1307->client = client;
+
+    if (ds1307->irq >= 0)
+    {
+        ds1307->irq_thread = rt_thread_create("rtc-ds1307", &ds1307_rtc_thread_isr,
+                ds1307, DM_THREAD_STACK_SIZE, RT_THREAD_PRIORITY_MAX / 2, 10);
+
+        if (!ds1307->irq_thread)
+        {
+            err = -RT_ERROR;
+            LOG_E("Create RTC IRQ thread fail");
+            goto _fail;
+        }
+
+        rt_thread_startup(ds1307->irq_thread);
+
+        rt_hw_interrupt_install(ds1307->irq, ds1307_rtc_isr, ds1307, "rtc-ds1307");
+        rt_hw_interrupt_umask(ds1307->irq);
+    }
+
+    dev->user_data = ds1307;
+
+    ds1307->parent.type = RT_Device_Class_RTC;
+#ifdef RT_USING_DEVICE_OPS
+    ds1307->parent.ops = &ds1307_rtc_ops;
+#else
+    ds1307->parent.control = ds1307_rtc_control;
+#endif
+
+    rtc_dev_set_name(&ds1307->parent);
+    dev_name = rt_dm_dev_get_name(&ds1307->parent);
+    rt_device_register(&ds1307->parent, dev_name, RT_DEVICE_FLAG_RDWR);
+
+    return RT_EOK;
+
+_fail:
+    if (ds1307->irq_thread)
+    {
+        rt_thread_delete(ds1307->irq_thread);
+    }
+    rt_free(ds1307);
+
+    return err;
+}
+
+static rt_err_t ds1307_rtc_remove(struct rt_i2c_client *client)
+{
+    struct ds1307_rtc *ds1307 = client->parent.user_data;
+
+    if (ds1307->irq >= 0)
+    {
+        if (ds1307->wkalarm.enable)
+        {
+            ds1307_rtc_alarm_irq_enable(ds1307, RT_FALSE);
+        }
+
+        rt_hw_interrupt_mask(ds1307->irq);
+        rt_pic_detach_irq(ds1307->irq, ds1307);
+
+        rt_thread_delete(ds1307->irq_thread);
+    }
+
+    rt_device_unregister(&ds1307->parent);
+
+    rt_free(ds1307);
+
+    return RT_EOK;
+}
+
+static const struct rt_i2c_device_id ds1307_rtc_ids[] =
+{
+    { .name = "ds1307", .data = (void *)ds1307 },
+    { .name = "ds1337", .data = (void *)ds1337 },
+    { .name = "ds1338", .data = (void *)ds1338 },
+    { .name = "ds1339", .data = (void *)ds1339 },
+    { .name = "ds1340", .data = (void *)ds1340 },
+    { .name = "m41t11", .data = (void *)m41t11 },
+    { .name = "mcp7940x", .data = (void *)mcp794xx },
+    { .name = "mcp7941x", .data = (void *)mcp794xx },
+    { /* sentinel */ },
+};
+
+static const struct rt_ofw_node_id ds1307_rtc_ofw_ids[] =
+{
+    { .compatible = "dallas,ds1307", .data = (void *)ds1307 },
+    { .compatible = "dallas,ds1337", .data = (void *)ds1337 },
+    { .compatible = "dallas,ds1338", .data = (void *)ds1338 },
+    { .compatible = "dallas,ds1339", .data = (void *)ds1339 },
+    { .compatible = "dallas,ds1340", .data = (void *)ds1340 },
+    { .compatible = "microchip,mcp7940x", .data = (void *)mcp794xx },
+    { .compatible = "microchip,mcp7941x", .data = (void *)mcp794xx },
+    { .compatible = "st,m41t11", .data = (void *)m41t11 },
+    { /* sentinel */ },
+};
+
+static struct rt_i2c_driver ds1307_rtc_driver =
+{
+    .ids = ds1307_rtc_ids,
+    .ofw_ids = ds1307_rtc_ofw_ids,
+
+    .probe = ds1307_rtc_probe,
+    .remove = ds1307_rtc_remove,
+};
+RT_I2C_DRIVER_EXPORT(ds1307_rtc_driver);

+ 270 - 0
components/drivers/rtc/rtc-goldfish.c

@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2022-11-26     GuEe-GUI     first version
+ */
+
+#include "rtc_dm.h"
+
+#define GOLDFISH_RTC_TIME_LOW           0x00 /* get low bits of current time and update GOLDFISH_RTC_TIME_HIGH */
+#define GOLDFISH_RTC_TIME_HIGH          0x04 /* get high bits of time at last GOLDFISH_RTC_TIME_LOW read */
+#define GOLDFISH_RTC_ALARM_LOW          0x08 /* set low bits of alarm and activate it */
+#define GOLDFISH_RTC_ALARM_HIGH         0x0c /* set high bits of next alarm */
+#define GOLDFISH_RTC_IRQ_ENABLED        0x10 /* enable alarm interrupt */
+#define GOLDFISH_RTC_CLEAR_ALARM        0x14 /* disarm an existing alarm */
+#define GOLDFISH_RTC_ALARM_STATUS       0x18 /* alarm status (running or not) */
+#define GOLDFISH_RTC_CLEAR_INTERRUPT    0x1c /* clear interrupt */
+
+#define NSEC_PER_SEC    1000000000L
+
+struct goldfish_rtc
+{
+    struct rt_device parent;
+
+    int irq;
+    void *base;
+
+    struct rt_rtc_wkalarm wkalarm;
+};
+
+#define raw_to_goldfish_rtc(raw) rt_container_of(raw, struct goldfish_rtc, parent)
+
+rt_inline rt_uint32_t goldfish_rtc_read(struct goldfish_rtc *grtc, int offset)
+{
+    return HWREG32(grtc->base + offset);
+}
+
+rt_inline void goldfish_rtc_write(struct goldfish_rtc *grtc, int offset, rt_uint32_t value)
+{
+    HWREG32(grtc->base + offset) = value;
+}
+
+static void goldfish_rtc_isr(int irqno, void *param)
+{
+    struct goldfish_rtc *grtc = param;
+
+    goldfish_rtc_write(grtc, GOLDFISH_RTC_CLEAR_INTERRUPT, 1);
+
+    rt_alarm_update(&grtc->parent, 1);
+}
+
+static void goldfish_rtc_get_secs(struct goldfish_rtc *grtc, time_t *sec)
+{
+    rt_uint64_t time = goldfish_rtc_read(grtc, GOLDFISH_RTC_TIME_LOW);
+
+    if (sizeof(*sec) >= sizeof(rt_uint64_t))
+    {
+        rt_uint64_t time_high = goldfish_rtc_read(grtc, GOLDFISH_RTC_TIME_HIGH);
+
+        time |= time_high << 32;
+    }
+
+    rt_do_div(time, NSEC_PER_SEC);
+
+    rt_memcpy(sec, &time, sizeof(*sec));
+}
+
+static void goldfish_rtc_set_secs(struct goldfish_rtc *grtc, time_t *sec)
+{
+    rt_uint64_t time = 0;
+
+    rt_memcpy(&time, sec, sizeof(*sec));
+
+    time *= NSEC_PER_SEC;
+
+    goldfish_rtc_write(grtc, GOLDFISH_RTC_TIME_LOW, (rt_uint32_t)(time & RT_UINT32_MAX));
+
+    if (sizeof(*sec) >= sizeof(rt_uint64_t))
+    {
+        goldfish_rtc_write(grtc, GOLDFISH_RTC_TIME_HIGH, (rt_uint32_t)(time >> 32));
+    }
+}
+
+static void goldfish_rtc_get_alarm(struct goldfish_rtc *grtc, struct rt_rtc_wkalarm *alarm)
+{
+    *alarm = grtc->wkalarm;
+
+    if (goldfish_rtc_read(grtc, GOLDFISH_RTC_ALARM_STATUS))
+    {
+        alarm->enable = RT_TRUE;
+    }
+    else
+    {
+        alarm->enable = RT_FALSE;
+    }
+}
+
+static void goldfish_rtc_set_alarm(struct goldfish_rtc *grtc, struct rt_rtc_wkalarm *alarm)
+{
+    struct rt_rtc_wkalarm *wkalarm = &grtc->wkalarm;
+
+    wkalarm->enable = alarm->enable;
+    wkalarm->tm_hour = alarm->tm_hour;
+    wkalarm->tm_min = alarm->tm_min;
+    wkalarm->tm_sec = alarm->tm_sec;
+
+    if (alarm->enable)
+    {
+        rt_uint64_t time = alarm->tm_hour * 3600 + alarm->tm_min * 60 + alarm->tm_sec;
+
+        time *= NSEC_PER_SEC;
+
+        goldfish_rtc_write(grtc, GOLDFISH_RTC_ALARM_HIGH, (rt_uint32_t)(time >> 32));
+        goldfish_rtc_write(grtc, GOLDFISH_RTC_ALARM_LOW, (rt_uint32_t)(time & RT_UINT32_MAX));
+
+        goldfish_rtc_write(grtc, GOLDFISH_RTC_IRQ_ENABLED, 1);
+    }
+    else
+    {
+        if (goldfish_rtc_read(grtc, GOLDFISH_RTC_ALARM_STATUS))
+        {
+            goldfish_rtc_write(grtc, GOLDFISH_RTC_CLEAR_ALARM, 1);
+        }
+
+        goldfish_rtc_write(grtc, GOLDFISH_RTC_IRQ_ENABLED, 0);
+    }
+}
+
+static rt_err_t goldfish_rtc_control(rt_device_t dev, int cmd, void *args)
+{
+    rt_err_t err = RT_EOK;
+    struct goldfish_rtc *grtc = raw_to_goldfish_rtc(dev);
+
+    if (!args)
+    {
+        return -RT_EINVAL;
+    }
+
+    switch (cmd)
+    {
+    case RT_DEVICE_CTRL_RTC_GET_TIME:
+        goldfish_rtc_get_secs(grtc, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_TIME:
+        goldfish_rtc_set_secs(grtc, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_GET_TIMEVAL:
+        goldfish_rtc_get_secs(grtc, (time_t *)&((struct timeval *)args)->tv_sec);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_TIMEVAL:
+        goldfish_rtc_set_secs(grtc, (time_t *)&((struct timeval *)args)->tv_sec);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_GET_ALARM:
+        goldfish_rtc_get_alarm(grtc, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_ALARM:
+        goldfish_rtc_set_alarm(grtc, args);
+        break;
+
+    default:
+        err = -RT_EINVAL;
+        break;
+    }
+
+    return err;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops goldfish_rtc_ops =
+{
+    .control = goldfish_rtc_control,
+};
+#endif
+
+static rt_err_t goldfish_rtc_probe(struct rt_platform_device *pdev)
+{
+    rt_err_t err = RT_EOK;
+    const char *dev_name;
+    struct rt_device *dev = &pdev->parent;
+    struct goldfish_rtc *grtc = rt_calloc(1, sizeof(*grtc));
+
+    if (!grtc)
+    {
+        return -RT_ENOMEM;
+    }
+
+    grtc->base = rt_dm_dev_iomap(dev, 0);
+
+    if (!grtc->base)
+    {
+        err = -RT_EIO;
+        goto _fail;
+    }
+
+    grtc->irq = rt_dm_dev_get_irq(dev, 0);
+
+    if (grtc->irq < 0)
+    {
+        err = grtc->irq;
+        goto _fail;
+    }
+
+    dev->user_data = grtc;
+
+    grtc->parent.type = RT_Device_Class_RTC;
+#ifdef RT_USING_DEVICE_OPS
+    grtc->parent.ops = &goldfish_rtc_ops;
+#else
+    grtc->parent.control = goldfish_rtc_control;
+#endif
+
+    rtc_dev_set_name(&grtc->parent);
+    dev_name = rt_dm_dev_get_name(&grtc->parent);
+    rt_device_register(&grtc->parent, dev_name, RT_DEVICE_FLAG_RDWR);
+
+    rt_hw_interrupt_install(grtc->irq, goldfish_rtc_isr, grtc, "rtc-goldfish");
+    rt_hw_interrupt_umask(grtc->irq);
+
+    return RT_EOK;
+
+_fail:
+    if (grtc->base)
+    {
+        rt_iounmap(grtc->base);
+    }
+
+    rt_free(grtc);
+
+    return err;
+}
+
+static rt_err_t goldfish_rtc_remove(struct rt_platform_device *pdev)
+{
+    struct goldfish_rtc *grtc = pdev->parent.user_data;
+
+    rt_hw_interrupt_mask(grtc->irq);
+    rt_pic_detach_irq(grtc->irq, grtc);
+
+    rt_device_unregister(&grtc->parent);
+
+    rt_iounmap(grtc->base);
+
+    rt_free(grtc);
+
+    return RT_EOK;
+}
+
+static const struct rt_ofw_node_id goldfish_rtc_ofw_ids[] =
+{
+    { .compatible = "google,goldfish-rtc" },
+    { /* sentinel */ }
+};
+
+static struct rt_platform_driver goldfish_rtc_driver =
+{
+    .name = "rtc-goldfish",
+    .ids = goldfish_rtc_ofw_ids,
+
+    .probe = goldfish_rtc_probe,
+    .remove = goldfish_rtc_remove,
+};
+RT_PLATFORM_DRIVER_EXPORT(goldfish_rtc_driver);

+ 767 - 0
components/drivers/rtc/rtc-hym8563.c

@@ -0,0 +1,767 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2022-12-06     GuEe-GUI     first version
+ */
+
+#include "rtc_dm.h"
+
+#define DBG_TAG "rtc.hym8563"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#define HYM8563_CTL1            0x00
+#define HYM8563_CTL1_TEST       RT_BIT(7)
+#define HYM8563_CTL1_STOP       RT_BIT(5)
+#define HYM8563_CTL1_TESTC      RT_BIT(3)
+
+#define HYM8563_CTL2            0x01
+#define HYM8563_CTL2_TI_TP      RT_BIT(4)
+#define HYM8563_CTL2_AF         RT_BIT(3)
+#define HYM8563_CTL2_TF         RT_BIT(2)
+#define HYM8563_CTL2_AIE        RT_BIT(1)
+#define HYM8563_CTL2_TIE        RT_BIT(0)
+
+#define HYM8563_SEC             0x02
+#define HYM8563_SEC_VL          RT_BIT(7)
+#define HYM8563_SEC_MASK        0x7f
+
+#define HYM8563_MIN             0x03
+#define HYM8563_MIN_MASK        0x7f
+
+#define HYM8563_HOUR            0x04
+#define HYM8563_HOUR_MASK       0x3f
+
+#define HYM8563_DAY             0x05
+#define HYM8563_DAY_MASK        0x3f
+
+#define HYM8563_WEEKDAY         0x06
+#define HYM8563_WEEKDAY_MASK    0x07
+
+#define HYM8563_MONTH           0x07
+#define HYM8563_MONTH_CENTURY   RT_BIT(7)
+#define HYM8563_MONTH_MASK      0x1f
+
+#define HYM8563_YEAR            0x08
+
+#define HYM8563_ALM_MIN         0x09
+#define HYM8563_ALM_HOUR        0x0a
+#define HYM8563_ALM_DAY         0x0b
+#define HYM8563_ALM_WEEK        0x0c
+
+/* Each alarm check can be disabled by setting this bit in the register */
+#define HYM8563_ALM_BIT_DISABLE RT_BIT(7)
+
+#define HYM8563_CLKOUT          0x0d
+#define HYM8563_CLKOUT_ENABLE   RT_BIT(7)
+#define HYM8563_CLKOUT_32768    0
+#define HYM8563_CLKOUT_1024     1
+#define HYM8563_CLKOUT_32       2
+#define HYM8563_CLKOUT_1        3
+#define HYM8563_CLKOUT_MASK     3
+
+#define HYM8563_TMR_CTL         0x0e
+#define HYM8563_TMR_CTL_ENABLE  RT_BIT(7)
+#define HYM8563_TMR_CTL_4096    0
+#define HYM8563_TMR_CTL_64      1
+#define HYM8563_TMR_CTL_1       2
+#define HYM8563_TMR_CTL_1_60    3
+#define HYM8563_TMR_CTL_MASK    3
+
+#define HYM8563_TMR_CNT         0x0f
+
+struct hym8563_rtc
+{
+    struct rt_device parent;
+    struct rt_clk_node clkout_hw;
+
+    struct rt_clk_cell cell;
+    struct rt_clk_cell *cells[1];
+
+    int irq;
+    struct rt_i2c_client *client;
+    struct rt_thread *irq_thread;
+
+    struct rt_rtc_wkalarm wkalarm;
+};
+
+#define raw_to_hym8563_rtc(raw) rt_container_of(raw, struct hym8563_rtc, parent)
+#define raw_to_hym8563_clkout(raw) rt_container_of(raw, struct hym8563_rtc, clkout_hw)
+
+static rt_int32_t i2c_smbus_read_byte_data(struct rt_i2c_client *client,
+        rt_uint8_t command)
+{
+    rt_int32_t res;
+    rt_uint8_t ret = 0;
+    struct rt_i2c_msg msg[2];
+
+    msg[0].buf = &command;
+    msg[0].addr = client->client_addr;
+    msg[0].len = 1;
+    msg[0].flags = RT_I2C_WR;
+
+    msg[1].buf = &ret;
+    msg[1].addr = client->client_addr;
+    msg[1].len = 1;
+    msg[1].flags = RT_I2C_RD;
+
+    res = rt_i2c_transfer(client->bus, msg, 2);
+
+    return res == 2 ? ret : res;
+}
+
+static rt_int32_t i2c_smbus_write_byte_data(struct rt_i2c_client *client,
+        rt_uint8_t command, rt_uint8_t value)
+{
+    rt_int32_t res;
+    struct rt_i2c_msg msg[1];
+    rt_uint8_t data[2] = { command, value };
+
+    msg[0].buf = data;
+    msg[0].addr = client->client_addr;
+    msg[0].len = 2;
+    msg[0].flags = RT_I2C_WR;
+
+    res = rt_i2c_transfer(client->bus, msg, 1);
+
+    return res == 1 ? 0 : res;
+}
+
+/* Returns the number of read bytes */
+static rt_int32_t i2c_smbus_read_i2c_block_data(struct rt_i2c_client *client,
+        rt_uint8_t command, rt_uint8_t length, rt_uint8_t *values)
+{
+    struct rt_i2c_msg msg[2];
+
+    msg[0].buf = &command;
+    msg[0].addr = client->client_addr;
+    msg[0].len = 1;
+    msg[0].flags = RT_I2C_WR;
+
+    msg[1].buf = values;
+    msg[1].addr = client->client_addr;
+    msg[1].len = length;
+    msg[1].flags = RT_I2C_RD;
+
+    return rt_i2c_transfer(client->bus, msg, 2);
+}
+
+static rt_int32_t i2c_smbus_write_i2c_block_data(struct rt_i2c_client *client,
+        rt_uint8_t command, rt_uint8_t length, const rt_uint8_t *values)
+{
+    rt_uint8_t data[32];
+    struct rt_i2c_msg msg[1];
+
+    length = rt_min_t(rt_uint8_t, length, RT_ARRAY_SIZE(data) - 1);
+
+    data[0] = command;
+    rt_memcpy(&data[1], values, length);
+
+    msg[0].buf = data;
+    msg[0].addr = client->client_addr;
+    msg[0].len = length + 1;
+    msg[0].flags = RT_I2C_WR;
+
+    return rt_i2c_transfer(client->bus, msg, 1);
+}
+
+static void hym8563_rtc_read_time(struct hym8563_rtc *hym8563, time_t *sec)
+{
+    struct tm tm;
+    rt_uint8_t buf[7];
+
+    if (i2c_smbus_read_i2c_block_data(hym8563->client, HYM8563_SEC, 7, buf) < 0)
+    {
+        return;
+    }
+
+    if (buf[0] & HYM8563_SEC_VL)
+    {
+        LOG_D("no valid clock/calendar values available");
+    }
+
+    tm.tm_sec = rt_bcd2bin(buf[0] & HYM8563_SEC_MASK);
+    tm.tm_min = rt_bcd2bin(buf[1] & HYM8563_MIN_MASK);
+    tm.tm_hour = rt_bcd2bin(buf[2] & HYM8563_HOUR_MASK);
+    tm.tm_mday = rt_bcd2bin(buf[3] & HYM8563_DAY_MASK);
+    tm.tm_wday = rt_bcd2bin(buf[4] & HYM8563_WEEKDAY_MASK); /* 0 = Sun */
+    tm.tm_mon = rt_bcd2bin(buf[5] & HYM8563_MONTH_MASK) - 1; /* 0 = Jan */
+    tm.tm_year = rt_bcd2bin(buf[6]) + 100;
+
+    *sec = timegm(&tm);
+}
+
+static void hym8563_rtc_set_time(struct hym8563_rtc *hym8563, time_t *sec)
+{
+    struct tm *tm;
+    rt_uint8_t buf[7];
+    struct rt_i2c_client *client = hym8563->client;
+
+    tm = localtime(sec);
+
+    /* Years >= 2100 are to far in the future, 19XX is to early */
+    if (tm->tm_year < 100 || tm->tm_year >= 200)
+    {
+        return;
+    }
+
+    buf[0] = rt_bin2bcd(tm->tm_sec);
+    buf[1] = rt_bin2bcd(tm->tm_min);
+    buf[2] = rt_bin2bcd(tm->tm_hour);
+    buf[3] = rt_bin2bcd(tm->tm_mday);
+    buf[4] = rt_bin2bcd(tm->tm_wday);
+    buf[5] = rt_bin2bcd(tm->tm_mon + 1);
+
+    /*
+     * While the HYM8563 has a century flag in the month register,
+     * it does not seem to carry it over a subsequent write/read.
+     * So we'll limit ourself to 100 years, starting at 2000 for now.
+     */
+    buf[6] = rt_bin2bcd(tm->tm_year - 100);
+
+    /* CTL1 only contains TEST-mode bits apart from stop, so no need to read the value first */
+    if (i2c_smbus_write_byte_data(client, HYM8563_CTL1, HYM8563_CTL1_STOP) < 0)
+    {
+        return;
+    }
+
+    if (i2c_smbus_write_i2c_block_data(client, HYM8563_SEC, 7, buf) < 0)
+    {
+        return;
+    }
+
+    if (i2c_smbus_write_byte_data(client, HYM8563_CTL1, 0) < 0)
+    {
+        return;
+    }
+}
+
+static int hym8563_rtc_alarm_irq_enable(struct hym8563_rtc *hym8563, rt_bool_t enabled)
+{
+    int data;
+    struct rt_i2c_client *client = hym8563->client;
+
+    data = i2c_smbus_read_byte_data(client, HYM8563_CTL2);
+
+    if (data < 0)
+    {
+        return data;
+    }
+
+    if (enabled)
+    {
+        data |= HYM8563_CTL2_AIE;
+    }
+    else
+    {
+        data &= ~HYM8563_CTL2_AIE;
+    }
+
+    return i2c_smbus_write_byte_data(client, HYM8563_CTL2, data);
+};
+
+static int hym8563_rtc_read_alarm(struct hym8563_rtc *hym8563,
+        struct rt_rtc_wkalarm *alarm)
+{
+    int res;
+    rt_uint8_t buf[4];
+    struct rt_i2c_client *client = hym8563->client;
+
+    res = i2c_smbus_read_i2c_block_data(client, HYM8563_ALM_MIN, 4, buf);
+
+    if (res < 0)
+    {
+        return res;
+    }
+
+    /* The alarm only has a minute accuracy */
+    alarm->tm_sec = 0;
+    alarm->tm_min = (buf[0] & HYM8563_ALM_BIT_DISABLE) ?
+            -1 : rt_bcd2bin(buf[0] & HYM8563_MIN_MASK);
+    alarm->tm_hour = (buf[1] & HYM8563_ALM_BIT_DISABLE) ?
+            -1 : rt_bcd2bin(buf[1] & HYM8563_HOUR_MASK);
+    alarm->tm_mday = (buf[2] & HYM8563_ALM_BIT_DISABLE) ?
+            -1 : rt_bcd2bin(buf[2] & HYM8563_DAY_MASK);
+    /*
+     * alarm->tm_wday = (buf[3] & HYM8563_ALM_BIT_DISABLE) ?
+     *         -1 : rt_bcd2bin(buf[3] & HYM8563_WEEKDAY_MASK);
+     */
+
+    res = i2c_smbus_read_byte_data(client, HYM8563_CTL2);
+
+    if (res < 0)
+    {
+        return res;
+    }
+
+    alarm->enable = res & HYM8563_CTL2_AIE ? RT_TRUE : RT_FALSE;
+
+    return 0;
+}
+
+static int hym8563_rtc_set_alarm(struct hym8563_rtc *hym8563,
+        struct rt_rtc_wkalarm *alarm)
+{
+    int res;
+    rt_uint8_t buf[4];
+    struct rt_i2c_client *client = hym8563->client;
+    struct rt_rtc_wkalarm *wkalarm = &hym8563->wkalarm;
+
+    res = i2c_smbus_read_byte_data(client, HYM8563_CTL2);
+
+    if (res < 0)
+    {
+        return res;
+    }
+
+    res &= ~HYM8563_CTL2_AIE;
+
+    res = i2c_smbus_write_byte_data(client, HYM8563_CTL2, res);
+
+    if (res < 0)
+    {
+        return res;
+    }
+
+    buf[0] = (alarm->tm_min < 60 && alarm->tm_min >= 0) ?
+            rt_bin2bcd(alarm->tm_min) : HYM8563_ALM_BIT_DISABLE;
+    buf[1] = (alarm->tm_hour < 24 && alarm->tm_hour >= 0) ?
+            rt_bin2bcd(alarm->tm_hour) : HYM8563_ALM_BIT_DISABLE;
+    buf[2] = (alarm->tm_mday <= 31 && alarm->tm_mday >= 1) ?
+            rt_bin2bcd(alarm->tm_mday) : HYM8563_ALM_BIT_DISABLE;
+    /*
+     * buf[3] = (alarm->tm_wday < 7 && alarm->tm_wday >= 0) ?
+     *         rt_bin2bcd(alarm->tm_wday) : HYM8563_ALM_BIT_DISABLE;
+     */
+
+    res = i2c_smbus_write_i2c_block_data(client, HYM8563_ALM_MIN, 4, buf);
+
+    if (res < 0)
+    {
+        return res;
+    }
+
+    res = hym8563_rtc_alarm_irq_enable(hym8563, alarm->enable);
+
+    if (!(res < 0))
+    {
+        wkalarm->enable = alarm->enable;
+        wkalarm->tm_hour = alarm->tm_hour;
+        wkalarm->tm_min = alarm->tm_min;
+        wkalarm->tm_sec = alarm->tm_sec;
+    }
+
+    return res;
+}
+
+static rt_err_t hym8563_rtc_control(rt_device_t dev, int cmd, void *args)
+{
+    rt_err_t err = RT_EOK;
+    struct hym8563_rtc *hym8563 = raw_to_hym8563_rtc(dev);
+
+    if (!args)
+    {
+        return -RT_EINVAL;
+    }
+
+    switch (cmd)
+    {
+    case RT_DEVICE_CTRL_RTC_GET_TIME:
+        hym8563_rtc_read_time(hym8563, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_TIME:
+        hym8563_rtc_set_time(hym8563, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_GET_TIMEVAL:
+        hym8563_rtc_read_time(hym8563, (time_t *)&((struct timeval *)args)->tv_sec);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_TIMEVAL:
+        hym8563_rtc_set_time(hym8563, (time_t *)&((struct timeval *)args)->tv_sec);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_GET_ALARM:
+        err = hym8563_rtc_read_alarm(hym8563, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_ALARM:
+        err = hym8563_rtc_set_alarm(hym8563, args);
+        break;
+
+    default:
+        err = -RT_EINVAL;
+        break;
+    }
+
+    return err;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops hym8563_rtc_ops =
+{
+    .control = hym8563_rtc_control,
+};
+#endif
+
+static void hym8563_rtc_thread_isr(void *param)
+{
+    int data, res;
+    struct hym8563_rtc *hym8563 = param;
+    struct rt_i2c_client *client = hym8563->client;
+
+    while (RT_TRUE)
+    {
+        rt_thread_suspend(hym8563->irq_thread);
+        rt_schedule();
+
+        data = i2c_smbus_read_byte_data(client, HYM8563_CTL2);
+
+        if (data < 0)
+        {
+            LOG_E("IRQ: error %sing i2c data %d", "read", data);
+
+            return;
+        }
+
+        data &= ~HYM8563_CTL2_AF;
+
+        res = i2c_smbus_write_byte_data(client, HYM8563_CTL2, data);
+
+        if (res < 0)
+        {
+            LOG_E("IRQ: error %sing i2c data %d", "writ", res);
+
+            return;
+        }
+
+        rt_alarm_update(&hym8563->parent, 1);
+    }
+}
+
+static void hym8563_rtc_isr(int irqno, void *param)
+{
+    struct hym8563_rtc *hym8563 = param;
+
+    rt_thread_resume(hym8563->irq_thread);
+}
+
+static const int clkout_rates[] =
+{
+    32768, 1024, 32, 1,
+};
+
+static int hym8563_clkout_control(struct hym8563_rtc *hym8563, rt_bool_t enable)
+{
+    int res = i2c_smbus_read_byte_data(hym8563->client, HYM8563_CLKOUT);
+
+    if (res < 0)
+    {
+        return res;
+    }
+
+    if (enable)
+    {
+        res |= HYM8563_CLKOUT_ENABLE;
+    }
+    else
+    {
+        res &= ~HYM8563_CLKOUT_ENABLE;
+    }
+
+    return i2c_smbus_write_byte_data(hym8563->client, HYM8563_CLKOUT, res);
+}
+
+static rt_err_t hym8563_clkout_prepare(struct rt_clk_cell *cell)
+{
+    struct hym8563_rtc *hym8563 = raw_to_hym8563_clkout(cell->clk_np);
+
+    return hym8563_clkout_control(hym8563, RT_TRUE);
+}
+
+static void hym8563_clkout_unprepare(struct rt_clk_cell *cell)
+{
+    struct hym8563_rtc *hym8563 = raw_to_hym8563_clkout(cell->clk_np);
+
+    hym8563_clkout_control(hym8563, RT_FALSE);
+}
+
+static rt_bool_t hym8563_clkout_is_prepared(struct rt_clk_cell *cell)
+{
+    int res;
+    struct hym8563_rtc *hym8563 = raw_to_hym8563_clkout(cell->clk_np);
+
+    res = i2c_smbus_read_byte_data(hym8563->client, HYM8563_CLKOUT);
+
+    if (res < 0)
+    {
+        return RT_FALSE;
+    }
+
+    return !!(res & HYM8563_CLKOUT_ENABLE);
+}
+
+static rt_ubase_t hym8563_clkout_recalc_rate(struct rt_clk_cell *cell, rt_ubase_t parent_rate)
+{
+    int res;
+    struct hym8563_rtc *hym8563 = raw_to_hym8563_clkout(cell->clk_np);
+
+    res = i2c_smbus_read_byte_data(hym8563->client, HYM8563_CLKOUT);
+
+    if (res < 0)
+    {
+        return res;
+    }
+
+    res &= HYM8563_CLKOUT_MASK;
+
+    return clkout_rates[res];
+}
+
+static rt_base_t hym8563_clkout_round_rate(struct rt_clk_cell *cell, rt_ubase_t drate,
+        rt_ubase_t *prate)
+{
+    for (int i = 0; i < RT_ARRAY_SIZE(clkout_rates); ++i)
+    {
+        if (clkout_rates[i] <= drate)
+        {
+            return clkout_rates[i];
+        }
+    }
+
+    return 0;
+}
+
+static rt_err_t hym8563_clkout_set_rate(struct rt_clk_cell *cell, rt_ubase_t rate,
+        rt_ubase_t parent_rate)
+{
+    int res;
+    struct hym8563_rtc *hym8563 = raw_to_hym8563_clkout(cell->clk_np);
+
+    res = i2c_smbus_read_byte_data(hym8563->client, HYM8563_CLKOUT);
+
+    if (res < 0)
+    {
+        return res;
+    }
+
+    for (int i = 0; i < RT_ARRAY_SIZE(clkout_rates); ++i)
+    {
+        if (clkout_rates[i] == rate)
+        {
+            res &= ~HYM8563_CLKOUT_MASK;
+            res |= i;
+
+            res = i2c_smbus_write_byte_data(hym8563->client, HYM8563_CLKOUT, res);
+
+            return res >= 0 ? RT_EOK : res;
+        }
+    }
+
+    return -RT_EINVAL;
+}
+
+static const struct rt_clk_ops hym8563_clkout_ops =
+{
+    .prepare = hym8563_clkout_prepare,
+    .unprepare = hym8563_clkout_unprepare,
+    .is_prepared = hym8563_clkout_is_prepared,
+    .recalc_rate = hym8563_clkout_recalc_rate,
+    .round_rate = hym8563_clkout_round_rate,
+    .set_rate = hym8563_clkout_set_rate,
+};
+
+static void hym8563_clkout_register_clk(struct hym8563_rtc *hym8563, struct rt_device *dev)
+{
+    if (i2c_smbus_write_byte_data(hym8563->client, HYM8563_CLKOUT, 0) < 0)
+    {
+        return;
+    }
+
+    hym8563->cells[0] = &hym8563->cell;
+    hym8563->cell.name = "hym8563-clkout";
+    hym8563->cell.ops = &hym8563_clkout_ops;
+    rt_dm_dev_prop_read_string(dev, "clock-output-names", &hym8563->cell.name);
+
+    hym8563->clkout_hw.dev = dev;
+    hym8563->clkout_hw.cells_nr = 1;
+    hym8563->clkout_hw.cells = hym8563->cells;
+
+    if (rt_clk_register(&hym8563->clkout_hw))
+    {
+        return;
+    }
+}
+
+static rt_err_t hym8563_init_device(struct rt_i2c_client *client)
+{
+    int res;
+
+    /* Clear stop flag if present */
+    res = i2c_smbus_write_byte_data(client, HYM8563_CTL1, 0);
+    if (res < 0)
+    {
+        return res;
+    }
+
+    res = i2c_smbus_read_byte_data(client, HYM8563_CTL2);
+    if (res < 0)
+    {
+        return res;
+    }
+
+    /* Disable alarm and timer interrupts */
+    res &= ~HYM8563_CTL2_AIE;
+    res &= ~HYM8563_CTL2_TIE;
+
+    /* Clear any pending alarm and timer flags */
+    if (res & HYM8563_CTL2_AF)
+    {
+        res &= ~HYM8563_CTL2_AF;
+    }
+
+    if (res & HYM8563_CTL2_TF)
+    {
+        res &= ~HYM8563_CTL2_TF;
+    }
+
+    res &= ~HYM8563_CTL2_TI_TP;
+
+    return i2c_smbus_write_byte_data(client, HYM8563_CTL2, res);
+}
+
+static rt_err_t hym8563_rtc_probe(struct rt_i2c_client *client)
+{
+    rt_err_t err;
+    rt_int32_t res;
+    const char *dev_name;
+    struct rt_device *dev = &client->parent;
+    struct hym8563_rtc *hym8563 = rt_calloc(1, sizeof(*hym8563));
+
+    if (!hym8563)
+    {
+        return -RT_ENOMEM;
+    }
+
+    if ((res = hym8563_init_device(client)) < 0)
+    {
+        err = res;
+
+        goto _fail;
+    }
+
+    hym8563->irq = rt_dm_dev_get_irq(dev, 0);
+    hym8563->client = client;
+
+    /* check state of calendar information */
+    if ((res = i2c_smbus_read_byte_data(client, HYM8563_SEC)) < 0)
+    {
+        err = res;
+
+        goto _fail;
+    }
+
+    LOG_D("rtc information is %s", (res & HYM8563_SEC_VL) ? "invalid" : "valid");
+
+    if (hym8563->irq >= 0)
+    {
+        hym8563->irq_thread = rt_thread_create("rtc-hym8563", &hym8563_rtc_thread_isr,
+                hym8563, DM_THREAD_STACK_SIZE, RT_THREAD_PRIORITY_MAX / 2, 10);
+
+        if (!hym8563->irq_thread)
+        {
+            err = -RT_ERROR;
+            LOG_E("Create RTC IRQ thread fail");
+            goto _fail;
+        }
+
+        rt_thread_startup(hym8563->irq_thread);
+
+        rt_hw_interrupt_install(hym8563->irq, hym8563_rtc_isr, hym8563, "rtc-hym8563");
+        rt_hw_interrupt_umask(hym8563->irq);
+    }
+
+    dev->user_data = hym8563;
+
+    hym8563->parent.type = RT_Device_Class_RTC;
+#ifdef RT_USING_DEVICE_OPS
+    hym8563->parent.ops = &hym8563_rtc_ops;
+#else
+    hym8563->parent.control = hym8563_rtc_control;
+#endif
+
+    rtc_dev_set_name(&hym8563->parent);
+    dev_name = rt_dm_dev_get_name(&hym8563->parent);
+    rt_device_register(&hym8563->parent, dev_name, RT_DEVICE_FLAG_RDWR);
+
+    hym8563_clkout_register_clk(hym8563, dev);
+
+    return RT_EOK;
+
+_fail:
+    if (hym8563->irq_thread)
+    {
+        rt_thread_delete(hym8563->irq_thread);
+    }
+    rt_free(hym8563);
+
+    return err;
+}
+
+static rt_err_t hym8563_rtc_remove(struct rt_i2c_client *client)
+{
+    struct hym8563_rtc *hym8563 = client->parent.user_data;
+
+    rt_dm_dev_unbind_fwdata(&client->parent, RT_NULL);
+
+    if (hym8563->irq >= 0)
+    {
+        if (hym8563->wkalarm.enable)
+        {
+            hym8563_rtc_alarm_irq_enable(hym8563, RT_FALSE);
+        }
+
+        rt_hw_interrupt_mask(hym8563->irq);
+        rt_pic_detach_irq(hym8563->irq, hym8563);
+
+        rt_thread_delete(hym8563->irq_thread);
+    }
+
+    if (hym8563->cells[0])
+    {
+        rt_clk_unregister(&hym8563->clkout_hw);
+    }
+
+    rt_device_unregister(&hym8563->parent);
+
+    rt_free(hym8563);
+
+    return RT_EOK;
+}
+
+static const struct rt_i2c_device_id hym8563_rtc_ids[] =
+{
+    { .name = "hym8563" },
+    { /* sentinel */ },
+};
+
+static const struct rt_ofw_node_id hym8563_rtc_ofw_ids[] =
+{
+    { .compatible = "haoyu,hym8563" },
+    { /* sentinel */ },
+};
+
+static struct rt_i2c_driver hym8563_rtc_driver =
+{
+    .ids = hym8563_rtc_ids,
+    .ofw_ids = hym8563_rtc_ofw_ids,
+
+    .probe = hym8563_rtc_probe,
+    .remove = hym8563_rtc_remove,
+};
+RT_I2C_DRIVER_EXPORT(hym8563_rtc_driver);

+ 538 - 0
components/drivers/rtc/rtc-pcf8523.c

@@ -0,0 +1,538 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-09-23     GuEe-GUI     first version
+ */
+
+#include "rtc_dm.h"
+
+#define DBG_TAG "rtc.pcf8523"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#define PCF8523_REG_CONTROL1        0x00
+#define PCF8523_CONTROL1_CAP_SEL    RT_BIT(7)
+#define PCF8523_CONTROL1_STOP       RT_BIT(5)
+#define PCF8523_CONTROL1_AIE        RT_BIT(1)
+
+#define PCF8523_REG_CONTROL2        0x01
+#define PCF8523_CONTROL2_AF         RT_BIT(3)
+
+#define PCF8523_REG_CONTROL3        0x02
+#define PCF8523_CONTROL3_PM         RT_GENMASK(7, 5)
+#define PCF8523_PM_STANDBY          0x7
+#define PCF8523_CONTROL3_BLF        RT_BIT(2) /* battery low bit, read-only */
+#define PCF8523_CONTROL3_BSF        RT_BIT(3)
+
+#define PCF8523_REG_SECONDS         0x03
+#define PCF8523_SECONDS_OS          RT_BIT(7)
+
+#define PCF8523_REG_MINUTES         0x04
+#define PCF8523_REG_HOURS           0x05
+#define PCF8523_REG_DAYS            0x06
+#define PCF8523_REG_WEEKDAYS        0x07
+#define PCF8523_REG_MONTHS          0x08
+#define PCF8523_REG_YEARS           0x09
+
+#define PCF8523_REG_MINUTE_ALARM    0x0a
+#define PCF8523_REG_HOUR_ALARM      0x0b
+#define PCF8523_REG_DAY_ALARM       0x0c
+#define PCF8523_REG_WEEKDAY_ALARM   0x0d
+#define ALARM_DIS                   RT_BIT(7)
+
+#define PCF8523_REG_OFFSET          0x0e
+#define PCF8523_OFFSET_MODE         RT_BIT(7)
+
+#define PCF8523_TMR_CLKOUT_CTRL     0x0f
+
+struct pcf8523_rtc
+{
+    struct rt_device parent;
+    struct rt_clk_node clkout_hw;
+
+    int irq;
+    struct rt_i2c_client *client;
+    struct rt_thread *irq_thread;
+
+    struct rt_rtc_wkalarm wkalarm;
+};
+
+#define raw_to_pcf8523_rtc(raw) rt_container_of(raw, struct pcf8523_rtc, parent)
+
+static rt_int32_t i2c_read_byte(struct rt_i2c_client *client,
+        rt_uint8_t command)
+{
+    rt_int32_t res;
+    rt_uint8_t ret = 0;
+    struct rt_i2c_msg msg[2];
+
+    msg[0].buf = &command;
+    msg[0].addr = client->client_addr;
+    msg[0].len = 1;
+    msg[0].flags = RT_I2C_WR;
+
+    msg[1].buf = &ret;
+    msg[1].addr = client->client_addr;
+    msg[1].len = 1;
+    msg[1].flags = RT_I2C_RD;
+
+    res = rt_i2c_transfer(client->bus, msg, 2);
+
+    return res == 2 ? ret : res;
+}
+
+static rt_int32_t i2c_write_byte(struct rt_i2c_client *client,
+        rt_uint8_t command, rt_uint8_t value)
+{
+    rt_int32_t res;
+    struct rt_i2c_msg msg[1];
+    rt_uint8_t data[2] = { command, value };
+
+    msg[0].buf = data;
+    msg[0].addr = client->client_addr;
+    msg[0].len = 2;
+    msg[0].flags = RT_I2C_WR;
+
+    res = rt_i2c_transfer(client->bus, msg, 1);
+
+    return res == 1 ? 0 : res;
+}
+
+static rt_int32_t i2c_update_byte_bits(struct rt_i2c_client *client,
+        rt_uint8_t command, rt_uint8_t mask, rt_uint8_t value)
+{
+    rt_int32_t res = i2c_read_byte(client, command);
+
+    if (res < 0)
+    {
+        return res;
+    }
+
+    res &= ~mask;
+    res |= value;
+
+    return i2c_write_byte(client, command, res);
+}
+
+/* Returns the number of read bytes */
+static rt_int32_t i2c_read_block(struct rt_i2c_client *client,
+        rt_uint8_t command, rt_uint8_t length, rt_uint8_t *values)
+{
+    struct rt_i2c_msg msg[2];
+
+    msg[0].buf = &command;
+    msg[0].addr = client->client_addr;
+    msg[0].len = 1;
+    msg[0].flags = RT_I2C_WR;
+
+    msg[1].buf = values;
+    msg[1].addr = client->client_addr;
+    msg[1].len = length;
+    msg[1].flags = RT_I2C_RD;
+
+    return rt_i2c_transfer(client->bus, msg, 2);
+}
+
+static rt_int32_t i2c_write_block(struct rt_i2c_client *client,
+        rt_uint8_t command, rt_uint8_t length, const rt_uint8_t *values)
+{
+    rt_uint8_t data[32];
+    struct rt_i2c_msg msg[1];
+
+    length = rt_min_t(rt_uint8_t, length, RT_ARRAY_SIZE(data) - 1);
+
+    data[0] = command;
+    rt_memcpy(&data[1], values, length);
+
+    msg[0].buf = data;
+    msg[0].addr = client->client_addr;
+    msg[0].len = length + 1;
+    msg[0].flags = RT_I2C_WR;
+
+    return rt_i2c_transfer(client->bus, msg, 1);
+}
+
+static void pcf8523_rtc_read_time(struct pcf8523_rtc *pcf8523, time_t *sec)
+{
+    rt_uint8_t regs[10];
+    struct tm tm;
+    struct rt_i2c_client *client = pcf8523->client;
+
+    if (i2c_read_block(client, PCF8523_REG_CONTROL1, sizeof(regs), regs) < 0)
+    {
+        return;
+    }
+
+    if ((regs[0] & PCF8523_CONTROL1_STOP) || (regs[3] & PCF8523_SECONDS_OS))
+    {
+        return;
+    }
+
+    tm.tm_sec = rt_bcd2bin(regs[3] & 0x7f);
+    tm.tm_min = rt_bcd2bin(regs[4] & 0x7f);
+    tm.tm_hour = rt_bcd2bin(regs[5] & 0x3f);
+    tm.tm_mday = rt_bcd2bin(regs[6] & 0x3f);
+    tm.tm_wday = regs[7] & 0x7;
+    tm.tm_mon = rt_bcd2bin(regs[8] & 0x1f) - 1;
+    tm.tm_year = rt_bcd2bin(regs[9]) + 100;
+
+    *sec = timegm(&tm);
+}
+
+static void pcf8523_rtc_set_time(struct pcf8523_rtc *pcf8523, time_t *sec)
+{
+    rt_uint8_t regs[7];
+    struct tm *tm;
+    struct rt_i2c_client *client = pcf8523->client;
+
+    if (i2c_update_byte_bits(client, PCF8523_REG_CONTROL1,
+            PCF8523_CONTROL1_STOP, PCF8523_CONTROL1_STOP) < 0)
+    {
+        return;
+    }
+
+    tm = localtime(sec);
+
+    regs[0] = rt_bin2bcd(tm->tm_sec);
+    regs[1] = rt_bin2bcd(tm->tm_min);
+    regs[2] = rt_bin2bcd(tm->tm_hour);
+    regs[3] = rt_bin2bcd(tm->tm_mday);
+    regs[4] = 0;
+    regs[5] = rt_bin2bcd(tm->tm_mon + 1);
+    regs[6] = rt_bin2bcd(tm->tm_year - 100);
+
+    if (i2c_write_block(client, PCF8523_REG_SECONDS, sizeof(regs), regs) < 0)
+    {
+        i2c_update_byte_bits(client, PCF8523_REG_CONTROL1, PCF8523_CONTROL1_STOP, 0);
+
+        return;
+    }
+
+    i2c_update_byte_bits(client, PCF8523_REG_CONTROL1, PCF8523_CONTROL1_STOP, 0);
+}
+
+static int pcf8523_rtc_alarm_irq_enable(struct pcf8523_rtc *pcf8523, rt_bool_t enabled)
+{
+    struct rt_i2c_client *client = pcf8523->client;
+
+    return i2c_update_byte_bits(client, PCF8523_REG_CONTROL1,
+            PCF8523_CONTROL1_AIE, enabled ? PCF8523_CONTROL1_AIE : 0);
+}
+
+static int pcf8523_rtc_read_alarm(struct pcf8523_rtc *pcf8523,
+        struct rt_rtc_wkalarm *alarm)
+{
+    int res;
+    rt_uint8_t regs[4];
+    struct rt_i2c_client *client = pcf8523->client;
+
+    if ((res = i2c_read_block(client, PCF8523_REG_MINUTE_ALARM, sizeof(regs), regs)))
+    {
+        return res;
+    }
+
+    alarm->tm_sec = 0;
+    alarm->tm_min = rt_bcd2bin(regs[0] & 0x7F);
+    alarm->tm_hour = rt_bcd2bin(regs[1] & 0x3F);
+    alarm->tm_mday = rt_bcd2bin(regs[2] & 0x3F);
+    /* alarm->tm_wday = rt_bcd2bin(regs[3] & 0x7); */
+
+    if ((res = i2c_read_byte(client, PCF8523_REG_CONTROL1)) < 0)
+    {
+        return res;
+    }
+
+    alarm->enable = !!(res & PCF8523_CONTROL1_AIE);
+
+    return RT_EOK;
+}
+
+static int pcf8523_rtc_set_alarm(struct pcf8523_rtc *pcf8523,
+        struct rt_rtc_wkalarm *alarm)
+{
+    int res;
+    rt_uint8_t regs[5];
+    struct rt_i2c_client *client = pcf8523->client;
+    struct rt_rtc_wkalarm *wkalarm = &pcf8523->wkalarm;
+
+    if ((res = pcf8523_rtc_alarm_irq_enable(pcf8523, RT_FALSE)) < 0)
+    {
+        return res;
+    }
+
+    if ((res = i2c_write_byte(client, PCF8523_REG_CONTROL2, 0)) < 0)
+    {
+        return res;
+    }
+
+    regs[0] = rt_bin2bcd(alarm->tm_min);
+    regs[1] = rt_bin2bcd(alarm->tm_hour);
+    regs[2] = rt_bin2bcd(alarm->tm_mday);
+    regs[3] = ALARM_DIS;
+
+    if ((res = i2c_write_block(client, PCF8523_REG_MINUTE_ALARM, sizeof(regs), regs)))
+    {
+        return res;
+    }
+
+    res = pcf8523_rtc_alarm_irq_enable(pcf8523, alarm->enable);
+
+    if (!(res < 0))
+    {
+        wkalarm->enable = alarm->enable;
+        wkalarm->tm_hour = alarm->tm_hour;
+        wkalarm->tm_min = alarm->tm_min;
+        wkalarm->tm_sec = alarm->tm_sec;
+    }
+
+    return res;
+}
+
+static rt_err_t pcf8523_rtc_control(rt_device_t dev, int cmd, void *args)
+{
+    rt_err_t err = RT_EOK;
+    struct pcf8523_rtc *pcf8523 = raw_to_pcf8523_rtc(dev);
+
+    if (!args)
+    {
+        return -RT_EINVAL;
+    }
+
+    switch (cmd)
+    {
+    case RT_DEVICE_CTRL_RTC_GET_TIME:
+        pcf8523_rtc_read_time(pcf8523, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_TIME:
+        pcf8523_rtc_set_time(pcf8523, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_GET_TIMEVAL:
+        pcf8523_rtc_read_time(pcf8523, (time_t *)&((struct timeval *)args)->tv_sec);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_TIMEVAL:
+        pcf8523_rtc_set_time(pcf8523, (time_t *)&((struct timeval *)args)->tv_sec);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_GET_ALARM:
+        err = pcf8523_rtc_read_alarm(pcf8523, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_ALARM:
+        err = pcf8523_rtc_set_alarm(pcf8523, args);
+        break;
+
+    default:
+        err = -RT_EINVAL;
+        break;
+    }
+
+    return err;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops pcf8523_rtc_ops =
+{
+    .control = pcf8523_rtc_control,
+};
+#endif
+
+static void pcf8523_rtc_thread_isr(void *param)
+{
+    rt_int32_t res;
+    struct pcf8523_rtc *pcf8523 = param;
+    struct rt_i2c_client *client = pcf8523->client;
+
+    while (RT_TRUE)
+    {
+        rt_thread_suspend(pcf8523->irq_thread);
+        rt_schedule();
+
+        if ((res = i2c_read_byte(client, PCF8523_REG_CONTROL2)) < 0)
+        {
+            continue;
+        }
+
+        if (res & PCF8523_CONTROL2_AF)
+        {
+            res &= ~PCF8523_CONTROL2_AF;
+            i2c_write_byte(client, PCF8523_REG_CONTROL2, res);
+
+            rt_alarm_update(&pcf8523->parent, 1);
+        }
+    }
+}
+
+static void pcf8523_rtc_isr(int irqno, void *param)
+{
+    struct pcf8523_rtc *pcf8523 = param;
+
+    rt_thread_resume(pcf8523->irq_thread);
+}
+
+static rt_err_t pcf8523_rtc_probe(struct rt_i2c_client *client)
+{
+    rt_err_t err;
+    rt_int32_t res;
+    const char *dev_name;
+    struct rt_device *dev = &client->parent;
+    struct pcf8523_rtc *pcf8523 = rt_calloc(1, sizeof(*pcf8523));
+
+    if (!pcf8523)
+    {
+        return -RT_ENOMEM;
+    }
+
+    pcf8523->irq = rt_dm_dev_get_irq(dev, 0);
+    pcf8523->client = client;
+
+#ifdef RT_USING_OFW
+    if (dev->ofw_node)
+    {
+        rt_uint32_t load = 12500, value = 0;
+
+        rt_ofw_prop_read_u32(dev->ofw_node, "quartz-load-femtofarads", &load);
+
+        if (load != 7000)
+        {
+            value = PCF8523_CONTROL1_CAP_SEL;
+        }
+
+        if ((res = i2c_update_byte_bits(client, PCF8523_REG_CONTROL1,
+                PCF8523_CONTROL1_CAP_SEL, value)) < 0)
+        {
+            err = res;
+            goto _fail;
+        }
+    }
+#endif /* RT_USING_OFW */
+
+    if ((res = i2c_read_byte(client, PCF8523_REG_SECONDS)) < 0)
+    {
+        err = res;
+        goto _fail;
+    }
+
+    if (res & PCF8523_SECONDS_OS)
+    {
+        if ((res = i2c_read_byte(client, PCF8523_REG_CONTROL3)) < 0)
+        {
+            err = res;
+            goto _fail;
+        }
+
+        if (RT_FIELD_GET(PCF8523_CONTROL3_PM, res) == PCF8523_PM_STANDBY)
+        {
+            res &= ~PCF8523_CONTROL3_PM;
+
+            if ((res = i2c_write_byte(client, PCF8523_REG_CONTROL3, res)) < 0)
+            {
+                err = res;
+                goto _fail;
+            }
+        }
+    }
+
+    if (pcf8523->irq >= 0)
+    {
+        if ((res = i2c_write_byte(client, PCF8523_TMR_CLKOUT_CTRL, 0x38)) < 0)
+        {
+            err = res;
+            goto _fail;
+        }
+
+        pcf8523->irq_thread = rt_thread_create("rtc-pcf8523", &pcf8523_rtc_thread_isr,
+                pcf8523, DM_THREAD_STACK_SIZE, RT_THREAD_PRIORITY_MAX / 2, 10);
+
+        if (!pcf8523->irq_thread)
+        {
+            err = -RT_ERROR;
+            LOG_E("Create RTC IRQ thread fail");
+            goto _fail;
+        }
+
+        rt_thread_startup(pcf8523->irq_thread);
+
+        rt_hw_interrupt_install(pcf8523->irq, pcf8523_rtc_isr, pcf8523, "rtc-pcf8523");
+        rt_hw_interrupt_umask(pcf8523->irq);
+    }
+
+    dev->user_data = pcf8523;
+
+    pcf8523->parent.type = RT_Device_Class_RTC;
+#ifdef RT_USING_DEVICE_OPS
+    pcf8523->parent.ops = &pcf8523_rtc_ops;
+#else
+    pcf8523->parent.control = pcf8523_rtc_control;
+#endif
+
+    rtc_dev_set_name(&pcf8523->parent);
+    dev_name = rt_dm_dev_get_name(&pcf8523->parent);
+    rt_device_register(&pcf8523->parent, dev_name, RT_DEVICE_FLAG_RDWR);
+
+    return RT_EOK;
+
+_fail:
+    if (pcf8523->irq_thread)
+    {
+        rt_thread_delete(pcf8523->irq_thread);
+    }
+    rt_free(pcf8523);
+
+    return err;
+}
+
+static rt_err_t pcf8523_rtc_remove(struct rt_i2c_client *client)
+{
+    struct pcf8523_rtc *pcf8523 = client->parent.user_data;
+
+    rt_dm_dev_unbind_fwdata(&client->parent, RT_NULL);
+
+    if (pcf8523->irq >= 0)
+    {
+        if (pcf8523->wkalarm.enable)
+        {
+            pcf8523_rtc_alarm_irq_enable(pcf8523, RT_FALSE);
+        }
+
+        rt_hw_interrupt_mask(pcf8523->irq);
+        rt_pic_detach_irq(pcf8523->irq, pcf8523);
+
+        rt_thread_delete(pcf8523->irq_thread);
+    }
+
+    rt_device_unregister(&pcf8523->parent);
+
+    rt_free(pcf8523);
+
+    return RT_EOK;
+}
+
+static const struct rt_i2c_device_id pcf8523_rtc_ids[] =
+{
+    { .name = "pcf8523" },
+    { /* sentinel */ },
+};
+
+static const struct rt_ofw_node_id pcf8523_rtc_ofw_ids[] =
+{
+    { .compatible = "nxp,pcf8523" },
+    { .compatible = "microcrystal,rv8523" },
+    { /* sentinel */ },
+};
+
+static struct rt_i2c_driver pcf8523_rtc_driver =
+{
+    .ids = pcf8523_rtc_ids,
+    .ofw_ids = pcf8523_rtc_ofw_ids,
+
+    .probe = pcf8523_rtc_probe,
+    .remove = pcf8523_rtc_remove,
+};
+RT_I2C_DRIVER_EXPORT(pcf8523_rtc_driver);

+ 673 - 0
components/drivers/rtc/rtc-pcf8563.c

@@ -0,0 +1,673 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2022-11-26     GuEe-GUI     first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "rtc.pcf8563"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include "rtc_dm.h"
+
+#define PCF8563_REG_ST1             0x00 /* status */
+#define PCF8563_REG_ST2             0x01
+#define PCF8563_BIT_AIE             RT_BIT(1)
+#define PCF8563_BIT_AF              RT_BIT(3)
+#define PCF8563_BITS_ST2_N          (7 << 5)
+
+#define PCF8563_REG_SC              0x02 /* datetime */
+#define PCF8563_REG_MN              0x03
+#define PCF8563_REG_HR              0x04
+#define PCF8563_REG_DM              0x05
+#define PCF8563_REG_DW              0x06
+#define PCF8563_REG_MO              0x07
+#define PCF8563_REG_YR              0x08
+
+#define PCF8563_REG_AMN             0x09 /* alarm */
+
+#define PCF8563_REG_CLKO            0x0d /* clock out */
+#define PCF8563_REG_CLKO_FE         0x80 /* clock out enabled */
+#define PCF8563_REG_CLKO_F_MASK     0x03 /* frequenc mask */
+#define PCF8563_REG_CLKO_F_32768H   0x00
+#define PCF8563_REG_CLKO_F_1024HZ   0x01
+#define PCF8563_REG_CLKO_F_32HZ     0x02
+#define PCF8563_REG_CLKO_F_1HZ      0x03
+
+#define PCF8563_REG_TMRC            0x0e /* timer control */
+#define PCF8563_TMRC_ENABLE         RT_BIT(7)
+#define PCF8563_TMRC_4096           0
+#define PCF8563_TMRC_64             1
+#define PCF8563_TMRC_1              2
+#define PCF8563_TMRC_1_60           3
+#define PCF8563_TMRC_MASK           3
+
+#define PCF8563_REG_TMR             0x0f /* timer */
+
+#define PCF8563_SC_LV               0x80 /* low voltage */
+#define PCF8563_MO_C                0x80 /* century */
+
+struct pcf8563_rtc
+{
+    struct rt_device parent;
+    struct rt_clk_node clkout_hw;
+
+    struct rt_clk_cell cell;
+    struct rt_clk_cell *cells[1];
+
+    int irq;
+    int c_polarity;
+
+    struct rt_i2c_client *client;
+    struct rt_thread *irq_thread;
+
+    struct rt_rtc_wkalarm wkalarm;
+};
+
+#define raw_to_pcf8563_rtc(raw) rt_container_of(raw, struct pcf8563_rtc, parent)
+#define raw_to_pcf8563_clkout(raw) rt_container_of(raw, struct pcf8563_rtc, clkout_hw)
+
+static rt_err_t pcf8563_read_block_data(struct pcf8563_rtc *pcf8563,
+        rt_uint8_t reg, rt_uint8_t length, rt_uint8_t *buf)
+{
+    rt_int32_t res;
+    struct rt_i2c_msg msg[2];
+    struct rt_i2c_client *client = pcf8563->client;
+
+    msg[0].buf = &reg;
+    msg[0].addr = client->client_addr;
+    msg[0].len = 1;
+    msg[0].flags = RT_I2C_WR;
+
+    msg[1].buf = buf;
+    msg[1].addr = client->client_addr;
+    msg[1].len = length;
+    msg[1].flags = RT_I2C_RD;
+
+    res = rt_i2c_transfer(client->bus, msg, 2);
+
+    return res > 0 ? RT_EOK : res;
+}
+
+static rt_err_t pcf8563_write_block_data(struct pcf8563_rtc *pcf8563,
+        rt_uint8_t reg, rt_uint8_t length, rt_uint8_t *buf)
+{
+    rt_int32_t res;
+    struct rt_i2c_client *client = pcf8563->client;
+
+    for (int i = 0; i < length; i++)
+    {
+        rt_uint8_t data[2] = { reg + i, buf[i] };
+
+        res = rt_i2c_master_send(client->bus, client->client_addr,
+                RT_I2C_WR, data, sizeof(data));
+
+        if (res != sizeof(data))
+        {
+            return -RT_EIO;
+        }
+    }
+
+    return RT_EOK;
+}
+
+static rt_err_t pcf8563_set_alarm_mode(struct pcf8563_rtc *pcf8563, rt_bool_t on)
+{
+    rt_err_t err;
+    rt_uint8_t buf;
+
+    err = pcf8563_read_block_data(pcf8563, PCF8563_REG_ST2, 1, &buf);
+
+    if (err)
+    {
+        return err;
+    }
+
+    if (on)
+    {
+        buf |= PCF8563_BIT_AIE;
+    }
+    else
+    {
+        buf &= ~PCF8563_BIT_AIE;
+    }
+
+    buf &= ~(PCF8563_BIT_AF | PCF8563_BITS_ST2_N);
+
+    err = pcf8563_write_block_data(pcf8563, PCF8563_REG_ST2, 1, &buf);
+
+    if (err)
+    {
+        LOG_E("Write %s error", "PCF8563_REG_ST2");
+
+        return -RT_EIO;
+    }
+
+    return RT_EOK;
+}
+
+static rt_err_t pcf8563_get_alarm_mode(struct pcf8563_rtc *pcf8563,
+        rt_uint8_t *en, rt_uint8_t *pen)
+{
+    rt_err_t err;
+    rt_uint8_t buf;
+
+    err = pcf8563_read_block_data(pcf8563, PCF8563_REG_ST2, 1, &buf);
+
+    if (err)
+    {
+        return err;
+    }
+
+    if (en)
+    {
+        *en = !!(buf & PCF8563_BIT_AIE);
+    }
+
+    if (pen)
+    {
+        *pen = !!(buf & PCF8563_BIT_AF);
+    }
+
+    return RT_EOK;
+}
+
+static int pcf8563_rtc_read_time(struct pcf8563_rtc *pcf8563, time_t *sec)
+{
+    rt_err_t err;
+    struct tm tm;
+    rt_uint8_t buf[9];
+
+    err = pcf8563_read_block_data(pcf8563, PCF8563_REG_ST1, 9, buf);
+
+    if (err)
+    {
+        return err;
+    }
+
+    if (buf[PCF8563_REG_SC] & PCF8563_SC_LV)
+    {
+        LOG_E("Low voltage detected, date/time is not reliable");
+
+        return -RT_EINVAL;
+    }
+
+    tm.tm_sec = rt_bcd2bin(buf[PCF8563_REG_SC] & 0x7f);
+    tm.tm_min = rt_bcd2bin(buf[PCF8563_REG_MN] & 0x7f);
+    tm.tm_hour = rt_bcd2bin(buf[PCF8563_REG_HR] & 0x3f); /* rtc hr 0-23 */
+    tm.tm_mday = rt_bcd2bin(buf[PCF8563_REG_DM] & 0x3f);
+    tm.tm_wday = buf[PCF8563_REG_DW] & 0x07;
+    tm.tm_mon = rt_bcd2bin(buf[PCF8563_REG_MO] & 0x1f) - 1; /* rtc mn 1-12 */
+    tm.tm_year = rt_bcd2bin(buf[PCF8563_REG_YR]) + 100;
+
+    /* detect the polarity heuristically. see note above. */
+    pcf8563->c_polarity = (buf[PCF8563_REG_MO] & PCF8563_MO_C) ?
+            (tm.tm_year >= 100) : (tm.tm_year < 100);
+
+    *sec = timegm(&tm);
+
+    return RT_EOK;
+}
+
+static int pcf8563_rtc_set_time(struct pcf8563_rtc *pcf8563, time_t *sec)
+{
+    struct tm *tm;
+    rt_uint8_t buf[9];
+
+    tm = localtime(sec);
+
+    /* hours, minutes and seconds */
+    buf[PCF8563_REG_SC] = rt_bin2bcd(tm->tm_sec);
+    buf[PCF8563_REG_MN] = rt_bin2bcd(tm->tm_min);
+    buf[PCF8563_REG_HR] = rt_bin2bcd(tm->tm_hour);
+
+    buf[PCF8563_REG_DM] = rt_bin2bcd(tm->tm_mday);
+
+    /* month, 1 - 12 */
+    buf[PCF8563_REG_MO] = rt_bin2bcd(tm->tm_mon + 1);
+
+    /* year and century */
+    buf[PCF8563_REG_YR] = rt_bin2bcd(tm->tm_year - 100);
+    if (pcf8563->c_polarity ? (tm->tm_year >= 100) : (tm->tm_year < 100))
+    {
+        buf[PCF8563_REG_MO] |= PCF8563_MO_C;
+    }
+
+    buf[PCF8563_REG_DW] = tm->tm_wday & 0x07;
+
+    return pcf8563_write_block_data(pcf8563, PCF8563_REG_SC,
+            9 - PCF8563_REG_SC, buf + PCF8563_REG_SC);
+}
+
+static int pcf8563_rtc_read_alarm(struct pcf8563_rtc *pcf8563,
+        struct rt_rtc_wkalarm *alarm)
+{
+    rt_err_t err;
+    rt_uint8_t pending, buf[4];
+
+    err = pcf8563_read_block_data(pcf8563, PCF8563_REG_AMN, 4, buf);
+
+    if (err)
+    {
+        return err;
+    }
+
+    alarm->tm_sec = 0;
+    alarm->tm_min = rt_bcd2bin(buf[0] & 0x7f);
+    alarm->tm_hour = rt_bcd2bin(buf[1] & 0x3f);
+    alarm->tm_mday = rt_bcd2bin(buf[2] & 0x3f);
+    /* alarm->tm_wday = rt_bcd2bin(buf[3] & 0x7); */
+
+    return pcf8563_get_alarm_mode(pcf8563, (rt_uint8_t *)&alarm->enable, &pending);
+}
+
+static int pcf8563_rtc_set_alarm(struct pcf8563_rtc *pcf8563,
+        struct rt_rtc_wkalarm *alarm)
+{
+    rt_err_t err;
+    rt_uint8_t buf[4];
+    struct rt_rtc_wkalarm *wkalarm = &pcf8563->wkalarm;
+
+    buf[0] = rt_bin2bcd(alarm->tm_min);
+    buf[1] = rt_bin2bcd(alarm->tm_hour);
+    buf[2] = rt_bin2bcd(alarm->tm_mday);
+    buf[3] = 0 & 0x07; /* alarm->tm_wday */
+
+    err = pcf8563_write_block_data(pcf8563, PCF8563_REG_AMN, 4, buf);
+
+    if (err)
+    {
+        return err;
+    }
+
+    err = pcf8563_set_alarm_mode(pcf8563, alarm->enable);
+
+    if (!err)
+    {
+        wkalarm->enable = alarm->enable;
+        wkalarm->tm_hour = alarm->tm_hour;
+        wkalarm->tm_min = alarm->tm_min;
+        wkalarm->tm_sec = alarm->tm_sec;
+    }
+
+    return err;
+}
+
+static rt_err_t pcf8563_rtc_control(rt_device_t dev, int cmd, void *args)
+{
+    rt_err_t err = RT_EOK;
+    struct pcf8563_rtc *pcf8563 = raw_to_pcf8563_rtc(dev);
+
+    if (!args)
+    {
+        return -RT_EINVAL;
+    }
+
+    switch (cmd)
+    {
+    case RT_DEVICE_CTRL_RTC_GET_TIME:
+        err = pcf8563_rtc_read_time(pcf8563, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_TIME:
+        err = pcf8563_rtc_set_time(pcf8563, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_GET_TIMEVAL:
+        err = pcf8563_rtc_read_time(pcf8563, (time_t *)&((struct timeval *)args)->tv_sec);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_TIMEVAL:
+        err = pcf8563_rtc_set_time(pcf8563, (time_t *)&((struct timeval *)args)->tv_sec);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_GET_ALARM:
+        err = pcf8563_rtc_read_alarm(pcf8563, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_ALARM:
+        err = pcf8563_rtc_set_alarm(pcf8563, args);
+        break;
+
+    default:
+        err = -RT_EINVAL;
+        break;
+    }
+
+    return err;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops pcf8563_rtc_ops =
+{
+    .control = pcf8563_rtc_control,
+};
+#endif
+
+static void pcf8563_rtc_thread_isr(void *param)
+{
+    rt_err_t err;
+    rt_uint8_t pending;
+    struct pcf8563_rtc *pcf8563 = param;
+
+    while (RT_TRUE)
+    {
+        rt_thread_suspend(pcf8563->irq_thread);
+        rt_schedule();
+
+        err = pcf8563_get_alarm_mode(pcf8563, NULL, &pending);
+
+        if (err)
+        {
+            continue;
+        }
+
+        if (pending)
+        {
+            rt_alarm_update(&pcf8563->parent, 1);
+
+            pcf8563_set_alarm_mode(pcf8563, 1);
+        }
+    }
+}
+
+static void pcf8563_rtc_isr(int irqno, void *param)
+{
+    struct pcf8563_rtc *pcf8563 = param;
+
+    rt_thread_resume(pcf8563->irq_thread);
+}
+
+static const int clkout_rates[] =
+{
+    32768, 1024, 32, 1,
+};
+
+static rt_err_t pcf8563_clkout_control(struct pcf8563_rtc *pcf8563, rt_bool_t enable)
+{
+    rt_err_t err;
+    rt_uint8_t buf;
+
+    if ((err = pcf8563_read_block_data(pcf8563, PCF8563_REG_CLKO, 1, &buf)))
+    {
+        return err;
+    }
+
+    if (enable)
+    {
+        buf |= PCF8563_REG_CLKO_FE;
+    }
+    else
+    {
+        buf &= ~PCF8563_REG_CLKO_FE;
+    }
+
+    return pcf8563_write_block_data(pcf8563, PCF8563_REG_CLKO, 1, &buf);
+}
+
+static rt_err_t pcf8563_clkout_prepare(struct rt_clk_cell *cell)
+{
+    struct pcf8563_rtc *pcf8563 = raw_to_pcf8563_clkout(cell->clk_np);
+
+    return pcf8563_clkout_control(pcf8563, RT_TRUE);
+}
+
+static void pcf8563_clkout_unprepare(struct rt_clk_cell *cell)
+{
+    struct pcf8563_rtc *pcf8563 = raw_to_pcf8563_clkout(cell->clk_np);
+
+    pcf8563_clkout_control(pcf8563, RT_FALSE);
+}
+
+static rt_bool_t pcf8563_clkout_is_prepared(struct rt_clk_cell *cell)
+{
+    rt_uint8_t buf;
+    struct pcf8563_rtc *pcf8563 = raw_to_pcf8563_clkout(cell->clk_np);
+
+    if (pcf8563_read_block_data(pcf8563, PCF8563_REG_CLKO, 1, &buf))
+    {
+        return RT_FALSE;
+    }
+
+    return !!(buf & PCF8563_REG_CLKO_FE);
+}
+
+static rt_ubase_t pcf8563_clkout_recalc_rate(struct rt_clk_cell *cell, rt_ubase_t parent_rate)
+{
+    rt_err_t err;
+    rt_uint8_t buf;
+    struct pcf8563_rtc *pcf8563 = raw_to_pcf8563_clkout(cell->clk_np);
+
+    err = pcf8563_read_block_data(pcf8563, PCF8563_REG_CLKO, 1, &buf);
+
+    if (err)
+    {
+        return err;
+    }
+
+    buf &= PCF8563_REG_CLKO_F_MASK;
+
+    return clkout_rates[buf];
+}
+
+static rt_base_t pcf8563_clkout_round_rate(struct rt_clk_cell *cell, rt_ubase_t drate,
+        rt_ubase_t *prate)
+{
+    for (int i = 0; i < RT_ARRAY_SIZE(clkout_rates); ++i)
+    {
+        if (clkout_rates[i] <= drate)
+        {
+            return clkout_rates[i];
+        }
+    }
+
+    return 0;
+}
+
+static rt_err_t pcf8563_clkout_set_rate(struct rt_clk_cell *cell, rt_ubase_t rate,
+        rt_ubase_t parent_rate)
+{
+    rt_err_t err;
+    rt_uint8_t buf;
+    struct pcf8563_rtc *pcf8563 = raw_to_pcf8563_clkout(cell->clk_np);
+
+    err = pcf8563_read_block_data(pcf8563, PCF8563_REG_CLKO, 1, &buf);
+
+    if (err)
+    {
+        return err;
+    }
+
+    for (int i = 0; i < RT_ARRAY_SIZE(clkout_rates); ++i)
+    {
+        if (clkout_rates[i] == rate)
+        {
+            buf &= ~PCF8563_REG_CLKO_F_MASK;
+            buf |= i;
+
+            return pcf8563_write_block_data(pcf8563, PCF8563_REG_CLKO, 1, &buf);
+        }
+    }
+
+
+    return -RT_EINVAL;
+}
+
+static const struct rt_clk_ops pcf8563_clkout_ops =
+{
+    .prepare = pcf8563_clkout_prepare,
+    .unprepare = pcf8563_clkout_unprepare,
+    .is_prepared = pcf8563_clkout_is_prepared,
+    .recalc_rate = pcf8563_clkout_recalc_rate,
+    .round_rate = pcf8563_clkout_round_rate,
+    .set_rate = pcf8563_clkout_set_rate,
+};
+
+static void pcf8563_clkout_register_clk(struct pcf8563_rtc *pcf8563, struct rt_device *dev)
+{
+    rt_uint8_t buf = 0;
+
+    /* Disable the clkout output */
+    if (pcf8563_write_block_data(pcf8563, PCF8563_REG_CLKO, 1, &buf))
+    {
+        return;
+    }
+
+    pcf8563->cells[0] = &pcf8563->cell;
+    pcf8563->cell.name = "pcf8563-clkout";
+    pcf8563->cell.ops = &pcf8563_clkout_ops;
+    rt_dm_dev_prop_read_string(dev, "clock-output-names", &pcf8563->cell.name);
+
+    pcf8563->clkout_hw.dev = dev;
+    pcf8563->clkout_hw.cells_nr = 1;
+    pcf8563->clkout_hw.cells = pcf8563->cells;
+
+    if (rt_clk_register(&pcf8563->clkout_hw))
+    {
+        return;
+    }
+}
+
+static rt_err_t pcf8563_rtc_probe(struct rt_i2c_client *client)
+{
+    rt_err_t err;
+    rt_uint8_t buf;
+    const char *dev_name;
+    struct rt_device *dev = &client->parent;
+    struct pcf8563_rtc *pcf8563 = rt_calloc(1, sizeof(*pcf8563));
+
+    if (!pcf8563)
+    {
+        return -RT_ENOMEM;
+    }
+
+    pcf8563->irq = rt_dm_dev_get_irq(dev, 0);
+    pcf8563->client = client;
+
+    /* Set timer to lowest frequency to save power (ref Haoyu datasheet) */
+    buf = PCF8563_TMRC_1_60;
+
+    if ((err = pcf8563_write_block_data(pcf8563, PCF8563_REG_TMRC, 1, &buf)))
+    {
+        LOG_E("Write %s error", "PCF8563_REG_TMRC");
+        goto _fail;
+    }
+
+    /* Clear flags and disable interrupts */
+    buf = 0;
+
+    if ((err = pcf8563_write_block_data(pcf8563, PCF8563_REG_ST2, 1, &buf)))
+    {
+        LOG_E("Write %s error", "PCF8563_REG_ST2");
+        goto _fail;
+    }
+
+    if (pcf8563->irq >= 0)
+    {
+        pcf8563->irq_thread = rt_thread_create("rtc-pcf8563", &pcf8563_rtc_thread_isr,
+                pcf8563, DM_THREAD_STACK_SIZE, RT_THREAD_PRIORITY_MAX / 2, 10);
+
+        if (!pcf8563->irq_thread)
+        {
+            err = -RT_ERROR;
+            LOG_E("Create RTC IRQ thread fail");
+            goto _fail;
+        }
+
+        rt_thread_startup(pcf8563->irq_thread);
+
+        rt_hw_interrupt_install(pcf8563->irq, pcf8563_rtc_isr, pcf8563, "rtc-pcf8563");
+        rt_hw_interrupt_umask(pcf8563->irq);
+    }
+
+    dev->user_data = pcf8563;
+
+    pcf8563->parent.type = RT_Device_Class_RTC;
+#ifdef RT_USING_DEVICE_OPS
+    pcf8563->parent.ops = &pcf8563_rtc_ops;
+#else
+    pcf8563->parent.control = pcf8563_rtc_control;
+#endif
+
+    rtc_dev_set_name(&pcf8563->parent);
+    dev_name = rt_dm_dev_get_name(&pcf8563->parent);
+    rt_device_register(&pcf8563->parent, dev_name, RT_DEVICE_FLAG_RDWR);
+
+    pcf8563_clkout_register_clk(pcf8563, dev);
+
+    return RT_EOK;
+
+_fail:
+    if (pcf8563->irq_thread)
+    {
+        rt_thread_delete(pcf8563->irq_thread);
+    }
+    rt_free(pcf8563);
+
+    return err;
+}
+
+static rt_err_t pcf8563_rtc_remove(struct rt_i2c_client *client)
+{
+    struct pcf8563_rtc *pcf8563 = client->parent.user_data;
+
+    rt_dm_dev_unbind_fwdata(&client->parent, RT_NULL);
+
+    if (pcf8563->irq >= 0)
+    {
+        if (pcf8563->wkalarm.enable)
+        {
+            pcf8563_set_alarm_mode(pcf8563, RT_FALSE);
+        }
+
+        rt_hw_interrupt_mask(pcf8563->irq);
+        rt_pic_detach_irq(pcf8563->irq, pcf8563);
+
+        rt_thread_delete(pcf8563->irq_thread);
+    }
+
+    if (pcf8563->cells[0])
+    {
+        rt_clk_unregister(&pcf8563->clkout_hw);
+    }
+
+    rt_device_unregister(&pcf8563->parent);
+
+    rt_free(pcf8563);
+
+    return RT_EOK;
+}
+
+static const struct rt_i2c_device_id pcf8563_rtc_ids[] =
+{
+    { .name = "pcf8563" },
+    { .name = "rtc8564" },
+    { .name = "pca8565" },
+    { /* sentinel */ },
+};
+
+static const struct rt_ofw_node_id pcf8563_rtc_ofw_ids[] =
+{
+    { .compatible = "nxp,pcf8563" },
+    { .compatible = "epson,rtc8564" },
+    { .compatible = "microcrystal,rv8564" },
+    { .compatible = "nxp,pca8565" },
+    { /* sentinel */ },
+};
+
+static struct rt_i2c_driver pcf8563_rtc_driver =
+{
+    .ids = pcf8563_rtc_ids,
+    .ofw_ids = pcf8563_rtc_ofw_ids,
+
+    .probe = pcf8563_rtc_probe,
+    .remove = pcf8563_rtc_remove,
+};
+RT_I2C_DRIVER_EXPORT(pcf8563_rtc_driver);

+ 294 - 0
components/drivers/rtc/rtc-pl031.c

@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2022-11-26     GuEe-GUI     first version
+ */
+
+#include "rtc_dm.h"
+
+#define PL031_DR        0x00    /* data read register */
+#define PL031_MR        0x04    /* match register */
+#define PL031_LR        0x08    /* data load register */
+#define PL031_CR        0x0c    /* control register */
+#define PL031_IMSC      0x10    /* interrupt mask and set register */
+#define PL031_RIS       0x14    /* raw interrupt status register */
+#define PL031_MIS       0x18    /* masked interrupt status register */
+#define PL031_ICR       0x1c    /* interrupt clear register */
+
+#define PL031_CR_OPEN   1
+#define PL031_CR_CLOSE  0
+#define PL031_BIT_AI    RT_BIT(0)   /* Alarm interrupt bit */
+#define PL031_BIT_PI    RT_BIT(1)   /* Periodic interrupt bit. ST variants only. */
+
+struct pl031
+{
+    struct rt_device parent;
+
+    int irq;
+    void *base;
+    struct rt_clk *pclk;
+
+    struct rt_rtc_wkalarm wkalarm;
+};
+
+#define raw_to_pl031(raw) rt_container_of(raw, struct pl031, parent)
+
+rt_inline rt_uint32_t pl031_read(struct pl031 *pl031, int offset)
+{
+    return HWREG32(pl031->base + offset);
+}
+
+rt_inline void pl031_write(struct pl031 *pl031, int offset, rt_uint32_t value)
+{
+    HWREG32(pl031->base + offset) = value;
+}
+
+static void pl031_isr(int irqno, void *param)
+{
+    struct pl031 *pl031 = param;
+    rt_uint32_t rtcmis = pl031_read(pl031, PL031_MIS);
+
+    if (rtcmis & PL031_BIT_AI)
+    {
+        pl031_write(pl031, PL031_ICR, PL031_BIT_AI);
+
+        rt_alarm_update(&pl031->parent, 1);
+    }
+}
+
+static void pl031_get_secs(struct pl031 *pl031, time_t *sec)
+{
+    *(rt_uint32_t *)sec = pl031_read(pl031, PL031_DR);
+}
+
+static void pl031_set_secs(struct pl031 *pl031, time_t *sec)
+{
+    pl031_write(pl031, PL031_LR, *(rt_uint32_t *)sec);
+}
+
+static void pl031_get_alarm(struct pl031 *pl031, struct rt_rtc_wkalarm *alarm)
+{
+    *alarm = pl031->wkalarm;
+
+    alarm->enable = pl031_read(pl031, PL031_IMSC) & PL031_BIT_AI;
+}
+
+static void pl031_set_alarm(struct pl031 *pl031, struct rt_rtc_wkalarm *alarm)
+{
+    rt_uint32_t imsc, time;
+    struct rt_rtc_wkalarm *wkalarm = &pl031->wkalarm;
+
+    wkalarm->enable = alarm->enable;
+    wkalarm->tm_hour = alarm->tm_hour;
+    wkalarm->tm_min = alarm->tm_min;
+    wkalarm->tm_sec = alarm->tm_sec;
+
+    time = pl031_read(pl031, PL031_DR);
+
+    /* Get alarm time */
+    time += alarm->tm_hour * 3600 + alarm->tm_min * 60 + alarm->tm_sec;
+
+    pl031_write(pl031, PL031_MR, time);
+
+    /* Clear any pending alarm interrupts. */
+    pl031_write(pl031, PL031_ICR, PL031_BIT_AI);
+
+    imsc = pl031_read(pl031, PL031_IMSC);
+
+    if (alarm->enable)
+    {
+        pl031_write(pl031, PL031_IMSC, imsc | PL031_BIT_AI);
+    }
+    else
+    {
+        pl031_write(pl031, PL031_IMSC, imsc & ~PL031_BIT_AI);
+    }
+}
+
+static void pl031_get_timeval(struct pl031 *pl031, struct timeval *tv)
+{
+    tv->tv_sec = pl031_read(pl031, PL031_DR);
+}
+
+static void pl031_set_timeval(struct pl031 *pl031, struct timeval *tv)
+{
+    pl031_write(pl031, PL031_LR, *(rt_uint32_t *)&tv->tv_sec);
+}
+
+static rt_err_t pl031_init(rt_device_t dev)
+{
+    struct pl031 *pl031 = raw_to_pl031(dev);
+
+    pl031_write(pl031, PL031_CR, PL031_CR_OPEN);
+
+    return RT_EOK;
+}
+
+static rt_err_t pl031_control(rt_device_t dev, int cmd, void *args)
+{
+    rt_err_t err = RT_EOK;
+    struct pl031 *pl031 = raw_to_pl031(dev);
+
+    if (!args)
+    {
+        return -RT_EINVAL;
+    }
+
+    switch (cmd)
+    {
+    case RT_DEVICE_CTRL_RTC_GET_TIME:
+        pl031_get_secs(pl031, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_TIME:
+        pl031_set_secs(pl031, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_GET_TIMEVAL:
+        pl031_get_timeval(pl031, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_TIMEVAL:
+        pl031_set_timeval(pl031, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_GET_ALARM:
+        pl031_get_alarm(pl031, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_ALARM:
+        pl031_set_alarm(pl031, args);
+        break;
+
+    default:
+        err = -RT_EINVAL;
+        break;
+    }
+
+    return err;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops pl031_rtc_ops =
+{
+    .init = pl031_init,
+    .control = pl031_control,
+};
+#endif
+
+static rt_err_t pl031_probe(struct rt_platform_device *pdev)
+{
+    rt_err_t err = RT_EOK;
+    const char *dev_name;
+    struct rt_device *dev = &pdev->parent;
+    struct pl031 *pl031 = rt_calloc(1, sizeof(*pl031));
+
+    if (!pl031)
+    {
+        return -RT_ENOMEM;
+    }
+
+    pl031->base = rt_dm_dev_iomap(dev, 0);
+
+    if (!pl031->base)
+    {
+        err = -RT_EIO;
+
+        goto _fail;
+    }
+
+    pl031->irq = rt_dm_dev_get_irq(dev, 0);
+
+    if (pl031->irq < 0)
+    {
+        err = pl031->irq;
+
+        goto _fail;
+    }
+
+    pl031->pclk = rt_clk_get_by_name(dev, "apb_pclk");
+
+    if (rt_is_err(pl031->pclk))
+    {
+        err = rt_ptr_err(pl031->pclk);
+
+        goto _fail;
+    }
+
+    if ((err = rt_clk_prepare_enable(pl031->pclk)))
+    {
+        goto _fail;
+    }
+
+    dev->user_data = pl031;
+
+    pl031->parent.type = RT_Device_Class_RTC;
+#ifdef RT_USING_DEVICE_OPS
+    pl031->parent.ops = &pl031_rtc_ops;
+#else
+    pl031->parent.init = pl031_init;
+    pl031->parent.control = pl031_control;
+#endif
+
+    rtc_dev_set_name(&pl031->parent);
+    dev_name = rt_dm_dev_get_name(&pl031->parent);
+    rt_device_register(&pl031->parent, dev_name, RT_DEVICE_FLAG_RDWR);
+
+    rt_hw_interrupt_install(pl031->irq, pl031_isr, pl031, "rtc-pl031");
+    rt_hw_interrupt_umask(pl031->irq);
+
+    return RT_EOK;
+
+_fail:
+    if (pl031->base)
+    {
+        rt_iounmap(pl031->base);
+    }
+
+    if (!rt_is_err_or_null(pl031->pclk))
+    {
+        rt_clk_disable_unprepare(pl031->pclk);
+        rt_clk_put(pl031->pclk);
+    }
+
+    rt_free(pl031);
+
+    return err;
+}
+
+static rt_err_t pl031_remove(struct rt_platform_device *pdev)
+{
+    struct pl031 *pl031 = pdev->parent.user_data;
+
+    rt_hw_interrupt_mask(pl031->irq);
+    rt_pic_detach_irq(pl031->irq, pl031);
+
+    rt_device_unregister(&pl031->parent);
+
+    rt_clk_disable_unprepare(pl031->pclk);
+    rt_clk_put(pl031->pclk);
+
+    rt_free(pl031);
+
+    return RT_EOK;
+}
+
+static const struct rt_ofw_node_id pl031_ofw_ids[] =
+{
+    { .compatible = "arm,pl031" },
+    { /* sentinel */ }
+};
+
+static struct rt_platform_driver pl031_driver =
+{
+    .name = "rtc-pl031",
+    .ids = pl031_ofw_ids,
+
+    .probe = pl031_probe,
+    .remove = pl031_remove,
+};
+RT_PLATFORM_DRIVER_EXPORT(pl031_driver);

+ 637 - 0
components/drivers/rtc/rtc-rx8010.c

@@ -0,0 +1,637 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2022-11-26     GuEe-GUI     first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "rtc.rx8010"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include "rtc_dm.h"
+
+#define RX8010_SEC              0x10
+#define RX8010_MIN              0x11
+#define RX8010_HOUR             0x12
+#define RX8010_WDAY             0x13
+#define RX8010_MDAY             0x14
+#define RX8010_MONTH            0x15
+#define RX8010_YEAR             0x16
+#define RX8010_RESV17           0x17
+#define RX8010_ALMIN            0x18
+#define RX8010_ALHOUR           0x19
+#define RX8010_ALWDAY           0x1a
+#define RX8010_TCOUNT0          0x1b
+#define RX8010_TCOUNT1          0x1c
+#define RX8010_EXT              0x1d
+#define RX8010_FLAG             0x1e
+#define RX8010_CTRL             0x1f
+/* 0x20 to 0x2F are user registers */
+#define RX8010_RESV30           0x30
+#define RX8010_RESV31           0x31
+#define RX8010_IRQ              0x32
+
+#define RX8010_EXT_WADA         RT_BIT(3)
+
+#define RX8010_FLAG_VLF         RT_BIT(1)
+#define RX8010_FLAG_AF          RT_BIT(3)
+#define RX8010_FLAG_TF          RT_BIT(4)
+#define RX8010_FLAG_UF          RT_BIT(5)
+
+#define RX8010_CTRL_AIE         RT_BIT(3)
+#define RX8010_CTRL_UIE         RT_BIT(5)
+#define RX8010_CTRL_STOP        RT_BIT(6)
+#define RX8010_CTRL_TEST        RT_BIT(7)
+
+#define RX8010_ALARM_AE         RT_BIT(7)
+
+struct rx8010_rtc
+{
+    struct rt_device parent;
+
+    int irq;
+    rt_uint8_t ctrlreg;
+
+    struct rt_i2c_client *client;
+    struct rt_thread *irq_thread;
+
+    struct rt_rtc_wkalarm wkalarm;
+};
+
+#define raw_to_rx8010_rtc(raw) rt_container_of(raw, struct rx8010_rtc, parent)
+
+static rt_err_t rx8010_rtc_write(struct rx8010_rtc *rx8010,
+        rt_uint8_t reg, rt_uint8_t value)
+{
+    rt_int32_t res;
+    struct rt_i2c_msg msg[1];
+    rt_uint8_t data[sizeof(reg) + sizeof(value)] = { reg };
+    struct rt_i2c_client *client = rx8010->client;
+
+    rt_memcpy(&data[sizeof(reg)], &value, sizeof(value));
+
+    msg[0].buf = data;
+    msg[0].addr = client->client_addr;
+    msg[0].len = sizeof(data);
+    msg[0].flags = RT_I2C_WR;
+
+    res = rt_i2c_transfer(client->bus, msg, 1);
+
+    return res > 0 ? RT_EOK : res;
+}
+
+static rt_err_t rx8010_rtc_read(struct rx8010_rtc *rx8010,
+        rt_uint8_t reg, rt_uint8_t *values)
+{
+    rt_int32_t res;
+    struct rt_i2c_msg msg[2];
+    struct rt_i2c_client *client = rx8010->client;
+
+    msg[0].buf = &reg;
+    msg[0].addr = client->client_addr;
+    msg[0].len = sizeof(reg);
+    msg[0].flags = RT_I2C_WR;
+
+    msg[1].buf = (rt_uint8_t *)values;
+    msg[1].addr = client->client_addr;
+    msg[1].len = sizeof(*values);
+    msg[1].flags = RT_I2C_RD;
+
+    res = rt_i2c_transfer(client->bus, msg, 2);
+
+    return res > 0 ? RT_EOK : res;
+}
+
+static rt_err_t rx8010_rtc_set_bit(struct rx8010_rtc *rx8010,
+        rt_uint8_t reg, rt_uint8_t bit)
+{
+    rt_err_t err;
+    rt_uint8_t value;
+
+    if ((err = rx8010_rtc_read(rx8010, reg, &value)))
+    {
+        return err;
+    }
+
+    return rx8010_rtc_write(rx8010, reg, value | bit);
+}
+
+static rt_err_t rx8010_rtc_clear_bit(struct rx8010_rtc *rx8010,
+        rt_uint8_t reg, rt_uint8_t bit)
+{
+    rt_err_t err;
+    rt_uint8_t value;
+
+    if ((err = rx8010_rtc_read(rx8010, reg, &value)))
+    {
+        return err;
+    }
+
+    return rx8010_rtc_write(rx8010, reg, value & ~bit);
+}
+
+static rt_err_t rx8010_rtc_read_time(struct rx8010_rtc *rx8010, time_t *sec)
+{
+    struct tm tm;
+    rt_err_t err;
+    rt_uint8_t flagreg, date[RX8010_YEAR - RX8010_SEC + 1];
+
+    if ((err = rx8010_rtc_read(rx8010, RX8010_FLAG, &flagreg)))
+    {
+        return err;
+    }
+
+    if (flagreg & RX8010_FLAG_VLF)
+    {
+        LOG_W("Frequency stop detected");
+
+        return -RT_EINVAL;
+    }
+
+    for (int i = 0; i < sizeof(date); ++i)
+    {
+        if ((err = rx8010_rtc_read(rx8010, RX8010_SEC + i, &date[i])))
+        {
+            return err;
+        }
+    }
+
+    tm.tm_sec = rt_bcd2bin(date[RX8010_SEC - RX8010_SEC] & 0x7f);
+    tm.tm_min = rt_bcd2bin(date[RX8010_MIN - RX8010_SEC] & 0x7f);
+    tm.tm_hour = rt_bcd2bin(date[RX8010_HOUR - RX8010_SEC] & 0x3f);
+    tm.tm_mday = rt_bcd2bin(date[RX8010_MDAY - RX8010_SEC] & 0x3f);
+    tm.tm_mon = rt_bcd2bin(date[RX8010_MONTH - RX8010_SEC] & 0x1f) - 1;
+    tm.tm_year = rt_bcd2bin(date[RX8010_YEAR - RX8010_SEC]) + 100;
+    tm.tm_wday = __rt_ffs(date[RX8010_WDAY - RX8010_SEC] & 0x7f);
+
+    *sec = timegm(&tm);
+
+    return RT_EOK;
+}
+
+static rt_err_t rx8010_rtc_set_time(struct rx8010_rtc *rx8010, time_t *sec)
+{
+    rt_err_t err;
+    struct tm *tm;
+    rt_uint8_t date[RX8010_YEAR - RX8010_SEC + 1];
+
+    /* Set STOP bit before changing clock/calendar */
+    if ((err = rx8010_rtc_set_bit(rx8010, RX8010_CTRL, RX8010_CTRL_STOP)))
+    {
+        return err;
+    }
+
+    tm = localtime(sec);
+
+    date[RX8010_SEC - RX8010_SEC] = rt_bin2bcd(tm->tm_sec);
+    date[RX8010_MIN - RX8010_SEC] = rt_bin2bcd(tm->tm_min);
+    date[RX8010_HOUR - RX8010_SEC] = rt_bin2bcd(tm->tm_hour);
+    date[RX8010_MDAY - RX8010_SEC] = rt_bin2bcd(tm->tm_mday);
+    date[RX8010_MONTH - RX8010_SEC] = rt_bin2bcd(tm->tm_mon + 1);
+    date[RX8010_YEAR - RX8010_SEC] = rt_bin2bcd(tm->tm_year - 100);
+    date[RX8010_WDAY - RX8010_SEC] = rt_bin2bcd(1 << tm->tm_wday);
+
+    for (int i = 0; i < sizeof(date); ++i)
+    {
+        if ((err = rx8010_rtc_write(rx8010, RX8010_SEC + i, date[i])))
+        {
+            return err;
+        }
+    }
+
+    /* Clear STOP bit after changing clock/calendar */
+    if ((err = rx8010_rtc_clear_bit(rx8010, RX8010_CTRL, RX8010_CTRL_STOP)))
+    {
+        return err;
+    }
+
+    if ((err = rx8010_rtc_clear_bit(rx8010, RX8010_FLAG, RX8010_FLAG_VLF)))
+    {
+        return err;
+    }
+
+    return RT_EOK;
+}
+
+static rt_err_t rx8010_rtc_read_alarm(struct rx8010_rtc *rx8010,
+        struct rt_rtc_wkalarm *alarm)
+{
+    rt_err_t err;
+    rt_uint8_t alarmvals[3], flagreg;
+
+    for (int i = 0; i < RT_ARRAY_SIZE(alarmvals); ++i)
+    {
+        if ((err = rx8010_rtc_read(rx8010, RX8010_ALMIN + i, &alarmvals[i])))
+        {
+            return err;
+        }
+    }
+
+    if ((err = rx8010_rtc_read(rx8010, RX8010_FLAG, &flagreg)))
+    {
+        return err;
+    }
+
+    alarm->tm_sec = 0;
+    alarm->tm_min = rt_bcd2bin(alarmvals[0] & 0x7f);
+    alarm->tm_hour = rt_bcd2bin(alarmvals[1] & 0x3f);
+
+    if (!(alarmvals[2] & RX8010_ALARM_AE))
+    {
+        alarm->tm_mday = rt_bcd2bin(alarmvals[2] & 0x7f);
+    }
+
+    alarm->enable = !!(rx8010->ctrlreg & RX8010_CTRL_AIE);
+
+    return RT_EOK;
+}
+
+static rt_err_t rx8010_alarm_irq_enable(struct rx8010_rtc *rx8010, rt_bool_t enabled)
+{
+    rt_err_t err;
+    rt_uint8_t ctrl;
+
+    ctrl = rx8010->ctrlreg;
+
+    if (enabled)
+    {
+        ctrl |= RX8010_CTRL_AIE | RX8010_CTRL_UIE;
+    }
+    else
+    {
+        ctrl &= ~(RX8010_CTRL_UIE | RX8010_CTRL_AIE);
+    }
+
+    if ((err = rx8010_rtc_clear_bit(rx8010, RX8010_FLAG, RX8010_FLAG_AF)))
+    {
+        return err;
+    }
+
+    if (ctrl != rx8010->ctrlreg)
+    {
+        rx8010->ctrlreg = ctrl;
+
+        if ((err = rx8010_rtc_write(rx8010, RX8010_CTRL, rx8010->ctrlreg)))
+        {
+            return err;
+        }
+    }
+
+    return RT_EOK;
+}
+
+static rt_err_t rx8010_rtc_set_alarm(struct rx8010_rtc *rx8010,
+        struct rt_rtc_wkalarm *alarm)
+{
+    rt_err_t err;
+    rt_uint8_t alarmvals[3];
+    struct rt_rtc_wkalarm *wkalarm = &rx8010->wkalarm;
+
+    if (rx8010->ctrlreg & (RX8010_CTRL_AIE | RX8010_CTRL_UIE))
+    {
+        rx8010->ctrlreg &= ~(RX8010_CTRL_AIE | RX8010_CTRL_UIE);
+
+        if ((err = rx8010_rtc_write(rx8010, RX8010_CTRL, rx8010->ctrlreg)))
+        {
+            return err;
+        }
+    }
+
+    if ((err = rx8010_rtc_clear_bit(rx8010, RX8010_FLAG, RX8010_FLAG_AF)))
+    {
+        return err;
+    }
+
+    alarmvals[0] = rt_bin2bcd(alarm->tm_min);
+    alarmvals[1] = rt_bin2bcd(alarm->tm_hour);
+    alarmvals[2] = rt_bin2bcd(alarm->tm_mday);
+
+    for (int i = 0; i < RT_ARRAY_SIZE(alarmvals) - 1; ++i)
+    {
+        if ((err = rx8010_rtc_write(rx8010, RX8010_ALMIN + i, alarmvals[i])))
+        {
+            return err;
+        }
+    }
+
+    if ((err = rx8010_rtc_clear_bit(rx8010, RX8010_EXT, RX8010_EXT_WADA)))
+    {
+        return err;
+    }
+
+    if (alarmvals[2] == 0)
+    {
+        alarmvals[2] |= RX8010_ALARM_AE;
+    }
+
+    if ((err = rx8010_rtc_write(rx8010, RX8010_ALWDAY, alarmvals[2])))
+    {
+        return err;
+    }
+
+    if (alarm->enable)
+    {
+        rx8010->ctrlreg |= RX8010_CTRL_UIE | RX8010_CTRL_AIE;
+
+        if ((err = rx8010_rtc_write(rx8010, RX8010_CTRL, rx8010->ctrlreg)))
+        {
+            return err;
+        }
+    }
+
+    if (!(err = rx8010_alarm_irq_enable(rx8010, alarm->enable)))
+    {
+        wkalarm->enable = alarm->enable;
+        wkalarm->tm_hour = alarm->tm_hour;
+        wkalarm->tm_min = alarm->tm_min;
+        wkalarm->tm_sec = alarm->tm_sec;
+    }
+
+    return err;
+}
+
+static rt_err_t rx8010_rtc_control(rt_device_t dev, int cmd, void *args)
+{
+    rt_err_t err = RT_EOK;
+    struct rx8010_rtc *rx8010 = raw_to_rx8010_rtc(dev);
+
+    if (!args)
+    {
+        return -RT_EINVAL;
+    }
+
+    switch (cmd)
+    {
+    case RT_DEVICE_CTRL_RTC_GET_TIME:
+        err = rx8010_rtc_read_time(rx8010, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_TIME:
+        err = rx8010_rtc_set_time(rx8010, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_GET_TIMEVAL:
+        err = rx8010_rtc_read_time(rx8010, (time_t *)&((struct timeval *)args)->tv_sec);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_TIMEVAL:
+        err = rx8010_rtc_set_time(rx8010, (time_t *)&((struct timeval *)args)->tv_sec);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_GET_ALARM:
+        err = rx8010_rtc_read_alarm(rx8010, args);
+        break;
+
+    case RT_DEVICE_CTRL_RTC_SET_ALARM:
+        err = rx8010_rtc_set_alarm(rx8010, args);
+        break;
+
+    default:
+        err = -RT_EINVAL;
+        break;
+    }
+
+    return err;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops rx8010_rtc_ops =
+{
+    .control = rx8010_rtc_control,
+};
+#endif
+
+static void rx8010_rtc_thread_isr(void *param)
+{
+    rt_err_t err;
+    rt_uint8_t flagreg;
+    struct rx8010_rtc *rx8010 = param;
+
+    while (RT_TRUE)
+    {
+        rt_thread_suspend(rx8010->irq_thread);
+        rt_schedule();
+
+        if ((err = rx8010_rtc_read(rx8010, RX8010_FLAG, &flagreg)))
+        {
+            LOG_E("Read flag error = %s", rt_strerror(err));
+            continue;
+        }
+
+        if (flagreg & RX8010_FLAG_VLF)
+        {
+            LOG_W("Frequency stop detected");
+        }
+
+        if (flagreg & RX8010_FLAG_TF)
+        {
+            flagreg &= ~RX8010_FLAG_TF;
+            rt_alarm_update(&rx8010->parent, 1);
+        }
+
+        if (flagreg & RX8010_FLAG_AF)
+        {
+            flagreg &= ~RX8010_FLAG_AF;
+            rt_alarm_update(&rx8010->parent, 1);
+        }
+
+        if (flagreg & RX8010_FLAG_UF)
+        {
+            flagreg &= ~RX8010_FLAG_UF;
+            rt_alarm_update(&rx8010->parent, 1);
+        }
+
+        if ((err = rx8010_rtc_write(rx8010, RX8010_FLAG, flagreg)))
+        {
+            LOG_E("Write flag error = %s", rt_strerror(err));
+        }
+    }
+}
+
+static void rx8010_rtc_isr(int irqno, void *param)
+{
+    struct rx8010_rtc *rx8010 = param;
+
+    rt_thread_resume(rx8010->irq_thread);
+}
+
+static rt_err_t rx8010_init(struct rx8010_rtc *rx8010)
+{
+    rt_err_t err;
+    rt_uint8_t ctrl[2];
+    int need_clear = 0;
+
+    /* Initialize reserved registers as specified in datasheet */
+    if ((err = rx8010_rtc_write(rx8010, RX8010_RESV17, 0xd8)))
+    {
+        return err;
+    }
+
+    if ((err = rx8010_rtc_write(rx8010, RX8010_RESV30, 0x00)))
+    {
+        return err;
+    }
+
+    if ((err = rx8010_rtc_write(rx8010, RX8010_RESV31, 0x08)))
+    {
+        return err;
+    }
+
+    if ((err = rx8010_rtc_write(rx8010, RX8010_IRQ, 0x00)))
+    {
+        return err;
+    }
+
+    err |= rx8010_rtc_read(rx8010, RX8010_FLAG, &ctrl[0]);
+    err |= rx8010_rtc_read(rx8010, RX8010_FLAG + 1, &ctrl[1]);
+
+    if (err)
+    {
+        return err;
+    }
+
+    if (ctrl[0] & RX8010_FLAG_VLF)
+    {
+        LOG_W("Frequency stop was detected");
+    }
+
+    if (ctrl[0] & RX8010_FLAG_AF)
+    {
+        LOG_W("Alarm was detected");
+        need_clear = 1;
+    }
+
+    if (ctrl[0] & (RX8010_FLAG_TF | RX8010_FLAG_UF))
+    {
+        need_clear = 1;
+    }
+
+    if (need_clear)
+    {
+        ctrl[0] &= ~(RX8010_FLAG_AF | RX8010_FLAG_TF | RX8010_FLAG_UF);
+
+        if ((err = rx8010_rtc_write(rx8010, RX8010_FLAG, ctrl[0])))
+        {
+            return err;
+        }
+    }
+
+    rx8010->ctrlreg = ctrl[1] & ~RX8010_CTRL_TEST;
+
+    return RT_EOK;
+}
+
+static rt_err_t rx8010_rtc_probe(struct rt_i2c_client *client)
+{
+    rt_err_t err;
+    const char *dev_name;
+    struct rt_device *dev = &client->parent;
+    struct rx8010_rtc *rx8010 = rt_calloc(1, sizeof(*rx8010));
+
+    if (!rx8010)
+    {
+        return -RT_ENOMEM;
+    }
+
+    rx8010->client = client;
+
+    if ((err = rx8010_init(rx8010)))
+    {
+        goto _fail;
+    }
+
+    rx8010->irq = rt_dm_dev_get_irq(dev, 0);
+
+    if (rx8010->irq >= 0)
+    {
+        rx8010->irq_thread = rt_thread_create("rtc-rx8010", &rx8010_rtc_thread_isr,
+                rx8010, DM_THREAD_STACK_SIZE, RT_THREAD_PRIORITY_MAX / 2, 10);
+
+        if (!rx8010->irq_thread)
+        {
+            err = -RT_ERROR;
+            LOG_E("Create RTC IRQ thread fail");
+            goto _fail;
+        }
+
+        rt_thread_startup(rx8010->irq_thread);
+
+        rt_hw_interrupt_install(rx8010->irq, rx8010_rtc_isr, rx8010, "rtc-rx8010");
+        rt_hw_interrupt_umask(rx8010->irq);
+    }
+
+    dev->user_data = rx8010;
+
+    rx8010->parent.type = RT_Device_Class_RTC;
+#ifdef RT_USING_DEVICE_OPS
+    rx8010->parent.ops = &rx8010_rtc_ops;
+#else
+    rx8010->parent.control = rx8010_rtc_control;
+#endif
+
+    rtc_dev_set_name(&rx8010->parent);
+    dev_name = rt_dm_dev_get_name(&rx8010->parent);
+    rt_device_register(&rx8010->parent, dev_name, RT_DEVICE_FLAG_RDWR);
+
+    return RT_EOK;
+
+_fail:
+    if (rx8010->irq_thread)
+    {
+        rt_thread_delete(rx8010->irq_thread);
+    }
+
+    rt_free(rx8010);
+
+    return err;
+}
+
+static rt_err_t rx8010_rtc_remove(struct rt_i2c_client *client)
+{
+    struct rx8010_rtc *rx8010 = client->parent.user_data;
+
+    rx8010_rtc_set_bit(rx8010, RX8010_CTRL, RX8010_CTRL_STOP);
+
+    if (rx8010->irq >= 0)
+    {
+        rt_hw_interrupt_mask(rx8010->irq);
+        rt_pic_detach_irq(rx8010->irq, rx8010);
+
+        rt_thread_delete(rx8010->irq_thread);
+    }
+
+    rt_device_unregister(&rx8010->parent);
+
+    rt_free(rx8010);
+
+    return RT_EOK;
+}
+
+static const struct rt_i2c_device_id rx8010_rtc_ids[] =
+{
+    { .name = "rx8010" },
+    { /* sentinel */ },
+};
+
+static const struct rt_ofw_node_id rx8010_rtc_ofw_ids[] =
+{
+    { .compatible = "epson,rx8010" },
+    { /* sentinel */ },
+};
+
+static struct rt_i2c_driver rx8010_rtc_driver =
+{
+    .ids = rx8010_rtc_ids,
+    .ofw_ids = rx8010_rtc_ofw_ids,
+
+    .probe = rx8010_rtc_probe,
+    .remove = rx8010_rtc_remove,
+};
+RT_I2C_DRIVER_EXPORT(rx8010_rtc_driver);

+ 61 - 0
components/drivers/rtc/rtc_dm.c

@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2022-12-06     GuEe-GUI     first version
+ */
+
+#include <rtatomic.h>
+#include "rtc_dm.h"
+
+#define DBG_TAG "rtc.dm"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+int rtc_dev_set_name(struct rt_device *rtc_dev)
+{
+    int id;
+    static volatile rt_atomic_t uid = 1;
+
+    RT_ASSERT(rtc_dev != RT_NULL)
+
+    if (rt_device_find("rtc"))
+    {
+        id = (int)rt_atomic_add(&uid, 1);
+
+        return rt_dm_dev_set_name(rtc_dev, "rtc%u", id);
+    }
+    else
+    {
+        return rt_dm_dev_set_name(rtc_dev, "rtc");
+    }
+}
+
+time_t rtc_wkalarm_to_timestamp(struct rt_rtc_wkalarm *alarm)
+{
+    struct tm tm_time;
+    time_t current_time;
+
+    current_time = time(RT_NULL);
+    localtime_r(&current_time, &tm_time);
+
+    tm_time.tm_sec = alarm->tm_sec;
+    tm_time.tm_min = alarm->tm_min;
+    tm_time.tm_hour = alarm->tm_hour;
+
+    return timegm(&tm_time);
+}
+
+void rtc_timestamp_to_wkalarm(time_t timestamp, struct rt_rtc_wkalarm *alarm)
+{
+    struct tm tm_time;
+
+    localtime_r(&timestamp, &tm_time);
+
+    alarm->tm_sec = tm_time.tm_sec;
+    alarm->tm_min = tm_time.tm_min;
+    alarm->tm_hour = tm_time.tm_hour;
+}

+ 24 - 0
components/drivers/rtc/rtc_dm.h

@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2022-12-06     GuEe-GUI     first version
+ */
+
+#ifndef __RTC_DM_H__
+#define __RTC_DM_H__
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#include <sys/time.h>
+
+int rtc_dev_set_name(struct rt_device *rtc_dev);
+time_t rtc_wkalarm_to_timestamp(struct rt_rtc_wkalarm *alarm);
+void rtc_timestamp_to_wkalarm(time_t timestamp, struct rt_rtc_wkalarm *alarm);
+
+#endif /* __RTC_DM_H__ */