drv_rtc.c 9.7 KB
Newer Older
W
Wayne Lin 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/**************************************************************************//**
*
* @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date            Author       Notes
* 2020-12-12      Wayne        First version
*
******************************************************************************/
#include <rtconfig.h>

#if defined (BSP_USING_RTC)

#include <rtdevice.h>
17
#include <sys/time.h>
W
Wayne Lin 已提交
18
#include "NuMicro.h"
W
Wayne Lin 已提交
19 20 21 22 23 24 25 26 27 28 29 30 31 32
#include <drv_sys.h>

/* Private define ---------------------------------------------------------------*/

/* convert the real year and month value to the format of struct tm. */
#define CONV_TO_TM_YEAR(year)           ((year) - 1900)
#define CONV_TO_TM_MON(mon)             ((mon) - 1)

/* convert the tm_year and tm_mon from struct tm to the real value. */
#define CONV_FROM_TM_YEAR(tm_year)      ((tm_year) + 1900)
#define CONV_FROM_TM_MON(tm_mon)        ((tm_mon) + 1)

/* rtc date upper bound reaches the year of 2099. */
#define RTC_TM_UPPER_BOUND                                              \
33 34 35 36 37 38
{   .tm_year = CONV_TO_TM_YEAR(2038),                                   \
    .tm_mon  = CONV_TO_TM_MON(1),                                       \
    .tm_mday  = 19,                                                     \
    .tm_hour  = 3,                                                      \
    .tm_min = 14,                                                       \
    .tm_sec  = 07,                                                      \
W
Wayne Lin 已提交
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
}

/* rtc date lower bound reaches the year of 2000. */
#define RTC_TM_LOWER_BOUND                                              \
{   .tm_year = CONV_TO_TM_YEAR(2000),                                   \
    .tm_mon  = CONV_TO_TM_MON(1),                                       \
    .tm_mday  = 1,                                                      \
    .tm_hour  = 0,                                                      \
    .tm_min = 0,                                                        \
    .tm_sec  = 0,                                                       \
}

/* Private typedef --------------------------------------------------------------*/

/* Private functions ------------------------------------------------------------*/
static rt_err_t nu_rtc_control(rt_device_t dev, int cmd, void *args);

#if defined (NU_RTC_SUPPORT_IO_RW)
    static rt_size_t nu_rtc_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
    static rt_size_t nu_rtc_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
#endif

61 62
static rt_err_t nu_rtc_is_date_valid(const time_t t);
static rt_err_t nu_rtc_init(void);
W
Wayne Lin 已提交
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78

#if defined(RT_USING_ALARM)
    static void nu_rtc_alarm_reset(void);
    static void nu_rtc_isr(int vector, void *param);
#endif

/* Public functions -------------------------------------------------------------*/
#if defined (NU_RTC_SUPPORT_MSH_CMD)
    extern rt_err_t set_date(rt_uint32_t year, rt_uint32_t month, rt_uint32_t day);
    extern rt_err_t set_time(rt_uint32_t hour, rt_uint32_t minute, rt_uint32_t second);
#endif

/* Private variables ------------------------------------------------------------*/
static struct rt_device device_rtc;


79
static rt_err_t nu_rtc_init(void)
W
Wayne Lin 已提交
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
{
    nu_sys_ipclk_enable(RTCCKEN);

    /* hw rtc initialise */
    RTC_Open(NULL);
    RTC_DisableInt(RTC_INTEN_ALMIEN_Msk | RTC_INTEN_TICKIEN_Msk);

#if defined(RT_USING_ALARM)

    nu_rtc_alarm_reset();
    RTC_EnableInt(RTC_INTEN_ALMIEN_Msk);

    rt_hw_interrupt_install(IRQ_RTC, nu_rtc_isr, &device_rtc, "rtc");
    rt_hw_interrupt_umask(IRQ_RTC);

#endif
96 97

    return RT_EOK;
W
Wayne Lin 已提交
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
}


#if defined(RT_USING_ALARM)
/* Reset alarm settings to avoid the unwanted values remain in rtc registers. */
static void nu_rtc_alarm_reset(void)
{
    S_RTC_TIME_DATA_T alarm;

    /* Reset alarm time and calendar. */
    alarm.u32Year       = RTC_YEAR2000;
    alarm.u32Month      = 0;
    alarm.u32Day        = 0;
    alarm.u32Hour       = 0;
    alarm.u32Minute     = 0;
    alarm.u32Second     = 0;
    alarm.u32TimeScale  = RTC_CLOCK_24;

    RTC_SetAlarmDateAndTime(&alarm);

    /* Clear alarm flag for safe */
    RTC_CLEAR_ALARM_INT_FLAG();
}
#endif


/* rtc device driver initialise. */
int rt_hw_rtc_init(void)
{
    rt_err_t ret;

    nu_rtc_init();

    /* register rtc device IO operations */
    device_rtc.type = RT_Device_Class_RTC;
    device_rtc.init = NULL;
    device_rtc.open = NULL;
    device_rtc.close = NULL;
    device_rtc.control = nu_rtc_control;

#if defined (NU_RTC_SUPPORT_IO_RW)
    device_rtc.read = nu_rtc_read;
    device_rtc.write = nu_rtc_write;
#else
    device_rtc.read = NULL;
    device_rtc.write = NULL;
#endif

    device_rtc.user_data = RT_NULL;
    device_rtc.rx_indicate = RT_NULL;
    device_rtc.tx_complete = RT_NULL;

    ret = rt_device_register(&device_rtc, "rtc", RT_DEVICE_FLAG_RDWR);

    return (int)ret;
}
INIT_BOARD_EXPORT(rt_hw_rtc_init);


#if defined (NU_RTC_SUPPORT_IO_RW)
/* Register rt-thread device.read() entry. */
static rt_size_t nu_rtc_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    (void) pos;
    nu_rtc_control(dev, RT_DEVICE_CTRL_RTC_GET_TIME, buffer);

    return size;
}
#endif


#if defined (NU_RTC_SUPPORT_IO_RW)
/* Register rt-thread device.write() entry. */
static rt_size_t nu_rtc_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    (void) pos;
    nu_rtc_control(dev, RT_DEVICE_CTRL_RTC_SET_TIME, (void *)buffer);

    return size;
}
#endif


181
static rt_err_t nu_rtc_is_date_valid(const time_t t)
W
Wayne Lin 已提交
182 183 184 185 186 187 188 189
{
    static struct tm tm_upper = RTC_TM_UPPER_BOUND;
    static struct tm tm_lower = RTC_TM_LOWER_BOUND;
    static time_t t_upper, t_lower;
    static rt_bool_t initialised = RT_FALSE;

    if (!initialised)
    {
190 191
        t_upper = timegm((struct tm *)&tm_upper);
        t_lower = timegm((struct tm *)&tm_lower);
W
Wayne Lin 已提交
192 193 194 195
        initialised = RT_TRUE;
    }

    /* check the date is supported by rtc. */
196
    if ((t > t_upper) || (t < t_lower))
W
Wayne Lin 已提交
197 198 199 200 201 202 203 204 205
        return -(RT_EINVAL);

    return RT_EOK;
}


/* Register rt-thread device.control() entry. */
static rt_err_t nu_rtc_control(rt_device_t dev, int cmd, void *args)
{
206
    struct tm tm_out, tm_in;
W
Wayne Lin 已提交
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
    time_t *time;
    S_RTC_TIME_DATA_T hw_time;

#if defined(RT_USING_ALARM)

    struct rt_rtc_wkalarm *wkalarm;
    S_RTC_TIME_DATA_T hw_alarm;
#endif

    if ((dev == NULL) || (args == NULL))
        return -(RT_EINVAL);

    switch (cmd)
    {
    case RT_DEVICE_CTRL_RTC_GET_TIME:

        time = (time_t *)args;
        RTC_GetDateAndTime(&hw_time);

        tm_out.tm_year = CONV_TO_TM_YEAR(hw_time.u32Year);
        tm_out.tm_mon = CONV_TO_TM_MON(hw_time.u32Month);
        tm_out.tm_mday = hw_time.u32Day;
        tm_out.tm_hour = hw_time.u32Hour;
        tm_out.tm_min = hw_time.u32Minute;
        tm_out.tm_sec = hw_time.u32Second;
232
        *time = timegm(&tm_out);
W
Wayne Lin 已提交
233 234 235 236 237 238
        break;

    case RT_DEVICE_CTRL_RTC_SET_TIME:

        time = (time_t *) args;

239
        if (nu_rtc_is_date_valid(*time) != RT_EOK)
W
Wayne Lin 已提交
240 241
            return -(RT_ERROR);

242 243 244 245 246 247 248
        gmtime_r(time, &tm_in);
        hw_time.u32Year = CONV_FROM_TM_YEAR(tm_in.tm_year);
        hw_time.u32Month = CONV_FROM_TM_MON(tm_in.tm_mon);
        hw_time.u32Day = tm_in.tm_mday;
        hw_time.u32Hour = tm_in.tm_hour;
        hw_time.u32Minute = tm_in.tm_min;
        hw_time.u32Second = tm_in.tm_sec;
W
Wayne Lin 已提交
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
        hw_time.u32TimeScale = RTC_CLOCK_24;
        hw_time.u32AmPm = 0;

        RTC_SetDateAndTime(&hw_time);
        break;

#if defined(RT_USING_ALARM)
    case RT_DEVICE_CTRL_RTC_GET_ALARM:

        wkalarm = (struct rt_rtc_wkalarm *) args;
        RTC_GetAlarmDateAndTime(&hw_alarm);

        wkalarm->tm_hour = hw_alarm.u32Hour;
        wkalarm->tm_min = hw_alarm.u32Minute;
        wkalarm->tm_sec = hw_alarm.u32Second;
        break;

    case RT_DEVICE_CTRL_RTC_SET_ALARM:

W
Wayne Lin 已提交
268 269
        RTC_GetDateAndTime(&hw_alarm);

W
Wayne Lin 已提交
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
        wkalarm = (struct rt_rtc_wkalarm *) args;
        hw_alarm.u32Hour = wkalarm->tm_hour;
        hw_alarm.u32Minute = wkalarm->tm_min;
        hw_alarm.u32Second = wkalarm->tm_sec;

        RTC_SetAlarmDateAndTime(&hw_alarm);
        break;

    default:
        return -(RT_EINVAL);
#endif
    }

    return RT_EOK;
}


#if defined (NU_RTC_SUPPORT_MSH_CMD)

/* Support "rtc_det_date" command line in msh mode */
static rt_err_t msh_rtc_set_date(int argc, char **argv)
{
    rt_uint32_t index, len, arg[3];

    rt_memset(arg, 0, sizeof(arg));
    len = (argc >= 4) ? 4 : argc;

    /* The date information stored in argv is represented by the following order :
       argv[0,1,2,3] = [cmd, year, month, day] */
    for (index = 0; index < (len - 1); index ++)
    {
        arg[index] = atol(argv[index + 1]);
    }

    return set_date(arg[0], arg[1], arg[2]);
}
MSH_CMD_EXPORT_ALIAS(msh_rtc_set_date, rtc_set_date, e.g: rtc_set_date 2020 1 20);
#endif


#if defined (NU_RTC_SUPPORT_MSH_CMD)

/* Support "rtc_det_time" command line in msh mode */
static rt_err_t msh_rtc_set_time(int argc, char **argv)
{
    rt_uint32_t index, len, arg[3];

    rt_memset(arg, 0, sizeof(arg));
    len = (argc >= 4) ? 4 : argc;

    /* The time information stored in argv is represented by the following order :
       argv[0,1,2,3] = [cmd, hour, minute, second] */
    for (index = 0; index < (len - 1); index ++)
    {
        arg[index] = atol(argv[index + 1]);
    }

    return set_time(arg[0], arg[1], arg[2]);
}
MSH_CMD_EXPORT_ALIAS(msh_rtc_set_time, rtc_set_time, e.g: rtc_set_time 18 30 00);
#endif

#if defined(RT_USING_ALARM)
/* rtc interrupt entry */
static void nu_rtc_isr(int vector, void *param)
{
    if (RTC_GET_TICK_INT_FLAG())
    {
        RTC_CLEAR_TICK_INT_FLAG();
    }

    if (RTC_GET_ALARM_INT_FLAG())
    {
        RTC_CLEAR_ALARM_INT_FLAG();

        /* Send an alarm event to notify rt-thread alarm service. */
        rt_alarm_update(&device_rtc, (rt_uint32_t)NULL);
    }

}
#endif

#endif /* BSP_USING_RTC */