PL031RealTimeClockLib.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. /** @file
  2. Implement EFI RealTimeClock runtime services via RTC Lib.
  3. Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
  4. Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.<BR>
  5. SPDX-License-Identifier: BSD-2-Clause-Patent
  6. **/
  7. #include <PiDxe.h>
  8. #include <Guid/EventGroup.h>
  9. #include <Guid/GlobalVariable.h>
  10. #include <Library/BaseLib.h>
  11. #include <Library/DebugLib.h>
  12. #include <Library/DxeServicesTableLib.h>
  13. #include <Library/IoLib.h>
  14. #include <Library/MemoryAllocationLib.h>
  15. #include <Library/PcdLib.h>
  16. #include <Library/RealTimeClockLib.h>
  17. #include <Library/TimeBaseLib.h>
  18. #include <Library/UefiBootServicesTableLib.h>
  19. #include <Library/UefiLib.h>
  20. #include <Library/UefiRuntimeServicesTableLib.h>
  21. #include <Library/UefiRuntimeLib.h>
  22. #include <Protocol/RealTimeClock.h>
  23. #include "PL031RealTimeClock.h"
  24. STATIC BOOLEAN mPL031Initialized = FALSE;
  25. STATIC EFI_EVENT mRtcVirtualAddrChangeEvent;
  26. STATIC UINTN mPL031RtcBase;
  27. EFI_STATUS
  28. IdentifyPL031 (
  29. VOID
  30. )
  31. {
  32. EFI_STATUS Status;
  33. // Check if this is a PrimeCell Peripheral
  34. if ( (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID0) != 0x0D)
  35. || (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID1) != 0xF0)
  36. || (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID2) != 0x05)
  37. || (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID3) != 0xB1)) {
  38. Status = EFI_NOT_FOUND;
  39. goto EXIT;
  40. }
  41. // Check if this PrimeCell Peripheral is the PL031 Real Time Clock
  42. if ( (MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID0) != 0x31)
  43. || (MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID1) != 0x10)
  44. || ((MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID2) & 0xF) != 0x04)
  45. || (MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID3) != 0x00)) {
  46. Status = EFI_NOT_FOUND;
  47. goto EXIT;
  48. }
  49. Status = EFI_SUCCESS;
  50. EXIT:
  51. return Status;
  52. }
  53. EFI_STATUS
  54. InitializePL031 (
  55. VOID
  56. )
  57. {
  58. EFI_STATUS Status;
  59. // Prepare the hardware
  60. Status = IdentifyPL031();
  61. if (EFI_ERROR (Status)) {
  62. goto EXIT;
  63. }
  64. // Ensure interrupts are masked. We do not want RTC interrupts in UEFI
  65. if ((MmioRead32 (mPL031RtcBase + PL031_RTC_IMSC_IRQ_MASK_SET_CLEAR_REGISTER) & PL031_SET_IRQ_MASK) != PL031_SET_IRQ_MASK) {
  66. MmioOr32 (mPL031RtcBase + PL031_RTC_IMSC_IRQ_MASK_SET_CLEAR_REGISTER, PL031_SET_IRQ_MASK);
  67. }
  68. // Clear any existing interrupts
  69. if ((MmioRead32 (mPL031RtcBase + PL031_RTC_RIS_RAW_IRQ_STATUS_REGISTER) & PL031_IRQ_TRIGGERED) == PL031_IRQ_TRIGGERED) {
  70. MmioOr32 (mPL031RtcBase + PL031_RTC_ICR_IRQ_CLEAR_REGISTER, PL031_CLEAR_IRQ);
  71. }
  72. // Start the clock counter
  73. if ((MmioRead32 (mPL031RtcBase + PL031_RTC_CR_CONTROL_REGISTER) & PL031_RTC_ENABLED) != PL031_RTC_ENABLED) {
  74. MmioOr32 (mPL031RtcBase + PL031_RTC_CR_CONTROL_REGISTER, PL031_RTC_ENABLED);
  75. }
  76. mPL031Initialized = TRUE;
  77. EXIT:
  78. return Status;
  79. }
  80. /**
  81. Returns the current time and date information, and the time-keeping capabilities
  82. of the hardware platform.
  83. @param Time A pointer to storage to receive a snapshot of the current time.
  84. @param Capabilities An optional pointer to a buffer to receive the real time clock
  85. device's capabilities.
  86. @retval EFI_SUCCESS The operation completed successfully.
  87. @retval EFI_INVALID_PARAMETER Time is NULL.
  88. @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error.
  89. @retval EFI_SECURITY_VIOLATION The time could not be retrieved due to an authentication failure.
  90. **/
  91. EFI_STATUS
  92. EFIAPI
  93. LibGetTime (
  94. OUT EFI_TIME *Time,
  95. OUT EFI_TIME_CAPABILITIES *Capabilities
  96. )
  97. {
  98. EFI_STATUS Status = EFI_SUCCESS;
  99. UINT32 EpochSeconds;
  100. // Ensure Time is a valid pointer
  101. if (Time == NULL) {
  102. return EFI_INVALID_PARAMETER;
  103. }
  104. // Initialize the hardware if not already done
  105. if (!mPL031Initialized) {
  106. Status = InitializePL031 ();
  107. if (EFI_ERROR (Status)) {
  108. return Status;
  109. }
  110. }
  111. EpochSeconds = MmioRead32 (mPL031RtcBase + PL031_RTC_DR_DATA_REGISTER);
  112. // Adjust for the correct time zone
  113. // The timezone setting also reflects the DST setting of the clock
  114. if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) {
  115. EpochSeconds += Time->TimeZone * SEC_PER_MIN;
  116. } else if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) {
  117. // Convert to adjusted time, i.e. spring forwards one hour
  118. EpochSeconds += SEC_PER_HOUR;
  119. }
  120. // Convert from internal 32-bit time to UEFI time
  121. EpochToEfiTime (EpochSeconds, Time);
  122. // Update the Capabilities info
  123. if (Capabilities != NULL) {
  124. // PL031 runs at frequency 1Hz
  125. Capabilities->Resolution = PL031_COUNTS_PER_SECOND;
  126. // Accuracy in ppm multiplied by 1,000,000, e.g. for 50ppm set 50,000,000
  127. Capabilities->Accuracy = (UINT32)PcdGet32 (PcdPL031RtcPpmAccuracy);
  128. // FALSE: Setting the time does not clear the values below the resolution level
  129. Capabilities->SetsToZero = FALSE;
  130. }
  131. return EFI_SUCCESS;
  132. }
  133. /**
  134. Sets the current local time and date information.
  135. @param Time A pointer to the current time.
  136. @retval EFI_SUCCESS The operation completed successfully.
  137. @retval EFI_INVALID_PARAMETER A time field is out of range.
  138. @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error.
  139. **/
  140. EFI_STATUS
  141. EFIAPI
  142. LibSetTime (
  143. IN EFI_TIME *Time
  144. )
  145. {
  146. EFI_STATUS Status;
  147. UINTN EpochSeconds;
  148. // Because the PL031 is a 32-bit counter counting seconds,
  149. // the maximum time span is just over 136 years.
  150. // Time is stored in Unix Epoch format, so it starts in 1970,
  151. // Therefore it can not exceed the year 2106.
  152. if ((Time->Year < 1970) || (Time->Year >= 2106)) {
  153. return EFI_UNSUPPORTED;
  154. }
  155. // Initialize the hardware if not already done
  156. if (!mPL031Initialized) {
  157. Status = InitializePL031 ();
  158. if (EFI_ERROR (Status)) {
  159. return Status;
  160. }
  161. }
  162. EpochSeconds = EfiTimeToEpoch (Time);
  163. // Adjust for the correct time zone, i.e. convert to UTC time zone
  164. // The timezone setting also reflects the DST setting of the clock
  165. if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) {
  166. EpochSeconds -= Time->TimeZone * SEC_PER_MIN;
  167. } else if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) {
  168. // Convert to un-adjusted time, i.e. fall back one hour
  169. EpochSeconds -= SEC_PER_HOUR;
  170. }
  171. // Set the PL031
  172. MmioWrite32 (mPL031RtcBase + PL031_RTC_LR_LOAD_REGISTER, EpochSeconds);
  173. return EFI_SUCCESS;
  174. }
  175. /**
  176. Returns the current wakeup alarm clock setting.
  177. @param Enabled Indicates if the alarm is currently enabled or disabled.
  178. @param Pending Indicates if the alarm signal is pending and requires acknowledgement.
  179. @param Time The current alarm setting.
  180. @retval EFI_SUCCESS The alarm settings were returned.
  181. @retval EFI_INVALID_PARAMETER Any parameter is NULL.
  182. @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error.
  183. **/
  184. EFI_STATUS
  185. EFIAPI
  186. LibGetWakeupTime (
  187. OUT BOOLEAN *Enabled,
  188. OUT BOOLEAN *Pending,
  189. OUT EFI_TIME *Time
  190. )
  191. {
  192. // Not a required feature
  193. return EFI_UNSUPPORTED;
  194. }
  195. /**
  196. Sets the system wakeup alarm clock time.
  197. @param Enabled Enable or disable the wakeup alarm.
  198. @param Time If Enable is TRUE, the time to set the wakeup alarm for.
  199. @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled. If
  200. Enable is FALSE, then the wakeup alarm was disabled.
  201. @retval EFI_INVALID_PARAMETER A time field is out of range.
  202. @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error.
  203. @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
  204. **/
  205. EFI_STATUS
  206. EFIAPI
  207. LibSetWakeupTime (
  208. IN BOOLEAN Enabled,
  209. OUT EFI_TIME *Time
  210. )
  211. {
  212. // Not a required feature
  213. return EFI_UNSUPPORTED;
  214. }
  215. /**
  216. Fixup internal data so that EFI can be call in virtual mode.
  217. Call the passed in Child Notify event and convert any pointers in
  218. lib to virtual mode.
  219. @param[in] Event The Event that is being processed
  220. @param[in] Context Event Context
  221. **/
  222. VOID
  223. EFIAPI
  224. LibRtcVirtualNotifyEvent (
  225. IN EFI_EVENT Event,
  226. IN VOID *Context
  227. )
  228. {
  229. //
  230. // Only needed if you are going to support the OS calling RTC functions in virtual mode.
  231. // You will need to call EfiConvertPointer (). To convert any stored physical addresses
  232. // to virtual address. After the OS transitions to calling in virtual mode, all future
  233. // runtime calls will be made in virtual mode.
  234. //
  235. EfiConvertPointer (0x0, (VOID**)&mPL031RtcBase);
  236. return;
  237. }
  238. /**
  239. This is the declaration of an EFI image entry point. This can be the entry point to an application
  240. written to this specification, an EFI boot service driver, or an EFI runtime driver.
  241. @param ImageHandle Handle that identifies the loaded image.
  242. @param SystemTable System Table for this image.
  243. @retval EFI_SUCCESS The operation completed successfully.
  244. **/
  245. EFI_STATUS
  246. EFIAPI
  247. LibRtcInitialize (
  248. IN EFI_HANDLE ImageHandle,
  249. IN EFI_SYSTEM_TABLE *SystemTable
  250. )
  251. {
  252. EFI_STATUS Status;
  253. EFI_HANDLE Handle;
  254. // Initialize RTC Base Address
  255. mPL031RtcBase = PcdGet32 (PcdPL031RtcBase);
  256. // Declare the controller as EFI_MEMORY_RUNTIME
  257. Status = gDS->AddMemorySpace (
  258. EfiGcdMemoryTypeMemoryMappedIo,
  259. mPL031RtcBase, SIZE_4KB,
  260. EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
  261. );
  262. if (EFI_ERROR (Status)) {
  263. return Status;
  264. }
  265. Status = gDS->SetMemorySpaceAttributes (mPL031RtcBase, SIZE_4KB, EFI_MEMORY_UC | EFI_MEMORY_RUNTIME);
  266. if (EFI_ERROR (Status)) {
  267. return Status;
  268. }
  269. // Install the protocol
  270. Handle = NULL;
  271. Status = gBS->InstallMultipleProtocolInterfaces (
  272. &Handle,
  273. &gEfiRealTimeClockArchProtocolGuid, NULL,
  274. NULL
  275. );
  276. ASSERT_EFI_ERROR (Status);
  277. //
  278. // Register for the virtual address change event
  279. //
  280. Status = gBS->CreateEventEx (
  281. EVT_NOTIFY_SIGNAL,
  282. TPL_NOTIFY,
  283. LibRtcVirtualNotifyEvent,
  284. NULL,
  285. &gEfiEventVirtualAddressChangeGuid,
  286. &mRtcVirtualAddrChangeEvent
  287. );
  288. ASSERT_EFI_ERROR (Status);
  289. return Status;
  290. }