TianoCore EDK2 master
Loading...
Searching...
No Matches
PL031RealTimeClockLib.c
Go to the documentation of this file.
1
12#include <PiDxe.h>
13
14#include <Guid/EventGroup.h>
15#include <Guid/GlobalVariable.h>
16
17#include <Library/BaseLib.h>
18#include <Library/DebugLib.h>
20#include <Library/IoLib.h>
22#include <Library/PcdLib.h>
24#include <Library/TimeBaseLib.h>
26#include <Library/UefiLib.h>
29
30#include "PL031RealTimeClock.h"
31
32STATIC BOOLEAN mPL031Initialized = FALSE;
33STATIC EFI_EVENT mRtcVirtualAddrChangeEvent;
34STATIC UINTN mPL031RtcBase;
35
37IdentifyPL031 (
38 VOID
39 )
40{
41 EFI_STATUS Status;
42
43 // Check if this is a PrimeCell Peripheral
44 if ( (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID0) != 0x0D)
45 || (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID1) != 0xF0)
46 || (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID2) != 0x05)
47 || (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID3) != 0xB1))
48 {
49 Status = EFI_NOT_FOUND;
50 goto EXIT;
51 }
52
53 // Check if this PrimeCell Peripheral is the PL031 Real Time Clock
54 if ( (MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID0) != 0x31)
55 || (MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID1) != 0x10)
56 || ((MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID2) & 0xF) != 0x04)
57 || (MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID3) != 0x00))
58 {
59 Status = EFI_NOT_FOUND;
60 goto EXIT;
61 }
62
63 Status = EFI_SUCCESS;
64
65EXIT:
66 return Status;
67}
68
70InitializePL031 (
71 VOID
72 )
73{
74 EFI_STATUS Status;
75
76 // Prepare the hardware
77 Status = IdentifyPL031 ();
78 if (EFI_ERROR (Status)) {
79 goto EXIT;
80 }
81
82 // Ensure interrupts are masked. We do not want RTC interrupts in UEFI
83 if ((MmioRead32 (mPL031RtcBase + PL031_RTC_IMSC_IRQ_MASK_SET_CLEAR_REGISTER) & PL031_SET_IRQ_MASK) != 0) {
84 MmioWrite32 (mPL031RtcBase + PL031_RTC_IMSC_IRQ_MASK_SET_CLEAR_REGISTER, 0);
85 }
86
87 // Clear any existing interrupts
88 if ((MmioRead32 (mPL031RtcBase + PL031_RTC_RIS_RAW_IRQ_STATUS_REGISTER) & PL031_IRQ_TRIGGERED) == PL031_IRQ_TRIGGERED) {
89 MmioOr32 (mPL031RtcBase + PL031_RTC_ICR_IRQ_CLEAR_REGISTER, PL031_CLEAR_IRQ);
90 }
91
92 // Start the clock counter
93 if ((MmioRead32 (mPL031RtcBase + PL031_RTC_CR_CONTROL_REGISTER) & PL031_RTC_ENABLED) != PL031_RTC_ENABLED) {
94 MmioOr32 (mPL031RtcBase + PL031_RTC_CR_CONTROL_REGISTER, PL031_RTC_ENABLED);
95 }
96
97 mPL031Initialized = TRUE;
98
99EXIT:
100 return Status;
101}
102
120EFIAPI
122 OUT EFI_TIME *Time,
123 OUT EFI_TIME_CAPABILITIES *Capabilities
124 )
125{
126 EFI_STATUS Status;
127 UINT32 EpochSeconds;
128
129 // Ensure Time is a valid pointer
130 if (Time == NULL) {
131 return EFI_INVALID_PARAMETER;
132 }
133
134 // Initialize the hardware if not already done
135 if (!mPL031Initialized) {
136 Status = InitializePL031 ();
137 if (EFI_ERROR (Status)) {
138 return Status;
139 }
140 }
141
142 EpochSeconds = MmioRead32 (mPL031RtcBase + PL031_RTC_DR_DATA_REGISTER);
143
144 // Adjust for the correct time zone
145 // The timezone setting also reflects the DST setting of the clock
146 if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) {
147 EpochSeconds += Time->TimeZone * SEC_PER_MIN;
148 } else if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) {
149 // Convert to adjusted time, i.e. spring forwards one hour
150 EpochSeconds += SEC_PER_HOUR;
151 }
152
153 // Convert from internal 32-bit time to UEFI time
154 EpochToEfiTime (EpochSeconds, Time);
155
156 // Update the Capabilities info
157 if (Capabilities != NULL) {
158 // PL031 runs at frequency 1Hz
159 Capabilities->Resolution = PL031_COUNTS_PER_SECOND;
160 // Accuracy in ppm multiplied by 1,000,000, e.g. for 50ppm set 50,000,000
161 Capabilities->Accuracy = (UINT32)PcdGet32 (PcdPL031RtcPpmAccuracy);
162 // FALSE: Setting the time does not clear the values below the resolution level
163 Capabilities->SetsToZero = FALSE;
164 }
165
166 return EFI_SUCCESS;
167}
168
183EFIAPI
185 IN EFI_TIME *Time
186 )
187{
188 EFI_STATUS Status;
189 UINT32 EpochSeconds;
190
191 // Because the PL031 is a 32-bit counter counting seconds,
192 // the maximum time span is just over 136 years.
193 // Time is stored in Unix Epoch format, so it starts in 1970,
194 // Therefore it can not exceed the year 2106.
195 if ((Time->Year < 1970) || (Time->Year >= 2106)) {
196 return EFI_UNSUPPORTED;
197 }
198
199 // Initialize the hardware if not already done
200 if (!mPL031Initialized) {
201 Status = InitializePL031 ();
202 if (EFI_ERROR (Status)) {
203 return Status;
204 }
205 }
206
207 EpochSeconds = (UINT32)EfiTimeToEpoch (Time);
208
209 // Adjust for the correct time zone, i.e. convert to UTC time zone
210 // The timezone setting also reflects the DST setting of the clock
211 if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) {
212 EpochSeconds -= Time->TimeZone * SEC_PER_MIN;
213 } else if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) {
214 // Convert to un-adjusted time, i.e. fall back one hour
215 EpochSeconds -= SEC_PER_HOUR;
216 }
217
218 // Set the PL031
219 MmioWrite32 (mPL031RtcBase + PL031_RTC_LR_LOAD_REGISTER, EpochSeconds);
220
221 return EFI_SUCCESS;
222}
223
242EFIAPI
244 OUT BOOLEAN *Enabled,
245 OUT BOOLEAN *Pending,
246 OUT EFI_TIME *Time
247 )
248{
249 // Not a required feature
250 return EFI_UNSUPPORTED;
251}
252
271EFIAPI
273 IN BOOLEAN Enabled,
274 OUT EFI_TIME *Time
275 )
276{
277 // Not a required feature
278 return EFI_UNSUPPORTED;
279}
280
289STATIC
290VOID
291EFIAPI
293 IN EFI_EVENT Event,
294 IN VOID *Context
295 )
296{
297 //
298 // Only needed if you are going to support the OS calling RTC functions in virtual mode.
299 // You will need to call EfiConvertPointer (). To convert any stored physical addresses
300 // to virtual address. After the OS transitions to calling in virtual mode, all future
301 // runtime calls will be made in virtual mode.
302 //
303 EfiConvertPointer (0x0, (VOID **)&mPL031RtcBase);
304 return;
305}
306
318EFIAPI
320 IN EFI_HANDLE ImageHandle,
321 IN EFI_SYSTEM_TABLE *SystemTable
322 )
323{
324 EFI_STATUS Status;
325
326 // Initialize RTC Base Address
327 mPL031RtcBase = PcdGet32 (PcdPL031RtcBase);
328
329 // Declare the controller as EFI_MEMORY_RUNTIME
330 Status = gDS->AddMemorySpace (
332 mPL031RtcBase,
333 SIZE_4KB,
334 EFI_MEMORY_UC | EFI_MEMORY_RUNTIME | EFI_MEMORY_XP
335 );
336 if (EFI_ERROR (Status)) {
337 return Status;
338 }
339
340 Status = gDS->SetMemorySpaceAttributes (mPL031RtcBase, SIZE_4KB, EFI_MEMORY_UC | EFI_MEMORY_RUNTIME | EFI_MEMORY_XP);
341 if (EFI_ERROR (Status)) {
342 return Status;
343 }
344
345 //
346 // Register for the virtual address change event
347 //
348 Status = gBS->CreateEventEx (
349 EVT_NOTIFY_SIGNAL,
350 TPL_NOTIFY,
352 NULL,
353 &gEfiEventVirtualAddressChangeGuid,
354 &mRtcVirtualAddrChangeEvent
355 );
356 ASSERT_EFI_ERROR (Status);
357
358 return Status;
359}
UINT64 UINTN
EFI_DXE_SERVICES * gDS
UINT32 EFIAPI MmioOr32(IN UINTN Address, IN UINT32 OrData)
Definition: IoHighLevel.c:1785
UINT8 EFIAPI MmioRead8(IN UINTN Address)
Definition: IoLib.c:82
UINT32 EFIAPI MmioRead32(IN UINTN Address)
Definition: IoLib.c:262
UINT32 EFIAPI MmioWrite32(IN UINTN Address, IN UINT32 Value)
Definition: IoLib.c:309
#define NULL
Definition: Base.h:319
#define STATIC
Definition: Base.h:264
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define ASSERT_EFI_ERROR(StatusParameter)
Definition: DebugLib.h:462
STATIC VOID EFIAPI VirtualNotifyEvent(IN EFI_EVENT Event, IN VOID *Context)
EFI_STATUS EFIAPI LibSetWakeupTime(IN BOOLEAN Enabled, OUT EFI_TIME *Time)
EFI_STATUS EFIAPI LibRtcInitialize(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
EFI_STATUS EFIAPI LibGetWakeupTime(OUT BOOLEAN *Enabled, OUT BOOLEAN *Pending, OUT EFI_TIME *Time)
EFI_STATUS EFIAPI LibSetTime(IN EFI_TIME *Time)
EFI_STATUS EFIAPI LibGetTime(OUT EFI_TIME *Time, OUT EFI_TIME_CAPABILITIES *Capabilities)
#define PcdGet32(TokenName)
Definition: PcdLib.h:362
@ EfiGcdMemoryTypeMemoryMappedIo
Definition: PiDxeCis.h:44
UINTN EFIAPI EfiTimeToEpoch(IN EFI_TIME *Time)
Definition: TimeBaseLib.c:119
VOID EFIAPI EpochToEfiTime(IN UINTN EpochSeconds, OUT EFI_TIME *Time)
Definition: TimeBaseLib.c:25
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
VOID * EFI_EVENT
Definition: UefiBaseType.h:37
VOID * EFI_HANDLE
Definition: UefiBaseType.h:33
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
EFI_BOOT_SERVICES * gBS
EFI_STATUS EFIAPI EfiConvertPointer(IN UINTN DebugDisposition, IN OUT VOID **Address)
Definition: RuntimeLib.c:561
#define EFI_UNSPECIFIED_TIMEZONE
Definition: UefiSpec.h:58