1 /*
2  * Copyright (c) 2016, Freescale Semiconductor, Inc.
3  * Copyright 2017-2019, NXP
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 #include "fsl_snvs_hp.h"
10 
11 /*******************************************************************************
12  * Definitions
13  ******************************************************************************/
14 
15 /* Component ID definition, used by tools. */
16 #ifndef FSL_COMPONENT_ID
17 #define FSL_COMPONENT_ID "platform.drivers.snvs_hp"
18 #endif
19 
20 #define SECONDS_IN_A_DAY    (86400U)
21 #define SECONDS_IN_A_HOUR   (3600U)
22 #define SECONDS_IN_A_MINUTE (60U)
23 #define DAYS_IN_A_YEAR      (365U)
24 #define YEAR_RANGE_START    (1970U)
25 #define YEAR_RANGE_END      (2099U)
26 
27 #if !(defined(SNVS_HPSR_PI_MASK))
28 #define SNVS_HPSR_PI_MASK (0x2U)
29 #endif
30 #if !(defined(SNVS_HPSR_HPTA_MASK))
31 #define SNVS_HPSR_HPTA_MASK (0x1U)
32 #endif
33 
34 /*******************************************************************************
35  * Prototypes
36  ******************************************************************************/
37 /*!
38  * @brief Checks whether the date and time passed in is valid
39  *
40  * @param datetime Pointer to structure where the date and time details are stored
41  *
42  * @return Returns false if the date & time details are out of range; true if in range
43  */
44 static bool SNVS_HP_CheckDatetimeFormat(const snvs_hp_rtc_datetime_t *datetime);
45 
46 /*!
47  * @brief Converts time data from datetime to seconds
48  *
49  * @param datetime Pointer to datetime structure where the date and time details are stored
50  *
51  * @return The result of the conversion in seconds
52  */
53 static uint32_t SNVS_HP_ConvertDatetimeToSeconds(const snvs_hp_rtc_datetime_t *datetime);
54 
55 /*!
56  * @brief Converts time data from seconds to a datetime structure
57  *
58  * @param seconds  Seconds value that needs to be converted to datetime format
59  * @param datetime Pointer to the datetime structure where the result of the conversion is stored
60  */
61 static void SNVS_HP_ConvertSecondsToDatetime(uint32_t seconds, snvs_hp_rtc_datetime_t *datetime);
62 
63 /*!
64  * @brief Returns RTC time in seconds.
65  *
66  * This function is used internally to get actual RTC time in seconds.
67  *
68  * @param base SNVS peripheral base address
69  *
70  * @return RTC time in seconds
71  */
72 static uint32_t SNVS_HP_RTC_GetSeconds(SNVS_Type *base);
73 
74 #if (!(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && \
75      defined(SNVS_HP_CLOCKS))
76 /*!
77  * @brief Get the SNVS instance from peripheral base address.
78  *
79  * @param base SNVS peripheral base address.
80  *
81  * @return SNVS instance.
82  */
83 static uint32_t SNVS_HP_GetInstance(SNVS_Type *base);
84 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
85 
86 /*******************************************************************************
87  * Variables
88  ******************************************************************************/
89 #if (!(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && \
90      defined(SNVS_HP_CLOCKS))
91 /*! @brief Pointer to snvs_hp clock. */
92 static const clock_ip_name_t s_snvsHpClock[] = SNVS_HP_CLOCKS;
93 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
94 
95 /*******************************************************************************
96  * Code
97  ******************************************************************************/
SNVS_HP_CheckDatetimeFormat(const snvs_hp_rtc_datetime_t * datetime)98 static bool SNVS_HP_CheckDatetimeFormat(const snvs_hp_rtc_datetime_t *datetime)
99 {
100     assert(datetime != NULL);
101 
102     /* Table of days in a month for a non leap year. First entry in the table is not used,
103      * valid months start from 1
104      */
105     uint8_t daysPerMonth[] = {0U, 31U, 28U, 31U, 30U, 31U, 30U, 31U, 31U, 30U, 31U, 30U, 31U};
106 
107     /* Check year, month, hour, minute, seconds */
108     if ((datetime->year < YEAR_RANGE_START) || (datetime->year > YEAR_RANGE_END) || (datetime->month > 12U) ||
109         (datetime->month < 1U) || (datetime->hour >= 24U) || (datetime->minute >= 60U) || (datetime->second >= 60U))
110     {
111         /* If not correct then error*/
112         return false;
113     }
114 
115     /* Adjust the days in February for a leap year */
116     if ((((datetime->year & 3U) == 0U) && (datetime->year % 100U != 0U)) || (datetime->year % 400U == 0U))
117     {
118         daysPerMonth[2] = 29U;
119     }
120 
121     /* Check the validity of the day */
122     if ((datetime->day > daysPerMonth[datetime->month]) || (datetime->day < 1U))
123     {
124         return false;
125     }
126 
127     return true;
128 }
129 
SNVS_HP_ConvertDatetimeToSeconds(const snvs_hp_rtc_datetime_t * datetime)130 static uint32_t SNVS_HP_ConvertDatetimeToSeconds(const snvs_hp_rtc_datetime_t *datetime)
131 {
132     assert(datetime != NULL);
133 
134     /* Number of days from begin of the non Leap-year*/
135     /* Number of days from begin of the non Leap-year*/
136     uint16_t monthDays[] = {0U, 0U, 31U, 59U, 90U, 120U, 151U, 181U, 212U, 243U, 273U, 304U, 334U};
137     uint32_t seconds;
138 
139     /* Compute number of days from 1970 till given year*/
140     seconds = (((uint32_t)datetime->year - 1970U) * DAYS_IN_A_YEAR);
141     /* Add leap year days */
142     seconds += (((uint32_t)datetime->year / 4U) - (1970U / 4U));
143     /* Add number of days till given month*/
144     seconds += monthDays[datetime->month];
145     /* Add days in given month. We subtract the current day as it is
146      * represented in the hours, minutes and seconds field*/
147     seconds += ((uint32_t)datetime->day - 1U);
148     /* For leap year if month less than or equal to Febraury, decrement day counter*/
149     if ((0U == (datetime->year & 3U)) && (datetime->month <= 2U))
150     {
151         seconds--;
152     }
153 
154     seconds = (seconds * SECONDS_IN_A_DAY) + (datetime->hour * SECONDS_IN_A_HOUR) +
155               (datetime->minute * SECONDS_IN_A_MINUTE) + datetime->second;
156 
157     return seconds;
158 }
159 
SNVS_HP_ConvertSecondsToDatetime(uint32_t seconds,snvs_hp_rtc_datetime_t * datetime)160 static void SNVS_HP_ConvertSecondsToDatetime(uint32_t seconds, snvs_hp_rtc_datetime_t *datetime)
161 {
162     assert(datetime != NULL);
163 
164     uint32_t x;
165     uint32_t secondsRemaining, days;
166     uint16_t daysInYear;
167     /* Table of days in a month for a non leap year. First entry in the table is not used,
168      * valid months start from 1
169      */
170     uint8_t daysPerMonth[] = {0U, 31U, 28U, 31U, 30U, 31U, 30U, 31U, 31U, 30U, 31U, 30U, 31U};
171 
172     /* Start with the seconds value that is passed in to be converted to date time format */
173     secondsRemaining = seconds;
174 
175     /* Calcuate the number of days, we add 1 for the current day which is represented in the
176      * hours and seconds field
177      */
178     days = secondsRemaining / SECONDS_IN_A_DAY + 1U;
179 
180     /* Update seconds left*/
181     secondsRemaining = secondsRemaining % SECONDS_IN_A_DAY;
182 
183     /* Calculate the datetime hour, minute and second fields */
184     datetime->hour   = (uint8_t)(secondsRemaining / SECONDS_IN_A_HOUR);
185     secondsRemaining = secondsRemaining % SECONDS_IN_A_HOUR;
186     datetime->minute = (uint8_t)(secondsRemaining / 60U);
187     datetime->second = (uint8_t)(secondsRemaining % SECONDS_IN_A_MINUTE);
188 
189     /* Calculate year */
190     daysInYear     = DAYS_IN_A_YEAR;
191     datetime->year = YEAR_RANGE_START;
192     while (days > daysInYear)
193     {
194         /* Decrease day count by a year and increment year by 1 */
195         days -= daysInYear;
196         datetime->year++;
197 
198         /* Adjust the number of days for a leap year */
199         if ((datetime->year & 3U) != 0U)
200         {
201             daysInYear = DAYS_IN_A_YEAR;
202         }
203         else
204         {
205             daysInYear = DAYS_IN_A_YEAR + 1U;
206         }
207     }
208 
209     /* Adjust the days in February for a leap year */
210     if (0U == (datetime->year & 3U))
211     {
212         daysPerMonth[2] = 29U;
213     }
214 
215     for (x = 1U; x <= 12U; x++)
216     {
217         if (days <= daysPerMonth[x])
218         {
219             datetime->month = (uint8_t)x;
220             break;
221         }
222         else
223         {
224             days -= daysPerMonth[x];
225         }
226     }
227 
228     datetime->day = (uint8_t)days;
229 }
230 
231 #if (!(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && \
232      defined(SNVS_HP_CLOCKS))
SNVS_HP_GetInstance(SNVS_Type * base)233 static uint32_t SNVS_HP_GetInstance(SNVS_Type *base)
234 {
235     return 0U;
236 }
237 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
238 
239 /*!
240  * brief Initialize the SNVS.
241  *
242  * note This API should be called at the beginning of the application using the SNVS driver.
243  *
244  * param base SNVS peripheral base address
245  */
SNVS_HP_Init(SNVS_Type * base)246 void SNVS_HP_Init(SNVS_Type *base)
247 {
248 #if (!(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && \
249      defined(SNVS_HP_CLOCKS))
250     uint32_t instance = SNVS_HP_GetInstance(base);
251     CLOCK_EnableClock(s_snvsHpClock[instance]);
252 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
253 }
254 
255 /*!
256  * brief Deinitialize the SNVS.
257  *
258  * param base SNVS peripheral base address
259  */
SNVS_HP_Deinit(SNVS_Type * base)260 void SNVS_HP_Deinit(SNVS_Type *base)
261 {
262 #if (!(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && \
263      defined(SNVS_HP_CLOCKS))
264     uint32_t instance = SNVS_HP_GetInstance(base);
265     CLOCK_DisableClock(s_snvsHpClock[instance]);
266 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
267 }
268 
269 /*!
270  * brief Ungates the SNVS clock and configures the peripheral for basic operation.
271  *
272  * note This API should be called at the beginning of the application using the SNVS driver.
273  *
274  * param base   SNVS peripheral base address
275  * param config Pointer to the user's SNVS configuration structure.
276  */
SNVS_HP_RTC_Init(SNVS_Type * base,const snvs_hp_rtc_config_t * config)277 void SNVS_HP_RTC_Init(SNVS_Type *base, const snvs_hp_rtc_config_t *config)
278 {
279     assert(config != NULL);
280 
281 #if (!(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && \
282      defined(SNVS_HP_CLOCKS))
283     uint32_t instance = SNVS_HP_GetInstance(base);
284     CLOCK_EnableClock(s_snvsHpClock[instance]);
285 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
286 
287     base->HPCOMR |= SNVS_HPCOMR_NPSWA_EN_MASK;
288 
289     base->HPCR = SNVS_HPCR_PI_FREQ(config->periodicInterruptFreq);
290 
291     if (config->rtcCalEnable)
292     {
293         base->HPCR |= SNVS_HPCR_HPCALB_VAL_MASK & (config->rtcCalValue << SNVS_HPCR_HPCALB_VAL_SHIFT);
294         base->HPCR |= SNVS_HPCR_HPCALB_EN_MASK;
295     }
296 }
297 
298 /*!
299  * brief Stops the RTC and SRTC timers.
300  *
301  * param base SNVS peripheral base address
302  */
SNVS_HP_RTC_Deinit(SNVS_Type * base)303 void SNVS_HP_RTC_Deinit(SNVS_Type *base)
304 {
305     base->HPCR &= ~SNVS_HPCR_RTC_EN_MASK;
306 
307 #if (!(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && \
308      defined(SNVS_HP_CLOCKS))
309     uint32_t instance = SNVS_HP_GetInstance(base);
310     CLOCK_DisableClock(s_snvsHpClock[instance]);
311 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
312 }
313 
314 /*!
315  * brief Fills in the SNVS config struct with the default settings.
316  *
317  * The default values are as follows.
318  * code
319  *    config->rtccalenable = false;
320  *    config->rtccalvalue = 0U;
321  *    config->PIFreq = 0U;
322  * endcode
323  * param config Pointer to the user's SNVS configuration structure.
324  */
SNVS_HP_RTC_GetDefaultConfig(snvs_hp_rtc_config_t * config)325 void SNVS_HP_RTC_GetDefaultConfig(snvs_hp_rtc_config_t *config)
326 {
327     assert(config != NULL);
328 
329     /* Initializes the configure structure to zero. */
330     (void)memset(config, 0, sizeof(*config));
331 
332     config->rtcCalEnable          = false;
333     config->rtcCalValue           = 0U;
334     config->periodicInterruptFreq = 0U;
335 }
336 
SNVS_HP_RTC_GetSeconds(SNVS_Type * base)337 static uint32_t SNVS_HP_RTC_GetSeconds(SNVS_Type *base)
338 {
339     uint32_t seconds = 0;
340     uint32_t tmp     = 0;
341 
342     /* Do consecutive reads until value is correct */
343     do
344     {
345         seconds = tmp;
346         tmp     = (base->HPRTCMR << 17U);
347         tmp |= (base->HPRTCLR >> 15U);
348     } while (tmp != seconds);
349 
350     return seconds;
351 }
352 
353 /*!
354  * brief Sets the SNVS RTC date and time according to the given time structure.
355  *
356  * param base     SNVS peripheral base address
357  * param datetime Pointer to the structure where the date and time details are stored.
358  *
359  * return kStatus_Success: Success in setting the time and starting the SNVS RTC
360  *         kStatus_InvalidArgument: Error because the datetime format is incorrect
361  */
SNVS_HP_RTC_SetDatetime(SNVS_Type * base,const snvs_hp_rtc_datetime_t * datetime)362 status_t SNVS_HP_RTC_SetDatetime(SNVS_Type *base, const snvs_hp_rtc_datetime_t *datetime)
363 {
364     assert(datetime != NULL);
365 
366     uint32_t seconds = 0U;
367     uint32_t tmp     = base->HPCR;
368 
369     /* disable RTC */
370     SNVS_HP_RTC_StopTimer(base);
371 
372     /* Return error if the time provided is not valid */
373     if (!(SNVS_HP_CheckDatetimeFormat(datetime)))
374     {
375         return kStatus_InvalidArgument;
376     }
377 
378     /* Set time in seconds */
379     seconds = SNVS_HP_ConvertDatetimeToSeconds(datetime);
380 
381     base->HPRTCMR = (uint32_t)(seconds >> 17U);
382     base->HPRTCLR = (uint32_t)(seconds << 15U);
383 
384     /* reenable RTC in case that it was enabled before */
385     if ((tmp & SNVS_HPCR_RTC_EN_MASK) != 0U)
386     {
387         SNVS_HP_RTC_StartTimer(base);
388     }
389 
390     return kStatus_Success;
391 }
392 
393 /*!
394  * brief Gets the SNVS RTC time and stores it in the given time structure.
395  *
396  * param base     SNVS peripheral base address
397  * param datetime Pointer to the structure where the date and time details are stored.
398  */
SNVS_HP_RTC_GetDatetime(SNVS_Type * base,snvs_hp_rtc_datetime_t * datetime)399 void SNVS_HP_RTC_GetDatetime(SNVS_Type *base, snvs_hp_rtc_datetime_t *datetime)
400 {
401     assert(datetime != NULL);
402 
403     SNVS_HP_ConvertSecondsToDatetime(SNVS_HP_RTC_GetSeconds(base), datetime);
404 }
405 
406 /*!
407  * brief Sets the SNVS RTC alarm time.
408  *
409  * The function sets the RTC alarm. It also checks whether the specified alarm time
410  * is greater than the present time. If not, the function does not set the alarm
411  * and returns an error.
412  *
413  * param base      SNVS peripheral base address
414  * param alarmTime Pointer to the structure where the alarm time is stored.
415  *
416  * return kStatus_Success: success in setting the SNVS RTC alarm
417  *         kStatus_InvalidArgument: Error because the alarm datetime format is incorrect
418  *         kStatus_Fail: Error because the alarm time has already passed
419  */
SNVS_HP_RTC_SetAlarm(SNVS_Type * base,const snvs_hp_rtc_datetime_t * alarmTime)420 status_t SNVS_HP_RTC_SetAlarm(SNVS_Type *base, const snvs_hp_rtc_datetime_t *alarmTime)
421 {
422     assert(alarmTime != NULL);
423 
424     uint32_t alarmSeconds = 0U;
425     uint32_t currSeconds  = 0U;
426     uint32_t tmp          = base->HPCR;
427 
428     /* Return error if the alarm time provided is not valid */
429     if (!(SNVS_HP_CheckDatetimeFormat(alarmTime)))
430     {
431         return kStatus_InvalidArgument;
432     }
433 
434     alarmSeconds = SNVS_HP_ConvertDatetimeToSeconds(alarmTime);
435     currSeconds  = SNVS_HP_RTC_GetSeconds(base);
436 
437     /* Return error if the alarm time has passed */
438     if (alarmSeconds < currSeconds)
439     {
440         return kStatus_Fail;
441     }
442 
443     /* disable RTC alarm interrupt */
444     base->HPCR &= ~SNVS_HPCR_HPTA_EN_MASK;
445     while ((base->HPCR & SNVS_HPCR_HPTA_EN_MASK) != 0U)
446     {
447     }
448 
449     /* Set alarm in seconds*/
450     base->HPTAMR = (uint32_t)(alarmSeconds >> 17U);
451     base->HPTALR = (uint32_t)(alarmSeconds << 15U);
452 
453     /* reenable RTC alarm interrupt in case that it was enabled before */
454     base->HPCR = tmp;
455 
456     return kStatus_Success;
457 }
458 
459 /*!
460  * brief Returns the SNVS RTC alarm time.
461  *
462  * param base     SNVS peripheral base address
463  * param datetime Pointer to the structure where the alarm date and time details are stored.
464  */
SNVS_HP_RTC_GetAlarm(SNVS_Type * base,snvs_hp_rtc_datetime_t * datetime)465 void SNVS_HP_RTC_GetAlarm(SNVS_Type *base, snvs_hp_rtc_datetime_t *datetime)
466 {
467     assert(datetime != NULL);
468 
469     uint32_t alarmSeconds = 0U;
470 
471     /* Get alarm in seconds  */
472     alarmSeconds = (base->HPTAMR << 17U);
473     alarmSeconds |= (base->HPTALR >> 15U);
474 
475     SNVS_HP_ConvertSecondsToDatetime(alarmSeconds, datetime);
476 }
477 
478 #if (defined(FSL_FEATURE_SNVS_HAS_SRTC) && (FSL_FEATURE_SNVS_HAS_SRTC > 0))
479 /*!
480  * brief The function synchronizes RTC counter value with SRTC.
481  *
482  * param base SNVS peripheral base address
483  */
SNVS_HP_RTC_TimeSynchronize(SNVS_Type * base)484 void SNVS_HP_RTC_TimeSynchronize(SNVS_Type *base)
485 {
486     uint32_t tmp = base->HPCR;
487 
488     /* disable RTC */
489     SNVS_HP_RTC_StopTimer(base);
490 
491     base->HPCR |= SNVS_HPCR_HP_TS_MASK;
492 
493     /* reenable RTC in case that it was enabled before */
494     if ((tmp & SNVS_HPCR_RTC_EN_MASK) != 0U)
495     {
496         SNVS_HP_RTC_StartTimer(base);
497     }
498 }
499 #endif /* FSL_FEATURE_SNVS_HAS_SRTC */
500 
501 /*!
502  * brief Gets the SNVS status flags.
503  *
504  * param base SNVS peripheral base address
505  *
506  * return The status flags. This is the logical OR of members of the
507  *         enumeration ::snvs_status_flags_t
508  */
SNVS_HP_RTC_GetStatusFlags(SNVS_Type * base)509 uint32_t SNVS_HP_RTC_GetStatusFlags(SNVS_Type *base)
510 {
511     uint32_t flags = 0U;
512 
513     if ((base->HPSR & SNVS_HPSR_PI_MASK) != 0U)
514     {
515         flags |= (uint32_t)kSNVS_RTC_PeriodicInterruptFlag;
516     }
517 
518     if ((base->HPSR & SNVS_HPSR_HPTA_MASK) != 0U)
519     {
520         flags |= (uint32_t)kSNVS_RTC_AlarmInterruptFlag;
521     }
522 
523     return flags;
524 }
525 
526 /*!
527  * brief Gets the enabled SNVS interrupts.
528  *
529  * param base SNVS peripheral base address
530  *
531  * return The enabled interrupts. This is the logical OR of members of the
532  *         enumeration ::snvs_interrupt_enable_t
533  */
SNVS_HP_RTC_GetEnabledInterrupts(SNVS_Type * base)534 uint32_t SNVS_HP_RTC_GetEnabledInterrupts(SNVS_Type *base)
535 {
536     uint32_t val = 0U;
537 
538     if ((base->HPCR & SNVS_HPCR_PI_EN_MASK) != 0U)
539     {
540         val |= (uint32_t)kSNVS_RTC_PeriodicInterrupt;
541     }
542 
543     if ((base->HPCR & SNVS_HPCR_HPTA_EN_MASK) != 0U)
544     {
545         val |= (uint32_t)kSNVS_RTC_AlarmInterrupt;
546     }
547 
548     return val;
549 }
550 
551 #if defined(FSL_FEATURE_SNVS_HAS_SET_LOCK) && (FSL_FEATURE_SNVS_HAS_SET_LOCK > 0)
552 /*!
553  * brief Set SNVS HP Set locks.
554  *
555  * param base SNVS peripheral base address
556  *
557  */
SNVS_HP_SetLocks(SNVS_Type * base)558 void SNVS_HP_SetLocks(SNVS_Type *base)
559 {
560     uint32_t sec_config = ((OCOTP_CTRL->HW_OCOTP_OTFAD_CFG3 & OCOTP_CTRL_HW_OCOTP_SEC_CONFIG1_MASK) >>
561                            OCOTP_CTRL_HW_OCOTP_SEC_CONFIG1_SHIFT);
562 
563     if (sec_config == SEC_CONFIG_OPEN)
564     {
565         /* Enable non-secure SW access */
566         base->HPCOMR |= SNVS_HPCOMR_NPSWA_EN(1);
567     }
568 
569     /* Set LP Software Reset Disable lock and ZMK Write Soft Lock */
570     base->HPCOMR |= SNVS_HPCOMR_LP_SWR_DIS(1);
571     base->HPLR |= SNVS_HPLR_ZMK_WSL(1);
572 }
573 #endif /* FSL_FEATURE_SNVS_HAS_SET_LOCK */
574