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