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