Ds1307RtcLib.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. /** Ds1307RtcLib.c
  2. Implement EFI RealTimeClock via RTC Lib for DS1307 RTC.
  3. Based on RTC implementation available in
  4. EmbeddedPkg/Library/TemplateRealTimeClockLib/RealTimeClockLib.c
  5. Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
  6. Copyright 2017, 2020 NXP
  7. SPDX-License-Identifier: BSD-2-Clause-Patent
  8. **/
  9. #include <PiDxe.h>
  10. #include <Base.h>
  11. #include <Library/BaseLib.h>
  12. #include <Library/DebugLib.h>
  13. #include <Library/RealTimeClockLib.h>
  14. #include <Library/UefiBootServicesTableLib.h>
  15. #include <Library/UefiLib.h>
  16. #include <Protocol/I2cMaster.h>
  17. #include "Ds1307Rtc.h"
  18. STATIC VOID *mDriverEventRegistration;
  19. STATIC EFI_HANDLE mI2cMasterHandle;
  20. STATIC EFI_I2C_MASTER_PROTOCOL *mI2cMaster;
  21. /**
  22. Read RTC register.
  23. Data Read-Slave Transmitter Mode
  24. <Slave Address> <Word Address (n)> <Slave Address> <Data(n)> <Data(n+1)> <Data(n+2)> <Data(n+X)>
  25. The first byte is received and handled as in the slave receiver mode.
  26. @param RtcRegAddr Register offset of RTC to be read.
  27. @retval Register Value read
  28. **/
  29. STATIC
  30. UINT8
  31. RtcRead (
  32. IN UINT8 RtcRegAddr
  33. )
  34. {
  35. RTC_I2C_REQUEST Req;
  36. EFI_STATUS Status;
  37. UINT8 Val;
  38. Val = 0;
  39. Req.OperationCount = 2;
  40. Req.SetAddressOp.Flags = 0;
  41. Req.SetAddressOp.LengthInBytes = sizeof (RtcRegAddr);
  42. Req.SetAddressOp.Buffer = &RtcRegAddr;
  43. Req.GetSetDateTimeOp.Flags = I2C_FLAG_READ;
  44. Req.GetSetDateTimeOp.LengthInBytes = sizeof (Val);
  45. Req.GetSetDateTimeOp.Buffer = &Val;
  46. Status = mI2cMaster->StartRequest (mI2cMaster, FixedPcdGet8 (PcdI2cSlaveAddress),
  47. (VOID *)&Req,
  48. NULL, NULL);
  49. if (EFI_ERROR (Status)) {
  50. DEBUG ((DEBUG_ERROR, "RTC read error at Addr:0x%x\n", RtcRegAddr));
  51. }
  52. return Val;
  53. }
  54. /**
  55. Write RTC register.
  56. Data Write-Slave Receiver Mode
  57. <Slave Address> <Word Address (n)> <Data(n)> <Data(n+1)> <Data(n+X)>
  58. @param RtcRegAddr Register offset of RTC to write.
  59. @param Val Value to be written
  60. **/
  61. STATIC
  62. VOID
  63. RtcWrite (
  64. IN UINT8 RtcRegAddr,
  65. IN UINT8 Val
  66. )
  67. {
  68. RTC_I2C_REQUEST Req;
  69. EFI_STATUS Status;
  70. UINT8 Buffer[2];
  71. Req.OperationCount = 1;
  72. Buffer[0] = RtcRegAddr;
  73. Buffer[1] = Val;
  74. Req.SetAddressOp.Flags = 0;
  75. Req.SetAddressOp.LengthInBytes = sizeof (Buffer);
  76. Req.SetAddressOp.Buffer = Buffer;
  77. Status = mI2cMaster->StartRequest (mI2cMaster, FixedPcdGet8 (PcdI2cSlaveAddress),
  78. (VOID *)&Req,
  79. NULL, NULL);
  80. if (EFI_ERROR (Status)) {
  81. DEBUG ((DEBUG_ERROR, "RTC write error at Addr:0x%x\n", RtcRegAddr));
  82. }
  83. }
  84. /**
  85. Returns the current time and date information, and the time-keeping capabilities
  86. of the hardware platform.
  87. @param Time A pointer to storage to receive a snapshot of the current time.
  88. @param Capabilities An optional pointer to a buffer to receive the real time clock
  89. device's capabilities.
  90. @retval EFI_SUCCESS The operation completed successfully.
  91. @retval EFI_INVALID_PARAMETER Time is NULL.
  92. @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error.
  93. **/
  94. EFI_STATUS
  95. EFIAPI
  96. LibGetTime (
  97. OUT EFI_TIME *Time,
  98. OUT EFI_TIME_CAPABILITIES *Capabilities
  99. )
  100. {
  101. EFI_STATUS Status;
  102. UINT8 Second;
  103. UINT8 Minute;
  104. UINT8 Hour;
  105. UINT8 Day;
  106. UINT8 Month;
  107. UINT8 Year;
  108. if (mI2cMaster == NULL) {
  109. return EFI_DEVICE_ERROR;
  110. }
  111. Status = EFI_SUCCESS;
  112. Second = RtcRead (DS1307_SEC_REG_ADDR);
  113. Minute = RtcRead (DS1307_MIN_REG_ADDR);
  114. Hour = RtcRead (DS1307_HR_REG_ADDR);
  115. Day = RtcRead (DS1307_DATE_REG_ADDR);
  116. Month = RtcRead (DS1307_MON_REG_ADDR);
  117. Year = RtcRead (DS1307_YR_REG_ADDR);
  118. if (Second & DS1307_SEC_BIT_CH) {
  119. DEBUG ((DEBUG_ERROR, "### Warning: RTC oscillator has stopped\n"));
  120. /* clear the CH flag */
  121. RtcWrite (DS1307_SEC_REG_ADDR,
  122. RtcRead (DS1307_SEC_REG_ADDR) & ~DS1307_SEC_BIT_CH);
  123. Status = EFI_DEVICE_ERROR;
  124. }
  125. Time->Second = BcdToDecimal8 (Second & MASK_SEC);
  126. Time->Minute = BcdToDecimal8 (Minute & MASK_MIN);
  127. Time->Hour = BcdToDecimal8 (Hour & MASK_HOUR);
  128. Time->Day = BcdToDecimal8 (Day & MASK_DAY);
  129. Time->Month = BcdToDecimal8 (Month & MASK_MONTH);
  130. //
  131. // RTC can save year 1970 to 2069
  132. // On writing Year, save year % 100
  133. // On Reading reversing the operation e.g. 2012
  134. // write = 12 (2012 % 100)
  135. // read = 2012 (12 + 2000)
  136. //
  137. Time->Year = BcdToDecimal8 (Year) +
  138. (BcdToDecimal8 (Year) >= 70 ? START_YEAR - 70 : END_YEAR -70);
  139. return Status;
  140. }
  141. /**
  142. Sets the current local time and date information.
  143. @param Time A pointer to the current time.
  144. @retval EFI_SUCCESS The operation completed successfully.
  145. @retval EFI_INVALID_PARAMETER A time field is out of range.
  146. **/
  147. EFI_STATUS
  148. EFIAPI
  149. LibSetTime (
  150. IN EFI_TIME *Time
  151. )
  152. {
  153. if (mI2cMaster == NULL) {
  154. return EFI_DEVICE_ERROR;
  155. }
  156. if (Time->Year < START_YEAR || Time->Year >= END_YEAR){
  157. DEBUG ((DEBUG_ERROR, "WARNING: Year should be between 1970 and 2069!\n"));
  158. return EFI_INVALID_PARAMETER;
  159. }
  160. RtcWrite (DS1307_YR_REG_ADDR, DecimalToBcd8 (Time->Year % 100));
  161. RtcWrite (DS1307_MON_REG_ADDR, DecimalToBcd8 (Time->Month));
  162. RtcWrite (DS1307_DATE_REG_ADDR, DecimalToBcd8 (Time->Day));
  163. RtcWrite (DS1307_HR_REG_ADDR, DecimalToBcd8 (Time->Hour));
  164. RtcWrite (DS1307_MIN_REG_ADDR, DecimalToBcd8 (Time->Minute));
  165. RtcWrite (DS1307_SEC_REG_ADDR, DecimalToBcd8 (Time->Second));
  166. return EFI_SUCCESS;
  167. }
  168. /**
  169. Returns the current wakeup alarm clock setting.
  170. @param Enabled Indicates if the alarm is currently enabled or disabled.
  171. @param Pending Indicates if the alarm signal is pending and requires acknowledgement.
  172. @param Time The current alarm setting.
  173. @retval EFI_SUCCESS The alarm settings were returned.
  174. @retval EFI_INVALID_PARAMETER Any parameter is NULL.
  175. @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error.
  176. @retval EFI_UNSUPPORTED A wakeup timer is not supported on this
  177. platform.
  178. **/
  179. EFI_STATUS
  180. EFIAPI
  181. LibGetWakeupTime (
  182. OUT BOOLEAN *Enabled,
  183. OUT BOOLEAN *Pending,
  184. OUT EFI_TIME *Time
  185. )
  186. {
  187. // The DS1307 does not support setting the alarm
  188. return EFI_UNSUPPORTED;
  189. }
  190. /**
  191. Sets the system wakeup alarm clock time.
  192. @param Enabled Enable or disable the wakeup alarm.
  193. @param Time If Enable is TRUE, the time to set the wakeup alarm for.
  194. @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled. If
  195. Enable is FALSE, then the wakeup alarm was disabled.
  196. @retval EFI_INVALID_PARAMETER A time field is out of range.
  197. @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error.
  198. @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
  199. **/
  200. EFI_STATUS
  201. EFIAPI
  202. LibSetWakeupTime (
  203. IN BOOLEAN Enabled,
  204. OUT EFI_TIME *Time
  205. )
  206. {
  207. // The DS1307 does not support setting the alarm
  208. return EFI_UNSUPPORTED;
  209. }
  210. STATIC
  211. VOID
  212. I2cDriverRegistrationEvent (
  213. IN EFI_EVENT Event,
  214. IN VOID *Context
  215. )
  216. {
  217. EFI_STATUS Status;
  218. EFI_I2C_MASTER_PROTOCOL *I2cMaster;
  219. UINTN BusFrequency;
  220. EFI_HANDLE Handle;
  221. UINTN BufferSize;
  222. //
  223. // Try to connect the newly registered driver to our handle.
  224. //
  225. do {
  226. BufferSize = sizeof (EFI_HANDLE);
  227. Status = gBS->LocateHandle (ByRegisterNotify,
  228. &gEfiI2cMasterProtocolGuid,
  229. mDriverEventRegistration,
  230. &BufferSize,
  231. &Handle);
  232. if (EFI_ERROR (Status)) {
  233. if (Status != EFI_NOT_FOUND) {
  234. DEBUG ((DEBUG_WARN, "%a: gBS->LocateHandle () returned %r\n",
  235. __FUNCTION__, Status));
  236. }
  237. break;
  238. }
  239. if (Handle != mI2cMasterHandle) {
  240. continue;
  241. }
  242. DEBUG ((DEBUG_INFO, "%a: found I2C master!\n", __FUNCTION__));
  243. gBS->CloseEvent (Event);
  244. Status = gBS->OpenProtocol (mI2cMasterHandle, &gEfiI2cMasterProtocolGuid,
  245. (VOID **)&I2cMaster, gImageHandle, NULL,
  246. EFI_OPEN_PROTOCOL_EXCLUSIVE);
  247. ASSERT_EFI_ERROR (Status);
  248. Status = I2cMaster->Reset (I2cMaster);
  249. if (EFI_ERROR (Status)) {
  250. DEBUG ((DEBUG_ERROR, "%a: I2CMaster->Reset () failed - %r\n",
  251. __FUNCTION__, Status));
  252. break;
  253. }
  254. BusFrequency = FixedPcdGet16 (PcdI2cBusFrequency);
  255. Status = I2cMaster->SetBusFrequency (I2cMaster, &BusFrequency);
  256. if (EFI_ERROR (Status)) {
  257. DEBUG ((DEBUG_ERROR, "%a: I2CMaster->SetBusFrequency () failed - %r\n",
  258. __FUNCTION__, Status));
  259. break;
  260. }
  261. mI2cMaster = I2cMaster;
  262. break;
  263. } while (TRUE);
  264. return;
  265. }
  266. /**
  267. This is the declaration of an EFI image entry point. This can be the entry point to an application
  268. written to this specification, an EFI boot service driver.
  269. @param ImageHandle Handle that identifies the loaded image.
  270. @param SystemTable System Table for this image.
  271. @retval EFI_SUCCESS The operation completed successfully.
  272. **/
  273. EFI_STATUS
  274. EFIAPI
  275. LibRtcInitialize (
  276. IN EFI_HANDLE ImageHandle,
  277. IN EFI_SYSTEM_TABLE *SystemTable
  278. )
  279. {
  280. EFI_STATUS Status;
  281. UINTN BufferSize;
  282. //
  283. // Find the handle that marks the controller
  284. // that will provide the I2C master protocol.
  285. //
  286. BufferSize = sizeof (EFI_HANDLE);
  287. Status = gBS->LocateHandle (
  288. ByProtocol,
  289. &gDs1307RealTimeClockLibI2cMasterProtocolGuid,
  290. NULL,
  291. &BufferSize,
  292. &mI2cMasterHandle
  293. );
  294. ASSERT_EFI_ERROR (Status);
  295. //
  296. // Register a protocol registration notification callback on the driver
  297. // binding protocol so we can attempt to connect our I2C master to it
  298. // as soon as it appears.
  299. //
  300. EfiCreateProtocolNotifyEvent (
  301. &gEfiI2cMasterProtocolGuid,
  302. TPL_CALLBACK,
  303. I2cDriverRegistrationEvent,
  304. NULL,
  305. &mDriverEventRegistration);
  306. return EFI_SUCCESS;
  307. }