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