| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- /*
- * Copyright (C) 2012 by Rob van Lieshout (info@pragmalab.nl)
- * Ole Reinhardt (ole.reinhardt@embedded-it.de)
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holders nor the names of
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * For additional information see http://www.ethernut.de/
- *
- */
- #include <string.h>
- #include <cfg/os.h>
- #include <cfg/clock.h>
- #include <cfg/arch.h>
- #include <sys/heap.h>
- #include <sys/event.h>
- #include <sys/timer.h>
- #include <dev/irqreg.h>
- #include <dev/rtc.h>
- #if defined(MCU_LPC176x)
- #include <arch/cm3/nxp/lpc176x_clk.h>
- #elif defined(MCU_LPC177x_8x)
- #include <arch/cm3/nxp/lpc177x_8x_clk.h>
- #elif defined(MCU_LPC407x_8x)
- #include <arch/cm3/nxp/lpc407x_8x_clk.h>
- #else
- #warning "Unknown LPC familiy"
- #endif
- #include <arch/cm3/nxp/lpc17xx_rtc.h>
- typedef struct _lpc17xx_rtc_dcb lpc17xx_rtc_dcb;
- struct _lpc17xx_rtc_dcb {
- uint32_t flags;
- };
- /*!
- * \brief Get date and time from an LPC 17xx hardware clock.
- *
- * \param tm Points to a structure that receives the date and time
- * information.
- *
- * \return 0 on success or -1 in case of an error.
- */
- static int Lpc17xxRtcGetClock(NUTRTC *rtc, struct _tm *tm)
- {
- if (tm) {
- tm->tm_mday= LPC_RTC->DOM & RTC_DOM_MASK;
- tm->tm_wday= LPC_RTC->DOW & RTC_DOW_MASK;
- tm->tm_yday= (LPC_RTC->DOY & RTC_DOY_MASK) - 1;
- tm->tm_hour= LPC_RTC->HOUR & RTC_HOUR_MASK;
- tm->tm_min = LPC_RTC->MIN & RTC_MIN_MASK;
- tm->tm_sec = LPC_RTC->SEC & RTC_SEC_MASK;
- tm->tm_mon = (LPC_RTC->MONTH & RTC_MONTH_MASK) - 1;
- tm->tm_year= (LPC_RTC->YEAR & RTC_YEAR_MASK) - 1900;
- return 0;
- } else {
- return -1;
- }
- }
- /*!
- * \brief Set the LPC 17xx hardware clock.
- *
- * \param tm Points to a structure which contains the date and time
- * information.
- *
- * \return 0 on success or -1 in case of an error.
- */
- static int Lpc17xxRtcSetClock(NUTRTC *rtc, const struct _tm *tm)
- {
- if (tm) {
- /* Disable timer / counter for writing, reset the 32Khz clock divider */
- LPC_RTC->CCR &= ~RTC_CCR_CLKEN;
- LPC_RTC->CCR |= RTC_CCR_CTCRST;
- LPC_RTC->DOM = tm->tm_mday & RTC_DOM_MASK;
- LPC_RTC->DOW = tm->tm_wday & RTC_DOW_MASK;
- LPC_RTC->DOY = (tm->tm_yday & RTC_DOY_MASK) + 1;
- LPC_RTC->HOUR = tm->tm_hour & RTC_HOUR_MASK;
- LPC_RTC->MIN = tm->tm_min & RTC_MIN_MASK;
- LPC_RTC->SEC = tm->tm_sec & RTC_SEC_MASK;
- LPC_RTC->MONTH = (tm->tm_mon & RTC_MONTH_MASK) + 1;
- LPC_RTC->YEAR = (tm->tm_year & RTC_YEAR_MASK) + 1900;
- /* Enable timer / counter again, disable ctc reset */
- LPC_RTC->CCR &= ~RTC_CCR_CTCRST;
- LPC_RTC->CCR |= RTC_CCR_CLKEN;
- return 0;
- } else {
- return -1;
- }
- }
- /*!
- * \brief Interrupt handler for RTC Alarm
- *
- */
- static void Lpc17xxRtcInterrupt(void *arg)
- {
- NUTRTC *rtc = (NUTRTC *)arg;
- /*
- * there is only 1 interrupt for the RTC, so check here
- * if it was the CIIF or the CALF. We need the CALF here
- */
- if (LPC_RTC->ILR & RTC_IRL_RTCCIF) {
- LPC_RTC->ILR |= RTC_IRL_RTCCIF;
- }
- /* Continue to check the Alarm match*/
- if (LPC_RTC->ILR & RTC_IRL_RTCALF) {
- ((lpc17xx_rtc_dcb *)rtc->dcb)->flags |= RTC_STATUS_AL0;
- /* Signal alarm event queue */
- NutEventPostFromIrq(&rtc->alarm);
- /* Clear pending interrupt */
- LPC_RTC->ILR |= RTC_IRL_RTCALF;
- }
- }
- /*!
- * \brief Set an alarm using the LPC 17xx hardware clock.
- *
- * \param Idx is ignored (LPC 17xx only has 1 Alarm)
- * information.
- *
- * \param tm Points to a structure which contains the date and time
- * information. May be NULL to clear the alarm.
- * \param aflgs Each bit enables a specific comparision.
- * - Bit 0: Seconds
- * - Bit 1: Minutes
- * - Bit 2: Hours
- * - Bit 3: Day of month
- * - Bit 4: Month
- * - Bit 7: Day of week (Sunday is zero)
- * - Bit 8: Year
- * - Bit 9: Day of year
- *
- * \return 0 on success or -1 in case of an error.
- */
- static int Lpc17xxRtcSetAlarm(NUTRTC *rtc, int idx, const struct _tm *tm, int aflags)
- {
- uint32_t amr = 0;
- if (tm) {
- if (aflags & RTC_ALARM_SECOND) {
- LPC_RTC->ALSEC = tm->tm_sec & RTC_SEC_MASK;
- amr |= RTC_AMR_AMRSEC;
- }
- if (aflags & RTC_ALARM_MINUTE) {
- LPC_RTC->ALMIN = tm->tm_min & RTC_MIN_MASK;
- amr |= RTC_AMR_AMRMIN;
- }
- if (aflags & RTC_ALARM_HOUR) {
- LPC_RTC->ALHOUR = tm->tm_hour & RTC_HOUR_MASK;
- amr |= RTC_AMR_AMRHOUR;
- }
- if (aflags & RTC_ALARM_MDAY) {
- LPC_RTC->ALDOM = tm->tm_mday & RTC_DOM_MASK;
- amr |= RTC_AMR_AMRDOM;
- }
- if (aflags & RTC_ALARM_MONTH) {
- LPC_RTC->ALMON = (tm->tm_mon & RTC_MONTH_MASK) + 1;
- amr |= RTC_AMR_AMRMON;
- }
- if (aflags & RTC_ALARM_WDAY) {
- LPC_RTC->ALDOW = tm->tm_wday & RTC_DOW_MASK;
- amr |= RTC_AMR_AMRDOW;
- }
- if (aflags & RTC_ALARM_YEAR) {
- LPC_RTC->ALYEAR = (tm->tm_year & RTC_YEAR_MASK) + 1900;
- amr |= RTC_AMR_AMRYEAR;
- }
- if (aflags & RTC_ALARM_YDAY) {
- LPC_RTC->ALDOY = (tm->tm_yday & RTC_DOY_MASK) + 1;
- amr |= RTC_AMR_AMRDOY;
- }
- LPC_RTC->AMR = (~amr) & RTC_AMR_BITMASK;
- return 0;
- } else {
- return -1;
- }
- }
- /*!
- * \brief Get an alarm using the LPC17xx hardware clock.
- *
- * \param Idx is ignored (LPC17xx only has 1 Alarm)
- * information.
- * \param tm Points to a structure which contains the date and time
- * information.
- *
- * \param aflags Points to an unsigned long that receives the enable flags.
- *
- * \return 0 on success or -1 in case of an error.
- */
- static int Lpc17xxRtcGetAlarm(NUTRTC *rtc, int idx, struct _tm *tm, int *aflags)
- {
- register uint32_t amr;
- memset(tm, 0, sizeof(struct _tm));
- *aflags = 0;
- if (aflags == NULL) {
- return -1;
- }
- if (tm) {
- /* Inverted mask register */
- amr = ~(LPC_RTC->AMR);
- if (amr & RTC_AMR_AMRDOM) {
- tm->tm_mday= LPC_RTC->ALDOM & RTC_DOM_MASK;
- *aflags |= RTC_ALARM_MDAY;
- }
- if (amr & RTC_AMR_AMRDOW) {
- tm->tm_wday= LPC_RTC->ALDOW & RTC_DOW_MASK;
- *aflags |= RTC_ALARM_WDAY;
- }
- if (amr & RTC_AMR_AMRDOY) {
- tm->tm_yday= (LPC_RTC->ALDOY & RTC_DOY_MASK) - 1;
- *aflags |= RTC_ALARM_YDAY;
- }
- if (amr & RTC_AMR_AMRHOUR) {
- tm->tm_hour= LPC_RTC->ALHOUR & RTC_HOUR_MASK;
- *aflags |= RTC_ALARM_HOUR;
- }
- if (amr & RTC_AMR_AMRMIN) {
- tm->tm_min= LPC_RTC->ALMIN & RTC_MIN_MASK;
- *aflags |= RTC_ALARM_MINUTE;
- }
- if (amr & RTC_AMR_AMRSEC) {
- tm->tm_sec = LPC_RTC->ALSEC & RTC_SEC_MASK;
- *aflags |= RTC_ALARM_SECOND;
- }
- if (amr & RTC_AMR_AMRMON) {
- tm->tm_mon = (LPC_RTC->ALMON & RTC_MONTH_MASK) - 1;
- *aflags |= RTC_ALARM_MONTH;
- }
- if (amr & RTC_AMR_AMRYEAR) {
- tm->tm_year= (LPC_RTC->ALYEAR & RTC_YEAR_MASK) - 1900;
- *aflags |= RTC_ALARM_YEAR;
- }
- return 0;
- } else {
- return -1;
- }
- }
- /*!
- * \brief Get status of the LPC17xx hardware clock.
- *
- * \param sflags Points to an unsigned long that receives the status flags.
- * - Bit 0: Power fail.
- * - Bit 5: Alarm occured.
- * \return 0 on success or -1 in case of an error.
- */
- static int Lpc17xxRtcGetStatus(NUTRTC *rtc, uint32_t *sflags)
- {
- /* Check for power failure and clear status bit */
- if (LPC_RTC->RTC_AUX & RTC_AUX_RTC_OSCF) {
- ((lpc17xx_rtc_dcb *)rtc->dcb)->flags |= RTC_STATUS_PF;
- LPC_RTC->RTC_AUX |= RTC_AUX_RTC_OSCF;
- }
- if (sflags) {
- *sflags = ((lpc17xx_rtc_dcb *)rtc->dcb)->flags;
- return 0;
- } else {
- return -1;
- }
- }
- /*!
- * \brief Clear status of the LPC17xx hardware clock.
- *
- * \param tm Points to a structure which contains the date and time
- * information.
- *
- * \return 0 on success or -1 in case of an error.
- */
- static int Lpc17xxRtcClearStatus(NUTRTC *rtc, uint32_t sflags)
- {
- ((lpc17xx_rtc_dcb *)rtc->dcb)->flags &= ~sflags;
- return 0;
- }
- /*!
- * \brief Initialize the RTC in LPC 17xx controller
- *
- * \return 0 on success or -1 in case of an error.
- *
- */
- static int Lpc17xxRtcInit(NUTRTC *rtc)
- {
- rtc->dcb = NutHeapAllocClear(sizeof(lpc17xx_rtc_dcb));
- if (rtc->dcb == NULL) {
- return -1;
- }
- if (NutRegisterIrqHandler(&sig_RTC, Lpc17xxRtcInterrupt, rtc) != 0) {
- NutHeapFree(rtc->dcb);
- return -1;
- }
- /* Set up clock and power for RTC module */
- SysCtlPeripheralClkEnable(CLKPWR_PCONP_PCRTC);
- /* Clear all register to be default */
- LPC_RTC->ILR = 0x03;
- LPC_RTC->CCR = 0x00;
- LPC_RTC->CIIR = 0x00;
- LPC_RTC->AMR = 0xFF;
- LPC_RTC->CALIBRATION = 0x00;
- /* enable RTC (run) */
- LPC_RTC->CCR |= RTC_CCR_CLKEN;
- ((lpc17xx_rtc_dcb *)rtc->dcb)->flags = 0x00000000;
- rtc->alarm = NULL;
- NutIrqEnable(&sig_RTC);
- return(0);
- }
- NUTRTC rtcLpc17xx = {
- /*.dcb = */ NULL, /*!< Driver control block */
- /*.rtc_init = */ Lpc17xxRtcInit, /*!< Hardware initialization, rtc_init */
- /*.rtc_gettime = */ Lpc17xxRtcGetClock, /*!< Read date and time, rtc_gettime */
- /*.rtc_settime = */ Lpc17xxRtcSetClock, /*!< Set date and time, rtc_settime */
- /*.rtc_getalarm = */ Lpc17xxRtcGetAlarm, /*!< Read alarm date and time, rtc_getalarm */
- /*.rtc_setalarm = */ Lpc17xxRtcSetAlarm, /*!< Set alarm date and time, rtc_setalarm */
- /*.rtc_getstatus = */ Lpc17xxRtcGetStatus, /*!< Read status flags, rtc_getstatus */
- /*.rtc_clrstatus = */ Lpc17xxRtcClearStatus, /*!< Clear status flags, rtc_clrstatus */
- /*.alarm = */ NULL, /*!< Handle for alarm event queue */
- };
|