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