]> git.proxmox.com Git - mirror_edk2.git/blame - EmbeddedPkg/Library/HalRuntimeServicesExampleLib/Rtc.c
Update the copyright notice format
[mirror_edk2.git] / EmbeddedPkg / Library / HalRuntimeServicesExampleLib / Rtc.c
CommitLineData
2ef2b01e
A
1/** @file\r
2 Simple PC RTC\r
3\r
60274cca
HT
4 Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>\r
5 Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>\r
2ef2b01e 6\r
60274cca 7 This program and the accompanying materials\r
2ef2b01e
A
8 are licensed and made available under the terms and conditions of the BSD License\r
9 which accompanies this distribution. The full text of the license may be found at\r
10 http://opensource.org/licenses/bsd-license.php\r
11\r
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
14\r
15\r
16**/\r
17 \r
18\r
19\r
20typedef struct {\r
21 EFI_LOCK RtcLock;\r
22 UINT16 SavedTimeZone;\r
23 UINT8 Daylight;\r
24} PC_RTC_GLOBALS;\r
25\r
26#define PCAT_RTC_ADDRESS_REGISTER 0x70\r
27#define PCAT_RTC_DATA_REGISTER 0x71\r
28\r
29//\r
30// Dallas DS12C887 Real Time Clock\r
31//\r
32#define RTC_ADDRESS_SECONDS 0 // R/W Range 0..59\r
33#define RTC_ADDRESS_SECONDS_ALARM 1 // R/W Range 0..59\r
34#define RTC_ADDRESS_MINUTES 2 // R/W Range 0..59\r
35#define RTC_ADDRESS_MINUTES_ALARM 3 // R/W Range 0..59\r
36#define RTC_ADDRESS_HOURS 4 // R/W Range 1..12 or 0..23 Bit 7 is AM/PM\r
37#define RTC_ADDRESS_HOURS_ALARM 5 // R/W Range 1..12 or 0..23 Bit 7 is AM/PM\r
38#define RTC_ADDRESS_DAY_OF_THE_WEEK 6 // R/W Range 1..7\r
39#define RTC_ADDRESS_DAY_OF_THE_MONTH 7 // R/W Range 1..31\r
40#define RTC_ADDRESS_MONTH 8 // R/W Range 1..12\r
41#define RTC_ADDRESS_YEAR 9 // R/W Range 0..99\r
42#define RTC_ADDRESS_REGISTER_A 10 // R/W[0..6] R0[7]\r
43#define RTC_ADDRESS_REGISTER_B 11 // R/W\r
44#define RTC_ADDRESS_REGISTER_C 12 // RO\r
45#define RTC_ADDRESS_REGISTER_D 13 // RO\r
46#define RTC_ADDRESS_CENTURY 50 // R/W Range 19..20 Bit 8 is R/W\r
47//\r
48// Date and time initial values.\r
49// They are used if the RTC values are invalid during driver initialization\r
50//\r
51#define RTC_INIT_SECOND 0\r
52#define RTC_INIT_MINUTE 0\r
53#define RTC_INIT_HOUR 0\r
54#define RTC_INIT_DAY 1\r
55#define RTC_INIT_MONTH 1\r
56#define RTC_INIT_YEAR 2001\r
57\r
58//\r
59// Register initial values\r
60//\r
61#define RTC_INIT_REGISTER_A 0x26\r
62#define RTC_INIT_REGISTER_B 0x02\r
63#define RTC_INIT_REGISTER_D 0x0\r
64\r
65#pragma pack(1)\r
66//\r
67// Register A\r
68//\r
69typedef struct {\r
70 UINT8 RS : 4; // Rate Selection Bits\r
71 UINT8 DV : 3; // Divisor\r
72 UINT8 UIP : 1; // Update in progress\r
73} RTC_REGISTER_A_BITS;\r
74\r
75typedef union {\r
76 RTC_REGISTER_A_BITS Bits;\r
77 UINT8 Data;\r
78} RTC_REGISTER_A;\r
79\r
80//\r
81// Register B\r
82//\r
83typedef struct {\r
84 UINT8 DSE : 1; // 0 - Daylight saving disabled 1 - Daylight savings enabled\r
85 UINT8 MIL : 1; // 0 - 12 hour mode 1 - 24 hour mode\r
86 UINT8 DM : 1; // 0 - BCD Format 1 - Binary Format\r
87 UINT8 SQWE : 1; // 0 - Disable SQWE output 1 - Enable SQWE output\r
88 UINT8 UIE : 1; // 0 - Update INT disabled 1 - Update INT enabled\r
89 UINT8 AIE : 1; // 0 - Alarm INT disabled 1 - Alarm INT Enabled\r
90 UINT8 PIE : 1; // 0 - Periodic INT disabled 1 - Periodic INT Enabled\r
91 UINT8 SET : 1; // 0 - Normal operation. 1 - Updates inhibited\r
92} RTC_REGISTER_B_BITS;\r
93\r
94typedef union {\r
95 RTC_REGISTER_B_BITS Bits;\r
96 UINT8 Data;\r
97} RTC_REGISTER_B;\r
98\r
99//\r
100// Register C\r
101//\r
102typedef struct {\r
103 UINT8 Reserved : 4; // Read as zero. Can not be written.\r
104 UINT8 UF : 1; // Update End Interrupt Flag\r
105 UINT8 AF : 1; // Alarm Interrupt Flag\r
106 UINT8 PF : 1; // Periodic Interrupt Flag\r
107 UINT8 IRQF : 1; // Iterrupt Request Flag = PF & PIE | AF & AIE | UF & UIE\r
108} RTC_REGISTER_C_BITS;\r
109\r
110typedef union {\r
111 RTC_REGISTER_C_BITS Bits;\r
112 UINT8 Data;\r
113} RTC_REGISTER_C;\r
114\r
115//\r
116// Register D\r
117//\r
118typedef struct {\r
119 UINT8 Reserved : 7; // Read as zero. Can not be written.\r
120 UINT8 VRT : 1; // Valid RAM and Time\r
121} RTC_REGISTER_D_BITS;\r
122\r
123typedef union {\r
124 RTC_REGISTER_D_BITS Bits;\r
125 UINT8 Data;\r
126} RTC_REGISTER_D;\r
127\r
128#pragma pack()\r
129\r
130PC_RTC_GLOBALS mRtc;\r
131\r
132BOOLEAN\r
133IsLeapYear (\r
134 IN EFI_TIME *Time\r
135 )\r
136{\r
137 if (Time->Year % 4 == 0) {\r
138 if (Time->Year % 100 == 0) {\r
139 if (Time->Year % 400 == 0) {\r
140 return TRUE;\r
141 } else {\r
142 return FALSE;\r
143 }\r
144 } else {\r
145 return TRUE;\r
146 }\r
147 } else {\r
148 return FALSE;\r
149 }\r
150}\r
151\r
152\r
153const INTN mDayOfMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };\r
154\r
155BOOLEAN\r
156DayValid (\r
157 IN EFI_TIME *Time\r
158 )\r
159{\r
160 if (Time->Day < 1 ||\r
161 Time->Day > mDayOfMonth[Time->Month - 1] ||\r
162 (Time->Month == 2 && (!IsLeapYear (Time) && Time->Day > 28))\r
163 ) {\r
164 return FALSE;\r
165 }\r
166\r
167 return TRUE;\r
168}\r
169\r
170\r
171UINT8\r
172DecimaltoBcd (\r
173 IN UINT8 DecValue\r
174 )\r
175{\r
176 UINTN High;\r
177 UINTN Low;\r
178\r
179 High = DecValue / 10;\r
180 Low = DecValue - (High * 10);\r
181\r
182 return (UINT8) (Low + (High << 4));\r
183}\r
184\r
185UINT8\r
186BcdToDecimal (\r
187 IN UINT8 BcdValue\r
188 )\r
189{\r
190 UINTN High;\r
191 UINTN Low;\r
192\r
193 High = BcdValue >> 4;\r
194 Low = BcdValue - (High << 4);\r
195\r
196 return (UINT8) (Low + (High * 10));\r
197}\r
198\r
199\r
200\r
201\r
202VOID\r
203ConvertEfiTimeToRtcTime (\r
204 IN EFI_TIME *Time,\r
205 IN RTC_REGISTER_B RegisterB,\r
206 IN UINT8 *Century\r
207 )\r
208{\r
209 BOOLEAN PM;\r
210\r
211 PM = TRUE;\r
212 //\r
213 // Adjust hour field if RTC in in 12 hour mode\r
214 //\r
215 if (RegisterB.Bits.MIL == 0) {\r
216 if (Time->Hour < 12) {\r
217 PM = FALSE;\r
218 }\r
219\r
220 if (Time->Hour >= 13) {\r
221 Time->Hour = (UINT8) (Time->Hour - 12);\r
222 } else if (Time->Hour == 0) {\r
223 Time->Hour = 12;\r
224 }\r
225 }\r
226 //\r
227 // Set the Time/Date/Daylight Savings values.\r
228 //\r
229 *Century = DecimaltoBcd ((UINT8) (Time->Year / 100));\r
230\r
231 Time->Year = (UINT16) (Time->Year % 100);\r
232\r
233 if (RegisterB.Bits.DM == 0) {\r
234 Time->Year = DecimaltoBcd ((UINT8) Time->Year);\r
235 Time->Month = DecimaltoBcd (Time->Month);\r
236 Time->Day = DecimaltoBcd (Time->Day);\r
237 Time->Hour = DecimaltoBcd (Time->Hour);\r
238 Time->Minute = DecimaltoBcd (Time->Minute);\r
239 Time->Second = DecimaltoBcd (Time->Second);\r
240 }\r
241 //\r
242 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.\r
243 //\r
244 if (RegisterB.Bits.MIL == 0 && PM) {\r
245 Time->Hour = (UINT8) (Time->Hour | 0x80);\r
246 }\r
247}\r
248\r
249EFI_STATUS\r
250RtcTimeFieldsValid (\r
251 IN EFI_TIME *Time\r
252 )\r
253/*++\r
254\r
255Routine Description:\r
256\r
257 Arguments:\r
258 \r
259 Returns: \r
260--*/\r
261// TODO: Time - add argument and description to function comment\r
262// TODO: EFI_INVALID_PARAMETER - add return value to function comment\r
263// TODO: EFI_SUCCESS - add return value to function comment\r
264{\r
265 if (Time->Year < 1998 ||\r
266 Time->Year > 2099 ||\r
267 Time->Month < 1 ||\r
268 Time->Month > 12 ||\r
269 (!DayValid (Time)) ||\r
270 Time->Hour > 23 ||\r
271 Time->Minute > 59 ||\r
272 Time->Second > 59 ||\r
273 Time->Nanosecond > 999999999 ||\r
274 (!(Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE || (Time->TimeZone >= -1440 && Time->TimeZone <= 1440))) ||\r
275 (Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT)))\r
276 ) {\r
277 return EFI_INVALID_PARAMETER;\r
278 }\r
279\r
280 return EFI_SUCCESS;\r
281}\r
282\r
283UINT8\r
284RtcRead (\r
285 IN UINT8 Address\r
286 )\r
287{\r
288 IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80)));\r
289 return IoRead8 (PCAT_RTC_DATA_REGISTER);\r
290}\r
291\r
292VOID\r
293RtcWrite (\r
294 IN UINT8 Address,\r
295 IN UINT8 Data\r
296 )\r
297{\r
298 IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80)));\r
299 IoWrite8 (PCAT_RTC_DATA_REGISTER, Data);\r
300}\r
301\r
302\r
303EFI_STATUS\r
304RtcTestCenturyRegister (\r
305 VOID\r
306 )\r
307{\r
308 UINT8 Century;\r
309 UINT8 Temp;\r
310\r
311 Century = RtcRead (RTC_ADDRESS_CENTURY);\r
312 //\r
313 // RtcWrite (RTC_ADDRESS_CENTURY, 0x00);\r
314 //\r
315 Temp = (UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f);\r
316 RtcWrite (RTC_ADDRESS_CENTURY, Century);\r
317 if (Temp == 0x19 || Temp == 0x20) {\r
318 return EFI_SUCCESS;\r
319 }\r
320\r
321 return EFI_DEVICE_ERROR;\r
322}\r
323\r
324VOID\r
325ConvertRtcTimeToEfiTime (\r
326 IN EFI_TIME *Time,\r
327 IN RTC_REGISTER_B RegisterB\r
328 )\r
329{\r
330 BOOLEAN PM;\r
331\r
332 if ((Time->Hour) & 0x80) {\r
333 PM = TRUE;\r
334 } else {\r
335 PM = FALSE;\r
336 }\r
337\r
338 Time->Hour = (UINT8) (Time->Hour & 0x7f);\r
339\r
340 if (RegisterB.Bits.DM == 0) {\r
341 Time->Year = BcdToDecimal ((UINT8) Time->Year);\r
342 Time->Month = BcdToDecimal (Time->Month);\r
343 Time->Day = BcdToDecimal (Time->Day);\r
344 Time->Hour = BcdToDecimal (Time->Hour);\r
345 Time->Minute = BcdToDecimal (Time->Minute);\r
346 Time->Second = BcdToDecimal (Time->Second);\r
347 }\r
348 //\r
349 // If time is in 12 hour format, convert it to 24 hour format\r
350 //\r
351 if (RegisterB.Bits.MIL == 0) {\r
352 if (PM && Time->Hour < 12) {\r
353 Time->Hour = (UINT8) (Time->Hour + 12);\r
354 }\r
355\r
356 if (!PM && Time->Hour == 12) {\r
357 Time->Hour = 0;\r
358 }\r
359 }\r
360\r
361 Time->Nanosecond = 0;\r
362 Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
363 Time->Daylight = 0;\r
364}\r
365\r
366EFI_STATUS\r
367RtcWaitToUpdate (\r
368 UINTN Timeout\r
369 )\r
370{\r
371 RTC_REGISTER_A RegisterA;\r
372 RTC_REGISTER_D RegisterD;\r
373\r
374 //\r
375 // See if the RTC is functioning correctly\r
376 //\r
377 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);\r
378\r
379 if (RegisterD.Bits.VRT == 0) {\r
380 return EFI_DEVICE_ERROR;\r
381 }\r
382 //\r
383 // Wait for up to 0.1 seconds for the RTC to be ready.\r
384 //\r
385 Timeout = (Timeout / 10) + 1;\r
386 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);\r
387 while (RegisterA.Bits.UIP == 1 && Timeout > 0) {\r
388 MicroSecondDelay (10);\r
389 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);\r
390 Timeout--;\r
391 }\r
392\r
393 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);\r
394 if (Timeout == 0 || RegisterD.Bits.VRT == 0) {\r
395 return EFI_DEVICE_ERROR;\r
396 }\r
397\r
398 return EFI_SUCCESS;\r
399}\r
400\r
401EFI_STATUS\r
402LibGetTime (\r
403 OUT EFI_TIME *Time,\r
404 OUT EFI_TIME_CAPABILITIES *Capabilities\r
405 )\r
406{\r
407 EFI_STATUS Status;\r
408 RTC_REGISTER_B RegisterB;\r
409 UINT8 Century;\r
410 UINTN BufferSize;\r
411\r
412 //\r
413 // Check parameters for null pointer\r
414 //\r
415 if (Time == NULL) {\r
416 return EFI_INVALID_PARAMETER;\r
417\r
418 }\r
419 //\r
420 // Acquire RTC Lock to make access to RTC atomic\r
421 //\r
422 EfiAcquireLock (&mRtc.RtcLock);\r
423\r
424 //\r
425 // Wait for up to 0.1 seconds for the RTC to be updated\r
426 //\r
427 Status = RtcWaitToUpdate (100000);\r
428 if (EFI_ERROR (Status)) {\r
429 EfiReleaseLock (&mRtc.RtcLock);\r
430 return Status;\r
431 }\r
432 //\r
433 // Read Register B\r
434 //\r
435 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
436\r
437 //\r
438 // Get the Time/Date/Daylight Savings values.\r
439 //\r
440 Time->Second = RtcRead (RTC_ADDRESS_SECONDS);\r
441 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES);\r
442 Time->Hour = RtcRead (RTC_ADDRESS_HOURS);\r
443 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r
444 Time->Month = RtcRead (RTC_ADDRESS_MONTH);\r
445 Time->Year = RtcRead (RTC_ADDRESS_YEAR);\r
446\r
447 ConvertRtcTimeToEfiTime (Time, RegisterB);\r
448\r
449 if (RtcTestCenturyRegister () == EFI_SUCCESS) {\r
450 Century = BcdToDecimal ((UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f));\r
451 } else {\r
452 Century = BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY));\r
453 }\r
454\r
455 Time->Year = (UINT16) (Century * 100 + Time->Year);\r
456\r
457 //\r
458 // Release RTC Lock.\r
459 //\r
460 EfiReleaseLock (&mRtc.RtcLock);\r
461\r
462 //\r
463 // Get the variable that containts the TimeZone and Daylight fields\r
464 //\r
465 Time->TimeZone = mRtc.SavedTimeZone;\r
466 Time->Daylight = mRtc.Daylight;\r
467\r
468 BufferSize = sizeof (INT16) + sizeof (UINT8);\r
469\r
470 //\r
471 // Make sure all field values are in correct range\r
472 //\r
473 Status = RtcTimeFieldsValid (Time);\r
474 if (EFI_ERROR (Status)) {\r
475 return EFI_DEVICE_ERROR;\r
476 }\r
477 //\r
478 // Fill in Capabilities if it was passed in\r
479 //\r
480 if (Capabilities) {\r
481 Capabilities->Resolution = 1;\r
482 //\r
483 // 1 hertz\r
484 //\r
485 Capabilities->Accuracy = 50000000;\r
486 //\r
487 // 50 ppm\r
488 //\r
489 Capabilities->SetsToZero = FALSE;\r
490 }\r
491\r
492 return EFI_SUCCESS;\r
493}\r
494\r
495\r
496\r
497EFI_STATUS\r
498LibSetTime (\r
499 IN EFI_TIME *Time\r
500 )\r
501{\r
502 EFI_STATUS Status;\r
503 EFI_TIME RtcTime;\r
504 RTC_REGISTER_B RegisterB;\r
505 UINT8 Century;\r
506\r
507 if (Time == NULL) {\r
508 return EFI_INVALID_PARAMETER;\r
509 }\r
510 //\r
511 // Make sure that the time fields are valid\r
512 //\r
513 Status = RtcTimeFieldsValid (Time);\r
514 if (EFI_ERROR (Status)) {\r
515 return Status;\r
516 }\r
517\r
518 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));\r
519\r
520 //\r
521 // Acquire RTC Lock to make access to RTC atomic\r
522 //\r
523 EfiAcquireLock (&mRtc.RtcLock);\r
524\r
525 //\r
526 // Wait for up to 0.1 seconds for the RTC to be updated\r
527 //\r
528 Status = RtcWaitToUpdate (100000);\r
529 if (EFI_ERROR (Status)) {\r
530 EfiReleaseLock (&mRtc.RtcLock);\r
531 return Status;\r
532 }\r
533 //\r
534 // Read Register B, and inhibit updates of the RTC\r
535 //\r
536 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
537 RegisterB.Bits.SET = 1;\r
538 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
539\r
540 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century);\r
541\r
542 RtcWrite (RTC_ADDRESS_SECONDS, RtcTime.Second);\r
543 RtcWrite (RTC_ADDRESS_MINUTES, RtcTime.Minute);\r
544 RtcWrite (RTC_ADDRESS_HOURS, RtcTime.Hour);\r
545 RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH, RtcTime.Day);\r
546 RtcWrite (RTC_ADDRESS_MONTH, RtcTime.Month);\r
547 RtcWrite (RTC_ADDRESS_YEAR, (UINT8) RtcTime.Year);\r
548 if (RtcTestCenturyRegister () == EFI_SUCCESS) {\r
549 Century = (UINT8) ((Century & 0x7f) | (RtcRead (RTC_ADDRESS_CENTURY) & 0x80));\r
550 }\r
551\r
552 RtcWrite (RTC_ADDRESS_CENTURY, Century);\r
553\r
554 //\r
555 // Allow updates of the RTC registers\r
556 //\r
557 RegisterB.Bits.SET = 0;\r
558 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
559\r
560 //\r
561 // Release RTC Lock.\r
562 //\r
563 EfiReleaseLock (&mRtc.RtcLock);\r
564\r
565 //\r
566 // Set the variable that containts the TimeZone and Daylight fields\r
567 //\r
568 mRtc.SavedTimeZone = Time->TimeZone;\r
569 mRtc.Daylight = Time->Daylight;\r
570 return Status;\r
571}\r
572\r
573EFI_STATUS\r
574libGetWakeupTime (\r
575 OUT BOOLEAN *Enabled,\r
576 OUT BOOLEAN *Pending,\r
577 OUT EFI_TIME *Time\r
578 )\r
579{\r
580 EFI_STATUS Status;\r
581 RTC_REGISTER_B RegisterB;\r
582 RTC_REGISTER_C RegisterC;\r
583 UINT8 Century;\r
584\r
585 //\r
586 // Check paramters for null pointers\r
587 //\r
588 if ((Enabled == NULL) || (Pending == NULL) || (Time == NULL)) {\r
589 return EFI_INVALID_PARAMETER;\r
590\r
591 }\r
592 //\r
593 // Acquire RTC Lock to make access to RTC atomic\r
594 //\r
595 EfiAcquireLock (&mRtc.RtcLock);\r
596\r
597 //\r
598 // Wait for up to 0.1 seconds for the RTC to be updated\r
599 //\r
600 Status = RtcWaitToUpdate (100000);\r
601 if (EFI_ERROR (Status)) {\r
602 EfiReleaseLock (&mRtc.RtcLock);\r
603 return EFI_DEVICE_ERROR;\r
604 }\r
605 //\r
606 // Read Register B and Register C\r
607 //\r
608 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
609 RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C);\r
610\r
611 //\r
612 // Get the Time/Date/Daylight Savings values.\r
613 //\r
614 *Enabled = RegisterB.Bits.AIE;\r
615 if (*Enabled) {\r
616 Time->Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM);\r
617 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM);\r
618 Time->Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM);\r
619 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r
620 Time->Month = RtcRead (RTC_ADDRESS_MONTH);\r
621 Time->Year = RtcRead (RTC_ADDRESS_YEAR);\r
622 } else {\r
623 Time->Second = 0;\r
624 Time->Minute = 0;\r
625 Time->Hour = 0;\r
626 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r
627 Time->Month = RtcRead (RTC_ADDRESS_MONTH);\r
628 Time->Year = RtcRead (RTC_ADDRESS_YEAR);\r
629 }\r
630\r
631 ConvertRtcTimeToEfiTime (Time, RegisterB);\r
632\r
633 if (RtcTestCenturyRegister () == EFI_SUCCESS) {\r
634 Century = BcdToDecimal ((UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f));\r
635 } else {\r
636 Century = BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY));\r
637 }\r
638\r
639 Time->Year = (UINT16) (Century * 100 + Time->Year);\r
640\r
641 //\r
642 // Release RTC Lock.\r
643 //\r
644 EfiReleaseLock (&mRtc.RtcLock);\r
645\r
646 //\r
647 // Make sure all field values are in correct range\r
648 //\r
649 Status = RtcTimeFieldsValid (Time);\r
650 if (EFI_ERROR (Status)) {\r
651 return EFI_DEVICE_ERROR;\r
652 }\r
653\r
654 *Pending = RegisterC.Bits.AF;\r
655\r
656 return EFI_SUCCESS;\r
657}\r
658\r
659EFI_STATUS\r
660LibSetWakeupTime (\r
661 IN BOOLEAN Enabled,\r
662 OUT EFI_TIME *Time\r
663 )\r
664{\r
665 EFI_STATUS Status;\r
666 EFI_TIME RtcTime;\r
667 RTC_REGISTER_B RegisterB;\r
668 UINT8 Century;\r
669 EFI_TIME_CAPABILITIES Capabilities;\r
670\r
671 if (Enabled) {\r
672\r
673 if (Time == NULL) {\r
674 return EFI_INVALID_PARAMETER;\r
675 }\r
676 //\r
677 // Make sure that the time fields are valid\r
678 //\r
679 Status = RtcTimeFieldsValid (Time);\r
680 if (EFI_ERROR (Status)) {\r
681 return EFI_INVALID_PARAMETER;\r
682 }\r
683 //\r
684 // Just support set alarm time within 24 hours\r
685 //\r
686 LibGetTime (&RtcTime, &Capabilities);\r
687 if (Time->Year != RtcTime.Year ||\r
688 Time->Month != RtcTime.Month ||\r
689 (Time->Day != RtcTime.Day && Time->Day != (RtcTime.Day + 1))\r
690 ) {\r
691 return EFI_UNSUPPORTED;\r
692 }\r
693 //\r
694 // Make a local copy of the time and date\r
695 //\r
696 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));\r
697\r
698 }\r
699 //\r
700 // Acquire RTC Lock to make access to RTC atomic\r
701 //\r
702 EfiAcquireLock (&mRtc.RtcLock);\r
703\r
704 //\r
705 // Wait for up to 0.1 seconds for the RTC to be updated\r
706 //\r
707 Status = RtcWaitToUpdate (100000);\r
708 if (EFI_ERROR (Status)) {\r
709 EfiReleaseLock (&mRtc.RtcLock);\r
710 return EFI_DEVICE_ERROR;\r
711 }\r
712 //\r
713 // Read Register B, and inhibit updates of the RTC\r
714 //\r
715 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
716\r
717 RegisterB.Bits.SET = 1;\r
718 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
719\r
720 if (Enabled) {\r
721 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century);\r
722\r
723 //\r
724 // Set RTC alarm time\r
725 //\r
726 RtcWrite (RTC_ADDRESS_SECONDS_ALARM, RtcTime.Second);\r
727 RtcWrite (RTC_ADDRESS_MINUTES_ALARM, RtcTime.Minute);\r
728 RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour);\r
729\r
730 RegisterB.Bits.AIE = 1;\r
731\r
732 } else {\r
733 RegisterB.Bits.AIE = 0;\r
734 }\r
735 //\r
736 // Allow updates of the RTC registers\r
737 //\r
738 RegisterB.Bits.SET = 0;\r
739 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
740\r
741 //\r
742 // Release RTC Lock.\r
743 //\r
744 EfiReleaseLock (&mRtc.RtcLock);\r
745\r
746 return EFI_SUCCESS;\r
747}\r
748\r
749\r
750\r
751VOID\r
752LibRtcVirtualAddressChangeEvent (\r
753 VOID\r
754 )\r
755{\r
756}\r
757\r
758\r
759VOID\r
760LibRtcInitialize (\r
761 VOID\r
762 )\r
763{\r
764 EFI_STATUS Status;\r
765 RTC_REGISTER_A RegisterA;\r
766 RTC_REGISTER_B RegisterB;\r
767 RTC_REGISTER_C RegisterC;\r
768 RTC_REGISTER_D RegisterD;\r
769 UINT8 Century;\r
770 EFI_TIME Time;\r
771\r
772 //\r
773 // Acquire RTC Lock to make access to RTC atomic\r
774 //\r
775 EfiAcquireLock (&mRtc.RtcLock);\r
776\r
777 //\r
778 // Initialize RTC Register\r
779 //\r
780 // Make sure Division Chain is properly configured,\r
781 // or RTC clock won't "tick" -- time won't increment\r
782 //\r
783 RegisterA.Data = RTC_INIT_REGISTER_A;\r
784 RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data);\r
785\r
786 //\r
787 // Read Register B\r
788 //\r
789 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
790\r
791 //\r
792 // Clear RTC flag register\r
793 //\r
794 RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C);\r
795\r
796 //\r
797 // Clear RTC register D\r
798 //\r
799 RegisterD.Data = RTC_INIT_REGISTER_D;\r
800 RtcWrite (RTC_ADDRESS_REGISTER_D, RegisterD.Data);\r
801\r
802 //\r
803 // Wait for up to 0.1 seconds for the RTC to be updated\r
804 //\r
805 Status = RtcWaitToUpdate (100000);\r
806 if (EFI_ERROR (Status)) {\r
807 EfiReleaseLock (&mRtc.RtcLock);\r
808 return;\r
809 }\r
810\r
811 //\r
812 // Get the Time/Date/Daylight Savings values.\r
813 //\r
814 Time.Second = RtcRead (RTC_ADDRESS_SECONDS);\r
815 Time.Minute = RtcRead (RTC_ADDRESS_MINUTES);\r
816 Time.Hour = RtcRead (RTC_ADDRESS_HOURS);\r
817 Time.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r
818 Time.Month = RtcRead (RTC_ADDRESS_MONTH);\r
819 Time.Year = RtcRead (RTC_ADDRESS_YEAR);\r
820\r
821 ConvertRtcTimeToEfiTime (&Time, RegisterB);\r
822\r
823 if (RtcTestCenturyRegister () == EFI_SUCCESS) {\r
824 Century = BcdToDecimal ((UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f));\r
825 } else {\r
826 Century = BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY));\r
827 }\r
828\r
829 Time.Year = (UINT16) (Century * 100 + Time.Year);\r
830\r
831 //\r
832 // Set RTC configuration after get original time\r
833 //\r
834 RtcWrite (RTC_ADDRESS_REGISTER_B, RTC_INIT_REGISTER_B);\r
835\r
836 //\r
837 // Release RTC Lock.\r
838 //\r
839 EfiReleaseLock (&mRtc.RtcLock);\r
840\r
841 //\r
842 // Validate time fields\r
843 //\r
844 Status = RtcTimeFieldsValid (&Time);\r
845 if (EFI_ERROR (Status)) {\r
846 Time.Second = RTC_INIT_SECOND;\r
847 Time.Minute = RTC_INIT_MINUTE;\r
848 Time.Hour = RTC_INIT_HOUR;\r
849 Time.Day = RTC_INIT_DAY;\r
850 Time.Month = RTC_INIT_MONTH;\r
851 Time.Year = RTC_INIT_YEAR;\r
852 }\r
853 //\r
854 // Reset time value according to new RTC configuration\r
855 //\r
856 LibSetTime (&Time);\r
857\r
858 return;\r
859}\r
860\r
861\r