1 /** @file
2 *
3 *  Copyright (c) 2011, ARM Limited. All rights reserved.
4 *
5 *  This program and the accompanying materials
6 *  are licensed and made available under the terms and conditions of the BSD License
7 *  which accompanies this distribution.  The full text of the license may be found at
8 *  http://opensource.org/licenses/bsd-license.php
9 *
10 *  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 *  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 *
13 **/
14 
15 #include <Uefi.h>
16 
17 #include <Library/BaseMemoryLib.h>
18 #include <Library/UefiBootServicesTableLib.h>
19 #include <Library/UefiRuntimeServicesTableLib.h>
20 #include <Library/DebugLib.h>
21 #include <Library/IoLib.h>
22 
23 #include <Protocol/RealTimeClock.h>
24 #include <Protocol/EmbeddedExternalDevice.h>
25 
26 #include <Omap3530/Omap3530.h>
27 #include <TPS65950.h>
28 
29 
30 EMBEDDED_EXTERNAL_DEVICE   *gTPS65950;
31 INT16                      TimeZone = EFI_UNSPECIFIED_TIMEZONE;
32 
33 /**
34   Returns the current time and date information, and the time-keeping capabilities
35   of the hardware platform.
36 
37   @param  Time                  A pointer to storage to receive a snapshot of the current time.
38   @param  Capabilities          An optional pointer to a buffer to receive the real time clock
39                                 device's capabilities.
40 
41   @retval EFI_SUCCESS           The operation completed successfully.
42   @retval EFI_INVALID_PARAMETER Time is NULL.
43   @retval EFI_DEVICE_ERROR      The time could not be retrieved due to hardware error.
44 
45 **/
46 EFI_STATUS
47 EFIAPI
LibGetTime(OUT EFI_TIME * Time,OUT EFI_TIME_CAPABILITIES * Capabilities)48 LibGetTime (
49   OUT EFI_TIME                *Time,
50   OUT EFI_TIME_CAPABILITIES   *Capabilities
51   )
52 {
53   EFI_STATUS            Status;
54   UINT8                 Data;
55   EFI_TPL               OldTpl;
56 
57   if (Time == NULL) {
58     return EFI_INVALID_PARAMETER;
59   }
60 
61   OldTpl = gBS->RaiseTPL(TPL_NOTIFY);
62 
63   /* Get time and date */
64   ZeroMem(Time, sizeof(EFI_TIME));
65 
66   // Latch values
67   Status = gTPS65950->Read (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, RTC_CTRL_REG), 1, &Data);
68   if (Status != EFI_SUCCESS) goto EXIT;
69   Data |= BIT6;
70   Status = gTPS65950->Write (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, RTC_CTRL_REG), 1, &Data);
71   if (Status != EFI_SUCCESS) goto EXIT;
72 
73   // Read registers
74   Status = gTPS65950->Read (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, YEARS_REG), 1, &Data);
75   if (Status != EFI_SUCCESS) goto EXIT;
76   Time->Year = 2000 + ((Data >> 4) & 0xF) * 10 + (Data & 0xF);
77 
78   Status = gTPS65950->Read (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, MONTHS_REG), 1, &Data);
79   if (Status != EFI_SUCCESS) goto EXIT;
80   Time->Month = ((Data >> 4) & 0x1) * 10 + (Data & 0xF);
81 
82   Status = gTPS65950->Read (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, DAYS_REG), 1, &Data);
83   if (Status != EFI_SUCCESS) goto EXIT;
84   Time->Day = ((Data >> 4) & 0x3) * 10 + (Data & 0xF);
85 
86   Status = gTPS65950->Read (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, HOURS_REG), 1, &Data);
87   if (Status != EFI_SUCCESS) goto EXIT;
88   Time->Hour = ((Data >> 4) & 0x3) * 10 + (Data & 0xF);
89 
90   Status = gTPS65950->Read (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, MINUTES_REG), 1, &Data);
91   if (Status != EFI_SUCCESS) goto EXIT;
92   Time->Minute = ((Data >> 4) & 0x7) * 10 + (Data & 0xF);
93 
94   Status = gTPS65950->Read (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, SECONDS_REG), 1, &Data);
95   if (Status != EFI_SUCCESS) goto EXIT;
96   Time->Second = ((Data >> 4) & 0x7) * 10 + (Data & 0xF);
97 
98   Time->TimeZone = TimeZone;
99   // TODO: check what to use here
100   Time->Daylight = EFI_TIME_ADJUST_DAYLIGHT;
101 
102   // Set capabilities
103 
104   // TODO: Set real capabilities
105   if (Capabilities != NULL) {
106     Capabilities->Resolution = 1;
107     Capabilities->Accuracy = 50000000;
108     Capabilities->SetsToZero = FALSE;
109   }
110 
111 EXIT:
112   gBS->RestoreTPL(OldTpl);
113 
114   return (Status == EFI_SUCCESS) ? Status : EFI_DEVICE_ERROR;
115 }
116 
117 /**
118   Sets the current local time and date information.
119 
120   @param  Time                  A pointer to the current time.
121 
122   @retval EFI_SUCCESS           The operation completed successfully.
123   @retval EFI_INVALID_PARAMETER A time field is out of range.
124   @retval EFI_DEVICE_ERROR      The time could not be set due due to hardware error.
125 
126 **/
127 EFI_STATUS
128 EFIAPI
LibSetTime(IN EFI_TIME * Time)129 LibSetTime (
130   IN EFI_TIME                *Time
131   )
132 {
133   EFI_STATUS Status;
134   UINT8      Data;
135   UINT8      MonthDayCount[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
136   EFI_TPL    OldTpl;
137 
138   // Input validation according both to UEFI spec and hardware constraints
139   // UEFI spec says valid year range is 1900-9999 but TPS only supports 2000-2099
140   if ( (Time == NULL)
141     || (Time->Year < 2000 || Time->Year > 2099)
142     || (Time->Month < 1 || Time->Month > 12)
143     || (Time->Day < 1 || Time->Day > MonthDayCount[Time->Month])
144     || (Time->Hour > 23)
145     || (Time->Minute > 59)
146     || (Time->Second > 59)
147     || (Time->Nanosecond > 999999999)
148     || ((Time->TimeZone < -1440 || Time->TimeZone > 1440) && Time->TimeZone != 2047)
149   ) {
150     return EFI_INVALID_PARAMETER;
151   }
152 
153   OldTpl = gBS->RaiseTPL(TPL_NOTIFY);
154 
155   Data = Time->Year - 2000;
156   Status = gTPS65950->Write (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, YEARS_REG), 1, &Data);
157   if (Status != EFI_SUCCESS) goto EXIT;
158 
159   Data = ((Time->Month / 10) << 4) | (Time->Month % 10);
160   Status = gTPS65950->Write (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, MONTHS_REG), 1, &Data);
161   if (Status != EFI_SUCCESS) goto EXIT;
162 
163   Data = ((Time->Day / 10) << 4) | (Time->Day % 10);
164   Status = gTPS65950->Write (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, DAYS_REG), 1, &Data);
165   if (Status != EFI_SUCCESS) goto EXIT;
166 
167   Data = ((Time->Hour / 10) << 4) | (Time->Hour % 10);
168   Status = gTPS65950->Write (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, HOURS_REG), 1, &Data);
169   if (Status != EFI_SUCCESS) goto EXIT;
170 
171   Data = ((Time->Minute / 10) << 4) | (Time->Minute % 10);
172   Status = gTPS65950->Write (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, MINUTES_REG), 1, &Data);
173   if (Status != EFI_SUCCESS) goto EXIT;
174 
175   Data = ((Time->Second / 10) << 4) | (Time->Second % 10);
176   Status = gTPS65950->Write (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, SECONDS_REG), 1, &Data);
177   if (Status != EFI_SUCCESS) goto EXIT;
178 
179   TimeZone = Time->TimeZone;
180 
181 EXIT:
182   gBS->RestoreTPL(OldTpl);
183 
184   return (Status == EFI_SUCCESS) ? Status : EFI_DEVICE_ERROR;
185 }
186 
187 /**
188   Returns the current wakeup alarm clock setting.
189 
190   @param  Enabled               Indicates if the alarm is currently enabled or disabled.
191   @param  Pending               Indicates if the alarm signal is pending and requires acknowledgement.
192   @param  Time                  The current alarm setting.
193 
194   @retval EFI_SUCCESS           The alarm settings were returned.
195   @retval EFI_INVALID_PARAMETER Any parameter is NULL.
196   @retval EFI_DEVICE_ERROR      The wakeup time could not be retrieved due to a hardware error.
197 
198 **/
199 EFI_STATUS
200 EFIAPI
LibGetWakeupTime(OUT BOOLEAN * Enabled,OUT BOOLEAN * Pending,OUT EFI_TIME * Time)201 LibGetWakeupTime (
202   OUT BOOLEAN     *Enabled,
203   OUT BOOLEAN     *Pending,
204   OUT EFI_TIME    *Time
205   )
206 {
207   return EFI_UNSUPPORTED;
208 }
209 
210 /**
211   Sets the system wakeup alarm clock time.
212 
213   @param  Enabled               Enable or disable the wakeup alarm.
214   @param  Time                  If Enable is TRUE, the time to set the wakeup alarm for.
215 
216   @retval EFI_SUCCESS           If Enable is TRUE, then the wakeup alarm was enabled. If
217                                 Enable is FALSE, then the wakeup alarm was disabled.
218   @retval EFI_INVALID_PARAMETER A time field is out of range.
219   @retval EFI_DEVICE_ERROR      The wakeup time could not be set due to a hardware error.
220   @retval EFI_UNSUPPORTED       A wakeup timer is not supported on this platform.
221 
222 **/
223 EFI_STATUS
224 EFIAPI
LibSetWakeupTime(IN BOOLEAN Enabled,OUT EFI_TIME * Time)225 LibSetWakeupTime (
226   IN BOOLEAN      Enabled,
227   OUT EFI_TIME    *Time
228   )
229 {
230   return EFI_UNSUPPORTED;
231 }
232 
233 /**
234   This is the declaration of an EFI image entry point. This can be the entry point to an application
235   written to this specification, an EFI boot service driver, or an EFI runtime driver.
236 
237   @param  ImageHandle           Handle that identifies the loaded image.
238   @param  SystemTable           System Table for this image.
239 
240   @retval EFI_SUCCESS           The operation completed successfully.
241 
242 **/
243 EFI_STATUS
244 EFIAPI
LibRtcInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)245 LibRtcInitialize (
246   IN EFI_HANDLE                            ImageHandle,
247   IN EFI_SYSTEM_TABLE                      *SystemTable
248   )
249 {
250   EFI_STATUS    Status;
251   EFI_HANDLE    Handle;
252   UINT8         Data;
253   EFI_TPL       OldTpl;
254 
255   Status = gBS->LocateProtocol (&gEmbeddedExternalDeviceProtocolGuid, NULL, (VOID **)&gTPS65950);
256   ASSERT_EFI_ERROR(Status);
257 
258   OldTpl = gBS->RaiseTPL(TPL_NOTIFY);
259   Data = 1;
260   Status = gTPS65950->Write (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, RTC_CTRL_REG), 1, &Data);
261   ASSERT_EFI_ERROR(Status);
262   gBS->RestoreTPL(OldTpl);
263 
264   // Setup the setters and getters
265   gRT->GetTime       = LibGetTime;
266   gRT->SetTime       = LibSetTime;
267   gRT->GetWakeupTime = LibGetWakeupTime;
268   gRT->SetWakeupTime = LibSetWakeupTime;
269 
270   // Install the protocol
271   Handle = NULL;
272   Status = gBS->InstallMultipleProtocolInterfaces (
273                   &Handle,
274                   &gEfiRealTimeClockArchProtocolGuid,  NULL,
275                   NULL
276                  );
277 
278   return Status;
279 }
280 
281 /**
282   Fixup internal data so that EFI can be call in virtual mode.
283   Call the passed in Child Notify event and convert any pointers in
284   lib to virtual mode.
285 
286   @param[in]    Event   The Event that is being processed
287   @param[in]    Context Event Context
288 **/
289 VOID
290 EFIAPI
LibRtcVirtualNotifyEvent(IN EFI_EVENT Event,IN VOID * Context)291 LibRtcVirtualNotifyEvent (
292   IN EFI_EVENT        Event,
293   IN VOID             *Context
294   )
295 {
296   return;
297 }
298