From 49ed9cac8617f840a30bcfb31f75b3e37e7759cc Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 21 Nov 2018 12:27:23 -0600 Subject: [PATCH] arch/arm/src/max326xx: Work-in-progress RTC driver for the MAX32660 --- arch/arm/src/max326xx/Kconfig | 5 - arch/arm/src/max326xx/Make.defs | 10 + .../max326xx/common/max326_rtc_lowerhalf.c | 762 ++++++++++++++++++ arch/arm/src/max326xx/hardware/max32660_rtc.h | 2 +- arch/arm/src/max326xx/max32660/max32660_dma.c | 2 +- arch/arm/src/max326xx/max32660/max32660_rtc.c | 637 +++++++++++++++ arch/arm/src/max326xx/max326_rtc.h | 168 ++++ 7 files changed, 1579 insertions(+), 7 deletions(-) create mode 100644 arch/arm/src/max326xx/common/max326_rtc_lowerhalf.c create mode 100644 arch/arm/src/max326xx/max32660/max32660_rtc.c create mode 100644 arch/arm/src/max326xx/max326_rtc.h diff --git a/arch/arm/src/max326xx/Kconfig b/arch/arm/src/max326xx/Kconfig index d0d08c3ea12..21fba58e753 100644 --- a/arch/arm/src/max326xx/Kconfig +++ b/arch/arm/src/max326xx/Kconfig @@ -126,10 +126,6 @@ config MAX326XX_HAVE_WAKEUP bool default n -config MAX326XX_HAVE_RTC - bool - default n - config MAX326XX_HAVE_CRC bool default n @@ -248,7 +244,6 @@ config MAX326XX_WAKEUP config MAX326XX_RTC bool "RTC" default n - depends on MAX326XX_HAVE_RTC config MAX326XX_CRC bool "CRC 16/32" diff --git a/arch/arm/src/max326xx/Make.defs b/arch/arm/src/max326xx/Make.defs index 5f1d0059269..88681d0fe68 100644 --- a/arch/arm/src/max326xx/Make.defs +++ b/arch/arm/src/max326xx/Make.defs @@ -86,6 +86,10 @@ ifeq ($(CONFIG_MAX326XX_ICC),y) CHIP_CSRCS += max326_icc.c endif +ifeq ($(CONFIG_RTC_DRIVER),y) +CHIP_CSRCS += max326_rtc_lowerhalf.c +endif + # Source Files for the MAX32620 and MAX32630 # Source Files for the MAX32660 @@ -122,6 +126,12 @@ CHIP_CSRCS += max32660_gpioirq.c endif endif +ifeq ($(CONFIG_MAX326XX_RTC),y) +ifeq ($(CONFIG_ARCH_FAMILY_MAX32660),y) +CHIP_CSRCS += max32660_rtc.c +endif +endif + ifeq ($(CONFIG_MAX32XX_WDT),y) ifeq ($(CONFIG_ARCH_FAMILY_MAX32660),y) CHIP_CSRCS += max32660_rtc.c diff --git a/arch/arm/src/max326xx/common/max326_rtc_lowerhalf.c b/arch/arm/src/max326xx/common/max326_rtc_lowerhalf.c new file mode 100644 index 00000000000..348a129e721 --- /dev/null +++ b/arch/arm/src/max326xx/common/max326_rtc_lowerhalf.c @@ -0,0 +1,762 @@ +/**************************************************************************** + * arch/arm/src/max326xx/max326_rtc_lowerhalf.c + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include +#include + +#include "up_arch.h" + +#include "chip.h" +#include "max326_rtc.h" + +#ifdef CONFIG_RTC_DRIVER + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define RTC_NALARMS 1 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +struct max326_cbinfo_s +{ + volatile rtc_alarm_callback_t cb; /* Callback when the alarm expires */ + volatile FAR void *priv; /* Private argument to accompany callback */ +}; +#endif + +/* This is the private type for the RTC state. It must be cast compatible + * with struct rtc_lowerhalf_s. + */ + +struct max326_lowerhalf_s +{ + /* This is the contained reference to the read-only, lower-half + * operations vtable (which may lie in FLASH or ROM) + */ + + FAR const struct rtc_ops_s *ops; + + /* Data following is private to this driver and not visible outside of + * this file. + */ + + sem_t devsem; /* Threads can only exclusively access the RTC */ + +#ifdef CONFIG_RTC_ALARM + /* Alarm callback information */ + + struct max326_cbinfo_s cbinfo[RTC_NALARMS]; +#endif + +#ifdef CONFIG_RTC_PERIODIC + /* Periodic wakeup information */ + + struct lower_setperiodic_s periodic; +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ +/* Prototypes for static methods in struct rtc_ops_s */ + +static int max326_rdtime(FAR struct rtc_lowerhalf_s *lower, + FAR struct rtc_time *rtctime); +static int max326_settime(FAR struct rtc_lowerhalf_s *lower, + FAR const struct rtc_time *rtctime); +static bool max326_havesettime(FAR struct rtc_lowerhalf_s *lower); + +#ifdef CONFIG_RTC_ALARM +static int max326_setalarm(FAR struct rtc_lowerhalf_s *lower, + FAR const struct lower_setalarm_s *alarminfo); +static int max326_setrelative(FAR struct rtc_lowerhalf_s *lower, + FAR const struct lower_setrelative_s *alarminfo); +static int max326_cancelalarm(FAR struct rtc_lowerhalf_s *lower, + int alarmid); +static int max326_rdalarm(FAR struct rtc_lowerhalf_s *lower, + FAR struct lower_rdalarm_s *alarminfo); +#endif + +#ifdef CONFIG_RTC_PERIODIC +static int max326_setperiodic(FAR struct rtc_lowerhalf_s *lower, + FAR const struct lower_setperiodic_s *alarminfo); +static int max326_cancelperiodic(FAR struct rtc_lowerhalf_s *lower, int id); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* RTC driver operations */ + +static const struct rtc_ops_s g_rtc_ops = +{ + .rdtime = max326_rdtime, + .settime = max326_settime, + .havesettime = max326_havesettime, +#ifdef CONFIG_RTC_ALARM + .setalarm = max326_setalarm, + .setrelative = max326_setrelative, + .cancelalarm = max326_cancelalarm, + .rdalarm = max326_rdalarm, +#endif +#ifdef CONFIG_RTC_PERIODIC + .setperiodic = max326_setperiodic, + .cancelperiodic = max326_cancelperiodic, +#endif +#ifdef CONFIG_RTC_IOCTL + .ioctl = NULL, +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + .destroy = NULL, +#endif +}; + +/* RTC device state */ + +static struct max326_lowerhalf_s g_rtc_lowerhalf = +{ + .ops = &g_rtc_ops, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: max326_alarm_callback + * + * Description: + * This is the function that is called from the RTC driver when the alarm + * goes off. It just invokes the upper half drivers callback. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +static void max326_alarm_callback(FAR void *arg, unsigned int alarmid) +{ + FAR struct max326_lowerhalf_s *lower; + FAR struct max326_cbinfo_s *cbinfo; + rtc_alarm_callback_t cb; + FAR void *priv; + + DEBUGASSERT(alarmid < RTC_NALARMS); + + lower = (struct max326_lowerhalf_s *)arg; + cbinfo = &lower->cbinfo[alarmid]; + + /* Sample and clear the callback information to minimize the window in + * time in which race conditions can occur. + */ + + cb = (rtc_alarm_callback_t)cbinfo->cb; + priv = (FAR void *)cbinfo->priv; + DEBUGASSERT(priv != NULL); + + cbinfo->cb = NULL; + cbinfo->priv = NULL; + + /* Perform the callback */ + + if (cb != NULL) + { + cb(priv, alarmid); + } +} +#endif /* CONFIG_RTC_ALARM */ + +/**************************************************************************** + * Name: max326_rdtime + * + * Description: + * Implements the rdtime() method of the RTC driver interface + * + * 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. + * + ****************************************************************************/ + +static int max326_rdtime(FAR struct rtc_lowerhalf_s *lower, + FAR struct rtc_time *rtctime) +{ +#if defined(CONFIG_RTC_DATETIME) + /* This operation depends on the fact that struct rtc_time is cast + * compatible with struct tm. + */ + + return up_rtc_getdatetime((FAR struct tm *)rtctime); + +#elif defined(CONFIG_RTC_HIRES) + FAR struct timespec ts; + int ret; + + /* Get the higher resolution time */ + + ret = up_rtc_gettime(&ts); + if (ret < 0) + { + goto errout_with_errno; + } + + /* Convert the one second epoch time to a struct tm. This operation + * depends on the fact that struct rtc_time and struct tm are cast + * compatible. + */ + + if (!gmtime_r(&ts.tv_sec, (FAR struct tm *)rtctime)) + { + goto errout_with_errno; + } + + return OK; + +errout_with_errno: + ret = get_errno(); + DEBUGASSERT(ret > 0); + return -ret; + +#else + 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, (FAR struct tm *)rtctime)) + { + int errcode = get_errno(); + DEBUGASSERT(errcode > 0); + return -errcode; + } + + return OK; +#endif +} + +/**************************************************************************** + * Name: max326_settime + * + * Description: + * Implements 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. + * + ****************************************************************************/ + +static int max326_settime(FAR struct rtc_lowerhalf_s *lower, + FAR const struct rtc_time *rtctime) +{ +#ifdef CONFIG_RTC_DATETIME + /* This operation depends on the fact that struct rtc_time is cast + * compatible with struct tm. + */ + + return max326_rtc_setdatetime((FAR const struct tm *)rtctime); + +#else + 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((FAR struct tm *)rtctime); + ts.tv_nsec = 0; + + /* Now set the time (to one second accuracy) */ + + return up_rtc_settime(&ts); +#endif +} + +/**************************************************************************** + * Name: max326_havesettime + * + * Description: + * Implements the havesettime() method of the RTC driver interface + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure + * + * Returned Value: + * Returns true if RTC date-time have been previously set. + * + ****************************************************************************/ + +static bool max326_havesettime(FAR struct rtc_lowerhalf_s *lower) +{ + return true; +} + +/**************************************************************************** + * Name: max326_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. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +static int max326_setalarm(FAR struct rtc_lowerhalf_s *lower, + FAR const struct lower_setalarm_s *alarminfo) +{ + FAR struct max326_lowerhalf_s *priv; + FAR struct max326_cbinfo_s *cbinfo; + int ret; + + DEBUGASSERT(lower != NULL && alarminfo != NULL && alarminfo->id == 0); + priv = (FAR struct max326_lowerhalf_s *)lower; + + ret = nxsem_wait(&priv->devsem); + if (ret < 0) + { + return ret; + } + + ret = -EINVAL; + if (alarminfo->id == 0) + { + struct timespec ts; + + /* Convert the RTC time to a timespec (1 second accuracy) */ + + ts.tv_sec = mktime((FAR struct tm *)&alarminfo->time); + ts.tv_nsec = 0; + + /* Remember the callback information */ + + cbinfo = &priv->cbinfo[0]; + cbinfo->cb = alarminfo->cb; + cbinfo->priv = alarminfo->priv; + + /* And set the alarm */ + + ret = max326_rtc_setalarm(&ts, max326_alarm_callback, lower); + if (ret < 0) + { + cbinfo->cb = NULL; + cbinfo->priv = NULL; + } + } + + nxsem_post(&priv->devsem); + + return ret; +} +#endif + +/**************************************************************************** + * Name: max326_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. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +static int max326_setrelative(FAR struct rtc_lowerhalf_s *lower, + FAR const struct lower_setrelative_s *alarminfo) +{ + FAR struct max326_lowerhalf_s *priv; + FAR struct max326_cbinfo_s *cbinfo; +#if defined(CONFIG_RTC_DATETIME) + struct tm time; +#endif + FAR struct timespec ts; + int ret = -EINVAL; + + DEBUGASSERT(lower != NULL && alarminfo != NULL && alarminfo->id == 0); + priv = (FAR struct max326_lowerhalf_s *)lower; + + if (alarminfo->id == 0 && alarminfo->reltime > 0) + { + /* Disable pre-emption while we do this so that we don't have to worry + * about being suspended and working on an old time. + */ + + sched_lock(); + + /* Get the current time in seconds */ + +#if defined(CONFIG_RTC_DATETIME) + /* Get the broken out time and convert to seconds */ + + ret = up_rtc_getdatetime(&time); + if (ret < 0) + { + sched_unlock(); + return ret; + } + + ts.tv_sec = mktime(&time); + ts.tv_nsec = 0; + +#elif defined(CONFIG_RTC_HIRES) + /* Get the higher resolution time */ + + ret = up_rtc_gettime(&ts); + if (ret < 0) + { + sched_unlock(); + return ret; + } +#else + /* The resolution of time is only 1 second */ + + ts.tv_sec = up_rtc_time(); + ts.tv_nsec = 0; +#endif + + /* Add the seconds offset. Add one to the number of seconds because + * we are unsure of the phase of the timer. + */ + + ts.tv_sec += (alarminfo->reltime + 1); + + /* Remember the callback information */ + + cbinfo = &priv->cbinfo[0]; + cbinfo->cb = alarminfo->cb; + cbinfo->priv = alarminfo->priv; + + /* And set the alarm */ + + ret = max326_rtc_setalarm(&ts, max326_alarm_callback, lower); + if (ret < 0) + { + cbinfo->cb = NULL; + cbinfo->priv = NULL; + } + + sched_unlock(); + } + + return ret; +} +#endif + +/**************************************************************************** + * Name: max326_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 + * 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. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +static int max326_cancelalarm(FAR struct rtc_lowerhalf_s *lower, int alarmid) +{ + FAR struct max326_lowerhalf_s *priv; + FAR struct max326_cbinfo_s *cbinfo; + + DEBUGASSERT(lower != NULL); + DEBUGASSERT(alarmid == 0); + priv = (FAR struct max326_lowerhalf_s *)lower; + + /* Nullify callback information to reduce window for race conditions */ + + cbinfo = &priv->cbinfo[0]; + cbinfo->cb = NULL; + cbinfo->priv = NULL; + + /* Then cancel the alarm */ + + return max326_rtc_cancelalarm(); +} +#endif + +/**************************************************************************** + * Name: max326_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. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +static int max326_rdalarm(FAR struct rtc_lowerhalf_s *lower, + FAR struct lower_rdalarm_s *alarminfo) +{ + struct alm_rdalarm_s lowerinfo; + int ret = -EINVAL; + + DEBUGASSERT(lower != NULL && alarminfo != NULL && alarminfo->time != NULL); + DEBUGASSERT(alarminfo->id < RTC_NALARMS); + + if (alarminfo->id < RTC_NALARMS) + { + /* Disable pre-emption while we do this so that we don't have to worry + * about being suspended and working on an old time. + */ + + sched_lock(); + lowerinfo.ar_time = alarminfo->time; + ret = max326_rtc_rdalarm(&lowerinfo); + sched_unlock(); + } + + return ret; +} +#endif + +/**************************************************************************** + * Name: max326_periodic_callback + * + * Description: + * This is the function that is called from the RTC driver when the periodic + * wakeup goes off. It just invokes the upper half drivers callback. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +static int max326_periodic_callback(void) +{ + FAR struct max326_lowerhalf_s *lower; + struct lower_setperiodic_s *cbinfo; + rtc_wakeup_callback_t cb; + FAR void *priv; + + lower = (FAR struct max326_lowerhalf_s *)&g_rtc_lowerhalf; + + cbinfo = &lower->periodic; + cb = (rtc_wakeup_callback_t)cbinfo->cb; + priv = (FAR void *)cbinfo->priv; + + /* Perform the callback */ + + if (cb != NULL) + { + cb(priv, 0); + } + + return OK; +} +#endif /* CONFIG_RTC_PERIODIC */ + +/**************************************************************************** + * Name: max326_setperiodic + * + * Description: + * Set a new periodic wakeup relative to the current time, with a given + * period. This function implements the setperiodic() 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 wakeup activity + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +static int max326_setperiodic(FAR struct rtc_lowerhalf_s *lower, + FAR const struct lower_setperiodic_s *alarminfo) +{ + FAR struct max326_lowerhalf_s *priv; + int ret; + + DEBUGASSERT(lower != NULL && alarminfo != NULL); + priv = (FAR struct max326_lowerhalf_s *)lower; + + ret = nxsem_wait(&priv->devsem); + if (ret < 0) + { + return ret; + } + + memcpy(&priv->periodic, alarminfo, sizeof(struct lower_setperiodic_s)); + + ret = max326_rtc_setperiodic(&alarminfo->period, max326_periodic_callback); + + nxsem_post(&priv->devsem); + + return ret; +} +#endif + +/**************************************************************************** + * Name: max326_cancelperiodic + * + * Description: + * Cancel the current periodic wakeup activity. This function implements + * the cancelperiodic() method of the RTC driver interface + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +static int max326_cancelperiodic(FAR struct rtc_lowerhalf_s *lower, int id) +{ + FAR struct max326_lowerhalf_s *priv; + int ret; + + DEBUGASSERT(lower != NULL); + priv = (FAR struct max326_lowerhalf_s *)lower; + + DEBUGASSERT(id == 0); + + ret = nxsem_wait(&priv->devsem); + if (ret < 0) + { + return ret; + } + + ret = max326_rtc_cancelperiodic(); + + nxsem_post(&priv->devsem); + + return ret; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: max326_rtc_lowerhalf + * + * Description: + * Instantiate the RTC lower half driver for the MAX326XX. General usage: + * + * #include + * #include "max326_rtc.h" + * + * struct rtc_lowerhalf_s *lower; + * lower = max326_rtc_lowerhalf(); + * rtc_initialize(0, lower); + * + * Input Parameters: + * None + * + * Returned Value: + * On success, a non-NULL RTC lower interface is returned. NULL is + * returned on any failure. + * + ****************************************************************************/ + +FAR struct rtc_lowerhalf_s *max326_rtc_lowerhalf(void) +{ + nxsem_init(&g_rtc_lowerhalf.devsem, 0, 1); + + return (FAR struct rtc_lowerhalf_s *)&g_rtc_lowerhalf; +} + +#endif /* CONFIG_RTC_DRIVER */ diff --git a/arch/arm/src/max326xx/hardware/max32660_rtc.h b/arch/arm/src/max326xx/hardware/max32660_rtc.h index b6fa75380b6..3f26a2543ea 100644 --- a/arch/arm/src/max326xx/hardware/max32660_rtc.h +++ b/arch/arm/src/max326xx/hardware/max32660_rtc.h @@ -69,7 +69,7 @@ /* Register Bit-field Definitions ***************************************************/ -/* Seconds Counter Register (32-bit seconds count) */ +/* Seconds Counter Register (32-bit seconds counter) */ /* Sub-Seconds Counter Register */ #define RTC_SSEC_MASK (0xff) /* Bits 0-7: Sub-second counter */ diff --git a/arch/arm/src/max326xx/max32660/max32660_dma.c b/arch/arm/src/max326xx/max32660/max32660_dma.c index 95c1c82890c..86824ed2f60 100644 --- a/arch/arm/src/max326xx/max32660/max32660_dma.c +++ b/arch/arm/src/max326xx/max32660/max32660_dma.c @@ -221,7 +221,7 @@ static int max326_dmach_interrupt(int irq, FAR void *context, FAR void *arg) ****************************************************************************/ /**************************************************************************** - * Name: stm32_dmainitialize + * Name: up_dma_initialize * * Description: * Initialize the DMA subsystem diff --git a/arch/arm/src/max326xx/max32660/max32660_rtc.c b/arch/arm/src/max326xx/max32660/max32660_rtc.c new file mode 100644 index 00000000000..0048c0a26e4 --- /dev/null +++ b/arch/arm/src/max326xx/max32660/max32660_rtc.c @@ -0,0 +1,637 @@ +/**************************************************************************** + * arch/arm/src/max326xx/max32660_rtc.c + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 +#include +#include + +#include +#include +#include +#include + +#include "up_arch.h" + +#include "hardware/max326_rtc.h" +#include "max326_rtc.h" + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* In hi-res mode, the RTC operates at 256Hz. In the lo-res mode, the RTC + * operates at 1Hz. + */ + +#ifdef CONFIG_RTC_HIRES +# ifndef CONFIG_RTC_FREQUENCY +# error "CONFIG_RTC_FREQUENCY is required for CONFIG_RTC_HIRES" +# elif CONFIG_RTC_FREQUENCY != 256 +# error "Only hi-res CONFIG_RTC_FREQUENCY of 256Hz is supported" +# endif +#else +# ifndef CONFIG_RTC_FREQUENCY +# define CONFIG_RTC_FREQUENCY 1 +# endif +# if CONFIG_RTC_FREQUENCY != 1 +# error "Only lo-res CONFIG_RTC_FREQUENCY of 1Hz is supported" +# endif +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +/* Callback to use when the alarm expires */ + +static alm_callback_t g_alarmcb; +static FAR void *g_alarmarg; +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Indicates that the RTC has be initialized */ + +volatile bool g_rtc_enabled = false; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: max326_rtc_waitbusy + * + * Description: + * Wait for registers to synchronize with the RTC module. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void max326_rtc_waitbusy(void) +{ + while ((getreg32(MAX326_RTC_CTRL) & RTC_CTRL_BUSY) != 0) + { + } +} + +/**************************************************************************** + * Name: max326_rtc_enable + * + * Description: + * Enable/disable the RTC. RTC must be disabled in order to set the SEC + * and SSEC registers. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void max326_rtc_enable(bool enable) +{ + uint32_t regval; + + regval = getreg32(MAX326_RTC_CTRL); + if (enable) + { + regval |= RTC_CTRL_ENABLE; + } + else + { + regval &= ~RTC_CTRL_ENABLE; + } + + putreg32(regval, MAX326_RTC_CTRL); + max326_rtc_waitbusy(); +} + +/**************************************************************************** + * Name: max326_rtc_wrenable + * + * Description: + * Enable/disable writing to the TRIM register or to the CTRL enable bit. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void max326_rtc_wrenable(bool enable) +{ + uint32_t regval; + + regval = getreg32(MAX326_RTC_CTRL); + if (enable) + { + regval |= RTC_CTRL_WRITEEN; + } + else + { + regval &= !RTC_CTRL_WRITEEN; + } + + putreg32(regval, MAX326_RTC_CTRL); +} + +/**************************************************************************** + * Name: max326_rtc_tm2b32 + * + * Description: + * Encode time in the standard struct timespec format to type b32_t. + * + * Input Parameters: + * tp - the time to use + * + * Returned Value: + * Encoded time + * + ****************************************************************************/ + +static b32_t max326_rtc_tm2b32(FAR const struct timespec *tp) +{ + b32_t intpart; + b32_t fracpart; + + DEBUGASSERT(tp != NULL && tp->tv_nsec < NSEC_PER_SEC); + + /* Convert the time to b32_t type: + * + * Integer part is easy: + * + * intpart: ssssssss ssssssss 00000000 00000000 + * 00000000 00000001 00000000 00000000 = 1 Sec + */ + + intpart = itob32(tp->tv_sec); + + /* Fractional part: + * + * fracpart: 00000000 00000000 ffffffff ffffffff + * 00000000 00000001 00000000 00000000 = 1 Sec + */ + + if (tp->tv_nsec > 0) + { + fracpart = itob32(tp->tv_nsec) / NSEC_PER_SEC; + } + else + { + fracpart = 0; + } + + return intpart + fracpart; +} + +/**************************************************************************** + * Name: max326_rtc_interrupt + * + * Description: + * RTC alarm interrupt service routine + * + * Input Parameters: + * irq - The IRQ number that generated the interrupt + * context - Architecture specific register save information. + * arg - Argument that my accompany the interrupt event + * + * Returned Value: + * Zero (OK) on success; A negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +static int max326_rtc_interrupt(int irq, void *context, FAR void *arg) +{ + uint32_t regval; + + /* Get the CTRL register */ + + regval = getreg32(MAX326_RTC_CTRL); + DEBUGASSERT((regval & RTC_CTRL_ALARM_SSFL) != 0); + + /* Clear pending interrupt flags. Disable alarms */ + + regval &= ~(RTC_CTRL_ALARM_TODEN | RTC_CTRL_ALARM_SSEN | + RTC_CTRL_ALARM_TODFL | RTC_CTRL_ALARM_SSFL); + putreg32(regval, MAX326_RTC_CTRL); + + /* Disable further RTC interrupts */ + + up_disable_irq(MAX326_IRQ_RTC); + + DEBUGASSERT(g_alarmcb != NULL); + if (g_alarmcb != NULL) + { + /* Perform the alarm callback */ + + g_alarmcb(g_alarmarg, 0); + g_alarmcb = NULL; + g_alarmarg = NULL; + } + + return OK; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_rtc_initialize + * + * Description: + * Initialize the hardware RTC per the selected configuration. This + * function is called once during the OS initialization sequence + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +int up_rtc_initialize(void) +{ + /* Configure initial the RTC CTRL register + * + * - RTC disabled + * - All interrupts disabled. + * - No Square Wave output. + * - Oscillator operates in noise immunity mode + * - Write disabled. + */ + + putreg32(0, MAX326_RTC_CTRL); + max326_rtc_waitbusy(); + + /* Enable write access */ + + max326_rtc_wrenable(false); + + /* No RTC Square Wave Output */ + + putreg32(0, MAX326_RTC_OSCCTRL); + max326_rtc_waitbusy(); + +#ifdef CONFIG_RTC_ALARM + /* Attach the ALARM interrupt handler */ + + up_disable_irq(MAX326_IRQ_RTC); + (void)irq_attach(MAX326_IRQ_RTC, max326_rtc_interrupt, NULL); +#endif + + /* Enable the RTC */ + + max326_rtc_enable(true); + + /* Disable write access */ + + max326_rtc_wrenable(false); + + g_rtc_enabled = true; + return OK; +} + +/**************************************************************************** + * Name: up_rtc_time + * + * Description: + * Get the current time in seconds. This is similar to the + * standard time() function. It is only used by the RTOS during + * [re-]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) +{ + uint32_t sec; + uint32_t ssec; + uint32_t verify; + + /* Read the time handling rollover to full seconds */ + + do + { + sec = getreg32(MAX326_RTC_SEC); + ssec = getreg32(MAX326_RTC_SSEC) &RTC_SSEC_MASK; + verify = getreg32(MAX326_RTC_SEC); + } + while (verify != sec); + + /* Round */ + + if (ssec > 128) + { + sec++; + } + + return sec; +} +#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 on failure + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_HIRES +int up_rtc_gettime(FAR struct timespec *tp) +{ + uint64_t tmp; + uint32_t sec; + uint32_t ssec; + uint32_t verify; + + /* Read the time handling rollover to full seconds */ + + do + { + sec = getreg32(MAX326_RTC_SEC); + ssec = getreg32(MAX326_RTC_SSEC) & RTC_SSEC_MASK; + verify = getreg32(MAX326_RTC_SEC); + } + while (verify != sec); + + /* Format as a tm */ + + tmp = ((uint64_t)ssec * NSEC_PER_SEC) / 256; + + tp->tv_sec = sec; + tp->tv_nsec = (long)tmp; + + return OK; +} +#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 tm. + * + * Input Parameters: + * tp - the time to use + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +int up_rtc_settime(FAR const struct timespec *tp) +{ + irqstate_t flags; + b32_t ftime; + uint32_t sec; + uint32_t ssec; + uint32_t verify; + + /* Get the time as a fixed precision number. */ + + ftime = max326_rtc_tm2b32(tp); + sec = b32toi(ftime); + ssec = (uint32_t)(b32frac(ftime) >> (32 - 8)); + + /* Enable write access to RTC configuration registers */ + + flags = spin_lock_irqsave(); + max326_rtc_wrenable(true); + + /* We need to disable the RTC in order to write to the SEC and SSEC + * registers. + */ + + max326_rtc_enable(false); + + /* Then write the time values to the SEC and SSEC registers. */ + + do + { + putreg32(sec, MAX326_RTC_SEC); + putreg32(ssec, MAX326_RTC_SSEC); + verify = getreg32(MAX326_RTC_SEC); + } + while (verify != sec); + + /* Re-enable the RTC and disable write access */ + + max326_rtc_enable(true); + max326_rtc_wrenable(false); + + spin_unlock_irqrestore(flags); + return OK; +} + +/**************************************************************************** + * Name: max326_rtc_setalarm + * + * Description: + * Set an alarm to an absolute time using associated hardware. + * + * Input Parameters: + * ts - Alarm time + * cb - Callback invoked when alarm expires + * arg - Argument passed with the alarm + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +int max326_rtc_setalarm(FAR struct timespec *ts, alm_callback_t cb, FAR void *arg) +{ + irqstate_t flags; + b32_t ftime; + uint32_t regval; + uint32_t sec; + uint32_t ssec; + uint32_t verify; + int ret = -EBUSY; + + DEBUGASSERT(alminfo != NULL && alminfo->as_cb != NULL); + + flags = spin_lock_irqsave(); + + /* Is there already something waiting on the ALARM? */ + + if (g_alarmcb == NULL) + { + /* No.. Save the callback function pointer */ + + g_alarmcb = cb; + g_alarmarg = arg; + + /* Get the time as a fixed precision number. */ + + ftime = max326_rtc_tm2b32(ts); + sec = b32toi(ftime); + ssec = (uint32_t)(b32frac(ftime) >> (32 - 8)); + + /* We need to disable ALARMs in order to write to the RAS and RSSA + * registers. + */ + + flags = spin_lock_irqsave(); + + regval = getreg32(MAX326_RTC_CTRL); + regval &= ~(RTC_CTRL_ALARM_TODEN | RTC_CTRL_ALARM_SSEN); + putreg32(regval, MAX326_RTC_CTRL); + max326_rtc_waitbusy(); + + /* Then write the time values to the RAS and RSSA registers. */ + + do + { + putreg32(sec, MAX326_RTC_RAS); + putreg32(ssec, MAX326_RTC_RSSA); + verify = getreg32(MAX326_RTC_RAS); + } + while (verify != sec); + + /* Enable sub-second alarm */ + + regval |= RTC_CTRL_ALARM_SSEN; + putreg32(regval, MAX326_RTC_CTRL); + + /* Enable the RTC interrupt at the NVIC */ + + up_enable_irq(MAX326_IRQ_RTC); + spin_unlock_irqrestore(flags); + + ret = OK; + } + + return ret; +} +#endif + +/**************************************************************************** + * Name: max326_rtc_cancelalarm + * + * Description: + * Cancel a pending alarm alarm + * + * Input Parameters: + * none + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +int max326_rtc_cancelalarm(void) +{ + irqstate_t flags; + uint32_t regval; + int ret = -ENODATA; + + flags = spin_lock_irqsave(); + + if (g_alarmcb != NULL) + { + /* Disable the alarm interrupt */ + + up_disable_irq(MAX326_IRQ_RTC); + + /* Disable the alarm */ + + regval = getreg32(MAX326_RTC_CTRL); + regval &= ~(RTC_CTRL_ALARM_TODEN | RTC_CTRL_ALARM_SSEN); + putreg32(regval, MAX326_RTC_CTRL); + max326_rtc_waitbusy(); + + /* Cancel the global callback function */ + + g_alarmcb = NULL; + g_alarmarg = NULL; + + /* Unset the alarm (for debug) */ + + putreg32(0, MAX326_RTC_RAS); + putreg32(0, MAX326_RTC_RSSA); + + ret = OK; + } + + spin_unlock_irqrestore(flags); + return ret; +} +#endif diff --git a/arch/arm/src/max326xx/max326_rtc.h b/arch/arm/src/max326xx/max326_rtc.h new file mode 100644 index 00000000000..4a650d06de3 --- /dev/null +++ b/arch/arm/src/max326xx/max326_rtc.h @@ -0,0 +1,168 @@ +/**************************************************************************** + * arch/arm/src/max326xx/max326_rtc.h + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt (On-going support and development) + * + * 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 __ARCH_ARM_SRC_MAX326XX_MAX326_RTC_H +#define __ARCH_ARM_SRC_MAX326XX_MAX326_RTC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +/* Type of callback invoked when an ALARM interrupt occurs */ + +typedef CODE void (*alm_callback_t)(FAR void *arg, unsigned int alarmid); + +/* Structure used to pass parameters to query an alarm */ + +struct alm_rdalarm_s +{ + FAR struct rtc_time *ar_time; /* Argument for storing ALARM RTC time */ +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: max326_rtc_setalarm + * + * Description: + * Set an alarm to an absolute time using associated hardware. + * + * Input Parameters: + * ts - Alarm time + * cb - Callback invoked when alarm expires + * arg - Argument passed with the alarm + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +int max326_rtc_setalarm(FAR struct timespec *ts, alm_callback_t cb, + FAR void *arg); +#endif + +/**************************************************************************** + * Name: max326_rtc_rdalarm + * + * Description: + * Query an alarm configured in hardware. + * + * Input Parameters: + * alminfo - Information about the alarm configuration. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +int max326_rtc_rdalarm(FAR struct alm_rdalarm_s *alminfo); +#endif + +/**************************************************************************** + * Name: max326_rtc_cancelalarm + * + * Description: + * Cancel a pending alarm alarm + * + * Input Parameters: + * none + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +int max326_rtc_cancelalarm(void); +#endif + +/**************************************************************************** + * Name: max326_rtc_lowerhalf + * + * Description: + * Instantiate the RTC lower half driver for the MAX326XX. General usage: + * + * #include + * #include "max326_rtc.h" + * + * struct rtc_lowerhalf_s *lower; + * lower = max326_rtc_lowerhalf(); + * rtc_initialize(0, lower); + * + * Input Parameters: + * None + * + * Returned Value: + * On success, a non-NULL RTC lower interface is returned. NULL is + * returned on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER +struct rtc_lowerhalf_s; +FAR struct rtc_lowerhalf_s *max326_rtc_lowerhalf(void); +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_MAX326XX_MAX326_RTC_H */