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