3e4147c58baced9ab6b1ca921b05493ded8c3b2b
[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 <Uefi.h>
18 #include <PiDxe.h>
19 #include <Library/BaseLib.h>
20 #include <Library/DebugLib.h>
21 #include <Library/UefiLib.h>
22 #include <Library/IoLib.h>
23 #include <Library/RealTimeClockLib.h>
24 #include <Library/MemoryAllocationLib.h>
25 #include <Library/PcdLib.h>
26 #include <Library/ArmPlatformSysConfigLib.h>
27 #include <Library/DxeServicesTableLib.h>
28 #include <Library/UefiBootServicesTableLib.h>
29 #include <Library/UefiRuntimeServicesTableLib.h>
30 #include <Library/UefiRuntimeLib.h>
31
32 #include <Protocol/RealTimeClock.h>
33
34 #include <Guid/GlobalVariable.h>
35 #include <Guid/EventGroup.h>
36
37 #include <Drivers/PL031RealTimeClock.h>
38
39 #include <ArmPlatform.h>
40
41 STATIC CONST CHAR16 mTimeZoneVariableName[] = L"PL031RtcTimeZone";
42 STATIC CONST CHAR16 mDaylightVariableName[] = L"PL031RtcDaylight";
43 STATIC BOOLEAN mPL031Initialized = FALSE;
44 STATIC EFI_EVENT mRtcVirtualAddrChangeEvent;
45 STATIC UINTN mPL031RtcBase;
46 STATIC EFI_RUNTIME_SERVICES *mRT;
47
48 EFI_STATUS
49 IdentifyPL031 (
50 VOID
51 )
52 {
53 EFI_STATUS Status;
54
55 // Check if this is a PrimeCell Peripheral
56 if ( (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID0) != 0x0D)
57 || (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID1) != 0xF0)
58 || (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID2) != 0x05)
59 || (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID3) != 0xB1)) {
60 Status = EFI_NOT_FOUND;
61 goto EXIT;
62 }
63
64 // Check if this PrimeCell Peripheral is the PL031 Real Time Clock
65 if ( (MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID0) != 0x31)
66 || (MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID1) != 0x10)
67 || ((MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID2) & 0xF) != 0x04)
68 || (MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID3) != 0x00)) {
69 Status = EFI_NOT_FOUND;
70 goto EXIT;
71 }
72
73 Status = EFI_SUCCESS;
74
75 EXIT:
76 return Status;
77 }
78
79 EFI_STATUS
80 InitializePL031 (
81 VOID
82 )
83 {
84 EFI_STATUS Status;
85
86 // Prepare the hardware
87 Status = IdentifyPL031();
88 if (EFI_ERROR (Status)) {
89 goto EXIT;
90 }
91
92 // Ensure interrupts are masked. We do not want RTC interrupts in UEFI
93 if ((MmioRead32 (mPL031RtcBase + PL031_RTC_IMSC_IRQ_MASK_SET_CLEAR_REGISTER) & PL031_SET_IRQ_MASK) != PL031_SET_IRQ_MASK) {
94 MmioOr32 (mPL031RtcBase + PL031_RTC_IMSC_IRQ_MASK_SET_CLEAR_REGISTER, PL031_SET_IRQ_MASK);
95 }
96
97 // Clear any existing interrupts
98 if ((MmioRead32 (mPL031RtcBase + PL031_RTC_RIS_RAW_IRQ_STATUS_REGISTER) & PL031_IRQ_TRIGGERED) == PL031_IRQ_TRIGGERED) {
99 MmioOr32 (mPL031RtcBase + PL031_RTC_ICR_IRQ_CLEAR_REGISTER, PL031_CLEAR_IRQ);
100 }
101
102 // Start the clock counter
103 if ((MmioRead32 (mPL031RtcBase + PL031_RTC_CR_CONTROL_REGISTER) & PL031_RTC_ENABLED) != PL031_RTC_ENABLED) {
104 MmioOr32 (mPL031RtcBase + PL031_RTC_CR_CONTROL_REGISTER, PL031_RTC_ENABLED);
105 }
106
107 mPL031Initialized = TRUE;
108
109 EXIT:
110 return Status;
111 }
112
113 /**
114 Converts Epoch seconds (elapsed since 1970 JANUARY 01, 00:00:00 UTC) to EFI_TIME
115 **/
116 VOID
117 EpochToEfiTime (
118 IN UINTN EpochSeconds,
119 OUT EFI_TIME *Time
120 )
121 {
122 UINTN a;
123 UINTN b;
124 UINTN c;
125 UINTN d;
126 UINTN g;
127 UINTN j;
128 UINTN m;
129 UINTN y;
130 UINTN da;
131 UINTN db;
132 UINTN dc;
133 UINTN dg;
134 UINTN hh;
135 UINTN mm;
136 UINTN ss;
137 UINTN J;
138
139 J = (EpochSeconds / 86400) + 2440588;
140 j = J + 32044;
141 g = j / 146097;
142 dg = j % 146097;
143 c = (((dg / 36524) + 1) * 3) / 4;
144 dc = dg - (c * 36524);
145 b = dc / 1461;
146 db = dc % 1461;
147 a = (((db / 365) + 1) * 3) / 4;
148 da = db - (a * 365);
149 y = (g * 400) + (c * 100) + (b * 4) + a;
150 m = (((da * 5) + 308) / 153) - 2;
151 d = da - (((m + 4) * 153) / 5) + 122;
152
153 Time->Year = y - 4800 + ((m + 2) / 12);
154 Time->Month = ((m + 2) % 12) + 1;
155 Time->Day = d + 1;
156
157 ss = EpochSeconds % 60;
158 a = (EpochSeconds - ss) / 60;
159 mm = a % 60;
160 b = (a - mm) / 60;
161 hh = b % 24;
162
163 Time->Hour = hh;
164 Time->Minute = mm;
165 Time->Second = ss;
166 Time->Nanosecond = 0;
167
168 }
169
170 /**
171 Converts EFI_TIME to Epoch seconds (elapsed since 1970 JANUARY 01, 00:00:00 UTC)
172 **/
173 UINTN
174 EfiTimeToEpoch (
175 IN EFI_TIME *Time
176 )
177 {
178 UINTN a;
179 UINTN y;
180 UINTN m;
181 UINTN JulianDate; // Absolute Julian Date representation of the supplied Time
182 UINTN EpochDays; // Number of days elapsed since EPOCH_JULIAN_DAY
183 UINTN EpochSeconds;
184
185 a = (14 - Time->Month) / 12 ;
186 y = Time->Year + 4800 - a;
187 m = Time->Month + (12*a) - 3;
188
189 JulianDate = Time->Day + ((153*m + 2)/5) + (365*y) + (y/4) - (y/100) + (y/400) - 32045;
190
191 ASSERT (JulianDate >= EPOCH_JULIAN_DATE);
192 EpochDays = JulianDate - EPOCH_JULIAN_DATE;
193
194 EpochSeconds = (EpochDays * SEC_PER_DAY) + ((UINTN)Time->Hour * SEC_PER_HOUR) + (Time->Minute * SEC_PER_MIN) + Time->Second;
195
196 return EpochSeconds;
197 }
198
199 BOOLEAN
200 IsLeapYear (
201 IN EFI_TIME *Time
202 )
203 {
204 if (Time->Year % 4 == 0) {
205 if (Time->Year % 100 == 0) {
206 if (Time->Year % 400 == 0) {
207 return TRUE;
208 } else {
209 return FALSE;
210 }
211 } else {
212 return TRUE;
213 }
214 } else {
215 return FALSE;
216 }
217 }
218
219 BOOLEAN
220 DayValid (
221 IN EFI_TIME *Time
222 )
223 {
224 INTN DayOfMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
225
226 if (Time->Day < 1 ||
227 Time->Day > DayOfMonth[Time->Month - 1] ||
228 (Time->Month == 2 && (!IsLeapYear (Time) && Time->Day > 28))
229 ) {
230 return FALSE;
231 }
232
233 return TRUE;
234 }
235
236 /**
237 Returns the current time and date information, and the time-keeping capabilities
238 of the hardware platform.
239
240 @param Time A pointer to storage to receive a snapshot of the current time.
241 @param Capabilities An optional pointer to a buffer to receive the real time clock
242 device's capabilities.
243
244 @retval EFI_SUCCESS The operation completed successfully.
245 @retval EFI_INVALID_PARAMETER Time is NULL.
246 @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error.
247 @retval EFI_SECURITY_VIOLATION The time could not be retrieved due to an authentication failure.
248
249 **/
250 EFI_STATUS
251 EFIAPI
252 LibGetTime (
253 OUT EFI_TIME *Time,
254 OUT EFI_TIME_CAPABILITIES *Capabilities
255 )
256 {
257 EFI_STATUS Status = EFI_SUCCESS;
258 UINT32 EpochSeconds;
259 INT16 TimeZone;
260 UINT8 Daylight;
261 UINTN Size;
262
263 // Initialize the hardware if not already done
264 if (!mPL031Initialized) {
265 Status = InitializePL031 ();
266 if (EFI_ERROR (Status)) {
267 goto EXIT;
268 }
269 }
270
271 // Snapshot the time as early in the function call as possible
272 // On some platforms we may have access to a battery backed up hardware clock.
273 // If such RTC exists try to use it first.
274 Status = ArmPlatformSysConfigGet (SYS_CFG_RTC, &EpochSeconds);
275 if (Status == EFI_UNSUPPORTED) {
276 // Battery backed up hardware RTC does not exist, revert to PL031
277 EpochSeconds = MmioRead32 (mPL031RtcBase + PL031_RTC_DR_DATA_REGISTER);
278 Status = EFI_SUCCESS;
279 } else if (EFI_ERROR (Status)) {
280 // Battery backed up hardware RTC exists but could not be read due to error. Abort.
281 goto EXIT;
282 } else {
283 // Battery backed up hardware RTC exists and we read the time correctly from it.
284 // Now sync the PL031 to the new time.
285 MmioWrite32 (mPL031RtcBase + PL031_RTC_LR_LOAD_REGISTER, EpochSeconds);
286 }
287
288 // Ensure Time is a valid pointer
289 if (Time == NULL) {
290 Status = EFI_INVALID_PARAMETER;
291 goto EXIT;
292 }
293
294 // Get the current time zone information from non-volatile storage
295 Size = sizeof (TimeZone);
296 Status = mRT->GetVariable (
297 (CHAR16 *)mTimeZoneVariableName,
298 &gEfiCallerIdGuid,
299 NULL,
300 &Size,
301 (VOID *)&TimeZone
302 );
303
304 if (EFI_ERROR (Status)) {
305 ASSERT(Status != EFI_INVALID_PARAMETER);
306 ASSERT(Status != EFI_BUFFER_TOO_SMALL);
307
308 if (Status != EFI_NOT_FOUND)
309 goto EXIT;
310
311 // The time zone variable does not exist in non-volatile storage, so create it.
312 Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE;
313 // Store it
314 Status = mRT->SetVariable (
315 (CHAR16 *)mTimeZoneVariableName,
316 &gEfiCallerIdGuid,
317 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
318 Size,
319 (VOID *)&(Time->TimeZone)
320 );
321 if (EFI_ERROR (Status)) {
322 DEBUG ((
323 EFI_D_ERROR,
324 "LibGetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",
325 mTimeZoneVariableName,
326 Status
327 ));
328 goto EXIT;
329 }
330 } else {
331 // Got the time zone
332 Time->TimeZone = TimeZone;
333
334 // Check TimeZone bounds: -1440 to 1440 or 2047
335 if (((Time->TimeZone < -1440) || (Time->TimeZone > 1440))
336 && (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE)) {
337 Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE;
338 }
339
340 // Adjust for the correct time zone
341 if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) {
342 EpochSeconds += Time->TimeZone * SEC_PER_MIN;
343 }
344 }
345
346 // Get the current daylight information from non-volatile storage
347 Size = sizeof (Daylight);
348 Status = mRT->GetVariable (
349 (CHAR16 *)mDaylightVariableName,
350 &gEfiCallerIdGuid,
351 NULL,
352 &Size,
353 (VOID *)&Daylight
354 );
355
356 if (EFI_ERROR (Status)) {
357 ASSERT(Status != EFI_INVALID_PARAMETER);
358 ASSERT(Status != EFI_BUFFER_TOO_SMALL);
359
360 if (Status != EFI_NOT_FOUND)
361 goto EXIT;
362
363 // The daylight variable does not exist in non-volatile storage, so create it.
364 Time->Daylight = 0;
365 // Store it
366 Status = mRT->SetVariable (
367 (CHAR16 *)mDaylightVariableName,
368 &gEfiCallerIdGuid,
369 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
370 Size,
371 (VOID *)&(Time->Daylight)
372 );
373 if (EFI_ERROR (Status)) {
374 DEBUG ((
375 EFI_D_ERROR,
376 "LibGetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",
377 mDaylightVariableName,
378 Status
379 ));
380 goto EXIT;
381 }
382 } else {
383 // Got the daylight information
384 Time->Daylight = Daylight;
385
386 // Adjust for the correct period
387 if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) {
388 // Convert to adjusted time, i.e. spring forwards one hour
389 EpochSeconds += SEC_PER_HOUR;
390 }
391 }
392
393 // Convert from internal 32-bit time to UEFI time
394 EpochToEfiTime (EpochSeconds, Time);
395
396 // Update the Capabilities info
397 if (Capabilities != NULL) {
398 // PL031 runs at frequency 1Hz
399 Capabilities->Resolution = PL031_COUNTS_PER_SECOND;
400 // Accuracy in ppm multiplied by 1,000,000, e.g. for 50ppm set 50,000,000
401 Capabilities->Accuracy = (UINT32)PcdGet32 (PcdPL031RtcPpmAccuracy);
402 // FALSE: Setting the time does not clear the values below the resolution level
403 Capabilities->SetsToZero = FALSE;
404 }
405
406 EXIT:
407 return Status;
408 }
409
410
411 /**
412 Sets the current local time and date information.
413
414 @param Time A pointer to the current time.
415
416 @retval EFI_SUCCESS The operation completed successfully.
417 @retval EFI_INVALID_PARAMETER A time field is out of range.
418 @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error.
419
420 **/
421 EFI_STATUS
422 EFIAPI
423 LibSetTime (
424 IN EFI_TIME *Time
425 )
426 {
427 EFI_STATUS Status;
428 UINTN EpochSeconds;
429
430 // Check the input parameters are within the range specified by UEFI
431 if ((Time->Year < 1900) ||
432 (Time->Year > 9999) ||
433 (Time->Month < 1 ) ||
434 (Time->Month > 12 ) ||
435 (!DayValid (Time) ) ||
436 (Time->Hour > 23 ) ||
437 (Time->Minute > 59 ) ||
438 (Time->Second > 59 ) ||
439 (Time->Nanosecond > 999999999) ||
440 (!((Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) || ((Time->TimeZone >= -1440) && (Time->TimeZone <= 1440)))) ||
441 (Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT)))
442 ) {
443 Status = EFI_INVALID_PARAMETER;
444 goto EXIT;
445 }
446
447 // Because the PL031 is a 32-bit counter counting seconds,
448 // the maximum time span is just over 136 years.
449 // Time is stored in Unix Epoch format, so it starts in 1970,
450 // Therefore it can not exceed the year 2106.
451 if ((Time->Year < 1970) || (Time->Year >= 2106)) {
452 Status = EFI_UNSUPPORTED;
453 goto EXIT;
454 }
455
456 // Initialize the hardware if not already done
457 if (!mPL031Initialized) {
458 Status = InitializePL031 ();
459 if (EFI_ERROR (Status)) {
460 goto EXIT;
461 }
462 }
463
464 EpochSeconds = EfiTimeToEpoch (Time);
465
466 // Adjust for the correct time zone, i.e. convert to UTC time zone
467 if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) {
468 EpochSeconds -= Time->TimeZone * SEC_PER_MIN;
469 }
470
471 // TODO: Automatic Daylight activation
472
473 // Adjust for the correct period
474 if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) {
475 // Convert to un-adjusted time, i.e. fall back one hour
476 EpochSeconds -= SEC_PER_HOUR;
477 }
478
479 // On some platforms we may have access to a battery backed up hardware clock.
480 //
481 // If such RTC exists then it must be updated first, before the PL031,
482 // to minimise any time drift. This is important because the battery backed-up
483 // RTC maintains the master time for the platform across reboots.
484 //
485 // If such RTC does not exist then the following function returns UNSUPPORTED.
486 Status = ArmPlatformSysConfigSet (SYS_CFG_RTC, EpochSeconds);
487 if ((EFI_ERROR (Status)) && (Status != EFI_UNSUPPORTED)){
488 // Any status message except SUCCESS and UNSUPPORTED indicates a hardware failure.
489 goto EXIT;
490 }
491
492
493 // Set the PL031
494 MmioWrite32 (mPL031RtcBase + PL031_RTC_LR_LOAD_REGISTER, EpochSeconds);
495
496 // The accesses to Variable Services can be very slow, because we may be writing to Flash.
497 // Do this after having set the RTC.
498
499 // Save the current time zone information into non-volatile storage
500 Status = mRT->SetVariable (
501 (CHAR16 *)mTimeZoneVariableName,
502 &gEfiCallerIdGuid,
503 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
504 sizeof (Time->TimeZone),
505 (VOID *)&(Time->TimeZone)
506 );
507 if (EFI_ERROR (Status)) {
508 DEBUG ((
509 EFI_D_ERROR,
510 "LibSetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",
511 mTimeZoneVariableName,
512 Status
513 ));
514 goto EXIT;
515 }
516
517 // Save the current daylight information into non-volatile storage
518 Status = mRT->SetVariable (
519 (CHAR16 *)mDaylightVariableName,
520 &gEfiCallerIdGuid,
521 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
522 sizeof(Time->Daylight),
523 (VOID *)&(Time->Daylight)
524 );
525 if (EFI_ERROR (Status)) {
526 DEBUG ((
527 EFI_D_ERROR,
528 "LibSetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",
529 mDaylightVariableName,
530 Status
531 ));
532 goto EXIT;
533 }
534
535 EXIT:
536 return Status;
537 }
538
539
540 /**
541 Returns the current wakeup alarm clock setting.
542
543 @param Enabled Indicates if the alarm is currently enabled or disabled.
544 @param Pending Indicates if the alarm signal is pending and requires acknowledgement.
545 @param Time The current alarm setting.
546
547 @retval EFI_SUCCESS The alarm settings were returned.
548 @retval EFI_INVALID_PARAMETER Any parameter is NULL.
549 @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error.
550
551 **/
552 EFI_STATUS
553 EFIAPI
554 LibGetWakeupTime (
555 OUT BOOLEAN *Enabled,
556 OUT BOOLEAN *Pending,
557 OUT EFI_TIME *Time
558 )
559 {
560 // Not a required feature
561 return EFI_UNSUPPORTED;
562 }
563
564
565 /**
566 Sets the system wakeup alarm clock time.
567
568 @param Enabled Enable or disable the wakeup alarm.
569 @param Time If Enable is TRUE, the time to set the wakeup alarm for.
570
571 @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled. If
572 Enable is FALSE, then the wakeup alarm was disabled.
573 @retval EFI_INVALID_PARAMETER A time field is out of range.
574 @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error.
575 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
576
577 **/
578 EFI_STATUS
579 EFIAPI
580 LibSetWakeupTime (
581 IN BOOLEAN Enabled,
582 OUT EFI_TIME *Time
583 )
584 {
585 // Not a required feature
586 return EFI_UNSUPPORTED;
587 }
588
589 /**
590 Fixup internal data so that EFI can be call in virtual mode.
591 Call the passed in Child Notify event and convert any pointers in
592 lib to virtual mode.
593
594 @param[in] Event The Event that is being processed
595 @param[in] Context Event Context
596 **/
597 VOID
598 EFIAPI
599 LibRtcVirtualNotifyEvent (
600 IN EFI_EVENT Event,
601 IN VOID *Context
602 )
603 {
604 //
605 // Only needed if you are going to support the OS calling RTC functions in virtual mode.
606 // You will need to call EfiConvertPointer (). To convert any stored physical addresses
607 // to virtual address. After the OS transitions to calling in virtual mode, all future
608 // runtime calls will be made in virtual mode.
609 //
610 EfiConvertPointer (0x0, (VOID**)&mPL031RtcBase);
611 EfiConvertPointer (0x0, (VOID**)&mRT);
612 return;
613 }
614
615 /**
616 This is the declaration of an EFI image entry point. This can be the entry point to an application
617 written to this specification, an EFI boot service driver, or an EFI runtime driver.
618
619 @param ImageHandle Handle that identifies the loaded image.
620 @param SystemTable System Table for this image.
621
622 @retval EFI_SUCCESS The operation completed successfully.
623
624 **/
625 EFI_STATUS
626 EFIAPI
627 LibRtcInitialize (
628 IN EFI_HANDLE ImageHandle,
629 IN EFI_SYSTEM_TABLE *SystemTable
630 )
631 {
632 EFI_STATUS Status;
633 EFI_HANDLE Handle;
634
635 // Initialize RTC Base Address
636 mPL031RtcBase = PcdGet32 (PcdPL031RtcBase);
637
638 // Declare the controller as EFI_MEMORY_RUNTIME
639 Status = gDS->AddMemorySpace (
640 EfiGcdMemoryTypeMemoryMappedIo,
641 mPL031RtcBase, SIZE_4KB,
642 EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
643 );
644 if (EFI_ERROR (Status)) {
645 return Status;
646 }
647
648 Status = gDS->SetMemorySpaceAttributes (mPL031RtcBase, SIZE_4KB, EFI_MEMORY_UC | EFI_MEMORY_RUNTIME);
649 if (EFI_ERROR (Status)) {
650 return Status;
651 }
652
653 // Setup the setters and getters
654 gRT->GetTime = LibGetTime;
655 gRT->SetTime = LibSetTime;
656 gRT->GetWakeupTime = LibGetWakeupTime;
657 gRT->SetWakeupTime = LibSetWakeupTime;
658
659 mRT = gRT;
660
661 // Install the protocol
662 Handle = NULL;
663 Status = gBS->InstallMultipleProtocolInterfaces (
664 &Handle,
665 &gEfiRealTimeClockArchProtocolGuid, NULL,
666 NULL
667 );
668 ASSERT_EFI_ERROR (Status);
669
670 //
671 // Register for the virtual address change event
672 //
673 Status = gBS->CreateEventEx (
674 EVT_NOTIFY_SIGNAL,
675 TPL_NOTIFY,
676 LibRtcVirtualNotifyEvent,
677 NULL,
678 &gEfiEventVirtualAddressChangeGuid,
679 &mRtcVirtualAddrChangeEvent
680 );
681 ASSERT_EFI_ERROR (Status);
682
683 return Status;
684 }