1
0
Эх сурвалжийг харах

[bsp][cvitek] add rtc driver (#8586)

Shicheng Chu 1 жил өмнө
parent
commit
5d70f98e4d

+ 5 - 0
bsp/cvitek/c906_little/board/Kconfig

@@ -86,4 +86,9 @@ menu "General Drivers Configuration"
             bool "Enable PWM 3"
             default n
         endif
+
+    config BSP_USING_RTC
+        bool "Enable RTC"
+        select RT_USING_RTC
+        default n
 endmenu

+ 3 - 0
bsp/cvitek/drivers/SConscript

@@ -26,6 +26,9 @@ if GetDepend('BSP_USING_PWM'):
     CPPPATH += [cwd + r'/cv1800b/pwm']
 CPPDEFINES += ['-DCONFIG_64BIT']
 
+if GetDepend('BSP_USING_RTC'):
+    src += ['drv_rtc.c']
+
 group = DefineGroup('drivers', src, depend = [''], CPPDEFINES = CPPDEFINES, CPPPATH = CPPPATH)
 
 Return('group')

+ 329 - 0
bsp/cvitek/drivers/drv_rtc.c

@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2006-2024, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date         Author          Notes
+ * 2024-03-04   ShichengChu     the first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#ifdef BSP_USING_RTC
+
+#define DBG_TAG "DRV.RTC"
+#define DBG_LVL DBG_WARNING
+#include <rtdbg.h>
+
+#include "pinctrl.h"
+#include "mmio.h"
+
+#define CVI_RTC_BASE 0x05026000U
+#define RTC_ALARM_O  17
+#define CVI_RTC_CTRL_BASE 0x05025000U
+#define CLK_EN_0 0x03002000U
+#define CLK_RTC_25M_BIT (1 << 8)
+
+/* CVITEK RTC registers */
+#define CVI_RTC_ANA_CALIB               0x0
+#define CVI_RTC_SEC_PULSE_GEN           0x4
+#define CVI_RTC_ALARM_TIME              0x8
+#define CVI_RTC_ALARM_ENABLE            0xC
+#define CVI_RTC_SET_SEC_CNTR_VALUE      0x10
+#define CVI_RTC_SET_SEC_CNTR_TRIG       0x14
+#define CVI_RTC_SEC_CNTR_VALUE          0x18
+#define CVI_RTC_APB_RDATA_SEL           0x3C
+#define CVI_RTC_POR_DB_MAGIC_KEY        0x68
+#define CVI_RTC_EN_PWR_WAKEUP           0xBC
+#define CVI_RTC_PWR_DET_SEL             0x140
+
+/* CVITEK RTC MACRO registers */
+#define RTC_MACRO_DA_CLEAR_ALL          0x480
+#define RTC_MACRO_DA_SOC_READY          0x48C
+#define RTC_MACRO_RO_T                  0x4A8
+#define RTC_MACRO_RG_SET_T              0x498
+
+/* CVITEK RTC CTRL registers */
+#define CVI_RTC_FC_COARSE_EN            0x40
+#define CVI_RTC_FC_COARSE_CAL           0x44
+#define CVI_RTC_FC_FINE_EN              0x48
+#define CVI_RTC_FC_FINE_CAL             0x50
+
+#define RTC_SEC_MAX_VAL     0xFFFFFFFF
+
+#define RTC_OFFSET_SN 0x5201800
+
+struct rtc_device_object
+{
+    rt_rtc_dev_t  rtc_dev;
+};
+
+static struct rtc_device_object rtc_device;
+
+#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
+
+typedef struct {
+    int tm_sec;             ///< Second.      [0-59]
+    int tm_min;             ///< Minute.      [0-59]
+    int tm_hour;            ///< Hour.        [0-23]
+    int tm_mday;            ///< Day.         [1-31]
+    int tm_mon;             ///< Month.       [0-11]
+    int tm_year;            ///< Year-1900.   [70- ]      !NOTE:Set 100 mean 2000
+    int tm_wday;            ///< Day of week. [0-6 ]      !NOTE:Set 0 mean Sunday
+    int tm_yday;            ///< Days in year.[0-365]     !NOTE:Set 0 mean January 1st
+} cvi_rtc_time_t;
+
+static const unsigned char cvi_rtc_days_in_month[] = {
+    31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+static inline int is_leap_year(unsigned int year)
+{
+    return (!(year % 4) && (year % 100)) || !(year % 400);
+}
+
+static int rtc_month_days(unsigned int month, unsigned int year)
+{
+    return cvi_rtc_days_in_month[month] + (is_leap_year(year) && month == 1);
+}
+
+static void hal_cvi_rtc_clk_set(int enable)
+{
+    uint32_t clk_state;
+
+    clk_state = mmio_read_32((long unsigned int)CLK_EN_0);
+
+    if(enable)
+        clk_state |= CLK_RTC_25M_BIT;
+    else
+        clk_state &= ~(CLK_RTC_25M_BIT);
+
+    mmio_write_32((long unsigned int)CLK_EN_0, clk_state);
+}
+
+static void hal_cvi_rtc_enable_sec_counter(uintptr_t rtc_base)
+{
+    uint32_t value = 0;
+
+    value = mmio_read_32(rtc_base + CVI_RTC_SEC_PULSE_GEN) & ~(1 << 31);
+    mmio_write_32(rtc_base + CVI_RTC_SEC_PULSE_GEN, value);
+
+    value = mmio_read_32(rtc_base + CVI_RTC_ANA_CALIB) & ~(1 << 31);
+    mmio_write_32(rtc_base + CVI_RTC_ANA_CALIB, value);
+
+    mmio_read_32(rtc_base + CVI_RTC_SEC_CNTR_VALUE);
+    mmio_write_32(rtc_base + CVI_RTC_ALARM_ENABLE, 0x0);
+}
+
+static void hal_cvi_rtc_set_time(uintptr_t rtc_base, unsigned long sec)
+{
+    mmio_write_32(rtc_base + CVI_RTC_SET_SEC_CNTR_VALUE, sec);
+    mmio_write_32(rtc_base + CVI_RTC_SET_SEC_CNTR_TRIG, 1);
+    mmio_write_32(rtc_base + RTC_MACRO_RG_SET_T, sec);
+    mmio_write_32(rtc_base + RTC_MACRO_DA_CLEAR_ALL, 1);
+    mmio_write_32(rtc_base + RTC_MACRO_DA_SOC_READY, 1);
+    mmio_write_32(rtc_base + RTC_MACRO_DA_CLEAR_ALL, 0);
+    mmio_write_32(rtc_base + RTC_MACRO_RG_SET_T, 0);
+    mmio_write_32(rtc_base + RTC_MACRO_DA_SOC_READY, 0);
+}
+
+static int hal_cvi_rtc_get_time_sec(uintptr_t rtc_base,unsigned long *ret_sec)
+{
+    int ret = 0;
+    unsigned long sec;
+    unsigned long sec_ro_t;
+
+    sec = mmio_read_32(rtc_base + CVI_RTC_SEC_CNTR_VALUE);
+    sec_ro_t = mmio_read_32(rtc_base + RTC_MACRO_RO_T);
+
+    LOG_D("sec=%lx, sec_ro_t=%lx\n", sec, sec_ro_t);
+
+    if (sec_ro_t > 0x30000000) {
+            sec = sec_ro_t;
+            // Writeback to SEC CVI_RTC_SEC_CNTR_VALUE
+            mmio_write_32(rtc_base + CVI_RTC_SET_SEC_CNTR_VALUE, sec);
+            mmio_write_32(rtc_base + CVI_RTC_SET_SEC_CNTR_TRIG, 1);
+    } else if (sec < 0x30000000) {
+        LOG_D("RTC invalid time\n");
+        ret = -EINVAL;
+    }
+
+    *ret_sec = sec;
+
+    return ret;
+}
+
+static inline int64_t div_u64_rem(uint64_t dividend, uint32_t divisor, uint32_t *remainder)
+{
+    *remainder = dividend % divisor;
+    return dividend / divisor;
+}
+
+/*
+ * rtc_time_to_tm64 - Converts time64_t to rtc_time.
+ * Convert seconds since 01-01-1970 00:00:00 to Gregorian date.
+ */
+static void rtc_time64_to_tm(int64_t time, cvi_rtc_time_t *cvi_tm)
+{
+    unsigned int month, year, secs;
+    int days;
+
+    /* time must be positive */
+    days = div_u64_rem(time, 86400, &secs);
+
+    /* day of the week, 1970-01-01 was a Thursday */
+    cvi_tm->tm_wday = (days + 4) % 7;
+
+    year = 1970 + days / 365;
+    days -= (year - 1970) * 365
+        + LEAPS_THRU_END_OF(year - 1)
+        - LEAPS_THRU_END_OF(1970 - 1);
+    while (days < 0) {
+        year -= 1;
+        days += 365 + is_leap_year(year);
+    }
+    cvi_tm->tm_year = year - 1900;
+    cvi_tm->tm_yday = days + 1;
+
+    for (month = 0; month < 11; month++) {
+        int newdays;
+
+        newdays = days - rtc_month_days(month, year);
+        if (newdays < 0)
+            break;
+        days = newdays;
+    }
+    cvi_tm->tm_mon = month;
+    cvi_tm->tm_mday = days + 1;
+
+    cvi_tm->tm_hour = secs / 3600;
+    secs -= cvi_tm->tm_hour * 3600;
+    cvi_tm->tm_min = secs / 60;
+    cvi_tm->tm_sec = secs - cvi_tm->tm_min * 60;
+}
+
+static int64_t mktime64(const unsigned int year0, const unsigned int mon0,
+        const unsigned int day, const unsigned int hour,
+        const unsigned int min, const unsigned int sec)
+{
+    unsigned int mon = mon0, year = year0;
+
+    /* 1..12 -> 11,12,1..10 */
+    if (0 >= (int) (mon -= 2)) {
+        mon += 12;  /* Puts Feb last since it has leap day */
+        year -= 1;
+    }
+
+    return ((((int64_t)
+          (year/4 - year/100 + year/400 + 367*mon/12 + day) +
+          year*365 - 719499
+        )*24 + hour /* now have hours - midnight tomorrow handled here */
+      )*60 + min /* now have minutes */
+    )*60 + sec; /* finally seconds */
+}
+
+/*
+ * rtc_tm_to_time64 - Converts rtc_time to time64_t.
+ * Convert Gregorian date to seconds since 01-01-1970 00:00:00.
+ */
+static int64_t rtc_tm_to_time64(const cvi_rtc_time_t *cvi_tm)
+{
+    return mktime64(cvi_tm->tm_year + 1900, cvi_tm->tm_mon + 1, cvi_tm->tm_mday,
+            cvi_tm->tm_hour, cvi_tm->tm_min, cvi_tm->tm_sec);
+}
+
+static rt_err_t _rtc_get_timeval(struct timeval *tv)
+{
+    unsigned long sec;
+    cvi_rtc_time_t t = {0};
+    struct tm tm_new = {0};
+
+    hal_cvi_rtc_get_time_sec(CVI_RTC_BASE, &sec);
+    rtc_time64_to_tm(sec, &t);
+
+    tm_new.tm_sec  = t.tm_sec;
+    tm_new.tm_min  = t.tm_min;
+    tm_new.tm_hour = t.tm_hour;
+    tm_new.tm_wday = t.tm_wday;
+    tm_new.tm_mday = t.tm_mday;
+    tm_new.tm_mon  = t.tm_mon;
+    tm_new.tm_year = t.tm_year;
+
+    tv->tv_sec = timegm(&tm_new);
+
+    return RT_EOK;
+}
+
+static rt_err_t _rtc_init(void)
+{
+    hal_cvi_rtc_clk_set(1);
+    hal_cvi_rtc_enable_sec_counter(CVI_RTC_BASE);
+
+    return RT_EOK;
+}
+
+static rt_err_t _rtc_get_secs(time_t *sec)
+{
+    struct timeval tv;
+
+    _rtc_get_timeval(&tv);
+    *(time_t *) sec = tv.tv_sec;
+    LOG_D("RTC: get rtc_time %d", *sec);
+
+    return RT_EOK;
+}
+
+static rt_err_t _rtc_set_secs(time_t *sec)
+{
+    rt_err_t result = RT_EOK;
+    cvi_rtc_time_t t = {0};
+    struct tm tm = {0};
+    unsigned long set_sec;
+
+    gmtime_r(sec, &tm);
+
+    t.tm_sec     = tm.tm_sec;
+    t.tm_min     = tm.tm_min;
+    t.tm_hour    = tm.tm_hour;
+    t.tm_mday    = tm.tm_mday;
+    t.tm_mon     = tm.tm_mon;
+    t.tm_year    = tm.tm_year;
+    t.tm_wday    = tm.tm_wday;
+
+    set_sec = rtc_tm_to_time64(&t);
+    hal_cvi_rtc_set_time(CVI_RTC_BASE, set_sec);
+
+    return result;
+}
+
+static const struct rt_rtc_ops _rtc_ops =
+{
+    _rtc_init,
+    _rtc_get_secs,
+    _rtc_set_secs,
+    RT_NULL,
+    RT_NULL,
+    _rtc_get_timeval,
+    RT_NULL,
+};
+
+static int rt_hw_rtc_init(void)
+{
+    rt_err_t result;
+
+    rtc_device.rtc_dev.ops = &_rtc_ops;
+    result = rt_hw_rtc_register(&rtc_device.rtc_dev, "rtc", RT_DEVICE_FLAG_RDWR, RT_NULL);
+    if (result != RT_EOK)
+    {
+        LOG_E("rtc register err code: %d", result);
+        return result;
+    }
+    LOG_D("rtc init success");
+
+    return RT_EOK;
+}
+INIT_DEVICE_EXPORT(rt_hw_rtc_init);
+
+#endif /* BSP_USING_RTC */