Add support for RTC Tested on Sub1GHz module because it's the only module i have...
authorNathael Pajani <nathael.pajani@ed3l.fr>
Mon, 2 Feb 2015 01:39:24 +0000 (02:39 +0100)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Tue, 8 Nov 2022 16:03:04 +0000 (17:03 +0100)
drivers/rtc.c [new file with mode: 0644]
include/core/lpc_regs_12xx.h
include/drivers/rtc.h [new file with mode: 0644]

diff --git a/drivers/rtc.c b/drivers/rtc.c
new file mode 100644 (file)
index 0000000..9c5b089
--- /dev/null
@@ -0,0 +1,218 @@
+/****************************************************************************
+ *  drivers/rtc.c
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+/***************************************************************************** */
+/*                RTC and RTC Interrupts                                    */
+/***************************************************************************** */
+
+
+
+#include <stdint.h>
+#include "core/lpc_regs_12xx.h"
+#include "core/lpc_core_cm0.h"
+#include "core/system.h"
+#include "core/pio.h"
+#include "drivers/rtc.h"
+
+
+static uint32_t match_first = 0;
+static uint32_t rtc_match_period = 0;
+static uint8_t periodic_match = 0;
+
+/***************************************************************************** */
+/* Return the number of RTC ticks from system power on.
+ * This count is from power being present, even if resets occured.
+ * The calls maded during the first three seconds after RTC timer start will return 0.
+ */
+uint32_t rtc_get_count(void)
+{
+       struct lpc_rtc* rtc = LPC_RTC;
+       static uint8_t rtc_start_ok = 0;
+
+       /* Is the count valid ? */
+       if (rtc_start_ok == 1) {
+               return rtc->data;
+       }
+       /* Not started for sufficient time to have a reliable value (see UM10441 section 16.6.1) */
+       if (is_systick_running() == 0) {
+               systick_start();
+               return 0;
+       } else {
+               uint32_t ticks = systick_get_tick_count();
+               uint32_t ms_period = systick_get_tick_ms_period();
+               if ((ticks * ms_period) < 3000) {
+                       return 0;
+               }
+               rtc_start_ok = 1;
+               return 0;
+       }
+}
+
+
+/***************************************************************************** */
+/*   RTC setup   */
+
+/* In case someone wants the RTC to count something different from seconds */
+void rtc_clk_src_select(uint8_t source, uint8_t clk_div)
+{
+       struct lpc_sys_control* sysctrl = LPC_SYS_CONTROL;
+       struct lpc_pm_unit* pm_unit = LPC_PMU;
+       uint32_t tmp = 0;
+
+       /* Turn off RTC controll regs */
+       subsystem_power(LPC_SYS_ABH_CLK_CTRL_RTC, 0);
+       /* Change clock source. Warning : do not write ones to reserved bits. */
+       tmp = (pm_unit->system_config & LPC_WAKEUP_PIN_HYST_MASK);
+       pm_unit->system_config = (tmp | ((source & 0x0F) << LPC_RTC_CLK_SRC_SHIFT));
+       /* Change the RTC Clock divider if source is PCLK */
+       if (source == LPC_RTC_CLK_PCLK) {
+               sysctrl->rtc_clk_div = clk_div;
+       } else {
+               sysctrl->rtc_clk_div = 0;
+       }
+}
+
+/* Start the RTC. Once the RTC has been started using this call, it cannot be stopped. */
+void rtc_on(void)
+{
+       struct lpc_rtc* rtc = LPC_RTC;
+       /* Provide power to RTC control block */
+       subsystem_power(LPC_SYS_ABH_CLK_CTRL_RTC, 1);
+       rtc->control = LPC_RTC_START;
+}
+/* Disable the RTC control block. Note that once started from software the RTC cannot be stopped. */
+void rtc_ctrl_off(void)
+{
+       /* Remove power from RTC control blockxs */
+       subsystem_power(LPC_SYS_ABH_CLK_CTRL_RTC, 0);
+}
+/* This will disable the RTC. This is only possible if the RTC has not been started before. */
+void rtc_disable(void)
+{
+       struct lpc_rtc* rtc = LPC_RTC;
+       subsystem_power(LPC_SYS_ABH_CLK_CTRL_RTC, 1);
+       rtc->control = LPC_RTC_DISABLE;
+       subsystem_power(LPC_SYS_ABH_CLK_CTRL_RTC, 0);
+}
+
+/* Change the RTC value */
+void rtc_set_date(uint32_t date)
+{
+       struct lpc_rtc* rtc = LPC_RTC;
+       rtc->load = date;
+}
+
+
+/***************************************************************************** */
+/* Set the match value for the RTC
+ * If periodic is 1 then the rtc match register will be updated after the interrupt
+ *    has been handled by adding the offset.
+ * If date is set, the match register will be set to date. (regardless of the periodic
+ *    argument, wich allows to have the first interrupt at a given date, and the following
+ *    ones at a period set by the offset argument.
+ * If date is 0, then the match register is set to current date + offset.
+ * return -1 if RTC is not started and synced
+ */
+int rtc_set_match(uint32_t date, uint32_t offset, uint8_t periodic)
+{
+       struct lpc_rtc* rtc = LPC_RTC;
+
+       if (rtc_get_count() == 0) {
+               return -1;
+       }
+
+       periodic_match = periodic;
+       rtc_match_period = offset;
+       if (date != 0) {
+               rtc->match = date;
+       } else {
+               rtc->match = rtc_get_count() + offset;
+       }
+       return 0;
+}
+
+/* RTC Interrupts Callbacks */
+static void (*rtc_calback) (uint32_t);
+
+void setup_callback(uint32_t ticks)
+{
+       uint32_t date = rtc_get_count();
+       if (date != 0) {
+               if (date >= match_first) {
+                       match_first = 0; /* First match missed, forget about it */
+               }
+               rtc_set_match(match_first, rtc_match_period, periodic_match);
+               remove_systick_callback(&setup_callback);
+       }
+}
+
+/* Register a callback for the RTC
+ * 'when' should be in the future (according to RTC counter) and 'period' can be used to get
+ *   periodic interrupts. 'period' is a number of RTC counter increments.
+ * Return a positive integer if registration is OK and callback has a chance of being called.
+ * Return a negative integer if the match configuration was not possible.
+ */
+int set_rtc_callback(void (*callback) (uint32_t), uint32_t when, uint32_t period)
+{
+       struct lpc_rtc* rtc = LPC_RTC;
+       int ret = 0;
+
+       /* Register the callback */
+       rtc_calback = callback;
+       if (rtc_get_count() != 0) {
+               rtc_set_match(when, period, ((period == 0) ? 0 : 1));
+       } else {
+               /* Add callback for "config later" */
+               ret = add_systick_callback(&setup_callback, (3000 / systick_get_tick_ms_period()));
+               match_first = when;
+               periodic_match = ((period == 0) ? 0 : 1);
+               rtc_match_period = period;
+       }
+       rtc->intr_control = LPC_RTC_INT_ENABLE;
+       NVIC_EnableIRQ(RTC_IRQ);
+       return ret;
+}
+void remove_rtc_callback()
+{
+       struct lpc_rtc* rtc = LPC_RTC;
+
+       /* Remove the handler */
+       rtc_calback = NULL;
+       rtc->intr_control = LPC_RTC_INT_DISABLE;
+       /* And disable the interrupt */
+       NVIC_DisableIRQ(RTC_IRQ);
+}
+
+/* Interrupt Handler */
+void RTC_Handler(void)
+{
+       struct lpc_rtc* rtc = LPC_RTC;
+       /* Call interrupt handler */
+       if (rtc_calback != NULL) {
+               rtc_calback(rtc->data);
+       }
+       rtc->intr_clear = LPC_RTC_CLEAR_INTR;
+       if (periodic_match == 1) {
+               rtc->match = rtc->data + rtc_match_period;
+       }
+}
+
+
+
index d4cdff2..e993de7 100644 (file)
@@ -323,6 +323,41 @@ struct lpc_system_tick {
 
 
 
+/***************************************************************************** */
+/*                    Cortex-M0 RTC (Real-Time Clock)                          */
+/***************************************************************************** */
+/* Cortex-M0 RTC Registers */
+struct lpc_rtc {
+    volatile uint32_t data;     /* 0x000 : Data register (R/-) */
+    volatile uint32_t match;    /* 0x004 : Match register (R/W) */
+    volatile uint32_t load;     /* 0x008 : Load register (R/W) */
+    volatile uint32_t control;  /* 0x00C : Control register (R/W) */
+    volatile uint32_t intr_control;        /* 0x010 : Interrupt control set/clear register (R/W) */
+    volatile uint32_t raw_intr_status;     /* 0x014 : Raw interrupt status register (R/-) */
+    volatile uint32_t masked_intr_status;  /* 0x018 : Masked interrupt status register (R/-) */
+    volatile uint32_t intr_clear;          /* 0x01C : Interrupt clear register (-/W) */
+};
+#define LPC_RTC  ((struct lpc_rtc*) LPC_RTC_BASE) /* SysTick configuration struct */
+
+/* RTC Clock source selection */
+#define LPC_RTC_CLK_1HZ          (0)
+#define LPC_RTC_CLK_1HZ_DELAYED  (0x01)
+#define LPC_RTC_CLK_1KHZ         (0x0A)
+#define LPC_RTC_CLK_PCLK         (0x04)  /* Main clock divided by RTC clock divider value */
+
+/* RTC control register */
+#define LPC_RTC_START  (1UL << 0)
+#define LPC_RTC_DISABLE  (0)
+
+/* RTC interrupt control register */
+#define LPC_RTC_INT_ENABLE  (1UL << 0)
+#define LPC_RTC_INT_DISABLE  (0)
+
+/* RTC interrupt clear register */
+#define LPC_RTC_CLEAR_INTR  (1UL << 0)
+
+
+
 /***************************************************************************** */
 /*                     Power Management Unit                                   */
 /***************************************************************************** */
@@ -336,6 +371,12 @@ struct lpc_pm_unit
 };
 #define LPC_PMU         ((struct lpc_pm_unit *) LPC_PMU_BASE)
 
+/* System config register */
+#define LPC_WAKEUP_PIN_HYST_MASK    (0x01 << 10)
+#define LPC_RTC_CLK_SRC_SHIFT       11
+#define LPC_RTC_CLK_SRC_MASK        (0x0F << LPC_RTC_CLK_SRC_SHIFT)
+/* See RTC section above for RTC Clock source selection bits */
+
 
 /***************************************************************************** */
 /*                     IO Control                                              */
diff --git a/include/drivers/rtc.h b/include/drivers/rtc.h
new file mode 100644 (file)
index 0000000..4540184
--- /dev/null
@@ -0,0 +1,81 @@
+/****************************************************************************
+ *  drivers/rtc.h
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+/***************************************************************************** */
+/*                RTC and RTC Interrupts                                    */
+/***************************************************************************** */
+
+#include <stdint.h>
+
+
+/* Return the number of RTC ticks from system power on.
+ * This count is from power being present, even if resets occured.
+ * The calls maded during the first three seconds after RTC timer start will return 0.
+ */
+uint32_t rtc_get_count(void);
+
+
+/***************************************************************************** */
+/*   RTC setup   */
+
+/* In case someone wants the RTC to count something different from seconds
+ * No need to call this function if you only want to count seconds, this is the default.
+ * source selection values are defined in lpc_regs_12xx.h
+ * clk_div is only usefull when selected clock source is PCLK (peripheral clock). Use value
+ *    betwween 1 and 255 included.
+ */
+void rtc_clk_src_select(uint8_t source, uint8_t clk_div);
+
+/* Start the RTC. Once the RTC has been started using this call, it cannot be stopped. */
+void rtc_on(void);
+
+/* Disable the RTC control block. Note that once started from software the RTC cannot be stopped. */
+void rtc_ctrl_off(void);
+
+/* This will disable the RTC. This is only possible if the RTC has not been started before. */
+void rtc_disable(void);
+
+/* Change the RTC value */
+void rtc_set_date(uint32_t date);
+
+
+/***************************************************************************** */
+/* Set the match value for the RTC
+ * If periodic is 1 then the rtc match register will be updated after the interrupt
+ *    has been handled by adding the offset.
+ * If date is set, the match register will be set to date. (regardless of the periodic
+ *    argument, wich allows to have the first interrupt at a given date, and the following
+ *    ones at a period set by the offset argument.
+ * If date is 0, then the match register is set to current date + offset.
+ * return -1 if RTC is not started and synced.
+ */
+int rtc_set_match(uint32_t date, uint32_t offset, uint8_t periodic);
+
+/* Register a callback for the RTC
+ * 'when' should be in the future (according to RTC counter) and 'period' can be used to get
+ *   periodic interrupts. 'period' is a number of RTC counter increments.
+ * Return a positive integer if registration is OK and callback has a chance of being called.
+ * Return a negative integer if the match configuration was not possible.
+ */
+int set_rtc_callback(void (*callback) (uint32_t), uint32_t when, uint32_t period);
+void remove_rtc_callback();
+
+
+