rewrite the device subsystem interfaces (#48)
[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/hart_state.h>
20
21 #include <hal/hwtimer.h>
22
23 LOG_MODULE("TIMER");
24
25 static void
26 timer_update();
27
28 static DEFINE_LLIST(timers);
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 }
40
41 void
42 timer_init()
43 {
44     timer_init_context();
45
46     hwtimer_init(SYS_TIMER_FREQUENCY_HZ, timer_update);
47
48     sched_ticks = (SYS_TIMER_FREQUENCY_HZ * SCHED_TIME_SLICE) / 1000;
49     sched_ticks_counter = 0;
50 }
51
52 struct lx_timer*
53 timer_run_second(u32_t second,
54                  void (*callback)(void*),
55                  void* payload,
56                  u8_t flags)
57 {
58     ticks_t t = hwtimer_to_ticks(second, TIME_SEC);
59     return timer_run(t, callback, payload, flags);
60 }
61
62 struct lx_timer*
63 timer_run_ms(u32_t millisecond,
64              void (*callback)(void*),
65              void* payload,
66              u8_t flags)
67 {
68     ticks_t t = hwtimer_to_ticks(millisecond, TIME_MS);
69     return timer_run(t, callback, payload, flags);
70 }
71
72 struct lx_timer*
73 timer_run(ticks_t ticks, void (*callback)(void*), void* payload, u8_t flags)
74 {
75     struct lx_timer* timer = (struct lx_timer*)cake_grab(timer_pile);
76
77     if (!timer)
78         return NULL;
79
80     timer->callback = callback;
81     timer->counter = ticks;
82     timer->deadline = ticks;
83     timer->payload = payload;
84     timer->flags = flags;
85
86     llist_append(&timers, &timer->link);
87
88     return timer;
89 }
90
91 static void
92 timer_update()
93 {
94     struct lx_timer *pos, *n;
95
96     llist_for_each(pos, n, &timers, link)
97     {
98         if (--(pos->counter)) {
99             continue;
100         }
101
102         pos->callback ? pos->callback(pos->payload) : 1;
103
104         if ((pos->flags & TIMER_MODE_PERIODIC)) {
105             pos->counter = pos->deadline;
106         } else {
107             llist_delete(&pos->link);
108             cake_release(timer_pile, pos);
109         }
110     }
111
112     sched_ticks_counter++;
113
114     if (sched_ticks_counter >= sched_ticks) {
115         sched_ticks_counter = 0;
116         thread_stats_update_entering(false);
117         schedule();
118     }
119 }