123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- /****************************************************************************
- * sched/clock/clock_initialize.c
- *
- * Copyright (C) 2007, 2009, 2011-2012, 2017-2018 Gregory Nutt. All rights
- * reserved.
- * Author: Gregory Nutt <gnutt@nuttx.org>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * 3. Neither the name NuttX nor the names of its contributors may be
- * used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- ****************************************************************************/
- /****************************************************************************
- * Included Files
- ****************************************************************************/
- #include <nuttx/config.h>
- #include <nuttx/compiler.h>
- #include <stdint.h>
- #include <time.h>
- #include <errno.h>
- #include <debug.h>
- #ifdef CONFIG_RTC
- # include <nuttx/irq.h>
- #endif
- #include <nuttx/arch.h>
- #include <nuttx/clock.h>
- #include <nuttx/time.h>
- #include "clock/clock.h"
- #ifdef CONFIG_CLOCK_TIMEKEEPING
- # include "clock/clock_timekeeping.h"
- #endif
- /****************************************************************************
- * Public Data
- ****************************************************************************/
- #ifndef CONFIG_SCHED_TICKLESS
- #ifdef CONFIG_SYSTEM_TIME64
- volatile uint64_t g_system_timer;
- #else
- volatile uint32_t g_system_timer;
- #endif
- #endif
- #ifndef CONFIG_CLOCK_TIMEKEEPING
- struct timespec g_basetime;
- #endif
- /****************************************************************************
- * Private Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: clock_basetime
- *
- * Description:
- * Get the initial time value from the best source available.
- *
- ****************************************************************************/
- #ifdef CONFIG_RTC
- #if defined(CONFIG_RTC_DATETIME)
- /* Initialize the system time using a broken out date/time structure */
- int clock_basetime(FAR struct timespec *tp)
- {
- struct tm rtctime;
- long nsecs = 0;
- int ret;
- /* Get the broken-out time from the date/time RTC. */
- #ifdef CONFIG_ARCH_HAVE_RTC_SUBSECONDS
- ret = up_rtc_getdatetime_with_subseconds(&rtctime, &nsecs);
- #else
- ret = up_rtc_getdatetime(&rtctime);
- #endif
- if (ret >= 0)
- {
- /* And use the broken-out time to initialize the system time */
- tp->tv_sec = mktime(&rtctime);
- tp->tv_nsec = nsecs;
- }
- return ret;
- }
- #elif defined(CONFIG_RTC_HIRES)
- /* Initialize the system time using a high-resolution structure */
- int clock_basetime(FAR struct timespec *tp)
- {
- /* Get the complete time from the hi-res RTC. */
- return up_rtc_gettime(tp);
- }
- #else
- /* Initialize the system time using seconds only */
- int clock_basetime(FAR struct timespec *tp)
- {
- /* Get the seconds (only) from the lo-resolution RTC */
- tp->tv_sec = up_rtc_time();
- tp->tv_nsec = 0;
- return OK;
- }
- #endif /* CONFIG_RTC_HIRES */
- #else /* CONFIG_RTC */
- int clock_basetime(FAR struct timespec *tp)
- {
- time_t jdn = 0;
- /* Get the EPOCH-relative julian date from the calendar year,
- * month, and date
- */
- jdn = clock_calendar2utc(CONFIG_START_YEAR, CONFIG_START_MONTH - 1,
- CONFIG_START_DAY);
- /* Set the base time as seconds into this julian day. */
- tp->tv_sec = jdn * SEC_PER_DAY;
- tp->tv_nsec = 0;
- return OK;
- }
- #endif /* CONFIG_RTC */
- /****************************************************************************
- * Name: clock_inittime
- *
- * Description:
- * Get the initial time value from the best source available.
- *
- ****************************************************************************/
- static void clock_inittime(void)
- {
- /* (Re-)initialize the time value to match the RTC */
- #ifndef CONFIG_CLOCK_TIMEKEEPING
- #ifndef CONFIG_RTC_HIRES
- clock_basetime(&g_basetime);
- #endif
- #ifndef CONFIG_SCHED_TICKLESS
- g_system_timer = INITIAL_SYSTEM_TIMER_TICKS;
- if (g_system_timer > 0)
- {
- struct timespec ts;
- (void)clock_ticks2time((sclock_t)g_system_timer, &ts);
- /* Adjust base time to hide initial timer ticks. */
- g_basetime.tv_sec -= ts.tv_sec;
- g_basetime.tv_nsec -= ts.tv_nsec;
- while (g_basetime.tv_nsec < 0)
- {
- g_basetime.tv_nsec += NSEC_PER_SEC;
- g_basetime.tv_sec--;
- }
- }
- #endif /* !CONFIG_SCHED_TICKLESS */
- #else
- clock_inittimekeeping();
- #endif
- }
- /****************************************************************************
- * Public Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: clock_initialize
- *
- * Description:
- * Perform one-time initialization of the timing facilities.
- *
- ****************************************************************************/
- void clock_initialize(void)
- {
- #if defined(CONFIG_RTC) && !defined(CONFIG_RTC_EXTERNAL)
- /* Initialize the internal RTC hardware. Initialization of external RTC
- * must be deferred until the system has booted.
- */
- up_rtc_initialize();
- #endif
- /* Initialize the time value to match the RTC */
- clock_inittime();
- }
- /****************************************************************************
- * Name: clock_synchronize
- *
- * Description:
- * Synchronize the system timer to a hardware RTC. This operation is
- * normally performed automatically by the system during clock
- * initialization. However, the user may also need to explicitly re-
- * synchronize the system timer to the RTC under certain conditions where
- * the system timer is known to be in error. For example, in certain low-
- * power states, the system timer may be stopped but the RTC will continue
- * keep correct time. After recovering from such low-power state, this
- * function should be called to restore the correct system time.
- *
- * Calling this function could result in system time going "backward" in
- * time, especially with certain lower resolution RTC implementations.
- * Time going backward could have bad consequences if there are ongoing
- * timers and delays. So use this interface with care.
- *
- * Input Parameters:
- * None
- *
- * Returned Value:
- * None
- *
- * Assumptions:
- *
- ****************************************************************************/
- #ifdef CONFIG_RTC
- void clock_synchronize(void)
- {
- irqstate_t flags;
- /* Re-initialize the time value to match the RTC */
- flags = enter_critical_section();
- clock_inittime();
- leave_critical_section(flags);
- }
- #endif
- /****************************************************************************
- * Name: clock_resynchronize
- *
- * Description:
- * Resynchronize the system timer to a hardware RTC. The user can
- * explicitly re-synchronize the system timer to the RTC under certain
- * conditions where the system timer is known to be in error. For example,
- * in certain low-power states, the system timer may be stopped but the
- * RTC will continue keep correct time. After recovering from such
- * low-power state, this function should be called to restore the correct
- * system time. Function also keeps monotonic clock at rate of RTC.
- *
- * Calling this function will not result in system time going "backward" in
- * time. If setting system time with RTC would result time going "backward"
- * then resynchronization is not performed.
- *
- * Input Parameters:
- * rtc_diff: amount of time system-time is adjusted forward with RTC
- *
- * Returned Value:
- * None
- *
- * Assumptions:
- *
- ****************************************************************************/
- #if defined(CONFIG_RTC) && !defined(CONFIG_SCHED_TICKLESS)
- void clock_resynchronize(FAR struct timespec *rtc_diff)
- {
- struct timespec rtc_time;
- struct timespec bias;
- struct timespec curr_ts;
- struct timespec rtc_diff_tmp;
- irqstate_t flags;
- int32_t carry;
- int ret;
- if (rtc_diff == NULL)
- {
- rtc_diff = &rtc_diff_tmp;
- }
- /* Set the time value to match the RTC */
- flags = enter_critical_section();
- /* Get RTC time */
- ret = clock_basetime(&rtc_time);
- if (ret < 0)
- {
- /* Error reading RTC, skip resynchronization. */
- sinfo("rtc error %d, skip resync\n", ret);
- rtc_diff->tv_sec = 0;
- rtc_diff->tv_nsec = 0;
- goto skip;
- }
- /* Get the elapsed time since power up (in milliseconds). This is a
- * bias value that we need to use to correct the base time.
- */
- (void)clock_systimespec(&bias);
- /* Add the base time to this. The base time is the time-of-day
- * setting. When added to the elapsed time since the time-of-day
- * was last set, this gives us the current time.
- */
- curr_ts.tv_sec = bias.tv_sec + g_basetime.tv_sec;
- curr_ts.tv_nsec = bias.tv_nsec + g_basetime.tv_nsec;
- /* Handle carry to seconds. */
- if (curr_ts.tv_nsec >= NSEC_PER_SEC)
- {
- carry = curr_ts.tv_nsec / NSEC_PER_SEC;
- curr_ts.tv_sec += carry;
- curr_ts.tv_nsec -= (carry * NSEC_PER_SEC);
- }
- /* Check if RTC has advanced past system time. */
- if (curr_ts.tv_sec > rtc_time.tv_sec ||
- (curr_ts.tv_sec == rtc_time.tv_sec && curr_ts.tv_nsec >= rtc_time.tv_nsec))
- {
- /* Setting system time with RTC now would result time going
- * backwards. Skip resynchronization.
- */
- sinfo("skip resync\n");
- rtc_diff->tv_sec = 0;
- rtc_diff->tv_nsec = 0;
- }
- else
- {
- /* Output difference between time at entry and new current time. */
- rtc_diff->tv_sec = rtc_time.tv_sec - curr_ts.tv_sec;
- rtc_diff->tv_nsec = rtc_time.tv_nsec - curr_ts.tv_nsec;
- /* Handle carry to seconds. */
- if (rtc_diff->tv_nsec < 0)
- {
- carry = -((-(rtc_diff->tv_nsec + 1)) / NSEC_PER_SEC + 1);
- }
- else if (rtc_diff->tv_nsec >= NSEC_PER_SEC)
- {
- carry = rtc_diff->tv_nsec / NSEC_PER_SEC;
- }
- else
- {
- carry = 0;
- }
- if (carry != 0)
- {
- rtc_diff->tv_sec += carry;
- rtc_diff->tv_nsec -= (carry * NSEC_PER_SEC);
- }
- /* Add the sleep time to correct system timer */
- g_system_timer += SEC2TICK(rtc_diff->tv_sec);
- g_system_timer += NSEC2TICK(rtc_diff->tv_nsec);
- }
- skip:
- leave_critical_section(flags);
- }
- #endif
- /****************************************************************************
- * Name: clock_timer
- *
- * Description:
- * This function must be called once every time the real time clock
- * interrupt occurs. The interval of this clock interrupt must be
- * USEC_PER_TICK
- *
- ****************************************************************************/
- #ifndef CONFIG_SCHED_TICKLESS
- void clock_timer(void)
- {
- /* Increment the per-tick system counter */
- g_system_timer++;
- }
- #endif
|