]> git.proxmox.com Git - mirror_edk2.git/blobdiff - PcAtChipsetPkg/Library/AcpiTimerLib/AcpiTimerLib.c
PcAtChipsetPkg: new AcpiTimerLib libraries.
[mirror_edk2.git] / PcAtChipsetPkg / Library / AcpiTimerLib / AcpiTimerLib.c
diff --git a/PcAtChipsetPkg/Library/AcpiTimerLib/AcpiTimerLib.c b/PcAtChipsetPkg/Library/AcpiTimerLib/AcpiTimerLib.c
new file mode 100644 (file)
index 0000000..19fdd76
--- /dev/null
@@ -0,0 +1,337 @@
+/** @file\r
+  ACPI Timer implements one instance of Timer Library.\r
+\r
+  Copyright (c) 2013 -2014, Intel Corporation. All rights reserved.<BR>\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Base.h>\r
+#include <Library/TimerLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/PciLib.h>\r
+#include <Library/IoLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <IndustryStandard/Acpi.h>\r
+\r
+/**\r
+  Internal function to retrieves the 64-bit frequency in Hz.\r
+\r
+  Internal function to retrieves the 64-bit frequency in Hz.\r
+\r
+  @return The frequency in Hz.\r
+\r
+**/\r
+UINT64\r
+InternalGetPerformanceCounterFrequency (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  The constructor function enables ACPI IO space.\r
+\r
+  If ACPI I/O space not enabled, this function will enable it.\r
+  It will always return RETURN_SUCCESS.\r
+\r
+  @retval EFI_SUCCESS   The constructor always returns RETURN_SUCCESS.\r
+\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+AcpiTimerLibConstructor (\r
+  VOID\r
+  )\r
+{\r
+  UINTN   Bus;\r
+  UINTN   Device;\r
+  UINTN   Function;\r
+  UINTN   EnableRegister;\r
+  UINT8   EnableMask;\r
+\r
+  //\r
+  // ASSERT for the invalid PCD values. They must be configured to the real value. \r
+  //\r
+  ASSERT (PcdGet16 (PcdAcpiIoPciBarRegisterOffset) != 0xFFFF);\r
+  ASSERT (PcdGet16 (PcdAcpiIoPortBaseAddress)      != 0xFFFF);\r
+\r
+  //\r
+  // If the register offset to the BAR for the ACPI I/O Port Base Address is 0x0000, then \r
+  // no PCI register programming is required to enable access to the the ACPI registers\r
+  // specified by PcdAcpiIoPortBaseAddress\r
+  //\r
+  if (PcdGet16 (PcdAcpiIoPciBarRegisterOffset) == 0x0000) {\r
+    return RETURN_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // ASSERT for the invalid PCD values. They must be configured to the real value. \r
+  //\r
+  ASSERT (PcdGet8  (PcdAcpiIoPciDeviceNumber)   != 0xFF);\r
+  ASSERT (PcdGet8  (PcdAcpiIoPciFunctionNumber) != 0xFF);\r
+  ASSERT (PcdGet16 (PcdAcpiIoPciEnableRegisterOffset) != 0xFFFF);\r
+\r
+  //\r
+  // Retrieve the PCD values for the PCI configuration space required to program the ACPI I/O Port Base Address\r
+  //\r
+  Bus            = PcdGet8  (PcdAcpiIoPciBusNumber);\r
+  Device         = PcdGet8  (PcdAcpiIoPciDeviceNumber);\r
+  Function       = PcdGet8  (PcdAcpiIoPciFunctionNumber);\r
+  EnableRegister = PcdGet16 (PcdAcpiIoPciEnableRegisterOffset);\r
+  EnableMask     = PcdGet8  (PcdAcpiIoBarEnableMask);\r
+\r
+  //\r
+  // If ACPI I/O space is not enabled yet, program ACPI I/O base address and enable it.\r
+  //\r
+  if ((PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, EnableRegister) & EnableMask) != EnableMask)) {\r
+    PciWrite16 (\r
+      PCI_LIB_ADDRESS (Bus, Device, Function, PcdGet16 (PcdAcpiIoPciBarRegisterOffset)),\r
+      PcdGet16 (PcdAcpiIoPortBaseAddress)\r
+      );\r
+    PciOr8 (\r
+      PCI_LIB_ADDRESS (Bus, Device, Function, EnableRegister),\r
+      EnableMask\r
+      );\r
+  }\r
+  \r
+  return RETURN_SUCCESS;\r
+}\r
+\r
+/**\r
+  Internal function to retrieve the ACPI I/O Port Base Address.\r
+\r
+  Internal function to retrieve the ACPI I/O Port Base Address.\r
+\r
+  @return The 16-bit ACPI I/O Port Base Address.\r
+\r
+**/\r
+UINT16\r
+InternalAcpiGetAcpiTimerIoPort (\r
+  VOID\r
+  )\r
+{\r
+  UINT16  Port;\r
+  \r
+  Port = PcdGet16 (PcdAcpiIoPciBarRegisterOffset);\r
+  \r
+  //\r
+  // If the register offset to the BAR for the ACPI I/O Port Base Address is not 0x0000, then \r
+  // read the PCI register for the APCI BAR value in case the BAR has been programmed to a \r
+  // value other than PcdAcpiIoPortBaseAddress\r
+  //\r
+  if (PcdGet16 (PcdAcpiIoPciBarRegisterOffset) != 0x0000) {\r
+    Port = PciRead16 (PCI_LIB_ADDRESS (\r
+                        PcdGet8  (PcdAcpiIoPciBusNumber), \r
+                        PcdGet8  (PcdAcpiIoPciDeviceNumber), \r
+                        PcdGet8  (PcdAcpiIoPciFunctionNumber), \r
+                        PcdGet16 (PcdAcpiIoPciBarRegisterOffset)\r
+                        ));\r
+  }\r
+  \r
+  return (Port & ~BIT0) + PcdGet16 (PcdAcpiPm1TmrOffset);\r
+}\r
+\r
+/**\r
+  Stalls the CPU for at least the given number of ticks.\r
+\r
+  Stalls the CPU for at least the given number of ticks. It's invoked by\r
+  MicroSecondDelay() and NanoSecondDelay().\r
+\r
+  @param  Delay     A period of time to delay in ticks.\r
+\r
+**/\r
+VOID\r
+InternalAcpiDelay (\r
+  IN UINT32  Delay\r
+  )\r
+{\r
+  UINT16   Port;\r
+  UINT32   Ticks;\r
+  UINT32   Times;\r
+\r
+  Port   = InternalAcpiGetAcpiTimerIoPort ();\r
+  Times  = Delay >> 22;\r
+  Delay &= BIT22 - 1;\r
+  do {\r
+    //\r
+    // The target timer count is calculated here\r
+    //\r
+    Ticks = IoRead32 (Port) + Delay;\r
+    Delay = BIT22;\r
+    //\r
+    // Wait until time out\r
+    // Delay >= 2^23 could not be handled by this function\r
+    // Timer wrap-arounds are handled correctly by this function\r
+    //\r
+    while (((Ticks - IoRead32 (Port)) & BIT23) == 0) {\r
+      CpuPause ();\r
+    }\r
+  } while (Times-- > 0);\r
+}\r
+\r
+/**\r
+  Stalls the CPU for at least the given number of microseconds.\r
+\r
+  Stalls the CPU for the number of microseconds specified by MicroSeconds.\r
+\r
+  @param  MicroSeconds  The minimum number of microseconds to delay.\r
+\r
+  @return MicroSeconds\r
+\r
+**/\r
+UINTN\r
+EFIAPI\r
+MicroSecondDelay (\r
+  IN UINTN  MicroSeconds\r
+  )\r
+{\r
+  InternalAcpiDelay (\r
+    (UINT32)DivU64x32 (\r
+              MultU64x32 (\r
+                MicroSeconds,\r
+                ACPI_TIMER_FREQUENCY\r
+                ),\r
+              1000000u\r
+              )\r
+    );\r
+  return MicroSeconds;\r
+}\r
+\r
+/**\r
+  Stalls the CPU for at least the given number of nanoseconds.\r
+\r
+  Stalls the CPU for the number of nanoseconds specified by NanoSeconds.\r
+\r
+  @param  NanoSeconds The minimum number of nanoseconds to delay.\r
+\r
+  @return NanoSeconds\r
+\r
+**/\r
+UINTN\r
+EFIAPI\r
+NanoSecondDelay (\r
+  IN UINTN  NanoSeconds\r
+  )\r
+{\r
+  InternalAcpiDelay (\r
+    (UINT32)DivU64x32 (\r
+              MultU64x32 (\r
+                NanoSeconds,\r
+                ACPI_TIMER_FREQUENCY\r
+                ),\r
+              1000000000u\r
+              )\r
+    );\r
+  return NanoSeconds;\r
+}\r
+\r
+/**\r
+  Retrieves the current value of a 64-bit free running performance counter.\r
+\r
+  Retrieves the current value of a 64-bit free running performance counter. The\r
+  counter can either count up by 1 or count down by 1. If the physical\r
+  performance counter counts by a larger increment, then the counter values\r
+  must be translated. The properties of the counter can be retrieved from\r
+  GetPerformanceCounterProperties().\r
+\r
+  @return The current value of the free running performance counter.\r
+\r
+**/\r
+UINT64\r
+EFIAPI\r
+GetPerformanceCounter (\r
+  VOID\r
+  )\r
+{\r
+  return AsmReadTsc ();\r
+}\r
+\r
+/**\r
+  Retrieves the 64-bit frequency in Hz and the range of performance counter\r
+  values.\r
+\r
+  If StartValue is not NULL, then the value that the performance counter starts\r
+  with immediately after is it rolls over is returned in StartValue. If\r
+  EndValue is not NULL, then the value that the performance counter end with\r
+  immediately before it rolls over is returned in EndValue. The 64-bit\r
+  frequency of the performance counter in Hz is always returned. If StartValue\r
+  is less than EndValue, then the performance counter counts up. If StartValue\r
+  is greater than EndValue, then the performance counter counts down. For\r
+  example, a 64-bit free running counter that counts up would have a StartValue\r
+  of 0 and an EndValue of 0xFFFFFFFFFFFFFFFF. A 24-bit free running counter\r
+  that counts down would have a StartValue of 0xFFFFFF and an EndValue of 0.\r
+\r
+  @param  StartValue  The value the performance counter starts with when it\r
+                      rolls over.\r
+  @param  EndValue    The value that the performance counter ends with before\r
+                      it rolls over.\r
+\r
+  @return The frequency in Hz.\r
+\r
+**/\r
+UINT64\r
+EFIAPI\r
+GetPerformanceCounterProperties (\r
+  OUT UINT64  *StartValue,  OPTIONAL\r
+  OUT UINT64  *EndValue     OPTIONAL\r
+  )\r
+{\r
+  if (StartValue != NULL) {\r
+    *StartValue = 0;\r
+  }\r
+\r
+  if (EndValue != NULL) {\r
+    *EndValue = 0xffffffffffffffffULL;\r
+  }\r
+  return InternalGetPerformanceCounterFrequency ();\r
+}\r
+\r
+/**\r
+  Converts elapsed ticks of performance counter to time in nanoseconds.\r
+\r
+  This function converts the elapsed ticks of running performance counter to\r
+  time value in unit of nanoseconds.\r
+\r
+  @param  Ticks     The number of elapsed ticks of running performance counter.\r
+\r
+  @return The elapsed time in nanoseconds.\r
+\r
+**/\r
+UINT64\r
+EFIAPI\r
+GetTimeInNanoSecond (\r
+  IN UINT64  Ticks\r
+  )\r
+{\r
+  UINT64  Frequency;\r
+  UINT64  NanoSeconds;\r
+  UINT64  Remainder;\r
+  INTN    Shift;\r
+\r
+  Frequency = GetPerformanceCounterProperties (NULL, NULL);\r
+\r
+  //\r
+  //          Ticks\r
+  // Time = --------- x 1,000,000,000\r
+  //        Frequency\r
+  //\r
+  NanoSeconds = MultU64x32 (DivU64x64Remainder (Ticks, Frequency, &Remainder), 1000000000u);\r
+\r
+  //\r
+  // Ensure (Remainder * 1,000,000,000) will not overflow 64-bit.\r
+  // Since 2^29 < 1,000,000,000 = 0x3B9ACA00 < 2^30, Remainder should < 2^(64-30) = 2^34,\r
+  // i.e. highest bit set in Remainder should <= 33.\r
+  //\r
+  Shift = MAX (0, HighBitSet64 (Remainder) - 33);\r
+  Remainder = RShiftU64 (Remainder, (UINTN) Shift);\r
+  Frequency = RShiftU64 (Frequency, (UINTN) Shift);\r
+  NanoSeconds += DivU64x64Remainder (MultU64x32 (Remainder, 1000000000u), Frequency, NULL);\r
+\r
+  return NanoSeconds;\r
+}\r