407bd9a3051d66fc44bf6bb00dd08606dc4090b7
[lunaix-os.git] / lunaix-os / kernel / time / timer.c
1 /**
2  * @file timer.c
3  * @author Lunaixsky
4  * @brief A simple timer implementation based on APIC with adjustable frequency
5  * and subscribable "timerlets"
6  * @version 0.1
7  * @date 2022-03-12
8  *
9  * @copyright Copyright (c) 2022
10  *
11  */
12
13 #include <lunaix/mm/cake.h>
14 #include <lunaix/mm/valloc.h>
15 #include <lunaix/sched.h>
16 #include <lunaix/spike.h>
17 #include <lunaix/syslog.h>
18 #include <lunaix/timer.h>
19 #include <lunaix/pcontext.h>
20
21 #include <hal/hwtimer.h>
22
23 LOG_MODULE("TIMER");
24
25 static void
26 timer_update();
27
28 static volatile struct lx_timer_context* timer_ctx = NULL;
29
30 static volatile u32_t sched_ticks = 0;
31 static volatile u32_t sched_ticks_counter = 0;
32
33 static struct cake_pile* timer_pile;
34
35 void
36 timer_init_context()
37 {
38     timer_pile = cake_new_pile("timer", sizeof(struct lx_timer), 1, 0);
39     timer_ctx =
40       (struct lx_timer_context*)valloc(sizeof(struct lx_timer_context));
41
42     assert_msg(timer_ctx, "Fail to initialize timer contex");
43
44     timer_ctx->active_timers = (struct lx_timer*)cake_grab(timer_pile);
45     llist_init_head(&timer_ctx->active_timers->link);
46 }
47
48 void
49 timer_init()
50 {
51     timer_init_context();
52
53     hwtimer_init(SYS_TIMER_FREQUENCY_HZ, timer_update);
54
55     timer_ctx->base_frequency = hwtimer_base_frequency();
56
57     sched_ticks = (SYS_TIMER_FREQUENCY_HZ * SCHED_TIME_SLICE) / 1000;
58     sched_ticks_counter = 0;
59 }
60
61 struct lx_timer*
62 timer_run_second(u32_t second,
63                  void (*callback)(void*),
64                  void* payload,
65                  u8_t flags)
66 {
67     ticks_t t = hwtimer_to_ticks(second, TIME_SEC);
68     return timer_run(t, callback, payload, flags);
69 }
70
71 struct lx_timer*
72 timer_run_ms(u32_t millisecond,
73              void (*callback)(void*),
74              void* payload,
75              u8_t flags)
76 {
77     ticks_t t = hwtimer_to_ticks(millisecond, TIME_MS);
78     return timer_run(t, callback, payload, flags);
79 }
80
81 struct lx_timer*
82 timer_run(ticks_t ticks, void (*callback)(void*), void* payload, u8_t flags)
83 {
84     struct lx_timer* timer = (struct lx_timer*)cake_grab(timer_pile);
85
86     if (!timer)
87         return NULL;
88
89     timer->callback = callback;
90     timer->counter = ticks;
91     timer->deadline = ticks;
92     timer->payload = payload;
93     timer->flags = flags;
94
95     llist_append(&timer_ctx->active_timers->link, &timer->link);
96
97     return timer;
98 }
99
100 static void
101 timer_update()
102 {
103     struct lx_timer *pos, *n;
104     struct lx_timer* timer_list_head = timer_ctx->active_timers;
105
106     llist_for_each(pos, n, &timer_list_head->link, link)
107     {
108         if (--(pos->counter)) {
109             continue;
110         }
111
112         pos->callback ? pos->callback(pos->payload) : 1;
113
114         if ((pos->flags & TIMER_MODE_PERIODIC)) {
115             pos->counter = pos->deadline;
116         } else {
117             llist_delete(&pos->link);
118             cake_release(timer_pile, pos);
119         }
120     }
121
122     sched_ticks_counter++;
123
124     if (sched_ticks_counter >= sched_ticks) {
125         sched_ticks_counter = 0;
126         schedule();
127     }
128 }
129
130 struct lx_timer_context*
131 timer_context()
132 {
133     return (struct lx_timer_context*)timer_ctx;
134 }