summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZe-Hou <[email protected]>2025-09-18 16:10:46 +0800
committerR b b666 <[email protected]>2025-09-22 22:30:20 +0800
commitbfbb3c93e777a36c886b3d11186543b310060f6f (patch)
tree054d17c80887fbc1e634e20ba191dfb9c0adde25
parent5a6acfa39b1737494a684f3f3e5405020b0055d4 (diff)
bsp: k230: add rtc driver
Requirement: The BSP for the k230 platform in the RT-Thread repository does not yet have an RTC driver. Solution: Provide RTC drivers for the k230 platform in the RT-Thread repository, implementing timekeeping and alarm functionalities, with interrupt support for the alarm feature. Signed-off-by: Ze-Hou <[email protected]>
-rw-r--r--bsp/k230/.config29
-rw-r--r--bsp/k230/board/Kconfig31
-rw-r--r--bsp/k230/drivers/interdrv/rtc/SConscript11
-rw-r--r--bsp/k230/drivers/interdrv/rtc/drv_rtc.c486
-rw-r--r--bsp/k230/drivers/interdrv/rtc/drv_rtc.h161
-rw-r--r--bsp/k230/drivers/utest/SConscript3
-rw-r--r--bsp/k230/drivers/utest/test_rtc.c217
-rw-r--r--bsp/k230/rtconfig.h8
8 files changed, 933 insertions, 13 deletions
diff --git a/bsp/k230/.config b/bsp/k230/.config
index 48d24617ee..2031a0ba16 100644
--- a/bsp/k230/.config
+++ b/bsp/k230/.config
@@ -602,6 +602,7 @@ CONFIG_RT_USING_VDSO=y
# CONFIG_PKG_USING_FREEMODBUS is not set
# CONFIG_PKG_USING_NANOPB is not set
# CONFIG_PKG_USING_WIFI_HOST_DRIVER is not set
+# CONFIG_PKG_USING_ESP_HOSTED is not set
#
# Wi-Fi
@@ -709,6 +710,7 @@ CONFIG_RT_USING_VDSO=y
# CONFIG_PKG_USING_QMODBUS is not set
# CONFIG_PKG_USING_PNET is not set
# CONFIG_PKG_USING_OPENER is not set
+# CONFIG_PKG_USING_FREEMQTT is not set
# end of IoT - internet of things
#
@@ -798,6 +800,7 @@ CONFIG_RT_USING_VDSO=y
# tools packages
#
# CONFIG_PKG_USING_CMBACKTRACE is not set
+# CONFIG_PKG_USING_MCOREDUMP is not set
# CONFIG_PKG_USING_EASYFLASH is not set
# CONFIG_PKG_USING_EASYLOGGER is not set
# CONFIG_PKG_USING_SYSTEMVIEW is not set
@@ -843,6 +846,7 @@ CONFIG_RT_USING_VDSO=y
# CONFIG_PKG_USING_ZDEBUG is not set
# CONFIG_PKG_USING_RVBACKTRACE is not set
# CONFIG_PKG_USING_HPATCHLITE is not set
+# CONFIG_PKG_USING_THREAD_METRIC is not set
# end of tools packages
#
@@ -936,6 +940,7 @@ CONFIG_RT_USING_VDSO=y
# CONFIG_PKG_USING_RMP is not set
# CONFIG_PKG_USING_R_RHEALSTONE is not set
# CONFIG_PKG_USING_HEARTBEAT is not set
+# CONFIG_PKG_USING_MICRO_ROS_RTTHREAD_PACKAGE is not set
# end of system packages
#
@@ -1018,6 +1023,7 @@ CONFIG_RT_USING_VDSO=y
# CONFIG_PKG_USING_NRF5X_SDK is not set
# CONFIG_PKG_USING_NRFX is not set
# CONFIG_PKG_USING_NUCLEI_SDK is not set
+# CONFIG_PKG_USING_RASPBERRYPI_PICO_RP2350_SDK is not set
# CONFIG_PKG_USING_RASPBERRYPI_PICO_SDK is not set
# CONFIG_PKG_USING_MM32 is not set
@@ -1060,6 +1066,10 @@ CONFIG_RT_USING_VDSO=y
#
# HC32 DDL Drivers
#
+# CONFIG_PKG_USING_HC32F3_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_HC32F3_SERIES_DRIVER is not set
+# CONFIG_PKG_USING_HC32F4_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_HC32F4_SERIES_DRIVER is not set
# end of HC32 DDL Drivers
#
@@ -1073,6 +1083,21 @@ CONFIG_RT_USING_VDSO=y
# CONFIG_PKG_USING_NXP_IMX6UL_DRIVER is not set
# CONFIG_PKG_USING_NXP_IMXRT_DRIVER is not set
# end of NXP HAL & SDK Drivers
+
+#
+# NUVOTON Drivers
+#
+# CONFIG_PKG_USING_NUVOTON_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_NUVOTON_SERIES_DRIVER is not set
+# CONFIG_PKG_USING_NUVOTON_ARM926_LIB is not set
+# end of NUVOTON Drivers
+
+#
+# GD32 Drivers
+#
+# CONFIG_PKG_USING_GD32_ARM_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_GD32_ARM_SERIES_DRIVER is not set
+# end of GD32 Drivers
# end of HAL & SDK Drivers
#
@@ -1148,6 +1173,7 @@ CONFIG_RT_USING_VDSO=y
# CONFIG_PKG_USING_STHS34PF80 is not set
# CONFIG_PKG_USING_P3T1755 is not set
# CONFIG_PKG_USING_QMI8658 is not set
+# CONFIG_PKG_USING_ICM20948 is not set
# end of sensors drivers
#
@@ -1242,6 +1268,8 @@ CONFIG_RT_USING_VDSO=y
# CONFIG_PKG_USING_SERVO is not set
# CONFIG_PKG_USING_SEAN_WS2812B is not set
# CONFIG_PKG_USING_IC74HC165 is not set
+# CONFIG_PKG_USING_IST8310 is not set
+# CONFIG_PKG_USING_ST7789_SPI is not set
# CONFIG_PKG_USING_SPI_TOOLS is not set
# end of peripheral libraries and drivers
@@ -1589,6 +1617,7 @@ CONFIG_PKG_ZLIB_VER="latest"
#
# Drivers Configuration
#
+# CONFIG_BSP_USING_RTC is not set
# CONFIG_BSP_USING_ADC is not set
# CONFIG_BSP_USING_TS is not set
CONFIG_BSP_USING_UART=y
diff --git a/bsp/k230/board/Kconfig b/bsp/k230/board/Kconfig
index 4c88e5df88..3604e5ad81 100644
--- a/bsp/k230/board/Kconfig
+++ b/bsp/k230/board/Kconfig
@@ -1,10 +1,15 @@
menu "Drivers Configuration"
+ config BSP_USING_RTC
+ bool "Enable RTC"
+ select RT_USING_RTC
+ default n
+
config BSP_USING_ADC
bool "Enable ADC"
select RT_USING_ADC
default n
-
+
config BSP_USING_TS
bool "Enable Temperature Sensor"
select RT_USING_TS
@@ -27,15 +32,15 @@ menu "Drivers Configuration"
config BSP_USING_UART1
bool "Enable UART1"
default n
-
+
config BSP_USING_UART2
bool "Enable UART2"
default n
-
+
config BSP_USING_UART3
bool "Enable UART3"
default n
-
+
config BSP_USING_UART4
bool "Enable UART4"
default n
@@ -146,37 +151,37 @@ menu "Drivers Configuration"
menuconfig BSP_USING_PDMA
bool "Enable PDMA"
select RT_USING_PDMA
- default n
-
+ default n
+
if BSP_USING_PDMA
config BSP_USING_PDMA_CHANNEL0
bool "Enable PDMA Channel 0"
default n
-
+
config BSP_USING_PDMA_CHANNEL1
bool "Enable PDMA Channel 1"
default n
-
+
config BSP_USING_PDMA_CHANNEL2
bool "Enable PDMA Channel 2"
default n
-
+
config BSP_USING_PDMA_CHANNEL3
bool "Enable PDMA Channel 3"
default n
-
+
config BSP_USING_PDMA_CHANNEL4
bool "Enable PDMA Channel 4"
default n
-
+
config BSP_USING_PDMA_CHANNEL5
bool "Enable PDMA Channel 5"
default n
-
+
config BSP_USING_PDMA_CHANNEL6
bool "Enable PDMA Channel 6"
default n
-
+
config BSP_USING_PDMA_CHANNEL7
bool "Enable PDMA Channel 7"
default n
diff --git a/bsp/k230/drivers/interdrv/rtc/SConscript b/bsp/k230/drivers/interdrv/rtc/SConscript
new file mode 100644
index 0000000000..035ecc94ad
--- /dev/null
+++ b/bsp/k230/drivers/interdrv/rtc/SConscript
@@ -0,0 +1,11 @@
+# RT-Thread building script for RTC component
+
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.c')
+CPPPATH = [cwd]
+
+group = DefineGroup('RTC', src, depend = ['BSP_USING_RTC'], CPPPATH = CPPPATH)
+
+Return('group')
diff --git a/bsp/k230/drivers/interdrv/rtc/drv_rtc.c b/bsp/k230/drivers/interdrv/rtc/drv_rtc.c
new file mode 100644
index 0000000000..b4820dc79a
--- /dev/null
+++ b/bsp/k230/drivers/interdrv/rtc/drv_rtc.c
@@ -0,0 +1,486 @@
+/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 2006-2025 RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <rtthread.h>
+#include <rthw.h>
+#include <rtdevice.h>
+#include <riscv_io.h>
+#include <ioremap.h>
+#include <time.h>
+#include "board.h"
+#include "drv_rtc.h"
+
+#undef DBG_TAG
+#undef DBG_LVL
+#define DBG_TAG "drv_rtc"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+struct k230_rtc_dev
+{
+ struct rt_device device;
+ const char *name;
+ rt_ubase_t base;
+ size_t size;
+ int vector;
+ void (*vector_callback)(void);
+};
+
+static void pmu_isolation_rtc(void)
+{
+ /* map pwr base address */
+ volatile void *reg_pmu_pwr = rt_ioremap((void *)PWR_BASE_ADDR, PWR_IO_SIZE);
+ uint32_t *addr = (uint32_t *)(reg_pmu_pwr + 0x158); /* pmu power control register */
+ uint32_t data;
+
+ /* disable pmu isolation */
+ data = *addr;
+ data &= ~0x20;
+ *addr = data;
+ rt_iounmap(reg_pmu_pwr);
+
+ /* map pmu base address */
+ volatile void *reg_pmu = rt_ioremap((void*)PMU_BASE_ADDR, PMU_IO_SIZE);
+ addr = (uint32_t*)(reg_pmu + 0x48); /* pmu int0 to cpu register */
+ /* enable int6 int7 */
+ data = *addr;
+ data |= 0x06;
+ *addr = data;
+
+ addr = (uint32_t*)(reg_pmu + 0x4c); /* pmu int detect en register */
+ /* enable int6 rtc alarm detection and int7 rtc tick detection */
+ data = *addr;
+ data |= 0x06;
+ *addr = data;
+ rt_iounmap(reg_pmu);
+}
+
+static int rtc_year_is_leap(int year)
+{
+ return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
+}
+
+static void rtc_timer_set_clock_count_value(struct k230_rtc_dev *dev, uint16_t count)
+{
+ volatile volatile rtc_t *rtc = (rtc_t *)dev->base;
+
+ rtc->count.curr_count = count;
+ rtc->count.sum_count = 0x7FFF;
+ rtc->int_ctrl.timer_w_en = 1;
+ rt_thread_mdelay(1);
+ rtc->int_ctrl.timer_w_en = 0;
+ rtc->int_ctrl.timer_r_en = 1;
+}
+
+static void rtc_interrupt_ctrl_set(struct k230_rtc_dev *dev, rtc_interrupt_mode_t mode)
+{
+ volatile rtc_t *rtc = (rtc_t *)dev->base;
+
+ if (mode < RTC_INT_TICK_YEAR)
+ {
+ rtc->int_ctrl.year_cmp = 0;
+ rtc->int_ctrl.month_cmp = 0;
+ rtc->int_ctrl.day_cmp = 0;
+ rtc->int_ctrl.week_cmp = 0;
+ rtc->int_ctrl.hour_cmp = 0;
+ rtc->int_ctrl.minute_cmp = 0;
+ rtc->int_ctrl.second_cmp = 0;
+
+ if (mode & RTC_INT_ALARM_YEAR)
+ {
+ rtc->int_ctrl.year_cmp = 1;
+ }
+ if (mode & RTC_INT_ALARM_MONTH)
+ {
+ rtc->int_ctrl.month_cmp = 1;
+ }
+ if (mode & RTC_INT_ALARM_DAY)
+ {
+ rtc->int_ctrl.day_cmp = 1;
+ }
+ if (mode & RTC_INT_ALARM_WEEK)
+ {
+ rtc->int_ctrl.week_cmp = 1;
+ }
+ if (mode & RTC_INT_ALARM_HOUR)
+ {
+ rtc->int_ctrl.hour_cmp = 1;
+ }
+ if (mode & RTC_INT_ALARM_MINUTE)
+ {
+ rtc->int_ctrl.minute_cmp = 1;
+ }
+ if (mode & RTC_INT_ALARM_SECOND)
+ {
+ rtc->int_ctrl.second_cmp = 1;
+ }
+ rtc->int_ctrl.alarm_en = 1;
+ }
+ else
+ {
+ switch(mode)
+ {
+ case RTC_INT_TICK_YEAR:
+ rtc->int_ctrl.tick_sel = 0x8;
+ rtc->int_ctrl.tick_en = 1;
+ break;
+ case RTC_INT_TICK_MONTH:
+ rtc->int_ctrl.tick_sel = 0x7;
+ rtc->int_ctrl.tick_en = 1;
+ break;
+ case RTC_INT_TICK_DAY:
+ rtc->int_ctrl.tick_sel = 0x6;
+ rtc->int_ctrl.tick_en = 1;
+ break;
+ case RTC_INT_TICK_WEEK:
+ rtc->int_ctrl.tick_sel = 0x5;
+ rtc->int_ctrl.tick_en = 1;
+ break;
+ case RTC_INT_TICK_HOUR:
+ rtc->int_ctrl.tick_sel = 0x4;
+ rtc->int_ctrl.tick_en = 1;
+ break;
+ case RTC_INT_TICK_MINUTE:
+ rtc->int_ctrl.tick_sel = 0x3;
+ rtc->int_ctrl.tick_en = 1;
+ break;
+ case RTC_INT_TICK_SECOND:
+ rtc->int_ctrl.tick_sel = 0x2;
+ rtc->int_ctrl.tick_en = 1;
+ break;
+ case RTC_INT_TICK_S8:
+ rtc->int_ctrl.tick_sel = 0x1;
+ rtc->int_ctrl.tick_en = 1;
+ break;
+ case RTC_INT_TICK_S64:
+ rtc->int_ctrl.tick_sel = 0x0;
+ rtc->int_ctrl.tick_en = 1;
+ break;
+ default :
+ break;
+ }
+ }
+}
+
+static void rtc_stop_interrupt(struct k230_rtc_dev *dev)
+{
+ rt_hw_interrupt_mask(dev->vector);
+}
+
+static void rtc_alarm_stop(struct k230_rtc_dev *dev)
+{
+ volatile rtc_t *rtc = (rtc_t *)dev->base;
+ rtc->int_ctrl.alarm_en = 0;
+ rtc_stop_interrupt(dev);
+}
+
+static void rtc_tick_stop(struct k230_rtc_dev *dev)
+{
+ volatile rtc_t *rtc = (rtc_t *)dev->base;
+ rtc->int_ctrl.tick_en = 0;
+ rtc_stop_interrupt(dev);
+}
+
+static void rtc_alarm_clear_interrupt(struct k230_rtc_dev *dev)
+{
+ volatile rtc_t *rtc = (rtc_t *)dev->base;
+
+ rtc->int_ctrl.alarm_clr = 1;
+}
+
+static void rtc_irq(int vector, void *param)
+{
+ struct k230_rtc_dev *dev = (struct k230_rtc_dev *)param;
+
+ rtc_alarm_clear_interrupt(dev);
+ if (dev->vector_callback != RT_NULL)
+ {
+ dev->vector_callback();
+ }
+}
+
+static void rtc_date_time_set(struct k230_rtc_dev *dev, int year, int month, int day, \
+ int hour, int minute, int second, int week)
+{
+ rtc_date_t date;
+ rtc_time_t time;
+ rtc_count_t count;
+ volatile rtc_t *rtc = (rtc_t *)dev->base;
+
+ int val = year % 100;
+ int year_l, year_h;
+ if(val == 0)
+ {
+ year_l = 100;
+ year_h = year / 100 - 1;
+ }
+ else
+ {
+ year_l = val;
+ year_h = (year - val) / 100;
+ }
+
+ rtc->int_ctrl.timer_w_en = 1;
+
+ date.year_h = year_h;
+ date.year_l = year_l;
+ date.month = month;
+ date.day = day;
+ date.leap_year = rtc_year_is_leap(year);
+ time.week = week;
+ time.hour = hour;
+ time.minute = minute;
+ time.second = second;
+
+ rtc->date = date;
+ rtc->time = time;
+}
+
+static void rtc_timer_get(struct k230_rtc_dev *dev, time_t *t)
+{
+ volatile rtc_t *rtc = (rtc_t *)dev->base;
+ struct tm tm;
+
+ if (rtc->int_ctrl.timer_r_en == 0)
+ {
+ rtc->int_ctrl.timer_r_en = 1;
+ }
+
+ tm.tm_sec = rtc->time.second;
+ tm.tm_min = rtc->time.minute;
+ tm.tm_hour = rtc->time.hour;
+ tm.tm_mday = rtc->date.day;
+ tm.tm_mon = rtc->date.month - 1;
+ tm.tm_year = (rtc->date.year_h * 100 + rtc->date.year_l) - 1900;
+ tm.tm_wday = rtc->time.week;
+
+ *t = timegm(&tm);
+}
+
+static void rtc_timer_set(struct k230_rtc_dev *dev, time_t *t)
+{
+ struct tm p_tm;
+ gmtime_r(t, &p_tm);
+
+ rtc_date_time_set(dev, (p_tm.tm_year + 1900), p_tm.tm_mon + 1, p_tm.tm_mday, \
+ p_tm.tm_hour, p_tm.tm_min, p_tm.tm_sec, p_tm.tm_wday);
+
+ rtc_timer_set_clock_count_value(dev, 0);
+}
+
+static void rtc_alarm_get(struct k230_rtc_dev *dev, void *args)
+{
+ struct tm *tm = (struct tm*)args;
+ volatile rtc_t *rtc = (rtc_t *)dev->base;
+ rtc_alarm_date_t alarm_date = rtc->alarm_date;
+ rtc_alarm_time_t alarm_time = rtc->alarm_time;
+
+ tm->tm_year = (alarm_date.alarm_year_h * 100 + alarm_date.alarm_year_l) -1900;
+ tm->tm_mon = alarm_date.alarm_month - 1;
+ tm->tm_mday = alarm_date.alarm_day;
+ tm->tm_hour = alarm_time.alarm_hour;
+ tm->tm_min = alarm_time.alarm_minute;
+ tm->tm_sec = alarm_time.alarm_second;
+}
+
+static void rtc_alarm_set(struct k230_rtc_dev *dev, void *args)
+{
+ rtc_alarm_setup_t *setup = (rtc_alarm_setup_t *)args;
+ struct tm tm = setup->tm;
+ time_t t;
+ struct tm p_tm;
+ volatile rtc_t *rtc = (rtc_t *)dev->base;
+ rtc_alarm_time_t alarm_time;
+ rtc_alarm_date_t alarm_date;
+ rtc_date_t date = rtc->date;
+ int year, year_l, year_h, val;
+
+ t = mktime(&tm);
+ gmtime_r(&t, &p_tm);
+
+ year = p_tm.tm_year + 1900;
+ val = year % 100;
+
+ if(val == 0)
+ {
+ year_l = 100;
+ year_h = year / 100 - 1;
+ }
+ else
+ {
+ year_l = val;
+ year_h = (year - val) / 100;
+ }
+
+ alarm_date.alarm_year_h = year_h;
+ alarm_date.alarm_year_l = year_l;
+ alarm_date.alarm_month = p_tm.tm_mon + 1;
+ alarm_date.alarm_day = p_tm.tm_mday;
+ alarm_time.alarm_hour = p_tm.tm_hour;
+ alarm_time.alarm_minute = p_tm.tm_min;
+ alarm_time.alarm_second = p_tm.tm_sec;
+
+ rtc->alarm_date = alarm_date;
+ rtc->alarm_time = alarm_time;
+
+ rtc_alarm_clear_interrupt(dev);
+ rt_hw_interrupt_install(dev->vector, rtc_irq, dev, "rtc");
+ rt_hw_interrupt_umask(dev->vector);
+ rtc_interrupt_ctrl_set(dev, setup->flag);
+}
+
+static rt_err_t rtc_device_init(rt_device_t dev)
+{
+ struct k230_rtc_dev *rtc_dev = rt_container_of(dev, struct k230_rtc_dev, device);
+
+ rtc_alarm_stop(rtc_dev);
+ rtc_tick_stop(rtc_dev);
+
+ return RT_EOK;
+}
+
+static rt_err_t rtc_device_open(rt_device_t dev, rt_uint16_t oflag)
+{
+ return RT_EOK;
+}
+
+static rt_err_t rtc_device_close(rt_device_t dev)
+{
+ return RT_EOK;
+}
+
+static rt_ssize_t rtc_device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
+{
+ time_t t;
+ struct k230_rtc_dev *rtc_dev = rt_container_of(dev, struct k230_rtc_dev, device);
+
+ rtc_timer_get(rtc_dev, &t);
+ rt_memcpy(buffer, (void*)&t, sizeof(t));
+ return size;
+}
+
+static rt_ssize_t rtc_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
+{
+ struct tm *tm = (struct tm*)buffer;
+ time_t t = mktime(tm);
+ struct k230_rtc_dev *rtc_dev = rt_container_of(dev, struct k230_rtc_dev, device);
+
+ rtc_timer_set(rtc_dev, &t);
+ return size;
+}
+
+static rt_err_t rtc_device_control(rt_device_t dev, int cmd, void *args)
+{
+ time_t time;
+ RT_ASSERT(dev != RT_NULL);
+
+ struct k230_rtc_dev *rtc_dev = rt_container_of(dev, struct k230_rtc_dev, device);
+ RT_ASSERT(rtc_dev != RT_NULL);
+
+ switch (cmd)
+ {
+ case RT_DEVICE_CTRL_RTC_GET_TIME:
+ rtc_timer_get(rtc_dev, (time_t*)args);
+ break;
+ case RT_DEVICE_CTRL_RTC_SET_TIME:
+ rtc_timer_set(rtc_dev, (time_t *)args);
+ break;
+ case RT_DEVICE_CTRL_RTC_GET_ALARM:
+ rtc_alarm_get(rtc_dev, args);
+ break;
+ case RT_DEVICE_CTRL_RTC_SET_ALARM:
+ rtc_alarm_set(rtc_dev, args);
+ break;
+ case RT_DEVICE_CTRL_RTC_STOP_ALARM:
+ rtc_alarm_stop(rtc_dev);
+ break;
+ case RT_DEVICE_CTRL_RTC_STOP_TICK:
+ rtc_tick_stop(rtc_dev);
+ break;
+ case RT_DEVICE_CTRL_RTC_SET_CALLBACK:
+ rtc_dev->vector_callback = args;
+ break;
+ default:
+ return -RT_EINVAL;
+ }
+ return RT_EOK;
+}
+
+const static struct rt_device_ops rtc_ops =
+{
+ .init = rtc_device_init,
+ .open = rtc_device_open,
+ .close = rtc_device_close,
+ .read = rtc_device_read,
+ .write = rtc_device_write,
+ .control = rtc_device_control,
+};
+
+static struct k230_rtc_dev rtc_dev =
+{
+ .name = "rtc",
+ .base = RTC_BASE_ADDR,
+ .size = RTC_IO_SIZE,
+ .vector = K230_IRQ_PMU,
+ .vector_callback = RT_NULL,
+};
+
+static int rt_hw_rtc_init(void)
+{
+ rt_err_t ret;
+
+ pmu_isolation_rtc();
+ rtc_dev.device.type = RT_Device_Class_RTC;
+ rtc_dev.device.rx_indicate = RT_NULL;
+ rtc_dev.device.tx_complete = RT_NULL;
+#ifdef RT_USING_DEVICE_OPS
+ rtc_dev.device.ops = &rtc_ops;
+#else
+ rtc_dev.device.init = rtc_device_init;
+ rtc_dev.device.open = rtc_device_open;
+ rtc_dev.device.close = rtc_device_close;
+ rtc_dev.device.read = rtc_device_read;
+ rtc_dev.device.write = rtc_device_write;
+ rtc_dev.device.control = rtc_device_control;
+#endif /* RT_USING_DEVICE_OPS */
+ rtc_dev.device.user_data = RT_NULL;
+ rtc_dev.base = (rt_ubase_t)rt_ioremap((void *)rtc_dev.base, rtc_dev.size);
+ RT_ASSERT(rtc_dev.base != RT_NULL);
+
+ ret = rt_device_register(&rtc_dev.device, "rtc", RT_DEVICE_FLAG_RDWR);
+ RT_ASSERT(ret == RT_EOK);
+ LOG_I("rtc driver register OK\n");
+
+ rtc_alarm_stop(&rtc_dev);
+ rtc_tick_stop(&rtc_dev);
+ return ret;
+}
+INIT_DEVICE_EXPORT(rt_hw_rtc_init);
diff --git a/bsp/k230/drivers/interdrv/rtc/drv_rtc.h b/bsp/k230/drivers/interdrv/rtc/drv_rtc.h
new file mode 100644
index 0000000000..ce026c4e7b
--- /dev/null
+++ b/bsp/k230/drivers/interdrv/rtc/drv_rtc.h
@@ -0,0 +1,161 @@
+/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2006-2025 RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __DRV_RTC_H__
+#define __DRV_RTC_H__
+#include <stdint.h>
+#include <drivers/dev_rtc.h>
+
+#define RT_DEVICE_CTRL_RTC_SET_CALLBACK 0x44
+#define RT_DEVICE_CTRL_RTC_STOP_ALARM 0x45
+#define RT_DEVICE_CTRL_RTC_STOP_TICK 0x46
+#define BIT(n) (1 << n)
+
+/* date register(reset value 0x10101, offset address 0x00) */
+typedef struct _rtc_date
+{
+ uint32_t day : 5;
+ uint32_t resv0 : 3;
+ uint32_t month : 4;
+ uint32_t resv1 : 4;
+ uint32_t year_l : 7;
+ uint32_t leap_year : 1;
+ uint32_t year_h : 7;
+ uint32_t resv2 : 1;
+} __attribute__((packed, aligned(4))) rtc_date_t;
+
+/* time register(reset value 0x00, offset address 0x04) */
+typedef struct _rtc_time
+{
+ uint32_t second : 6;
+ uint32_t resv0 : 2;
+ uint32_t minute : 6;
+ uint32_t resv1 : 2;
+ uint32_t hour : 5;
+ uint32_t resv2 : 3;
+ uint32_t week : 3;
+ uint32_t resv3 : 5;
+} __attribute__((packed, aligned(4))) rtc_time_t;
+
+/* alarm date register(reset value 0x10101, offset address 0x08) */
+typedef struct _rtc_alarm_date
+{
+ uint32_t alarm_day : 5;
+ uint32_t resv0 : 3;
+ uint32_t alarm_month : 4;
+ uint32_t resv1 : 4;
+ uint32_t alarm_year_l : 7;
+ uint32_t resv2 : 1;
+ uint32_t alarm_year_h : 7;
+ uint32_t resv3 : 1;
+} __attribute__((packed, aligned(4))) rtc_alarm_date_t;
+
+/* alarm time register(reset value 0x00, offset address 0x0C) */
+typedef struct _rtc_alarm_time
+{
+ uint32_t alarm_second : 6;
+ uint32_t resv0 : 2;
+ uint32_t alarm_minute : 6;
+ uint32_t resv1 : 2;
+ uint32_t alarm_hour : 5;
+ uint32_t resv2 : 3;
+ uint32_t alarm_week : 3;
+ uint32_t resv3 : 5;
+} __attribute__((packed, aligned(4))) rtc_alarm_time_t;
+
+/* count register(reset value 0x7FFF0000, offset address 0x10) */
+typedef struct _rtc_count
+{
+ uint32_t curr_count : 15; /*!< RTC counter currunt value */
+ uint32_t resv0 : 1;
+ uint32_t sum_count : 15; /*!< RTC counter max value */
+ uint32_t resv1 : 1;
+} __attribute__((packed, aligned(4))) rtc_count_t;
+
+/* interrupt control register(reset value 0x00, offset address 0x14) */
+typedef struct _rtc_int_ctrl
+{
+ uint32_t timer_w_en : 1;
+ uint32_t timer_r_en : 1;
+ uint32_t resv0 : 6;
+ uint32_t tick_en : 1;
+ uint32_t tick_sel : 4;
+ uint32_t resv1 : 3;
+ uint32_t alarm_en : 1;
+ uint32_t alarm_clr : 1;
+ uint32_t resv2 : 6;
+ uint32_t second_cmp : 1;
+ uint32_t minute_cmp : 1;
+ uint32_t hour_cmp : 1;
+ uint32_t week_cmp : 1;
+ uint32_t day_cmp : 1;
+ uint32_t month_cmp : 1;
+ uint32_t year_cmp : 1;
+ uint32_t resv3 : 1;
+} __attribute__((packed, aligned(4))) rtc_int_ctrl_t;
+
+/* rtc register */
+typedef struct _rtc
+{
+ rtc_date_t date;
+ rtc_time_t time;
+ rtc_alarm_date_t alarm_date;
+ rtc_alarm_time_t alarm_time;
+ rtc_count_t count;
+ rtc_int_ctrl_t int_ctrl;
+} __attribute__((packed, aligned(4))) rtc_t;
+
+typedef enum _rtc_tick_interrupt_mode_e
+{
+ RTC_INT_ALARM_YEAR = BIT(0),
+ RTC_INT_ALARM_MONTH = BIT(1),
+ RTC_INT_ALARM_DAY = BIT(2),
+ RTC_INT_ALARM_WEEK = BIT(3),
+ RTC_INT_ALARM_HOUR = BIT(4),
+ RTC_INT_ALARM_MINUTE = BIT(5),
+ RTC_INT_ALARM_SECOND = BIT(6),
+ RTC_INT_TICK_YEAR = BIT(7),
+ RTC_INT_TICK_MONTH,
+ RTC_INT_TICK_DAY,
+ RTC_INT_TICK_WEEK,
+ RTC_INT_TICK_HOUR,
+ RTC_INT_TICK_MINUTE,
+ RTC_INT_TICK_SECOND,
+ RTC_INT_TICK_S8,
+ RTC_INT_TICK_S64,
+} rtc_interrupt_mode_t;
+
+typedef struct _rtc_alarm_setup
+{
+ rt_uint32_t flag; /* alarm flag */
+ struct tm tm; /* when will the alarm wake up user */
+} rtc_alarm_setup_t;
+
+#endif /* __DRV_RTC_H__ */
diff --git a/bsp/k230/drivers/utest/SConscript b/bsp/k230/drivers/utest/SConscript
index 92ea98982f..eeaa0f78f4 100644
--- a/bsp/k230/drivers/utest/SConscript
+++ b/bsp/k230/drivers/utest/SConscript
@@ -26,6 +26,9 @@ if GetDepend('RT_UTEST_USING_ALL_CASES') or GetDepend('BSP_UTEST_DRIVERS'):
if GetDepend('BSP_USING_UART'):
src += ['test_uart.c']
+
+ if GetDepend('BSP_USING_RTC'):
+ src += ['test_rtc.c']
group = DefineGroup('utestcases', src, depend = [''])
diff --git a/bsp/k230/drivers/utest/test_rtc.c b/bsp/k230/drivers/utest/test_rtc.c
new file mode 100644
index 0000000000..0b1ffaab1c
--- /dev/null
+++ b/bsp/k230/drivers/utest/test_rtc.c
@@ -0,0 +1,217 @@
+/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 2006-2025 RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+#include <time.h>
+#include <utest.h>
+#include <finsh.h>
+#include "drv_rtc.h"
+
+/*
+ * 测试 RTC 的时间功能与闹钟功能,在RTCd的寄存器中保存的时间信息
+ * 是以 UTC 时间保存的,要使用本地时间需要配置时区,当前配置的时
+ * 区是东八区,即北京时间(CST)
+ *
+ * 在设置时间日期时使用 set_time() 与 set_date() 接口,设置的时
+ * 间这两个接口内部会做时区转换
+ *
+ * 测试说明:
+ * 基于庐山派开发板测试(01Studio 的开发板无法使用硬件RTC,
+ * 因为没有接 int0/4 的上拉)
+ * RTC 为 K230 自带的 RTC
+ * RTC 的时钟源为外部 32.768KHz 晶振
+ * 在测试终端运行该测试后,会分别进行 test_rtc_set(),
+ * test_rtc_alarm() 与 test_rtc_interface() 三个测试。
+ * 其中 test_rtc_set() 会通过 set_time() 与 set_date() 设置时间,
+ * 注意设置的时间需要为本地时间,内部会转换成 UTC 时间,然后调用
+ * drv_rtc.c 内的接口设置到 RTC 寄存器中;
+ * test_rtc_alarm() 会设置一个 5 秒后的闹钟时间,并注册一个闹钟
+ * 中断回调函数,同样用户设置的闹钟时间为本地时间,内部会转换成UTC
+ * 时间然后保存进 RTC 中;
+ * test_rtc_interface() 会测试读写 RTC 的接口,写 RTC 时同样需要
+ * 提供本地时间,读出来后需要转换成本地时间(如果有需要)。
+ */
+
+#define RTC_NAME "rtc"
+
+static void test_rtc_set(void)
+{
+ rt_err_t ret = RT_EOK;
+ time_t now;
+ uint32_t i;
+ rt_device_t rtc_dev = RT_NULL;
+
+ LOG_I("rtc set time test\n");
+ rtc_dev = rt_device_find(RTC_NAME);
+ uassert_not_null(rtc_dev);
+ ret = rt_device_open(rtc_dev, RT_DEVICE_OFLAG_RDWR);
+ uassert_int_equal(ret, RT_EOK);
+ ret = set_time(23, 59, 59);
+ uassert_int_equal(ret, RT_EOK);
+ ret = set_date(2025, 9, 16);
+ uassert_int_equal(ret, RT_EOK);
+ rt_thread_mdelay(500);
+ /* 设置完时间后打印10次时间 */
+ for (i=0; i<10; i++)
+ {
+ now = time(RT_NULL);
+ LOG_I("%s\n", ctime(&now));
+ rt_thread_mdelay(1000);
+ }
+
+ rt_device_close(rtc_dev);
+}
+
+static void test_rtc_alarm_callback(void)
+{
+ LOG_I("rtc alarm triggered!\n");
+}
+
+static void test_rtc_alarm(void)
+{
+ rt_err_t ret = RT_EOK;
+ time_t now;
+ uint32_t i;
+ struct tm p_tm;
+ rt_device_t rtc_dev = RT_NULL;
+ struct rt_alarm *alarm = RT_NULL;
+ rtc_alarm_setup_t setup;
+
+ LOG_I("rtc alarm test\n");
+ rtc_dev = rt_device_find(RTC_NAME);
+ uassert_not_null(rtc_dev);
+ ret = rt_device_open(rtc_dev, RT_DEVICE_OFLAG_RDWR);
+ uassert_int_equal(ret, RT_EOK);
+ ret = set_time(23, 59, 59);
+ uassert_int_equal(ret, RT_EOK);
+ ret = set_date(2025, 9, 16);
+ uassert_int_equal(ret, RT_EOK);
+ rt_thread_mdelay(500);
+ now = time(RT_NULL);
+ LOG_I("%s\n", ctime(&now));
+ now += 5; //alarm after 5s
+ localtime_r(&now, &p_tm);
+
+ setup.flag = RTC_INT_ALARM_MINUTE | RTC_INT_ALARM_SECOND;
+ setup.tm.tm_year = p_tm.tm_year;
+ setup.tm.tm_mon = p_tm.tm_mon;
+ setup.tm.tm_mday = p_tm.tm_mday;
+ setup.tm.tm_wday = p_tm.tm_wday;
+ setup.tm.tm_hour = p_tm.tm_hour;
+ setup.tm.tm_min = p_tm.tm_min;
+ setup.tm.tm_sec = p_tm.tm_sec;
+
+ rt_device_control(rtc_dev, RT_DEVICE_CTRL_RTC_SET_CALLBACK, &test_rtc_alarm_callback); //set rtc intr callback
+ rt_device_control(rtc_dev, RT_DEVICE_CTRL_RTC_SET_ALARM, &setup); //set alarm time
+ rt_memset(&p_tm, 0, sizeof(p_tm));
+ rt_device_control(rtc_dev, RT_DEVICE_CTRL_RTC_GET_ALARM, &p_tm); //get alarm time
+ now = timegm(&p_tm);
+ LOG_I("get alarm time: %s\n", ctime(&now));
+
+ for (i=0; i<10; i++)
+ {
+ now = time(RT_NULL);
+ LOG_I("%s\n", ctime(&now));
+ rt_thread_mdelay(1000);
+ }
+ rt_device_control(rtc_dev, RT_DEVICE_CTRL_RTC_STOP_ALARM, RT_NULL); //stop alarm
+
+ rt_device_close(rtc_dev);
+}
+
+static void test_rtc_interface(void)
+{
+ rt_err_t ret = RT_EOK;
+ uint32_t i;
+ rt_device_t rtc_dev = RT_NULL;
+ time_t now;
+ struct tm tm;
+
+ LOG_I("rtc interface test\n");
+ rtc_dev = rt_device_find(RTC_NAME);
+ uassert_not_null(rtc_dev);
+ ret = rt_device_open(rtc_dev, RT_DEVICE_OFLAG_RDWR);
+ uassert_int_equal(ret, RT_EOK);
+
+ LOG_I("write rtc\n");
+ tm.tm_year = 2025 - 1900;
+ tm.tm_mon = 9 - 1;
+ tm.tm_mday = 16;
+ tm.tm_wday = 2;
+ tm.tm_hour = 23;
+ tm.tm_min = 59;
+ tm.tm_sec = 59;
+ rt_device_write(rtc_dev, RT_NULL, (void*)&tm, sizeof(tm));
+ rt_thread_mdelay(500);
+
+ /* 设置完时间后打印10次时间 */
+ for (i=0; i<10; i++)
+ {
+ now = time(RT_NULL);
+ LOG_I("[sys]:%s\n", ctime(&now));
+ rt_thread_mdelay(1000);
+ }
+
+ LOG_I("read rtc\n");
+ for (i=0; i<10; i++)
+ {
+ rt_device_read(rtc_dev, RT_NULL, (void*)&now, sizeof(now));
+ LOG_I("[read]: %s\n", ctime(&now));
+ rt_thread_mdelay(1000);
+ }
+
+ rt_device_close(rtc_dev);
+}
+
+static void test_rtc(void)
+{
+ test_rtc_set();
+ test_rtc_alarm();
+ test_rtc_interface();
+}
+
+static void testcase(void)
+{
+ LOG_I("This is a rtc test case.\n");
+ UTEST_UNIT_RUN(test_rtc);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ return RT_EOK;
+}
+UTEST_TC_EXPORT(testcase, "bsp.k230.drivers.rtc", utest_tc_init, utest_tc_cleanup, 100);
diff --git a/bsp/k230/rtconfig.h b/bsp/k230/rtconfig.h
index 9c521884a6..a1ba8e430e 100644
--- a/bsp/k230/rtconfig.h
+++ b/bsp/k230/rtconfig.h
@@ -494,6 +494,14 @@
/* NXP HAL & SDK Drivers */
/* end of NXP HAL & SDK Drivers */
+
+/* NUVOTON Drivers */
+
+/* end of NUVOTON Drivers */
+
+/* GD32 Drivers */
+
+/* end of GD32 Drivers */
/* end of HAL & SDK Drivers */
/* sensors drivers */