PcRtc.c 33 KB

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