/* * mbed Microcontroller Library * Copyright (c) 2017-2018 Future Electronics * Copyright (c) 2018-2019 Cypress Semiconductor Corporation * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "device.h" #include "rtc_api.h" #include "mbed_error.h" #include "mbed_mktime.h" #include "cy_rtc.h" #if DEVICE_RTC /* * Since Mbed tests insist on supporting 1970 - 2106 years range * and Cypress h/w supports only 2000 - 2099 years range, * two backup registers are used to flag century correction. */ #define BR_LAST_YEAR_READ 14 #define BR_CENTURY_CORRECTION 15 static int enabled = 0; static uint32_t rtc_read_convert_year(uint32_t short_year) { uint32_t century = BACKUP->BREG[BR_CENTURY_CORRECTION]; if (BACKUP->BREG[BR_LAST_YEAR_READ] > short_year) { BACKUP->BREG[BR_CENTURY_CORRECTION] = ++century; } BACKUP->BREG[BR_LAST_YEAR_READ] = short_year; return century * 100 + short_year; } static uint32_t rtc_write_convert_year(uint32_t long_year) { uint32_t short_year = long_year; uint32_t century = short_year / 100; short_year -= century * 100; BACKUP->BREG[BR_CENTURY_CORRECTION] = century; BACKUP->BREG[BR_LAST_YEAR_READ] = short_year; return short_year; } void rtc_init(void) { static cy_stc_rtc_config_t init_val = { /* Time information */ .hrFormat = CY_RTC_24_HOURS, .sec = 0, .min = 0, .hour = 0, .dayOfWeek = CY_RTC_SATURDAY, .date = 1, .month = 1, .year = 0 // 2000 - 30 == 1970 }; cy_stc_rtc_config_t cy_time; if (!enabled) { // Setup power management callback. // Setup century interrupt. // Verify RTC time consistency. Cy_RTC_GetDateAndTime(&cy_time); if (CY_RTC_IS_SEC_VALID(cy_time.sec) && CY_RTC_IS_MIN_VALID(cy_time.min) && CY_RTC_IS_HOUR_VALID(cy_time.hour) && CY_RTC_IS_DOW_VALID(cy_time.dayOfWeek) && CY_RTC_IS_MONTH_VALID(cy_time.month) && CY_RTC_IS_YEAR_SHORT_VALID(cy_time.year) && (cy_time.hrFormat == CY_RTC_24_HOURS)) { enabled = 1; } else { // reinitialize init_val.year = rtc_write_convert_year(1970); if (Cy_RTC_Init(&init_val) == CY_RTC_SUCCESS) { enabled = 1; } } } } void rtc_free(void) { // Nothing to do } int rtc_isenabled(void) { return enabled; } time_t rtc_read(void) { cy_stc_rtc_config_t cy_time; struct tm gmt; time_t timestamp = 0; uint32_t interrupt_state; // Since RTC reading function is unreliable when the RTC is busy with previous update // we have to make sure it's not before calling it. while (CY_RTC_BUSY == Cy_RTC_GetSyncStatus()) {} interrupt_state = Cy_SysLib_EnterCriticalSection(); Cy_RTC_GetDateAndTime(&cy_time); gmt.tm_sec = cy_time.sec; gmt.tm_min = cy_time.min; gmt.tm_hour = cy_time.hour; gmt.tm_mday = cy_time.date; gmt.tm_mon = cy_time.month - 1; gmt.tm_year = rtc_read_convert_year(cy_time.year); gmt.tm_isdst = 0; Cy_SysLib_ExitCriticalSection(interrupt_state); _rtc_maketime(&gmt, ×tamp, RTC_4_YEAR_LEAP_YEAR_SUPPORT); return timestamp; } void rtc_write(time_t t) { cy_en_rtc_status_t status; struct tm gmt; if (_rtc_localtime(t, &gmt, RTC_4_YEAR_LEAP_YEAR_SUPPORT)) { uint32_t year; uint32_t interrupt_state; // Make sure RTC is not busy and can be updated. while (CY_RTC_BUSY == Cy_RTC_GetSyncStatus()) {} interrupt_state = Cy_SysLib_EnterCriticalSection(); year = rtc_write_convert_year(gmt.tm_year); status = Cy_RTC_SetDateAndTimeDirect(gmt.tm_sec, gmt.tm_min, gmt.tm_hour, gmt.tm_mday, gmt.tm_mon + 1, year); Cy_SysLib_ExitCriticalSection(interrupt_state); if (status != CY_RTC_SUCCESS) { error("Error 0x%x while setting RTC time.", status); } } } #endif // DEVICE_RTC