1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/device/timer.hpp"
4
5#include "mos/device/clocksource.hpp"
6#include "mos/lib/structures/list.hpp"
7#include "mos/lib/sync/spinlock.hpp"
8#include "mos/platform/platform.hpp"
9#include "mos/tasks/schedule.hpp"
10#include "mos/tasks/signal.hpp"
11
12static list_head timer_queue; ///< list of timers that are waiting to be executed
13static spinlock_t timer_queue_lock;
14
15static bool timer_do_wakeup(ktimer_t *timer, void *arg)
16{
17 MOS_UNUSED(arg);
18
19 if (timer->thread)
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 return -ENOTSUP;
43
44 const u64 offset = ms * active_clocksource->frequency / 1000;
45 const u64 target_val = active_clocksource_ticks() + offset;
46
47 ktimer_t timer = {
48 .timeout = target_val,
49 .thread = current_thread,
50 .ticked = false,
51 .callback = timer_do_wakeup,
52 .arg = NULL,
53 };
54
55 spinlock_acquire(&timer_queue_lock);
56 list_node_append(head: &timer_queue, list_node(&timer));
57 spinlock_release(&timer_queue_lock);
58
59 while (!timer.ticked)
60 {
61 blocked_reschedule();
62 if (signal_has_pending())
63 {
64 spinlock_acquire(&timer_queue_lock);
65 list_remove(&timer);
66 spinlock_release(&timer_queue_lock);
67 return -EINTR; // interrupted by signal
68 }
69 }
70
71 MOS_ASSERT(list_is_empty(list_node(&timer)));
72 return 0;
73}
74