1 /** @file
2
3 Copyright (c) 2017, Linaro, Ltd. All rights reserved.<BR>
4
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8
9 #include <PiDxe.h>
10 #include <Library/DebugLib.h>
11 #include <Library/IoLib.h>
12 #include <Library/RealTimeClockLib.h>
13 #include <Library/UefiBootServicesTableLib.h>
14 #include <Library/UefiLib.h>
15 #include <Library/UefiRuntimeLib.h>
16 #include <Protocol/I2cMaster.h>
17
18 #define SLAVE_ADDRESS (FixedPcdGet8 (PcdI2cSlaveAddress))
19 #define PCF8563_DATA_REG_OFFSET 0x2
20
21 #define PCF8563_CLOCK_INVALID 0x80
22 #define PCF8563_SECONDS_MASK 0x7f
23 #define PCF8563_MINUTES_MASK 0x7f
24 #define PCF8563_HOURS_MASK 0x3f
25 #define PCF8563_DAYS_MASK 0x3f
26 #define PCF8563_WEEKDAYS_MASK 0x07
27 #define PCF8563_MONTHS_MASK 0x1f
28 #define PCF8563_CENTURY_MASK 0x80
29
30 //
31 // The PCF8563 has a 'century' flag, which means it could theoretically span
32 // 200 years. However, it treats all years divisible by 4 as leap years,
33 // including the years 1900 and 2100 (which are not leap years), so the only
34 // sane base year is 2000 (which was a leap year).
35 //
36 #define EPOCH_BASE 2000
37
38 STATIC EFI_HANDLE mI2cMasterHandle;
39 STATIC VOID *mI2cMasterEventRegistration;
40 STATIC EFI_I2C_MASTER_PROTOCOL *mI2cMaster;
41 STATIC EFI_EVENT mRtcVirtualAddrChangeEvent;
42
43 #pragma pack(1)
44 typedef struct {
45 UINT8 VL_seconds;
46 UINT8 Minutes;
47 UINT8 Hours;
48 UINT8 Days;
49 UINT8 Weekdays;
50 UINT8 Century_months;
51 UINT8 Years;
52 } RTC_DATETIME;
53
54 typedef struct {
55 UINT8 Reg;
56 RTC_DATETIME DateTime;
57 } RTC_SET_DATETIME_PACKET;
58 #pragma pack()
59
60 typedef struct {
61 UINTN OperationCount;
62 EFI_I2C_OPERATION SetAddressOp;
63 EFI_I2C_OPERATION GetDateTimeOp;
64 } RTC_GET_I2C_REQUEST;
65
66 typedef EFI_I2C_REQUEST_PACKET RTC_SET_I2C_REQUEST;
67 /**
68 Returns the current time and date information, and the time-keeping
69 capabilities of the hardware platform.
70
71 @param Time A pointer to storage to receive a snapshot of
72 the current time.
73 @param Capabilities An optional pointer to a buffer to receive the
74 real time clock device's capabilities.
75
76 @retval EFI_SUCCESS The operation completed successfully.
77 @retval EFI_INVALID_PARAMETER Time is NULL.
78 @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware
79 error.
80
81 **/
82 EFI_STATUS
83 EFIAPI
LibGetTime(OUT EFI_TIME * Time,OUT EFI_TIME_CAPABILITIES * Capabilities)84 LibGetTime (
85 OUT EFI_TIME *Time,
86 OUT EFI_TIME_CAPABILITIES *Capabilities
87 )
88 {
89 RTC_GET_I2C_REQUEST Op;
90 RTC_DATETIME DateTime;
91 EFI_STATUS Status;
92 UINT8 Reg;
93
94 if (Time == NULL) {
95 return EFI_INVALID_PARAMETER;
96 }
97
98 if (mI2cMaster == NULL) {
99 return EFI_DEVICE_ERROR;
100 }
101
102 Reg = PCF8563_DATA_REG_OFFSET;
103
104 Op.OperationCount = 2;
105
106 Op.SetAddressOp.Flags = 0;
107 Op.SetAddressOp.LengthInBytes = 1;
108 Op.SetAddressOp.Buffer = &Reg;
109
110 Op.GetDateTimeOp.Flags = I2C_FLAG_READ;
111 Op.GetDateTimeOp.LengthInBytes = sizeof (RTC_DATETIME);
112 Op.GetDateTimeOp.Buffer = (VOID *)&DateTime;
113
114 Status = mI2cMaster->StartRequest (mI2cMaster, SLAVE_ADDRESS,
115 (VOID *)&Op, NULL, NULL);
116 if (EFI_ERROR (Status)) {
117 return EFI_DEVICE_ERROR;
118 }
119
120 if ((DateTime.VL_seconds & PCF8563_CLOCK_INVALID) != 0) {
121 Time->Second = 0;
122 Time->Minute = 0;
123 Time->Hour = 0;
124 Time->Day = 1;
125 Time->Month = 1;
126 Time->Year = EPOCH_BASE;
127 } else {
128 Time->Second = BcdToDecimal8 (DateTime.VL_seconds & PCF8563_SECONDS_MASK);
129 Time->Minute = BcdToDecimal8 (DateTime.Minutes & PCF8563_MINUTES_MASK);
130 Time->Hour = BcdToDecimal8 (DateTime.Hours & PCF8563_HOURS_MASK);
131 Time->Day = BcdToDecimal8 (DateTime.Days & PCF8563_DAYS_MASK);
132 Time->Month = BcdToDecimal8 (DateTime.Century_months & PCF8563_MONTHS_MASK);
133 Time->Year = BcdToDecimal8 (DateTime.Years) + EPOCH_BASE;
134
135 if (DateTime.Century_months & PCF8563_CENTURY_MASK) {
136 Time->Year += 100;
137 }
138 }
139
140 if (Capabilities != NULL) {
141 Capabilities->Resolution = 1;
142 Capabilities->Accuracy = 0;
143 Capabilities->SetsToZero = TRUE;
144 }
145 return EFI_SUCCESS;
146 }
147
148
149 /**
150 Sets the current local time and date information.
151
152 @param Time A pointer to the current time.
153
154 @retval EFI_SUCCESS The operation completed successfully.
155 @retval EFI_INVALID_PARAMETER A time field is out of range.
156 @retval EFI_DEVICE_ERROR The time could not be set due due to hardware
157 error.
158
159 **/
160 EFI_STATUS
161 EFIAPI
LibSetTime(IN EFI_TIME * Time)162 LibSetTime (
163 IN EFI_TIME *Time
164 )
165 {
166 RTC_SET_I2C_REQUEST Op;
167 RTC_SET_DATETIME_PACKET Packet;
168 EFI_STATUS Status;
169
170 if (mI2cMaster == NULL) {
171 return EFI_DEVICE_ERROR;
172 }
173
174 Packet.DateTime.VL_seconds = DecimalToBcd8 (Time->Second);
175 Packet.DateTime.Minutes = DecimalToBcd8 (Time->Minute);
176 Packet.DateTime.Hours = DecimalToBcd8 (Time->Hour);
177 Packet.DateTime.Days = DecimalToBcd8 (Time->Day);
178 Packet.DateTime.Weekdays = 0;
179 Packet.DateTime.Century_months = DecimalToBcd8 (Time->Month);
180 Packet.DateTime.Years = DecimalToBcd8 (Time->Year % 100);
181 if (Time->Year >= EPOCH_BASE + 100) {
182 if (Time->Year >= EPOCH_BASE + 200) {
183 return EFI_DEVICE_ERROR;
184 }
185 Packet.DateTime.Century_months |= PCF8563_CENTURY_MASK;
186 }
187
188 Packet.Reg = PCF8563_DATA_REG_OFFSET;
189
190 Op.OperationCount = 1;
191 Op.Operation[0].Flags = 0;
192 Op.Operation[0].LengthInBytes = sizeof (RTC_SET_DATETIME_PACKET);
193 Op.Operation[0].Buffer = (VOID *)&Packet;
194
195 Status = mI2cMaster->StartRequest (mI2cMaster, SLAVE_ADDRESS,
196 (VOID *)&Op, NULL, NULL);
197 if (EFI_ERROR (Status)) {
198 return EFI_DEVICE_ERROR;
199 }
200 return EFI_SUCCESS;
201 }
202
203
204 /**
205 Returns the current wakeup alarm clock setting.
206
207 @param Enabled Indicates if the alarm is currently enabled or
208 disabled.
209 @param Pending Indicates if the alarm signal is pending and
210 requires acknowledgement.
211 @param Time The current alarm setting.
212
213 @retval EFI_SUCCESS The alarm settings were returned.
214 @retval EFI_INVALID_PARAMETER Any parameter is NULL.
215 @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a
216 hardware error.
217 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this
218 platform.
219
220 **/
221 EFI_STATUS
222 EFIAPI
LibGetWakeupTime(OUT BOOLEAN * Enabled,OUT BOOLEAN * Pending,OUT EFI_TIME * Time)223 LibGetWakeupTime (
224 OUT BOOLEAN *Enabled,
225 OUT BOOLEAN *Pending,
226 OUT EFI_TIME *Time
227 )
228 {
229 //
230 // Currently unimplemented. The PCF8563 does not support setting the alarm
231 // for an arbitrary date/time, but only for a minute/hour/day/weekday
232 // combination. It should also depend on a platform specific setting that
233 // indicates whether the PCF8563's interrupt line is connected in a way that
234 // allows it to power up the system in the first place.
235 //
236 return EFI_UNSUPPORTED;
237 }
238
239
240 /**
241 Sets the system wakeup alarm clock time.
242
243 @param Enabled Enable or disable the wakeup alarm.
244 @param Time If Enable is TRUE, the time to set the wakeup
245 alarm for.
246
247 @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was
248 enabled. If Enable is FALSE, then the wakeup
249 alarm was disabled.
250 @retval EFI_INVALID_PARAMETER A time field is out of range.
251 @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a
252 hardware error.
253 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this
254 platform.
255
256 **/
257 EFI_STATUS
258 EFIAPI
LibSetWakeupTime(IN BOOLEAN Enabled,OUT EFI_TIME * Time)259 LibSetWakeupTime (
260 IN BOOLEAN Enabled,
261 OUT EFI_TIME *Time
262 )
263 {
264 // see comment above
265 return EFI_UNSUPPORTED;
266 }
267
268 STATIC
269 VOID
I2cMasterRegistrationEvent(IN EFI_EVENT Event,IN VOID * Context)270 I2cMasterRegistrationEvent (
271 IN EFI_EVENT Event,
272 IN VOID *Context
273 )
274 {
275 EFI_HANDLE Handle;
276 UINTN BufferSize;
277 EFI_STATUS Status;
278 EFI_I2C_MASTER_PROTOCOL *I2cMaster;
279 UINTN BusFrequency;
280
281 //
282 // Try to connect the newly registered driver to our handle.
283 //
284 do {
285 BufferSize = sizeof (EFI_HANDLE);
286 Status = gBS->LocateHandle (ByRegisterNotify,
287 &gEfiI2cMasterProtocolGuid,
288 mI2cMasterEventRegistration,
289 &BufferSize,
290 &Handle);
291 if (EFI_ERROR (Status)) {
292 if (Status != EFI_NOT_FOUND) {
293 DEBUG ((DEBUG_WARN, "%a: gBS->LocateHandle () returned %r\n",
294 __FUNCTION__, Status));
295 }
296 break;
297 }
298
299 if (Handle != mI2cMasterHandle) {
300 continue;
301 }
302
303 DEBUG ((DEBUG_INFO, "%a: found I2C master!\n", __FUNCTION__));
304
305 gBS->CloseEvent (Event);
306
307 Status = gBS->OpenProtocol (mI2cMasterHandle, &gEfiI2cMasterProtocolGuid,
308 (VOID **)&I2cMaster, gImageHandle, NULL,
309 EFI_OPEN_PROTOCOL_EXCLUSIVE);
310 ASSERT_EFI_ERROR (Status);
311
312 Status = I2cMaster->Reset (I2cMaster);
313 if (EFI_ERROR (Status)) {
314 DEBUG ((DEBUG_ERROR, "%a: I2CMaster->Reset () failed - %r\n",
315 __FUNCTION__, Status));
316 break;
317 }
318
319 BusFrequency = FixedPcdGet16 (PcdI2cBusFrequency);
320 Status = I2cMaster->SetBusFrequency (I2cMaster, &BusFrequency);
321 if (EFI_ERROR (Status)) {
322 DEBUG ((DEBUG_ERROR, "%a: I2CMaster->SetBusFrequency () failed - %r\n",
323 __FUNCTION__, Status));
324 break;
325 }
326
327 mI2cMaster = I2cMaster;
328 break;
329 } while (TRUE);
330 }
331
332 /**
333 Fixup internal data so that EFI can be call in virtual mode.
334 Call the passed in Child Notify event and convert any pointers in
335 lib to virtual mode.
336
337 @param[in] Event The Event that is being processed
338 @param[in] Context Event Context
339 **/
340 VOID
341 EFIAPI
LibRtcVirtualNotifyEvent(IN EFI_EVENT Event,IN VOID * Context)342 LibRtcVirtualNotifyEvent (
343 IN EFI_EVENT Event,
344 IN VOID *Context
345 )
346 {
347 EfiConvertPointer (0x0, (VOID **)&mI2cMaster);
348 }
349
350 /**
351 Library entry point
352
353 @param ImageHandle Handle that identifies the loaded image.
354 @param SystemTable System Table for this image.
355
356 @retval EFI_SUCCESS The operation completed successfully.
357
358 **/
359 EFI_STATUS
360 EFIAPI
LibRtcInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)361 LibRtcInitialize (
362 IN EFI_HANDLE ImageHandle,
363 IN EFI_SYSTEM_TABLE *SystemTable
364 )
365 {
366 EFI_STATUS Status;
367 UINTN BufferSize;
368
369 //
370 // Find the handle that marks the controller
371 // that will provide the I2C master protocol.
372 //
373 BufferSize = sizeof (EFI_HANDLE);
374 Status = gBS->LocateHandle (ByProtocol,
375 &gPcf8563RealTimeClockLibI2cMasterProtocolGuid, NULL,
376 &BufferSize, &mI2cMasterHandle);
377 ASSERT_EFI_ERROR (Status);
378
379 //
380 // Register a protocol registration notification callback on the I2C master
381 // protocol. This will notify us even if the protocol instance we are looking
382 // for has already been installed.
383 //
384 EfiCreateProtocolNotifyEvent (
385 &gEfiI2cMasterProtocolGuid,
386 TPL_CALLBACK,
387 I2cMasterRegistrationEvent,
388 NULL,
389 &mI2cMasterEventRegistration);
390
391 //
392 // Register for the virtual address change event
393 //
394 Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
395 LibRtcVirtualNotifyEvent, NULL,
396 &gEfiEventVirtualAddressChangeGuid,
397 &mRtcVirtualAddrChangeEvent);
398 ASSERT_EFI_ERROR (Status);
399
400 return EFI_SUCCESS;
401 }
402