Home | History | Annotate | Download | only in DS3231RealTimeClockLib
      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
     47 IdentifyDS3231 (
     48   VOID
     49   )
     50 {
     51   EFI_STATUS    Status;
     52 
     53   Status = EFI_SUCCESS;
     54   return Status;
     55 }
     56 
     57 EFI_STATUS
     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
    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
    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
    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
    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
    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
    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