Squashed 'third_party/pico-sdk/' content from commit 2062372d2
Change-Id: Ic20f199d3ed0ea8d3a6a1bbf513f875ec7500cc6
git-subtree-dir: third_party/pico-sdk
git-subtree-split: 2062372d203b372849d573f252cf7c6dc2800c0a
Signed-off-by: Austin Schuh <austin.linux@gmail.com>
diff --git a/src/rp2_common/hardware_rtc/CMakeLists.txt b/src/rp2_common/hardware_rtc/CMakeLists.txt
new file mode 100644
index 0000000..dce6eff
--- /dev/null
+++ b/src/rp2_common/hardware_rtc/CMakeLists.txt
@@ -0,0 +1 @@
+pico_simple_hardware_target(rtc)
\ No newline at end of file
diff --git a/src/rp2_common/hardware_rtc/include/hardware/rtc.h b/src/rp2_common/hardware_rtc/include/hardware/rtc.h
new file mode 100644
index 0000000..8757e36
--- /dev/null
+++ b/src/rp2_common/hardware_rtc/include/hardware/rtc.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef _HARDWARE_RTC_H
+#define _HARDWARE_RTC_H
+
+#include "pico.h"
+#include "hardware/structs/rtc.h"
+
+/** \file hardware/rtc.h
+ * \defgroup hardware_rtc hardware_rtc
+ *
+ * Hardware Real Time Clock API
+ *
+ * The RTC keeps track of time in human readable format and generates events when the time is equal
+ * to a preset value. Think of a digital clock, not epoch time used by most computers. There are seven
+ * fields, one each for year (12 bit), month (4 bit), day (5 bit), day of the week (3 bit), hour (5 bit)
+ * minute (6 bit) and second (6 bit), storing the data in binary format.
+ *
+ * \sa datetime_t
+ *
+ * \subsection rtc_example Example
+ * \addtogroup hardware_rtc
+ *
+ * \include hello_rtc.c
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*! Callback function type for RTC alarms
+ * \ingroup hardware_rtc
+ *
+ * \sa rtc_set_alarm()
+ */
+typedef void (*rtc_callback_t)(void);
+
+/*! \brief Initialise the RTC system
+ * \ingroup hardware_rtc
+ */
+void rtc_init(void);
+
+/*! \brief Set the RTC to the specified time
+ * \ingroup hardware_rtc
+ *
+ * \param t Pointer to a \ref datetime_t structure contains time to set
+ * \return true if set, false if the passed in datetime was invalid.
+ */
+bool rtc_set_datetime(datetime_t *t);
+
+/*! \brief Get the current time from the RTC
+ * \ingroup hardware_rtc
+ *
+ * \param t Pointer to a \ref datetime_t structure to receive the current RTC time
+ * \return true if datetime is valid, false if the RTC is not running.
+ */
+bool rtc_get_datetime(datetime_t *t);
+
+/*! \brief Is the RTC running?
+ * \ingroup hardware_rtc
+ *
+ */
+bool rtc_running(void);
+
+/*! \brief Set a time in the future for the RTC to call a user provided callback
+ * \ingroup hardware_rtc
+ *
+ * \param t Pointer to a \ref datetime_t structure containing a time in the future to fire the alarm. Any values set to -1 will not be matched on.
+ * \param user_callback pointer to a \ref rtc_callback_t to call when the alarm fires
+ */
+void rtc_set_alarm(datetime_t *t, rtc_callback_t user_callback);
+
+/*! \brief Enable the RTC alarm (if inactive)
+ * \ingroup hardware_rtc
+ */
+void rtc_enable_alarm(void);
+
+/*! \brief Disable the RTC alarm (if active)
+ * \ingroup hardware_rtc
+ */
+void rtc_disable_alarm(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/rp2_common/hardware_rtc/rtc.c b/src/rp2_common/hardware_rtc/rtc.c
new file mode 100644
index 0000000..5429acd
--- /dev/null
+++ b/src/rp2_common/hardware_rtc/rtc.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "pico.h"
+
+#include "hardware/irq.h"
+#include "hardware/rtc.h"
+#include "hardware/resets.h"
+#include "hardware/clocks.h"
+
+// Set this when setting an alarm
+static rtc_callback_t _callback = NULL;
+static bool _alarm_repeats = false;
+
+bool rtc_running(void) {
+ return (rtc_hw->ctrl & RTC_CTRL_RTC_ACTIVE_BITS);
+}
+
+void rtc_init(void) {
+ // Get clk_rtc freq and make sure it is running
+ uint rtc_freq = clock_get_hz(clk_rtc);
+ assert(rtc_freq != 0);
+
+ // Take rtc out of reset now that we know clk_rtc is running
+ reset_block(RESETS_RESET_RTC_BITS);
+ unreset_block_wait(RESETS_RESET_RTC_BITS);
+
+ // Set up the 1 second divider.
+ // If rtc_freq is 400 then clkdiv_m1 should be 399
+ rtc_freq -= 1;
+
+ // Check the freq is not too big to divide
+ assert(rtc_freq <= RTC_CLKDIV_M1_BITS);
+
+ // Write divide value
+ rtc_hw->clkdiv_m1 = rtc_freq;
+}
+
+static bool valid_datetime(datetime_t *t) {
+ // Valid ranges taken from RTC doc. Note when setting an RTC alarm
+ // these values are allowed to be -1 to say "don't match this value"
+ if (!(t->year >= 0 && t->year <= 4095)) return false;
+ if (!(t->month >= 1 && t->month <= 12)) return false;
+ if (!(t->day >= 1 && t->day <= 31)) return false;
+ if (!(t->dotw >= 0 && t->dotw <= 6)) return false;
+ if (!(t->hour >= 0 && t->hour <= 23)) return false;
+ if (!(t->min >= 0 && t->min <= 59)) return false;
+ if (!(t->sec >= 0 && t->sec <= 59)) return false;
+ return true;
+}
+
+bool rtc_set_datetime(datetime_t *t) {
+ if (!valid_datetime(t)) {
+ return false;
+ }
+
+ // Disable RTC
+ rtc_hw->ctrl = 0;
+ // Wait while it is still active
+ while (rtc_running()) {
+ tight_loop_contents();
+ }
+
+ // Write to setup registers
+ rtc_hw->setup_0 = (((uint)t->year) << RTC_SETUP_0_YEAR_LSB ) |
+ (((uint)t->month) << RTC_SETUP_0_MONTH_LSB) |
+ (((uint)t->day) << RTC_SETUP_0_DAY_LSB);
+ rtc_hw->setup_1 = (((uint)t->dotw) << RTC_SETUP_1_DOTW_LSB) |
+ (((uint)t->hour) << RTC_SETUP_1_HOUR_LSB) |
+ (((uint)t->min) << RTC_SETUP_1_MIN_LSB) |
+ (((uint)t->sec) << RTC_SETUP_1_SEC_LSB);
+
+ // Load setup values into rtc clock domain
+ rtc_hw->ctrl = RTC_CTRL_LOAD_BITS;
+
+ // Enable RTC and wait for it to be running
+ rtc_hw->ctrl = RTC_CTRL_RTC_ENABLE_BITS;
+ while (!rtc_running()) {
+ tight_loop_contents();
+ }
+
+ return true;
+}
+
+bool rtc_get_datetime(datetime_t *t) {
+ // Make sure RTC is running
+ if (!rtc_running()) {
+ return false;
+ }
+
+ // Note: RTC_0 should be read before RTC_1
+ uint32_t rtc_0 = rtc_hw->rtc_0;
+ uint32_t rtc_1 = rtc_hw->rtc_1;
+
+ t->dotw = (rtc_0 & RTC_RTC_0_DOTW_BITS ) >> RTC_RTC_0_DOTW_LSB;
+ t->hour = (rtc_0 & RTC_RTC_0_HOUR_BITS ) >> RTC_RTC_0_HOUR_LSB;
+ t->min = (rtc_0 & RTC_RTC_0_MIN_BITS ) >> RTC_RTC_0_MIN_LSB;
+ t->sec = (rtc_0 & RTC_RTC_0_SEC_BITS ) >> RTC_RTC_0_SEC_LSB;
+ t->year = (rtc_1 & RTC_RTC_1_YEAR_BITS ) >> RTC_RTC_1_YEAR_LSB;
+ t->month = (rtc_1 & RTC_RTC_1_MONTH_BITS) >> RTC_RTC_1_MONTH_LSB;
+ t->day = (rtc_1 & RTC_RTC_1_DAY_BITS ) >> RTC_RTC_1_DAY_LSB;
+
+ return true;
+}
+
+void rtc_enable_alarm(void) {
+ // Set matching and wait for it to be enabled
+ hw_set_bits(&rtc_hw->irq_setup_0, RTC_IRQ_SETUP_0_MATCH_ENA_BITS);
+ while(!(rtc_hw->irq_setup_0 & RTC_IRQ_SETUP_0_MATCH_ACTIVE_BITS)) {
+ tight_loop_contents();
+ }
+}
+
+static void rtc_irq_handler(void) {
+ // Always disable the alarm to clear the current IRQ.
+ // Even if it is a repeatable alarm, we don't want it to keep firing.
+ // If it matches on a second it can keep firing for that second.
+ rtc_disable_alarm();
+
+ if (_alarm_repeats) {
+ // If it is a repeatable alarm, re enable the alarm.
+ rtc_enable_alarm();
+ }
+
+ // Call user callback function
+ if (_callback) {
+ _callback();
+ }
+}
+
+static bool rtc_alarm_repeats(datetime_t *t) {
+ // If any value is set to -1 then we don't match on that value
+ // hence the alarm will eventually repeat
+ if (t->year < 0) return true;
+ if (t->month < 0) return true;
+ if (t->day < 0) return true;
+ if (t->dotw < 0) return true;
+ if (t->hour < 0) return true;
+ if (t->min < 0) return true;
+ if (t->sec < 0) return true;
+ return false;
+}
+
+void rtc_set_alarm(datetime_t *t, rtc_callback_t user_callback) {
+ rtc_disable_alarm();
+
+ // Only add to setup if it isn't -1
+ rtc_hw->irq_setup_0 = ((t->year < 0) ? 0 : (((uint)t->year) << RTC_IRQ_SETUP_0_YEAR_LSB )) |
+ ((t->month < 0) ? 0 : (((uint)t->month) << RTC_IRQ_SETUP_0_MONTH_LSB)) |
+ ((t->day < 0) ? 0 : (((uint)t->day) << RTC_IRQ_SETUP_0_DAY_LSB ));
+ rtc_hw->irq_setup_1 = ((t->dotw < 0) ? 0 : (((uint)t->dotw) << RTC_IRQ_SETUP_1_DOTW_LSB)) |
+ ((t->hour < 0) ? 0 : (((uint)t->hour) << RTC_IRQ_SETUP_1_HOUR_LSB)) |
+ ((t->min < 0) ? 0 : (((uint)t->min) << RTC_IRQ_SETUP_1_MIN_LSB )) |
+ ((t->sec < 0) ? 0 : (((uint)t->sec) << RTC_IRQ_SETUP_1_SEC_LSB ));
+
+ // Set the match enable bits for things we care about
+ if (t->year >= 0) hw_set_bits(&rtc_hw->irq_setup_0, RTC_IRQ_SETUP_0_YEAR_ENA_BITS);
+ if (t->month >= 0) hw_set_bits(&rtc_hw->irq_setup_0, RTC_IRQ_SETUP_0_MONTH_ENA_BITS);
+ if (t->day >= 0) hw_set_bits(&rtc_hw->irq_setup_0, RTC_IRQ_SETUP_0_DAY_ENA_BITS);
+ if (t->dotw >= 0) hw_set_bits(&rtc_hw->irq_setup_1, RTC_IRQ_SETUP_1_DOTW_ENA_BITS);
+ if (t->hour >= 0) hw_set_bits(&rtc_hw->irq_setup_1, RTC_IRQ_SETUP_1_HOUR_ENA_BITS);
+ if (t->min >= 0) hw_set_bits(&rtc_hw->irq_setup_1, RTC_IRQ_SETUP_1_MIN_ENA_BITS);
+ if (t->sec >= 0) hw_set_bits(&rtc_hw->irq_setup_1, RTC_IRQ_SETUP_1_SEC_ENA_BITS);
+
+ // Does it repeat? I.e. do we not match on any of the bits
+ _alarm_repeats = rtc_alarm_repeats(t);
+
+ // Store function pointer we can call later
+ _callback = user_callback;
+
+ irq_set_exclusive_handler(RTC_IRQ, rtc_irq_handler);
+
+ // Enable the IRQ at the peri
+ rtc_hw->inte = RTC_INTE_RTC_BITS;
+
+ // Enable the IRQ at the proc
+ irq_set_enabled(RTC_IRQ, true);
+
+ rtc_enable_alarm();
+}
+
+void rtc_disable_alarm(void) {
+ // Disable matching and wait for it to stop being active
+ hw_clear_bits(&rtc_hw->irq_setup_0, RTC_IRQ_SETUP_0_MATCH_ENA_BITS);
+ while(rtc_hw->irq_setup_0 & RTC_IRQ_SETUP_0_MATCH_ACTIVE_BITS) {
+ tight_loop_contents();
+ }
+}