From d7391bf6bc8f7d89579949e2dc956086c9088266 Mon Sep 17 00:00:00 2001 From: zhuyanlin Date: Thu, 27 Jan 2022 15:56:19 +0800 Subject: [PATCH] xtensa: add xtensa arch oneshot ops As xtensa timer is common in all xtensa chips, Use oneshot ops, implement a common xtensa oneshot timer. Signed-off-by: zhuyanlin --- arch/xtensa/Kconfig | 7 + arch/xtensa/src/common/xtensa.h | 5 + arch/xtensa/src/common/xtensa_oneshot.c | 200 ++++++++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 arch/xtensa/src/common/xtensa_oneshot.c diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 12bddac038..a4d2985ac7 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -159,6 +159,13 @@ config XTENSA_DCACHE_LOCK ---help--- Enable Xtensa D-Cache lock & unlock feature +config XTENSA_ONESHOT + bool "Xtensa oneshot lower half driver" + default n + depends on ONESHOT + ---help--- + Enable Xtensa oneshot driver. + config ARCH_FAMILY_LX6 bool default n diff --git a/arch/xtensa/src/common/xtensa.h b/arch/xtensa/src/common/xtensa.h index b02571ddcc..a984976ef0 100644 --- a/arch/xtensa/src/common/xtensa.h +++ b/arch/xtensa/src/common/xtensa.h @@ -318,6 +318,11 @@ void xtensa_add_region(void); # define xtensa_add_region() #endif +/* Watchdog timer ***********************************************************/ + +struct oneshot_lowerhalf_s * +xtensa_oneshot_initialize(uint32_t irq, uint32_t freq); + /* Serial output */ void up_lowputc(char ch); diff --git a/arch/xtensa/src/common/xtensa_oneshot.c b/arch/xtensa/src/common/xtensa_oneshot.c new file mode 100644 index 0000000000..102cb8d718 --- /dev/null +++ b/arch/xtensa/src/common/xtensa_oneshot.c @@ -0,0 +1,200 @@ +/**************************************************************************** + * arch/xtensa/src/common/xtensa_oneshot.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 + +#include +#include +#include +#include + +#include "xtensa_counter.h" +#include "xtensa.h" + +/**************************************************************************** + * 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 xoneshot_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 */ + uint32_t irq; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int xtensa_oneshot_maxdelay(struct oneshot_lowerhalf_s *lower, + struct timespec *ts); +static int xtensa_oneshot_start(struct oneshot_lowerhalf_s *lower, + oneshot_callback_t callback, void *arg, + const struct timespec *ts); +static int xtensa_oneshot_cancel(struct oneshot_lowerhalf_s *lower, + struct timespec *ts); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct oneshot_operations_s g_xtensa_oneshot_ops = +{ + .max_delay = xtensa_oneshot_maxdelay, + .start = xtensa_oneshot_start, + .cancel = xtensa_oneshot_cancel, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static inline uint64_t nsec_from_count(uint32_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 xtensa_oneshot_start(struct oneshot_lowerhalf_s *lower_, + oneshot_callback_t callback, void *arg, + const struct timespec *ts) +{ + struct xoneshot_lowerhalf_s *lower = + (struct xoneshot_lowerhalf_s *)lower_; + uint32_t count; + irqstate_t flags; + + flags = enter_critical_section(); + + lower->callback = callback; + lower->arg = arg; + + count = sec_to_count((uint64_t)ts->tv_sec, lower->freq) + + nsec_to_count((uint64_t)ts->tv_nsec, lower->freq); + + count = xtensa_getcount() + count; + xtensa_setcompare(count); + + up_enable_irq(lower->irq); + + leave_critical_section(flags); + + return 0; +} + +static int xtensa_oneshot_cancel(struct oneshot_lowerhalf_s *lower_, + struct timespec *ts) +{ + struct xoneshot_lowerhalf_s *lower = + (struct xoneshot_lowerhalf_s *)lower_; + irqstate_t flags; + + flags = enter_critical_section(); + + lower->callback = NULL; + lower->arg = NULL; + + up_disable_irq(lower->irq); + + leave_critical_section(flags); + + return 0; +} + +static int xtensa_oneshot_maxdelay(struct oneshot_lowerhalf_s *lower_, + struct timespec *ts) +{ + struct xoneshot_lowerhalf_s *lower = + (struct xoneshot_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 xtensa_oneshot_interrupt(int irq, void *context, void *arg) +{ + struct xoneshot_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 * +xtensa_oneshot_initialize(uint32_t irq, uint32_t freq) +{ + struct xoneshot_lowerhalf_s *lower = + (struct xoneshot_lowerhalf_s *)kmm_zalloc(sizeof(*lower)); + + if (lower == NULL) + { + return NULL; + } + + lower->lh.ops = &g_xtensa_oneshot_ops; + lower->freq = freq; + lower->irq = irq; + + irq_attach(irq, xtensa_oneshot_interrupt, lower); + + return (struct oneshot_lowerhalf_s *)lower; +}