diff --git a/drivers/timers/Kconfig b/drivers/timers/Kconfig index 1c6e8c29e8..d0829da51e 100644 --- a/drivers/timers/Kconfig +++ b/drivers/timers/Kconfig @@ -13,6 +13,17 @@ config TIMER driver. See include/nuttx/timers/timer.h for further timer driver information. +if TIMER + +config TIMER_ARCH + bool "Timer Arch Implementation" + select ARCH_HAVE_TICKLESS + select ARCH_HAVE_TIMEKEEPING + ---help--- + Implement timer arch API on top of timer driver interface. + +endif # TIMER + config ONESHOT bool "Oneshot timer driver" default n @@ -21,6 +32,17 @@ config ONESHOT driver. See include/nuttx/timers/oneshot.h for further oneshot timer driver information. +if ONESHOT + +config ALARM_ARCH + bool "Alarm Arch Implementation" + select ARCH_HAVE_TICKLESS + select ARCH_HAVE_TIMEKEEPING + ---help--- + Implement alarm arch API on top of oneshot driver interface. + +endif # ONESHOT + menuconfig RTC bool "RTC Driver Support" default n @@ -95,6 +117,12 @@ config RTC_DRIVER if RTC_DRIVER +config RTC_ARCH + bool "RTC Arch Implementation" + default n + ---help--- + Implement RTC arch API on top of RTC driver interface. + config RTC_PERIODIC bool "RTC Periodic Interrupts" default n diff --git a/drivers/timers/Make.defs b/drivers/timers/Make.defs index 64940c43bb..c6c82c679c 100644 --- a/drivers/timers/Make.defs +++ b/drivers/timers/Make.defs @@ -51,12 +51,24 @@ ifeq ($(CONFIG_TIMER),y) TMRVPATH = :timers endif +ifeq ($(CONFIG_TIMER_ARCH),y) + CSRCS += arch_timer.c + TMRDEPPATH = --dep-path timers + TMRVPATH = :timers +endif + ifeq ($(CONFIG_ONESHOT),y) CSRCS += oneshot.c TMRDEPPATH = --dep-path timers TMRVPATH = :timers endif +ifeq ($(CONFIG_ALARM_ARCH),y) + CSRCS += arch_alarm.c + TMRDEPPATH = --dep-path timers + TMRVPATH = :timers +endif + ifeq ($(CONFIG_RTC_DSXXXX),y) CSRCS += ds3231.c TMRDEPPATH = --dep-path timers @@ -69,6 +81,12 @@ ifeq ($(CONFIG_RTC_PCF85263),y) TMRVPATH = :timers endif +ifeq ($(CONFIG_RTC_ARCH),y) + CSRCS += arch_rtc.c + TMRDEPPATH = --dep-path timers + TMRVPATH = :timers +endif + ifeq ($(CONFIG_RTC_DRIVER),y) CSRCS += rtc.c TMRDEPPATH = --dep-path timers diff --git a/drivers/timers/arch_alarm.c b/drivers/timers/arch_alarm.c new file mode 100644 index 0000000000..d86e90440f --- /dev/null +++ b/drivers/timers/arch_alarm.c @@ -0,0 +1,418 @@ +/**************************************************************************** + * drivers/timers/arch_alarm.c + * + * Copyright (C) 2017 Pinecone Inc. All rights reserved. + * Author: Xiang Xiao + * + * 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 + +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_SCHED_TICKLESS + +# ifndef CONFIG_SCHED_TICKLESS_ALARM +# error CONFIG_SCHED_TICKLESS_ALARM must be set to use CONFIG_SCHED_TICKLESS +# endif + +# ifndef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP +# error CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP must be set to use CONFIG_SCHED_TICKLESS +# endif + +#endif + +#define CONFIG_BOARD_LOOPSPER100USEC ((CONFIG_BOARD_LOOPSPERMSEC+5)/10) +#define CONFIG_BOARD_LOOPSPER10USEC ((CONFIG_BOARD_LOOPSPERMSEC+50)/100) +#define CONFIG_BOARD_LOOPSPERUSEC ((CONFIG_BOARD_LOOPSPERMSEC+500)/1000) + +#define timespec_to_usec(ts) \ + ((uint64_t)(ts)->tv_sec * USEC_PER_SEC + (ts)->tv_nsec / NSEC_PER_USEC) + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static FAR struct oneshot_lowerhalf_s *g_oneshot_lower; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static inline void timespec_from_usec(FAR struct timespec *ts, + unsigned int microseconds) +{ + ts->tv_sec = microseconds / USEC_PER_SEC; + microseconds -= (uint64_t)ts->tv_sec * USEC_PER_SEC; + ts->tv_nsec = microseconds * NSEC_PER_USEC; +} + +static inline int timespec_compare(FAR const struct timespec *ts1, + FAR const struct timespec *ts2) +{ + if (ts1->tv_sec != ts2->tv_sec) + { + return ts1->tv_sec - ts2->tv_sec; + } + else + { + return ts1->tv_nsec - ts2->tv_nsec; + } +} + +static void udelay_accurate(useconds_t microseconds) +{ + struct timespec now; + struct timespec end; + struct timespec delta; + + ONESHOT_CURRENT(g_oneshot_lower, &now); + timespec_from_usec(&delta, microseconds); + clock_timespec_add(&now, &delta, &end); + + while (timespec_compare(&now, &end) < 0) + { + ONESHOT_CURRENT(g_oneshot_lower, &now); + } +} + +static void udelay_coarse(useconds_t microseconds) +{ + volatile int i; + + /* We'll do this a little at a time because we expect that the + * CONFIG_BOARD_LOOPSPERUSEC is very inaccurate during to truncation in + * the divisions of its calculation. We'll use the largest values that + * we can in order to prevent significant error buildup in the loops. + */ + + while (microseconds > 1000) + { + for (i = 0; i < CONFIG_BOARD_LOOPSPERMSEC; i++) + { + } + + microseconds -= 1000; + } + + while (microseconds > 100) + { + for (i = 0; i < CONFIG_BOARD_LOOPSPER100USEC; i++) + { + } + + microseconds -= 100; + } + + while (microseconds > 10) + { + for (i = 0; i < CONFIG_BOARD_LOOPSPER10USEC; i++) + { + } + + microseconds -= 10; + } + + while (microseconds > 0) + { + for (i = 0; i < CONFIG_BOARD_LOOPSPERUSEC; i++) + { + } + + microseconds--; + } +} + +static void oneshot_callback(FAR struct oneshot_lowerhalf_s *lower, FAR void *arg) +{ +#ifdef CONFIG_SCHED_TICKLESS + struct timespec now; + + ONESHOT_CURRENT(g_oneshot_lower, &now); + sched_alarm_expiration(&now); +#else + struct timespec now; + struct timespec next; + struct timespec delta; + static uint64_t tick = 1; + + timespec_from_usec(&next, ++tick * USEC_PER_TICK); + ONESHOT_CURRENT(g_oneshot_lower, &now); + clock_timespec_subtract(&next, &now, &delta); + ONESHOT_START(g_oneshot_lower, oneshot_callback, NULL, &delta); + sched_process_timer(); +#endif +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void up_alarm_set_lowerhalf(FAR struct oneshot_lowerhalf_s *lower) +{ +#ifdef CONFIG_SCHED_TICKLESS + struct timespec maxts; + uint64_t maxticks; + + g_oneshot_lower = lower; + ONESHOT_MAX_DELAY(g_oneshot_lower, &maxts); + maxticks = timespec_to_usec(&maxts) / USEC_PER_TICK; + g_oneshot_maxticks = maxticks < UINT32_MAX ? maxticks : UINT32_MAX; +#else + struct timespec ts; + + g_oneshot_lower = lower; + timespec_from_usec(&ts, USEC_PER_TICK); + ONESHOT_START(g_oneshot_lower, oneshot_callback, NULL, &ts); +#endif +} + +/**************************************************************************** + * Name: up_timer_gettime + * + * Description: + * Return the elapsed time since power-up (or, more correctly, since + * the archtecture-specific timer was initialized). This function is + * functionally equivalent to: + * + * int clock_gettime(clockid_t clockid, FAR struct timespec *ts); + * + * when clockid is CLOCK_MONOTONIC. + * + * This function provides the basis for reporting the current time and + * also is used to eliminate error build-up from small errors in interval + * time calculations. + * + * Provided by platform-specific code and called from the RTOS base code. + * + * Input Parameters: + * ts - Provides the location in which to return the up-time. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + * Assumptions: + * Called from the normal tasking context. The implementation must + * provide whatever mutual exclusion is necessary for correct operation. + * This can include disabling interrupts in order to assure atomic register + * operations. + * + ****************************************************************************/ + +#ifdef CONFIG_CLOCK_TIMEKEEPING +int up_timer_getcounter(FAR uint64_t *cycles) +{ + struct timespec now; + + if (!g_oneshot_lower) + { + *cycles = 0; + return 0; + } + + ONESHOT_CURRENT(g_oneshot_lower, &now); + *cycles = timespec_to_usec(&now) / USEC_PER_TICK; + return 0; +} + +void up_timer_getmask(FAR uint64_t *mask) +{ + struct timespec maxts; + uint64_t maxticks = 0; + + if (g_oneshot_lower) + { + ONESHOT_MAX_DELAY(g_oneshot_lower, &maxts); + maxticks = timespec_to_usec(&maxts) / USEC_PER_TICK; + } + + *mask = 0; + while (1) + { + uint64_t next = (*mask << 1) | 1; + if (next > maxticks) + { + break; + } + *mask = next; + } +} +#elif defined(CONFIG_SCHED_TICKLESS) +int up_timer_gettime(FAR struct timespec *ts) +{ + if (!g_oneshot_lower) + { + memset(ts, 0, sizeof(*ts)); + return 0; + } + + ONESHOT_CURRENT(g_oneshot_lower, ts); + return 0; +} +#endif + +/**************************************************************************** + * Name: up_alarm_cancel + * + * Description: + * Cancel the alarm and return the time of cancellation of the alarm. + * These two steps need to be as nearly atomic as possible. + * sched_alarm_expiration() will not be called unless the alarm is + * restarted with up_alarm_start(). + * + * If, as a race condition, the alarm has already expired when this + * function is called, then time returned is the current time. + * + * NOTE: This function may execute at a high rate with no timer running (as + * when pre-emption is enabled and disabled). + * + * Provided by platform-specific code and called from the RTOS base code. + * + * Input Parameters: + * ts - Location to return the expiration time. The current time should + * returned if the alarm is not active. ts may be NULL in which + * case the time is not returned. + * + * Returned Value: + * Zero (OK) is returned on success. A call to up_alarm_cancel() when + * the timer is not active should also return success; a negated errno + * value is returned on any failure. + * + * Assumptions: + * May be called from interrupt level handling or from the normal tasking + * level. Interrupts may need to be disabled internally to assure + * non-reentrancy. + * + ****************************************************************************/ + +#ifdef CONFIG_SCHED_TICKLESS +int up_alarm_cancel(FAR struct timespec *ts) +{ + if (!g_oneshot_lower) + { + return -EAGAIN; + } + + ONESHOT_CANCEL(g_oneshot_lower, ts); + ONESHOT_CURRENT(g_oneshot_lower, ts); + return 0; +} +#endif + +/**************************************************************************** + * Name: up_alarm_start + * + * Description: + * Start the alarm. sched_alarm_expiration() will be called when the + * alarm occurs (unless up_alaram_cancel is called to stop it). + * + * Provided by platform-specific code and called from the RTOS base code. + * + * Input Parameters: + * ts - The time in the future at the alarm is expected to occur. When + * the alarm occurs the timer logic will call sched_alarm_expiration(). + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + * Assumptions: + * May be called from interrupt level handling or from the normal tasking + * level. Interrupts may need to be disabled internally to assure + * non-reentrancy. + * + ****************************************************************************/ + +#ifdef CONFIG_SCHED_TICKLESS +int up_alarm_start(FAR const struct timespec *ts) +{ + struct timespec now; + struct timespec delta; + + if (!g_oneshot_lower) + { + return -EAGAIN; + } + + ONESHOT_CURRENT(g_oneshot_lower, &now); + clock_timespec_subtract(ts, &now, &delta); + ONESHOT_START(g_oneshot_lower, oneshot_callback, NULL, &delta); + return 0; +} +#endif + +/**************************************************************************** + * Name: up_mdelay + * + * Description: + * Delay inline for the requested number of milliseconds. + * *** NOT multi-tasking friendly *** + * + ****************************************************************************/ + +void up_mdelay(unsigned int milliseconds) +{ + up_udelay(USEC_PER_MSEC * milliseconds); +} + +/**************************************************************************** + * Name: up_udelay + * + * Description: + * Delay inline for the requested number of microseconds. + * + * *** NOT multi-tasking friendly *** + * + ****************************************************************************/ + +void up_udelay(useconds_t microseconds) +{ + if (g_oneshot_lower) + { + udelay_accurate(microseconds); + } + else /* oneshot timer doesn't init yet */ + { + udelay_coarse(microseconds); + } +} diff --git a/drivers/timers/arch_rtc.c b/drivers/timers/arch_rtc.c new file mode 100644 index 0000000000..5a59470176 --- /dev/null +++ b/drivers/timers/arch_rtc.c @@ -0,0 +1,276 @@ +/**************************************************************************** + * drivers/timers/arch_rtc.c + * + * Copyright (C) 2017 Pinecone Inc. All rights reserved. + * Author: Xiang Xiao + * + * 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 + +#include +#include + +#include + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static FAR struct rtc_lowerhalf_s *g_rtc_lower; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Variable determines the state of the RTC module. + * + * After initialization value is set to 'true' if RTC starts successfully. + * The value can be changed to false also during operation if RTC for + * some reason fails. + */ + +volatile bool g_rtc_enabled = false; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void up_rtc_set_lowerhalf(FAR struct rtc_lowerhalf_s *lower) +{ + g_rtc_lower = lower; + g_rtc_enabled = true; +} + +/************************************************************************************ + * 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 selected. It is only used by the RTOS during + * initialization to set up the system time when CONFIG_RTC is set but neither + * CONFIG_RTC_HIRES nor CONFIG_RTC_DATETIME are set. + * + * Input Parameters: + * None + * + * Returned Value: + * The current time in seconds. + * + ************************************************************************************/ + +#ifndef CONFIG_RTC_HIRES +time_t up_rtc_time(void) +{ + struct rtc_time rtctime; + time_t time = 0; + + if (!g_rtc_lower) + { + return 0; + } + + if (g_rtc_lower->ops->rdtime(g_rtc_lower, &rtctime) == 0) + { + time = mktime((FAR struct tm *)&rtctime); + } + + return time; +} +#endif + +/************************************************************************************ + * Name: up_rtc_gettime + * + * Description: + * Get the current time from the high resolution RTC clock/counter. 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 high resolution time value. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ************************************************************************************/ + +#ifdef CONFIG_RTC_HIRES +int up_rtc_gettime(FAR struct timespec *tp) +{ + struct rtc_time rtctime; + int ret; + + if (!g_rtc_lower) + { + memset(tp, 0, sizeof(*tp)); + return 0; + } + + ret = g_rtc_lower->ops->rdtime(g_rtc_lower, &rtctime); + if (ret == 0) + { + tp->tv_sec = mktime((FAR struct tm *)&rtctime); + tp->tv_nsec = rtctime.tm_nsec; + } + + return ret; +} +#endif + +/************************************************************************************ + * Name: up_rtc_getdatetime + * + * Description: + * Get the current date and time from the date/time RTC. This interface + * is only supported by the date/time RTC hardware implementation. + * It is used to replace the system timer. It is only used by the RTOS during + * initialization to set up the system time when CONFIG_RTC and CONFIG_RTC_DATETIME + * are selected (and CONFIG_RTC_HIRES is not). + * + * NOTE: Some date/time RTC hardware is capability of sub-second accuracy. That + * sub-second accuracy is lost in this interface. However, since the system time + * is reinitialized on each power-up/reset, there will be no timing inaccuracy in + * the long run. + * + * Input Parameters: + * tp - The location to return the high resolution time value. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ************************************************************************************/ + +#ifdef CONFIG_RTC_DATETIME +int up_rtc_getdatetime(FAR struct tm *tp) +{ + struct rtc_time rtctime; + int ret; + + if (!g_rtc_lower) + { + memset(tp, 0, sizeof(*tp)); + return 0; + } + + ret = g_rtc_lower->ops->rdtime(g_rtc_lower, &rtctime); + if (ret == 0) + { + *tp = *((FAR struct tm *)&rtctime); + } + + return ret; +} +#endif + +/************************************************************************************ + * Name: up_rtc_getdatetime_with_subseconds + * + * Description: + * Get the current date and time from the date/time RTC. This interface + * is only supported by the date/time RTC hardware implementation. + * It is used to replace the system timer. It is only used by the RTOS during + * initialization to set up the system time when CONFIG_RTC and CONFIG_RTC_DATETIME + * are selected (and CONFIG_RTC_HIRES is not). + * + * NOTE: This interface exposes sub-second accuracy capability of RTC hardware. + * This interface allow maintaining timing accuracy when system time needs constant + * resynchronization with RTC, for example on MCU with low-power state that + * stop system timer. + * + * Input Parameters: + * tp - The location to return the high resolution time value. + * nsec - The location to return the subsecond time value. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +#if defined(CONFIG_RTC_DATETIME) && defined(CONFIG_ARCH_HAVE_RTC_SUBSECONDS) +int up_rtc_getdatetime_with_subseconds(FAR struct tm *tp, FAR long *nsec) +{ + struct rtc_time rtctime; + int ret; + + if (!g_rtc_lower) + { + memset(tp, 0, sizeof(*tp)); + *nsec = 0; + return 0; + } + + ret = g_rtc_lower->ops->rdtime(g_rtc_lower, &rtctime); + if (ret == 0) + { + *tp = *((FAR struct tm *)&rtctime); + *nsec = rtctime.tm_nsec; + } + + return ret; +} +#endif + +/************************************************************************************ + * 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: + * tp - the time to use + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ************************************************************************************/ + +int up_rtc_settime(FAR const struct timespec *tp) +{ + struct rtc_time rtctime; + + if (!g_rtc_lower) + { + return -EAGAIN; + } + + gmtime_r(&tp->tv_sec, (FAR struct tm *)&rtctime); +#if defined(CONFIG_RTC_HIRES) || defined(CONFIG_ARCH_HAVE_RTC_SUBSECONDS) + rtctime.tm_nsec = tp->tv_nsec; +#endif + + return g_rtc_lower->ops->settime(g_rtc_lower, &rtctime); +} diff --git a/drivers/timers/arch_timer.c b/drivers/timers/arch_timer.c new file mode 100644 index 0000000000..9fced5b1da --- /dev/null +++ b/drivers/timers/arch_timer.c @@ -0,0 +1,457 @@ +/**************************************************************************** + * drivers/timers/arch_timer.c + * + * Copyright (C) 2017 Pinecone Inc. All rights reserved. + * Author: Xiang Xiao + * + * 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 + +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_SCHED_TICKLESS + +# ifdef CONFIG_SCHED_TICKLESS_ALARM +# error CONFIG_SCHED_TICKLESS_ALARM must be unset to use CONFIG_SCHED_TICKLESS +# endif + +# ifndef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP +# error CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP must be set to use CONFIG_SCHED_TICKLESS +# endif + +#endif + +#define CONFIG_BOARD_LOOPSPER100USEC ((CONFIG_BOARD_LOOPSPERMSEC+5)/10) +#define CONFIG_BOARD_LOOPSPER10USEC ((CONFIG_BOARD_LOOPSPERMSEC+50)/100) +#define CONFIG_BOARD_LOOPSPERUSEC ((CONFIG_BOARD_LOOPSPERMSEC+500)/1000) + +#define TIMER_START(l) ((l)->ops->start(l)) +#define TIMER_GETSTATUS(l,s) ((l)->ops->getstatus(l,s)) +#define TIMER_SETTIMEOUT(l,t) ((l)->ops->settimeout(l,t)) +#define TIMER_SETCALLBACK(l,c,a) ((l)->ops->setcallback(l,c,a)) +#define TIMER_MAXTIMEOUT(l,t) ((l)->ops->maxtimeout(l,t)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct arch_timer_s +{ + FAR struct timer_lowerhalf_s *lower; + uint32_t *next_interval; + uint32_t maxtimeout; + uint64_t timebase; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct arch_timer_s g_timer; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +#ifdef CONFIG_SCHED_TICKLESS +static inline uint64_t timespec_to_usec(const FAR struct timespec *ts) +{ + return (uint64_t)ts->tv_sec * USEC_PER_SEC + ts->tv_nsec / NSEC_PER_USEC; +} + +static inline void timespec_from_usec(FAR struct timespec *ts, + uint64_t microseconds) +{ + ts->tv_sec = microseconds / USEC_PER_SEC; + microseconds -= (uint64_t)ts->tv_sec * USEC_PER_SEC; + ts->tv_nsec = microseconds * NSEC_PER_USEC; +} + +static inline bool timeout_diff(uint32_t new, uint32_t old) +{ + return new < old ? old - new >= USEC_PER_TICK : new - old >= USEC_PER_TICK; +} + +static uint32_t update_timeout(uint32_t timeout) +{ + struct timer_status_s status; + + /* Don't need critical section here + * since caller already do it for us + */ + + TIMER_GETSTATUS(g_timer.lower, &status); + if (g_timer.next_interval) + { + /* If the timer interrupt is in the process, + * let the callback return the right interval. + */ + + *g_timer.next_interval = timeout; + } + else if (timeout_diff(timeout, status.timeleft)) + { + /* Otherwise, update the timeout directly. */ + + TIMER_SETTIMEOUT(g_timer.lower, timeout); + g_timer.timebase += status.timeout - status.timeleft; + } + + return status.timeleft; +} +#endif + +static uint64_t current_usec(void) +{ + struct timer_status_s status; + uint64_t timebase; + irqstate_t flags; + + flags = enter_critical_section(); + TIMER_GETSTATUS(g_timer.lower, &status); + timebase = g_timer.timebase; + leave_critical_section(flags); + + return timebase + (status.timeout - status.timeleft); +} + +static void udelay_accurate(useconds_t microseconds) +{ + uint64_t start = current_usec(); + while (current_usec() - start < microseconds) + { + ; /* Wait until the timeout reach */ + } +} + +static void udelay_coarse(useconds_t microseconds) +{ + volatile int i; + + /* We'll do this a little at a time because we expect that the + * CONFIG_BOARD_LOOPSPERUSEC is very inaccurate during to truncation in + * the divisions of its calculation. We'll use the largest values that + * we can in order to prevent significant error buildup in the loops. + */ + + while (microseconds > 1000) + { + for (i = 0; i < CONFIG_BOARD_LOOPSPERMSEC; i++) + { + } + + microseconds -= 1000; + } + + while (microseconds > 100) + { + for (i = 0; i < CONFIG_BOARD_LOOPSPER100USEC; i++) + { + } + + microseconds -= 100; + } + + while (microseconds > 10) + { + for (i = 0; i < CONFIG_BOARD_LOOPSPER10USEC; i++) + { + } + + microseconds -= 10; + } + + while (microseconds > 0) + { + for (i = 0; i < CONFIG_BOARD_LOOPSPERUSEC; i++) + { + } + + microseconds--; + } +} + +static bool timer_callback(FAR uint32_t *next_interval_us, FAR void *arg) +{ +#ifdef CONFIG_SCHED_TICKLESS + struct timer_status_s status; + uint32_t next_interval; + + g_timer.timebase += *next_interval_us; + next_interval = g_timer.maxtimeout; + g_timer.next_interval = &next_interval; + sched_timer_expiration(); + g_timer.next_interval = NULL; + + TIMER_GETSTATUS(g_timer.lower, &status); + if (timeout_diff(next_interval, status.timeleft)) + { + g_timer.timebase += status.timeout - status.timeleft; + *next_interval_us = next_interval; + } +#else + g_timer.timebase += USEC_PER_TICK; + sched_process_timer(); +#endif + + return true; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void up_timer_set_lowerhalf(FAR struct timer_lowerhalf_s *lower) +{ + g_timer.lower = lower; + + TIMER_MAXTIMEOUT(g_timer.lower, &g_timer.maxtimeout); + +#ifdef CONFIG_SCHED_TICKLESS + g_oneshot_maxticks = g_timer.maxtimeout / USEC_PER_TICK; + TIMER_SETTIMEOUT(g_timer.lower, g_timer.maxtimeout); +#else + TIMER_SETTIMEOUT(g_timer.lower, USEC_PER_TICK); +#endif + + TIMER_SETCALLBACK(g_timer.lower, timer_callback, NULL); + TIMER_START(g_timer.lower); +} + +/**************************************************************************** + * Name: up_timer_gettime + * + * Description: + * Return the elapsed time since power-up (or, more correctly, since + * the archtecture-specific timer was initialized). This function is + * functionally equivalent to: + * + * int clock_gettime(clockid_t clockid, FAR struct timespec *ts); + * + * when clockid is CLOCK_MONOTONIC. + * + * This function provides the basis for reporting the current time and + * also is used to eliminate error build-up from small errors in interval + * time calculations. + * + * Provided by platform-specific code and called from the RTOS base code. + * + * Input Parameters: + * ts - Provides the location in which to return the up-time. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + * Assumptions: + * Called from the normal tasking context. The implementation must + * provide whatever mutual exclusion is necessary for correct operation. + * This can include disabling interrupts in order to assure atomic register + * operations. + * + ****************************************************************************/ + +#ifdef CONFIG_CLOCK_TIMEKEEPING +int up_timer_getcounter(FAR uint64_t *cycles) +{ + if (!g_timer.lower) + { + *cycles = 0; + return 0; + } + + *cycles = current_usec() / USEC_PER_TICK; + return 0; +} + +void up_timer_getmask(FAR uint64_t *mask) +{ + uint32_t maxticks = g_timer.maxtimeout / USEC_PER_TICK; + + *mask = 0; + while (1) + { + uint64_t next = (*mask << 1) | 1; + if (next > maxticks) + { + break; + } + *mask = next; + } +} +#elif defined(CONFIG_SCHED_TICKLESS) +int up_timer_gettime(FAR struct timespec *ts) +{ + if (!g_timer.lower) + { + memset(ts, 0, sizeof(*ts)); + return 0; + } + + timespec_from_usec(ts, current_usec()); + return 0; +} +#endif + +/**************************************************************************** + * Name: up_timer_cancel + * + * Description: + * Cancel the interval timer and return the time remaining on the timer. + * These two steps need to be as nearly atomic as possible. + * sched_timer_expiration() will not be called unless the timer is + * restarted with up_timer_start(). + * + * If, as a race condition, the timer has already expired when this + * function is called, then that pending interrupt must be cleared so + * that up_timer_start() and the remaining time of zero should be + * returned. + * + * NOTE: This function may execute at a high rate with no timer running (as + * when pre-emption is enabled and disabled). + * + * Provided by platform-specific code and called from the RTOS base code. + * + * Input Parameters: + * ts - Location to return the remaining time. Zero should be returned + * if the timer is not active. ts may be zero in which case the + * time remaining is not returned. + * + * Returned Value: + * Zero (OK) is returned on success. A call to up_timer_cancel() when + * the timer is not active should also return success; a negated errno + * value is returned on any failure. + * + * Assumptions: + * May be called from interrupt level handling or from the normal tasking + * level. Interrupts may need to be disabled internally to assure + * non-reentrancy. + * + ****************************************************************************/ + +#ifdef CONFIG_SCHED_TICKLESS +int up_timer_cancel(FAR struct timespec *ts) +{ + if (!g_timer.lower) + { + return -EAGAIN; + } + + timespec_from_usec(ts, update_timeout(g_timer.maxtimeout)); + return 0; +} +#endif + +/**************************************************************************** + * Name: up_timer_start + * + * Description: + * Start the interval timer. sched_timer_expiration() will be called at + * the completion of the timeout (unless up_timer_cancel is called to stop + * the timing. + * + * Provided by platform-specific code and called from the RTOS base code. + * + * Input Parameters: + * ts - Provides the time interval until sched_timer_expiration() is + * called. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + * Assumptions: + * May be called from interrupt level handling or from the normal tasking + * level. Interrupts may need to be disabled internally to assure + * non-reentrancy. + * + ****************************************************************************/ + +#ifdef CONFIG_SCHED_TICKLESS +int up_timer_start(FAR const struct timespec *ts) +{ + if (!g_timer.lower) + { + return -EAGAIN; + } + + update_timeout(timespec_to_usec(ts)); + return 0; +} +#endif + +/**************************************************************************** + * Name: up_mdelay + * + * Description: + * Delay inline for the requested number of milliseconds. + * *** NOT multi-tasking friendly *** + * + ****************************************************************************/ + +void up_mdelay(unsigned int milliseconds) +{ + up_udelay(USEC_PER_MSEC * milliseconds); +} + +/**************************************************************************** + * Name: up_udelay + * + * Description: + * Delay inline for the requested number of microseconds. + * + * *** NOT multi-tasking friendly *** + * + ****************************************************************************/ + +void up_udelay(useconds_t microseconds) +{ + if (g_timer.lower) + { + udelay_accurate(microseconds); + } + else /* period timer doesn't init yet */ + { + udelay_coarse(microseconds); + } +} diff --git a/include/nuttx/timers/arch_alarm.h b/include/nuttx/timers/arch_alarm.h new file mode 100644 index 0000000000..c66299b82e --- /dev/null +++ b/include/nuttx/timers/arch_alarm.h @@ -0,0 +1,73 @@ +/**************************************************************************** + * include/nuttx/timers/arch_alarm.h + * + * Copyright (C) 2017 Pinecone Inc. All rights reserved. + * Author: Xiang Xiao + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_TIMERS_ARCH_ALARM_H +#define __INCLUDE_NUTTX_TIMERS_ARCH_ALARM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +#ifdef CONFIG_ALARM_ARCH + +void up_alarm_set_lowerhalf(FAR struct oneshot_lowerhalf_s *lower); + +#else + +# define up_alarm_set_lowerhalf(lower) + +#endif + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_TIMERS_ARCH_ALARM_H */ diff --git a/include/nuttx/timers/arch_rtc.h b/include/nuttx/timers/arch_rtc.h new file mode 100644 index 0000000000..b996c35915 --- /dev/null +++ b/include/nuttx/timers/arch_rtc.h @@ -0,0 +1,73 @@ +/************************************************************************************ + * include/nuttx/timers/arch_rtc.h + * + * Copyright (C) 2017 Pinecone Inc. All rights reserved. + * Author: Xiang Xiao + * + * 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. + * + ************************************************************************************/ + +#ifndef __INCLUDE_NUTTX_TIMERS_ARCH_RTC_H +#define __INCLUDE_NUTTX_TIMERS_ARCH_RTC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +#ifdef CONFIG_RTC_ARCH + +void up_rtc_set_lowerhalf(FAR struct rtc_lowerhalf_s *lower); + +#else + +# define up_rtc_set_lowerhalf(lower) + +#endif /* CONFIG_RTC_ARCH */ + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_TIMERS_ARCH_RTC_H */ diff --git a/include/nuttx/timers/arch_timer.h b/include/nuttx/timers/arch_timer.h new file mode 100644 index 0000000000..a436c0c2bf --- /dev/null +++ b/include/nuttx/timers/arch_timer.h @@ -0,0 +1,73 @@ +/**************************************************************************** + * include/nuttx/timers/arch_timer.h + * + * Copyright (C) 2017 Pinecone Inc. All rights reserved. + * Author: Xiang Xiao + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_TIMERS_ARCH_TIMER_H +#define __INCLUDE_NUTTX_TIMERS_ARCH_TIMER_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +#ifdef CONFIG_TIMER_ARCH + +void up_timer_set_lowerhalf(struct timer_lowerhalf_s *lower); + +#else + +# define up_timer_set_lowerhalf(lower) + +#endif + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_TIMERS_ARCH_TIMER_H */ diff --git a/include/nuttx/timers/rtc.h b/include/nuttx/timers/rtc.h index 627ed904d5..300092f3f3 100644 --- a/include/nuttx/timers/rtc.h +++ b/include/nuttx/timers/rtc.h @@ -240,6 +240,9 @@ struct rtc_time int tm_yday; /* Day of the year (0-365) (unused) */ int tm_isdst; /* Non-0 if daylight savings time is in effect (unused) */ #endif +#if defined(CONFIG_RTC_HIRES) || defined(CONFIG_ARCH_HAVE_RTC_SUBSECONDS) + long tm_nsec; /* Nanosecond (0-999999999) */ +#endif }; #ifdef CONFIG_RTC_ALARM