arch/arm/src/max326xx: Work-in-progress RTC driver for the MAX32660

This commit is contained in:
Gregory Nutt 2018-11-21 12:27:23 -06:00
parent 357deb41f4
commit 49ed9cac86
7 changed files with 1579 additions and 7 deletions

View file

@ -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"

View file

@ -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

View file

@ -0,0 +1,762 @@
/****************************************************************************
* arch/arm/src/max326xx/max326_rtc_lowerhalf.c
*
* Copyright (C) 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 <sys/types.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <nuttx/arch.h>
#include <nuttx/timers/rtc.h>
#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 <nuttx/timers/rtc.h>
* #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 */

View file

@ -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 */

View file

@ -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

View file

@ -0,0 +1,637 @@
/****************************************************************************
* arch/arm/src/max326xx/max32660_rtc.c
*
* Copyright (C) 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 <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <fixedmath.h>
#include <errno.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include <nuttx/clock.h>
#include <nuttx/timers/rtc.h>
#include "up_arch.h"
#include "hardware/max326_rtc.h"
#include "max326_rtc.h"
#include <arch/board/board.h>
/****************************************************************************
* 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

View file

@ -0,0 +1,168 @@
/****************************************************************************
* arch/arm/src/max326xx/max326_rtc.h
*
* Copyright (C) 2018 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org> (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 <nuttx/config.h>
/****************************************************************************
* 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 <nuttx/timers/rtc.h>
* #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 */