arch/arm/src/armv7-m: Implement SYSTICK timer driver
This commit is contained in:
parent
2c93467436
commit
22a44465e4
3 changed files with 428 additions and 0 deletions
|
|
@ -255,3 +255,9 @@ config ARMV7M_ITMSYSLOG_SWODIV
|
|||
range 1 8192
|
||||
|
||||
endif # ARMV7M_ITMSYSLOG
|
||||
|
||||
config ARMV7M_SYSTICK
|
||||
bool "SysTick timer driver"
|
||||
depends on TIMER
|
||||
---help---
|
||||
Enable SysTick timer driver.
|
||||
|
|
|
|||
91
arch/arm/src/armv7-m/systick.h
Normal file
91
arch/arm/src/armv7-m/systick.h
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/****************************************************************************
|
||||
* arch/arm/src/armv7-m/systick.h
|
||||
*
|
||||
* Copyright (C) 2018 Pinecone Inc. All rights reserved.
|
||||
* Author: Xiang Xiao <xiaoxiang@pinecone.net>
|
||||
*
|
||||
* 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_ARMV7_M_SYSTICK_H
|
||||
#define __ARCH_ARM_SRC_ARMV7_M_SYSTICK_H
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
#include <nuttx/timers/timer.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define EXTERN extern "C"
|
||||
extern "C"
|
||||
{
|
||||
#else
|
||||
#define EXTERN extern
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: systick_initialize
|
||||
*
|
||||
* Description:
|
||||
* This function initialize SYSTICK hardware module which is part of NVIC
|
||||
* and return an instance of a "lower half" timer interface.
|
||||
*
|
||||
* Input parameters:
|
||||
* coreclk - false if SYSTICK working clock come from the external
|
||||
* reference clock, otherwise true.
|
||||
* freq - The clock frequency in Hz. If freq is zero, calculate the value
|
||||
* from NVIC_SYSTICK_CALIB register.
|
||||
* minor - The timer driver minor number, skip the registration if minor
|
||||
* < 0.
|
||||
*
|
||||
* Returned Value:
|
||||
* On success, a non-NULL timer_lowerhalf_s is returned to the caller.
|
||||
* In the event of any failure, a NULL value is returned.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_ARMV7M_SYSTICK
|
||||
struct timer_lowerhalf_s *systick_initialize(bool coreclk, unsigned int freq,
|
||||
int minor);
|
||||
#else
|
||||
# define systick_initialize(coreclk, freq, minor) NULL
|
||||
#endif /* CONFIG_ARMV7M_SYSTICK */
|
||||
|
||||
#undef EXTERN
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ARCH_ARM_SRC_ARMV7_M_SYSTICK_H */
|
||||
331
arch/arm/src/armv7-m/up_systick.c
Normal file
331
arch/arm/src/armv7-m/up_systick.c
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
/****************************************************************************
|
||||
* arch/arm/src/armv7-m/up_systick.c
|
||||
*
|
||||
* Copyright (C) 2018 Pinecone Inc. All rights reserved.
|
||||
* Author: Xiang Xiao <xiaoxiang@pinecone.net>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name NuttX nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/irq.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nvic.h"
|
||||
#include "systick.h"
|
||||
#include "up_arch.h"
|
||||
|
||||
#ifdef CONFIG_ARMV7M_SYSTICK
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define NVIC_IRQ_SYSTICK 15 /* Vector 15: System tick */
|
||||
|
||||
/****************************************************************************
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
/* This structure provides the private representation of the "lower-half"
|
||||
* driver state structure. This structure must be cast-compatible with the
|
||||
* timer_lowerhalf_s structure.
|
||||
*/
|
||||
|
||||
struct systick_lowerhalf_s
|
||||
{
|
||||
const struct timer_ops_s *ops; /* Lower half operations */
|
||||
uint32_t freq; /* Timer working clock frequency(Hz) */
|
||||
tccb_t callback; /* Current user interrupt callback */
|
||||
void *arg; /* Argument passed to upper half callback */
|
||||
uint32_t *next_interval;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
static int systick_start(FAR struct timer_lowerhalf_s *lower_);
|
||||
static int systick_stop(FAR struct timer_lowerhalf_s *lower_);
|
||||
static int systick_getstatus(FAR struct timer_lowerhalf_s *lower_,
|
||||
FAR struct timer_status_s *status);
|
||||
static int systick_settimeout(FAR struct timer_lowerhalf_s *lower_,
|
||||
uint32_t timeout);
|
||||
static void systick_setcallback(FAR struct timer_lowerhalf_s *lower_,
|
||||
tccb_t callback, FAR void *arg);
|
||||
static int systick_maxtimeout(FAR struct timer_lowerhalf_s *lower_,
|
||||
uint32_t *maxtimeout);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static const struct timer_ops_s g_systick_ops =
|
||||
{
|
||||
.start = systick_start,
|
||||
.stop = systick_stop,
|
||||
.getstatus = systick_getstatus,
|
||||
.settimeout = systick_settimeout,
|
||||
.setcallback = systick_setcallback,
|
||||
.maxtimeout = systick_maxtimeout,
|
||||
};
|
||||
|
||||
static struct systick_lowerhalf_s g_systick_lower =
|
||||
{
|
||||
.ops = &g_systick_ops,
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
static inline uint64_t usec_from_count(uint32_t count, uint32_t freq)
|
||||
{
|
||||
return (uint64_t)count * USEC_PER_SEC / freq;
|
||||
}
|
||||
|
||||
static inline uint64_t usec_to_count(uint32_t usec, uint32_t freq)
|
||||
{
|
||||
return (uint64_t)usec * freq / USEC_PER_SEC;
|
||||
}
|
||||
|
||||
static bool systick_is_running(void)
|
||||
{
|
||||
return !!(getreg32(NVIC_SYSTICK_CTRL) & NVIC_SYSTICK_CTRL_ENABLE);
|
||||
}
|
||||
|
||||
static bool systick_irq_pending(struct systick_lowerhalf_s *lower)
|
||||
{
|
||||
if (lower->next_interval)
|
||||
{
|
||||
return false; /* Interrupt is in process */
|
||||
}
|
||||
else
|
||||
{
|
||||
return !!(getreg32(NVIC_INTCTRL) & NVIC_INTCTRL_PENDSTSET);
|
||||
}
|
||||
}
|
||||
|
||||
static int systick_start(FAR struct timer_lowerhalf_s *lower_)
|
||||
{
|
||||
putreg32(0, NVIC_SYSTICK_CURRENT);
|
||||
modifyreg32(NVIC_SYSTICK_CTRL, 0, NVIC_SYSTICK_CTRL_ENABLE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int systick_stop(FAR struct timer_lowerhalf_s *lower_)
|
||||
{
|
||||
modifyreg32(NVIC_SYSTICK_CTRL, NVIC_SYSTICK_CTRL_ENABLE, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int systick_getstatus(FAR struct timer_lowerhalf_s *lower_,
|
||||
FAR struct timer_status_s *status)
|
||||
{
|
||||
struct systick_lowerhalf_s *lower = (struct systick_lowerhalf_s *)lower_;
|
||||
irqstate_t flags = enter_critical_section();
|
||||
|
||||
status->flags = lower->callback ? TCFLAGS_HANDLER : 0;
|
||||
status->flags |= systick_is_running() ? TCFLAGS_ACTIVE : 0;
|
||||
status->timeout = usec_from_count(getreg32(NVIC_SYSTICK_RELOAD),
|
||||
lower->freq);
|
||||
status->timeleft = usec_from_count(getreg32(NVIC_SYSTICK_CURRENT),
|
||||
lower->freq);
|
||||
|
||||
if (systick_irq_pending(lower))
|
||||
{
|
||||
/* Interrupt is pending and the timer wrap happen? */
|
||||
|
||||
if (status->timeleft)
|
||||
{
|
||||
/* Make timeout-timeleft equal the real elapsed time */
|
||||
|
||||
status->timeout += status->timeout - status->timeleft;
|
||||
status->timeleft = 0;
|
||||
}
|
||||
}
|
||||
else if (status->timeleft == 0)
|
||||
{
|
||||
status->timeleft = status->timeout;
|
||||
}
|
||||
|
||||
leave_critical_section(flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int systick_settimeout(FAR struct timer_lowerhalf_s *lower_,
|
||||
uint32_t timeout)
|
||||
{
|
||||
struct systick_lowerhalf_s *lower = (struct systick_lowerhalf_s *)lower_;
|
||||
|
||||
irqstate_t flags = enter_critical_section();
|
||||
if (lower->next_interval)
|
||||
{
|
||||
/* If the timer callback is in the process,
|
||||
* delay the update to timer interrupt handler.
|
||||
*/
|
||||
|
||||
*lower->next_interval = timeout;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t reload;
|
||||
|
||||
reload = usec_to_count(timeout, lower->freq);
|
||||
putreg32(reload, NVIC_SYSTICK_RELOAD);
|
||||
if (systick_is_running())
|
||||
{
|
||||
if (reload != getreg32(NVIC_SYSTICK_CURRENT))
|
||||
{
|
||||
putreg32(0, NVIC_SYSTICK_CURRENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
leave_critical_section(flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void systick_setcallback(FAR struct timer_lowerhalf_s *lower_,
|
||||
CODE tccb_t callback, FAR void *arg)
|
||||
{
|
||||
struct systick_lowerhalf_s *lower = (struct systick_lowerhalf_s *)lower_;
|
||||
|
||||
irqstate_t flags = enter_critical_section();
|
||||
lower->callback = callback;
|
||||
lower->arg = arg;
|
||||
leave_critical_section(flags);
|
||||
}
|
||||
|
||||
static int systick_maxtimeout(FAR struct timer_lowerhalf_s *lower_,
|
||||
uint32_t *maxtimeout)
|
||||
{
|
||||
uint64_t maxtimeout64 = usec_from_count(
|
||||
NVIC_SYSTICK_RELOAD_MASK, g_systick_lower.freq);
|
||||
|
||||
if (maxtimeout64 > UINT32_MAX)
|
||||
{
|
||||
*maxtimeout = UINT32_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
*maxtimeout = maxtimeout64;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int systick_interrupt(int irq, FAR void *context, FAR void *arg)
|
||||
{
|
||||
struct systick_lowerhalf_s *lower = arg;
|
||||
|
||||
if (lower->callback && systick_is_running())
|
||||
{
|
||||
uint32_t reload = getreg32(NVIC_SYSTICK_RELOAD);
|
||||
uint32_t interval = usec_from_count(reload, lower->freq);
|
||||
uint32_t next_interval = interval;
|
||||
|
||||
lower->next_interval = &next_interval;
|
||||
if (lower->callback(&next_interval, lower->arg))
|
||||
{
|
||||
if (next_interval && next_interval != interval)
|
||||
{
|
||||
reload = usec_to_count(next_interval, lower->freq);
|
||||
putreg32(reload, NVIC_SYSTICK_RELOAD);
|
||||
putreg32(0, NVIC_SYSTICK_CURRENT);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
modifyreg32(NVIC_SYSTICK_CTRL, NVIC_SYSTICK_CTRL_ENABLE, 0);
|
||||
}
|
||||
|
||||
lower->next_interval = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
struct timer_lowerhalf_s *systick_initialize(bool coreclk,
|
||||
unsigned int freq, int minor)
|
||||
{
|
||||
struct systick_lowerhalf_s *lower = (struct systick_lowerhalf_s *)&g_systick_lower;
|
||||
|
||||
/* Calculate the working clock frequency if need */
|
||||
|
||||
if (freq == 0)
|
||||
{
|
||||
uint32_t calib = getreg32(NVIC_SYSTICK_CALIB);
|
||||
uint32_t tenms = calib & NVIC_SYSTICK_CALIB_TENMS_MASK;
|
||||
lower->freq = 100 * (tenms + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
lower->freq = freq;
|
||||
}
|
||||
|
||||
/* Disable SYSTICK, but enable interrupt */
|
||||
|
||||
if (coreclk)
|
||||
{
|
||||
putreg32(NVIC_SYSTICK_CTRL_CLKSOURCE | NVIC_SYSTICK_CTRL_TICKINT, NVIC_SYSTICK_CTRL);
|
||||
}
|
||||
else
|
||||
{
|
||||
putreg32(NVIC_SYSTICK_CTRL_TICKINT, NVIC_SYSTICK_CTRL);
|
||||
}
|
||||
|
||||
irq_attach(NVIC_IRQ_SYSTICK, systick_interrupt, lower);
|
||||
up_enable_irq(NVIC_IRQ_SYSTICK);
|
||||
|
||||
/* Register the timer driver if need */
|
||||
|
||||
if (minor >= 0)
|
||||
{
|
||||
char devname[32];
|
||||
|
||||
sprintf(devname, "/dev/timer%d", minor);
|
||||
timer_register(devname, (struct timer_lowerhalf_s *)lower);
|
||||
}
|
||||
|
||||
return (struct timer_lowerhalf_s *)lower;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ARMV7M_SYSTICK */
|
||||
Loading…
Add table
Reference in a new issue