armv7-a/r: use generic timer to realize arm_timer

Signed-off-by: ligd <liguiding1@xiaomi.com>
This commit is contained in:
ligd 2022-08-26 23:55:29 +08:00 committed by Petro Karashchenko
parent bc17563a8f
commit 37d37dcae5
7 changed files with 848 additions and 0 deletions

View File

@ -51,6 +51,10 @@ CMN_CSRCS += arm_schedulesigaction.c arm_sigdeliver.c
CMN_CSRCS += arm_syscall.c arm_tcbinfo.c arm_undefinedinsn.c CMN_CSRCS += arm_syscall.c arm_tcbinfo.c arm_undefinedinsn.c
CMN_CSRCS += arm_perf.c CMN_CSRCS += arm_perf.c
ifeq ($(CONFIG_ARMV7A_HAVE_PTM), y)
CMN_CSRCS += arm_timer.c
endif
ifeq ($(CONFIG_ARMV7A_L2CC_PL310),y) ifeq ($(CONFIG_ARMV7A_L2CC_PL310),y)
CMN_CSRCS += arm_l2cc_pl310.c CMN_CSRCS += arm_l2cc_pl310.c
endif endif

View File

@ -0,0 +1,346 @@
/****************************************************************************
* arch/arm/src/armv7-a/arm_timer.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include <nuttx/kmalloc.h>
#include "arm_timer.h"
#include "barriers.h"
#include "gic.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define ARM_TIMER_CTRL_ENABLE (1 << 0)
#define ARM_TIMER_CTRL_INT_MASK (1 << 1)
#define ARM_TIMER_CTRL_INT_STAT (1 << 2)
/****************************************************************************
* Private Types
****************************************************************************/
/* This structure provides the private representation of the "lower-half"
* driver state structure. This structure must be cast-compatible with the
* oneshot_lowerhalf_s structure.
*/
struct arm_timer_lowerhalf_s
{
struct oneshot_lowerhalf_s lh; /* Lower half operations */
uint32_t freq; /* Timer working clock frequency(Hz) */
oneshot_callback_t callback; /* Current user interrupt callback */
void *arg; /* Argument passed to upper half callback */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int arm_timer_maxdelay(struct oneshot_lowerhalf_s *lower,
struct timespec *ts);
static int arm_timer_start(struct oneshot_lowerhalf_s *lower,
oneshot_callback_t callback, void *arg,
const struct timespec *ts);
static int arm_timer_cancel(struct oneshot_lowerhalf_s *lower,
struct timespec *ts);
static int arm_timer_current(struct oneshot_lowerhalf_s *lower,
struct timespec *ts);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct oneshot_operations_s g_arm_timer_ops =
{
.max_delay = arm_timer_maxdelay,
.start = arm_timer_start,
.cancel = arm_timer_cancel,
.current = arm_timer_current,
};
/****************************************************************************
* Private Functions
****************************************************************************/
static inline uint32_t arm_timer_get_freq(void)
{
uint32_t freq;
ARM_ISB();
__asm__ __volatile__
(
"\tmrc p15, 0, %0, c14, c0, 0\n" /* Read CNTFRQ */
: "=r"(freq)
:
:
);
return freq;
}
static inline void arm_timer_set_freq(uint32_t freq)
{
__asm__ __volatile__
(
"\tmcr p15, 0, %0, c14, c0, 0\n" /* Write CNTFRQ */
:
: "r"(freq)
:
);
ARM_ISB();
}
static inline uint64_t arm_timer_get_count(void)
{
uint64_t count;
ARM_ISB();
__asm__ __volatile__
(
"\tmrrc p15, 0, %Q0, %R0, c14\n" /* Read CNTPCT */
: "=r"(count)
:
:
);
return count;
}
static inline uint32_t arm_timer_get_ctrl(void)
{
uint32_t ctrl;
ARM_ISB();
__asm__ __volatile__
(
"\tmrc p15, 0, %0, c14, c2, 1\n" /* Read CNTP_CTL */
: "=r"(ctrl)
:
:
);
return ctrl;
}
static inline void arm_timer_set_ctrl(uint32_t ctrl)
{
__asm__ __volatile__
(
"\tmcr p15, 0, %0, c14, c2, 1\n" /* Write CNTP_CTL */
:
: "r"(ctrl)
:
);
ARM_ISB();
}
static inline uint32_t arm_timer_get_tval(void)
{
uint32_t tval;
ARM_ISB();
__asm__ __volatile__
(
"\tmrc p15, 0, %0, c14, c2, 0\n" /* Read CNTP_TVAL */
: "=r"(tval)
:
:
);
return tval;
}
static inline void arm_timer_set_tval(uint32_t tval)
{
__asm__ __volatile__
(
"\tmcr p15, 0, %0, c14, c2, 0\n" /* Write CNTP_TVAL */
:
: "r"(tval)
:
);
ARM_ISB();
}
static inline uint64_t nsec_from_count(uint64_t count, uint32_t freq)
{
return (uint64_t)count * NSEC_PER_SEC / freq;
}
static inline uint64_t nsec_to_count(uint32_t nsec, uint32_t freq)
{
return (uint64_t)nsec * freq / NSEC_PER_SEC;
}
static inline uint64_t sec_to_count(uint32_t sec, uint32_t freq)
{
return (uint64_t)sec * freq;
}
static int arm_timer_maxdelay(struct oneshot_lowerhalf_s *lower_,
struct timespec *ts)
{
struct arm_timer_lowerhalf_s *lower =
(struct arm_timer_lowerhalf_s *)lower_;
uint64_t maxnsec = nsec_from_count(UINT32_MAX, lower->freq);
ts->tv_sec = maxnsec / NSEC_PER_SEC;
ts->tv_nsec = maxnsec % NSEC_PER_SEC;
return 0;
}
static int arm_timer_start(struct oneshot_lowerhalf_s *lower_,
oneshot_callback_t callback, void *arg,
const struct timespec *ts)
{
struct arm_timer_lowerhalf_s *lower =
(struct arm_timer_lowerhalf_s *)lower_;
irqstate_t flags;
uint32_t count;
uint32_t ctrl;
flags = up_irq_save();
lower->callback = callback;
lower->arg = arg;
count = sec_to_count(ts->tv_sec, lower->freq) +
nsec_to_count(ts->tv_nsec, lower->freq);
arm_timer_set_tval(count);
ctrl = arm_timer_get_ctrl();
ctrl &= ~ARM_TIMER_CTRL_INT_MASK;
arm_timer_set_ctrl(ctrl);
up_irq_restore(flags);
return 0;
}
static int arm_timer_cancel(struct oneshot_lowerhalf_s *lower_,
struct timespec *ts)
{
struct arm_timer_lowerhalf_s *lower =
(struct arm_timer_lowerhalf_s *)lower_;
irqstate_t flags;
uint32_t ctrl;
flags = up_irq_save();
lower->callback = NULL;
lower->arg = NULL;
ctrl = arm_timer_get_ctrl();
ctrl |= ARM_TIMER_CTRL_INT_MASK;
arm_timer_set_ctrl(ctrl);
up_irq_restore(flags);
return 0;
}
static int arm_timer_current(struct oneshot_lowerhalf_s *lower_,
struct timespec *ts)
{
struct arm_timer_lowerhalf_s *lower =
(struct arm_timer_lowerhalf_s *)lower_;
uint64_t nsec = nsec_from_count(arm_timer_get_count(), lower->freq);
ts->tv_sec = nsec / NSEC_PER_SEC;
ts->tv_nsec = nsec % NSEC_PER_SEC;
return 0;
}
static int arm_timer_interrupt(int irq, void *context, void *arg)
{
struct arm_timer_lowerhalf_s *lower = arg;
oneshot_callback_t callback;
void *cbarg;
DEBUGASSERT(lower != NULL);
if (lower->callback != NULL)
{
callback = lower->callback;
cbarg = lower->arg;
lower->callback = NULL;
lower->arg = NULL;
/* Then perform the callback */
callback(&lower->lh, cbarg);
}
return 0;
}
/****************************************************************************
* Public Functions
****************************************************************************/
struct oneshot_lowerhalf_s *arm_timer_initialize(unsigned int freq)
{
struct arm_timer_lowerhalf_s *lower;
uint32_t ctrl;
lower = kmm_zalloc(sizeof(*lower));
if (lower == NULL)
{
return NULL;
}
if (freq == 0)
{
freq = arm_timer_get_freq();
}
lower->lh.ops = &g_arm_timer_ops;
lower->freq = freq;
arm_timer_set_freq(freq);
/* Enable timer, but disable interrupt */
ctrl = arm_timer_get_ctrl();
ctrl |= ARM_TIMER_CTRL_ENABLE | ARM_TIMER_CTRL_INT_MASK;
arm_timer_set_ctrl(ctrl);
irq_attach(GIC_IRQ_PTM, arm_timer_interrupt, lower);
up_enable_irq(GIC_IRQ_PTM);
return (struct oneshot_lowerhalf_s *)lower;
}

View File

@ -0,0 +1,71 @@
/****************************************************************************
* arch/arm/src/armv7-a/arm_timer.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __ARCH_ARM_SRC_ARMV7_A_ARM_TIMER_H
#define __ARCH_ARM_SRC_ARMV7_A_ARM_TIMER_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/timers/oneshot.h>
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/****************************************************************************
* Name: arm_timer_initialize
*
* Description:
* This function initialize generic timer hardware module
* and return an instance of a "lower half" timer interface.
*
* Input parameters:
* freq - The clock frequency in Hz. If freq is zero, get the value
* from CNTFRQ register.
*
* Returned Value:
* On success, a non-NULL oneshot_lowerhalf_s is returned to the caller.
* In the event of any failure, a NULL value is returned.
*
****************************************************************************/
#ifdef CONFIG_ARMV7A_HAVE_PTM
struct oneshot_lowerhalf_s *arm_timer_initialize(unsigned int freq);
#else
# define arm_timer_initialize(freq) NULL
#endif
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* __ARCH_ARM_SRC_ARMV7_A_ARM_TIMER_H */

View File

@ -12,6 +12,13 @@ config ARMV7R_HAVE_GICv2
Selected by the configuration tool if the architecture supports the Selected by the configuration tool if the architecture supports the
Generic Interrupt Controller (GIC) Generic Interrupt Controller (GIC)
config ARMV7R_HAVE_PTM
bool
default n
---help---
Selected by the configuration tool if the architecture supports the
per-processor Private Timers (PTMs)
config ARMV7R_MEMINIT config ARMV7R_MEMINIT
bool bool
default y if BOOT_SDRAM_DATA default y if BOOT_SDRAM_DATA

View File

@ -44,6 +44,9 @@ CMN_ASRCS += cp15_coherent_dcache.S cp15_flush_dcache_all.S
CMN_ASRCS += cp15_flush_dcache.S cp15_invalidate_dcache_all.S CMN_ASRCS += cp15_flush_dcache.S cp15_invalidate_dcache_all.S
CMN_ASRCS += cp15_invalidate_dcache.S CMN_ASRCS += cp15_invalidate_dcache.S
ifeq ($(CONFIG_ARMV7R_HAVE_PTM), y)
CMN_CSRCS += arm_timer.c
endif
ifeq ($(CONFIG_BUILD_PROTECTED),y) ifeq ($(CONFIG_BUILD_PROTECTED),y)
CMN_CSRCS += arm_mpu.c CMN_CSRCS += arm_mpu.c

View File

@ -0,0 +1,346 @@
/****************************************************************************
* arch/arm/src/armv7-r/arm_timer.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include <nuttx/kmalloc.h>
#include "arm_timer.h"
#include "barriers.h"
#include "gic.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define ARM_TIMER_CTRL_ENABLE (1 << 0)
#define ARM_TIMER_CTRL_INT_MASK (1 << 1)
#define ARM_TIMER_CTRL_INT_STAT (1 << 2)
/****************************************************************************
* Private Types
****************************************************************************/
/* This structure provides the private representation of the "lower-half"
* driver state structure. This structure must be cast-compatible with the
* oneshot_lowerhalf_s structure.
*/
struct arm_timer_lowerhalf_s
{
struct oneshot_lowerhalf_s lh; /* Lower half operations */
uint32_t freq; /* Timer working clock frequency(Hz) */
oneshot_callback_t callback; /* Current user interrupt callback */
void *arg; /* Argument passed to upper half callback */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int arm_timer_maxdelay(struct oneshot_lowerhalf_s *lower,
struct timespec *ts);
static int arm_timer_start(struct oneshot_lowerhalf_s *lower,
oneshot_callback_t callback, void *arg,
const struct timespec *ts);
static int arm_timer_cancel(struct oneshot_lowerhalf_s *lower,
struct timespec *ts);
static int arm_timer_current(struct oneshot_lowerhalf_s *lower,
struct timespec *ts);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct oneshot_operations_s g_arm_timer_ops =
{
.max_delay = arm_timer_maxdelay,
.start = arm_timer_start,
.cancel = arm_timer_cancel,
.current = arm_timer_current,
};
/****************************************************************************
* Private Functions
****************************************************************************/
static inline uint32_t arm_timer_get_freq(void)
{
uint32_t freq;
ARM_ISB();
__asm__ __volatile__
(
"\tmrc p15, 0, %0, c14, c0, 0\n" /* Read CNTFRQ */
: "=r"(freq)
:
:
);
return freq;
}
static inline void arm_timer_set_freq(uint32_t freq)
{
__asm__ __volatile__
(
"\tmcr p15, 0, %0, c14, c0, 0\n" /* Write CNTFRQ */
:
: "r"(freq)
:
);
ARM_ISB();
}
static inline uint64_t arm_timer_get_count(void)
{
uint64_t count;
ARM_ISB();
__asm__ __volatile__
(
"\tmrrc p15, 0, %Q0, %R0, c14\n" /* Read CNTPCT */
: "=r"(count)
:
:
);
return count;
}
static inline uint32_t arm_timer_get_ctrl(void)
{
uint32_t ctrl;
ARM_ISB();
__asm__ __volatile__
(
"\tmrc p15, 0, %0, c14, c2, 1\n" /* Read CNTP_CTL */
: "=r"(ctrl)
:
:
);
return ctrl;
}
static inline void arm_timer_set_ctrl(uint32_t ctrl)
{
__asm__ __volatile__
(
"\tmcr p15, 0, %0, c14, c2, 1\n" /* Write CNTP_CTL */
:
: "r"(ctrl)
:
);
ARM_ISB();
}
static inline uint32_t arm_timer_get_tval(void)
{
uint32_t tval;
ARM_ISB();
__asm__ __volatile__
(
"\tmrc p15, 0, %0, c14, c2, 0\n" /* Read CNTP_TVAL */
: "=r"(tval)
:
:
);
return tval;
}
static inline void arm_timer_set_tval(uint32_t tval)
{
__asm__ __volatile__
(
"\tmcr p15, 0, %0, c14, c2, 0\n" /* Write CNTP_TVAL */
:
: "r"(tval)
:
);
ARM_ISB();
}
static inline uint64_t nsec_from_count(uint64_t count, uint32_t freq)
{
return (uint64_t)count * NSEC_PER_SEC / freq;
}
static inline uint64_t nsec_to_count(uint32_t nsec, uint32_t freq)
{
return (uint64_t)nsec * freq / NSEC_PER_SEC;
}
static inline uint64_t sec_to_count(uint32_t sec, uint32_t freq)
{
return (uint64_t)sec * freq;
}
static int arm_timer_maxdelay(struct oneshot_lowerhalf_s *lower_,
struct timespec *ts)
{
struct arm_timer_lowerhalf_s *lower =
(struct arm_timer_lowerhalf_s *)lower_;
uint64_t maxnsec = nsec_from_count(UINT32_MAX, lower->freq);
ts->tv_sec = maxnsec / NSEC_PER_SEC;
ts->tv_nsec = maxnsec % NSEC_PER_SEC;
return 0;
}
static int arm_timer_start(struct oneshot_lowerhalf_s *lower_,
oneshot_callback_t callback, void *arg,
const struct timespec *ts)
{
struct arm_timer_lowerhalf_s *lower =
(struct arm_timer_lowerhalf_s *)lower_;
irqstate_t flags;
uint32_t count;
uint32_t ctrl;
flags = up_irq_save();
lower->callback = callback;
lower->arg = arg;
count = sec_to_count(ts->tv_sec, lower->freq) +
nsec_to_count(ts->tv_nsec, lower->freq);
arm_timer_set_tval(count);
ctrl = arm_timer_get_ctrl();
ctrl &= ~ARM_TIMER_CTRL_INT_MASK;
arm_timer_set_ctrl(ctrl);
up_irq_restore(flags);
return 0;
}
static int arm_timer_cancel(struct oneshot_lowerhalf_s *lower_,
struct timespec *ts)
{
struct arm_timer_lowerhalf_s *lower =
(struct arm_timer_lowerhalf_s *)lower_;
irqstate_t flags;
uint32_t ctrl;
flags = up_irq_save();
lower->callback = NULL;
lower->arg = NULL;
ctrl = arm_timer_get_ctrl();
ctrl |= ARM_TIMER_CTRL_INT_MASK;
arm_timer_set_ctrl(ctrl);
up_irq_restore(flags);
return 0;
}
static int arm_timer_current(struct oneshot_lowerhalf_s *lower_,
struct timespec *ts)
{
struct arm_timer_lowerhalf_s *lower =
(struct arm_timer_lowerhalf_s *)lower_;
uint64_t nsec = nsec_from_count(arm_timer_get_count(), lower->freq);
ts->tv_sec = nsec / NSEC_PER_SEC;
ts->tv_nsec = nsec % NSEC_PER_SEC;
return 0;
}
static int arm_timer_interrupt(int irq, void *context, void *arg)
{
struct arm_timer_lowerhalf_s *lower = arg;
oneshot_callback_t callback;
void *cbarg;
DEBUGASSERT(lower != NULL);
if (lower->callback != NULL)
{
callback = lower->callback;
cbarg = lower->arg;
lower->callback = NULL;
lower->arg = NULL;
/* Then perform the callback */
callback(&lower->lh, cbarg);
}
return 0;
}
/****************************************************************************
* Public Functions
****************************************************************************/
struct oneshot_lowerhalf_s *arm_timer_initialize(unsigned int freq)
{
struct arm_timer_lowerhalf_s *lower;
uint32_t ctrl;
lower = kmm_zalloc(sizeof(*lower));
if (lower == NULL)
{
return NULL;
}
if (freq == 0)
{
freq = arm_timer_get_freq();
}
lower->lh.ops = &g_arm_timer_ops;
lower->freq = freq;
arm_timer_set_freq(freq);
/* Enable timer, but disable interrupt */
ctrl = arm_timer_get_ctrl();
ctrl |= ARM_TIMER_CTRL_ENABLE | ARM_TIMER_CTRL_INT_MASK;
arm_timer_set_ctrl(ctrl);
irq_attach(GIC_IRQ_PTM, arm_timer_interrupt, lower);
up_enable_irq(GIC_IRQ_PTM);
return (struct oneshot_lowerhalf_s *)lower;
}

View File

@ -0,0 +1,71 @@
/****************************************************************************
* arch/arm/src/armv7-r/arm_timer.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __ARCH_ARM_SRC_ARMV7_R_ARM_TIMER_H
#define __ARCH_ARM_SRC_ARMV7_R_ARM_TIMER_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/timers/oneshot.h>
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/****************************************************************************
* Name: arm_timer_initialize
*
* Description:
* This function initialize generic timer hardware module
* and return an instance of a "lower half" timer interface.
*
* Input parameters:
* freq - The clock frequency in Hz. If freq is zero, get the value
* from CNTFRQ register.
*
* Returned Value:
* On success, a non-NULL oneshot_lowerhalf_s is returned to the caller.
* In the event of any failure, a NULL value is returned.
*
****************************************************************************/
#ifdef CONFIG_ARMV7A_HAVE_PTM
struct oneshot_lowerhalf_s *arm_timer_initialize(unsigned int freq);
#else
# define arm_timer_initialize(freq) NULL
#endif
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* __ARCH_ARM_SRC_ARMV7_R_ARM_TIMER_H */