1 /** @file
2 Implement EFI RealTimeClock runtime services via RTC Lib.
3
4 Currently this driver does not support runtime virtual calling.
5
6 Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
7 Copyright (c) 2011-2013, ARM Ltd. All rights reserved.<BR>
8 Copyright (c) 2015, Hisilicon Limited. All rights reserved.<BR>
9 Copyright (c) 2015, Linaro Limited. All rights reserved.<BR>
10
11 This program and the accompanying materials
12 are licensed and made available under the terms and conditions of the BSD License
13 which accompanies this distribution. The full text of the license may be found at
14 http://opensource.org/licenses/bsd-license.php
15
16 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
17 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18
19 Based on the files under ArmPlatformPkg/Library/PL031RealTimeClockLib/PL031RealTimeClockLib.inf
20
21 **/
22
23 #include <Uefi.h>
24 #include <PiDxe.h>
25 #include <Library/BaseLib.h>
26 #include <Library/BaseMemoryLib.h>
27 #include <Library/DebugLib.h>
28 #include <Library/UefiLib.h>
29 // Use EfiAtRuntime to check stage
30 #include <Library/UefiRuntimeLib.h>
31 #include <Library/IoLib.h>
32 #include <Library/MemoryAllocationLib.h>
33 #include <Library/PcdLib.h>
34 #include <Library/UefiBootServicesTableLib.h>
35 #include <Library/UefiRuntimeServicesTableLib.h>
36 #include <Library/TimerLib.h>
37 #include <Library/EfiTimeBaseLib.h>
38 #include <Protocol/RealTimeClock.h>
39 #include <Library/I2CLib.h>
40 #include "DS3231RealTimeClock.h"
41
42 extern I2C_DEVICE gDS3231RtcDevice;
43
44 STATIC BOOLEAN mDS3231Initialized = FALSE;
45
46 EFI_STATUS
IdentifyDS3231(VOID)47 IdentifyDS3231 (
48 VOID
49 )
50 {
51 EFI_STATUS Status;
52
53 Status = EFI_SUCCESS;
54 return Status;
55 }
56
57 EFI_STATUS
InitializeDS3231(VOID)58 InitializeDS3231 (
59 VOID
60 )
61 {
62 EFI_STATUS Status;
63 I2C_DEVICE Dev;
64 RTC_DS3231_CONTROL Temp;
65 RTC_DS3231_HOURS Hours;
66
67 // Prepare the hardware
68 (VOID)IdentifyDS3231();
69
70 (VOID) CopyMem(&Dev, &gDS3231RtcDevice, sizeof(Dev));
71
72 Status = I2CInit(Dev.Socket,Dev.Port,Normal);
73 if (EFI_ERROR (Status)) {
74 goto EXIT;
75 }
76 // Ensure interrupts are masked. We do not want RTC interrupts in UEFI
77 Status = I2CRead(&Dev,DS3231_REGADDR_CONTROL,1,&Temp.u8);
78 if (EFI_ERROR (Status)) {
79 goto EXIT;
80 }
81 Temp.bits.INTCN = 0;
82 Status = I2CWrite(&Dev,DS3231_REGADDR_CONTROL,1,&Temp.u8);
83 if (EFI_ERROR (Status)) {
84 goto EXIT;
85 }
86
87 MicroSecondDelay(2000);
88 Status = I2CRead(&Dev,DS3231_REGADDR_HOURS,1,&Hours.u8);
89 if (EFI_ERROR (Status)) {
90 goto EXIT;
91 }
92 Hours.bits.Hour24_n = 0;
93 Status = I2CWrite(&Dev,DS3231_REGADDR_HOURS,1,&Hours.u8);
94 if (EFI_ERROR (Status)) {
95 goto EXIT;
96 }
97
98
99 mDS3231Initialized = TRUE;
100
101 EXIT:
102 return Status;
103 }
104
105
106 /**
107 Returns the current time and date information, and the time-keeping capabilities
108 of the hardware platform.
109
110 @param Time A pointer to storage to receive a snapshot of the current time.
111 @param Capabilities An optional pointer to a buffer to receive the real time clock
112 device's capabilities.
113
114 @retval EFI_SUCCESS The operation completed successfully.
115 @retval EFI_INVALID_PARAMETER Time is NULL.
116 @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error.
117 @retval EFI_SECURITY_VIOLATION The time could not be retrieved due to an authentication failure.
118 **/
119 EFI_STATUS
120 EFIAPI
LibGetTime(OUT EFI_TIME * Time,OUT EFI_TIME_CAPABILITIES * Capabilities)121 LibGetTime (
122 OUT EFI_TIME *Time,
123 OUT EFI_TIME_CAPABILITIES *Capabilities
124 )
125 {
126 EFI_STATUS Status = EFI_SUCCESS;
127 UINT8 Temp;
128 UINT8 BaseHour = 0;
129 UINT16 BaseYear = 1900;
130
131 I2C_DEVICE Dev;
132
133 // Ensure Time is a valid pointer
134 if (NULL == Time) {
135 return EFI_INVALID_PARAMETER;
136 }
137
138 // Initialize the hardware if not already done
139 if (!mDS3231Initialized) {
140 Status = InitializeDS3231 ();
141 if (EFI_ERROR (Status)) {
142 return EFI_NOT_READY;
143 }
144 }
145
146 (VOID) CopyMem(&Dev, &gDS3231RtcDevice, sizeof(Dev));
147
148
149 Status |= I2CRead(&Dev,DS3231_REGADDR_MONTH,1,&Temp);
150
151 Time->Month = ((Temp>>4)&1)*10+(Temp&0x0F);
152
153
154 if(Temp&0x80){
155 BaseYear = 2000;
156 }
157
158 Status |= I2CRead(&Dev,DS3231_REGADDR_YEAR,1,&Temp);
159
160 Time->Year = BaseYear+(Temp>>4) *10 + (Temp&0x0F);
161
162
163 Status |= I2CRead(&Dev,DS3231_REGADDR_DATE,1,&Temp);
164
165 Time->Day = ((Temp>>4)&3) *10 + (Temp&0x0F);
166
167
168 Status |= I2CRead(&Dev,DS3231_REGADDR_HOURS,1,&Temp);
169
170 BaseHour = 0;
171 if((Temp&0x30) == 0x30){
172 return EFI_DEVICE_ERROR;
173 }else if(Temp&0x20){
174 BaseHour = 20;
175 }else if(Temp&0x10){
176 BaseHour = 10;
177 }
178 Time->Hour = BaseHour + (Temp&0x0F);
179
180
181 Status |= I2CRead(&Dev,DS3231_REGADDR_MIUTES,1,&Temp);
182
183 Time->Minute = ((Temp>>4)&7) * 10 + (Temp&0x0F);
184
185
186 Status |= I2CRead(&Dev,DS3231_REGADDR_SECONDS,1,&Temp);
187
188 Time->Second = (Temp>>4) * 10 + (Temp&0x0F);
189
190 Time->Nanosecond = 0;
191 Time->Daylight = 0;
192 Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE;
193
194 if((EFI_ERROR(Status)) || (!IsTimeValid(Time)) || ((Time->Year - BaseYear) > 99)) {
195 return EFI_DEVICE_ERROR;
196 }
197
198 return EFI_SUCCESS;
199 }
200
201
202 /**
203 Sets the current local time and date information.
204
205 @param Time A pointer to the current time.
206
207 @retval EFI_SUCCESS The operation completed successfully.
208 @retval EFI_INVALID_PARAMETER A time field is out of range.
209 @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error.
210
211 **/
212 EFI_STATUS
213 EFIAPI
LibSetTime(IN EFI_TIME * Time)214 LibSetTime (
215 IN EFI_TIME *Time
216 )
217 {
218 EFI_STATUS Status = EFI_SUCCESS;
219 I2C_DEVICE Dev;
220 UINT8 Temp;
221 UINT16 BaseYear = 1900;
222
223 // Check the input parameters are within the range specified by UEFI
224 if(!IsTimeValid(Time)){
225 return EFI_INVALID_PARAMETER;
226 }
227
228 // Initialize the hardware if not already done
229 if (!mDS3231Initialized) {
230 Status = InitializeDS3231 ();
231 if (EFI_ERROR (Status)) {
232 goto EXIT;
233 }
234 }
235
236 (VOID) CopyMem(&Dev, &gDS3231RtcDevice, sizeof(Dev));
237
238 Temp = ((Time->Second/10)<<4) | (Time->Second%10);
239 MicroSecondDelay(1000);
240 Status = I2CWrite(&Dev,DS3231_REGADDR_SECONDS,1,&Temp);
241 if(EFI_ERROR (Status)){
242 goto EXIT;
243 }
244
245 Temp = ((Time->Minute/10)<<4) | (Time->Minute%10);
246 MicroSecondDelay(1000);
247 Status = I2CWrite(&Dev,DS3231_REGADDR_MIUTES,1,&Temp);
248 if(EFI_ERROR (Status)){
249 goto EXIT;
250 }
251
252 Temp = 0;
253 if(Time->Hour > 19){
254 Temp = 2;
255 } else if(Time->Hour > 9){
256 Temp = 1;
257 }
258 Temp = (Temp << 4) | (Time->Hour%10);
259 MicroSecondDelay(1000);
260 Status = I2CWrite(&Dev,DS3231_REGADDR_HOURS,1,&Temp);
261 if(EFI_ERROR (Status)){
262 goto EXIT;
263 }
264
265 Temp = ((Time->Day/10)<<4) | (Time->Day%10);
266 MicroSecondDelay(1000);
267 Status = I2CWrite(&Dev,DS3231_REGADDR_DATE,1,&Temp);
268 if(EFI_ERROR (Status)){
269 goto EXIT;
270 }
271
272 Temp = 0;
273 if(Time->Year >= 2000){
274 Temp = 0x8;
275 BaseYear = 2000;
276 }
277
278 if(Time->Month > 9){
279 Temp |= 0x1;
280 }
281 Temp = (Temp<<4) | (Time->Month%10);
282 MicroSecondDelay(1000);
283 Status = I2CWrite(&Dev,DS3231_REGADDR_MONTH,1,&Temp);
284 if(EFI_ERROR (Status)){
285 goto EXIT;
286 }
287
288 Temp = (((Time->Year-BaseYear)/10)<<4) | (Time->Year%10);
289 MicroSecondDelay(1000);
290 Status = I2CWrite(&Dev,DS3231_REGADDR_YEAR,1,&Temp);
291 if(EFI_ERROR (Status)){
292 goto EXIT;
293 }
294
295 EXIT:
296 return Status;
297 }
298
299
300 /**
301 Returns the current wakeup alarm clock setting.
302
303 @param Enabled Indicates if the alarm is currently enabled or disabled.
304 @param Pending Indicates if the alarm signal is pending and requires acknowledgement.
305 @param Time The current alarm setting.
306
307 @retval EFI_SUCCESS The alarm settings were returned.
308 @retval EFI_INVALID_PARAMETER Any parameter is NULL.
309 @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error.
310
311 **/
312 EFI_STATUS
313 EFIAPI
LibGetWakeupTime(OUT BOOLEAN * Enabled,OUT BOOLEAN * Pending,OUT EFI_TIME * Time)314 LibGetWakeupTime (
315 OUT BOOLEAN *Enabled,
316 OUT BOOLEAN *Pending,
317 OUT EFI_TIME *Time
318 )
319 {
320 // Not a required feature
321 return EFI_UNSUPPORTED;
322 }
323
324
325 /**
326 Sets the system wakeup alarm clock time.
327
328 @param Enabled Enable or disable the wakeup alarm.
329 @param Time If Enable is TRUE, the time to set the wakeup alarm for.
330
331 @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled. If
332 Enable is FALSE, then the wakeup alarm was disabled.
333 @retval EFI_INVALID_PARAMETER A time field is out of range.
334 @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error.
335 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
336
337 **/
338 EFI_STATUS
339 EFIAPI
LibSetWakeupTime(IN BOOLEAN Enabled,OUT EFI_TIME * Time)340 LibSetWakeupTime (
341 IN BOOLEAN Enabled,
342 OUT EFI_TIME *Time
343 )
344 {
345 // Not a required feature
346 return EFI_UNSUPPORTED;
347 }
348
349
350
351 /**
352 This is the declaration of an EFI image entry point. This can be the entry point to an application
353 written to this specification, an EFI boot service driver, or an EFI runtime driver.
354
355 @param ImageHandle Handle that identifies the loaded image.
356 @param SystemTable System Table for this image.
357
358 @retval EFI_SUCCESS The operation completed successfully.
359
360 **/
361 EFI_STATUS
362 EFIAPI
LibRtcInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)363 LibRtcInitialize (
364 IN EFI_HANDLE ImageHandle,
365 IN EFI_SYSTEM_TABLE *SystemTable
366 )
367 {
368 EFI_STATUS Status;
369 EFI_HANDLE Handle;
370
371
372 EFI_TIME EfiTime;
373
374 // Setup the setters and getters
375 gRT->GetTime = LibGetTime;
376 gRT->SetTime = LibSetTime;
377 gRT->GetWakeupTime = LibGetWakeupTime;
378 gRT->SetWakeupTime = LibSetWakeupTime;
379
380 Status = gRT->GetTime (&EfiTime, NULL);
381 if(EFI_ERROR (Status) || (EfiTime.Year < 2000) || (EfiTime.Year > 2099)){
382 EfiTime.Year = 2000;
383 EfiTime.Month = 1;
384 EfiTime.Day = 1;
385 EfiTime.Hour = 0;
386 EfiTime.Minute = 0;
387 EfiTime.Second = 0;
388 EfiTime.Nanosecond = 0;
389 EfiTime.Daylight = 0;
390 EfiTime.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
391
392 Status = gRT->SetTime(&EfiTime);
393 if (EFI_ERROR(Status))
394 {
395 DEBUG((EFI_D_ERROR, "[%a]:[%dL] Status : %r\n", __FUNCTION__, __LINE__, Status));
396 }
397 }
398
399 // Install the protocol
400 Handle = NULL;
401 Status = gBS->InstallMultipleProtocolInterfaces (
402 &Handle,
403 &gEfiRealTimeClockArchProtocolGuid, NULL,
404 NULL
405 );
406
407 return Status;
408 }
409
410
411 /**
412 Fixup internal data so that EFI can be call in virtual mode.
413 Call the passed in Child Notify event and convert any pointers in
414 lib to virtual mode.
415
416 @param[in] Event The Event that is being processed
417 @param[in] Context Event Context
418 **/
419 VOID
420 EFIAPI
LibRtcVirtualNotifyEvent(IN EFI_EVENT Event,IN VOID * Context)421 LibRtcVirtualNotifyEvent (
422 IN EFI_EVENT Event,
423 IN VOID *Context
424 )
425 {
426 //
427 // Only needed if you are going to support the OS calling RTC functions in virtual mode.
428 // You will need to call EfiConvertPointer (). To convert any stored physical addresses
429 // to virtual address. After the OS transitions to calling in virtual mode, all future
430 // runtime calls will be made in virtual mode.
431 //
432 return;
433 }
434