1 /** @file
2   ACPI Timer implements one instance of Timer Library.
3 
4   Copyright (c) 2013 - 2015, 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 /**
25   Internal function to retrieves the 64-bit frequency in Hz.
26 
27   Internal function to retrieves the 64-bit frequency in Hz.
28 
29   @return The frequency in Hz.
30 
31 **/
32 UINT64
33 InternalGetPerformanceCounterFrequency (
34   VOID
35   );
36 
37 /**
38   The constructor function enables ACPI IO space.
39 
40   If ACPI I/O space not enabled, this function will enable it.
41   It will always return RETURN_SUCCESS.
42 
43   @retval EFI_SUCCESS   The constructor always returns RETURN_SUCCESS.
44 
45 **/
46 RETURN_STATUS
47 EFIAPI
AcpiTimerLibConstructor(VOID)48 AcpiTimerLibConstructor (
49   VOID
50   )
51 {
52   UINTN   Bus;
53   UINTN   Device;
54   UINTN   Function;
55   UINTN   EnableRegister;
56   UINT8   EnableMask;
57 
58   //
59   // ASSERT for the invalid PCD values. They must be configured to the real value.
60   //
61   ASSERT (PcdGet16 (PcdAcpiIoPciBarRegisterOffset) != 0xFFFF);
62   ASSERT (PcdGet16 (PcdAcpiIoPortBaseAddress)      != 0xFFFF);
63 
64   //
65   // If the register offset to the BAR for the ACPI I/O Port Base Address is 0x0000, then
66   // no PCI register programming is required to enable access to the the ACPI registers
67   // specified by PcdAcpiIoPortBaseAddress
68   //
69   if (PcdGet16 (PcdAcpiIoPciBarRegisterOffset) == 0x0000) {
70     return RETURN_SUCCESS;
71   }
72 
73   //
74   // ASSERT for the invalid PCD values. They must be configured to the real value.
75   //
76   ASSERT (PcdGet8  (PcdAcpiIoPciDeviceNumber)   != 0xFF);
77   ASSERT (PcdGet8  (PcdAcpiIoPciFunctionNumber) != 0xFF);
78   ASSERT (PcdGet16 (PcdAcpiIoPciEnableRegisterOffset) != 0xFFFF);
79 
80   //
81   // Retrieve the PCD values for the PCI configuration space required to program the ACPI I/O Port Base Address
82   //
83   Bus            = PcdGet8  (PcdAcpiIoPciBusNumber);
84   Device         = PcdGet8  (PcdAcpiIoPciDeviceNumber);
85   Function       = PcdGet8  (PcdAcpiIoPciFunctionNumber);
86   EnableRegister = PcdGet16 (PcdAcpiIoPciEnableRegisterOffset);
87   EnableMask     = PcdGet8  (PcdAcpiIoBarEnableMask);
88 
89   //
90   // If ACPI I/O space is not enabled yet, program ACPI I/O base address and enable it.
91   //
92   if ((PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, EnableRegister) & EnableMask) != EnableMask)) {
93     PciWrite16 (
94       PCI_LIB_ADDRESS (Bus, Device, Function, PcdGet16 (PcdAcpiIoPciBarRegisterOffset)),
95       PcdGet16 (PcdAcpiIoPortBaseAddress)
96       );
97     PciOr8 (
98       PCI_LIB_ADDRESS (Bus, Device, Function, EnableRegister),
99       EnableMask
100       );
101   }
102 
103   return RETURN_SUCCESS;
104 }
105 
106 /**
107   Internal function to retrieve the ACPI I/O Port Base Address.
108 
109   Internal function to retrieve the ACPI I/O Port Base Address.
110 
111   @return The 16-bit ACPI I/O Port Base Address.
112 
113 **/
114 UINT16
InternalAcpiGetAcpiTimerIoPort(VOID)115 InternalAcpiGetAcpiTimerIoPort (
116   VOID
117   )
118 {
119   UINT16  Port;
120 
121   Port = PcdGet16 (PcdAcpiIoPortBaseAddress);
122 
123   //
124   // If the register offset to the BAR for the ACPI I/O Port Base Address is not 0x0000, then
125   // read the PCI register for the ACPI BAR value in case the BAR has been programmed to a
126   // value other than PcdAcpiIoPortBaseAddress
127   //
128   if (PcdGet16 (PcdAcpiIoPciBarRegisterOffset) != 0x0000) {
129     Port = PciRead16 (PCI_LIB_ADDRESS (
130                         PcdGet8  (PcdAcpiIoPciBusNumber),
131                         PcdGet8  (PcdAcpiIoPciDeviceNumber),
132                         PcdGet8  (PcdAcpiIoPciFunctionNumber),
133                         PcdGet16 (PcdAcpiIoPciBarRegisterOffset)
134                         ));
135   }
136 
137   return (Port & PcdGet16 (PcdAcpiIoPortBaseAddressMask)) + PcdGet16 (PcdAcpiPm1TmrOffset);
138 }
139 
140 /**
141   Stalls the CPU for at least the given number of ticks.
142 
143   Stalls the CPU for at least the given number of ticks. It's invoked by
144   MicroSecondDelay() and NanoSecondDelay().
145 
146   @param  Delay     A period of time to delay in ticks.
147 
148 **/
149 VOID
InternalAcpiDelay(IN UINT32 Delay)150 InternalAcpiDelay (
151   IN UINT32  Delay
152   )
153 {
154   UINT16   Port;
155   UINT32   Ticks;
156   UINT32   Times;
157 
158   Port   = InternalAcpiGetAcpiTimerIoPort ();
159   Times  = Delay >> 22;
160   Delay &= BIT22 - 1;
161   do {
162     //
163     // The target timer count is calculated here
164     //
165     Ticks = IoRead32 (Port) + Delay;
166     Delay = BIT22;
167     //
168     // Wait until time out
169     // Delay >= 2^23 could not be handled by this function
170     // Timer wrap-arounds are handled correctly by this function
171     //
172     while (((Ticks - IoRead32 (Port)) & BIT23) == 0) {
173       CpuPause ();
174     }
175   } while (Times-- > 0);
176 }
177 
178 /**
179   Stalls the CPU for at least the given number of microseconds.
180 
181   Stalls the CPU for the number of microseconds specified by MicroSeconds.
182 
183   @param  MicroSeconds  The minimum number of microseconds to delay.
184 
185   @return MicroSeconds
186 
187 **/
188 UINTN
189 EFIAPI
MicroSecondDelay(IN UINTN MicroSeconds)190 MicroSecondDelay (
191   IN UINTN  MicroSeconds
192   )
193 {
194   InternalAcpiDelay (
195     (UINT32)DivU64x32 (
196               MultU64x32 (
197                 MicroSeconds,
198                 ACPI_TIMER_FREQUENCY
199                 ),
200               1000000u
201               )
202     );
203   return MicroSeconds;
204 }
205 
206 /**
207   Stalls the CPU for at least the given number of nanoseconds.
208 
209   Stalls the CPU for the number of nanoseconds specified by NanoSeconds.
210 
211   @param  NanoSeconds The minimum number of nanoseconds to delay.
212 
213   @return NanoSeconds
214 
215 **/
216 UINTN
217 EFIAPI
NanoSecondDelay(IN UINTN NanoSeconds)218 NanoSecondDelay (
219   IN UINTN  NanoSeconds
220   )
221 {
222   InternalAcpiDelay (
223     (UINT32)DivU64x32 (
224               MultU64x32 (
225                 NanoSeconds,
226                 ACPI_TIMER_FREQUENCY
227                 ),
228               1000000000u
229               )
230     );
231   return NanoSeconds;
232 }
233 
234 /**
235   Retrieves the current value of a 64-bit free running performance counter.
236 
237   Retrieves the current value of a 64-bit free running performance counter. The
238   counter can either count up by 1 or count down by 1. If the physical
239   performance counter counts by a larger increment, then the counter values
240   must be translated. The properties of the counter can be retrieved from
241   GetPerformanceCounterProperties().
242 
243   @return The current value of the free running performance counter.
244 
245 **/
246 UINT64
247 EFIAPI
GetPerformanceCounter(VOID)248 GetPerformanceCounter (
249   VOID
250   )
251 {
252   return AsmReadTsc ();
253 }
254 
255 /**
256   Retrieves the 64-bit frequency in Hz and the range of performance counter
257   values.
258 
259   If StartValue is not NULL, then the value that the performance counter starts
260   with immediately after is it rolls over is returned in StartValue. If
261   EndValue is not NULL, then the value that the performance counter end with
262   immediately before it rolls over is returned in EndValue. The 64-bit
263   frequency of the performance counter in Hz is always returned. If StartValue
264   is less than EndValue, then the performance counter counts up. If StartValue
265   is greater than EndValue, then the performance counter counts down. For
266   example, a 64-bit free running counter that counts up would have a StartValue
267   of 0 and an EndValue of 0xFFFFFFFFFFFFFFFF. A 24-bit free running counter
268   that counts down would have a StartValue of 0xFFFFFF and an EndValue of 0.
269 
270   @param  StartValue  The value the performance counter starts with when it
271                       rolls over.
272   @param  EndValue    The value that the performance counter ends with before
273                       it rolls over.
274 
275   @return The frequency in Hz.
276 
277 **/
278 UINT64
279 EFIAPI
GetPerformanceCounterProperties(OUT UINT64 * StartValue,OPTIONAL OUT UINT64 * EndValue OPTIONAL)280 GetPerformanceCounterProperties (
281   OUT UINT64  *StartValue,  OPTIONAL
282   OUT UINT64  *EndValue     OPTIONAL
283   )
284 {
285   if (StartValue != NULL) {
286     *StartValue = 0;
287   }
288 
289   if (EndValue != NULL) {
290     *EndValue = 0xffffffffffffffffULL;
291   }
292   return InternalGetPerformanceCounterFrequency ();
293 }
294 
295 /**
296   Converts elapsed ticks of performance counter to time in nanoseconds.
297 
298   This function converts the elapsed ticks of running performance counter to
299   time value in unit of nanoseconds.
300 
301   @param  Ticks     The number of elapsed ticks of running performance counter.
302 
303   @return The elapsed time in nanoseconds.
304 
305 **/
306 UINT64
307 EFIAPI
GetTimeInNanoSecond(IN UINT64 Ticks)308 GetTimeInNanoSecond (
309   IN UINT64  Ticks
310   )
311 {
312   UINT64  Frequency;
313   UINT64  NanoSeconds;
314   UINT64  Remainder;
315   INTN    Shift;
316 
317   Frequency = GetPerformanceCounterProperties (NULL, NULL);
318 
319   //
320   //          Ticks
321   // Time = --------- x 1,000,000,000
322   //        Frequency
323   //
324   NanoSeconds = MultU64x32 (DivU64x64Remainder (Ticks, Frequency, &Remainder), 1000000000u);
325 
326   //
327   // Ensure (Remainder * 1,000,000,000) will not overflow 64-bit.
328   // Since 2^29 < 1,000,000,000 = 0x3B9ACA00 < 2^30, Remainder should < 2^(64-30) = 2^34,
329   // i.e. highest bit set in Remainder should <= 33.
330   //
331   Shift = MAX (0, HighBitSet64 (Remainder) - 33);
332   Remainder = RShiftU64 (Remainder, (UINTN) Shift);
333   Frequency = RShiftU64 (Frequency, (UINTN) Shift);
334   NanoSeconds += DivU64x64Remainder (MultU64x32 (Remainder, 1000000000u), Frequency, NULL);
335 
336   return NanoSeconds;
337 }
338