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