diff --git a/arch/x86_64/src/intel64/CMakeLists.txt b/arch/x86_64/src/intel64/CMakeLists.txt index c2f8482757c..3b00bd2f002 100644 --- a/arch/x86_64/src/intel64/CMakeLists.txt +++ b/arch/x86_64/src/intel64/CMakeLists.txt @@ -83,11 +83,11 @@ endif() if(CONFIG_ARCH_INTEL64_HAVE_TSC) if(CONFIG_SCHED_TICKLESS) - list(APPEND SRCS intel64_tsc_tickless.c) + list(APPEND SRCS intel64_tsc_oneshot.c) else() list(APPEND SRCS intel64_tsc_timerisr.c) + list(APPEND SRCS intel64_tsc_ndelay.c) endif() - list(APPEND SRCS intel64_tsc_ndelay.c) endif() if(CONFIG_INTEL64_HPET) diff --git a/arch/x86_64/src/intel64/Kconfig b/arch/x86_64/src/intel64/Kconfig index aac0c149fb3..9787d989670 100644 --- a/arch/x86_64/src/intel64/Kconfig +++ b/arch/x86_64/src/intel64/Kconfig @@ -67,6 +67,10 @@ choice config ARCH_INTEL64_TSC_DEADLINE bool "TSC DEADLINE timer support" select ARCH_INTEL64_HAVE_TSC + select ONESHOT + select ONESHOT_COUNT + select ONESHOT_FAST_DIVISION + select ALARM_ARCH ---help--- Select to enable the use of TSC DEADLINE timer of x86_64 @@ -78,7 +82,7 @@ config ARCH_INTEL64_TSC config ARCH_INTEL64_HPET_ALARM bool "HPET timer alarm support" - depends on ALARM_ARCH + select 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 @@ -172,7 +176,7 @@ config INTEL64_HPET_MIN_DELAY endif # INTEL64_HPET config INTEL64_ONESHOT - bool "Oneshot timer support" + bool "Oneshot HPET timer support" select INTEL64_HPET default n ---help--- diff --git a/arch/x86_64/src/intel64/Make.defs b/arch/x86_64/src/intel64/Make.defs index 7a337322da3..1dd6fea18f9 100644 --- a/arch/x86_64/src/intel64/Make.defs +++ b/arch/x86_64/src/intel64/Make.defs @@ -74,12 +74,12 @@ endif ifeq ($(CONFIG_ARCH_INTEL64_HAVE_TSC),y) ifeq ($(CONFIG_SCHED_TICKLESS),y) -CHIP_CSRCS += intel64_tsc_tickless.c +CHIP_CSRCS += intel64_tsc_oneshot.c else CHIP_CSRCS += intel64_tsc_timerisr.c -endif CHIP_CSRCS += intel64_tsc_ndelay.c endif +endif ifeq ($(CONFIG_INTEL64_HPET),y) CHIP_CSRCS += intel64_hpet.c diff --git a/arch/x86_64/src/intel64/intel64_tsc_oneshot.c b/arch/x86_64/src/intel64/intel64_tsc_oneshot.c new file mode 100644 index 00000000000..3da60d27f36 --- /dev/null +++ b/arch/x86_64/src/intel64/intel64_tsc_oneshot.c @@ -0,0 +1,280 @@ +/**************************************************************************** + * arch/x86_64/src/intel64/intel64_tsc_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 + +#include +#include +#include +#include +#include + +#include "x86_64_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define TMR_IRQ IRQ14 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static clkcnt_t intel64_tsc_max_delay(struct oneshot_lowerhalf_s *lower); +static clkcnt_t intel64_tsc_current(struct oneshot_lowerhalf_s *lower); +static void intel64_tsc_start_absolute(struct oneshot_lowerhalf_s *lower, + clkcnt_t expected); +static void intel64_tsc_start(struct oneshot_lowerhalf_s *lower, + clkcnt_t delta); +static void intel64_tsc_cancel(struct oneshot_lowerhalf_s *lower); + +static const struct oneshot_operations_s g_intel64_tsc_ops = +{ + .current = intel64_tsc_current, + .start = intel64_tsc_start, + .start_absolute = intel64_tsc_start_absolute, + .cancel = intel64_tsc_cancel, + .max_delay = intel64_tsc_max_delay +}; + +static struct oneshot_lowerhalf_s g_intel64_tsc_lowerhalf = +{ + .ops = &g_intel64_tsc_ops +}; + +static uint64_t g_tsc_offset; + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +extern unsigned long g_x86_64_timer_freq; + +#ifdef CONFIG_ARCH_INTEL64_HAVE_TSC_ADJUST +static inline_function void intel64_tsc_set_offset(uint64_t offset) +{ + uint64_t val = read_msr(MSR_IA32_TSC_ADJUST) + offset; + write_msr(MSR_IA32_TSC_ADJUST, val); +} + +static inline_function uint64_t intel64_tsc_get_offset(void) +{ + return 0ull; +} +#else +static inline_function void intel64_tsc_set_offset(uint64_t offset) +{ + g_tsc_offset = offset; +} +static inline_function uint64_t intel64_tsc_get_offset(void) +{ + return g_tsc_offset; +} +#endif + +static inline_function void intel64_mask_tmr(void) +{ + /* Disable TSC Deadline interrupt */ + +#ifdef CONFIG_ARCH_INTEL64_TSC_DEADLINE + write_msr(MSR_X2APIC_LVTT, TMR_IRQ | MSR_X2APIC_LVTT_TSC_DEADLINE | + (1 << 16)); +#else + write_msr(MSR_X2APIC_LVTT, TMR_IRQ | (1 << 16)); +#endif + + /* Required when using TSC deadline mode. */ + + __asm__ volatile("mfence" : : : "memory"); +} + +static inline_function void intel64_unmask_tmr(void) +{ + /* Enable TSC Deadline interrupt */ + +#ifdef CONFIG_ARCH_INTEL64_TSC_DEADLINE + write_msr(MSR_X2APIC_LVTT, TMR_IRQ | MSR_X2APIC_LVTT_TSC_DEADLINE); +#else + write_msr(MSR_X2APIC_LVTT, TMR_IRQ); +#endif + + /* Required when using TSC deadline mode. */ + + __asm__ volatile("mfence" : : : "memory"); +} + +static inline_function void intel64_tsc_set_compare(uint64_t deadline) +{ + write_msr(MSR_IA32_TSC_DEADLINE, deadline); +} + +static inline_function uint64_t intel64_tsc_count(void) +{ + /* We do not need the memory barrier here. */ + + return rdtsc(); +} + +static inline_function uint64_t intel64_tsc_freq(void) +{ +#if CONFIG_ARCH_INTEL64_CORE_FREQ_KHZ == 0 + return g_x86_64_timer_freq; +#else + return CONFIG_ARCH_INTEL64_CORE_FREQ_KHZ * 1000ull; +#endif +} + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: intel64_tsc_compare_isr + * + * Description: + * Common timer interrupt callback. When any oneshot timer interrupt + * expires, this function will be called. It will forward the call to + * the next level up. + * + * Input Parameters: + * oneshot - The state associated with the expired timer + * + * Returned Value: + * Always returns OK + * + ****************************************************************************/ + +static int intel64_tsc_compare_isr(int irq, void *regs, void *arg) +{ + struct oneshot_lowerhalf_s *priv = &g_intel64_tsc_lowerhalf; + + intel64_tsc_set_compare(UINT64_MAX); + + /* Then perform the callback */ + + oneshot_process_callback(priv); + + return OK; +} + +static clkcnt_t intel64_tsc_max_delay(struct oneshot_lowerhalf_s *lower) +{ + return UINT64_MAX; +} + +static clkcnt_t intel64_tsc_current(struct oneshot_lowerhalf_s *lower) +{ + /* We do not need memory barrier here. */ + + return intel64_tsc_count() + intel64_tsc_get_offset(); +} + +static void intel64_tsc_start_absolute(struct oneshot_lowerhalf_s *lower, + clkcnt_t expected) +{ + intel64_tsc_set_compare(expected - intel64_tsc_get_offset()); +} + +static void intel64_tsc_start(struct oneshot_lowerhalf_s *lower, + clkcnt_t delta) +{ + /* TSC Deadline is triggered when the counter is less than or equal + * to the compare value, so it cannot set an overflow value. + */ + + clkcnt_t now = intel64_tsc_count() + intel64_tsc_get_offset(); + clkcnt_t expected = now + delta >= now ? now + delta : UINT64_MAX; + intel64_tsc_set_compare(expected); +} + +static void intel64_tsc_cancel(struct oneshot_lowerhalf_s *lower) +{ + intel64_tsc_set_compare(UINT64_MAX); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: oneshot_initialize + * + * Description: + * Initialize the oneshot timer and return a oneshot lower half driver + * instance. + * + * Returned Value: + * On success, a non-NULL instance of the oneshot lower-half driver is + * returned. NULL is return on any failure. + * + ****************************************************************************/ + +struct oneshot_lowerhalf_s *intel64_tsc_initialize(void) +{ + struct oneshot_lowerhalf_s *priv = &g_intel64_tsc_lowerhalf; + uint64_t freq; + + tmrinfo("oneshot_initialize\n"); + + /* Attach handler */ + + freq = intel64_tsc_freq(); + + DEBUGASSERT(freq <= UINT32_MAX); + + oneshot_count_init(priv, (uint32_t)freq); + + tmrinfo("oneshot_initialize ok %p \n", priv); + + return priv; +} + +void up_timer_initialize(void) +{ + uint64_t tsc = intel64_tsc_count(); + + g_tsc_offset = 0ull - tsc; + intel64_tsc_set_offset(g_tsc_offset); + + up_alarm_set_lowerhalf(intel64_tsc_initialize()); + irq_attach(TMR_IRQ, (xcpt_t)intel64_tsc_compare_isr, NULL); + + intel64_tsc_set_compare(UINT64_MAX); + intel64_unmask_tmr(); +} + +void intel64_timer_secondary_init(void) +{ + intel64_tsc_set_offset(g_tsc_offset); + + intel64_tsc_set_compare(UINT64_MAX); + intel64_unmask_tmr(); +} diff --git a/arch/x86_64/src/intel64/intel64_tsc_tickless.c b/arch/x86_64/src/intel64/intel64_tsc_tickless.c deleted file mode 100644 index 1017725b410..00000000000 --- a/arch/x86_64/src/intel64/intel64_tsc_tickless.c +++ /dev/null @@ -1,513 +0,0 @@ -/**************************************************************************** - * arch/x86_64/src/intel64/intel64_tsc_tickless.c - * - * SPDX-License-Identifier: Apache-2.0 - * - * 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. - * - ****************************************************************************/ - -/**************************************************************************** - * Tickless OS Support. - * - * When CONFIG_SCHED_TICKLESS is enabled, all support for timer interrupts - * is suppressed and the platform specific code is expected to provide the - * following custom functions. - * - * void sim_timer_initialize(void): Initializes the timer facilities. - * Called early in the initialization sequence (by up_initialize()). - * int up_timer_gettime(struct timespec *ts): Returns the current - * time from the platform specific time source. - * int up_timer_cancel(void): Cancels the interval timer. - * int up_timer_start(const struct timespec *ts): Start (or re-starts) - * the interval timer. - * - * The RTOS will provide the following interfaces for use by the platform- - * specific interval timer implementation: - * - * void sched_timer_expiration(void): Called by the platform-specific - * logic when the interval timer expires. - * - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include - -#include -#include -#include -#include -#include - -#include -#include - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -#define NS_PER_USEC 1000UL -#define NS_PER_MSEC 1000000UL -#define NS_PER_SEC 1000000000UL - -#define TMR_IRQ IRQ14 - -#define ROUND_INT_DIV(s, d) (s + (d >> 1)) / d - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -extern unsigned long g_x86_64_timer_freq; - -#ifdef CONFIG_SCHED_TICKLESS_ALARM -static struct timespec g_goal_time_ts; -#else -static uint64_t g_goal_time; -#endif - -#ifdef CONFIG_ARCH_INTEL64_HAVE_TSC_ADJUST -static uint64_t g_tsc_adjust; -#else -static uint64_t g_tsc_start; -#endif - -static uint32_t g_timer_active; - -static irqstate_t g_tmr_sync_count; -static irqstate_t g_tmr_flags; - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -#ifdef CONFIG_ARCH_INTEL64_HAVE_TSC_ADJUST -uint64_t get_tsc_adjust(void) -{ - return g_tsc_adjust; -} -#endif - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -static inline uint64_t get_tsc_offset(void) -{ -#ifdef CONFIG_ARCH_INTEL64_HAVE_TSC_ADJUST - return 0; -#else - return g_tsc_start; -#endif -} - -void up_mask_tmr(void) -{ - /* Disable TSC Deadline interrupt */ - -#ifdef CONFIG_ARCH_INTEL64_TSC_DEADLINE - write_msr(MSR_X2APIC_LVTT, TMR_IRQ | MSR_X2APIC_LVTT_TSC_DEADLINE | - (1 << 16)); -#else - write_msr(MSR_X2APIC_LVTT, TMR_IRQ | (1 << 16)); -#endif - - /* Required when using TSC deadline mode. */ - - __asm__ volatile("mfence" : : : "memory"); -} - -void up_unmask_tmr(void) -{ - /* Enable TSC Deadline interrupt */ - -#ifdef CONFIG_ARCH_INTEL64_TSC_DEADLINE - write_msr(MSR_X2APIC_LVTT, TMR_IRQ | MSR_X2APIC_LVTT_TSC_DEADLINE); -#else - write_msr(MSR_X2APIC_LVTT, TMR_IRQ); -#endif - - /* Required when using TSC deadline mode. */ - - __asm__ volatile("mfence" : : : "memory"); -} - -#ifdef CONFIG_SCHED_TICKLESS_ALARM -void up_alarm_expire(void); -#else -void up_timer_expire(void); -#endif - -void up_timer_initialize(void) -{ - uint64_t tsc = rdtscp(); -#ifdef CONFIG_ARCH_INTEL64_HAVE_TSC_ADJUST - g_tsc_adjust = read_msr(MSR_IA32_TSC_ADJUST) - tsc; - write_msr(MSR_IA32_TSC_ADJUST, g_tsc_adjust); -#else - g_tsc_start = tsc; -#endif - -#ifdef CONFIG_SCHED_TICKLESS_ALARM - irq_attach(TMR_IRQ, (xcpt_t)up_alarm_expire, NULL); -#else - irq_attach(TMR_IRQ, (xcpt_t)up_timer_expire, NULL); -#endif -} - -void intel64_timer_secondary_init(void) -{ -#ifdef CONFIG_ARCH_INTEL64_HAVE_TSC_ADJUST - write_msr(MSR_IA32_TSC_ADJUST, get_tsc_adjust()); -#endif -} - -static inline uint64_t up_ts2tick(const struct timespec *ts) -{ - return ROUND_INT_DIV((uint64_t)ts->tv_nsec * g_x86_64_timer_freq, - NS_PER_SEC) + - (uint64_t)ts->tv_sec * g_x86_64_timer_freq; -} - -static inline void up_tick2ts(uint64_t tick, struct timespec *ts) -{ - ts->tv_sec = (tick / g_x86_64_timer_freq); - ts->tv_nsec = (uint64_t)(ROUND_INT_DIV((tick % g_x86_64_timer_freq) * - NSEC_PER_SEC, g_x86_64_timer_freq)); -} - -static inline void up_tmr_sync_up(void) -{ - if (!g_tmr_sync_count) - { - g_tmr_flags = enter_critical_section(); - } - - g_tmr_sync_count++; -} - -static inline void up_tmr_sync_down(void) -{ - if (g_tmr_sync_count == 1) - { - leave_critical_section(g_tmr_flags); - } - - if (g_tmr_sync_count > 0) - { - g_tmr_sync_count--; - } -} - -/**************************************************************************** - * Name: up_timer_gettime - * - * Description: - * Return the elapsed time since power-up (or, more correctly, since - * sim_timer_initialize() was called). This function is functionally - * equivalent to: - * - * int clock_gettime(clockid_t clockid, struct timespec *ts); - * - * when clockid is CLOCK_MONOTONIC. - * - * This function provides the basis for reporting the current time and - * also is used to eliminate error build-up from small errors in interval - * time calculations. - * - * Provided by platform-specific code and called from the RTOS base code. - * - * Input Parameters: - * ts - Provides the location in which to return the up-time. - * - * Returned Value: - * Zero (OK) is returned on success; a negated errno value is returned on - * any failure. - * - * Assumptions: - * Called from the normal tasking context. The implementation must - * provide whatever mutual exclusion is necessary for correct operation. - * This can include disabling interrupts in order to assure atomic register - * operations. - * - ****************************************************************************/ - -int up_timer_gettime(struct timespec *ts) -{ - uint64_t diff = rdtscp() - get_tsc_offset(); - up_tick2ts(diff, ts); - return OK; -} - -#ifdef CONFIG_SCHED_TICKLESS_ALARM - -/**************************************************************************** - * Name: up_timer_cancel - * - * Description: - * Cancel the interval timer and return the time remaining on the timer. - * These two steps need to be as nearly atomic as possible. - * sched_timer_expiration() will not be called unless the timer is - * restarted with up_timer_start(). - * - * If, as a race condition, the timer has already expired when this - * function is called, then that pending interrupt must be cleared so - * that up_timer_start() and the remaining time of zero should be - * returned. - * - * Provided by platform-specific code and called from the RTOS base code. - * - * Input Parameters: - * ts - Location to return the remaining time. Zero should be returned - * if the timer is not active. - * - * Returned Value: - * Zero (OK) is returned on success; a negated errno value is returned on - * any failure. - * - * Assumptions: - * May be called from interrupt level handling or from the normal tasking - * level. Interrupts may need to be disabled internally to assure - * non-reentrancy. - * - ****************************************************************************/ - -int up_alarm_cancel(struct timespec *ts) -{ - up_tmr_sync_up(); - - up_mask_tmr(); - - if (ts != NULL) - { - up_timer_gettime(ts); - } - - g_timer_active = 0; - - up_tmr_sync_down(); - - return OK; -} - -/**************************************************************************** - * Name: up_timer_start - * - * Description: - * Start the interval timer. sched_timer_expiration() will be - * called at the completion of the timeout (unless up_timer_cancel - * is called to stop the timing. - * - * Provided by platform-specific code and called from the RTOS base code. - * - * Input Parameters: - * ts - Provides the time interval until sched_timer_expiration() is - * called. - * - * Returned Value: - * Zero (OK) is returned on success; a negated errno value is returned on - * any failure. - * - * Assumptions: - * May be called from interrupt level handling or from the normal tasking - * level. Interrupts may need to be disabled internally to assure - * non-reentrancy. - * - ****************************************************************************/ - -int up_alarm_start(const struct timespec *ts) -{ - uint64_t ticks; - - up_tmr_sync_up(); - - up_unmask_tmr(); - - ticks = up_ts2tick(ts) + get_tsc_offset(); - - write_msr(MSR_IA32_TSC_DEADLINE, ticks); - - g_timer_active = 1; - - g_goal_time_ts.tv_sec = ts->tv_sec; - g_goal_time_ts.tv_nsec = ts->tv_nsec; - - up_tmr_sync_down(); - - tmrinfo("%" PRIdMAX ".%09ld\n", (uintmax_t)ts->tv_sec, ts->tv_nsec); - tmrinfo("start\n"); - - return OK; -} - -/**************************************************************************** - * Name: up_timer_update - * - * Description: - * Called as the IRQ handler for alarm expiration. - * - * Input Parameters: - * None - * - * Returned Value: - * None - * - ****************************************************************************/ - -void up_alarm_expire(void) -{ - struct timespec now; - - up_mask_tmr(); - tmrinfo("expire\n"); - - g_timer_active = 0; - - up_timer_gettime(&now); - - nxsched_alarm_expiration(&now); -} - -#else - -/**************************************************************************** - * Name: up_timer_cancel - * - * Description: - * Cancel the interval timer and return the time remaining on the timer. - * These two steps need to be as nearly atomic as possible. - * sched_timer_expiration() will not be called unless the timer is - * restarted with up_timer_start(). - * - * If, as a race condition, the timer has already expired when this - * function is called, then that pending interrupt must be cleared so - * that up_timer_start() and the remaining time of zero should be - * returned. - * - * Provided by platform-specific code and called from the RTOS base code. - * - * Input Parameters: - * ts - Location to return the remaining time. Zero should be returned - * if the timer is not active. - * - * Returned Value: - * Zero (OK) is returned on success; a negated errno value is returned on - * any failure. - * - * Assumptions: - * May be called from interrupt level handling or from the normal tasking - * level. Interrupts may need to be disabled internally to assure - * non-reentrancy. - * - ****************************************************************************/ - -int up_timer_cancel(struct timespec *ts) -{ - up_tmr_sync_up(); - - up_mask_tmr(); - - if (ts != NULL) - { - if (g_timer_active) - { - up_tick2ts(g_goal_time - rdtscp(), ts); - } - else - { - ts->tv_sec = 0; - ts->tv_nsec = 0; - } - } - - g_timer_active = 0; - - up_tmr_sync_down(); - - return OK; -} - -/**************************************************************************** - * Name: up_timer_start - * - * Description: - * Start the interval timer. sched_timer_expiration() will be - * called at the completion of the timeout (unless up_timer_cancel - * is called to stop the timing. - * - * Provided by platform-specific code and called from the RTOS base code. - * - * Input Parameters: - * ts - Provides the time interval until sched_timer_expiration() is - * called. - * - * Returned Value: - * Zero (OK) is returned on success; a negated errno value is returned on - * any failure. - * - * Assumptions: - * May be called from interrupt level handling or from the normal tasking - * level. Interrupts may need to be disabled internally to assure - * non-reentrancy. - * - ****************************************************************************/ - -int up_timer_start(const struct timespec *ts) -{ - uint64_t ticks; - - up_tmr_sync_up(); - - up_unmask_tmr(); - - ticks = up_ts2tick(ts) + rdtscp(); - - g_timer_active = 1; - - write_msr(MSR_IA32_TSC_DEADLINE, ticks); - - g_goal_time = ticks; - - up_tmr_sync_down(); - return OK; -} - -/**************************************************************************** - * Name: up_timer_expire - * - * Description: - * Called as the IRQ handler for alarm expiration. - * - * Input Parameters: - * None - * - * Returned Value: - * None - * - ****************************************************************************/ - -void up_timer_expire(void) -{ - g_timer_active = 0; - - up_mask_tmr(); - nxsched_timer_expiration(); -} - -#endif /* CONFIG_SCHED_TICKLESS_ALARM */