1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/device/timer.h"
4
5#include "mos/device/clocksource.h"
6#include "mos/lib/structures/list.h"
7#include "mos/lib/sync/spinlock.h"
8#include "mos/platform/platform.h"
9#include "mos/tasks/schedule.h"
10#include "mos/tasks/signal.h"
11
12static list_head timer_queue = LIST_HEAD_INIT(timer_queue); ///< list of timers that are waiting to be executed
13static spinlock_t timer_queue_lock = SPINLOCK_INIT;
14
15static bool timer_do_wakeup(ktimer_t *timer, void *arg)
16{
17 MOS_UNUSED(arg);
18
19 if (timer->thread != NULL)
20 scheduler_wake_thread(thread: timer->thread);
21
22 return true;
23}
24
25void timer_tick()
26{
27 spinlock_acquire(&timer_queue_lock);
28 list_foreach(ktimer_t, timer, timer_queue)
29 {
30 if (active_clocksource_ticks() >= (u64) timer->timeout)
31 {
32 if (timer->callback(timer, timer->arg))
33 timer->ticked = true, list_remove(timer);
34 }
35 }
36 spinlock_release(&timer_queue_lock);
37}
38
39long timer_msleep(u64 ms)
40{
41 if (!active_clocksource)
42 {
43 spinlock_acquire(&current_thread->state_lock);
44 reschedule(); // simulate a reschedule if no clocksource is available
45 return 0;
46 }
47
48 const u64 offset = ms * active_clocksource->frequency / 1000;
49 const u64 target_val = active_clocksource_ticks() + offset;
50
51 ktimer_t timer = {
52 .list_node = LIST_NODE_INIT(timer),
53 .timeout = target_val,
54 .thread = current_thread,
55 .ticked = false,
56 .callback = timer_do_wakeup,
57 .arg = NULL,
58 };
59
60 spinlock_acquire(&timer_queue_lock);
61 list_node_append(head: &timer_queue, list_node(&timer));
62 spinlock_release(&timer_queue_lock);
63
64 while (!timer.ticked)
65 {
66 blocked_reschedule();
67 if (signal_has_pending())
68 {
69 spinlock_acquire(&timer_queue_lock);
70 list_remove(&timer);
71 spinlock_release(&timer_queue_lock);
72 return -EINTR; // interrupted by signal
73 }
74 }
75
76 MOS_ASSERT(list_is_empty(list_node(&timer)));
77 return 0;
78}
79