]> git.proxmox.com Git - mirror_edk2.git/blob - PcAtChipsetPkg/Library/AcpiTimerLib/AcpiTimerLib.c
PcAtChipsetPkg: Add PeiAcpiTimerLib to save Frequency in HOB
[mirror_edk2.git] / PcAtChipsetPkg / Library / AcpiTimerLib / AcpiTimerLib.c
1 /** @file
2 ACPI Timer implements one instance of Timer Library.
3
4 Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include <Base.h>
16 #include <Library/TimerLib.h>
17 #include <Library/BaseLib.h>
18 #include <Library/PcdLib.h>
19 #include <Library/PciLib.h>
20 #include <Library/IoLib.h>
21 #include <Library/DebugLib.h>
22 #include <IndustryStandard/Acpi.h>
23
24 GUID mFrequencyHobGuid = { 0x3fca54f6, 0xe1a2, 0x4b20, { 0xbe, 0x76, 0x92, 0x6b, 0x4b, 0x48, 0xbf, 0xaa }};
25
26 /**
27 Internal function to retrieves the 64-bit frequency in Hz.
28
29 Internal function to retrieves the 64-bit frequency in Hz.
30
31 @return The frequency in Hz.
32
33 **/
34 UINT64
35 InternalGetPerformanceCounterFrequency (
36 VOID
37 );
38
39 /**
40 The constructor function enables ACPI IO space.
41
42 If ACPI I/O space not enabled, this function will enable it.
43 It will always return RETURN_SUCCESS.
44
45 @retval EFI_SUCCESS The constructor always returns RETURN_SUCCESS.
46
47 **/
48 RETURN_STATUS
49 EFIAPI
50 AcpiTimerLibConstructor (
51 VOID
52 )
53 {
54 UINTN Bus;
55 UINTN Device;
56 UINTN Function;
57 UINTN EnableRegister;
58 UINT8 EnableMask;
59
60 //
61 // ASSERT for the invalid PCD values. They must be configured to the real value.
62 //
63 ASSERT (PcdGet16 (PcdAcpiIoPciBarRegisterOffset) != 0xFFFF);
64 ASSERT (PcdGet16 (PcdAcpiIoPortBaseAddress) != 0xFFFF);
65
66 //
67 // If the register offset to the BAR for the ACPI I/O Port Base Address is 0x0000, then
68 // no PCI register programming is required to enable access to the the ACPI registers
69 // specified by PcdAcpiIoPortBaseAddress
70 //
71 if (PcdGet16 (PcdAcpiIoPciBarRegisterOffset) == 0x0000) {
72 return RETURN_SUCCESS;
73 }
74
75 //
76 // ASSERT for the invalid PCD values. They must be configured to the real value.
77 //
78 ASSERT (PcdGet8 (PcdAcpiIoPciDeviceNumber) != 0xFF);
79 ASSERT (PcdGet8 (PcdAcpiIoPciFunctionNumber) != 0xFF);
80 ASSERT (PcdGet16 (PcdAcpiIoPciEnableRegisterOffset) != 0xFFFF);
81
82 //
83 // Retrieve the PCD values for the PCI configuration space required to program the ACPI I/O Port Base Address
84 //
85 Bus = PcdGet8 (PcdAcpiIoPciBusNumber);
86 Device = PcdGet8 (PcdAcpiIoPciDeviceNumber);
87 Function = PcdGet8 (PcdAcpiIoPciFunctionNumber);
88 EnableRegister = PcdGet16 (PcdAcpiIoPciEnableRegisterOffset);
89 EnableMask = PcdGet8 (PcdAcpiIoBarEnableMask);
90
91 //
92 // If ACPI I/O space is not enabled yet, program ACPI I/O base address and enable it.
93 //
94 if ((PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, EnableRegister)) & EnableMask) != EnableMask) {
95 PciWrite16 (
96 PCI_LIB_ADDRESS (Bus, Device, Function, PcdGet16 (PcdAcpiIoPciBarRegisterOffset)),
97 PcdGet16 (PcdAcpiIoPortBaseAddress)
98 );
99 PciOr8 (
100 PCI_LIB_ADDRESS (Bus, Device, Function, EnableRegister),
101 EnableMask
102 );
103 }
104
105 return RETURN_SUCCESS;
106 }
107
108 /**
109 Internal function to retrieve the ACPI I/O Port Base Address.
110
111 Internal function to retrieve the ACPI I/O Port Base Address.
112
113 @return The 16-bit ACPI I/O Port Base Address.
114
115 **/
116 UINT16
117 InternalAcpiGetAcpiTimerIoPort (
118 VOID
119 )
120 {
121 UINT16 Port;
122
123 Port = PcdGet16 (PcdAcpiIoPortBaseAddress);
124
125 //
126 // If the register offset to the BAR for the ACPI I/O Port Base Address is not 0x0000, then
127 // read the PCI register for the ACPI BAR value in case the BAR has been programmed to a
128 // value other than PcdAcpiIoPortBaseAddress
129 //
130 if (PcdGet16 (PcdAcpiIoPciBarRegisterOffset) != 0x0000) {
131 Port = PciRead16 (PCI_LIB_ADDRESS (
132 PcdGet8 (PcdAcpiIoPciBusNumber),
133 PcdGet8 (PcdAcpiIoPciDeviceNumber),
134 PcdGet8 (PcdAcpiIoPciFunctionNumber),
135 PcdGet16 (PcdAcpiIoPciBarRegisterOffset)
136 ));
137 }
138
139 return (Port & PcdGet16 (PcdAcpiIoPortBaseAddressMask)) + PcdGet16 (PcdAcpiPm1TmrOffset);
140 }
141
142 /**
143 Stalls the CPU for at least the given number of ticks.
144
145 Stalls the CPU for at least the given number of ticks. It's invoked by
146 MicroSecondDelay() and NanoSecondDelay().
147
148 @param Delay A period of time to delay in ticks.
149
150 **/
151 VOID
152 InternalAcpiDelay (
153 IN UINT32 Delay
154 )
155 {
156 UINT16 Port;
157 UINT32 Ticks;
158 UINT32 Times;
159
160 Port = InternalAcpiGetAcpiTimerIoPort ();
161 Times = Delay >> 22;
162 Delay &= BIT22 - 1;
163 do {
164 //
165 // The target timer count is calculated here
166 //
167 Ticks = IoBitFieldRead32 (Port, 0, 23) + Delay;
168 Delay = BIT22;
169 //
170 // Wait until time out
171 // Delay >= 2^23 could not be handled by this function
172 // Timer wrap-arounds are handled correctly by this function
173 //
174 while (((Ticks - IoBitFieldRead32 (Port, 0, 23)) & BIT23) == 0) {
175 CpuPause ();
176 }
177 } while (Times-- > 0);
178 }
179
180 /**
181 Stalls the CPU for at least the given number of microseconds.
182
183 Stalls the CPU for the number of microseconds specified by MicroSeconds.
184
185 @param MicroSeconds The minimum number of microseconds to delay.
186
187 @return MicroSeconds
188
189 **/
190 UINTN
191 EFIAPI
192 MicroSecondDelay (
193 IN UINTN MicroSeconds
194 )
195 {
196 InternalAcpiDelay (
197 (UINT32)DivU64x32 (
198 MultU64x32 (
199 MicroSeconds,
200 ACPI_TIMER_FREQUENCY
201 ),
202 1000000u
203 )
204 );
205 return MicroSeconds;
206 }
207
208 /**
209 Stalls the CPU for at least the given number of nanoseconds.
210
211 Stalls the CPU for the number of nanoseconds specified by NanoSeconds.
212
213 @param NanoSeconds The minimum number of nanoseconds to delay.
214
215 @return NanoSeconds
216
217 **/
218 UINTN
219 EFIAPI
220 NanoSecondDelay (
221 IN UINTN NanoSeconds
222 )
223 {
224 InternalAcpiDelay (
225 (UINT32)DivU64x32 (
226 MultU64x32 (
227 NanoSeconds,
228 ACPI_TIMER_FREQUENCY
229 ),
230 1000000000u
231 )
232 );
233 return NanoSeconds;
234 }
235
236 /**
237 Retrieves the current value of a 64-bit free running performance counter.
238
239 Retrieves the current value of a 64-bit free running performance counter. The
240 counter can either count up by 1 or count down by 1. If the physical
241 performance counter counts by a larger increment, then the counter values
242 must be translated. The properties of the counter can be retrieved from
243 GetPerformanceCounterProperties().
244
245 @return The current value of the free running performance counter.
246
247 **/
248 UINT64
249 EFIAPI
250 GetPerformanceCounter (
251 VOID
252 )
253 {
254 return AsmReadTsc ();
255 }
256
257 /**
258 Retrieves the 64-bit frequency in Hz and the range of performance counter
259 values.
260
261 If StartValue is not NULL, then the value that the performance counter starts
262 with immediately after is it rolls over is returned in StartValue. If
263 EndValue is not NULL, then the value that the performance counter end with
264 immediately before it rolls over is returned in EndValue. The 64-bit
265 frequency of the performance counter in Hz is always returned. If StartValue
266 is less than EndValue, then the performance counter counts up. If StartValue
267 is greater than EndValue, then the performance counter counts down. For
268 example, a 64-bit free running counter that counts up would have a StartValue
269 of 0 and an EndValue of 0xFFFFFFFFFFFFFFFF. A 24-bit free running counter
270 that counts down would have a StartValue of 0xFFFFFF and an EndValue of 0.
271
272 @param StartValue The value the performance counter starts with when it
273 rolls over.
274 @param EndValue The value that the performance counter ends with before
275 it rolls over.
276
277 @return The frequency in Hz.
278
279 **/
280 UINT64
281 EFIAPI
282 GetPerformanceCounterProperties (
283 OUT UINT64 *StartValue, OPTIONAL
284 OUT UINT64 *EndValue OPTIONAL
285 )
286 {
287 if (StartValue != NULL) {
288 *StartValue = 0;
289 }
290
291 if (EndValue != NULL) {
292 *EndValue = 0xffffffffffffffffULL;
293 }
294 return InternalGetPerformanceCounterFrequency ();
295 }
296
297 /**
298 Converts elapsed ticks of performance counter to time in nanoseconds.
299
300 This function converts the elapsed ticks of running performance counter to
301 time value in unit of nanoseconds.
302
303 @param Ticks The number of elapsed ticks of running performance counter.
304
305 @return The elapsed time in nanoseconds.
306
307 **/
308 UINT64
309 EFIAPI
310 GetTimeInNanoSecond (
311 IN UINT64 Ticks
312 )
313 {
314 UINT64 Frequency;
315 UINT64 NanoSeconds;
316 UINT64 Remainder;
317 INTN Shift;
318
319 Frequency = GetPerformanceCounterProperties (NULL, NULL);
320
321 //
322 // Ticks
323 // Time = --------- x 1,000,000,000
324 // Frequency
325 //
326 NanoSeconds = MultU64x32 (DivU64x64Remainder (Ticks, Frequency, &Remainder), 1000000000u);
327
328 //
329 // Ensure (Remainder * 1,000,000,000) will not overflow 64-bit.
330 // Since 2^29 < 1,000,000,000 = 0x3B9ACA00 < 2^30, Remainder should < 2^(64-30) = 2^34,
331 // i.e. highest bit set in Remainder should <= 33.
332 //
333 Shift = MAX (0, HighBitSet64 (Remainder) - 33);
334 Remainder = RShiftU64 (Remainder, (UINTN) Shift);
335 Frequency = RShiftU64 (Frequency, (UINTN) Shift);
336 NanoSeconds += DivU64x64Remainder (MultU64x32 (Remainder, 1000000000u), Frequency, NULL);
337
338 return NanoSeconds;
339 }
340
341 /**
342 Calculate TSC frequency.
343
344 The TSC counting frequency is determined by comparing how far it counts
345 during a 101.4 us period as determined by the ACPI timer.
346 The ACPI timer is used because it counts at a known frequency.
347 The TSC is sampled, followed by waiting 363 counts of the ACPI timer,
348 or 101.4 us. The TSC is then sampled again. The difference multiplied by
349 9861 is the TSC frequency. There will be a small error because of the
350 overhead of reading the ACPI timer. An attempt is made to determine and
351 compensate for this error.
352
353 @return The number of TSC counts per second.
354
355 **/
356 UINT64
357 InternalCalculateTscFrequency (
358 VOID
359 )
360 {
361 UINT64 StartTSC;
362 UINT64 EndTSC;
363 UINT16 TimerAddr;
364 UINT32 Ticks;
365 UINT64 TscFrequency;
366 BOOLEAN InterruptState;
367
368 InterruptState = SaveAndDisableInterrupts ();
369
370 TimerAddr = InternalAcpiGetAcpiTimerIoPort ();
371 //
372 // Compute the number of ticks to wait to measure TSC frequency.
373 // Use 363 * 9861 = 3579543 Hz which is within 2 Hz of ACPI_TIMER_FREQUENCY.
374 // 363 counts is a calibration time of 101.4 uS.
375 //
376 Ticks = IoBitFieldRead32 (TimerAddr, 0, 23) + 363;
377
378 StartTSC = AsmReadTsc (); // Get base value for the TSC
379 //
380 // Wait until the ACPI timer has counted 101.4 us.
381 // Timer wrap-arounds are handled correctly by this function.
382 // When the current ACPI timer value is greater than 'Ticks',
383 // the while loop will exit.
384 //
385 while (((Ticks - IoBitFieldRead32 (TimerAddr, 0, 23)) & BIT23) == 0) {
386 CpuPause();
387 }
388 EndTSC = AsmReadTsc (); // TSC value 101.4 us later
389
390 TscFrequency = MultU64x32 (
391 (EndTSC - StartTSC), // Number of TSC counts in 101.4 us
392 9861 // Number of 101.4 us in a second
393 );
394
395 SetInterruptState (InterruptState);
396
397 return TscFrequency;
398 }
399