pkarashchenko commented on code in PR #8980: URL: https://github.com/apache/nuttx/pull/8980#discussion_r1161565239
########## arch/risc-v/src/espressif/esp_rtc.c: ########## @@ -0,0 +1,878 @@ +/**************************************************************************** + * arch/risc-v/src/espressif/esp_rtc.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <assert.h> +#include <debug.h> +#include <errno.h> +#include <stdbool.h> +#include <string.h> +#include <sys/types.h> + +#include <nuttx/arch.h> +#include <nuttx/spinlock.h> +#include <nuttx/timers/rtc.h> + +#include "clock/clock.h" + +#include "esp_hr_timer.h" +#include "esp_rtc.h" +#include "riscv_internal.h" + +#include "esp_attr.h" +#include "soc/rtc.h" + +/* Chip-dependent headers from esp-hal-3rdparty */ + +#ifdef CONFIG_ESPRESSIF_ESP32C3 +#include "esp32c3/rtc.h" +#include "esp32c3/rom/rtc.h" +#elif defined(CONFIG_ESPRESSIF_ESP32C6) +#include "esp32c6/rtc.h" +#include "esp32c6/rom/rtc.h" +#elif defined(CONFIG_ESPRESSIF_ESP32H2) +#include "esp32h2/rtc.h" +#include "esp32h2/rom/rtc.h" +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The magic data for the struct esp_rtc_backup_s that is in RTC slow + * memory. + */ + +#define MAGIC_RTC_SAVE (UINT64_C(0x11223344556677)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER + +#ifdef CONFIG_RTC_ALARM +struct alm_cbinfo_s +{ + struct esp_hr_timer_s *alarm_hdl; /* Timer id point to here */ + rtc_alarm_callback_t ac_cb; /* Client callback function */ + volatile void *ac_arg; /* Argument to pass with the callback function */ + uint64_t deadline_us; + uint8_t index; +}; +#endif /* CONFIG_RTC_ALARM */ + +/* This is the private type for the RTC state. It must be cast compatible + * with struct rtc_lowerhalf_s. + */ + +struct esp_rtc_lowerhalf_s +{ + /* This is the contained reference to the read-only, lower-half + * operations vtable (which may lie in FLASH or ROM) + */ + + const struct rtc_ops_s *ops; +#ifdef CONFIG_RTC_ALARM + /* Alarm callback information */ + + struct alm_cbinfo_s alarmcb[CONFIG_RTC_NALARMS]; +#endif /* CONFIG_RTC_ALARM */ +}; + +#endif/* CONFIG_RTC_DRIVER */ + +struct esp_rtc_backup_s +{ + uint64_t magic; + int64_t offset; /* Offset time from RTC HW value */ + int64_t reserved0; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Prototypes for static methods in struct rtc_ops_s */ + +#ifdef CONFIG_RTC_DRIVER +static int esp_rtc_rdtime(struct rtc_lowerhalf_s *lower, + struct rtc_time *rtctime); +static int esp_rtc_settime(struct rtc_lowerhalf_s *lower, + const struct rtc_time *rtctime); +static bool esp_rtc_havesettime(struct rtc_lowerhalf_s *lower); + +#ifdef CONFIG_RTC_ALARM +static void IRAM_ATTR rtc_hr_timer_cb(void *arg); +static int esp_rtc_setalarm(struct rtc_lowerhalf_s *lower, + const struct lower_setalarm_s *alarminfo); +static int esp_rtc_setrelative(struct rtc_lowerhalf_s *lower, + const struct lower_setrelative_s *alarminfo); +static int esp_rtc_cancelalarm(struct rtc_lowerhalf_s *lower, + int alarmid); +static int esp_rtc_rdalarm(struct rtc_lowerhalf_s *lower, + struct lower_rdalarm_s *alarminfo); +#endif /* CONFIG_RTC_ALARM */ +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER +/* RTC driver operations */ + +static const struct rtc_ops_s g_rtc_ops = +{ + .rdtime = esp_rtc_rdtime, + .settime = esp_rtc_settime, + .havesettime = esp_rtc_havesettime, +#ifdef CONFIG_RTC_ALARM + .setalarm = esp_rtc_setalarm, + .setrelative = esp_rtc_setrelative, + .cancelalarm = esp_rtc_cancelalarm, + .rdalarm = esp_rtc_rdalarm, +#endif /* CONFIG_RTC_ALARM */ +}; + +/* RTC device state */ + +static struct esp_rtc_lowerhalf_s g_rtc_lowerhalf = +{ + .ops = &g_rtc_ops +}; + +/* Flag for tracking HR Timer enable status */ + +static bool g_hr_timer_enabled = false; + +#endif /* CONFIG_RTC_DRIVER */ + +/* Saved data for persistent RTC time */ + +static RTC_DATA_ATTR struct esp_rtc_backup_s g_rtc_saved_data; +static struct esp_rtc_backup_s *g_rtc_save; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +volatile bool g_rtc_enabled = false; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp_rtc_set_boot_time + * + * Description: + * Set time to RTC register to replace the original boot time. + * + * Input Parameters: + * time_us - set time in microseconds. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void IRAM_ATTR esp_rtc_set_boot_time(uint64_t time_us) +{ + putreg32((uint32_t)(time_us & UINT32_MAX), RTC_BOOT_TIME_LOW_REG); + putreg32((uint32_t)(time_us >> 32), RTC_BOOT_TIME_HIGH_REG); +} + +/**************************************************************************** + * Name: esp_rtc_get_boot_time + * + * Description: + * Get time of RTC register to indicate the original boot time. + * + * Input Parameters: + * None. + * + * Returned Value: + * Boot time in microseconds. + * + ****************************************************************************/ + +static uint64_t IRAM_ATTR esp_rtc_get_boot_time(void) +{ + return ((uint64_t)getreg32(RTC_BOOT_TIME_LOW_REG)) + + (((uint64_t)getreg32(RTC_BOOT_TIME_HIGH_REG)) << 32); +} + +/**************************************************************************** + * Name: rtc_hr_timer_cb + * + * Description: + * Callback to be called upon HR-Timer expiration. + * + * Input Parameters: + * arg - Information about the HR-Timer configuration. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#if defined(CONFIG_RTC_DRIVER) && defined(CONFIG_RTC_ALARM) +static void IRAM_ATTR rtc_hr_timer_cb(void *arg) +{ + struct alm_cbinfo_s *cbinfo = (struct alm_cbinfo_s *)arg; + rtc_alarm_callback_t cb; + void *cb_arg; + int alminfo_id; + + DEBUGASSERT(cbinfo != NULL); + + alminfo_id = cbinfo->index; + + if (cbinfo->ac_cb != NULL) + { + /* Alarm callback */ + + cb = cbinfo->ac_cb; + cb_arg = (void *)cbinfo->ac_arg; + cbinfo->ac_cb = NULL; + cbinfo->ac_arg = NULL; + cbinfo->deadline_us = 0; + cb(cb_arg, alminfo_id); + } +} +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Name: esp_rtc_rdtime + * + * Description: + * Return the current RTC time. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * rcttime - The location in which to return the current RTC time. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER + +#ifdef CONFIG_RTC_HIRES +static int esp_rtc_rdtime(struct rtc_lowerhalf_s *lower, + struct rtc_time *rtctime) +{ + struct timespec ts; + int ret; + + /* Get the higher resolution time */ + + ret = up_rtc_gettime(&ts); + if (ret < 0) + { + goto errout; + } + + /* Convert the one second epoch time to a struct tmThis operation + * depends on the fact that struct rtc_time and struct tm are cast + * compatible. + */ + + if (!gmtime_r(&ts.tv_sec, (struct tm *)rtctime)) + { + ret = -get_errno(); + goto errout; + } + + return OK; + +errout: + rtcerr("Failed to get RTC time: %d\n", ret); + return ret; +} + +#else /* !CONFIG_RTC_HIRES */ + +static int esp_rtc_rdtime(struct rtc_lowerhalf_s *lower, + struct rtc_time *rtctime) +{ + time_t timer; + + /* The resolution of time is only 1 second */ + + timer = up_rtc_time(); + + /* Convert the one second epoch time to a struct tm */ + + if (gmtime_r(&timer, (struct tm *)rtctime) == 0) + { + int errcode = get_errno(); + DEBUGASSERT(errcode > 0); + + rtcerr("gmtime_r failed: %d\n", errcode); + return -errcode; + } + + return OK; +} +#endif /* CONFIG_RTC_HIRES */ + +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Name: esp_rtc_settime + * + * Description: + * Implement the settime() method of the RTC driver interface. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * rcttime - The new time to set. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER +static int esp_rtc_settime(struct rtc_lowerhalf_s *lower, + const struct rtc_time *rtctime) +{ + struct timespec ts; + + /* Convert the struct rtc_time to a time_t. Here we assume that struct + * rtc_time is cast compatible with struct tm. + */ + + ts.tv_sec = mktime((struct tm *)rtctime); + ts.tv_nsec = 0; + + /* Now set the time (with a accuracy of seconds) */ + + return up_rtc_settime(&ts); +} +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Name: esp_rtc_havesettime + * + * Description: + * Implement the havesettime() method of the RTC driver interface. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * + * Returned Value: + * True if RTC date-time have been previously set, false otherwise. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER +static bool esp_rtc_havesettime(struct rtc_lowerhalf_s *lower) +{ + return esp_rtc_get_boot_time() != 0; +} +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Name: esp_rtc_setalarm + * + * Description: + * Set a new alarm. This function implements the setalarm() method of the + * RTC driver interface. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * alarminfo - Provided information needed to set the alarm. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#if defined(CONFIG_RTC_DRIVER) && defined(CONFIG_RTC_ALARM) +static int esp_rtc_setalarm(struct rtc_lowerhalf_s *lower, + const struct lower_setalarm_s *alarminfo) +{ + struct esp_rtc_lowerhalf_s *priv = (struct esp_rtc_lowerhalf_s *)lower; + struct alm_cbinfo_s *cbinfo; + uint64_t timeout; + irqstate_t flags; + int id; + + DEBUGASSERT(priv != NULL); + DEBUGASSERT(alarminfo != NULL); + + timeout = mktime((struct tm *)&alarminfo->time); Review Comment: ditto ########## arch/risc-v/src/espressif/esp_rtc.c: ########## @@ -0,0 +1,878 @@ +/**************************************************************************** + * arch/risc-v/src/espressif/esp_rtc.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <assert.h> +#include <debug.h> +#include <errno.h> +#include <stdbool.h> +#include <string.h> +#include <sys/types.h> + +#include <nuttx/arch.h> +#include <nuttx/spinlock.h> +#include <nuttx/timers/rtc.h> + +#include "clock/clock.h" + +#include "esp_hr_timer.h" +#include "esp_rtc.h" +#include "riscv_internal.h" + +#include "esp_attr.h" +#include "soc/rtc.h" + +/* Chip-dependent headers from esp-hal-3rdparty */ + +#ifdef CONFIG_ESPRESSIF_ESP32C3 +#include "esp32c3/rtc.h" +#include "esp32c3/rom/rtc.h" +#elif defined(CONFIG_ESPRESSIF_ESP32C6) +#include "esp32c6/rtc.h" +#include "esp32c6/rom/rtc.h" +#elif defined(CONFIG_ESPRESSIF_ESP32H2) +#include "esp32h2/rtc.h" +#include "esp32h2/rom/rtc.h" +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The magic data for the struct esp_rtc_backup_s that is in RTC slow + * memory. + */ + +#define MAGIC_RTC_SAVE (UINT64_C(0x11223344556677)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER + +#ifdef CONFIG_RTC_ALARM +struct alm_cbinfo_s +{ + struct esp_hr_timer_s *alarm_hdl; /* Timer id point to here */ + rtc_alarm_callback_t ac_cb; /* Client callback function */ + volatile void *ac_arg; /* Argument to pass with the callback function */ + uint64_t deadline_us; + uint8_t index; +}; +#endif /* CONFIG_RTC_ALARM */ + +/* This is the private type for the RTC state. It must be cast compatible + * with struct rtc_lowerhalf_s. + */ + +struct esp_rtc_lowerhalf_s +{ + /* This is the contained reference to the read-only, lower-half + * operations vtable (which may lie in FLASH or ROM) + */ + + const struct rtc_ops_s *ops; +#ifdef CONFIG_RTC_ALARM + /* Alarm callback information */ + + struct alm_cbinfo_s alarmcb[CONFIG_RTC_NALARMS]; +#endif /* CONFIG_RTC_ALARM */ +}; + +#endif/* CONFIG_RTC_DRIVER */ + +struct esp_rtc_backup_s +{ + uint64_t magic; + int64_t offset; /* Offset time from RTC HW value */ + int64_t reserved0; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Prototypes for static methods in struct rtc_ops_s */ + +#ifdef CONFIG_RTC_DRIVER +static int esp_rtc_rdtime(struct rtc_lowerhalf_s *lower, + struct rtc_time *rtctime); +static int esp_rtc_settime(struct rtc_lowerhalf_s *lower, + const struct rtc_time *rtctime); +static bool esp_rtc_havesettime(struct rtc_lowerhalf_s *lower); + +#ifdef CONFIG_RTC_ALARM +static void IRAM_ATTR rtc_hr_timer_cb(void *arg); +static int esp_rtc_setalarm(struct rtc_lowerhalf_s *lower, + const struct lower_setalarm_s *alarminfo); +static int esp_rtc_setrelative(struct rtc_lowerhalf_s *lower, + const struct lower_setrelative_s *alarminfo); +static int esp_rtc_cancelalarm(struct rtc_lowerhalf_s *lower, + int alarmid); +static int esp_rtc_rdalarm(struct rtc_lowerhalf_s *lower, + struct lower_rdalarm_s *alarminfo); +#endif /* CONFIG_RTC_ALARM */ +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER +/* RTC driver operations */ + +static const struct rtc_ops_s g_rtc_ops = +{ + .rdtime = esp_rtc_rdtime, + .settime = esp_rtc_settime, + .havesettime = esp_rtc_havesettime, +#ifdef CONFIG_RTC_ALARM + .setalarm = esp_rtc_setalarm, + .setrelative = esp_rtc_setrelative, + .cancelalarm = esp_rtc_cancelalarm, + .rdalarm = esp_rtc_rdalarm, +#endif /* CONFIG_RTC_ALARM */ +}; + +/* RTC device state */ + +static struct esp_rtc_lowerhalf_s g_rtc_lowerhalf = +{ + .ops = &g_rtc_ops +}; + +/* Flag for tracking HR Timer enable status */ + +static bool g_hr_timer_enabled = false; + +#endif /* CONFIG_RTC_DRIVER */ + +/* Saved data for persistent RTC time */ + +static RTC_DATA_ATTR struct esp_rtc_backup_s g_rtc_saved_data; +static struct esp_rtc_backup_s *g_rtc_save; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +volatile bool g_rtc_enabled = false; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp_rtc_set_boot_time + * + * Description: + * Set time to RTC register to replace the original boot time. + * + * Input Parameters: + * time_us - set time in microseconds. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void IRAM_ATTR esp_rtc_set_boot_time(uint64_t time_us) +{ + putreg32((uint32_t)(time_us & UINT32_MAX), RTC_BOOT_TIME_LOW_REG); + putreg32((uint32_t)(time_us >> 32), RTC_BOOT_TIME_HIGH_REG); +} + +/**************************************************************************** + * Name: esp_rtc_get_boot_time + * + * Description: + * Get time of RTC register to indicate the original boot time. + * + * Input Parameters: + * None. + * + * Returned Value: + * Boot time in microseconds. + * + ****************************************************************************/ + +static uint64_t IRAM_ATTR esp_rtc_get_boot_time(void) +{ + return ((uint64_t)getreg32(RTC_BOOT_TIME_LOW_REG)) + + (((uint64_t)getreg32(RTC_BOOT_TIME_HIGH_REG)) << 32); +} + +/**************************************************************************** + * Name: rtc_hr_timer_cb + * + * Description: + * Callback to be called upon HR-Timer expiration. + * + * Input Parameters: + * arg - Information about the HR-Timer configuration. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#if defined(CONFIG_RTC_DRIVER) && defined(CONFIG_RTC_ALARM) +static void IRAM_ATTR rtc_hr_timer_cb(void *arg) +{ + struct alm_cbinfo_s *cbinfo = (struct alm_cbinfo_s *)arg; + rtc_alarm_callback_t cb; + void *cb_arg; + int alminfo_id; + + DEBUGASSERT(cbinfo != NULL); + + alminfo_id = cbinfo->index; + + if (cbinfo->ac_cb != NULL) + { + /* Alarm callback */ + + cb = cbinfo->ac_cb; + cb_arg = (void *)cbinfo->ac_arg; + cbinfo->ac_cb = NULL; + cbinfo->ac_arg = NULL; + cbinfo->deadline_us = 0; + cb(cb_arg, alminfo_id); + } +} +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Name: esp_rtc_rdtime + * + * Description: + * Return the current RTC time. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * rcttime - The location in which to return the current RTC time. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER + +#ifdef CONFIG_RTC_HIRES +static int esp_rtc_rdtime(struct rtc_lowerhalf_s *lower, + struct rtc_time *rtctime) +{ + struct timespec ts; + int ret; + + /* Get the higher resolution time */ + + ret = up_rtc_gettime(&ts); + if (ret < 0) + { + goto errout; + } + + /* Convert the one second epoch time to a struct tmThis operation + * depends on the fact that struct rtc_time and struct tm are cast + * compatible. + */ + + if (!gmtime_r(&ts.tv_sec, (struct tm *)rtctime)) + { + ret = -get_errno(); + goto errout; + } + + return OK; + +errout: + rtcerr("Failed to get RTC time: %d\n", ret); + return ret; +} + +#else /* !CONFIG_RTC_HIRES */ + +static int esp_rtc_rdtime(struct rtc_lowerhalf_s *lower, + struct rtc_time *rtctime) +{ + time_t timer; + + /* The resolution of time is only 1 second */ + + timer = up_rtc_time(); + + /* Convert the one second epoch time to a struct tm */ + + if (gmtime_r(&timer, (struct tm *)rtctime) == 0) + { + int errcode = get_errno(); + DEBUGASSERT(errcode > 0); + + rtcerr("gmtime_r failed: %d\n", errcode); + return -errcode; + } + + return OK; +} +#endif /* CONFIG_RTC_HIRES */ + +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Name: esp_rtc_settime + * + * Description: + * Implement the settime() method of the RTC driver interface. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * rcttime - The new time to set. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER +static int esp_rtc_settime(struct rtc_lowerhalf_s *lower, + const struct rtc_time *rtctime) +{ + struct timespec ts; + + /* Convert the struct rtc_time to a time_t. Here we assume that struct + * rtc_time is cast compatible with struct tm. + */ + + ts.tv_sec = mktime((struct tm *)rtctime); + ts.tv_nsec = 0; + + /* Now set the time (with a accuracy of seconds) */ + + return up_rtc_settime(&ts); +} +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Name: esp_rtc_havesettime + * + * Description: + * Implement the havesettime() method of the RTC driver interface. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * + * Returned Value: + * True if RTC date-time have been previously set, false otherwise. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER +static bool esp_rtc_havesettime(struct rtc_lowerhalf_s *lower) +{ + return esp_rtc_get_boot_time() != 0; +} +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Name: esp_rtc_setalarm + * + * Description: + * Set a new alarm. This function implements the setalarm() method of the + * RTC driver interface. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * alarminfo - Provided information needed to set the alarm. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#if defined(CONFIG_RTC_DRIVER) && defined(CONFIG_RTC_ALARM) +static int esp_rtc_setalarm(struct rtc_lowerhalf_s *lower, + const struct lower_setalarm_s *alarminfo) +{ + struct esp_rtc_lowerhalf_s *priv = (struct esp_rtc_lowerhalf_s *)lower; + struct alm_cbinfo_s *cbinfo; + uint64_t timeout; + irqstate_t flags; + int id; + + DEBUGASSERT(priv != NULL); + DEBUGASSERT(alarminfo != NULL); + + timeout = mktime((struct tm *)&alarminfo->time); + + /* Set the alarm in RT-Timer */ + + id = alarminfo->id; + cbinfo = &priv->alarmcb[id]; + + if (cbinfo->ac_cb != NULL) + { + return -EBUSY; + } + + /* Create the RT-Timer alarm */ + + flags = spin_lock_irqsave(NULL); + + if (cbinfo->alarm_hdl == NULL) + { + struct esp_hr_timer_args_s hr_timer_args; + int ret; + + cbinfo->index = id; + hr_timer_args.arg = cbinfo; + hr_timer_args.callback = rtc_hr_timer_cb; + + ret = esp_hr_timer_create(&hr_timer_args, &cbinfo->alarm_hdl); + if (ret < 0) + { + rtcerr("Failed to create HR Timer=%d\n", ret); + spin_unlock_irqrestore(NULL, flags); + return ret; + } + } + + cbinfo->ac_cb = alarminfo->cb; + cbinfo->ac_arg = alarminfo->priv; + cbinfo->deadline_us = timeout * USEC_PER_SEC; + + rtcinfo("Starting alarm ID %d\n", id); + + esp_hr_timer_start(cbinfo->alarm_hdl, cbinfo->deadline_us, false); + + spin_unlock_irqrestore(NULL, flags); + + return OK; +} +#endif /* CONFIG_RTC_DRIVER && CONFIG_RTC_ALARM */ + +/**************************************************************************** + * Name: esp_rtc_setrelative + * + * Description: + * Set a new alarm relative to the current time. This function implements + * the setrelative() method of the RTC driver interface. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * alarminfo - Provided information needed to set the alarm. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#if defined(CONFIG_RTC_DRIVER) && defined(CONFIG_RTC_ALARM) +static int esp_rtc_setrelative(struct rtc_lowerhalf_s *lower, + const struct lower_setrelative_s *alarminfo) +{ + struct lower_setalarm_s setalarm; + time_t seconds; + int ret = -EINVAL; + irqstate_t flags; + + DEBUGASSERT(lower != NULL && alarminfo != NULL); + + if (alarminfo->reltime > 0) + { + flags = spin_lock_irqsave(NULL); + + seconds = alarminfo->reltime; + gmtime_r(&seconds, (struct tm *)&setalarm.time); + + /* The set the alarm using this absolute time */ + + setalarm.id = alarminfo->id; + setalarm.cb = alarminfo->cb; + setalarm.priv = alarminfo->priv; + ret = esp_rtc_setalarm(lower, &setalarm); + + spin_unlock_irqrestore(NULL, flags); + } + + return ret; +} +#endif /* CONFIG_RTC_DRIVER && CONFIG_RTC_ALARM */ + +/**************************************************************************** + * Name: esp_rtc_cancelalarm + * + * Description: + * Cancel the current alarm. This function implements the cancelalarm() + * method of the RTC driver interface. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * alarmid - ID of the alarm to be cancelled. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#if defined(CONFIG_RTC_DRIVER) && defined(CONFIG_RTC_ALARM) +static int esp_rtc_cancelalarm(struct rtc_lowerhalf_s *lower, int alarmid) +{ + struct esp_rtc_lowerhalf_s *priv; + struct alm_cbinfo_s *cbinfo; + irqstate_t flags; + + DEBUGASSERT(lower != NULL); + + priv = (struct esp_rtc_lowerhalf_s *)lower; + + /* Set the alarm in hardware and enable interrupts */ + + cbinfo = &priv->alarmcb[alarmid]; + + if (cbinfo->ac_cb == NULL) + { + return -ENODATA; + } + + flags = spin_lock_irqsave(NULL); + + /* Stop and delete the alarm */ + + rtcinfo("Cancelling alarm ID %d\n", alarmid); + + esp_hr_timer_stop(cbinfo->alarm_hdl); + esp_hr_timer_delete(cbinfo->alarm_hdl); + + cbinfo->ac_cb = NULL; + cbinfo->deadline_us = 0; + cbinfo->alarm_hdl = NULL; + + spin_unlock_irqrestore(NULL, flags); + + return OK; +} +#endif /* CONFIG_RTC_DRIVER && CONFIG_RTC_ALARM */ + +/**************************************************************************** + * Name: esp_rtc_rdalarm + * + * Description: + * Query the RTC alarm. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * alarminfo - Provided information needed to query the alarm. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#if defined(CONFIG_RTC_DRIVER) && defined(CONFIG_RTC_ALARM) +static int esp_rtc_rdalarm(struct rtc_lowerhalf_s *lower, + struct lower_rdalarm_s *alarminfo) +{ + struct esp_rtc_lowerhalf_s *priv; + struct timespec ts; + struct alm_cbinfo_s *cbinfo; + irqstate_t flags; + + DEBUGASSERT(lower != NULL); + DEBUGASSERT(alarminfo != NULL); + DEBUGASSERT(alarminfo->time != NULL); + + priv = (struct esp_rtc_lowerhalf_s *)lower; + + flags = spin_lock_irqsave(NULL); + + /* Get the alarm according to the alarm ID */ + + cbinfo = &priv->alarmcb[alarminfo->id]; + + ts.tv_sec = (esp_hr_timer_time_us() + g_rtc_save->offset + + cbinfo->deadline_us) / USEC_PER_SEC; + ts.tv_nsec = ((esp_hr_timer_time_us() + g_rtc_save->offset + + cbinfo->deadline_us) % USEC_PER_SEC) * NSEC_PER_USEC; + + localtime_r((const time_t *)&ts.tv_sec, Review Comment: type cast is not needed here ########## arch/risc-v/src/espressif/esp_rtc.c: ########## @@ -0,0 +1,878 @@ +/**************************************************************************** + * arch/risc-v/src/espressif/esp_rtc.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <assert.h> +#include <debug.h> +#include <errno.h> +#include <stdbool.h> +#include <string.h> +#include <sys/types.h> + +#include <nuttx/arch.h> +#include <nuttx/spinlock.h> +#include <nuttx/timers/rtc.h> + +#include "clock/clock.h" + +#include "esp_hr_timer.h" +#include "esp_rtc.h" +#include "riscv_internal.h" + +#include "esp_attr.h" +#include "soc/rtc.h" + +/* Chip-dependent headers from esp-hal-3rdparty */ + +#ifdef CONFIG_ESPRESSIF_ESP32C3 +#include "esp32c3/rtc.h" +#include "esp32c3/rom/rtc.h" +#elif defined(CONFIG_ESPRESSIF_ESP32C6) +#include "esp32c6/rtc.h" +#include "esp32c6/rom/rtc.h" +#elif defined(CONFIG_ESPRESSIF_ESP32H2) +#include "esp32h2/rtc.h" +#include "esp32h2/rom/rtc.h" +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The magic data for the struct esp_rtc_backup_s that is in RTC slow + * memory. + */ + +#define MAGIC_RTC_SAVE (UINT64_C(0x11223344556677)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER + +#ifdef CONFIG_RTC_ALARM +struct alm_cbinfo_s +{ + struct esp_hr_timer_s *alarm_hdl; /* Timer id point to here */ + rtc_alarm_callback_t ac_cb; /* Client callback function */ + volatile void *ac_arg; /* Argument to pass with the callback function */ + uint64_t deadline_us; + uint8_t index; +}; +#endif /* CONFIG_RTC_ALARM */ + +/* This is the private type for the RTC state. It must be cast compatible + * with struct rtc_lowerhalf_s. + */ + +struct esp_rtc_lowerhalf_s +{ + /* This is the contained reference to the read-only, lower-half + * operations vtable (which may lie in FLASH or ROM) + */ + + const struct rtc_ops_s *ops; +#ifdef CONFIG_RTC_ALARM + /* Alarm callback information */ + + struct alm_cbinfo_s alarmcb[CONFIG_RTC_NALARMS]; +#endif /* CONFIG_RTC_ALARM */ +}; + +#endif/* CONFIG_RTC_DRIVER */ + +struct esp_rtc_backup_s +{ + uint64_t magic; + int64_t offset; /* Offset time from RTC HW value */ + int64_t reserved0; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Prototypes for static methods in struct rtc_ops_s */ + +#ifdef CONFIG_RTC_DRIVER +static int esp_rtc_rdtime(struct rtc_lowerhalf_s *lower, + struct rtc_time *rtctime); +static int esp_rtc_settime(struct rtc_lowerhalf_s *lower, + const struct rtc_time *rtctime); +static bool esp_rtc_havesettime(struct rtc_lowerhalf_s *lower); + +#ifdef CONFIG_RTC_ALARM +static void IRAM_ATTR rtc_hr_timer_cb(void *arg); +static int esp_rtc_setalarm(struct rtc_lowerhalf_s *lower, + const struct lower_setalarm_s *alarminfo); +static int esp_rtc_setrelative(struct rtc_lowerhalf_s *lower, + const struct lower_setrelative_s *alarminfo); +static int esp_rtc_cancelalarm(struct rtc_lowerhalf_s *lower, + int alarmid); +static int esp_rtc_rdalarm(struct rtc_lowerhalf_s *lower, + struct lower_rdalarm_s *alarminfo); +#endif /* CONFIG_RTC_ALARM */ +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER +/* RTC driver operations */ + +static const struct rtc_ops_s g_rtc_ops = +{ + .rdtime = esp_rtc_rdtime, + .settime = esp_rtc_settime, + .havesettime = esp_rtc_havesettime, +#ifdef CONFIG_RTC_ALARM + .setalarm = esp_rtc_setalarm, + .setrelative = esp_rtc_setrelative, + .cancelalarm = esp_rtc_cancelalarm, + .rdalarm = esp_rtc_rdalarm, +#endif /* CONFIG_RTC_ALARM */ +}; + +/* RTC device state */ + +static struct esp_rtc_lowerhalf_s g_rtc_lowerhalf = +{ + .ops = &g_rtc_ops +}; + +/* Flag for tracking HR Timer enable status */ + +static bool g_hr_timer_enabled = false; + +#endif /* CONFIG_RTC_DRIVER */ + +/* Saved data for persistent RTC time */ + +static RTC_DATA_ATTR struct esp_rtc_backup_s g_rtc_saved_data; +static struct esp_rtc_backup_s *g_rtc_save; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +volatile bool g_rtc_enabled = false; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp_rtc_set_boot_time + * + * Description: + * Set time to RTC register to replace the original boot time. + * + * Input Parameters: + * time_us - set time in microseconds. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void IRAM_ATTR esp_rtc_set_boot_time(uint64_t time_us) +{ + putreg32((uint32_t)(time_us & UINT32_MAX), RTC_BOOT_TIME_LOW_REG); + putreg32((uint32_t)(time_us >> 32), RTC_BOOT_TIME_HIGH_REG); +} + +/**************************************************************************** + * Name: esp_rtc_get_boot_time + * + * Description: + * Get time of RTC register to indicate the original boot time. + * + * Input Parameters: + * None. + * + * Returned Value: + * Boot time in microseconds. + * + ****************************************************************************/ + +static uint64_t IRAM_ATTR esp_rtc_get_boot_time(void) +{ + return ((uint64_t)getreg32(RTC_BOOT_TIME_LOW_REG)) + + (((uint64_t)getreg32(RTC_BOOT_TIME_HIGH_REG)) << 32); +} + +/**************************************************************************** + * Name: rtc_hr_timer_cb + * + * Description: + * Callback to be called upon HR-Timer expiration. + * + * Input Parameters: + * arg - Information about the HR-Timer configuration. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#if defined(CONFIG_RTC_DRIVER) && defined(CONFIG_RTC_ALARM) +static void IRAM_ATTR rtc_hr_timer_cb(void *arg) +{ + struct alm_cbinfo_s *cbinfo = (struct alm_cbinfo_s *)arg; + rtc_alarm_callback_t cb; + void *cb_arg; + int alminfo_id; + + DEBUGASSERT(cbinfo != NULL); + + alminfo_id = cbinfo->index; + + if (cbinfo->ac_cb != NULL) + { + /* Alarm callback */ + + cb = cbinfo->ac_cb; + cb_arg = (void *)cbinfo->ac_arg; + cbinfo->ac_cb = NULL; + cbinfo->ac_arg = NULL; + cbinfo->deadline_us = 0; + cb(cb_arg, alminfo_id); + } +} +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Name: esp_rtc_rdtime + * + * Description: + * Return the current RTC time. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * rcttime - The location in which to return the current RTC time. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER + +#ifdef CONFIG_RTC_HIRES +static int esp_rtc_rdtime(struct rtc_lowerhalf_s *lower, + struct rtc_time *rtctime) +{ + struct timespec ts; + int ret; + + /* Get the higher resolution time */ + + ret = up_rtc_gettime(&ts); + if (ret < 0) + { + goto errout; + } + + /* Convert the one second epoch time to a struct tmThis operation + * depends on the fact that struct rtc_time and struct tm are cast + * compatible. + */ + + if (!gmtime_r(&ts.tv_sec, (struct tm *)rtctime)) + { + ret = -get_errno(); + goto errout; + } + + return OK; + +errout: + rtcerr("Failed to get RTC time: %d\n", ret); + return ret; +} + +#else /* !CONFIG_RTC_HIRES */ + +static int esp_rtc_rdtime(struct rtc_lowerhalf_s *lower, + struct rtc_time *rtctime) +{ + time_t timer; + + /* The resolution of time is only 1 second */ + + timer = up_rtc_time(); + + /* Convert the one second epoch time to a struct tm */ + + if (gmtime_r(&timer, (struct tm *)rtctime) == 0) + { + int errcode = get_errno(); + DEBUGASSERT(errcode > 0); + + rtcerr("gmtime_r failed: %d\n", errcode); + return -errcode; + } + + return OK; +} +#endif /* CONFIG_RTC_HIRES */ + +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Name: esp_rtc_settime + * + * Description: + * Implement the settime() method of the RTC driver interface. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * rcttime - The new time to set. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER +static int esp_rtc_settime(struct rtc_lowerhalf_s *lower, + const struct rtc_time *rtctime) +{ + struct timespec ts; + + /* Convert the struct rtc_time to a time_t. Here we assume that struct + * rtc_time is cast compatible with struct tm. + */ + + ts.tv_sec = mktime((struct tm *)rtctime); + ts.tv_nsec = 0; + + /* Now set the time (with a accuracy of seconds) */ + + return up_rtc_settime(&ts); +} +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Name: esp_rtc_havesettime + * + * Description: + * Implement the havesettime() method of the RTC driver interface. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * + * Returned Value: + * True if RTC date-time have been previously set, false otherwise. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER +static bool esp_rtc_havesettime(struct rtc_lowerhalf_s *lower) +{ + return esp_rtc_get_boot_time() != 0; +} +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Name: esp_rtc_setalarm + * + * Description: + * Set a new alarm. This function implements the setalarm() method of the + * RTC driver interface. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * alarminfo - Provided information needed to set the alarm. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#if defined(CONFIG_RTC_DRIVER) && defined(CONFIG_RTC_ALARM) +static int esp_rtc_setalarm(struct rtc_lowerhalf_s *lower, + const struct lower_setalarm_s *alarminfo) +{ + struct esp_rtc_lowerhalf_s *priv = (struct esp_rtc_lowerhalf_s *)lower; + struct alm_cbinfo_s *cbinfo; + uint64_t timeout; + irqstate_t flags; + int id; + + DEBUGASSERT(priv != NULL); + DEBUGASSERT(alarminfo != NULL); + + timeout = mktime((struct tm *)&alarminfo->time); + + /* Set the alarm in RT-Timer */ + + id = alarminfo->id; + cbinfo = &priv->alarmcb[id]; + + if (cbinfo->ac_cb != NULL) + { + return -EBUSY; + } + + /* Create the RT-Timer alarm */ + + flags = spin_lock_irqsave(NULL); + + if (cbinfo->alarm_hdl == NULL) + { + struct esp_hr_timer_args_s hr_timer_args; + int ret; + + cbinfo->index = id; + hr_timer_args.arg = cbinfo; + hr_timer_args.callback = rtc_hr_timer_cb; + + ret = esp_hr_timer_create(&hr_timer_args, &cbinfo->alarm_hdl); + if (ret < 0) + { + rtcerr("Failed to create HR Timer=%d\n", ret); + spin_unlock_irqrestore(NULL, flags); + return ret; + } + } + + cbinfo->ac_cb = alarminfo->cb; + cbinfo->ac_arg = alarminfo->priv; + cbinfo->deadline_us = timeout * USEC_PER_SEC; + + rtcinfo("Starting alarm ID %d\n", id); + + esp_hr_timer_start(cbinfo->alarm_hdl, cbinfo->deadline_us, false); + + spin_unlock_irqrestore(NULL, flags); + + return OK; +} +#endif /* CONFIG_RTC_DRIVER && CONFIG_RTC_ALARM */ + +/**************************************************************************** + * Name: esp_rtc_setrelative + * + * Description: + * Set a new alarm relative to the current time. This function implements + * the setrelative() method of the RTC driver interface. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * alarminfo - Provided information needed to set the alarm. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#if defined(CONFIG_RTC_DRIVER) && defined(CONFIG_RTC_ALARM) +static int esp_rtc_setrelative(struct rtc_lowerhalf_s *lower, + const struct lower_setrelative_s *alarminfo) +{ + struct lower_setalarm_s setalarm; + time_t seconds; + int ret = -EINVAL; + irqstate_t flags; + + DEBUGASSERT(lower != NULL && alarminfo != NULL); + + if (alarminfo->reltime > 0) + { + flags = spin_lock_irqsave(NULL); + + seconds = alarminfo->reltime; + gmtime_r(&seconds, (struct tm *)&setalarm.time); Review Comment: Maybe we should drop `const` from `alarminfo` instead? ########## arch/risc-v/src/espressif/esp_rtc.c: ########## @@ -0,0 +1,878 @@ +/**************************************************************************** + * arch/risc-v/src/espressif/esp_rtc.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <assert.h> +#include <debug.h> +#include <errno.h> +#include <stdbool.h> +#include <string.h> +#include <sys/types.h> + +#include <nuttx/arch.h> +#include <nuttx/spinlock.h> +#include <nuttx/timers/rtc.h> + +#include "clock/clock.h" + +#include "esp_hr_timer.h" +#include "esp_rtc.h" +#include "riscv_internal.h" + +#include "esp_attr.h" +#include "soc/rtc.h" + +/* Chip-dependent headers from esp-hal-3rdparty */ + +#ifdef CONFIG_ESPRESSIF_ESP32C3 +#include "esp32c3/rtc.h" +#include "esp32c3/rom/rtc.h" +#elif defined(CONFIG_ESPRESSIF_ESP32C6) +#include "esp32c6/rtc.h" +#include "esp32c6/rom/rtc.h" +#elif defined(CONFIG_ESPRESSIF_ESP32H2) +#include "esp32h2/rtc.h" +#include "esp32h2/rom/rtc.h" +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The magic data for the struct esp_rtc_backup_s that is in RTC slow + * memory. + */ + +#define MAGIC_RTC_SAVE (UINT64_C(0x11223344556677)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER + +#ifdef CONFIG_RTC_ALARM +struct alm_cbinfo_s +{ + struct esp_hr_timer_s *alarm_hdl; /* Timer id point to here */ + rtc_alarm_callback_t ac_cb; /* Client callback function */ + volatile void *ac_arg; /* Argument to pass with the callback function */ + uint64_t deadline_us; + uint8_t index; +}; +#endif /* CONFIG_RTC_ALARM */ + +/* This is the private type for the RTC state. It must be cast compatible + * with struct rtc_lowerhalf_s. + */ + +struct esp_rtc_lowerhalf_s +{ + /* This is the contained reference to the read-only, lower-half + * operations vtable (which may lie in FLASH or ROM) + */ + + const struct rtc_ops_s *ops; +#ifdef CONFIG_RTC_ALARM + /* Alarm callback information */ + + struct alm_cbinfo_s alarmcb[CONFIG_RTC_NALARMS]; +#endif /* CONFIG_RTC_ALARM */ +}; + +#endif/* CONFIG_RTC_DRIVER */ + +struct esp_rtc_backup_s +{ + uint64_t magic; + int64_t offset; /* Offset time from RTC HW value */ + int64_t reserved0; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Prototypes for static methods in struct rtc_ops_s */ + +#ifdef CONFIG_RTC_DRIVER +static int esp_rtc_rdtime(struct rtc_lowerhalf_s *lower, + struct rtc_time *rtctime); +static int esp_rtc_settime(struct rtc_lowerhalf_s *lower, + const struct rtc_time *rtctime); +static bool esp_rtc_havesettime(struct rtc_lowerhalf_s *lower); + +#ifdef CONFIG_RTC_ALARM +static void IRAM_ATTR rtc_hr_timer_cb(void *arg); +static int esp_rtc_setalarm(struct rtc_lowerhalf_s *lower, + const struct lower_setalarm_s *alarminfo); +static int esp_rtc_setrelative(struct rtc_lowerhalf_s *lower, + const struct lower_setrelative_s *alarminfo); +static int esp_rtc_cancelalarm(struct rtc_lowerhalf_s *lower, + int alarmid); +static int esp_rtc_rdalarm(struct rtc_lowerhalf_s *lower, + struct lower_rdalarm_s *alarminfo); +#endif /* CONFIG_RTC_ALARM */ +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER +/* RTC driver operations */ + +static const struct rtc_ops_s g_rtc_ops = +{ + .rdtime = esp_rtc_rdtime, + .settime = esp_rtc_settime, + .havesettime = esp_rtc_havesettime, +#ifdef CONFIG_RTC_ALARM + .setalarm = esp_rtc_setalarm, + .setrelative = esp_rtc_setrelative, + .cancelalarm = esp_rtc_cancelalarm, + .rdalarm = esp_rtc_rdalarm, +#endif /* CONFIG_RTC_ALARM */ +}; + +/* RTC device state */ + +static struct esp_rtc_lowerhalf_s g_rtc_lowerhalf = +{ + .ops = &g_rtc_ops +}; + +/* Flag for tracking HR Timer enable status */ + +static bool g_hr_timer_enabled = false; + +#endif /* CONFIG_RTC_DRIVER */ + +/* Saved data for persistent RTC time */ + +static RTC_DATA_ATTR struct esp_rtc_backup_s g_rtc_saved_data; +static struct esp_rtc_backup_s *g_rtc_save; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +volatile bool g_rtc_enabled = false; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp_rtc_set_boot_time + * + * Description: + * Set time to RTC register to replace the original boot time. + * + * Input Parameters: + * time_us - set time in microseconds. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void IRAM_ATTR esp_rtc_set_boot_time(uint64_t time_us) +{ + putreg32((uint32_t)(time_us & UINT32_MAX), RTC_BOOT_TIME_LOW_REG); + putreg32((uint32_t)(time_us >> 32), RTC_BOOT_TIME_HIGH_REG); +} + +/**************************************************************************** + * Name: esp_rtc_get_boot_time + * + * Description: + * Get time of RTC register to indicate the original boot time. + * + * Input Parameters: + * None. + * + * Returned Value: + * Boot time in microseconds. + * + ****************************************************************************/ + +static uint64_t IRAM_ATTR esp_rtc_get_boot_time(void) +{ + return ((uint64_t)getreg32(RTC_BOOT_TIME_LOW_REG)) + + (((uint64_t)getreg32(RTC_BOOT_TIME_HIGH_REG)) << 32); +} + +/**************************************************************************** + * Name: rtc_hr_timer_cb + * + * Description: + * Callback to be called upon HR-Timer expiration. + * + * Input Parameters: + * arg - Information about the HR-Timer configuration. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#if defined(CONFIG_RTC_DRIVER) && defined(CONFIG_RTC_ALARM) +static void IRAM_ATTR rtc_hr_timer_cb(void *arg) +{ + struct alm_cbinfo_s *cbinfo = (struct alm_cbinfo_s *)arg; + rtc_alarm_callback_t cb; + void *cb_arg; + int alminfo_id; + + DEBUGASSERT(cbinfo != NULL); + + alminfo_id = cbinfo->index; + + if (cbinfo->ac_cb != NULL) + { + /* Alarm callback */ + + cb = cbinfo->ac_cb; + cb_arg = (void *)cbinfo->ac_arg; + cbinfo->ac_cb = NULL; + cbinfo->ac_arg = NULL; + cbinfo->deadline_us = 0; + cb(cb_arg, alminfo_id); + } +} +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Name: esp_rtc_rdtime + * + * Description: + * Return the current RTC time. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * rcttime - The location in which to return the current RTC time. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER + +#ifdef CONFIG_RTC_HIRES +static int esp_rtc_rdtime(struct rtc_lowerhalf_s *lower, + struct rtc_time *rtctime) +{ + struct timespec ts; + int ret; + + /* Get the higher resolution time */ + + ret = up_rtc_gettime(&ts); + if (ret < 0) + { + goto errout; + } + + /* Convert the one second epoch time to a struct tmThis operation + * depends on the fact that struct rtc_time and struct tm are cast + * compatible. + */ + + if (!gmtime_r(&ts.tv_sec, (struct tm *)rtctime)) + { + ret = -get_errno(); + goto errout; + } + + return OK; + +errout: + rtcerr("Failed to get RTC time: %d\n", ret); + return ret; +} + +#else /* !CONFIG_RTC_HIRES */ + +static int esp_rtc_rdtime(struct rtc_lowerhalf_s *lower, + struct rtc_time *rtctime) +{ + time_t timer; + + /* The resolution of time is only 1 second */ + + timer = up_rtc_time(); + + /* Convert the one second epoch time to a struct tm */ + + if (gmtime_r(&timer, (struct tm *)rtctime) == 0) + { + int errcode = get_errno(); + DEBUGASSERT(errcode > 0); + + rtcerr("gmtime_r failed: %d\n", errcode); + return -errcode; + } + + return OK; +} +#endif /* CONFIG_RTC_HIRES */ + +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Name: esp_rtc_settime + * + * Description: + * Implement the settime() method of the RTC driver interface. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * rcttime - The new time to set. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER +static int esp_rtc_settime(struct rtc_lowerhalf_s *lower, + const struct rtc_time *rtctime) +{ + struct timespec ts; + + /* Convert the struct rtc_time to a time_t. Here we assume that struct + * rtc_time is cast compatible with struct tm. + */ + + ts.tv_sec = mktime((struct tm *)rtctime); + ts.tv_nsec = 0; + + /* Now set the time (with a accuracy of seconds) */ + + return up_rtc_settime(&ts); +} +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Name: esp_rtc_havesettime + * + * Description: + * Implement the havesettime() method of the RTC driver interface. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * + * Returned Value: + * True if RTC date-time have been previously set, false otherwise. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER +static bool esp_rtc_havesettime(struct rtc_lowerhalf_s *lower) +{ + return esp_rtc_get_boot_time() != 0; +} +#endif /* CONFIG_RTC_DRIVER */ + +/**************************************************************************** + * Name: esp_rtc_setalarm + * + * Description: + * Set a new alarm. This function implements the setalarm() method of the + * RTC driver interface. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * alarminfo - Provided information needed to set the alarm. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#if defined(CONFIG_RTC_DRIVER) && defined(CONFIG_RTC_ALARM) +static int esp_rtc_setalarm(struct rtc_lowerhalf_s *lower, + const struct lower_setalarm_s *alarminfo) +{ + struct esp_rtc_lowerhalf_s *priv = (struct esp_rtc_lowerhalf_s *)lower; + struct alm_cbinfo_s *cbinfo; + uint64_t timeout; + irqstate_t flags; + int id; + + DEBUGASSERT(priv != NULL); + DEBUGASSERT(alarminfo != NULL); + + timeout = mktime((struct tm *)&alarminfo->time); + + /* Set the alarm in RT-Timer */ + + id = alarminfo->id; + cbinfo = &priv->alarmcb[id]; + + if (cbinfo->ac_cb != NULL) + { + return -EBUSY; + } + + /* Create the RT-Timer alarm */ + + flags = spin_lock_irqsave(NULL); + + if (cbinfo->alarm_hdl == NULL) + { + struct esp_hr_timer_args_s hr_timer_args; + int ret; + + cbinfo->index = id; + hr_timer_args.arg = cbinfo; + hr_timer_args.callback = rtc_hr_timer_cb; + + ret = esp_hr_timer_create(&hr_timer_args, &cbinfo->alarm_hdl); + if (ret < 0) + { + rtcerr("Failed to create HR Timer=%d\n", ret); + spin_unlock_irqrestore(NULL, flags); + return ret; + } + } + + cbinfo->ac_cb = alarminfo->cb; + cbinfo->ac_arg = alarminfo->priv; + cbinfo->deadline_us = timeout * USEC_PER_SEC; + + rtcinfo("Starting alarm ID %d\n", id); + + esp_hr_timer_start(cbinfo->alarm_hdl, cbinfo->deadline_us, false); + + spin_unlock_irqrestore(NULL, flags); + + return OK; +} +#endif /* CONFIG_RTC_DRIVER && CONFIG_RTC_ALARM */ + +/**************************************************************************** + * Name: esp_rtc_setrelative + * + * Description: + * Set a new alarm relative to the current time. This function implements + * the setrelative() method of the RTC driver interface. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * alarminfo - Provided information needed to set the alarm. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#if defined(CONFIG_RTC_DRIVER) && defined(CONFIG_RTC_ALARM) +static int esp_rtc_setrelative(struct rtc_lowerhalf_s *lower, + const struct lower_setrelative_s *alarminfo) +{ + struct lower_setalarm_s setalarm; + time_t seconds; + int ret = -EINVAL; + irqstate_t flags; + + DEBUGASSERT(lower != NULL && alarminfo != NULL); + + if (alarminfo->reltime > 0) + { + flags = spin_lock_irqsave(NULL); + + seconds = alarminfo->reltime; + gmtime_r(&seconds, (struct tm *)&setalarm.time); + + /* The set the alarm using this absolute time */ + + setalarm.id = alarminfo->id; + setalarm.cb = alarminfo->cb; + setalarm.priv = alarminfo->priv; + ret = esp_rtc_setalarm(lower, &setalarm); + + spin_unlock_irqrestore(NULL, flags); + } + + return ret; +} +#endif /* CONFIG_RTC_DRIVER && CONFIG_RTC_ALARM */ + +/**************************************************************************** + * Name: esp_rtc_cancelalarm + * + * Description: + * Cancel the current alarm. This function implements the cancelalarm() + * method of the RTC driver interface. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * alarmid - ID of the alarm to be cancelled. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#if defined(CONFIG_RTC_DRIVER) && defined(CONFIG_RTC_ALARM) +static int esp_rtc_cancelalarm(struct rtc_lowerhalf_s *lower, int alarmid) +{ + struct esp_rtc_lowerhalf_s *priv; + struct alm_cbinfo_s *cbinfo; + irqstate_t flags; + + DEBUGASSERT(lower != NULL); + + priv = (struct esp_rtc_lowerhalf_s *)lower; + + /* Set the alarm in hardware and enable interrupts */ + + cbinfo = &priv->alarmcb[alarmid]; + + if (cbinfo->ac_cb == NULL) + { + return -ENODATA; + } + + flags = spin_lock_irqsave(NULL); + + /* Stop and delete the alarm */ + + rtcinfo("Cancelling alarm ID %d\n", alarmid); + + esp_hr_timer_stop(cbinfo->alarm_hdl); + esp_hr_timer_delete(cbinfo->alarm_hdl); + + cbinfo->ac_cb = NULL; + cbinfo->deadline_us = 0; + cbinfo->alarm_hdl = NULL; + + spin_unlock_irqrestore(NULL, flags); + + return OK; +} +#endif /* CONFIG_RTC_DRIVER && CONFIG_RTC_ALARM */ + +/**************************************************************************** + * Name: esp_rtc_rdalarm + * + * Description: + * Query the RTC alarm. + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure. + * alarminfo - Provided information needed to query the alarm. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#if defined(CONFIG_RTC_DRIVER) && defined(CONFIG_RTC_ALARM) +static int esp_rtc_rdalarm(struct rtc_lowerhalf_s *lower, + struct lower_rdalarm_s *alarminfo) +{ + struct esp_rtc_lowerhalf_s *priv; + struct timespec ts; + struct alm_cbinfo_s *cbinfo; + irqstate_t flags; + + DEBUGASSERT(lower != NULL); + DEBUGASSERT(alarminfo != NULL); + DEBUGASSERT(alarminfo->time != NULL); + + priv = (struct esp_rtc_lowerhalf_s *)lower; + + flags = spin_lock_irqsave(NULL); + + /* Get the alarm according to the alarm ID */ + + cbinfo = &priv->alarmcb[alarminfo->id]; + + ts.tv_sec = (esp_hr_timer_time_us() + g_rtc_save->offset + + cbinfo->deadline_us) / USEC_PER_SEC; + ts.tv_nsec = ((esp_hr_timer_time_us() + g_rtc_save->offset + + cbinfo->deadline_us) % USEC_PER_SEC) * NSEC_PER_USEC; + + localtime_r((const time_t *)&ts.tv_sec, + (struct tm *)alarminfo->time); + + spin_unlock_irqrestore(NULL, flags); + + return OK; +} +#endif /* CONFIG_RTC_DRIVER && CONFIG_RTC_ALARM */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_rtc_time + * + * Description: + * Get the current time in seconds. This is similar to the standard time() + * function. This interface is only required if the low-resolution + * RTC/counter hardware implementation is selected. It is only used by the + * RTOS during initialization to set up the system time when CONFIG_RTC is + * set but CONFIG_RTC_HIRES is not set. + * + * Input Parameters: + * None. + * + * Returned Value: + * Current time in seconds. + * + ****************************************************************************/ + +#ifndef CONFIG_RTC_HIRES +time_t up_rtc_time(void) +{ + uint64_t time_us; + irqstate_t flags; + + flags = spin_lock_irqsave(NULL); + +#ifdef CONFIG_RTC_DRIVER + /* NOTE: HR-Timer starts to work after the board is initialized, and the + * RTC controller starts works after up_rtc_initialize is initialized. + * Since the system clock starts to work before the board is initialized, + * if CONFIG_RTC is enabled, the system time must be matched by the time + * of the RTC controller (up_rtc_initialize has already been initialized, + * and HR-Timer cannot work). + */ + + /* Determine if HR-Timer is started */ + + if (g_hr_timer_enabled) + { + /* Get the time from HR-Timer, the time interval between RTC + * controller and HR-Timer is stored in g_rtc_save->offset. + */ + + time_us = esp_hr_timer_time_us() + g_rtc_save->offset + + esp_rtc_get_boot_time(); + } + else +#endif + { + /* Get the time from RTC controller */ + + time_us = esp_rtc_get_time_us() + esp_rtc_get_boot_time(); + } + + spin_unlock_irqrestore(NULL, flags); + + return (time_t)(time_us / USEC_PER_SEC); +} +#endif /* !CONFIG_RTC_HIRES */ + +/**************************************************************************** + * Name: up_rtc_gettime + * + * Description: + * Get the current time from the high resolution RTC time or HR-Timer. This + * interface is only supported by the high-resolution RTC/counter hardware + * implementation. It is used to replace the system timer. + * + * Input Parameters: + * tp - The location to return the RTC time or HR-Timer value. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_HIRES +int up_rtc_gettime(struct timespec *tp) +{ + irqstate_t flags; + uint64_t time_us; + + flags = spin_lock_irqsave(NULL); + +#ifdef CONFIG_RTC_DRIVER + if (g_hr_timer_enabled) + { + time_us = esp_hr_timer_time_us() + g_rtc_save->offset + + esp_rtc_get_boot_time(); + } + else +#endif + { + time_us = esp_rtc_get_time_us() + esp_rtc_get_boot_time(); + } + + tp->tv_sec = time_us / USEC_PER_SEC; + tp->tv_nsec = (time_us % USEC_PER_SEC) * NSEC_PER_USEC; + + spin_unlock_irqrestore(NULL, flags); + + return OK; +} +#endif /* CONFIG_RTC_HIRES */ + +/**************************************************************************** + * Name: up_rtc_settime + * + * Description: + * Set the RTC to the provided time. All RTC implementations must be + * able to set their time based on a standard timespec. + * + * Input Parameters: + * ts - Time to set. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +int up_rtc_settime(const struct timespec *ts) +{ + uint64_t now_us; + uint64_t rtc_offset_us; + irqstate_t flags; + + DEBUGASSERT(ts != NULL && ts->tv_nsec < NSEC_PER_SEC); + + flags = spin_lock_irqsave(NULL); + + now_us = ((uint64_t) ts->tv_sec) * USEC_PER_SEC + Review Comment: remove space from `(uint64_t) ts->tv_sec` -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: commits-unsubscr...@nuttx.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org