1 // © 2021 Qualcomm Innovation Center, Inc. All rights reserved.
2 //
3 // SPDX-License-Identifier: BSD-3-Clause
4
5 #include <assert.h>
6 #include <hyptypes.h>
7
8 #include <hypregisters.h>
9
10 #include <irq.h>
11 #include <object.h>
12 #include <panic.h>
13 #include <preempt.h>
14
15 #include <asm/barrier.h>
16
17 #include "event_handlers.h"
18 #include "platform_timer.h"
19 #include "platform_timer_consts.h"
20
21 #if !defined(IRQ_NULL)
22 #include <partition.h>
23 #include <partition_alloc.h>
24
25 #include <events/platform.h>
26 #endif
27
28 #if !defined(IRQ_NULL)
29 static hwirq_t *hyp_timer_hwirq;
30 #endif
31
32 static void
platform_timer_enable_and_unmask(void)33 platform_timer_enable_and_unmask(void)
34 {
35 CNT_CTL_t cnthp_ctl;
36
37 CNT_CTL_init(&cnthp_ctl);
38 CNT_CTL_set_ENABLE(&cnthp_ctl, true);
39 CNT_CTL_set_IMASK(&cnthp_ctl, false);
40 register_CNTHP_CTL_EL2_write_ordered(cnthp_ctl, &asm_ordering);
41 }
42
43 void
platform_timer_cancel_timeout(void)44 platform_timer_cancel_timeout(void)
45 {
46 CNT_CTL_t cnthp_ctl;
47
48 CNT_CTL_init(&cnthp_ctl);
49 CNT_CTL_set_ENABLE(&cnthp_ctl, false);
50 CNT_CTL_set_IMASK(&cnthp_ctl, true);
51 register_CNTHP_CTL_EL2_write_ordered(cnthp_ctl, &asm_ordering);
52 __asm__ volatile("isb" : "+m"(asm_ordering));
53 }
54
55 uint32_t
platform_timer_get_frequency(void)56 platform_timer_get_frequency(void)
57 {
58 return PLATFORM_ARCH_TIMER_FREQ;
59 }
60
61 uint64_t
platform_timer_get_current_ticks(void)62 platform_timer_get_current_ticks(void)
63 {
64 // This register read below is allowed to occur speculatively at any
65 // time after the most recent context sync event. If caller the wants
66 // it to actually reflect the exact current time, it must execute an
67 // ordered ISB before calling this function.
68 CNTPCT_EL0_t cntpct =
69 register_CNTPCT_EL0_read_volatile_ordered(&asm_ordering);
70
71 return CNTPCT_EL0_get_CountValue(&cntpct);
72 }
73
74 uint64_t
platform_timer_get_timeout(void)75 platform_timer_get_timeout(void)
76 {
77 CNT_CVAL_t cnthp_cval =
78 register_CNTHP_CVAL_EL2_read_volatile_ordered(&asm_ordering);
79
80 return CNT_CVAL_get_CompareValue(&cnthp_cval);
81 }
82
83 void
platform_timer_set_timeout(ticks_t timeout)84 platform_timer_set_timeout(ticks_t timeout)
85 {
86 assert_preempt_disabled();
87
88 register_CNTHP_CVAL_EL2_write_ordered(CNT_CVAL_cast(timeout),
89 &asm_ordering);
90 platform_timer_enable_and_unmask();
91 __asm__ volatile("isb" : "+m"(asm_ordering));
92 }
93
94 ticks_t
platform_timer_convert_ns_to_ticks(nanoseconds_t ns)95 platform_timer_convert_ns_to_ticks(nanoseconds_t ns)
96 {
97 return (ticks_t)((ns * PLATFORM_TIMER_NS_TO_FREQ_MULT) /
98 PLATFORM_TIMER_FREQ_TO_NS_MULT);
99 }
100
101 nanoseconds_t
platform_timer_convert_ticks_to_ns(ticks_t ticks)102 platform_timer_convert_ticks_to_ns(ticks_t ticks)
103 {
104 return (nanoseconds_t)((ticks * PLATFORM_TIMER_FREQ_TO_NS_MULT) /
105 PLATFORM_TIMER_NS_TO_FREQ_MULT);
106 }
107
108 ticks_t
platform_timer_convert_ms_to_ticks(milliseconds_t ms)109 platform_timer_convert_ms_to_ticks(milliseconds_t ms)
110 {
111 return (ticks_t)((ms * PLATFORM_TIMER_MS_TO_FREQ_MULT) /
112 PLATFORM_TIMER_FREQ_TO_MS_MULT);
113 }
114
115 milliseconds_t
platform_timer_convert_ticks_to_ms(ticks_t ticks)116 platform_timer_convert_ticks_to_ms(ticks_t ticks)
117 {
118 return (milliseconds_t)((ticks * PLATFORM_TIMER_FREQ_TO_MS_MULT) /
119 PLATFORM_TIMER_MS_TO_FREQ_MULT);
120 }
121
122 void
platform_timer_handle_boot_cpu_cold_init(void)123 platform_timer_handle_boot_cpu_cold_init(void)
124 {
125 CNTFRQ_EL0_t cntfrq = register_CNTFRQ_EL0_read();
126 assert(CNTFRQ_EL0_get_ClockFrequency(&cntfrq) ==
127 PLATFORM_ARCH_TIMER_FREQ);
128
129 #if !defined(IRQ_NULL)
130 if (hyp_timer_hwirq != NULL) {
131 irq_enable_local(hyp_timer_hwirq);
132 }
133 #endif
134 }
135
136 #if !defined(IRQ_NULL)
137 void
platform_timer_handle_boot_hypervisor_start(void)138 platform_timer_handle_boot_hypervisor_start(void)
139 {
140 // Create the hyp arch timer IRQ
141 hwirq_create_t params = {
142 .irq = PLATFORM_HYP_ARCH_TIMER_IRQ,
143 .action = HWIRQ_ACTION_HYP_TIMER,
144 };
145
146 hwirq_ptr_result_t ret =
147 partition_allocate_hwirq(partition_get_private(), params);
148
149 if (ret.e != OK) {
150 panic("Failed to create Hyp Timer IRQ");
151 }
152
153 if (object_activate_hwirq(ret.r) != OK) {
154 panic("Failed to activate Hyp Timer IRQ");
155 }
156
157 hyp_timer_hwirq = ret.r;
158
159 irq_enable_local(hyp_timer_hwirq);
160 }
161
162 bool
platform_timer_handle_irq_received(void)163 platform_timer_handle_irq_received(void)
164 {
165 trigger_platform_timer_expiry_event();
166
167 return true;
168 }
169
170 void
platform_timer_ndelay(nanoseconds_t duration)171 platform_timer_ndelay(nanoseconds_t duration)
172 {
173 ticks_t cur_ticks = platform_timer_get_current_ticks();
174 ticks_t duration_ticks = platform_timer_convert_ns_to_ticks(duration);
175 ticks_t target_ticks = cur_ticks + duration_ticks;
176
177 // NOTE: assume we don't have overflow case since it covers huge range.
178 // And assumes the timer is always enabled/configured correctly.
179 while (platform_timer_get_current_ticks() < target_ticks) {
180 // Wait for the delay period
181 }
182 }
183 #endif
184