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