1/* @title: Clock Event Device */
2#pragma once
3#include <err.h>
4#include <mem/nofail.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 err (*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 err (*change_state)(struct clock_evdev *,
45 enum clock_evdev_state state);
46 enum err (*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_node list_internal; /* Used in global list */
55 struct list_node 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 clock_evdev_count; /* 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_node list_internal; /* Global list as well */
75};
76
77struct clock_evdev_group *clock_evdev_group_create(const char *fmt, ...)
78 cc_printf_like(1, 2);
79struct clock_evdev *clock_evdev_create(const char *fmt, ...)
80 cc_printf_like(1, 2);
81
82struct clock_evdev_group *clock_evdev_group_search_for(const char *name);
83void clock_evdev_register(struct clock_evdev *ced);
84void clock_evdev_group_register(struct clock_evdev_group *cedg);
85
86static inline void clock_evdev_group_add(struct clock_evdev_group *cedg,
87 struct clock_evdev *ced) {
88 if (!ced->in_global_list)
89 clock_evdev_register(ced);
90
91 list_head_push_back(h: &cedg->clock_evdevs, n: &ced->group_list);
92 cedg->clock_evdev_count++;
93}
94
95static inline struct clock_evdev *
96clock_evdev_for_cpu(struct clock_evdev_group *cedg, cpu_id_t cpu) {
97 if (!(cedg->flags & CLOCK_EVDEV_GROUP_PERCPU) || cedg->evdev_for_cpu)
98 return enforce(cedg->evdev_for_cpu(cedg, cpu));
99
100 struct clock_evdev *iter, *found = NULL;
101 list_for_each_entry(iter, &cedg->clock_evdevs, group_list) {
102 if (iter->bound_to_cpu == cpu) {
103
104 /* sanity check */
105 enforce(!found, DUPLICATE, "only one CED per CEDG per CPU allowed");
106 found = iter;
107 }
108 }
109
110 return enforce(found, NOT_FOUND);
111}
112
113void clock_evdev_group_unregister(struct clock_evdev_group *cedg);
114