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