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