1 /*
2  * Copyright 2017-2020 NXP
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "fsl_rtc.h"
9 
10 /*******************************************************************************
11  * Definitions
12  ******************************************************************************/
13 
14 /* Component ID definition, used by tools. */
15 #ifndef FSL_COMPONENT_ID
16 #define FSL_COMPONENT_ID "platform.drivers.rtc_1"
17 #endif
18 
19 #define SECONDS_IN_A_DAY    (86400U)
20 #define SECONDS_IN_A_HOUR   (3600U)
21 #define SECONDS_IN_A_MINUTE (60U)
22 #define DAYS_IN_A_YEAR      (365U)
23 #define YEAR_RANGE_START    (1970U)
24 #define YEAR_RANGE_END      (2099U)
25 
26 /*******************************************************************************
27  * Variables
28  ******************************************************************************/
29 static volatile uint32_t s_CurrentTimeSeconds;
30 static volatile uint32_t s_AlarmTimeSeconds;
31 static rtc_alarm_callback_t s_RtcAlarmCallback;
32 
33 /*******************************************************************************
34  * Prototypes
35  ******************************************************************************/
36 /*!
37  * @brief Checks whether the date and time passed in is valid
38  *
39  * @param datetime Pointer to structure where the date and time details are stored
40  *
41  * @return Returns false if the date & time details are out of range; true if in range
42  */
43 static bool RTC_CheckDatetimeFormat(const rtc_datetime_t *datetime);
44 
45 /*!
46  * @brief Converts time data from datetime to seconds
47  *
48  * @param datetime Pointer to datetime structure where the date and time details are stored
49  *
50  * @return The result of the conversion in seconds
51  */
52 static uint32_t RTC_ConvertDatetimeToSeconds(const rtc_datetime_t *datetime);
53 
54 /*!
55  * @brief Converts time data from seconds to a datetime structure
56  *
57  * @param seconds  Seconds value that needs to be converted to datetime format
58  * @param datetime Pointer to the datetime structure where the result of the conversion is stored
59  */
60 static void RTC_ConvertSecondsToDatetime(uint32_t seconds, rtc_datetime_t *datetime);
61 
62 /*******************************************************************************
63  * Code
64  ******************************************************************************/
RTC_CheckDatetimeFormat(const rtc_datetime_t * datetime)65 static bool RTC_CheckDatetimeFormat(const rtc_datetime_t *datetime)
66 {
67     assert(datetime);
68 
69     /* Table of days in a month for a non leap year. First entry in the table is not used,
70      * valid months start from 1
71      */
72     uint8_t daysPerMonth[] = {0U, 31U, 28U, 31U, 30U, 31U, 30U, 31U, 31U, 30U, 31U, 30U, 31U};
73 
74     /* Check year, month, hour, minute, seconds */
75     if ((datetime->year < YEAR_RANGE_START) || (datetime->year > YEAR_RANGE_END) || (datetime->month > 12U) ||
76         (datetime->month < 1U) || (datetime->hour >= 24U) || (datetime->minute >= 60U) || (datetime->second >= 60U))
77     {
78         /* If not correct then error*/
79         return false;
80     }
81 
82     /* Adjust the days in February for a leap year */
83     if ((((datetime->year & 3U) == 0U) && (datetime->year % 100U != 0U)) || (datetime->year % 400U == 0U))
84     {
85         daysPerMonth[2] = 29U;
86     }
87 
88     /* Check the validity of the day */
89     if ((datetime->day > daysPerMonth[datetime->month]) || (datetime->day < 1U))
90     {
91         return false;
92     }
93 
94     return true;
95 }
96 
RTC_ConvertDatetimeToSeconds(const rtc_datetime_t * datetime)97 static uint32_t RTC_ConvertDatetimeToSeconds(const rtc_datetime_t *datetime)
98 {
99     assert(datetime);
100 
101     /* Number of days from begin of the non Leap-year*/
102     /* Number of days from begin of the non Leap-year*/
103     uint16_t monthDays[] = {0U, 0U, 31U, 59U, 90U, 120U, 151U, 181U, 212U, 243U, 273U, 304U, 334U};
104     uint32_t seconds;
105 
106     /* Compute number of days from 1970 till given year*/
107     seconds = ((uint32_t)datetime->year - 1970U) * DAYS_IN_A_YEAR;
108     /* Add leap year days */
109     seconds += (((uint32_t)datetime->year / 4U) - (1970U / 4U));
110     /* Add number of days till given month*/
111     seconds += monthDays[datetime->month];
112     /* Add days in given month. We subtract the current day as it is
113      * represented in the hours, minutes and seconds field*/
114     seconds += ((uint32_t)datetime->day - 1U);
115     /* For leap year if month less than or equal to Febraury, decrement day counter*/
116     if (((datetime->year & 3U) == 0x00U) && (datetime->month <= 2U))
117     {
118         seconds--;
119     }
120 
121     seconds = (seconds * SECONDS_IN_A_DAY) + (datetime->hour * SECONDS_IN_A_HOUR) +
122               (datetime->minute * SECONDS_IN_A_MINUTE) + datetime->second;
123 
124     return seconds;
125 }
126 
RTC_ConvertSecondsToDatetime(uint32_t seconds,rtc_datetime_t * datetime)127 static void RTC_ConvertSecondsToDatetime(uint32_t seconds, rtc_datetime_t *datetime)
128 {
129     assert(datetime);
130 
131     uint8_t i;
132     uint16_t daysInYear;
133     uint32_t secondsRemaining;
134     uint32_t days;
135     /* Table of days in a month for a non leap year. First entry in the table is not used,
136      * valid months start from 1
137      */
138     uint8_t daysPerMonth[] = {0U, 31U, 28U, 31U, 30U, 31U, 30U, 31U, 31U, 30U, 31U, 30U, 31U};
139 
140     /* Start with the seconds value that is passed in to be converted to date time format */
141     secondsRemaining = seconds;
142 
143     /* Calcuate the number of days, we add 1 for the current day which is represented in the
144      * hours and seconds field
145      */
146     days = secondsRemaining / SECONDS_IN_A_DAY + 1U;
147 
148     /* Update seconds left*/
149     secondsRemaining = secondsRemaining % SECONDS_IN_A_DAY;
150 
151     /* Calculate the datetime hour, minute and second fields */
152     datetime->hour   = (uint8_t)(secondsRemaining / SECONDS_IN_A_HOUR);
153     secondsRemaining = secondsRemaining % SECONDS_IN_A_HOUR;
154     datetime->minute = (uint8_t)(secondsRemaining / 60U);
155     datetime->second = (uint8_t)(secondsRemaining % SECONDS_IN_A_MINUTE);
156 
157     /* Calculate year */
158     daysInYear     = DAYS_IN_A_YEAR;
159     datetime->year = YEAR_RANGE_START;
160     while (days > daysInYear)
161     {
162         /* Decrease day count by a year and increment year by 1 */
163         days -= daysInYear;
164         datetime->year++;
165 
166         /* Adjust the number of days for a leap year */
167         if ((datetime->year & 3U) != 0x00U)
168         {
169             daysInYear = DAYS_IN_A_YEAR;
170         }
171         else
172         {
173             daysInYear = DAYS_IN_A_YEAR + 1U;
174         }
175     }
176 
177     /* Adjust the days in February for a leap year */
178     if ((datetime->year & 3U) == 0x00U)
179     {
180         daysPerMonth[2] = 29U;
181     }
182 
183     for (i = 1U; i <= 12U; i++)
184     {
185         if (days <= daysPerMonth[i])
186         {
187             datetime->month = i;
188             break;
189         }
190         else
191         {
192             days -= daysPerMonth[i];
193         }
194     }
195 
196     datetime->day = (uint8_t)days;
197 }
198 
199 /*!
200  * brief Ungates the RTC clock and configures the peripheral for basic operation.
201  *
202  * note This API should be called at the beginning of the application using the RTC driver.
203  *
204  * param base   RTC peripheral base address
205  */
RTC_Init(RTC_Type * base,const rtc_config_t * config)206 void RTC_Init(RTC_Type *base, const rtc_config_t *config)
207 {
208     assert(config);
209 
210     uint32_t srcClock_Hz = 0;
211 #if defined(RTC_CLOCKS)
212 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
213     CLOCK_EnableClock(kCLOCK_Rtc0);
214 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
215 #endif /* RTC_CLOCKS */
216 
217     /* Clear Real-Time Interrupt Flag to 0 */
218     base->SC |= RTC_SC_RTIF_MASK;
219 
220     /* Select Real-Time Clock Source and Clock Prescaler */
221     base->SC &= ~(RTC_SC_RTCLKS_MASK | RTC_SC_RTCPS_MASK);
222     base->SC |= RTC_SC_RTCLKS(config->clockSource) | RTC_SC_RTCPS(config->prescaler);
223 
224     /* Get RTC clock frequency */
225     if (config->clockSource == kRTC_ExternalClock)
226     {
227         srcClock_Hz = CLOCK_GetFreq(kCLOCK_Osc0ErClk);
228     }
229 
230     if (config->clockSource == kRTC_LPOCLK)
231     {
232         srcClock_Hz = CLOCK_GetFreq(kCLOCK_LpoClk);
233     }
234 
235     if (config->clockSource == kRTC_ICSIRCLK)
236     {
237         srcClock_Hz = CLOCK_GetFreq(kCLOCK_ICSInternalRefClk);
238     }
239 
240     if (config->clockSource == kRTC_BusClock)
241     {
242         srcClock_Hz = CLOCK_GetFreq(kCLOCK_BusClk);
243     }
244 
245     /* Set RTC module value */
246     RTC_SetModuloValue(
247         base,
248         (uint32_t)(USEC_TO_COUNT(config->time_us, (uint64_t)srcClock_Hz / (uint64_t)RTC_GetDivideValue(base)) - 1U));
249 }
250 
251 /*!
252  * brief Stops the timer and gate the RTC clock.
253  *
254  * param base RTC peripheral base address
255  */
RTC_Deinit(RTC_Type * base)256 void RTC_Deinit(RTC_Type *base)
257 {
258 #if defined(RTC_CLOCKS)
259 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
260     /* Gate the module clock */
261     CLOCK_DisableClock(kCLOCK_Rtc0);
262 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
263 #endif /* RTC_CLOCKS */
264 }
265 
266 /*!
267  * brief Fills in the RTC config struct with the default settings.
268  *
269  * The default values are as follows.
270  * code
271  *    config->clockSource = kRTC_BusClock;
272  *    config->prescaler = kRTC_ClockDivide_16_2048;
273  *    config->time_us = 1000000U;
274  * endcode
275  * param config Pointer to the user's RTC configuration structure.
276  */
RTC_GetDefaultConfig(rtc_config_t * config)277 void RTC_GetDefaultConfig(rtc_config_t *config)
278 {
279     assert(config != NULL);
280 
281     /* Initializes the configure structure to zero. */
282     (void)memset(config, 0, sizeof(*config));
283 
284     config->clockSource = kRTC_BusClock;
285 
286     config->prescaler = kRTC_ClockDivide_16_2048;
287 
288     /* Configure RTC to interrupt every 1000000us */
289     config->time_us = 1000000U;
290 }
291 
292 /*!
293  * brief  Get the RTC Divide value.
294  *
295  * note This API should be called after selecting clock source and clock prescaler.
296  *
297  * param base    RTC peripheral base address
298  * return  The Divider value. The Divider value depends on clock source and clock prescaler
299  */
RTC_GetDivideValue(RTC_Type * base)300 uint32_t RTC_GetDivideValue(RTC_Type *base)
301 {
302     uint32_t clocks, divide, prescale;
303 
304     /* Get RTCLKS and RTCPS configuration value */
305     clocks   = (uint32_t)((base->SC & RTC_SC_RTCLKS_MASK) >> RTC_SC_RTCLKS_SHIFT);
306     prescale = (uint32_t)((base->SC & RTC_SC_RTCPS_MASK) >> RTC_SC_RTCPS_SHIFT);
307 
308     assert(prescale > 0UL);
309 
310     if ((clocks & 1UL) == 0UL)
311     {
312         divide = 1UL << (prescale - 1UL);
313     }
314     if ((clocks & 1UL) == 1UL)
315     {
316         if (prescale < 6UL)
317         {
318             divide = 1UL << (prescale + 6UL);
319         }
320         else if (prescale == 6UL)
321         {
322             divide = 100UL;
323         }
324         else
325         {
326             divide = 1000UL;
327         }
328     }
329     return divide;
330 }
331 
332 /*!
333  * brief Sets the RTC date and time according to the given time structure.
334  *
335  * param datetime Pointer to the structure where the date and time details are stored.
336  *
337  * return kStatus_Success: Success in setting the time and starting the RTC
338  *         kStatus_InvalidArgument: Error because the datetime format is incorrect
339  */
RTC_SetDatetime(rtc_datetime_t * datetime)340 status_t RTC_SetDatetime(rtc_datetime_t *datetime)
341 {
342     assert(datetime != NULL);
343 
344     status_t status = kStatus_Success;
345 
346     /* Return error if the time provided is not valid */
347     if (!(RTC_CheckDatetimeFormat(datetime)))
348     {
349         status = kStatus_InvalidArgument;
350     }
351     else
352     {
353         /* Set current time seconds */
354         s_CurrentTimeSeconds = RTC_ConvertDatetimeToSeconds(datetime);
355     }
356 
357     return status;
358 }
359 
360 /*!
361  * brief Gets the RTC time and stores it in the given time structure.
362  *
363  * param datetime Pointer to the structure where the date and time details are stored.
364  */
RTC_GetDatetime(rtc_datetime_t * datetime)365 void RTC_GetDatetime(rtc_datetime_t *datetime)
366 {
367     assert(datetime);
368 
369     /* Get current data time */
370     RTC_ConvertSecondsToDatetime(s_CurrentTimeSeconds, datetime);
371 }
372 
373 /*!
374  * brief Sets the RTC alarm time.
375  *
376  * param second   Second value. User input the number of second.
377  *                 After seconds user input, alarm occurs.
378  */
RTC_SetAlarm(uint32_t second)379 void RTC_SetAlarm(uint32_t second)
380 {
381     /* Set alarm time seconds */
382     s_AlarmTimeSeconds = second + s_CurrentTimeSeconds;
383 }
384 
385 /*!
386  * brief Returns the RTC alarm time.
387  *
388  * param datetime Pointer to the structure where the alarm date and time details are stored.
389  */
RTC_GetAlarm(rtc_datetime_t * datetime)390 void RTC_GetAlarm(rtc_datetime_t *datetime)
391 {
392     assert(datetime);
393 
394     /* Get alarm data time */
395     RTC_ConvertSecondsToDatetime(s_AlarmTimeSeconds, datetime);
396 }
397 
398 /*!
399  * brief Set the RTC alarm callback.
400  *
401  * param callback The callback function.
402  */
RTC_SetAlarmCallback(rtc_alarm_callback_t callback)403 void RTC_SetAlarmCallback(rtc_alarm_callback_t callback)
404 {
405     s_RtcAlarmCallback = callback;
406 }
407 
408 void RTC_DriverIRQHandler(void);
RTC_DriverIRQHandler(void)409 void RTC_DriverIRQHandler(void)
410 {
411     uint32_t alarmTimeSeconds = s_AlarmTimeSeconds; /* Fix the order of volatile accesses undefined issue. */
412 
413     if ((RTC_GetInterruptFlags(RTC) & (uint32_t)kRTC_InterruptFlag) != 0U)
414     {
415         s_CurrentTimeSeconds++;
416         /* Clear second interrupt flag */
417         RTC_ClearInterruptFlags(RTC, (uint32_t)kRTC_InterruptFlag);
418     }
419 
420     if (alarmTimeSeconds == s_CurrentTimeSeconds)
421     {
422         if (s_RtcAlarmCallback != NULL)
423         {
424             s_RtcAlarmCallback();
425         }
426     }
427 }
428