--- /dev/null
+/****************************************************************************
+ * 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;
+ }
+}
+
+
+
+/***************************************************************************** */
+/* 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 */
/***************************************************************************** */
};
#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 */
--- /dev/null
+/****************************************************************************
+ * 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();
+
+
+