diff --git a/arch/risc-v/src/common/riscv_mtimer.c b/arch/risc-v/src/common/riscv_mtimer.c new file mode 100644 index 0000000000..112919e4ba --- /dev/null +++ b/arch/risc-v/src/common/riscv_mtimer.c @@ -0,0 +1,306 @@ +/**************************************************************************** + * arch/risc-v/src/common/riscv_mtimer.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 "riscv_mtimer.h" +#include "riscv_internal.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 riscv_mtimer_lowerhalf_s +{ + struct oneshot_lowerhalf_s lower; + uintptr_t mtime; + uintptr_t mtimecmp; + uint64_t freq; + uint64_t alarm; + oneshot_callback_t callback; + void *arg; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int riscv_mtimer_max_delay(struct oneshot_lowerhalf_s *lower, + struct timespec *ts); +static int riscv_mtimer_start(struct oneshot_lowerhalf_s *lower, + oneshot_callback_t callback, void *arg, + const struct timespec *ts); +static int riscv_mtimer_cancel(struct oneshot_lowerhalf_s *lower, + struct timespec *ts); +static int riscv_mtimer_current(struct oneshot_lowerhalf_s *lower, + struct timespec *ts); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct oneshot_operations_s g_riscv_mtimer_ops = +{ + .max_delay = riscv_mtimer_max_delay, + .start = riscv_mtimer_start, + .cancel = riscv_mtimer_cancel, + .current = riscv_mtimer_current, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint64_t riscv_mtimer_get_mtime(struct riscv_mtimer_lowerhalf_s *priv) +{ +#ifdef CONFIG_ARCH_RV64 + return getreg64(priv->mtime); +#else + uint32_t hi; + uint32_t lo; + + do + { + hi = getreg32(priv->mtime + 4); + lo = getreg32(priv->mtime); + } + while (getreg32(priv->mtime + 4) != hi); + + return ((uint64_t)hi << 32) | lo; +#endif +} + +static void riscv_mtimer_set_mtimecmp(struct riscv_mtimer_lowerhalf_s *priv, + uint64_t value) +{ +#ifdef CONFIG_ARCH_RV64 + putreg64(value, priv->mtimecmp); +#else + putreg32(UINT32_MAX, priv->mtimecmp + 4); + putreg32(value, priv->mtimecmp); + putreg32(value >> 32, priv->mtimecmp + 4); +#endif +} + +/**************************************************************************** + * Name: riscv_mtimer_max_delay + * + * Description: + * Determine the maximum delay of the one-shot timer + * + * Input Parameters: + * lower An instance of the lower-half oneshot state structure. This + * structure must have been previously initialized via a call to + * oneshot_initialize(); + * ts The location in which to return the maximum delay. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +static int riscv_mtimer_max_delay(struct oneshot_lowerhalf_s *lower, + struct timespec *ts) +{ + ts->tv_sec = UINT32_MAX; + ts->tv_nsec = NSEC_PER_SEC - 1; + + return 0; +} + +/**************************************************************************** + * Name: riscv_mtimer_start + * + * Description: + * Start the oneshot timer + * + * Input Parameters: + * lower An instance of the lower-half oneshot state structure. This + * structure must have been previously initialized via a call to + * oneshot_initialize(); + * handler The function to call when when the oneshot timer expires. + * arg An opaque argument that will accompany the callback. + * ts Provides the duration of the one shot timer. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +static int riscv_mtimer_start(struct oneshot_lowerhalf_s *lower, + oneshot_callback_t callback, void *arg, + const struct timespec *ts) +{ + struct riscv_mtimer_lowerhalf_s *priv = + (struct riscv_mtimer_lowerhalf_s *)lower; + uint64_t mtime = riscv_mtimer_get_mtime(priv); + + priv->alarm = mtime + ts->tv_sec * priv->freq + + ts->tv_nsec * priv->freq / NSEC_PER_SEC; + if (priv->alarm < mtime) + { + priv->alarm = UINT64_MAX; + } + + priv->callback = callback; + priv->arg = arg; + + riscv_mtimer_set_mtimecmp(priv, priv->alarm); + return 0; +} + +/**************************************************************************** + * Name: riscv_mtimer_cancel + * + * Description: + * Cancel the oneshot timer and return the time remaining on the timer. + * + * NOTE: This function may execute at a high rate with no timer running (as + * when pre-emption is enabled and disabled). + * + * Input Parameters: + * lower Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * oneshot_initialize(); + * ts The location in which to return the time remaining on the + * oneshot timer. A time of zero is returned if the timer is + * not running. + * + * Returned Value: + * Zero (OK) is returned on success. A call to up_timer_cancel() when + * the timer is not active should also return success; a negated errno + * value is returned on any failure. + * + ****************************************************************************/ + +static int riscv_mtimer_cancel(struct oneshot_lowerhalf_s *lower, + struct timespec *ts) +{ + struct riscv_mtimer_lowerhalf_s *priv = + (struct riscv_mtimer_lowerhalf_s *)lower; + uint64_t mtime; + + riscv_mtimer_set_mtimecmp(priv, UINT64_MAX); + + mtime = riscv_mtimer_get_mtime(priv); + if (priv->alarm > mtime) + { + uint64_t nsec = (priv->alarm - mtime) * + NSEC_PER_SEC / priv->freq; + + ts->tv_sec = nsec / NSEC_PER_SEC; + ts->tv_nsec = nsec % NSEC_PER_SEC; + } + else + { + ts->tv_sec = 0; + ts->tv_nsec = 0; + } + + priv->alarm = 0; + priv->callback = NULL; + priv->arg = NULL; + + return 0; +} + +/**************************************************************************** + * Name: riscv_mtimer_current + * + * Description: + * Get the current time. + * + * Input Parameters: + * lower Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * oneshot_initialize(); + * ts The location in which to return the current time. A time of zero + * is returned for the initialization moment. + * + * Returned Value: + * Zero (OK) is returned on success, a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +static int riscv_mtimer_current(struct oneshot_lowerhalf_s *lower, + struct timespec *ts) +{ + struct riscv_mtimer_lowerhalf_s *priv = + (struct riscv_mtimer_lowerhalf_s *)lower; + uint64_t mtime = riscv_mtimer_get_mtime(priv); + uint64_t nsec = mtime * NSEC_PER_SEC / priv->freq; + + ts->tv_sec = nsec / NSEC_PER_SEC; + ts->tv_nsec = nsec % NSEC_PER_SEC; + + return 0; +} + +static int riscv_mtimer_interrupt(int irq, void *context, void *arg) +{ + struct riscv_mtimer_lowerhalf_s *priv = arg; + + riscv_mtimer_set_mtimecmp(priv, UINT64_MAX); + if (priv->callback != NULL) + { + priv->callback(&priv->lower, priv->arg); + } + + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +struct oneshot_lowerhalf_s * +riscv_mtimer_initialize(uintptr_t mtime, uintptr_t mtimecmp, + int irq, uint64_t freq) +{ + struct riscv_mtimer_lowerhalf_s *priv; + + priv = kmm_zalloc(sizeof(*priv)); + if (priv != NULL) + { + priv->lower.ops = &g_riscv_mtimer_ops; + priv->mtime = mtime; + priv->mtimecmp = mtimecmp; + priv->freq = freq; + + riscv_mtimer_set_mtimecmp(priv, UINT64_MAX); + irq_attach(irq, riscv_mtimer_interrupt, priv); + up_enable_irq(irq); + } + + return (struct oneshot_lowerhalf_s *)priv; +} diff --git a/arch/risc-v/src/common/riscv_mtimer.h b/arch/risc-v/src/common/riscv_mtimer.h new file mode 100644 index 0000000000..ce3339cec9 --- /dev/null +++ b/arch/risc-v/src/common/riscv_mtimer.h @@ -0,0 +1,51 @@ +/**************************************************************************** + * arch/risc-v/src/common/riscv_mtimer.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_RISCV_SRC_COMMON_RISCV_MTIMER_H +#define __ARCH_RISCV_SRC_COMMON_RISCV_MTIMER_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +struct oneshot_lowerhalf_s * +riscv_mtimer_initialize(uintptr_t mtime, uintptr_t mtimecmp, + int irq, uint64_t freq); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __ARCH_RISCV_SRC_COMMON_RISCV_MTIMER_H */