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

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 <yingkezhou@qq.com>
Ze-Hou 3 месяцев назад
Родитель
Сommit
bfbb3c93e7

+ 29 - 0
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

+ 18 - 13
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

+ 11 - 0
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')

+ 486 - 0
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);

+ 161 - 0
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__ */

+ 3 - 0
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 = [''])
 

+ 217 - 0
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);

+ 8 - 0
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 */