Watchdog support fixed and tested. To be completed though.
authorNathael Pajani <nathael.pajani@ed3l.fr>
Sat, 1 May 2021 19:34:03 +0000 (21:34 +0200)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Fri, 10 Feb 2023 18:02:59 +0000 (19:02 +0100)
core/watchdog.c
include/core/watchdog.h

index 96445d8..ae3870c 100644 (file)
@@ -40,8 +40,45 @@ void watchdog_feed(void)
        lpc_enable_irq();
 }
 
-static void (*wdt_callback)(void) = NULL;
 
+/* Return 0 if flag is not set, or LPC_WDT_TIMEOUT_FLAG if flag is set.
+ * This function does not clear the flag
+ */
+uint32_t watchdog_check_timeout_flag(void)
+{
+       struct lpc_watchdog* wdt = LPC_WDT;
+       return (wdt->mode & LPC_WDT_TIMEOUT_FLAG);
+}
+
+/* Clear the timeout flag.
+ * No checks performed, clear even if flag is not set
+ */
+void watchdog_clear_timeout_flag(void)
+{
+       struct lpc_watchdog* wdt = LPC_WDT;
+       wdt->mode |= LPC_WDT_TIMEOUT_FLAG;
+}
+
+/* Return current watchdog timer value */
+uint32_t watchdog_get_timer_val(void)
+{
+       struct lpc_watchdog* wdt = LPC_WDT;
+       return wdt->timer_value;
+}
+
+/* Change the watchdog timer value, if not protected */
+void watchdog_set_timer_val(uint32_t nb_clk)
+{
+       struct lpc_watchdog* wdt = LPC_WDT;
+
+       if (!(wdt->mode & LPC_WDT_TIMER_VAL_PROTECT)) {
+               wdt->timer_const = ((nb_clk >> 2) & LPC_WDT_TIMER_MAX);
+       }
+}
+
+
+/***************************************************************************** */
+static void (*wdt_callback)(void) = NULL;
 
 /* With no callback, the watchdog interupt may be used to wake up by generating an interrupt */
 void WDT_Handler(void)
@@ -54,6 +91,7 @@ void WDT_Handler(void)
        }
 }
 
+/***************************************************************************** */
 
 /* Lock the watchdog clock source power.
  * Once locked, writes to the current watchdog clock power bits in powerdown_*_cfg will
@@ -79,16 +117,6 @@ void watchdog_lock_timer_val(void)
        wdt->mode |= LPC_WDT_TIMER_VAL_PROTECT;
 }
 
-/* Change the watchdog timer value, if not protected */
-void watchdog_set_timer_val(uint32_t nb_clk)
-{
-       struct lpc_watchdog* wdt = LPC_WDT;
-
-       if (!(wdt->mode & LPC_WDT_TIMER_VAL_PROTECT)) {
-               wdt->timer_const = ((nb_clk >> 2) & LPC_WDT_TIMER_MAX);
-       }
-}
-
 /* Lock the watchdog and all related features.
  * Calls all the other watchdog_lock_* functions (clk_src, clk_src_power, timer_val, enable).
  */
@@ -98,12 +126,13 @@ void watchdog_lock_full(void)
        watchdog_lock_clk_src_power();
 }
 
+
+
+/***************************************************************************** */
+
 /*
  * Configure the watchdog.
- * clk_sel is either 0 (IRC) or 1 (WDTCLK). The corresponding clock source will be powered on.
- * Note : only WDTCLK is running in deep power down mode
- * Note : protecting the clock source power will prevent turning off the IRC for power saving
- *   if it is selected as main clock source.
+ * The clock to the Watchdog timer is set to 1MHz
  */
 void watchdog_config(const struct wdt_config* wd_conf)
 {
@@ -111,26 +140,34 @@ void watchdog_config(const struct wdt_config* wd_conf)
        struct lpc_watchdog* wdt = LPC_WDT;
 
        NVIC_DisableIRQ(WDT_IRQ);
+
        /* Power wadchdog block before changing it's configuration */
        subsystem_power(LPC_SYS_AHB_CLK_CTRL_Watchdog, 1);
+       /* Configure the watchdog clock source frequency  */
+       sys_config->WDT_osc_ctrl = LPC_WDT_CLK_DIVIDER(4) | LPC_WDT_FREQSEL_4MHz; /* 1MHz */
+       /* Power ON Watchdog clock */
+       sys_config->powerdown_run_cfg &= ~(LPC_POWER_DOWN_WDT_OSC);
+
        /* Configure watchdog timeout for normal operation */
        wdt->timer_const = ((wd_conf->nb_clk >> 2) & LPC_WDT_TIMER_MAX);
+
        /* If intr_mode_only is set, a watchdog timeout will trigger an interrupt instead of a reset */
        if (wd_conf->intr_mode_only == 1) {
                wdt->mode = LPC_WDT_EN;
        } else {
                wdt->mode = LPC_WDT_EN | LPC_WDT_RESET_ON_TIMEOUT;
        }
+
        /* Register the callback for the interrupt */
        wdt_callback = wd_conf->callback;
-       /* Power ON Watchdog clock */
-       sys_config->powerdown_run_cfg &= ~(LPC_POWER_DOWN_WDT_OSC);
+
        /* Use the windows functionnality ? */
        if (wd_conf->wdt_window > 0x100) {
                wdt->window_compare = (wd_conf->wdt_window & LPC_WDT_TIMER_MAX);
        } else {
                wdt->window_compare = LPC_WDT_TIMER_MAX;
        }
+
        /* Warning interrupt ? */
        if (wd_conf->wdt_warn != 0) {
                if (wd_conf->wdt_warn > LPC_WDT_WARNINT_MAXVAL) {
@@ -139,7 +176,18 @@ void watchdog_config(const struct wdt_config* wd_conf)
                        wdt->warning_int_compare = wd_conf->wdt_warn;
                }
        }
+
+       /* Feed sequence to validate the configuration */
+       watchdog_feed();
+
+       /* FIXME : Always enable the interupt ? */
+       NVIC_EnableIRQ(WDT_IRQ);
+
+       /* Wait for watchdog to start before locking (if requested) */
+       do { } while (wdt->timer_value == 0x0000ff);
+
        /* Protect any of the watchdog functions now ? */
+       /* FIXME : not working - not tested */
        if (wd_conf->locks != 0) {
                uint32_t mode = wdt->mode;
                if (wd_conf->locks & WDT_CLK_POWER_LOCK) {
@@ -148,11 +196,11 @@ void watchdog_config(const struct wdt_config* wd_conf)
                if (wd_conf->locks & WDT_TIMER_VAL_LOCK) {
                        mode |= LPC_WDT_TIMER_VAL_PROTECT;
                }
+               /* Wait at least 6 Watchdog clocks before locking */
+               /* FIXME : sleep set to an arbitrary value */
+               usleep(100);
                wdt->mode = mode;
        }
-       /* Feed sequence to validate the configuration */
-       watchdog_feed();
-       NVIC_EnableIRQ(WDT_IRQ);
 }
 
 
index 6f54169..8cbf288 100644 (file)
 #include "core/lpc_regs.h"
 
 
+
 #define WDT_CLK_POWER_LOCK  (0x01 << 0)
-#define WDT_EN_LOCK         (0x01 << 1)
+#define WDT_EN_LOCK         (0x01 << 1) /* Unused for LPC82x */
 #define WDT_TIMER_VAL_LOCK  (0x01 << 2)
 
+
+/* NOTE : The clock to the Watchdog timer is set to 1MHz */
+
 struct wdt_config {
        uint8_t clk_sel; /* Unused, only here for API compatibility */
        uint8_t intr_mode_only; /* If set to 1, a watchdog timeout will trigger an interrupt instead of a reset */
        void (*callback)(void);
-       uint32_t locks; /* Bitfield from WDT_*_LOCK defined in watchdog.h */
+       uint32_t locks; /* Bitfield from WDT_*_LOCK defined above this struct */
        /* Number of clk_src clocks before the watchdog timer times out. Will be divided by 4 to give
         *   the watchdog reload value */
        uint32_t nb_clk;  /* 0x3FF to 0x03FFFFFF */
@@ -50,10 +54,31 @@ struct wdt_config {
        uint16_t wdt_warn; /* 0x00 to 0x3FF */
 };
 
+
+
 /***************************************************************************** */
 void watchdog_feed(void);
 
 
+/***************************************************************************** */
+/* Return 0 if flag is not set, or LPC_WDT_TIMEOUT_FLAG if flag is set.
+ * This function does not clear the flag
+ */
+uint32_t watchdog_check_timeout_flag(void);
+
+/* Clear the timeout flag.
+ * No checks performed, clear even if flag is not set
+ */
+void watchdog_clear_timeout_flag(void);
+
+/* Return current watchdog timer value */
+uint32_t watchdog_get_timer_val(void);
+
+/* Change the watchdog timer value, if not protected */
+void watchdog_set_timer_val(uint32_t nb_clk);
+
+
+/***************************************************************************** */
 /* Lock the watchdog clock source power.
  * Once locked, writes to the current watchdog clock power bits in powerdown_*_cfg will
  *   have no effect.
@@ -65,24 +90,19 @@ void watchdog_lock_clk_src_power(void);
 /* Lock the watchdog timer value */
 void watchdog_lock_timer_val(void);
 
-/* Change the watchdog timer value, if not protected */
-void watchdog_set_timer_val(uint32_t nb_clk);
-
 /* Lock the watchdog and all related features.
  * Calls all the other watchdog_lock_* functions (clk_src, clk_src_power, timer_val, enable).
  */
 void watchdog_lock_full(void);
 
+
+/***************************************************************************** */
 /*
  * Configure the watchdog.
- * clk_sel is either 0 (IRC) or 1 (WDTCLK). The corresponding clock source will be powered on.
- * Note : only WDTCLK is running in deep power down mode
- * Note : protecting the clock source power will prevent turning off the IRC for power saving
- *   if it is selected as main clock source.
+ * The clock to the Watchdog timer is set to 1MHz
  */
 void watchdog_config(const struct wdt_config* wd_conf);
 
-
 /*
  * Stop the watchdog
  * This function can be used during system operation to stop the watchdog if it has not
@@ -95,7 +115,6 @@ void watchdog_config(const struct wdt_config* wd_conf);
  */
 int stop_watchdog(void);
 
-
 /*
  * Disable the watchdog
  * This function can be used upon system startup to disable watchdog operation