]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
clockevents: Provide support for clocksource coupled comparators
authorThomas Gleixner <tglx@kernel.org>
Tue, 24 Feb 2026 16:36:45 +0000 (17:36 +0100)
committerPeter Zijlstra <peterz@infradead.org>
Fri, 27 Feb 2026 15:40:08 +0000 (16:40 +0100)
Some clockevent devices are coupled to the system clocksource by
implementing a less than or equal comparator which compares the programmed
absolute expiry time against the underlying time counter.

The timekeeping core provides a function to convert and absolute
CLOCK_MONOTONIC based expiry time to a absolute clock cycles time which can
be directly fed into the comparator. That spares two time reads in the next
event progamming path, one to convert the absolute nanoseconds time to a
delta value and the other to convert the delta value back to a absolute
time value suitable for the comparator.

Provide a new clocksource callback which takes the absolute cycle value and
wire it up in clockevents_program_event(). Similar to clocksources allow
architectures to inline the rearm operation.

Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://patch.msgid.link/20260224163430.010425428@kernel.org
include/linux/clockchips.h
kernel/time/Kconfig
kernel/time/clockevents.c

index 5e8f7819f6a617930de4afd32ffab0faf4641ec3..92d90220c0d49427aee475aa4cdff18948a2f977 100644 (file)
@@ -43,8 +43,9 @@ enum clock_event_state {
 /*
  * Clock event features
  */
-# define CLOCK_EVT_FEAT_PERIODIC       0x000001
-# define CLOCK_EVT_FEAT_ONESHOT                0x000002
+# define CLOCK_EVT_FEAT_PERIODIC               0x000001
+# define CLOCK_EVT_FEAT_ONESHOT                        0x000002
+# define CLOCK_EVT_FEAT_CLOCKSOURCE_COUPLED    0x000004
 
 /*
  * x86(64) specific (mis)features:
@@ -100,6 +101,7 @@ struct clock_event_device {
        void                    (*event_handler)(struct clock_event_device *);
        int                     (*set_next_event)(unsigned long evt, struct clock_event_device *);
        int                     (*set_next_ktime)(ktime_t expires, struct clock_event_device *);
+       void                    (*set_next_coupled)(u64 cycles, struct clock_event_device *);
        ktime_t                 next_event;
        u64                     max_delta_ns;
        u64                     min_delta_ns;
@@ -107,6 +109,7 @@ struct clock_event_device {
        u32                     shift;
        enum clock_event_state  state_use_accessors;
        unsigned int            features;
+       enum clocksource_ids    cs_id;
        unsigned long           retries;
 
        int                     (*set_state_periodic)(struct clock_event_device *);
index b51bc5625129e8b0aaec5ab72b7fd50211d0e442..e1968ab8b37f95425e56e03e89fb9f77972a1dce 100644 (file)
@@ -50,6 +50,10 @@ config GENERIC_CLOCKEVENTS_MIN_ADJUST
 config GENERIC_CLOCKEVENTS_COUPLED
        bool
 
+config GENERIC_CLOCKEVENTS_COUPLED_INLINE
+       select GENERIC_CLOCKEVENTS_COUPLED
+       bool
+
 # Generic update of CMOS clock
 config GENERIC_CMOS_UPDATE
        bool
index 5abaeef08e6a9908cedc80fed25c781e23af24c5..83712aa1d385c7046b4b367399419183b144f8f1 100644 (file)
@@ -292,6 +292,38 @@ static int clockevents_program_min_delta(struct clock_event_device *dev)
 
 #endif /* CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST */
 
+#ifdef CONFIG_GENERIC_CLOCKEVENTS_COUPLED
+#ifdef CONFIG_GENERIC_CLOCKEVENTS_COUPLED_INLINE
+#include <asm/clock_inlined.h>
+#else
+static __always_inline void
+arch_inlined_clockevent_set_next_coupled(u64 u64 cycles, struct clock_event_device *dev) { }
+#endif
+
+static inline bool clockevent_set_next_coupled(struct clock_event_device *dev, ktime_t expires)
+{
+       u64 cycles;
+
+       if (unlikely(!(dev->features & CLOCK_EVT_FEAT_CLOCKSOURCE_COUPLED)))
+               return false;
+
+       if (unlikely(!ktime_expiry_to_cycles(dev->cs_id, expires, &cycles)))
+               return false;
+
+       if (IS_ENABLED(CONFIG_GENERIC_CLOCKEVENTS_COUPLED_INLINE))
+               arch_inlined_clockevent_set_next_coupled(cycles, dev);
+       else
+               dev->set_next_coupled(cycles, dev);
+       return true;
+}
+
+#else
+static inline bool clockevent_set_next_coupled(struct clock_event_device *dev, ktime_t expires)
+{
+       return false;
+}
+#endif
+
 /**
  * clockevents_program_event - Reprogram the clock event device.
  * @dev:       device to program
@@ -300,11 +332,10 @@ static int clockevents_program_min_delta(struct clock_event_device *dev)
  *
  * Returns 0 on success, -ETIME when the event is in the past.
  */
-int clockevents_program_event(struct clock_event_device *dev, ktime_t expires,
-                             bool force)
+int clockevents_program_event(struct clock_event_device *dev, ktime_t expires, bool force)
 {
-       unsigned long long clc;
        int64_t delta;
+       u64 cycles;
        int rc;
 
        if (WARN_ON_ONCE(expires < 0))
@@ -323,6 +354,9 @@ int clockevents_program_event(struct clock_event_device *dev, ktime_t expires,
        if (unlikely(dev->features & CLOCK_EVT_FEAT_HRTIMER))
                return dev->set_next_ktime(expires, dev);
 
+       if (likely(clockevent_set_next_coupled(dev, expires)))
+               return 0;
+
        delta = ktime_to_ns(ktime_sub(expires, ktime_get()));
        if (delta <= 0)
                return force ? clockevents_program_min_delta(dev) : -ETIME;
@@ -330,8 +364,8 @@ int clockevents_program_event(struct clock_event_device *dev, ktime_t expires,
        delta = min(delta, (int64_t) dev->max_delta_ns);
        delta = max(delta, (int64_t) dev->min_delta_ns);
 
-       clc = ((unsigned long long) delta * dev->mult) >> dev->shift;
-       rc = dev->set_next_event((unsigned long) clc, dev);
+       cycles = ((u64)delta * dev->mult) >> dev->shift;
+       rc = dev->set_next_event((unsigned long) cycles, dev);
 
        return (rc && force) ? clockevents_program_min_delta(dev) : rc;
 }