• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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