PcRtc.c 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393
  1. /** @file
  2. RTC Architectural Protocol GUID as defined in DxeCis 0.96.
  3. Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
  4. Copyright (c) 2017, AMD Inc. All rights reserved.<BR>
  5. Copyright (c) 2018 - 2020, ARM Limited. All rights reserved.<BR>
  6. SPDX-License-Identifier: BSD-2-Clause-Patent
  7. **/
  8. #include "PcRtc.h"
  9. extern UINTN mRtcIndexRegister;
  10. extern UINTN mRtcTargetRegister;
  11. //
  12. // Days of month.
  13. //
  14. UINTN mDayOfMonth[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  15. //
  16. // The name of NV variable to store the timezone and daylight saving information.
  17. //
  18. CHAR16 mTimeZoneVariableName[] = L"RTC";
  19. /**
  20. Compare the Hour, Minute and Second of the From time and the To time.
  21. Only compare H/M/S in EFI_TIME and ignore other fields here.
  22. @param From the first time
  23. @param To the second time
  24. @return >0 The H/M/S of the From time is later than those of To time
  25. @return ==0 The H/M/S of the From time is same as those of To time
  26. @return <0 The H/M/S of the From time is earlier than those of To time
  27. **/
  28. INTN
  29. CompareHMS (
  30. IN EFI_TIME *From,
  31. IN EFI_TIME *To
  32. );
  33. /**
  34. To check if second date is later than first date within 24 hours.
  35. @param From the first date
  36. @param To the second date
  37. @retval TRUE From is previous to To within 24 hours.
  38. @retval FALSE From is later, or it is previous to To more than 24 hours.
  39. **/
  40. BOOLEAN
  41. IsWithinOneDay (
  42. IN EFI_TIME *From,
  43. IN EFI_TIME *To
  44. );
  45. /**
  46. Read RTC content through its registers using IO access.
  47. @param Address Address offset of RTC. It is recommended to use
  48. macros such as RTC_ADDRESS_SECONDS.
  49. @return The data of UINT8 type read from RTC.
  50. **/
  51. STATIC
  52. UINT8
  53. IoRtcRead (
  54. IN UINTN Address
  55. )
  56. {
  57. IoWrite8 (
  58. PcdGet8 (PcdRtcIndexRegister),
  59. (UINT8)(Address | (UINT8)(IoRead8 (PcdGet8 (PcdRtcIndexRegister)) & 0x80))
  60. );
  61. return IoRead8 (PcdGet8 (PcdRtcTargetRegister));
  62. }
  63. /**
  64. Write RTC through its registers using IO access.
  65. @param Address Address offset of RTC. It is recommended to use
  66. macros such as RTC_ADDRESS_SECONDS.
  67. @param Data The content you want to write into RTC.
  68. **/
  69. STATIC
  70. VOID
  71. IoRtcWrite (
  72. IN UINTN Address,
  73. IN UINT8 Data
  74. )
  75. {
  76. IoWrite8 (
  77. PcdGet8 (PcdRtcIndexRegister),
  78. (UINT8)(Address | (UINT8)(IoRead8 (PcdGet8 (PcdRtcIndexRegister)) & 0x80))
  79. );
  80. IoWrite8 (PcdGet8 (PcdRtcTargetRegister), Data);
  81. }
  82. /**
  83. Read RTC content through its registers using MMIO access.
  84. @param Address Address offset of RTC. It is recommended to use
  85. macros such as RTC_ADDRESS_SECONDS.
  86. @return The data of UINT8 type read from RTC.
  87. **/
  88. STATIC
  89. UINT8
  90. MmioRtcRead (
  91. IN UINTN Address
  92. )
  93. {
  94. MmioWrite8 (
  95. mRtcIndexRegister,
  96. (UINT8)(Address | (UINT8)(MmioRead8 (mRtcIndexRegister) & 0x80))
  97. );
  98. return MmioRead8 (mRtcTargetRegister);
  99. }
  100. /**
  101. Write RTC through its registers using MMIO access.
  102. @param Address Address offset of RTC. It is recommended to use
  103. macros such as RTC_ADDRESS_SECONDS.
  104. @param Data The content you want to write into RTC.
  105. **/
  106. STATIC
  107. VOID
  108. MmioRtcWrite (
  109. IN UINTN Address,
  110. IN UINT8 Data
  111. )
  112. {
  113. MmioWrite8 (
  114. mRtcIndexRegister,
  115. (UINT8)(Address | (UINT8)(MmioRead8 (mRtcIndexRegister) & 0x80))
  116. );
  117. MmioWrite8 (mRtcTargetRegister, Data);
  118. }
  119. /**
  120. Read RTC content through its registers.
  121. @param Address Address offset of RTC. It is recommended to use
  122. macros such as RTC_ADDRESS_SECONDS.
  123. @return The data of UINT8 type read from RTC.
  124. **/
  125. STATIC
  126. UINT8
  127. RtcRead (
  128. IN UINTN Address
  129. )
  130. {
  131. if (FeaturePcdGet (PcdRtcUseMmio)) {
  132. return MmioRtcRead (Address);
  133. }
  134. return IoRtcRead (Address);
  135. }
  136. /**
  137. Write RTC through its registers.
  138. @param Address Address offset of RTC. It is recommended to use
  139. macros such as RTC_ADDRESS_SECONDS.
  140. @param Data The content you want to write into RTC.
  141. **/
  142. STATIC
  143. VOID
  144. RtcWrite (
  145. IN UINTN Address,
  146. IN UINT8 Data
  147. )
  148. {
  149. if (FeaturePcdGet (PcdRtcUseMmio)) {
  150. MmioRtcWrite (Address, Data);
  151. } else {
  152. IoRtcWrite (Address, Data);
  153. }
  154. }
  155. /**
  156. Initialize RTC.
  157. @param Global For global use inside this module.
  158. @retval EFI_DEVICE_ERROR Initialization failed due to device error.
  159. @retval EFI_SUCCESS Initialization successful.
  160. **/
  161. EFI_STATUS
  162. PcRtcInit (
  163. IN PC_RTC_MODULE_GLOBALS *Global
  164. )
  165. {
  166. EFI_STATUS Status;
  167. RTC_REGISTER_A RegisterA;
  168. RTC_REGISTER_B RegisterB;
  169. RTC_REGISTER_D RegisterD;
  170. EFI_TIME Time;
  171. UINTN DataSize;
  172. UINT32 TimerVar;
  173. BOOLEAN Enabled;
  174. BOOLEAN Pending;
  175. //
  176. // Acquire RTC Lock to make access to RTC atomic
  177. //
  178. if (!EfiAtRuntime ()) {
  179. EfiAcquireLock (&Global->RtcLock);
  180. }
  181. //
  182. // Initialize RTC Register
  183. //
  184. // Make sure Division Chain is properly configured,
  185. // or RTC clock won't "tick" -- time won't increment
  186. //
  187. RegisterA.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterA);
  188. RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data);
  189. //
  190. // Read Register B
  191. //
  192. RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
  193. //
  194. // Clear RTC flag register
  195. //
  196. RtcRead (RTC_ADDRESS_REGISTER_C);
  197. //
  198. // Clear RTC register D
  199. //
  200. RegisterD.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterD);
  201. RtcWrite (RTC_ADDRESS_REGISTER_D, RegisterD.Data);
  202. //
  203. // Wait for up to 0.1 seconds for the RTC to be updated
  204. //
  205. Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
  206. if (EFI_ERROR (Status)) {
  207. //
  208. // Set the variable with default value if the RTC is functioning incorrectly.
  209. //
  210. Global->SavedTimeZone = EFI_UNSPECIFIED_TIMEZONE;
  211. Global->Daylight = 0;
  212. if (!EfiAtRuntime ()) {
  213. EfiReleaseLock (&Global->RtcLock);
  214. }
  215. return EFI_DEVICE_ERROR;
  216. }
  217. //
  218. // Get the Time/Date/Daylight Savings values.
  219. //
  220. Time.Second = RtcRead (RTC_ADDRESS_SECONDS);
  221. Time.Minute = RtcRead (RTC_ADDRESS_MINUTES);
  222. Time.Hour = RtcRead (RTC_ADDRESS_HOURS);
  223. Time.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
  224. Time.Month = RtcRead (RTC_ADDRESS_MONTH);
  225. Time.Year = RtcRead (RTC_ADDRESS_YEAR);
  226. //
  227. // Set RTC configuration after get original time
  228. // The value of bit AIE should be reserved.
  229. //
  230. RegisterB.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterB) | (RegisterB.Data & BIT5);
  231. RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
  232. //
  233. // Release RTC Lock.
  234. //
  235. if (!EfiAtRuntime ()) {
  236. EfiReleaseLock (&Global->RtcLock);
  237. }
  238. //
  239. // Get the data of Daylight saving and time zone, if they have been
  240. // stored in NV variable during previous boot.
  241. //
  242. DataSize = sizeof (UINT32);
  243. Status = EfiGetVariable (
  244. mTimeZoneVariableName,
  245. &gEfiCallerIdGuid,
  246. NULL,
  247. &DataSize,
  248. &TimerVar
  249. );
  250. if (!EFI_ERROR (Status)) {
  251. Time.TimeZone = (INT16)TimerVar;
  252. Time.Daylight = (UINT8)(TimerVar >> 16);
  253. } else {
  254. Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
  255. Time.Daylight = 0;
  256. }
  257. //
  258. // Validate time fields
  259. //
  260. Status = ConvertRtcTimeToEfiTime (&Time, RegisterB);
  261. if (!EFI_ERROR (Status)) {
  262. Status = RtcTimeFieldsValid (&Time);
  263. }
  264. if (EFI_ERROR (Status)) {
  265. //
  266. // Report Status Code to indicate that the RTC has bad date and time
  267. //
  268. REPORT_STATUS_CODE (
  269. EFI_ERROR_CODE | EFI_ERROR_MINOR,
  270. (EFI_SOFTWARE_DXE_RT_DRIVER | EFI_SW_EC_BAD_DATE_TIME)
  271. );
  272. Time.Second = RTC_INIT_SECOND;
  273. Time.Minute = RTC_INIT_MINUTE;
  274. Time.Hour = RTC_INIT_HOUR;
  275. Time.Day = RTC_INIT_DAY;
  276. Time.Month = RTC_INIT_MONTH;
  277. Time.Year = PcdGet16 (PcdMinimalValidYear);
  278. Time.Nanosecond = 0;
  279. Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
  280. Time.Daylight = 0;
  281. }
  282. //
  283. // Reset time value according to new RTC configuration
  284. //
  285. Status = PcRtcSetTime (&Time, Global);
  286. if (EFI_ERROR (Status)) {
  287. return EFI_DEVICE_ERROR;
  288. }
  289. //
  290. // Reset wakeup time value to valid state when wakeup alarm is disabled and wakeup time is invalid.
  291. // Global variable has already had valid SavedTimeZone and Daylight,
  292. // so we can use them to get and set wakeup time.
  293. //
  294. Status = PcRtcGetWakeupTime (&Enabled, &Pending, &Time, Global);
  295. if ((Enabled) || (!EFI_ERROR (Status))) {
  296. return EFI_SUCCESS;
  297. }
  298. //
  299. // When wakeup time is disabled and invalid, reset wakeup time register to valid state
  300. // but keep wakeup alarm disabled.
  301. //
  302. Time.Second = RTC_INIT_SECOND;
  303. Time.Minute = RTC_INIT_MINUTE;
  304. Time.Hour = RTC_INIT_HOUR;
  305. Time.Day = RTC_INIT_DAY;
  306. Time.Month = RTC_INIT_MONTH;
  307. Time.Year = PcdGet16 (PcdMinimalValidYear);
  308. Time.Nanosecond = 0;
  309. Time.TimeZone = Global->SavedTimeZone;
  310. Time.Daylight = Global->Daylight;
  311. //
  312. // Acquire RTC Lock to make access to RTC atomic
  313. //
  314. if (!EfiAtRuntime ()) {
  315. EfiAcquireLock (&Global->RtcLock);
  316. }
  317. //
  318. // Wait for up to 0.1 seconds for the RTC to be updated
  319. //
  320. Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
  321. if (EFI_ERROR (Status)) {
  322. if (!EfiAtRuntime ()) {
  323. EfiReleaseLock (&Global->RtcLock);
  324. }
  325. return EFI_DEVICE_ERROR;
  326. }
  327. ConvertEfiTimeToRtcTime (&Time, RegisterB);
  328. //
  329. // Set the Y/M/D info to variable as it has no corresponding hw registers.
  330. //
  331. Status = EfiSetVariable (
  332. L"RTCALARM",
  333. &gEfiCallerIdGuid,
  334. EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
  335. sizeof (Time),
  336. &Time
  337. );
  338. if (EFI_ERROR (Status)) {
  339. if (!EfiAtRuntime ()) {
  340. EfiReleaseLock (&Global->RtcLock);
  341. }
  342. return EFI_DEVICE_ERROR;
  343. }
  344. //
  345. // Inhibit updates of the RTC
  346. //
  347. RegisterB.Bits.Set = 1;
  348. RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
  349. //
  350. // Set RTC alarm time registers
  351. //
  352. RtcWrite (RTC_ADDRESS_SECONDS_ALARM, Time.Second);
  353. RtcWrite (RTC_ADDRESS_MINUTES_ALARM, Time.Minute);
  354. RtcWrite (RTC_ADDRESS_HOURS_ALARM, Time.Hour);
  355. //
  356. // Allow updates of the RTC registers
  357. //
  358. RegisterB.Bits.Set = 0;
  359. RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
  360. //
  361. // Release RTC Lock.
  362. //
  363. if (!EfiAtRuntime ()) {
  364. EfiReleaseLock (&Global->RtcLock);
  365. }
  366. return EFI_SUCCESS;
  367. }
  368. /**
  369. Returns the current time and date information, and the time-keeping capabilities
  370. of the hardware platform.
  371. @param Time A pointer to storage to receive a snapshot of the current time.
  372. @param Capabilities An optional pointer to a buffer to receive the real time clock
  373. device's capabilities.
  374. @param Global For global use inside this module.
  375. @retval EFI_SUCCESS The operation completed successfully.
  376. @retval EFI_INVALID_PARAMETER Time is NULL.
  377. @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error.
  378. **/
  379. EFI_STATUS
  380. PcRtcGetTime (
  381. OUT EFI_TIME *Time,
  382. OUT EFI_TIME_CAPABILITIES *Capabilities OPTIONAL,
  383. IN PC_RTC_MODULE_GLOBALS *Global
  384. )
  385. {
  386. EFI_STATUS Status;
  387. RTC_REGISTER_B RegisterB;
  388. //
  389. // Check parameters for null pointer
  390. //
  391. if (Time == NULL) {
  392. return EFI_INVALID_PARAMETER;
  393. }
  394. //
  395. // Acquire RTC Lock to make access to RTC atomic
  396. //
  397. if (!EfiAtRuntime ()) {
  398. EfiAcquireLock (&Global->RtcLock);
  399. }
  400. //
  401. // Wait for up to 0.1 seconds for the RTC to be updated
  402. //
  403. Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
  404. if (EFI_ERROR (Status)) {
  405. if (!EfiAtRuntime ()) {
  406. EfiReleaseLock (&Global->RtcLock);
  407. }
  408. return Status;
  409. }
  410. //
  411. // Read Register B
  412. //
  413. RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
  414. //
  415. // Get the Time/Date/Daylight Savings values.
  416. //
  417. Time->Second = RtcRead (RTC_ADDRESS_SECONDS);
  418. Time->Minute = RtcRead (RTC_ADDRESS_MINUTES);
  419. Time->Hour = RtcRead (RTC_ADDRESS_HOURS);
  420. Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
  421. Time->Month = RtcRead (RTC_ADDRESS_MONTH);
  422. Time->Year = RtcRead (RTC_ADDRESS_YEAR);
  423. //
  424. // Release RTC Lock.
  425. //
  426. if (!EfiAtRuntime ()) {
  427. EfiReleaseLock (&Global->RtcLock);
  428. }
  429. //
  430. // Get the variable that contains the TimeZone and Daylight fields
  431. //
  432. Time->TimeZone = Global->SavedTimeZone;
  433. Time->Daylight = Global->Daylight;
  434. //
  435. // Make sure all field values are in correct range
  436. //
  437. Status = ConvertRtcTimeToEfiTime (Time, RegisterB);
  438. if (!EFI_ERROR (Status)) {
  439. Status = RtcTimeFieldsValid (Time);
  440. }
  441. if (EFI_ERROR (Status)) {
  442. return EFI_DEVICE_ERROR;
  443. }
  444. //
  445. // Fill in Capabilities if it was passed in
  446. //
  447. if (Capabilities != NULL) {
  448. Capabilities->Resolution = 1;
  449. //
  450. // 1 hertz
  451. //
  452. Capabilities->Accuracy = 50000000;
  453. //
  454. // 50 ppm
  455. //
  456. Capabilities->SetsToZero = FALSE;
  457. }
  458. return EFI_SUCCESS;
  459. }
  460. /**
  461. Sets the current local time and date information.
  462. @param Time A pointer to the current time.
  463. @param Global For global use inside this module.
  464. @retval EFI_SUCCESS The operation completed successfully.
  465. @retval EFI_INVALID_PARAMETER A time field is out of range.
  466. @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error.
  467. **/
  468. EFI_STATUS
  469. PcRtcSetTime (
  470. IN EFI_TIME *Time,
  471. IN PC_RTC_MODULE_GLOBALS *Global
  472. )
  473. {
  474. EFI_STATUS Status;
  475. EFI_TIME RtcTime;
  476. RTC_REGISTER_B RegisterB;
  477. UINT32 TimerVar;
  478. if (Time == NULL) {
  479. return EFI_INVALID_PARAMETER;
  480. }
  481. //
  482. // Make sure that the time fields are valid
  483. //
  484. Status = RtcTimeFieldsValid (Time);
  485. if (EFI_ERROR (Status)) {
  486. return Status;
  487. }
  488. CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
  489. //
  490. // Acquire RTC Lock to make access to RTC atomic
  491. //
  492. if (!EfiAtRuntime ()) {
  493. EfiAcquireLock (&Global->RtcLock);
  494. }
  495. //
  496. // Wait for up to 0.1 seconds for the RTC to be updated
  497. //
  498. Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
  499. if (EFI_ERROR (Status)) {
  500. if (!EfiAtRuntime ()) {
  501. EfiReleaseLock (&Global->RtcLock);
  502. }
  503. return Status;
  504. }
  505. //
  506. // Write timezone and daylight to RTC variable
  507. //
  508. if ((Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) && (Time->Daylight == 0)) {
  509. Status = EfiSetVariable (
  510. mTimeZoneVariableName,
  511. &gEfiCallerIdGuid,
  512. 0,
  513. 0,
  514. NULL
  515. );
  516. if (Status == EFI_NOT_FOUND) {
  517. Status = EFI_SUCCESS;
  518. }
  519. } else {
  520. TimerVar = Time->Daylight;
  521. TimerVar = (UINT32)((TimerVar << 16) | (UINT16)(Time->TimeZone));
  522. Status = EfiSetVariable (
  523. mTimeZoneVariableName,
  524. &gEfiCallerIdGuid,
  525. EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
  526. sizeof (TimerVar),
  527. &TimerVar
  528. );
  529. }
  530. if (EFI_ERROR (Status)) {
  531. if (!EfiAtRuntime ()) {
  532. EfiReleaseLock (&Global->RtcLock);
  533. }
  534. return EFI_DEVICE_ERROR;
  535. }
  536. //
  537. // Read Register B, and inhibit updates of the RTC
  538. //
  539. RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
  540. RegisterB.Bits.Set = 1;
  541. RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
  542. //
  543. // Store the century value to RTC before converting to BCD format.
  544. //
  545. if (Global->CenturyRtcAddress != 0) {
  546. RtcWrite (Global->CenturyRtcAddress, DecimalToBcd8 ((UINT8)(RtcTime.Year / 100)));
  547. }
  548. ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);
  549. RtcWrite (RTC_ADDRESS_SECONDS, RtcTime.Second);
  550. RtcWrite (RTC_ADDRESS_MINUTES, RtcTime.Minute);
  551. RtcWrite (RTC_ADDRESS_HOURS, RtcTime.Hour);
  552. RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH, RtcTime.Day);
  553. RtcWrite (RTC_ADDRESS_MONTH, RtcTime.Month);
  554. RtcWrite (RTC_ADDRESS_YEAR, (UINT8)RtcTime.Year);
  555. //
  556. // Allow updates of the RTC registers
  557. //
  558. RegisterB.Bits.Set = 0;
  559. RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
  560. //
  561. // Release RTC Lock.
  562. //
  563. if (!EfiAtRuntime ()) {
  564. EfiReleaseLock (&Global->RtcLock);
  565. }
  566. //
  567. // Set the variable that contains the TimeZone and Daylight fields
  568. //
  569. Global->SavedTimeZone = Time->TimeZone;
  570. Global->Daylight = Time->Daylight;
  571. return EFI_SUCCESS;
  572. }
  573. /**
  574. Returns the current wakeup alarm clock setting.
  575. @param Enabled Indicates if the alarm is currently enabled or disabled.
  576. @param Pending Indicates if the alarm signal is pending and requires acknowledgment.
  577. @param Time The current alarm setting.
  578. @param Global For global use inside this module.
  579. @retval EFI_SUCCESS The alarm settings were returned.
  580. @retval EFI_INVALID_PARAMETER Enabled is NULL.
  581. @retval EFI_INVALID_PARAMETER Pending is NULL.
  582. @retval EFI_INVALID_PARAMETER Time is NULL.
  583. @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error.
  584. @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
  585. **/
  586. EFI_STATUS
  587. PcRtcGetWakeupTime (
  588. OUT BOOLEAN *Enabled,
  589. OUT BOOLEAN *Pending,
  590. OUT EFI_TIME *Time,
  591. IN PC_RTC_MODULE_GLOBALS *Global
  592. )
  593. {
  594. EFI_STATUS Status;
  595. RTC_REGISTER_B RegisterB;
  596. RTC_REGISTER_C RegisterC;
  597. EFI_TIME RtcTime;
  598. UINTN DataSize;
  599. //
  600. // Check parameters for null pointers
  601. //
  602. if ((Enabled == NULL) || (Pending == NULL) || (Time == NULL)) {
  603. return EFI_INVALID_PARAMETER;
  604. }
  605. //
  606. // Acquire RTC Lock to make access to RTC atomic
  607. //
  608. if (!EfiAtRuntime ()) {
  609. EfiAcquireLock (&Global->RtcLock);
  610. }
  611. //
  612. // Wait for up to 0.1 seconds for the RTC to be updated
  613. //
  614. Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
  615. if (EFI_ERROR (Status)) {
  616. if (!EfiAtRuntime ()) {
  617. EfiReleaseLock (&Global->RtcLock);
  618. }
  619. return EFI_DEVICE_ERROR;
  620. }
  621. //
  622. // Read Register B and Register C
  623. //
  624. RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
  625. RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C);
  626. //
  627. // Get the Time/Date/Daylight Savings values.
  628. //
  629. *Enabled = RegisterB.Bits.Aie;
  630. *Pending = RegisterC.Bits.Af;
  631. Time->Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM);
  632. Time->Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM);
  633. Time->Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM);
  634. Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
  635. Time->Month = RtcRead (RTC_ADDRESS_MONTH);
  636. Time->Year = RtcRead (RTC_ADDRESS_YEAR);
  637. Time->TimeZone = Global->SavedTimeZone;
  638. Time->Daylight = Global->Daylight;
  639. //
  640. // Get the alarm info from variable
  641. //
  642. DataSize = sizeof (EFI_TIME);
  643. Status = EfiGetVariable (
  644. L"RTCALARM",
  645. &gEfiCallerIdGuid,
  646. NULL,
  647. &DataSize,
  648. &RtcTime
  649. );
  650. if (!EFI_ERROR (Status)) {
  651. //
  652. // The alarm variable exists. In this case, we read variable to get info.
  653. //
  654. Time->Day = RtcTime.Day;
  655. Time->Month = RtcTime.Month;
  656. Time->Year = RtcTime.Year;
  657. }
  658. //
  659. // Release RTC Lock.
  660. //
  661. if (!EfiAtRuntime ()) {
  662. EfiReleaseLock (&Global->RtcLock);
  663. }
  664. //
  665. // Make sure all field values are in correct range
  666. //
  667. Status = ConvertRtcTimeToEfiTime (Time, RegisterB);
  668. if (!EFI_ERROR (Status)) {
  669. Status = RtcTimeFieldsValid (Time);
  670. }
  671. if (EFI_ERROR (Status)) {
  672. return EFI_DEVICE_ERROR;
  673. }
  674. return EFI_SUCCESS;
  675. }
  676. /**
  677. Sets the system wakeup alarm clock time.
  678. @param Enabled Enable or disable the wakeup alarm.
  679. @param Time If Enable is TRUE, the time to set the wakeup alarm for.
  680. If Enable is FALSE, then this parameter is optional, and may be NULL.
  681. @param Global For global use inside this module.
  682. @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled.
  683. If Enable is FALSE, then the wakeup alarm was disabled.
  684. @retval EFI_INVALID_PARAMETER A time field is out of range.
  685. @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error.
  686. @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
  687. **/
  688. EFI_STATUS
  689. PcRtcSetWakeupTime (
  690. IN BOOLEAN Enable,
  691. IN EFI_TIME *Time OPTIONAL,
  692. IN PC_RTC_MODULE_GLOBALS *Global
  693. )
  694. {
  695. EFI_STATUS Status;
  696. EFI_TIME RtcTime;
  697. RTC_REGISTER_B RegisterB;
  698. EFI_TIME_CAPABILITIES Capabilities;
  699. ZeroMem (&RtcTime, sizeof (RtcTime));
  700. if (Enable) {
  701. if (Time == NULL) {
  702. return EFI_INVALID_PARAMETER;
  703. }
  704. //
  705. // Make sure that the time fields are valid
  706. //
  707. Status = RtcTimeFieldsValid (Time);
  708. if (EFI_ERROR (Status)) {
  709. return EFI_INVALID_PARAMETER;
  710. }
  711. //
  712. // Just support set alarm time within 24 hours
  713. //
  714. PcRtcGetTime (&RtcTime, &Capabilities, Global);
  715. Status = RtcTimeFieldsValid (&RtcTime);
  716. if (EFI_ERROR (Status)) {
  717. return EFI_DEVICE_ERROR;
  718. }
  719. if (!IsWithinOneDay (&RtcTime, Time)) {
  720. return EFI_UNSUPPORTED;
  721. }
  722. //
  723. // Make a local copy of the time and date
  724. //
  725. CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
  726. }
  727. //
  728. // Acquire RTC Lock to make access to RTC atomic
  729. //
  730. if (!EfiAtRuntime ()) {
  731. EfiAcquireLock (&Global->RtcLock);
  732. }
  733. //
  734. // Wait for up to 0.1 seconds for the RTC to be updated
  735. //
  736. Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
  737. if (EFI_ERROR (Status)) {
  738. if (!EfiAtRuntime ()) {
  739. EfiReleaseLock (&Global->RtcLock);
  740. }
  741. return EFI_DEVICE_ERROR;
  742. }
  743. //
  744. // Read Register B
  745. //
  746. RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
  747. if (Enable) {
  748. ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);
  749. } else {
  750. //
  751. // if the alarm is disable, record the current setting.
  752. //
  753. RtcTime.Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM);
  754. RtcTime.Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM);
  755. RtcTime.Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM);
  756. RtcTime.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
  757. RtcTime.Month = RtcRead (RTC_ADDRESS_MONTH);
  758. RtcTime.Year = RtcRead (RTC_ADDRESS_YEAR);
  759. RtcTime.TimeZone = Global->SavedTimeZone;
  760. RtcTime.Daylight = Global->Daylight;
  761. }
  762. //
  763. // Set the Y/M/D info to variable as it has no corresponding hw registers.
  764. //
  765. Status = EfiSetVariable (
  766. L"RTCALARM",
  767. &gEfiCallerIdGuid,
  768. EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
  769. sizeof (RtcTime),
  770. &RtcTime
  771. );
  772. if (EFI_ERROR (Status)) {
  773. if (!EfiAtRuntime ()) {
  774. EfiReleaseLock (&Global->RtcLock);
  775. }
  776. return EFI_DEVICE_ERROR;
  777. }
  778. //
  779. // Inhibit updates of the RTC
  780. //
  781. RegisterB.Bits.Set = 1;
  782. RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
  783. if (Enable) {
  784. //
  785. // Set RTC alarm time
  786. //
  787. RtcWrite (RTC_ADDRESS_SECONDS_ALARM, RtcTime.Second);
  788. RtcWrite (RTC_ADDRESS_MINUTES_ALARM, RtcTime.Minute);
  789. RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour);
  790. RegisterB.Bits.Aie = 1;
  791. } else {
  792. RegisterB.Bits.Aie = 0;
  793. }
  794. //
  795. // Allow updates of the RTC registers
  796. //
  797. RegisterB.Bits.Set = 0;
  798. RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
  799. //
  800. // Release RTC Lock.
  801. //
  802. if (!EfiAtRuntime ()) {
  803. EfiReleaseLock (&Global->RtcLock);
  804. }
  805. return EFI_SUCCESS;
  806. }
  807. /**
  808. Checks an 8-bit BCD value, and converts to an 8-bit value if valid.
  809. This function checks the 8-bit BCD value specified by Value.
  810. If valid, the function converts it to an 8-bit value and returns it.
  811. Otherwise, return 0xff.
  812. @param Value The 8-bit BCD value to check and convert
  813. @return The 8-bit value converted. Or 0xff if Value is invalid.
  814. **/
  815. UINT8
  816. CheckAndConvertBcd8ToDecimal8 (
  817. IN UINT8 Value
  818. )
  819. {
  820. if ((Value < 0xa0) && ((Value & 0xf) < 0xa)) {
  821. return BcdToDecimal8 (Value);
  822. }
  823. return 0xff;
  824. }
  825. /**
  826. Converts time read from RTC to EFI_TIME format defined by UEFI spec.
  827. This function converts raw time data read from RTC to the EFI_TIME format
  828. defined by UEFI spec.
  829. If data mode of RTC is BCD, then converts it to decimal,
  830. If RTC is in 12-hour format, then converts it to 24-hour format.
  831. @param Time On input, the time data read from RTC to convert
  832. On output, the time converted to UEFI format
  833. @param RegisterB Value of Register B of RTC, indicating data mode
  834. and hour format.
  835. @retval EFI_INVALID_PARAMETER Parameters passed in are invalid.
  836. @retval EFI_SUCCESS Convert RTC time to EFI time successfully.
  837. **/
  838. EFI_STATUS
  839. ConvertRtcTimeToEfiTime (
  840. IN OUT EFI_TIME *Time,
  841. IN RTC_REGISTER_B RegisterB
  842. )
  843. {
  844. BOOLEAN IsPM;
  845. UINT8 Century;
  846. if ((Time->Hour & 0x80) != 0) {
  847. IsPM = TRUE;
  848. } else {
  849. IsPM = FALSE;
  850. }
  851. Time->Hour = (UINT8)(Time->Hour & 0x7f);
  852. if (RegisterB.Bits.Dm == 0) {
  853. Time->Year = CheckAndConvertBcd8ToDecimal8 ((UINT8)Time->Year);
  854. Time->Month = CheckAndConvertBcd8ToDecimal8 (Time->Month);
  855. Time->Day = CheckAndConvertBcd8ToDecimal8 (Time->Day);
  856. Time->Hour = CheckAndConvertBcd8ToDecimal8 (Time->Hour);
  857. Time->Minute = CheckAndConvertBcd8ToDecimal8 (Time->Minute);
  858. Time->Second = CheckAndConvertBcd8ToDecimal8 (Time->Second);
  859. }
  860. if ((Time->Year == 0xff) || (Time->Month == 0xff) || (Time->Day == 0xff) ||
  861. (Time->Hour == 0xff) || (Time->Minute == 0xff) || (Time->Second == 0xff))
  862. {
  863. return EFI_INVALID_PARAMETER;
  864. }
  865. //
  866. // For minimal/maximum year range [1970, 2069],
  867. // Century is 19 if RTC year >= 70,
  868. // Century is 20 otherwise.
  869. //
  870. Century = (UINT8)(PcdGet16 (PcdMinimalValidYear) / 100);
  871. if (Time->Year < PcdGet16 (PcdMinimalValidYear) % 100) {
  872. Century++;
  873. }
  874. Time->Year = (UINT16)(Century * 100 + Time->Year);
  875. //
  876. // If time is in 12 hour format, convert it to 24 hour format
  877. //
  878. if (RegisterB.Bits.Mil == 0) {
  879. if (IsPM && (Time->Hour < 12)) {
  880. Time->Hour = (UINT8)(Time->Hour + 12);
  881. }
  882. if (!IsPM && (Time->Hour == 12)) {
  883. Time->Hour = 0;
  884. }
  885. }
  886. Time->Nanosecond = 0;
  887. return EFI_SUCCESS;
  888. }
  889. /**
  890. Wait for a period for the RTC to be ready.
  891. @param Timeout Tell how long it should take to wait.
  892. @retval EFI_DEVICE_ERROR RTC device error.
  893. @retval EFI_SUCCESS RTC is updated and ready.
  894. **/
  895. EFI_STATUS
  896. RtcWaitToUpdate (
  897. UINTN Timeout
  898. )
  899. {
  900. RTC_REGISTER_A RegisterA;
  901. RTC_REGISTER_D RegisterD;
  902. //
  903. // See if the RTC is functioning correctly
  904. //
  905. RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
  906. if (RegisterD.Bits.Vrt == 0) {
  907. return EFI_DEVICE_ERROR;
  908. }
  909. //
  910. // Wait for up to 0.1 seconds for the RTC to be ready.
  911. //
  912. Timeout = (Timeout / 10) + 1;
  913. RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
  914. while (RegisterA.Bits.Uip == 1 && Timeout > 0) {
  915. MicroSecondDelay (10);
  916. RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
  917. Timeout--;
  918. }
  919. RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
  920. if ((Timeout == 0) || (RegisterD.Bits.Vrt == 0)) {
  921. return EFI_DEVICE_ERROR;
  922. }
  923. return EFI_SUCCESS;
  924. }
  925. /**
  926. See if all fields of a variable of EFI_TIME type is correct.
  927. @param Time The time to be checked.
  928. @retval EFI_INVALID_PARAMETER Some fields of Time are not correct.
  929. @retval EFI_SUCCESS Time is a valid EFI_TIME variable.
  930. **/
  931. EFI_STATUS
  932. RtcTimeFieldsValid (
  933. IN EFI_TIME *Time
  934. )
  935. {
  936. if ((Time->Year < PcdGet16 (PcdMinimalValidYear)) ||
  937. (Time->Year > PcdGet16 (PcdMaximalValidYear)) ||
  938. (Time->Month < 1) ||
  939. (Time->Month > 12) ||
  940. (!DayValid (Time)) ||
  941. (Time->Hour > 23) ||
  942. (Time->Minute > 59) ||
  943. (Time->Second > 59) ||
  944. (Time->Nanosecond > 999999999) ||
  945. (!((Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) || ((Time->TimeZone >= -1440) && (Time->TimeZone <= 1440)))) ||
  946. ((Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT))) != 0))
  947. {
  948. return EFI_INVALID_PARAMETER;
  949. }
  950. return EFI_SUCCESS;
  951. }
  952. /**
  953. See if field Day of an EFI_TIME is correct.
  954. @param Time Its Day field is to be checked.
  955. @retval TRUE Day field of Time is correct.
  956. @retval FALSE Day field of Time is NOT correct.
  957. **/
  958. BOOLEAN
  959. DayValid (
  960. IN EFI_TIME *Time
  961. )
  962. {
  963. //
  964. // The validity of Time->Month field should be checked before
  965. //
  966. ASSERT (Time->Month >= 1);
  967. ASSERT (Time->Month <= 12);
  968. if ((Time->Day < 1) ||
  969. (Time->Day > mDayOfMonth[Time->Month - 1]) ||
  970. ((Time->Month == 2) && (!IsLeapYear (Time) && (Time->Day > 28)))
  971. )
  972. {
  973. return FALSE;
  974. }
  975. return TRUE;
  976. }
  977. /**
  978. Check if it is a leap year.
  979. @param Time The time to be checked.
  980. @retval TRUE It is a leap year.
  981. @retval FALSE It is NOT a leap year.
  982. **/
  983. BOOLEAN
  984. IsLeapYear (
  985. IN EFI_TIME *Time
  986. )
  987. {
  988. if (Time->Year % 4 == 0) {
  989. if (Time->Year % 100 == 0) {
  990. if (Time->Year % 400 == 0) {
  991. return TRUE;
  992. } else {
  993. return FALSE;
  994. }
  995. } else {
  996. return TRUE;
  997. }
  998. } else {
  999. return FALSE;
  1000. }
  1001. }
  1002. /**
  1003. Converts time from EFI_TIME format defined by UEFI spec to RTC format.
  1004. This function converts time from EFI_TIME format defined by UEFI spec to RTC format.
  1005. If data mode of RTC is BCD, then converts EFI_TIME to it.
  1006. If RTC is in 12-hour format, then converts EFI_TIME to it.
  1007. @param Time On input, the time data read from UEFI to convert
  1008. On output, the time converted to RTC format
  1009. @param RegisterB Value of Register B of RTC, indicating data mode
  1010. **/
  1011. VOID
  1012. ConvertEfiTimeToRtcTime (
  1013. IN OUT EFI_TIME *Time,
  1014. IN RTC_REGISTER_B RegisterB
  1015. )
  1016. {
  1017. BOOLEAN IsPM;
  1018. IsPM = TRUE;
  1019. //
  1020. // Adjust hour field if RTC is in 12 hour mode
  1021. //
  1022. if (RegisterB.Bits.Mil == 0) {
  1023. if (Time->Hour < 12) {
  1024. IsPM = FALSE;
  1025. }
  1026. if (Time->Hour >= 13) {
  1027. Time->Hour = (UINT8)(Time->Hour - 12);
  1028. } else if (Time->Hour == 0) {
  1029. Time->Hour = 12;
  1030. }
  1031. }
  1032. //
  1033. // Set the Time/Date values.
  1034. //
  1035. Time->Year = (UINT16)(Time->Year % 100);
  1036. if (RegisterB.Bits.Dm == 0) {
  1037. Time->Year = DecimalToBcd8 ((UINT8)Time->Year);
  1038. Time->Month = DecimalToBcd8 (Time->Month);
  1039. Time->Day = DecimalToBcd8 (Time->Day);
  1040. Time->Hour = DecimalToBcd8 (Time->Hour);
  1041. Time->Minute = DecimalToBcd8 (Time->Minute);
  1042. Time->Second = DecimalToBcd8 (Time->Second);
  1043. }
  1044. //
  1045. // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.
  1046. //
  1047. if ((RegisterB.Bits.Mil == 0) && IsPM) {
  1048. Time->Hour = (UINT8)(Time->Hour | 0x80);
  1049. }
  1050. }
  1051. /**
  1052. Compare the Hour, Minute and Second of the From time and the To time.
  1053. Only compare H/M/S in EFI_TIME and ignore other fields here.
  1054. @param From the first time
  1055. @param To the second time
  1056. @return >0 The H/M/S of the From time is later than those of To time
  1057. @return ==0 The H/M/S of the From time is same as those of To time
  1058. @return <0 The H/M/S of the From time is earlier than those of To time
  1059. **/
  1060. INTN
  1061. CompareHMS (
  1062. IN EFI_TIME *From,
  1063. IN EFI_TIME *To
  1064. )
  1065. {
  1066. if ((From->Hour > To->Hour) ||
  1067. ((From->Hour == To->Hour) && (From->Minute > To->Minute)) ||
  1068. ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second > To->Second)))
  1069. {
  1070. return 1;
  1071. } else if ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second == To->Second)) {
  1072. return 0;
  1073. } else {
  1074. return -1;
  1075. }
  1076. }
  1077. /**
  1078. To check if second date is later than first date within 24 hours.
  1079. @param From the first date
  1080. @param To the second date
  1081. @retval TRUE From is previous to To within 24 hours.
  1082. @retval FALSE From is later, or it is previous to To more than 24 hours.
  1083. **/
  1084. BOOLEAN
  1085. IsWithinOneDay (
  1086. IN EFI_TIME *From,
  1087. IN EFI_TIME *To
  1088. )
  1089. {
  1090. BOOLEAN Adjacent;
  1091. Adjacent = FALSE;
  1092. //
  1093. // The validity of From->Month field should be checked before
  1094. //
  1095. ASSERT (From->Month >= 1);
  1096. ASSERT (From->Month <= 12);
  1097. if (From->Year == To->Year) {
  1098. if (From->Month == To->Month) {
  1099. if ((From->Day + 1) == To->Day) {
  1100. if ((CompareHMS (From, To) >= 0)) {
  1101. Adjacent = TRUE;
  1102. }
  1103. } else if (From->Day == To->Day) {
  1104. if ((CompareHMS (From, To) <= 0)) {
  1105. Adjacent = TRUE;
  1106. }
  1107. }
  1108. } else if (((From->Month + 1) == To->Month) && (To->Day == 1)) {
  1109. if ((From->Month == 2) && !IsLeapYear (From)) {
  1110. if (From->Day == 28) {
  1111. if ((CompareHMS (From, To) >= 0)) {
  1112. Adjacent = TRUE;
  1113. }
  1114. }
  1115. } else if (From->Day == mDayOfMonth[From->Month - 1]) {
  1116. if ((CompareHMS (From, To) >= 0)) {
  1117. Adjacent = TRUE;
  1118. }
  1119. }
  1120. }
  1121. } else if (((From->Year + 1) == To->Year) &&
  1122. (From->Month == 12) &&
  1123. (From->Day == 31) &&
  1124. (To->Month == 1) &&
  1125. (To->Day == 1))
  1126. {
  1127. if ((CompareHMS (From, To) >= 0)) {
  1128. Adjacent = TRUE;
  1129. }
  1130. }
  1131. return Adjacent;
  1132. }
  1133. /**
  1134. Get the century RTC address from the ACPI FADT table.
  1135. @return The century RTC address or 0 if not found.
  1136. **/
  1137. UINT8
  1138. GetCenturyRtcAddress (
  1139. VOID
  1140. )
  1141. {
  1142. EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt;
  1143. Fadt = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *)EfiLocateFirstAcpiTable (
  1144. EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
  1145. );
  1146. if ((Fadt != NULL) &&
  1147. (Fadt->Century > RTC_ADDRESS_REGISTER_D) && (Fadt->Century < 0x80)
  1148. )
  1149. {
  1150. return Fadt->Century;
  1151. } else {
  1152. return 0;
  1153. }
  1154. }
  1155. /**
  1156. Notification function of ACPI Table change.
  1157. This is a notification function registered on ACPI Table change event.
  1158. It saves the Century address stored in ACPI FADT table.
  1159. @param Event Event whose notification function is being invoked.
  1160. @param Context Pointer to the notification function's context.
  1161. **/
  1162. VOID
  1163. EFIAPI
  1164. PcRtcAcpiTableChangeCallback (
  1165. IN EFI_EVENT Event,
  1166. IN VOID *Context
  1167. )
  1168. {
  1169. EFI_STATUS Status;
  1170. EFI_TIME Time;
  1171. UINT8 CenturyRtcAddress;
  1172. UINT8 Century;
  1173. CenturyRtcAddress = GetCenturyRtcAddress ();
  1174. if ((CenturyRtcAddress != 0) && (mModuleGlobal.CenturyRtcAddress != CenturyRtcAddress)) {
  1175. mModuleGlobal.CenturyRtcAddress = CenturyRtcAddress;
  1176. Status = PcRtcGetTime (&Time, NULL, &mModuleGlobal);
  1177. if (!EFI_ERROR (Status)) {
  1178. Century = (UINT8)(Time.Year / 100);
  1179. Century = DecimalToBcd8 (Century);
  1180. DEBUG ((DEBUG_INFO, "PcRtc: Write 0x%x to CMOS location 0x%x\n", Century, mModuleGlobal.CenturyRtcAddress));
  1181. RtcWrite (mModuleGlobal.CenturyRtcAddress, Century);
  1182. }
  1183. }
  1184. }