1 /***************************************************************************//**
2 * \file cyhal_rtc.c
3 *
4 * \brief
5 * Provides a high level interface for interacting with the Infineon Real-Time Clock.
6 * This interface abstracts out the chip specific details. If any chip specific
7 * functionality is necessary, or performance is critical the low level functions
8 * can be used directly.
9 *
10 ********************************************************************************
11 * \copyright
12 * Copyright 2018-2022 Cypress Semiconductor Corporation (an Infineon company) or
13 * an affiliate of Cypress Semiconductor Corporation
14 *
15 * SPDX-License-Identifier: Apache-2.0
16 *
17 * Licensed under the Apache License, Version 2.0 (the "License");
18 * you may not use this file except in compliance with the License.
19 * You may obtain a copy of the License at
20 *
21 *     http://www.apache.org/licenses/LICENSE-2.0
22 *
23 * Unless required by applicable law or agreed to in writing, software
24 * distributed under the License is distributed on an "AS IS" BASIS,
25 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26 * See the License for the specific language governing permissions and
27 * limitations under the License.
28 *******************************************************************************/
29 
30 #include "cy_rtc.h"
31 #include "cy_utils.h"
32 #include "cyhal_rtc.h"
33 #include "cyhal_system.h"
34 #include "cyhal_utils_impl.h"
35 #include "cyhal_irq_impl.h"
36 
37 /**
38 * \addtogroup group_hal_impl_rtc RTC (Real Time Clock)
39 * \ingroup group_hal_impl
40 * \{
41 *
42 * Internally the CAT1 RTC only stores the year as a two digit BCD value
43 * (0-99); no century information is stored. On RTC initialization the HAL must,
44 * as a result, assume a default century. If cyhal_rtc_write has been called
45 * with a different century than the default, its value must be stored and that
46 * value must persist through deep sleep, hibernate, software resets, etc. CAT1
47 * hardware provides a number of BREG registers which exist in the BACKUP domain
48 * and will persist over these power modes and resets. The HAL uses the highest
49 * indexed BACKUP->BREG register to store the century for the RTC.
50 *
51 * Therefore do not use the highest indexed BACKUP->BREG register as it is
52 * reserved for internal HAL usage.
53 *
54 * \note A century rollover (eg: 1999 to 2000) will only be detected if the backup
55 * domain is reset. This happens automatically on a hardware reset, or can be done
56 * manually by calling Cy_SysLib_ResetBackupDomain(). Calling the reset function
57 * will clear any existing RTC/WCO/WDT configuration, so they must be setup after
58 * the reset.
59 *
60 * \} group_hal_impl_wdt
61 */
62 
63 #if (CYHAL_DRIVER_AVAILABLE_RTC)
64 
65 #if defined(__cplusplus)
66 extern "C" {
67 #endif
68 
69 #define _CYHAL_RTC_STATE_UNINITIALIZED 0
70 #define _CYHAL_RTC_STATE_ENABLED 1
71 #define _CYHAL_RTC_STATE_TIME_SET 2
72 #if (CORE == CM0P)
73 // To account for the lower __NVIC_PRIO_BITS value
74 #define _CYHAL_RTC_DEFAULT_PRIORITY 3
75 #else
76 #define _CYHAL_RTC_DEFAULT_PRIORITY 5
77 #endif // (defined(COMPONENT_CAT1C) && (CORE == CM0P))
78 #define _CYHAL_RTC_INIT_CENTURY 2000
79 #define _CYHAL_RTC_TM_YEAR_BASE 1900
80 
81 #if defined(COMPONENT_CAT1A) || defined (COMPONENT_CAT1C)
82     #define _CYHAL_RTC_BREG (BACKUP->BREG[SRSS_BACKUP_NUM_BREG-1])
83 #elif defined(COMPONENT_CAT1B)
84     #if defined(SRSS_BACKUP_NUM_BREG3) && (SRSS_BACKUP_NUM_BREG3 > 0)
85     #define _CYHAL_RTC_BREG (BACKUP->BREG_SET3[SRSS_BACKUP_NUM_BREG3-1])
86     #elif defined(SRSS_BACKUP_NUM_BREG2) && (SRSS_BACKUP_NUM_BREG2 > 0)
87     #define _CYHAL_RTC_BREG (BACKUP->BREG_SET2[SRSS_BACKUP_NUM_BREG2-1])
88     #elif defined(SRSS_BACKUP_NUM_BREG1) && (SRSS_BACKUP_NUM_BREG1 > 0)
89     #define _CYHAL_RTC_BREG (BACKUP->BREG_SET1[SRSS_BACKUP_NUM_BREG1-1])
90     #elif defined(SRSS_BACKUP_NUM_BREG0) && (SRSS_BACKUP_NUM_BREG0 > 0)
91     #define _CYHAL_RTC_BREG (BACKUP->BREG_SET0[SRSS_BACKUP_NUM_BREG0-1])
92     #endif
93 #elif defined(COMPONENT_CAT1D)
94     #if defined(SRSS_NUM_HIBDATA) && ((SRSS_NUM_HIBDATA) > 0)
95     #define _CYHAL_RTC_BREG (SRSS->PWR_HIB_DATA[SRSS_NUM_HIBDATA-1])
96     #endif
97 #endif /* defined(COMPONENT_CAT1B) */
98 
99 #define _CYHAL_RTC_BREG_CENTURY_Pos 0UL
100 #define _CYHAL_RTC_BREG_CENTURY_Msk 0x0000FFFFUL
101 #define _CYHAL_RTC_BREG_STATE_Pos 16UL
102 #define _CYHAL_RTC_BREG_STATE_Msk 0xFFFF0000UL
103 
104 #if defined(COMPONENT_CAT1D)
105 #define _CYHAL_RTC_IRQ_NAME      (srss_interrupt_rtc_IRQn)
106 #else
107 #define _CYHAL_RTC_IRQ_NAME      (srss_interrupt_backup_IRQn)
108 #endif /* defined(COMPONENT_CAT1D) */
109 
110 static const uint32_t _CYHAL_RTC_MAX_RETRY = 10;
111 static const uint32_t _CYHAL_RTC_RETRY_DELAY_MS = 1;
112 
113 // Note: Use PDL directly rather than HAL. RTOS-aware delay is not needed and actually breaks functionality.
114 #define _CYHAL_RTC_WAIT_ONE_MS()   Cy_SysLib_Delay(_CYHAL_RTC_RETRY_DELAY_MS);
115 
_cyhal_rtc_from_pdl_time(cy_stc_rtc_config_t * pdlTime,const int year,struct tm * time)116 static void _cyhal_rtc_from_pdl_time(cy_stc_rtc_config_t *pdlTime, const int year, struct tm *time) {
117     CY_ASSERT(NULL != pdlTime);
118     CY_ASSERT(NULL != time);
119 
120     // The number of days that precede each month of the year, not including Feb 29
121     static const uint16_t CUMULATIVE_DAYS[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
122 
123     time->tm_sec = (int)pdlTime->sec;
124     time->tm_min = (int)pdlTime->min;
125     time->tm_hour = (int)pdlTime->hour;
126     time->tm_mday = (int)pdlTime->date;
127     time->tm_mon = (int)(pdlTime->month - 1u);
128     time->tm_year = (int)(year - _CYHAL_RTC_TM_YEAR_BASE);
129     time->tm_wday = (int)(pdlTime->dayOfWeek - 1u);
130     time->tm_yday = (int)CUMULATIVE_DAYS[time->tm_mon] + (int)pdlTime->date - 1 +
131         (((int)(pdlTime->month) >= 3 && (int)(Cy_RTC_IsLeapYear((uint32_t)year) ? 1u : 0u)));
132     time->tm_isdst = -1;
133 }
134 
135 /** Wrapper around the PDL Cy_RTC_DeepSleepCallback to adapt the function signature */
_cyhal_rtc_syspm_callback(cy_stc_syspm_callback_params_t * params,cy_en_syspm_callback_mode_t mode)136 static cy_en_syspm_status_t _cyhal_rtc_syspm_callback(cy_stc_syspm_callback_params_t *params, cy_en_syspm_callback_mode_t mode)
137 {
138     return Cy_RTC_DeepSleepCallback(params, mode);
139 }
140 
141 static bool _cyhal_rtc_dst_skip_next_alarm = false;
142 static cy_stc_rtc_dst_t *_cyhal_rtc_dst;
143 static cy_stc_syspm_callback_params_t _cyhal_rtc_pm_cb_params = {NULL, NULL};
144 static cy_stc_syspm_callback_t _cyhal_rtc_pm_cb = {
145     .callback = &_cyhal_rtc_syspm_callback,
146     .type = CY_SYSPM_DEEPSLEEP,
147     .callbackParams = &_cyhal_rtc_pm_cb_params,
148 };
149 
150 static cyhal_rtc_event_callback_t _cyhal_rtc_user_handler;
151 static void *_cyhal_rtc_handler_arg;
152 
153 /* Returns century portion of BREG register used to store century info */
_cyhal_rtc_get_century(void)154 static inline uint16_t _cyhal_rtc_get_century(void)
155 {
156     return _FLD2VAL(_CYHAL_RTC_BREG_CENTURY, _CYHAL_RTC_BREG);
157 }
158 
159 /* Sets century portion of BREG register used to store century info */
_cyhal_rtc_set_century(uint16_t century)160 static inline void _cyhal_rtc_set_century(uint16_t century)
161 {
162     _CYHAL_RTC_BREG &= _CYHAL_RTC_BREG_STATE_Msk;
163     _CYHAL_RTC_BREG |= _VAL2FLD(_CYHAL_RTC_BREG_CENTURY, century);
164 }
165 
166 /* Returns state portion of BREG register used to store century info */
_cyhal_rtc_get_state(void)167 static inline uint16_t _cyhal_rtc_get_state(void)
168 {
169     return _FLD2VAL(_CYHAL_RTC_BREG_STATE, _CYHAL_RTC_BREG);
170 }
171 
172 /* Sets state portion of BREG register used to store century info */
_cyhal_rtc_set_state(uint16_t init)173 static inline void _cyhal_rtc_set_state(uint16_t init)
174 {
175     _CYHAL_RTC_BREG &= _CYHAL_RTC_BREG_CENTURY_Msk;
176     _CYHAL_RTC_BREG |= _VAL2FLD(_CYHAL_RTC_BREG_STATE, init);
177 }
178 
_cyhal_rtc_initialize_dst(const cyhal_rtc_dst_t * hal,cy_stc_rtc_dst_format_t * pdl)179 static void _cyhal_rtc_initialize_dst(const cyhal_rtc_dst_t *hal, cy_stc_rtc_dst_format_t *pdl)
180 {
181     pdl->format = (hal->format == CYHAL_RTC_DST_FIXED) ? CY_RTC_DST_FIXED : CY_RTC_DST_RELATIVE;
182     pdl->hour = hal->hour;
183     pdl->dayOfMonth = (hal->format == CYHAL_RTC_DST_FIXED) ? hal->dayOfMonth : 1;
184     pdl->weekOfMonth = (hal->format == CYHAL_RTC_DST_FIXED) ? 1 : hal->weekOfMonth + 1;
185     pdl->dayOfWeek = (hal->format == CYHAL_RTC_DST_FIXED) ? 1 : hal->dayOfWeek + 1;
186     pdl->month = hal->month;
187 }
188 
189 /** Wrapper around the PDL RTC interrupt handler to adapt the function signature */
_cyhal_rtc_isr_handler(void)190 static void _cyhal_rtc_isr_handler(void)
191 {
192     Cy_RTC_Interrupt(_cyhal_rtc_dst, NULL != _cyhal_rtc_dst);
193 }
194 
195 /* Override weak function from PDL */
Cy_RTC_Alarm1Interrupt(void)196 void Cy_RTC_Alarm1Interrupt(void)
197 {
198     if (_cyhal_rtc_dst_skip_next_alarm)
199     {
200         _cyhal_rtc_dst_skip_next_alarm = false;
201     }
202     else if (NULL != _cyhal_rtc_user_handler)
203     {
204         (*_cyhal_rtc_user_handler)(_cyhal_rtc_handler_arg, CYHAL_RTC_ALARM);
205     }
206 }
207 
Cy_RTC_CenturyInterrupt(void)208 void Cy_RTC_CenturyInterrupt(void)
209 {
210     _cyhal_rtc_set_century(_cyhal_rtc_get_century() + 100);
211 }
212 
_cyhal_rtc_init_common(const cy_stc_rtc_config_t * default_time)213 static cy_rslt_t _cyhal_rtc_init_common(const cy_stc_rtc_config_t* default_time)
214 {
215     cy_rslt_t rslt = CY_RSLT_SUCCESS;
216     if (_cyhal_rtc_get_state() == _CYHAL_RTC_STATE_UNINITIALIZED)
217     {
218         if (Cy_RTC_IsExternalResetOccurred())
219         {
220             // Reset to default time
221             Cy_RTC_SetDateAndTime(default_time);
222             _cyhal_rtc_set_century(_CYHAL_RTC_INIT_CENTURY);
223         }
224 
225         if (Cy_SysPm_RegisterCallback(&_cyhal_rtc_pm_cb))
226         {
227             _cyhal_rtc_set_state(_CYHAL_RTC_STATE_ENABLED);
228         }
229         else
230         {
231             rslt = CY_RSLT_RTC_NOT_INITIALIZED;
232         }
233     }
234     else if(_cyhal_rtc_get_state() == _CYHAL_RTC_STATE_ENABLED || _cyhal_rtc_get_state() == _CYHAL_RTC_STATE_TIME_SET)
235     {
236         if (Cy_RTC_GetInterruptStatus() & CY_RTC_INTR_CENTURY)
237             Cy_RTC_CenturyInterrupt();
238     }
239 
240     Cy_RTC_ClearInterrupt(CY_RTC_INTR_CENTURY);
241     Cy_RTC_SetInterruptMask(CY_RTC_INTR_CENTURY);
242     _cyhal_irq_register(_CYHAL_RTC_IRQ_NAME, _CYHAL_RTC_DEFAULT_PRIORITY, _cyhal_rtc_isr_handler);
243 
244     if (rslt == CY_RSLT_SUCCESS)
245     {
246         _cyhal_rtc_dst = NULL;
247         _cyhal_irq_enable(_CYHAL_RTC_IRQ_NAME);
248     }
249 
250     return rslt;
251 }
252 
cyhal_rtc_init(cyhal_rtc_t * obj)253 cy_rslt_t cyhal_rtc_init(cyhal_rtc_t *obj)
254 {
255     CY_UNUSED_PARAMETER(obj);
256     CY_ASSERT(NULL != obj);
257 
258     static const cy_stc_rtc_config_t default_time =
259     {
260         .dayOfWeek = CY_RTC_SATURDAY,
261         .date = 1,
262         .month = 1,
263         .year = 0,
264     };
265     return _cyhal_rtc_init_common(&default_time);
266 }
267 
cyhal_rtc_init_cfg(cyhal_rtc_t * obj,const cyhal_rtc_configurator_t * cfg)268 cy_rslt_t cyhal_rtc_init_cfg(cyhal_rtc_t *obj, const cyhal_rtc_configurator_t *cfg)
269 {
270     CY_UNUSED_PARAMETER(obj);
271     CY_ASSERT(NULL != obj);
272 
273     cy_rslt_t rslt = _cyhal_rtc_init_common(cfg->config);
274     if (NULL != cfg->dst_config)
275     {
276         _cyhal_rtc_set_state(_CYHAL_RTC_STATE_TIME_SET);
277 
278         cy_stc_rtc_config_t dateTime;
279         Cy_RTC_GetDateAndTime(&dateTime);
280         rslt = Cy_RTC_EnableDstTime(cfg->dst_config, &dateTime);
281         if (rslt == CY_RSLT_SUCCESS)
282         {
283             obj->dst = *(cfg->dst_config);
284             _cyhal_rtc_dst = &(obj->dst);
285         }
286     }
287     return rslt;
288 }
289 
cyhal_rtc_free(cyhal_rtc_t * obj)290 void cyhal_rtc_free(cyhal_rtc_t *obj)
291 {
292     CY_UNUSED_PARAMETER(obj);
293     CY_ASSERT(NULL != obj);
294     _cyhal_irq_free(_CYHAL_RTC_IRQ_NAME);
295 
296     Cy_RTC_SetInterruptMask(CY_RTC_INTR_CENTURY);
297     _cyhal_rtc_dst = NULL;
298 }
299 
cyhal_rtc_is_enabled(cyhal_rtc_t * obj)300 bool cyhal_rtc_is_enabled(cyhal_rtc_t *obj)
301 {
302     CY_UNUSED_PARAMETER(obj);
303     CY_ASSERT(NULL != obj);
304     return (_cyhal_rtc_get_state() == _CYHAL_RTC_STATE_TIME_SET);
305 }
306 
cyhal_rtc_read(cyhal_rtc_t * obj,struct tm * time)307 cy_rslt_t cyhal_rtc_read(cyhal_rtc_t *obj, struct tm *time)
308 {
309     CY_UNUSED_PARAMETER(obj);
310     CY_ASSERT(NULL != obj);
311 
312     cy_stc_rtc_config_t dateTime = { .hrFormat = CY_RTC_24_HOURS };
313     uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
314     Cy_RTC_GetDateAndTime(&dateTime);
315     const int year = (int)(dateTime.year + _cyhal_rtc_get_century());
316     cyhal_system_critical_section_exit(savedIntrStatus);
317 
318     _cyhal_rtc_from_pdl_time(&dateTime, year, time);
319 
320     return CY_RSLT_SUCCESS;
321 }
322 
cyhal_rtc_write(cyhal_rtc_t * obj,const struct tm * time)323 cy_rslt_t cyhal_rtc_write(cyhal_rtc_t *obj, const struct tm *time)
324 {
325     CY_ASSERT(NULL != obj);
326     return cyhal_rtc_write_direct(obj, time->tm_sec, time->tm_min, time->tm_hour, time->tm_mday,
327                                     time->tm_mon + 1, _CYHAL_RTC_TM_YEAR_BASE + time->tm_year);
328 }
329 
cyhal_rtc_write_direct(cyhal_rtc_t * obj,uint32_t sec,uint32_t min,uint32_t hour,uint32_t day,uint32_t month,uint32_t year)330 cy_rslt_t cyhal_rtc_write_direct(cyhal_rtc_t *obj, uint32_t sec, uint32_t min, uint32_t hour,
331                                  uint32_t day, uint32_t month, uint32_t year)
332 {
333     CY_UNUSED_PARAMETER(obj);
334     uint32_t year2digit = year % 100;
335 
336     cy_rslt_t rslt;
337     uint32_t retry = 0;
338     if (!CY_RTC_IS_SEC_VALID(sec) || !CY_RTC_IS_MIN_VALID(min) || !CY_RTC_IS_HOUR_VALID(hour) || !CY_RTC_IS_MONTH_VALID(month) || !CY_RTC_IS_YEAR_SHORT_VALID(year2digit))
339     {
340         return CY_RSLT_RTC_BAD_ARGUMENT;
341     }
342     do
343     {
344         if (retry != 0)
345             _CYHAL_RTC_WAIT_ONE_MS();
346         uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
347         rslt = Cy_RTC_SetDateAndTimeDirect(sec, min, hour, day, month, year2digit);
348         if (rslt == CY_RSLT_SUCCESS)
349             _cyhal_rtc_set_century((uint16_t)(year) - (uint16_t)(year2digit));
350         cyhal_system_critical_section_exit(savedIntrStatus);
351         ++retry;
352     } while (rslt == CY_RTC_INVALID_STATE && retry < _CYHAL_RTC_MAX_RETRY);
353 
354     retry = 0;
355     while (CY_RTC_BUSY == Cy_RTC_GetSyncStatus() && retry < _CYHAL_RTC_MAX_RETRY)
356     {
357         _CYHAL_RTC_WAIT_ONE_MS();
358         ++retry;
359     }
360 
361     if (rslt == CY_RSLT_SUCCESS)
362     {
363         _cyhal_rtc_set_state(_CYHAL_RTC_STATE_TIME_SET);
364     }
365     return rslt;
366 }
367 
cyhal_rtc_set_dst(cyhal_rtc_t * obj,const cyhal_rtc_dst_t * start,const cyhal_rtc_dst_t * stop)368 cy_rslt_t cyhal_rtc_set_dst(cyhal_rtc_t *obj, const cyhal_rtc_dst_t *start, const cyhal_rtc_dst_t *stop)
369 {
370     CY_UNUSED_PARAMETER(obj);
371     CY_ASSERT(NULL != obj);
372     CY_ASSERT(NULL != start);
373     CY_ASSERT(NULL != stop);
374 
375     _cyhal_rtc_initialize_dst(start, &(obj->dst.startDst));
376     _cyhal_rtc_initialize_dst(stop, &(obj->dst.stopDst));
377 
378     cy_stc_rtc_config_t dateTime;
379     Cy_RTC_GetDateAndTime(&dateTime);
380     cy_rslt_t rslt = Cy_RTC_EnableDstTime(&(obj->dst), &dateTime);
381     if (rslt == CY_RSLT_SUCCESS)
382         _cyhal_rtc_dst = &(obj->dst);
383 
384     return rslt;
385 }
386 
cyhal_rtc_is_dst(cyhal_rtc_t * obj)387 bool cyhal_rtc_is_dst(cyhal_rtc_t *obj)
388 {
389     CY_UNUSED_PARAMETER(obj);
390     CY_ASSERT(NULL != obj);
391 
392     cy_stc_rtc_config_t dateTime;
393     Cy_RTC_GetDateAndTime(&dateTime);
394     return Cy_RTC_GetDstStatus(_cyhal_rtc_dst, &dateTime);
395 }
396 
cyhal_rtc_set_alarm(cyhal_rtc_t * obj,const struct tm * time,cyhal_alarm_active_t active)397 cy_rslt_t cyhal_rtc_set_alarm(cyhal_rtc_t *obj, const struct tm *time, cyhal_alarm_active_t active)
398 {
399     // Note: the hardware does not support year matching
400     CY_UNUSED_PARAMETER(obj);
401     CY_ASSERT(NULL != obj);
402     _cyhal_rtc_dst_skip_next_alarm = false;
403     cy_stc_rtc_alarm_t alarm =
404     {
405         .sec = (uint32_t)time->tm_sec,
406         .secEn = active.en_sec ? CY_RTC_ALARM_ENABLE : CY_RTC_ALARM_DISABLE,
407         .min = (uint32_t)time->tm_min,
408         .minEn = active.en_min ? CY_RTC_ALARM_ENABLE : CY_RTC_ALARM_DISABLE,
409         .hour = (uint32_t)time->tm_hour,
410         .hourEn = active.en_hour ? CY_RTC_ALARM_ENABLE : CY_RTC_ALARM_DISABLE,
411         .dayOfWeek = (uint32_t)(time->tm_wday + 1),
412         .dayOfWeekEn = active.en_day ? CY_RTC_ALARM_ENABLE : CY_RTC_ALARM_DISABLE,
413         .date = (uint32_t)time->tm_mday,
414         .dateEn = active.en_date ? CY_RTC_ALARM_ENABLE : CY_RTC_ALARM_DISABLE,
415         .month = (uint32_t)(time->tm_mon + 1),
416         .monthEn = active.en_month ? CY_RTC_ALARM_ENABLE : CY_RTC_ALARM_DISABLE,
417         .almEn = CY_RTC_ALARM_ENABLE
418     };
419 
420     cy_rslt_t rslt;
421     uint32_t retry = 0;
422     do
423     {
424         if (retry != 0)
425             _CYHAL_RTC_WAIT_ONE_MS();
426         rslt = (cy_rslt_t)Cy_RTC_SetAlarmDateAndTime(&alarm, CY_RTC_ALARM_1);
427         ++retry;
428     } while (rslt == CY_RTC_INVALID_STATE && retry < _CYHAL_RTC_MAX_RETRY);
429 
430     return rslt;
431 }
432 
_cyhal_rtc_update_field(uint32_t remaining,uint32_t * curr,uint32_t * next,uint32_t max)433 static uint32_t _cyhal_rtc_update_field(uint32_t remaining, uint32_t* curr, uint32_t *next, uint32_t max)
434 {
435     *curr += remaining % max;
436     if (*curr >= max)
437     {
438         *curr %= max;
439         (*next)++;
440     }
441     return remaining / max;
442 }
443 
cyhal_rtc_set_alarm_by_seconds(cyhal_rtc_t * obj,const uint32_t seconds)444 cy_rslt_t cyhal_rtc_set_alarm_by_seconds(cyhal_rtc_t *obj, const uint32_t seconds)
445 {
446     CY_ASSERT(NULL != obj);
447     static const uint32_t SECONDS_IN_YEAR = 365*24*60*60; // 31,536,000
448 
449     // Note: The hardware does not support year matching so return error if
450     // seconds is greater than 1 year in the future
451     if(seconds > SECONDS_IN_YEAR)
452         return CY_RSLT_RTC_BAD_ARGUMENT;
453 
454     cy_stc_rtc_config_t now;
455     uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
456     Cy_RTC_GetDateAndTime(&now);
457     const int year = (int)(now.year + _cyhal_rtc_get_century());
458     cyhal_system_critical_section_exit(savedIntrStatus);
459 
460     bool nowDst = (NULL != _cyhal_rtc_dst) && Cy_RTC_GetDstStatus(_cyhal_rtc_dst, &now);
461 
462     uint32_t remaining = seconds;
463     remaining = _cyhal_rtc_update_field(remaining, &now.sec, &now.min, 60);
464     remaining = _cyhal_rtc_update_field(remaining, &now.min, &now.hour, 60);
465     remaining = _cyhal_rtc_update_field(remaining, &now.hour, &now.date, 24);
466 
467     uint32_t days;
468     now.date += remaining;
469     while (now.date > (days = Cy_RTC_DaysInMonth(now.month, year)))
470     {
471         now.date -= days;
472         now.month++;
473         if (now.month > 12)
474         {
475             now.year++;
476             now.month = 1;
477         }
478     }
479 
480     bool setSkipNextAlarm = false;
481 
482     // Handle crossing of daylight savings time boundaries
483     if (NULL != _cyhal_rtc_dst)
484     {
485         bool futureDst = Cy_RTC_GetDstStatus(_cyhal_rtc_dst, &now);
486 
487         if (nowDst && !futureDst)
488         {
489             // If the alarm time is within the hour following the end of DST,
490             // ignore the first alarm since the adjusted time will be before
491             // the DST boundary causing two alarms to occur: one before the
492             // boundary and one after the boundary.
493             if (now.hour == _cyhal_rtc_dst->stopDst.hour)
494             {
495                 setSkipNextAlarm = true;
496             }
497 
498             if (now.hour == 0)
499             {
500                 now.hour = 23;
501                 now.date--;
502 
503                 if (now.date < 1)
504                 {
505                     now.month--;
506                     if (now.month < 1)
507                     {
508                         now.month = 12;
509                         now.year--;
510                     }
511 
512                     now.date = Cy_RTC_DaysInMonth(now.month, year);
513                 }
514             }
515             else
516             {
517                 now.hour--;
518             }
519         }
520         else if (!nowDst && futureDst)
521         {
522             now.hour++;
523             if (now.hour >= 24)
524             {
525                 now.hour = 0;
526                 now.date++;
527 
528                 if (now.date > days)
529                 {
530                     now.date = 1;
531                     now.month++;
532                     if (now.month > 12)
533                     {
534                         now.month = 1;
535                         // Increment year, but alarm doesn't care
536                     }
537                 }
538             }
539         }
540     }
541 
542     struct tm future;
543     _cyhal_rtc_from_pdl_time(&now, year, &future);
544 
545     static const cyhal_alarm_active_t active =
546     {
547         .en_sec   = CY_RTC_ALARM_ENABLE,
548         .en_min   = CY_RTC_ALARM_ENABLE,
549         .en_hour  = CY_RTC_ALARM_ENABLE,
550         .en_day   = CY_RTC_ALARM_DISABLE, // We do not actually compute the day as we don't care.
551         .en_date  = CY_RTC_ALARM_ENABLE,  // The absolute time (eg: date) is what is important.
552         .en_month = CY_RTC_ALARM_ENABLE
553     };
554 
555     savedIntrStatus = cyhal_system_critical_section_enter();
556     cy_rslt_t result = cyhal_rtc_set_alarm(obj, &future, active);
557     _cyhal_rtc_dst_skip_next_alarm = setSkipNextAlarm;
558     cyhal_system_critical_section_exit(savedIntrStatus);
559 
560     return result;
561 }
562 
cyhal_rtc_register_callback(cyhal_rtc_t * obj,cyhal_rtc_event_callback_t callback,void * callback_arg)563 void cyhal_rtc_register_callback(cyhal_rtc_t *obj, cyhal_rtc_event_callback_t callback, void *callback_arg)
564 {
565     CY_UNUSED_PARAMETER(obj);
566     CY_ASSERT(NULL != obj);
567     uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
568     _cyhal_rtc_handler_arg = callback_arg;
569     _cyhal_rtc_user_handler = callback;
570     cyhal_system_critical_section_exit(savedIntrStatus);
571 }
572 
cyhal_rtc_enable_event(cyhal_rtc_t * obj,cyhal_rtc_event_t event,uint8_t intr_priority,bool enable)573 void cyhal_rtc_enable_event(cyhal_rtc_t *obj, cyhal_rtc_event_t event, uint8_t intr_priority, bool enable)
574 {
575     CY_UNUSED_PARAMETER(obj);
576     CY_UNUSED_PARAMETER(event);
577     CY_ASSERT(NULL != obj);
578     CY_ASSERT(CYHAL_RTC_ALARM == event);
579     Cy_RTC_ClearInterrupt(CY_RTC_INTR_ALARM1 | CY_RTC_INTR_ALARM2);
580     uint32_t alarm2_status = (Cy_RTC_GetInterruptMask() & CY_RTC_INTR_ALARM2);
581     Cy_RTC_SetInterruptMask((enable ? CY_RTC_INTR_ALARM1 : 0) | CY_RTC_INTR_CENTURY | alarm2_status);
582     _cyhal_irq_set_priority(_CYHAL_RTC_IRQ_NAME, intr_priority);
583 }
584 
585 #if defined(__cplusplus)
586 }
587 #endif
588 
589 #endif /* CYHAL_DRIVER_AVAILABLE_RTC */
590