]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Library/ArmArchTimerLib/ArmArchTimerLib.c
ArmPkg: Apply uncrustify changes
[mirror_edk2.git] / ArmPkg / Library / ArmArchTimerLib / ArmArchTimerLib.c
1 /** @file
2 Generic ARM implementation of TimerLib.h
3
4 Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.<BR>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include <Base.h>
11 #include <Library/ArmLib.h>
12 #include <Library/BaseLib.h>
13 #include <Library/TimerLib.h>
14 #include <Library/DebugLib.h>
15 #include <Library/PcdLib.h>
16 #include <Library/ArmGenericTimerCounterLib.h>
17
18 #define TICKS_PER_MICRO_SEC (PcdGet32 (PcdArmArchTimerFreqInHz)/1000000U)
19
20 // Select appropriate multiply function for platform architecture.
21 #ifdef MDE_CPU_ARM
22 #define MULT_U64_X_N MultU64x32
23 #else
24 #define MULT_U64_X_N MultU64x64
25 #endif
26
27 RETURN_STATUS
28 EFIAPI
29 TimerConstructor (
30 VOID
31 )
32 {
33 //
34 // Check if the ARM Generic Timer Extension is implemented.
35 //
36 if (ArmIsArchTimerImplemented ()) {
37 //
38 // Check if Architectural Timer frequency is pre-determined by the platform
39 // (ie. nonzero).
40 //
41 if (PcdGet32 (PcdArmArchTimerFreqInHz) != 0) {
42 //
43 // Check if ticks/uS is not 0. The Architectural timer runs at constant
44 // frequency, irrespective of CPU frequency. According to Generic Timer
45 // Ref manual, lower bound of the frequency is in the range of 1-10MHz.
46 //
47 ASSERT (TICKS_PER_MICRO_SEC);
48
49 #ifdef MDE_CPU_ARM
50 //
51 // Only set the frequency for ARMv7. We expect the secure firmware to
52 // have already done it.
53 // If the security extension is not implemented, set Timer Frequency
54 // here.
55 //
56 if (ArmHasSecurityExtensions ()) {
57 ArmGenericTimerSetTimerFreq (PcdGet32 (PcdArmArchTimerFreqInHz));
58 }
59
60 #endif
61 }
62
63 //
64 // Architectural Timer Frequency must be set in Secure privileged
65 // mode (if secure extension is supported).
66 // If the reset value (0) is returned, just ASSERT.
67 //
68 ASSERT (ArmGenericTimerGetTimerFreq () != 0);
69 } else {
70 DEBUG ((DEBUG_ERROR, "ARM Architectural Timer is not available in the CPU, hence this library cannot be used.\n"));
71 ASSERT (0);
72 }
73
74 return RETURN_SUCCESS;
75 }
76
77 /**
78 A local utility function that returns the PCD value, if specified.
79 Otherwise it defaults to ArmGenericTimerGetTimerFreq.
80
81 @return The timer frequency.
82
83 **/
84 STATIC
85 UINTN
86 EFIAPI
87 GetPlatformTimerFreq (
88 )
89 {
90 UINTN TimerFreq;
91
92 TimerFreq = PcdGet32 (PcdArmArchTimerFreqInHz);
93 if (TimerFreq == 0) {
94 TimerFreq = ArmGenericTimerGetTimerFreq ();
95 }
96
97 return TimerFreq;
98 }
99
100 /**
101 Stalls the CPU for the number of microseconds specified by MicroSeconds.
102
103 @param MicroSeconds The minimum number of microseconds to delay.
104
105 @return The value of MicroSeconds input.
106
107 **/
108 UINTN
109 EFIAPI
110 MicroSecondDelay (
111 IN UINTN MicroSeconds
112 )
113 {
114 UINT64 TimerTicks64;
115 UINT64 SystemCounterVal;
116
117 // Calculate counter ticks that represent requested delay:
118 // = MicroSeconds x TICKS_PER_MICRO_SEC
119 // = MicroSeconds x Frequency.10^-6
120 TimerTicks64 = DivU64x32 (
121 MULT_U64_X_N (
122 MicroSeconds,
123 GetPlatformTimerFreq ()
124 ),
125 1000000U
126 );
127
128 // Read System Counter value
129 SystemCounterVal = ArmGenericTimerGetSystemCount ();
130
131 TimerTicks64 += SystemCounterVal;
132
133 // Wait until delay count expires.
134 while (SystemCounterVal < TimerTicks64) {
135 SystemCounterVal = ArmGenericTimerGetSystemCount ();
136 }
137
138 return MicroSeconds;
139 }
140
141 /**
142 Stalls the CPU for at least the given number of nanoseconds.
143
144 Stalls the CPU for the number of nanoseconds specified by NanoSeconds.
145
146 When the timer frequency is 1MHz, each tick corresponds to 1 microsecond.
147 Therefore, the nanosecond delay will be rounded up to the nearest 1 microsecond.
148
149 @param NanoSeconds The minimum number of nanoseconds to delay.
150
151 @return The value of NanoSeconds inputted.
152
153 **/
154 UINTN
155 EFIAPI
156 NanoSecondDelay (
157 IN UINTN NanoSeconds
158 )
159 {
160 UINTN MicroSeconds;
161
162 // Round up to 1us Tick Number
163 MicroSeconds = NanoSeconds / 1000;
164 MicroSeconds += ((NanoSeconds % 1000) == 0) ? 0 : 1;
165
166 MicroSecondDelay (MicroSeconds);
167
168 return NanoSeconds;
169 }
170
171 /**
172 Retrieves the current value of a 64-bit free running performance counter.
173
174 The counter can either count up by 1 or count down by 1. If the physical
175 performance counter counts by a larger increment, then the counter values
176 must be translated. The properties of the counter can be retrieved from
177 GetPerformanceCounterProperties().
178
179 @return The current value of the free running performance counter.
180
181 **/
182 UINT64
183 EFIAPI
184 GetPerformanceCounter (
185 VOID
186 )
187 {
188 // Just return the value of system count
189 return ArmGenericTimerGetSystemCount ();
190 }
191
192 /**
193 Retrieves the 64-bit frequency in Hz and the range of performance counter
194 values.
195
196 If StartValue is not NULL, then the value that the performance counter starts
197 with immediately after is it rolls over is returned in StartValue. If
198 EndValue is not NULL, then the value that the performance counter end with
199 immediately before it rolls over is returned in EndValue. The 64-bit
200 frequency of the performance counter in Hz is always returned. If StartValue
201 is less than EndValue, then the performance counter counts up. If StartValue
202 is greater than EndValue, then the performance counter counts down. For
203 example, a 64-bit free running counter that counts up would have a StartValue
204 of 0 and an EndValue of 0xFFFFFFFFFFFFFFFF. A 24-bit free running counter
205 that counts down would have a StartValue of 0xFFFFFF and an EndValue of 0.
206
207 @param StartValue The value the performance counter starts with when it
208 rolls over.
209 @param EndValue The value that the performance counter ends with before
210 it rolls over.
211
212 @return The frequency in Hz.
213
214 **/
215 UINT64
216 EFIAPI
217 GetPerformanceCounterProperties (
218 OUT UINT64 *StartValue OPTIONAL,
219 OUT UINT64 *EndValue OPTIONAL
220 )
221 {
222 if (StartValue != NULL) {
223 // Timer starts at 0
224 *StartValue = (UINT64)0ULL;
225 }
226
227 if (EndValue != NULL) {
228 // Timer counts up.
229 *EndValue = 0xFFFFFFFFFFFFFFFFUL;
230 }
231
232 return (UINT64)ArmGenericTimerGetTimerFreq ();
233 }
234
235 /**
236 Converts elapsed ticks of performance counter to time in nanoseconds.
237
238 This function converts the elapsed ticks of running performance counter to
239 time value in unit of nanoseconds.
240
241 @param Ticks The number of elapsed ticks of running performance counter.
242
243 @return The elapsed time in nanoseconds.
244
245 **/
246 UINT64
247 EFIAPI
248 GetTimeInNanoSecond (
249 IN UINT64 Ticks
250 )
251 {
252 UINT64 NanoSeconds;
253 UINT32 Remainder;
254 UINT32 TimerFreq;
255
256 TimerFreq = GetPlatformTimerFreq ();
257 //
258 // Ticks
259 // Time = --------- x 1,000,000,000
260 // Frequency
261 //
262 NanoSeconds = MULT_U64_X_N (
263 DivU64x32Remainder (
264 Ticks,
265 TimerFreq,
266 &Remainder
267 ),
268 1000000000U
269 );
270
271 //
272 // Frequency < 0x100000000, so Remainder < 0x100000000, then (Remainder * 1,000,000,000)
273 // will not overflow 64-bit.
274 //
275 NanoSeconds += DivU64x32 (
276 MULT_U64_X_N (
277 (UINT64)Remainder,
278 1000000000U
279 ),
280 TimerFreq
281 );
282
283 return NanoSeconds;
284 }