lpc17xx_rtc.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. /*
  2. * Copyright (C) 2012 by Rob van Lieshout (info@pragmalab.nl)
  3. * Ole Reinhardt (ole.reinhardt@embedded-it.de)
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. *
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * 3. Neither the name of the copyright holders nor the names of
  15. * contributors may be used to endorse or promote products derived
  16. * from this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  21. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  22. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  23. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  24. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  25. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  26. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  27. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  28. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  29. * SUCH DAMAGE.
  30. *
  31. * For additional information see http://www.ethernut.de/
  32. *
  33. */
  34. #include <string.h>
  35. #include <cfg/os.h>
  36. #include <cfg/clock.h>
  37. #include <cfg/arch.h>
  38. #include <sys/heap.h>
  39. #include <sys/event.h>
  40. #include <sys/timer.h>
  41. #include <dev/irqreg.h>
  42. #include <dev/rtc.h>
  43. #if defined(MCU_LPC176x)
  44. #include <arch/cm3/nxp/lpc176x_clk.h>
  45. #elif defined(MCU_LPC177x_8x)
  46. #include <arch/cm3/nxp/lpc177x_8x_clk.h>
  47. #elif defined(MCU_LPC407x_8x)
  48. #include <arch/cm3/nxp/lpc407x_8x_clk.h>
  49. #else
  50. #warning "Unknown LPC familiy"
  51. #endif
  52. #include <arch/cm3/nxp/lpc17xx_rtc.h>
  53. typedef struct _lpc17xx_rtc_dcb lpc17xx_rtc_dcb;
  54. struct _lpc17xx_rtc_dcb {
  55. uint32_t flags;
  56. };
  57. /*!
  58. * \brief Get date and time from an LPC 17xx hardware clock.
  59. *
  60. * \param tm Points to a structure that receives the date and time
  61. * information.
  62. *
  63. * \return 0 on success or -1 in case of an error.
  64. */
  65. static int Lpc17xxRtcGetClock(NUTRTC *rtc, struct _tm *tm)
  66. {
  67. if (tm) {
  68. tm->tm_mday= LPC_RTC->DOM & RTC_DOM_MASK;
  69. tm->tm_wday= LPC_RTC->DOW & RTC_DOW_MASK;
  70. tm->tm_yday= (LPC_RTC->DOY & RTC_DOY_MASK) - 1;
  71. tm->tm_hour= LPC_RTC->HOUR & RTC_HOUR_MASK;
  72. tm->tm_min = LPC_RTC->MIN & RTC_MIN_MASK;
  73. tm->tm_sec = LPC_RTC->SEC & RTC_SEC_MASK;
  74. tm->tm_mon = (LPC_RTC->MONTH & RTC_MONTH_MASK) - 1;
  75. tm->tm_year= (LPC_RTC->YEAR & RTC_YEAR_MASK) - 1900;
  76. return 0;
  77. } else {
  78. return -1;
  79. }
  80. }
  81. /*!
  82. * \brief Set the LPC 17xx hardware clock.
  83. *
  84. * \param tm Points to a structure which contains the date and time
  85. * information.
  86. *
  87. * \return 0 on success or -1 in case of an error.
  88. */
  89. static int Lpc17xxRtcSetClock(NUTRTC *rtc, const struct _tm *tm)
  90. {
  91. if (tm) {
  92. /* Disable timer / counter for writing, reset the 32Khz clock divider */
  93. LPC_RTC->CCR &= ~RTC_CCR_CLKEN;
  94. LPC_RTC->CCR |= RTC_CCR_CTCRST;
  95. LPC_RTC->DOM = tm->tm_mday & RTC_DOM_MASK;
  96. LPC_RTC->DOW = tm->tm_wday & RTC_DOW_MASK;
  97. LPC_RTC->DOY = (tm->tm_yday & RTC_DOY_MASK) + 1;
  98. LPC_RTC->HOUR = tm->tm_hour & RTC_HOUR_MASK;
  99. LPC_RTC->MIN = tm->tm_min & RTC_MIN_MASK;
  100. LPC_RTC->SEC = tm->tm_sec & RTC_SEC_MASK;
  101. LPC_RTC->MONTH = (tm->tm_mon & RTC_MONTH_MASK) + 1;
  102. LPC_RTC->YEAR = (tm->tm_year & RTC_YEAR_MASK) + 1900;
  103. /* Enable timer / counter again, disable ctc reset */
  104. LPC_RTC->CCR &= ~RTC_CCR_CTCRST;
  105. LPC_RTC->CCR |= RTC_CCR_CLKEN;
  106. return 0;
  107. } else {
  108. return -1;
  109. }
  110. }
  111. /*!
  112. * \brief Interrupt handler for RTC Alarm
  113. *
  114. */
  115. static void Lpc17xxRtcInterrupt(void *arg)
  116. {
  117. NUTRTC *rtc = (NUTRTC *)arg;
  118. /*
  119. * there is only 1 interrupt for the RTC, so check here
  120. * if it was the CIIF or the CALF. We need the CALF here
  121. */
  122. if (LPC_RTC->ILR & RTC_IRL_RTCCIF) {
  123. LPC_RTC->ILR |= RTC_IRL_RTCCIF;
  124. }
  125. /* Continue to check the Alarm match*/
  126. if (LPC_RTC->ILR & RTC_IRL_RTCALF) {
  127. ((lpc17xx_rtc_dcb *)rtc->dcb)->flags |= RTC_STATUS_AL0;
  128. /* Signal alarm event queue */
  129. NutEventPostFromIrq(&rtc->alarm);
  130. /* Clear pending interrupt */
  131. LPC_RTC->ILR |= RTC_IRL_RTCALF;
  132. }
  133. }
  134. /*!
  135. * \brief Set an alarm using the LPC 17xx hardware clock.
  136. *
  137. * \param Idx is ignored (LPC 17xx only has 1 Alarm)
  138. * information.
  139. *
  140. * \param tm Points to a structure which contains the date and time
  141. * information. May be NULL to clear the alarm.
  142. * \param aflgs Each bit enables a specific comparision.
  143. * - Bit 0: Seconds
  144. * - Bit 1: Minutes
  145. * - Bit 2: Hours
  146. * - Bit 3: Day of month
  147. * - Bit 4: Month
  148. * - Bit 7: Day of week (Sunday is zero)
  149. * - Bit 8: Year
  150. * - Bit 9: Day of year
  151. *
  152. * \return 0 on success or -1 in case of an error.
  153. */
  154. static int Lpc17xxRtcSetAlarm(NUTRTC *rtc, int idx, const struct _tm *tm, int aflags)
  155. {
  156. uint32_t amr = 0;
  157. if (tm) {
  158. if (aflags & RTC_ALARM_SECOND) {
  159. LPC_RTC->ALSEC = tm->tm_sec & RTC_SEC_MASK;
  160. amr |= RTC_AMR_AMRSEC;
  161. }
  162. if (aflags & RTC_ALARM_MINUTE) {
  163. LPC_RTC->ALMIN = tm->tm_min & RTC_MIN_MASK;
  164. amr |= RTC_AMR_AMRMIN;
  165. }
  166. if (aflags & RTC_ALARM_HOUR) {
  167. LPC_RTC->ALHOUR = tm->tm_hour & RTC_HOUR_MASK;
  168. amr |= RTC_AMR_AMRHOUR;
  169. }
  170. if (aflags & RTC_ALARM_MDAY) {
  171. LPC_RTC->ALDOM = tm->tm_mday & RTC_DOM_MASK;
  172. amr |= RTC_AMR_AMRDOM;
  173. }
  174. if (aflags & RTC_ALARM_MONTH) {
  175. LPC_RTC->ALMON = (tm->tm_mon & RTC_MONTH_MASK) + 1;
  176. amr |= RTC_AMR_AMRMON;
  177. }
  178. if (aflags & RTC_ALARM_WDAY) {
  179. LPC_RTC->ALDOW = tm->tm_wday & RTC_DOW_MASK;
  180. amr |= RTC_AMR_AMRDOW;
  181. }
  182. if (aflags & RTC_ALARM_YEAR) {
  183. LPC_RTC->ALYEAR = (tm->tm_year & RTC_YEAR_MASK) + 1900;
  184. amr |= RTC_AMR_AMRYEAR;
  185. }
  186. if (aflags & RTC_ALARM_YDAY) {
  187. LPC_RTC->ALDOY = (tm->tm_yday & RTC_DOY_MASK) + 1;
  188. amr |= RTC_AMR_AMRDOY;
  189. }
  190. LPC_RTC->AMR = (~amr) & RTC_AMR_BITMASK;
  191. return 0;
  192. } else {
  193. return -1;
  194. }
  195. }
  196. /*!
  197. * \brief Get an alarm using the LPC17xx hardware clock.
  198. *
  199. * \param Idx is ignored (LPC17xx only has 1 Alarm)
  200. * information.
  201. * \param tm Points to a structure which contains the date and time
  202. * information.
  203. *
  204. * \param aflags Points to an unsigned long that receives the enable flags.
  205. *
  206. * \return 0 on success or -1 in case of an error.
  207. */
  208. static int Lpc17xxRtcGetAlarm(NUTRTC *rtc, int idx, struct _tm *tm, int *aflags)
  209. {
  210. register uint32_t amr;
  211. memset(tm, 0, sizeof(struct _tm));
  212. *aflags = 0;
  213. if (aflags == NULL) {
  214. return -1;
  215. }
  216. if (tm) {
  217. /* Inverted mask register */
  218. amr = ~(LPC_RTC->AMR);
  219. if (amr & RTC_AMR_AMRDOM) {
  220. tm->tm_mday= LPC_RTC->ALDOM & RTC_DOM_MASK;
  221. *aflags |= RTC_ALARM_MDAY;
  222. }
  223. if (amr & RTC_AMR_AMRDOW) {
  224. tm->tm_wday= LPC_RTC->ALDOW & RTC_DOW_MASK;
  225. *aflags |= RTC_ALARM_WDAY;
  226. }
  227. if (amr & RTC_AMR_AMRDOY) {
  228. tm->tm_yday= (LPC_RTC->ALDOY & RTC_DOY_MASK) - 1;
  229. *aflags |= RTC_ALARM_YDAY;
  230. }
  231. if (amr & RTC_AMR_AMRHOUR) {
  232. tm->tm_hour= LPC_RTC->ALHOUR & RTC_HOUR_MASK;
  233. *aflags |= RTC_ALARM_HOUR;
  234. }
  235. if (amr & RTC_AMR_AMRMIN) {
  236. tm->tm_min= LPC_RTC->ALMIN & RTC_MIN_MASK;
  237. *aflags |= RTC_ALARM_MINUTE;
  238. }
  239. if (amr & RTC_AMR_AMRSEC) {
  240. tm->tm_sec = LPC_RTC->ALSEC & RTC_SEC_MASK;
  241. *aflags |= RTC_ALARM_SECOND;
  242. }
  243. if (amr & RTC_AMR_AMRMON) {
  244. tm->tm_mon = (LPC_RTC->ALMON & RTC_MONTH_MASK) - 1;
  245. *aflags |= RTC_ALARM_MONTH;
  246. }
  247. if (amr & RTC_AMR_AMRYEAR) {
  248. tm->tm_year= (LPC_RTC->ALYEAR & RTC_YEAR_MASK) - 1900;
  249. *aflags |= RTC_ALARM_YEAR;
  250. }
  251. return 0;
  252. } else {
  253. return -1;
  254. }
  255. }
  256. /*!
  257. * \brief Get status of the LPC17xx hardware clock.
  258. *
  259. * \param sflags Points to an unsigned long that receives the status flags.
  260. * - Bit 0: Power fail.
  261. * - Bit 5: Alarm occured.
  262. * \return 0 on success or -1 in case of an error.
  263. */
  264. static int Lpc17xxRtcGetStatus(NUTRTC *rtc, uint32_t *sflags)
  265. {
  266. /* Check for power failure and clear status bit */
  267. if (LPC_RTC->RTC_AUX & RTC_AUX_RTC_OSCF) {
  268. ((lpc17xx_rtc_dcb *)rtc->dcb)->flags |= RTC_STATUS_PF;
  269. LPC_RTC->RTC_AUX |= RTC_AUX_RTC_OSCF;
  270. }
  271. if (sflags) {
  272. *sflags = ((lpc17xx_rtc_dcb *)rtc->dcb)->flags;
  273. return 0;
  274. } else {
  275. return -1;
  276. }
  277. }
  278. /*!
  279. * \brief Clear status of the LPC17xx hardware clock.
  280. *
  281. * \param tm Points to a structure which contains the date and time
  282. * information.
  283. *
  284. * \return 0 on success or -1 in case of an error.
  285. */
  286. static int Lpc17xxRtcClearStatus(NUTRTC *rtc, uint32_t sflags)
  287. {
  288. ((lpc17xx_rtc_dcb *)rtc->dcb)->flags &= ~sflags;
  289. return 0;
  290. }
  291. /*!
  292. * \brief Initialize the RTC in LPC 17xx controller
  293. *
  294. * \return 0 on success or -1 in case of an error.
  295. *
  296. */
  297. static int Lpc17xxRtcInit(NUTRTC *rtc)
  298. {
  299. rtc->dcb = NutHeapAllocClear(sizeof(lpc17xx_rtc_dcb));
  300. if (rtc->dcb == NULL) {
  301. return -1;
  302. }
  303. if (NutRegisterIrqHandler(&sig_RTC, Lpc17xxRtcInterrupt, rtc) != 0) {
  304. NutHeapFree(rtc->dcb);
  305. return -1;
  306. }
  307. /* Set up clock and power for RTC module */
  308. SysCtlPeripheralClkEnable(CLKPWR_PCONP_PCRTC);
  309. /* Clear all register to be default */
  310. LPC_RTC->ILR = 0x03;
  311. LPC_RTC->CCR = 0x00;
  312. LPC_RTC->CIIR = 0x00;
  313. LPC_RTC->AMR = 0xFF;
  314. LPC_RTC->CALIBRATION = 0x00;
  315. /* enable RTC (run) */
  316. LPC_RTC->CCR |= RTC_CCR_CLKEN;
  317. ((lpc17xx_rtc_dcb *)rtc->dcb)->flags = 0x00000000;
  318. rtc->alarm = NULL;
  319. NutIrqEnable(&sig_RTC);
  320. return(0);
  321. }
  322. NUTRTC rtcLpc17xx = {
  323. /*.dcb = */ NULL, /*!< Driver control block */
  324. /*.rtc_init = */ Lpc17xxRtcInit, /*!< Hardware initialization, rtc_init */
  325. /*.rtc_gettime = */ Lpc17xxRtcGetClock, /*!< Read date and time, rtc_gettime */
  326. /*.rtc_settime = */ Lpc17xxRtcSetClock, /*!< Set date and time, rtc_settime */
  327. /*.rtc_getalarm = */ Lpc17xxRtcGetAlarm, /*!< Read alarm date and time, rtc_getalarm */
  328. /*.rtc_setalarm = */ Lpc17xxRtcSetAlarm, /*!< Set alarm date and time, rtc_setalarm */
  329. /*.rtc_getstatus = */ Lpc17xxRtcGetStatus, /*!< Read status flags, rtc_getstatus */
  330. /*.rtc_clrstatus = */ Lpc17xxRtcClearStatus, /*!< Clear status flags, rtc_clrstatus */
  331. /*.alarm = */ NULL, /*!< Handle for alarm event queue */
  332. };