1 /** @file
2   CPUID Leaf 0x15 for Core Crystal Clock frequency instance of Timer Library.
3 
4   Copyright (c) 2019 Intel Corporation. All rights reserved.<BR>
5   SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include <Base.h>
10 #include <Library/TimerLib.h>
11 #include <Library/BaseLib.h>
12 #include <Library/PcdLib.h>
13 #include <Library/DebugLib.h>
14 #include <Register/Cpuid.h>
15 
16 GUID mCpuCrystalFrequencyHobGuid = { 0xe1ec5ad0, 0x8569, 0x46bd, { 0x8d, 0xcd, 0x3b, 0x9f, 0x6f, 0x45, 0x82, 0x7a } };
17 
18 /**
19   Internal function to retrieves the 64-bit frequency in Hz.
20 
21   Internal function to retrieves the 64-bit frequency in Hz.
22 
23   @return The frequency in Hz.
24 
25 **/
26 UINT64
27 InternalGetPerformanceCounterFrequency (
28   VOID
29   );
30 
31 /**
32   CPUID Leaf 0x15 for Core Crystal Clock Frequency.
33 
34   The TSC counting frequency is determined by using CPUID leaf 0x15. Frequency in MHz = Core XTAL frequency * EBX/EAX.
35   In newer flavors of the CPU, core xtal frequency is returned in ECX or 0 if not supported.
36   @return The number of TSC counts per second.
37 
38 **/
39 UINT64
CpuidCoreClockCalculateTscFrequency(VOID)40 CpuidCoreClockCalculateTscFrequency (
41   VOID
42   )
43 {
44   UINT64                 TscFrequency;
45   UINT64                 CoreXtalFrequency;
46   UINT32                 RegEax;
47   UINT32                 RegEbx;
48   UINT32                 RegEcx;
49 
50   //
51   // Use CPUID leaf 0x15 Time Stamp Counter and Nominal Core Crystal Clock Information
52   // EBX returns 0 if not supported. ECX, if non zero, provides Core Xtal Frequency in hertz.
53   // TSC frequency = (ECX, Core Xtal Frequency) * EBX/EAX.
54   //
55   AsmCpuid (CPUID_TIME_STAMP_COUNTER, &RegEax, &RegEbx, &RegEcx, NULL);
56 
57   //
58   // If EAX or EBX returns 0, the XTAL ratio is not enumerated.
59   //
60   if (RegEax == 0 || RegEbx ==0 ) {
61     ASSERT (RegEax != 0);
62     ASSERT (RegEbx != 0);
63     return 0;
64   }
65   //
66   // If ECX returns 0, the XTAL frequency is not enumerated.
67   // And PcdCpuCoreCrystalClockFrequency defined should base on processor series.
68   //
69   if (RegEcx == 0) {
70     CoreXtalFrequency = PcdGet64 (PcdCpuCoreCrystalClockFrequency);
71   } else {
72     CoreXtalFrequency = (UINT64) RegEcx;
73   }
74 
75   //
76   // Calculate TSC frequency = (ECX, Core Xtal Frequency) * EBX/EAX
77   //
78   TscFrequency = DivU64x32 (MultU64x32 (CoreXtalFrequency, RegEbx) + (UINT64)(RegEax >> 1), RegEax);
79 
80   return TscFrequency;
81 }
82 
83 /**
84   Stalls the CPU for at least the given number of ticks.
85 
86   Stalls the CPU for at least the given number of ticks. It's invoked by
87   MicroSecondDelay() and NanoSecondDelay().
88 
89   @param  Delay     A period of time to delay in ticks.
90 
91 **/
92 VOID
InternalCpuDelay(IN UINT64 Delay)93 InternalCpuDelay (
94   IN UINT64  Delay
95   )
96 {
97   UINT64  Ticks;
98 
99   //
100   // The target timer count is calculated here
101   //
102   Ticks = AsmReadTsc() + Delay;
103 
104   //
105   // Wait until time out
106   // Timer wrap-arounds are NOT handled correctly by this function.
107   // Thus, this function must be called within 10 years of reset since
108   // Intel guarantees a minimum of 10 years before the TSC wraps.
109   //
110   while (AsmReadTsc() <= Ticks) {
111     CpuPause();
112   }
113 }
114 
115 /**
116   Stalls the CPU for at least the given number of microseconds.
117 
118   Stalls the CPU for the number of microseconds specified by MicroSeconds.
119 
120   @param[in]  MicroSeconds  The minimum number of microseconds to delay.
121 
122   @return MicroSeconds
123 
124 **/
125 UINTN
126 EFIAPI
MicroSecondDelay(IN UINTN MicroSeconds)127 MicroSecondDelay (
128   IN UINTN  MicroSeconds
129   )
130 {
131 
132   InternalCpuDelay (
133     DivU64x32 (
134       MultU64x64 (
135         MicroSeconds,
136         InternalGetPerformanceCounterFrequency ()
137         ),
138       1000000u
139     )
140   );
141 
142   return MicroSeconds;
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   @param  NanoSeconds The minimum number of nanoseconds to delay.
151 
152   @return NanoSeconds
153 
154 **/
155 UINTN
156 EFIAPI
NanoSecondDelay(IN UINTN NanoSeconds)157 NanoSecondDelay (
158   IN UINTN  NanoSeconds
159   )
160 {
161 
162   InternalCpuDelay (
163     DivU64x32 (
164       MultU64x64 (
165         NanoSeconds,
166         InternalGetPerformanceCounterFrequency ()
167         ),
168       1000000000u
169     )
170   );
171 
172   return NanoSeconds;
173 }
174 
175 /**
176   Retrieves the current value of a 64-bit free running performance counter.
177 
178   Retrieves the current value of a 64-bit free running performance counter. The
179   counter can either count up by 1 or count down by 1. If the physical
180   performance counter counts by a larger increment, then the counter values
181   must be translated. The properties of the counter can be retrieved from
182   GetPerformanceCounterProperties().
183 
184   @return The current value of the free running performance counter.
185 
186 **/
187 UINT64
188 EFIAPI
GetPerformanceCounter(VOID)189 GetPerformanceCounter (
190   VOID
191   )
192 {
193   return AsmReadTsc ();
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
GetPerformanceCounterProperties(OUT UINT64 * StartValue,OPTIONAL OUT UINT64 * EndValue OPTIONAL)221 GetPerformanceCounterProperties (
222   OUT UINT64  *StartValue,  OPTIONAL
223   OUT UINT64  *EndValue     OPTIONAL
224   )
225 {
226   if (StartValue != NULL) {
227     *StartValue = 0;
228   }
229 
230   if (EndValue != NULL) {
231     *EndValue = 0xffffffffffffffffULL;
232   }
233   return InternalGetPerformanceCounterFrequency ();
234 }
235 
236 /**
237   Converts elapsed ticks of performance counter to time in nanoseconds.
238 
239   This function converts the elapsed ticks of running performance counter to
240   time value in unit of nanoseconds.
241 
242   @param  Ticks     The number of elapsed ticks of running performance counter.
243 
244   @return The elapsed time in nanoseconds.
245 
246 **/
247 UINT64
248 EFIAPI
GetTimeInNanoSecond(IN UINT64 Ticks)249 GetTimeInNanoSecond (
250   IN UINT64  Ticks
251   )
252 {
253   UINT64  Frequency;
254   UINT64  NanoSeconds;
255   UINT64  Remainder;
256   INTN    Shift;
257 
258   Frequency = GetPerformanceCounterProperties (NULL, NULL);
259 
260   //
261   //          Ticks
262   // Time = --------- x 1,000,000,000
263   //        Frequency
264   //
265   NanoSeconds = MultU64x32 (DivU64x64Remainder (Ticks, Frequency, &Remainder), 1000000000u);
266 
267   //
268   // Ensure (Remainder * 1,000,000,000) will not overflow 64-bit.
269   // Since 2^29 < 1,000,000,000 = 0x3B9ACA00 < 2^30, Remainder should < 2^(64-30) = 2^34,
270   // i.e. highest bit set in Remainder should <= 33.
271   //
272   Shift = MAX (0, HighBitSet64 (Remainder) - 33);
273   Remainder = RShiftU64 (Remainder, (UINTN) Shift);
274   Frequency = RShiftU64 (Frequency, (UINTN) Shift);
275   NanoSeconds += DivU64x64Remainder (MultU64x32 (Remainder, 1000000000u), Frequency, NULL);
276 
277   return NanoSeconds;
278 }
279 
280