1/*
2 * Copyright 2022 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "arch_timer_generic.h"
8
9
10#define TIMER_IRQ 30
11
12
13static uint32_t
14get_counter_freq(void)
15{
16    uint32_t freq;
17    asm volatile ("MRC p15, 0, %0, c14, c0, 0": "=r" (freq));
18    return freq;
19}
20
21
22static uint64_t
23get_counter(void)
24{
25    uint32_t counter_low;
26    uint32_t counter_high;
27
28    asm volatile ("ISB\n"
29        "MRRC p15, 0, %0, %1, c14"
30        : "=r" (counter_low), "=r" (counter_high));
31    return ((uint64_t)counter_high << 32) | counter_low;
32}
33
34
35void
36ARMGenericTimer::SetTimeout(bigtime_t timeout)
37{
38	uint32_t timeout_ticks = timeout * fTimerFrequencyMHz;
39	asm volatile ("ISB\n"
40		"MCR p15, 0, %0, c14, c2, 0"
41		: : "r"(timeout_ticks));
42
43	uint32_t timer_ctl = 1;
44	asm volatile ("MCR p15, 0, %0, c14, c2, 1"
45		: : "r"(timer_ctl));
46}
47
48
49void
50ARMGenericTimer::Clear()
51{
52	uint32_t timer_ctl = 2;
53	asm volatile ("MCR p15, 0, %0, c14, c2, 1"
54		: : "r"(timer_ctl));
55}
56
57
58bigtime_t
59ARMGenericTimer::Time()
60{
61	return get_counter() / fTimerFrequencyMHz;
62}
63
64
65int32
66ARMGenericTimer::_InterruptWrapper(void *data)
67{
68	return ((ARMGenericTimer*)data)->HandleInterrupt();
69}
70
71
72int32
73ARMGenericTimer::HandleInterrupt()
74{
75	return timer_interrupt();
76}
77
78
79ARMGenericTimer::ARMGenericTimer()
80: HardwareTimer()
81{
82	Clear();
83
84	install_io_interrupt_handler(TIMER_IRQ,
85		&ARMGenericTimer::_InterruptWrapper, this, 0);
86
87	fTimerFrequency = get_counter_freq();
88	fTimerFrequencyMHz = fTimerFrequency / 1000000;
89}
90
91
92bool
93ARMGenericTimer::IsAvailable()
94{
95	uint32_t pfr1;
96	asm volatile("MRC p15, 0, %0, c0, c1, 1": "=r" (pfr1));
97	return (pfr1 & 0x000F0000) == 0x00010000;
98}
99