1/* @title: Clock Event Device */
2#pragma once
3#include <errno.h>
4#include <mem/alloc_or_die.h>
5#include <smp/domain.h>
6#include <structures/list.h>
7#include <time/clock.h>
8#include <types/types.h>
9
10enum clock_evdev_state {
11 CLOCK_EVDEV_STATE_OFF,
12 CLOCK_EVDEV_STATE_PERIODIC,
13 CLOCK_EVDEV_STATE_ONESHOT,
14 CLOCK_EVDEV_STATE_ONESHOT_STOPPED,
15};
16
17enum clock_evdev_flags {
18 CLOCK_EVDEV_PERIODIC = 1,
19 CLOCK_EVDEV_ONESHOT = 1 << 1,
20 CLOCK_EVDEV_DYNIRQ = 1 << 2,
21 CLOCK_EVDEV_PERCPU = 1 << 3,
22 CLOCK_EVDEV_TICK_SUITABLE = 1 << 4,
23};
24
25enum clock_evdev_group_flags {
26 CLOCK_EVDEV_GROUP_NONE = 0,
27 CLOCK_EVDEV_GROUP_PERCPU = 1, /* Each CPU has one */
28};
29
30struct clock_evdev {
31 char *name;
32
33 /* Guaranteed to be called at IRQL_HIGH_LEVEL */
34 enum errno (*set_next_event)(struct clock_evdev *, time_ns_t delta_ns);
35 timestamp_t next_event;
36
37 time_ns_t min_delta_ns;
38 time_ns_t max_delta_ns;
39
40 enum clock_evdev_state state;
41 enum clock_evdev_flags flags;
42 enum clock_rating rating;
43
44 enum errno (*change_state)(struct clock_evdev *,
45 enum clock_evdev_state state);
46 enum errno (*resume_tick)(struct clock_evdev *);
47
48 size_t min_delta_ticks;
49 size_t max_delta_ticks;
50
51 fx32_32_t mult; /* Tick -> NS */
52
53 bool in_global_list;
54 struct list_head list_internal; /* Used in global list */
55 struct list_head group_list; /* struct clock_evdev_group */
56 cpu_id_t bound_to_cpu; /* CPU_ID_NONE/!EVDEV_PERCPU = no binding */
57 struct cpu_mask cpu_mask;
58
59 irq_t irq;
60 struct clock *clock; /* Optional pointer */
61 void *private;
62};
63
64/* The clock evdevs are meant to be sequentially added to this */
65struct clock_evdev_group {
66 char *name;
67 struct list_head clock_evdevs;
68 size_t n_clock_evdevs; /* This is not synchronized because this happens
69 * once at group initialization */
70 struct clock_evdev *(*evdev_for_cpu)(struct clock_evdev_group *,
71 cpu_id_t cpu);
72
73 enum clock_evdev_group_flags flags;
74 struct list_head list_internal; /* Global list as well */
75};
76
77struct clock_evdev_group *clock_evdev_group_create(const char *fmt, ...);
78struct clock_evdev *clock_evdev_create(const char *fmt, ...);
79
80struct clock_evdev_group *clock_evdev_group_search_for(const char *name);
81void clock_evdev_register(struct clock_evdev *ced);
82void clock_evdev_group_register(struct clock_evdev_group *cedg);
83
84static inline void clock_evdev_group_add(struct clock_evdev_group *cedg,
85 struct clock_evdev *ced) {
86 if (!ced->in_global_list)
87 clock_evdev_register(ced);
88
89 list_add_tail(new: &ced->group_list, head: &cedg->clock_evdevs);
90 cedg->n_clock_evdevs++;
91}
92
93static inline struct clock_evdev *
94clock_evdev_for_cpu(struct clock_evdev_group *cedg, cpu_id_t cpu) {
95 if (!(cedg->flags & CLOCK_EVDEV_GROUP_PERCPU) || cedg->evdev_for_cpu)
96 return kassert(cedg->evdev_for_cpu(cedg, cpu));
97
98 struct clock_evdev *iter, *found = NULL;
99 list_for_each_entry(iter, &cedg->clock_evdevs, group_list) {
100 if (iter->bound_to_cpu == cpu) {
101
102 /* sanity check */
103 kassert(!found, "only one CED per CEDG per CPU allowed");
104 found = iter;
105 }
106 }
107
108 return kassert(found);
109}
110