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