From 3c05da536a9e4a365dca5e9f2cc486c255d749df Mon Sep 17 00:00:00 2001 From: p-szafonimateusz Date: Wed, 5 Jun 2024 10:22:50 +0200 Subject: [PATCH] arch/intel64: add support for HPET as system clock HPET can be used as system clock for x86_64 to set HPET as system clock you have to enable: CONFIG_ONESHOT=y CONFIG_ALARM_ARCH=y CONFIG_INTEL64_ONESHOT=y CONFIG_ARCH_INTEL64_HPET_ALARM=y Signed-off-by: p-szafonimateusz --- arch/x86_64/src/intel64/CMakeLists.txt | 4 + arch/x86_64/src/intel64/Kconfig | 13 ++ arch/x86_64/src/intel64/Make.defs | 4 + arch/x86_64/src/intel64/intel64_hpet_alarm.c | 42 ++++ arch/x86_64/src/intel64/intel64_oneshot.c | 30 +++ arch/x86_64/src/intel64/intel64_oneshot.h | 11 + .../src/intel64/intel64_oneshot_lower.c | 194 +++++++++++++++++- .../intel64/qemu-intel64/src/qemu_bringup.c | 15 +- 8 files changed, 309 insertions(+), 4 deletions(-) create mode 100644 arch/x86_64/src/intel64/intel64_hpet_alarm.c diff --git a/arch/x86_64/src/intel64/CMakeLists.txt b/arch/x86_64/src/intel64/CMakeLists.txt index 5bf42a106f..f5b1a76977 100644 --- a/arch/x86_64/src/intel64/CMakeLists.txt +++ b/arch/x86_64/src/intel64/CMakeLists.txt @@ -76,6 +76,10 @@ if(CONFIG_INTEL64_HPET) list(APPEND SRCS intel64_hpet.c) endif() +if(CONFIG_ARCH_INTEL64_HPET_ALARM) + list(APPEND SRCS intel64_hpet_alarm.c) +endif() + if(CONFIG_INTEL64_ONESHOT) list(APPEND SRCS intel64_oneshot.c intel64_oneshot_lower.c) endif() diff --git a/arch/x86_64/src/intel64/Kconfig b/arch/x86_64/src/intel64/Kconfig index 86d96138e2..3d28782b55 100644 --- a/arch/x86_64/src/intel64/Kconfig +++ b/arch/x86_64/src/intel64/Kconfig @@ -68,6 +68,14 @@ config ARCH_INTEL64_TSC ---help--- Select to enable the use of TSC APIC timer of x86_64 +config ARCH_INTEL64_HPET_ALARM + bool "HPET timer alarm support" + depends on ALARM_ARCH + ---help--- + With this option you can enable ALARM_ARCH features that works on top of + the HPET timer instance. This is an alternative method to TSC timer to + provide the system clock. + endchoice # System Timer Source if ARCH_INTEL64_TSC_DEADLINE @@ -108,6 +116,11 @@ config INTEL64_HPET_BASE Configure base address for HPET. In the future, we can get this address from the ACPI table if ACPI support is enabled. +config ARCH_INTEL64_HPET_ALARM_CHAN + int "HPET timer alarm channel" + depends on ARCH_INTEL64_HPET_ALARM + default 0 + config INTEL64_HPET_CHANNELS int "HPET timer supported channels" default 2 diff --git a/arch/x86_64/src/intel64/Make.defs b/arch/x86_64/src/intel64/Make.defs index 479dd0235c..e1c8579c97 100644 --- a/arch/x86_64/src/intel64/Make.defs +++ b/arch/x86_64/src/intel64/Make.defs @@ -66,6 +66,10 @@ ifeq ($(CONFIG_INTEL64_HPET),y) CHIP_CSRCS += intel64_hpet.c endif +ifeq ($(CONFIG_ARCH_INTEL64_HPET_ALARM),y) +CHIP_CSRCS += intel64_hpet_alarm.c +endif + ifeq ($(CONFIG_INTEL64_ONESHOT),y) CHIP_CSRCS += intel64_oneshot.c intel64_oneshot_lower.c endif diff --git a/arch/x86_64/src/intel64/intel64_hpet_alarm.c b/arch/x86_64/src/intel64/intel64_hpet_alarm.c new file mode 100644 index 0000000000..fcc35a40e1 --- /dev/null +++ b/arch/x86_64/src/intel64/intel64_hpet_alarm.c @@ -0,0 +1,42 @@ +/**************************************************************************** + * arch/x86_64/src/intel64/intel64_hpet_alarm.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 "intel64_oneshot.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#ifdef CONFIG_ARCH_INTEL64_HPET_ALARM +void up_timer_initialize(void) +{ + struct oneshot_lowerhalf_s *lower = + oneshot_initialize(CONFIG_ARCH_INTEL64_HPET_ALARM_CHAN, 10); + up_alarm_set_lowerhalf(lower); +} +#endif diff --git a/arch/x86_64/src/intel64/intel64_oneshot.c b/arch/x86_64/src/intel64/intel64_oneshot.c index 2822c4100c..6646c3934b 100644 --- a/arch/x86_64/src/intel64/intel64_oneshot.c +++ b/arch/x86_64/src/intel64/intel64_oneshot.c @@ -191,6 +191,12 @@ int intel64_oneshot_initialize(struct intel64_oneshot_s *oneshot, int chan, oneshot->period = INTEL64_TIM_GETPERIOD(oneshot->tch); oneshot->frequency = 1e15 / oneshot->period; + /* Calculations in intel64_oneshot_current() requires that HPET + * frequency is less than 1GHz + */ + + DEBUGASSERT(oneshot->frequency < 1000000000); + /* Initialize the remaining fields in the state structure. */ oneshot->chan = chan; @@ -423,3 +429,27 @@ int intel64_oneshot_cancel(struct intel64_oneshot_s *oneshot, return OK; } + +/**************************************************************************** + * Name: intel64_oneshot_current + * + * Description: + * Get the current time. + * + ****************************************************************************/ + +int intel64_oneshot_current(struct intel64_oneshot_s *oneshot, + uint64_t *usec) +{ + uint64_t counter; + + /* Get the current counter value */ + + counter = INTEL64_TIM_GETCOUNTER(oneshot->tch); + + /* Use nano seconds to avoid 64-bit overflow */ + + *usec = (counter * (NSEC_PER_SEC / oneshot->frequency)) / 1000; + + return OK; +} diff --git a/arch/x86_64/src/intel64/intel64_oneshot.h b/arch/x86_64/src/intel64/intel64_oneshot.h index fd9a024f66..db4b0859aa 100644 --- a/arch/x86_64/src/intel64/intel64_oneshot.h +++ b/arch/x86_64/src/intel64/intel64_oneshot.h @@ -168,6 +168,17 @@ int intel64_oneshot_start(struct intel64_oneshot_s *oneshot, int intel64_oneshot_cancel(struct intel64_oneshot_s *oneshot, struct timespec *ts); +/**************************************************************************** + * Name: intel64_oneshot_current + * + * Description: + * Get the current time. + * + ****************************************************************************/ + +int intel64_oneshot_current(struct intel64_oneshot_s *oneshot, + uint64_t *usec); + #undef EXTERN #if defined(__cplusplus) } diff --git a/arch/x86_64/src/intel64/intel64_oneshot_lower.c b/arch/x86_64/src/intel64/intel64_oneshot_lower.c index 3e6f324b51..766b87e313 100644 --- a/arch/x86_64/src/intel64/intel64_oneshot_lower.c +++ b/arch/x86_64/src/intel64/intel64_oneshot_lower.c @@ -64,6 +64,17 @@ static int intel64_start(struct oneshot_lowerhalf_s *lower, const struct timespec *ts); static int intel64_cancel(struct oneshot_lowerhalf_s *lower, struct timespec *ts); +static int intel64_current(struct oneshot_lowerhalf_s *lower, + struct timespec *ts); +static int intel64_tick_max_delay(struct oneshot_lowerhalf_s *lower, + clock_t *ticks); +static int intel64_tick_start(struct oneshot_lowerhalf_s *lower, + oneshot_callback_t callback, void *arg, + clock_t ticks); +static int intel64_tick_cancel(struct oneshot_lowerhalf_s *lower, + clock_t *ticks); +static int intel64_tick_current(struct oneshot_lowerhalf_s *lower, + clock_t *ticks); /**************************************************************************** * Private Data @@ -73,9 +84,14 @@ static int intel64_cancel(struct oneshot_lowerhalf_s *lower, static const struct oneshot_operations_s g_oneshot_ops = { - .max_delay = intel64_max_delay, - .start = intel64_start, - .cancel = intel64_cancel, + .max_delay = intel64_max_delay, + .start = intel64_start, + .cancel = intel64_cancel, + .current = intel64_current, + .tick_max_delay = intel64_tick_max_delay, + .tick_start = intel64_tick_start, + .tick_cancel = intel64_tick_cancel, + .tick_current = intel64_tick_current, }; static spinlock_t g_oneshotlow_spin; @@ -268,6 +284,178 @@ static int intel64_cancel(struct oneshot_lowerhalf_s *lower, return ret; } +/**************************************************************************** + * Name: intel64_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(); + * ticks The location in which to return the current time. + * + * Returned Value: + * Zero (OK) is returned on success, a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +static int intel64_current(struct oneshot_lowerhalf_s *lower, + struct timespec *ts) +{ + struct intel64_oneshot_lowerhalf_s *priv = + (struct intel64_oneshot_lowerhalf_s *)lower; + uint64_t current_us; + + DEBUGASSERT(priv != NULL && ts != NULL); + + intel64_oneshot_current(&priv->oneshot, ¤t_us); + ts->tv_sec = current_us / USEC_PER_SEC; + current_us = current_us - ts->tv_sec * USEC_PER_SEC; + ts->tv_nsec = current_us * NSEC_PER_USEC; + + return OK; +} + +/**************************************************************************** + * Name: intel64_tick_max_delay + * + * Description: + * Determine the maximum delay of the one-shot timer (in microseconds) + * + * 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(); + * ticks 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 intel64_tick_max_delay(struct oneshot_lowerhalf_s *lower, + clock_t *ticks) +{ + struct timespec ts; + int ret; + + ret = intel64_max_delay(lower, &ts); + + /* Convert time to ticks */ + + clock_time2ticks(&ts, (sclock_t *)ticks); + + return ret; +} + +/**************************************************************************** + * Name: intel64_tick_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. + * ticks 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 intel64_tick_start(struct oneshot_lowerhalf_s *lower, + oneshot_callback_t callback, void *arg, + clock_t ticks) +{ + struct timespec ts; + + /* Convert ticks to time */ + + clock_ticks2time(ticks, &ts); + + return intel64_start(lower, callback, arg, &ts); +} + +/**************************************************************************** + * Name: intel64_tick_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(); + * ticks The location in which to return the time remaining on the + * oneshot timer. + * + * 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 intel64_tick_cancel(struct oneshot_lowerhalf_s *lower, + clock_t *ticks) +{ + struct timespec ts; + int ret; + + ret = intel64_cancel(lower, &ts); + + /* Convert time to ticks */ + + clock_time2ticks(&ts, (sclock_t *)ticks); + + return ret; +} + +/**************************************************************************** + * Name: intel64_tick_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(); + * ticks The location in which to return the current time. + * + * Returned Value: + * Zero (OK) is returned on success, a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +static int intel64_tick_current(struct oneshot_lowerhalf_s *lower, + clock_t *ticks) +{ + struct timespec ts; + int ret; + + ret = intel64_current(lower, &ts); + + /* Convert time to ticks */ + + clock_time2ticks(&ts, (sclock_t *)ticks); + + return ret; +} + /**************************************************************************** * Public Functions ****************************************************************************/ diff --git a/boards/x86_64/intel64/qemu-intel64/src/qemu_bringup.c b/boards/x86_64/intel64/qemu-intel64/src/qemu_bringup.c index 38675a887e..8a39c3c2ab 100644 --- a/boards/x86_64/intel64/qemu-intel64/src/qemu_bringup.c +++ b/boards/x86_64/intel64/qemu-intel64/src/qemu_bringup.c @@ -39,6 +39,19 @@ #include "x86_64_internal.h" #include "qemu_intel64.h" +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_ARCH_INTEL64_HPET_ALARM +# if CONFIG_ARCH_INTEL64_HPET_ALARM_CHAN != 0 +# error this logic requires that HPET_ALARM_CHAN is set to 0 +# endif +# define ONESHOT_TIMER 1 +#else +# define ONESHOT_TIMER 0 +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -72,7 +85,7 @@ int qemu_bringup(void) #endif #ifdef CONFIG_ONESHOT - os = oneshot_initialize(0, 10); + os = oneshot_initialize(ONESHOT_TIMER, 10); if (os) { oneshot_register("/dev/oneshot", os);