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 #include <Library/CpldD03.h>
     42 #include <Library/CpldIoLib.h>
     43 
     44 extern I2C_DEVICE gDS3231RtcDevice;
     45 
     46 STATIC BOOLEAN       mDS3231Initialized = FALSE;
     47 
     48 EFI_STATUS
     49 IdentifyDS3231 (
     50   VOID
     51   )
     52 {
     53   EFI_STATUS    Status;
     54 
     55   Status = EFI_SUCCESS;
     56   return Status;
     57 }
     58 
     59 EFI_STATUS
     60 SwitchRtcI2cChannelAndLock (
     61   VOID
     62   )
     63 {
     64   UINT8   Temp;
     65   UINT8   Count;
     66 
     67   for (Count = 0; Count < 20; Count++) {
     68     Temp = ReadCpldReg (CPLD_I2C_SWITCH_FLAG);
     69 
     70     if ((Temp & BMC_I2C_STATUS) != 0) {
     71       //The I2C channel is shared with BMC,
     72       //Check if BMC has taken ownership of I2C.
     73       //If so, wait 30ms, then try again.
     74       //If not, start using I2C.
     75       //And the CPLD_I2C_SWITCH_FLAG will be set to CPU_GET_I2C_CONTROL
     76       //BMC will check this flag to decide to use I2C or not.
     77       MicroSecondDelay (30000);
     78       continue;
     79     }
     80 
     81     Temp = ReadCpldReg (CPLD_I2C_SWITCH_FLAG);
     82     Temp = Temp | CPU_GET_I2C_CONTROL;
     83     WriteCpldReg (CPLD_I2C_SWITCH_FLAG, Temp);
     84 
     85     //This is empirical value,give cpld some time to make sure the
     86     //value is wrote in
     87     MicroSecondDelay (2);
     88     Temp = ReadCpldReg (CPLD_I2C_SWITCH_FLAG);
     89 
     90     if ((Temp & CPU_GET_I2C_CONTROL) == CPU_GET_I2C_CONTROL) {
     91       return EFI_SUCCESS;
     92     }
     93 
     94     //There need 30ms to keep consistent with the previous loops if the CPU failed
     95     //to get control of I2C
     96     MicroSecondDelay (30000);
     97   }
     98 
     99   Temp = ReadCpldReg (CPLD_I2C_SWITCH_FLAG);
    100   Temp = Temp & ~CPU_GET_I2C_CONTROL;
    101   WriteCpldReg (CPLD_I2C_SWITCH_FLAG, Temp);
    102 
    103   return EFI_NOT_READY;
    104 }
    105 
    106 
    107 EFI_STATUS
    108 InitializeDS3231 (
    109   VOID
    110   )
    111 {
    112   EFI_STATUS    Status;
    113   I2C_DEVICE    Dev;
    114   RTC_DS3231_CONTROL Temp;
    115   RTC_DS3231_HOURS   Hours;
    116 
    117   // Prepare the hardware
    118   (VOID)IdentifyDS3231();
    119 
    120   (VOID) CopyMem(&Dev, &gDS3231RtcDevice, sizeof(Dev));
    121 
    122   Status = I2CInit(Dev.Socket,Dev.Port,Normal);
    123   if (EFI_ERROR (Status)) {
    124     goto EXIT;
    125   }
    126   // Ensure interrupts are masked. We do not want RTC interrupts in UEFI
    127   Status = I2CRead(&Dev,DS3231_REGADDR_CONTROL,1,&Temp.u8);
    128   if (EFI_ERROR (Status)) {
    129     goto EXIT;
    130   }
    131   Temp.bits.INTCN = 0;
    132   Status = I2CWrite(&Dev,DS3231_REGADDR_CONTROL,1,&Temp.u8);
    133   if (EFI_ERROR (Status)) {
    134     goto EXIT;
    135   }
    136 
    137   MicroSecondDelay(2000);
    138   Status = I2CRead(&Dev,DS3231_REGADDR_HOURS,1,&Hours.u8);
    139   if (EFI_ERROR (Status)) {
    140     goto EXIT;
    141   }
    142   Hours.bits.Hour24_n = 0;
    143   Status = I2CWrite(&Dev,DS3231_REGADDR_HOURS,1,&Hours.u8);
    144   if (EFI_ERROR (Status)) {
    145     goto EXIT;
    146   }
    147 
    148 
    149   mDS3231Initialized = TRUE;
    150 
    151   EXIT:
    152   return Status;
    153 }
    154 
    155 /**
    156   Returns the current time and date information, and the time-keeping capabilities
    157   of the hardware platform.
    158 
    159   @param  Time                   A pointer to storage to receive a snapshot of the current time.
    160   @param  Capabilities           An optional pointer to a buffer to receive the real time clock
    161                                  device's capabilities.
    162 
    163   @retval EFI_SUCCESS            The operation completed successfully.
    164   @retval EFI_INVALID_PARAMETER  Time is NULL.
    165   @retval EFI_DEVICE_ERROR       The time could not be retrieved due to hardware error.
    166   @retval EFI_SECURITY_VIOLATION The time could not be retrieved due to an authentication failure.
    167 **/
    168 EFI_STATUS
    169 EFIAPI
    170 LibGetTime (
    171   OUT EFI_TIME                *Time,
    172   OUT EFI_TIME_CAPABILITIES   *Capabilities
    173   )
    174 {
    175   EFI_STATUS  Status = EFI_SUCCESS;
    176   UINT8       Temp;
    177   UINT8       BaseHour = 0;
    178 
    179   UINT16      BaseYear = 1900;
    180 
    181   I2C_DEVICE    Dev;
    182 
    183   // Ensure Time is a valid pointer
    184   if (NULL == Time) {
    185     return EFI_INVALID_PARAMETER;
    186   }
    187 
    188   Status = SwitchRtcI2cChannelAndLock();
    189   if(EFI_ERROR (Status)) {
    190     return Status;
    191   }
    192 
    193   // Initialize the hardware if not already done
    194   if (!mDS3231Initialized) {
    195     Status = InitializeDS3231 ();
    196     if (EFI_ERROR (Status)) {
    197       Status = EFI_NOT_READY;
    198       goto GExit;
    199     }
    200   }
    201 
    202   (VOID) CopyMem(&Dev, &gDS3231RtcDevice, sizeof(Dev));
    203 
    204   Status |= I2CRead(&Dev,DS3231_REGADDR_MONTH,1,&Temp);
    205 
    206   Time->Month = ((Temp>>4)&1)*10+(Temp&0x0F);
    207 
    208 
    209   if(Temp&0x80){
    210     BaseYear = 2000;
    211   }
    212 
    213   Status |= I2CRead(&Dev,DS3231_REGADDR_YEAR,1,&Temp);
    214 
    215   Time->Year  = BaseYear+(Temp>>4) *10 + (Temp&0x0F);
    216 
    217   Status |= I2CRead(&Dev,DS3231_REGADDR_DATE,1,&Temp);
    218 
    219   Time->Day   = ((Temp>>4)&3) *10 + (Temp&0x0F);
    220 
    221   Status |= I2CRead(&Dev,DS3231_REGADDR_HOURS,1,&Temp);
    222 
    223   BaseHour = 0;
    224   if((Temp&0x30) == 0x30){
    225     Status = EFI_DEVICE_ERROR;
    226     goto GExit;
    227   }else if(Temp&0x20){
    228     BaseHour = 20;
    229   }else if(Temp&0x10){
    230     BaseHour = 10;
    231   }
    232   Time->Hour        = BaseHour + (Temp&0x0F);
    233 
    234   Status |= I2CRead(&Dev,DS3231_REGADDR_MIUTES,1,&Temp);
    235 
    236   Time->Minute      = ((Temp>>4)&7) * 10 + (Temp&0x0F);
    237 
    238   Status |= I2CRead(&Dev,DS3231_REGADDR_SECONDS,1,&Temp);
    239 
    240   Time->Second      = (Temp>>4) * 10 + (Temp&0x0F);
    241 
    242   Time->Nanosecond  = 0;
    243   Time->Daylight    = 0;
    244   Time->TimeZone    = EFI_UNSPECIFIED_TIMEZONE;
    245 
    246   if((EFI_ERROR(Status)) || (!IsTimeValid(Time)) || ((Time->Year - BaseYear) > 99)) {
    247     Status = EFI_UNSUPPORTED;
    248   }
    249 
    250 GExit:
    251   Temp = ReadCpldReg (CPLD_I2C_SWITCH_FLAG);
    252   Temp = Temp & ~CPU_GET_I2C_CONTROL;
    253   WriteCpldReg (CPLD_I2C_SWITCH_FLAG, Temp);
    254 
    255   return Status;
    256 
    257 }
    258 
    259 
    260 /**
    261   Sets the current local time and date information.
    262 
    263   @param  Time                  A pointer to the current time.
    264 
    265   @retval EFI_SUCCESS           The operation completed successfully.
    266   @retval EFI_INVALID_PARAMETER A time field is out of range.
    267   @retval EFI_DEVICE_ERROR      The time could not be set due due to hardware error.
    268 
    269 **/
    270 EFI_STATUS
    271 EFIAPI
    272 LibSetTime (
    273   IN  EFI_TIME                *Time
    274   )
    275 {
    276   EFI_STATUS  Status = EFI_SUCCESS;
    277   I2C_DEVICE    Dev;
    278   UINT8 Temp;
    279 
    280   UINT16 BaseYear = 1900;
    281 
    282 
    283 
    284   // Check the input parameters are within the range specified by UEFI
    285   if(!IsTimeValid(Time)){
    286     return EFI_INVALID_PARAMETER;
    287   }
    288 
    289   Status = SwitchRtcI2cChannelAndLock();
    290   if(EFI_ERROR (Status)) {
    291     return Status;
    292   }
    293 
    294   // Initialize the hardware if not already done
    295   if (!mDS3231Initialized) {
    296     Status = InitializeDS3231 ();
    297     if (EFI_ERROR (Status)) {
    298       goto EXIT;
    299     }
    300   }
    301 
    302   (VOID) CopyMem(&Dev, &gDS3231RtcDevice, sizeof(Dev));
    303 
    304   Temp = ((Time->Second/10)<<4) | (Time->Second%10);
    305   MicroSecondDelay(1000);
    306   Status = I2CWrite(&Dev,DS3231_REGADDR_SECONDS,1,&Temp);
    307   if(EFI_ERROR (Status)){
    308     goto EXIT;
    309   }
    310 
    311   Temp = ((Time->Minute/10)<<4) | (Time->Minute%10);
    312   MicroSecondDelay(1000);
    313   Status = I2CWrite(&Dev,DS3231_REGADDR_MIUTES,1,&Temp);
    314   if(EFI_ERROR (Status)){
    315     goto EXIT;
    316   }
    317 
    318   Temp = 0;
    319   if(Time->Hour > 19){
    320     Temp = 2;
    321   } else if(Time->Hour > 9){
    322     Temp = 1;
    323   }
    324 
    325   Temp = (Temp << 4) | (Time->Hour%10);
    326   MicroSecondDelay(1000);
    327   Status = I2CWrite(&Dev,DS3231_REGADDR_HOURS,1,&Temp);
    328   if(EFI_ERROR (Status)){
    329     goto EXIT;
    330   }
    331 
    332   Temp = ((Time->Day/10)<<4) | (Time->Day%10);
    333   MicroSecondDelay(1000);
    334   Status = I2CWrite(&Dev,DS3231_REGADDR_DATE,1,&Temp);
    335   if(EFI_ERROR (Status)){
    336     goto EXIT;
    337   }
    338 
    339 
    340   Temp = 0;
    341   if(Time->Year >= 2000){
    342     Temp = 0x8;
    343     BaseYear = 2000;
    344   }
    345 
    346   if(Time->Month > 9){
    347     Temp |= 0x1;
    348   }
    349   Temp = (Temp<<4) | (Time->Month%10);
    350   MicroSecondDelay(1000);
    351   Status = I2CWrite(&Dev,DS3231_REGADDR_MONTH,1,&Temp);
    352   if(EFI_ERROR (Status)){
    353     goto EXIT;
    354   }
    355 
    356   Temp = (((Time->Year-BaseYear)/10)<<4) | (Time->Year%10);
    357   MicroSecondDelay(1000);
    358   Status = I2CWrite(&Dev,DS3231_REGADDR_YEAR,1,&Temp);
    359   if(EFI_ERROR (Status)){
    360     goto EXIT;
    361   }
    362 
    363   EXIT:
    364 
    365   Temp = ReadCpldReg (CPLD_I2C_SWITCH_FLAG);
    366   Temp = Temp & ~CPU_GET_I2C_CONTROL;
    367   WriteCpldReg (CPLD_I2C_SWITCH_FLAG, Temp);
    368 
    369   return Status;
    370 }
    371 
    372 
    373 /**
    374   Returns the current wakeup alarm clock setting.
    375 
    376   @param  Enabled               Indicates if the alarm is currently enabled or disabled.
    377   @param  Pending               Indicates if the alarm signal is pending and requires acknowledgement.
    378   @param  Time                  The current alarm setting.
    379 
    380   @retval EFI_SUCCESS           The alarm settings were returned.
    381   @retval EFI_INVALID_PARAMETER Any parameter is NULL.
    382   @retval EFI_DEVICE_ERROR      The wakeup time could not be retrieved due to a hardware error.
    383 
    384 **/
    385 EFI_STATUS
    386 EFIAPI
    387 LibGetWakeupTime (
    388   OUT BOOLEAN     *Enabled,
    389   OUT BOOLEAN     *Pending,
    390   OUT EFI_TIME    *Time
    391   )
    392 {
    393   // Not a required feature
    394   return EFI_UNSUPPORTED;
    395 }
    396 
    397 
    398 /**
    399   Sets the system wakeup alarm clock time.
    400 
    401   @param  Enabled               Enable or disable the wakeup alarm.
    402   @param  Time                  If Enable is TRUE, the time to set the wakeup alarm for.
    403 
    404   @retval EFI_SUCCESS           If Enable is TRUE, then the wakeup alarm was enabled. If
    405                                 Enable is FALSE, then the wakeup alarm was disabled.
    406   @retval EFI_INVALID_PARAMETER A time field is out of range.
    407   @retval EFI_DEVICE_ERROR      The wakeup time could not be set due to a hardware error.
    408   @retval EFI_UNSUPPORTED       A wakeup timer is not supported on this platform.
    409 
    410 **/
    411 EFI_STATUS
    412 EFIAPI
    413 LibSetWakeupTime (
    414   IN BOOLEAN      Enabled,
    415   OUT EFI_TIME    *Time
    416   )
    417 {
    418   // Not a required feature
    419   return EFI_UNSUPPORTED;
    420 }
    421 
    422 
    423 
    424 /**
    425   This is the declaration of an EFI image entry point. This can be the entry point to an application
    426   written to this specification, an EFI boot service driver, or an EFI runtime driver.
    427 
    428   @param  ImageHandle           Handle that identifies the loaded image.
    429   @param  SystemTable           System Table for this image.
    430 
    431   @retval EFI_SUCCESS           The operation completed successfully.
    432 
    433 **/
    434 EFI_STATUS
    435 EFIAPI
    436 LibRtcInitialize (
    437   IN EFI_HANDLE                            ImageHandle,
    438   IN EFI_SYSTEM_TABLE                      *SystemTable
    439   )
    440 {
    441   EFI_STATUS    Status;
    442   EFI_HANDLE    Handle;
    443 
    444 
    445   EFI_TIME      EfiTime;
    446 
    447   // Setup the setters and getters
    448   gRT->GetTime       = LibGetTime;
    449   gRT->SetTime       = LibSetTime;
    450   gRT->GetWakeupTime = LibGetWakeupTime;
    451   gRT->SetWakeupTime = LibSetWakeupTime;
    452 
    453 
    454   (VOID)gRT->GetTime (&EfiTime, NULL);
    455   if((EfiTime.Year < 2015) || (EfiTime.Year > 2099)){
    456       EfiTime.Year          = 2015;
    457       EfiTime.Month         = 1;
    458       EfiTime.Day           = 1;
    459       EfiTime.Hour          = 0;
    460       EfiTime.Minute        = 0;
    461       EfiTime.Second        = 0;
    462       EfiTime.Nanosecond    = 0;
    463       Status = gRT->SetTime(&EfiTime);
    464       if (EFI_ERROR(Status))
    465       {
    466         DEBUG((EFI_D_ERROR, "[%a]:[%dL] Status : %r\n", __FUNCTION__, __LINE__, Status));
    467       }
    468   }
    469 
    470   // Install the protocol
    471   Handle = NULL;
    472   Status = gBS->InstallMultipleProtocolInterfaces (
    473                   &Handle,
    474                   &gEfiRealTimeClockArchProtocolGuid,  NULL,
    475                   NULL
    476                  );
    477 
    478   return Status;
    479 }
    480 
    481 
    482 /**
    483   Fixup internal data so that EFI can be call in virtual mode.
    484   Call the passed in Child Notify event and convert any pointers in
    485   lib to virtual mode.
    486 
    487   @param[in]    Event   The Event that is being processed
    488   @param[in]    Context Event Context
    489 **/
    490 VOID
    491 EFIAPI
    492 LibRtcVirtualNotifyEvent (
    493   IN EFI_EVENT        Event,
    494   IN VOID             *Context
    495   )
    496 {
    497   //
    498   // Only needed if you are going to support the OS calling RTC functions in virtual mode.
    499   // You will need to call EfiConvertPointer (). To convert any stored physical addresses
    500   // to virtual address. After the OS transitions to calling in virtual mode, all future
    501   // runtime calls will be made in virtual mode.
    502   //
    503   return;
    504 }
    505