*/
#include "core/system.h"
+#include "core/pio.h"
+#include "drivers/gpio.h"
/* Private defines */
sys_config->sys_AHB_clk_ctrl = LPC_SYS_ABH_CLK_CTRL_MEM_ALL;
}
-/* Enter deep sleep.
- * NOTE : entering deep sleep implies a lot of side effects. I'll try to list them all here
+/* Enter sleep.
+ * NOTE : entering sleep implies some side effects. I'll try to list them all here
* so this can be done right.
*
- * Note : see remark about RTC and deep sleep in section 5.3.3 of UM10441
+ * Note : sleep mode is described in section 4.7.2 of UM10441
*/
-void enter_deep_sleep(void)
+void enter_sleep(void)
+{
+ struct lpc_pm_unit* pmu = LPC_PMU;
+ struct syst_ctrl_block_regs* scb = LPC_SCB;
+
+ /* Set sleep config */
+ pmu->power_ctrl &= ~(LPC_DPD_EN);
+ scb->scr &= ~(SCB_SCR_SLEEPDEEP);
+ /* Enter sleep */
+ wfi();
+}
+
+/* Enter deep sleep.
+ * IMPORTANT : Entering deep sleep implies a lot of side effects. I'll try to list them all
+ * here so this can be done right.
+ *
+ * If pc_wake_up (peripheral controlled wake up) is 1, the watchdog clock will be enabled
+ * and set to the lowest possible rate.
+ * If wdt_off is 1 and both pc_wake_up is 0 and watchdog clock is not locked, then the
+ * watchdog clock will be turned off and wakeup will only be possible using external
+ * interrupt sources or RTC wakeup
+ *
+ * The programmer has to handle all special cases such as locked watchdog clock source
+ * which would prevent deep sleep entry.
+ * Refer to chapter 4.8 of UM10441 for Deep-sleep mode details
+ * Refer to chapter 4.7.3 of UM10441 for indications about how to enter Deep-sleep mode.
+ *
+ * If external pin wakeup is used the programmer must provide a "WAKEUP_Handler".
+ * This handler may either be empty, or hold the waking related code.
+ *
+ * Before calling this function, the programmer must :
+ * stop the watchdog if it was configured (unless ths watchdog clock was locked)
+ * flush communication buffers
+ * configure used (and maybe unused) GPIO to output low in order to reduce power used
+ * during deep sleep
+ * turn off the systick_timer
+ *
+ * The code executed after the call to enter_deep_sleep() will run on the internal RC clock.
+ * The programmer has to restore the running state (system clock, PIO/GPIO configuration,
+ * systick timer, ...) according to his needs.
+ * Note that comparator input pins (port 0 pin 19 to 26) will have been turned to output low
+ * by the enter_deep_sleep() function.
+ * The easiest way to do so is to call NVIC_SystemReset() within the WAKEUP_Handler()
+ * callback, which will reset the micro-controller.
+ * This function will have to be completed to restore most of the system state, disable
+ * start logic interrupts, restore previous main clock, and so on.
+ *
+ * Note : See remark about RTC and deep sleep in section 5.3.3 of UM10441
+ */
+#include "drivers/serial.h"
+void enter_deep_sleep(int wdt_off, int pc_wake_up, int rtc_off, const struct wakeup_pin* wakers)
{
struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
+ struct lpc_pm_unit* pmu = LPC_PMU;
+ struct syst_ctrl_block_regs* scb = LPC_SCB;
+ struct lpc_watchdog* wdt = LPC_WDT;
+ struct lpc_sys_start_logic_ctrl* stlog = sys_config->start_log_ctrl;
+ uint32_t tmp = 0, old_sys_clk = 0;
+ int i = 0;
+
+ /* Avoid any interference */
+ lpc_disable_irq();
+
+ /* Do not try to turn off watchdog clock if it is locked */
+ if (wdt->mode & LPC_WDT_CLK_POWER_LOCK) {
+ wdt_off = 0;
+ }
+
+ /* Disable deep power down */
+ pmu->power_ctrl &= ~(LPC_DPD_EN);
+
+ /* Set deep-sleep config */
+ if (wdt_off == 1) {
+ if (lpc_private.brown_out_detection_enabled) {
+ sys_config->powerdown_sleep_cfg = LPC_DEEP_SLEEP_CFG_WD_OFF_BOD_ON;
+ } else {
+ sys_config->powerdown_sleep_cfg = LPC_DEEP_SLEEP_CFG_WD_OFF_BOD_OFF;
+ }
+ } else {
+ if (lpc_private.brown_out_detection_enabled) {
+ sys_config->powerdown_sleep_cfg = LPC_DEEP_SLEEP_CFG_WD_ON_BOD_ON;
+ } else {
+ sys_config->powerdown_sleep_cfg = LPC_DEEP_SLEEP_CFG_WD_ON_BOD_OFF;
+ }
+ }
/* Ask for the same clock status when waking up */
sys_config->powerdown_wake_cfg = sys_config->powerdown_run_cfg;
- /* Set deep_sleep config */
- if (lpc_private.brown_out_detection_enabled) {
- sys_config->powerdown_sleep_cfg = LPC_DEEP_SLEEP_CFG_NOWDTLOCK_BOD_ON;
+
+ /* Change current clock source */
+ if (pc_wake_up == 1) {
+ /* Switch to low-speed WDO (watchdog oscilator) */
+ sys_config->powerdown_run_cfg &= ~(LPC_POWER_DOWN_WDT_OSC);
+ sys_config->main_clk_sel = LPC_MAIN_CLK_SRC_WATCHDOG_OSC;
} else {
- sys_config->powerdown_sleep_cfg = LPC_DEEP_SLEEP_CFG_NOWDTLOCK_BOD_OFF;
+ sys_config->main_clk_sel = LPC_MAIN_CLK_SRC_IRC_OSC;
+ }
+ sys_config->main_clk_upd_en = 0;
+ sys_config->main_clk_upd_en = 1;
+ while (!(sys_config->main_clk_upd_en & 0x01));
+
+ /* Configure start logic for GPIO */
+ if (wakers != NULL) {
+ uint32_t ext_pin_bits = 0;
+ uint32_t ext_edges_rising = 0;
+ i = 0;
+ while (wakers[i].pin <= 11) {
+ ext_pin_bits |= LPC_STL(wakers[i].pin);
+ if (wakers[i].edge == EDGE_RISING) { /* Defined in drivers/gpio.h */
+ ext_edges_rising |= LPC_STL(wakers[i].pin);
+ }
+ i++;
+ }
+ stlog[0].reset = ext_pin_bits;
+ stlog[0].edge_ctrl = ext_edges_rising;
+ stlog[0].signal_en = ext_pin_bits;
+ }
+ /* Enable the start logic interrupts in NVIC */
+ if (wakers != NULL) {
+ i = 0;
+ while (wakers[i].pin <= 11) {
+ NVIC_EnableIRQ(wakers[i].pin);
+ i++;
+ }
}
+
+ /* FIXME : Configure start logic for RTC, Watchdog and BOD */
+
+
+ /* Turn all comparator inputs to GPIO, set them to output low */
+ {
+ const struct pio tmp_pins[] = {
+ LPC_GPIO_0_19, LPC_GPIO_0_20, LPC_GPIO_0_21, LPC_GPIO_0_22,
+ LPC_GPIO_0_23, LPC_GPIO_0_24, LPC_GPIO_0_25, LPC_GPIO_0_26,
+ };
+ for (i = 0; i < 8; i++) {
+ config_gpio(&(tmp_pins[i]), LPC_IO_MODE_INACTIVE, GPIO_DIR_OUT, 0);
+ }
+ }
+
+ /* Configure sys_AHB_clk_ctrl to leave only Watchdog clock running */
+ tmp = LPC_SYS_ABH_CLK_CTRL_MEM_ALL;
+ if (wdt_off == 0) {
+ tmp |= LPC_SYS_ABH_CLK_CTRL_Watchdog;
+ }
+ if (rtc_off == 0) {
+ tmp |= LPC_SYS_ABH_CLK_CTRL_RTC;
+ }
+ old_sys_clk = sys_config->sys_AHB_clk_ctrl;
+ sys_config->sys_AHB_clk_ctrl = tmp;
+
/* Enter deep sleep */
+ scb->scr |= SCB_SCR_SLEEPDEEP;
+ /* Turn interrupts on */
+ lpc_enable_irq();
+ wfi();
+
+ /* Avoid any interference ... again */
+ lpc_disable_irq();
+ /* Restore running state */
+ sys_config->sys_AHB_clk_ctrl = old_sys_clk;
/* FIXME */
+
+ /* Turn interrupts on once again */
+ lpc_enable_irq();
}
/* Enter deep power down.
/* And call all clock updaters */
propagate_main_clock();
- /* Turn interrupts on once again*/
+ /* Turn interrupts on once again */
lpc_enable_irq();
}
#include "core/lpc_regs.h"
#include "core/lpc_core.h"
#include "core/watchdog.h"
+#include "core/pio.h"
/***************************************************************************** */
/***************************************************************************** */
/* Power */
/***************************************************************************** */
-/* Enter deep sleep.
- * NOTE : entering deep sleep implies a lot of side effects. I'll try to list them all here
+/* Enter sleep.
+ * NOTE : entering sleep implies some side effects. I'll try to list them all here
* so this can be done right.
*
- * Note : see remark about RTC and deep sleep in section 5.3.3 of UM10441
+ * Note : sleep mode is described in section 4.7.2 of UM10441
*/
-void enter_deep_sleep(void);
+void enter_sleep(void);
+
+
+struct wakeup_pin {
+ uint8_t pin;
+ uint8_t edge;
+};
+#define ARRAY_LAST_WAKER {0xFF, 0xFF}
+
+/* Enter deep sleep.
+ * IMPORTANT : Entering deep sleep implies a lot of side effects. I'll try to list them all
+ * here so this can be done right.
+ *
+ * If pc_wake_up (peripheral controlled wake up) is 1, the watchdog clock will be enabled
+ * and set to the lowest possible rate.
+ * If wdt_off is 1 and both pc_wake_up is 0 and watchdog clock is not locked, then the
+ * watchdog clock will be turned off and wakeup will only be possible using external
+ * interrupt sources or RTC wakeup
+ *
+ * The programmer has to handle all special cases such as locked watchdog clock source
+ * which would prevent deep sleep entry.
+ * Refer to chapter 4.8 of UM10441 for Deep-sleep mode details
+ * Refer to chapter 4.7.3 of UM10441 for indications about how to enter Deep-sleep mode.
+ *
+ * If external pin wakeup is used the programmer must provide a "WAKEUP_Handler".
+ * This handler may either be empty, or hold the waking related code.
+ *
+ * Before calling this function, the programmer must :
+ * stop the watchdog if it was configured (unless ths watchdog clock was locked)
+ * flush communication buffers
+ * configure used (and maybe unused) GPIO to output low in order to reduce power used
+ * during deep sleep
+ * turn off the systick_timer
+ *
+ * The code executed after the call to enter_deep_sleep() will run on the internal RC clock.
+ * The programmer has to restore the running state (system clock, PIO/GPIO configuration,
+ * systick timer, ...) according to his needs.
+ * Note that comparator input pins (port 0 pin 19 to 26) will have been turned to output low
+ * by the enter_deep_sleep() function.
+ * The easiest way to do so is to call NVIC_SystemReset() within the WAKEUP_Handler()
+ * callback, which will reset the micro-controller.
+ * This function will have to be completed to restore most of the system state, disable
+ * start logic interrupts, restore previous main clock, and so on.
+ *
+ * Note : See remark about RTC and deep sleep in section 5.3.3 of UM10441
+ */
+void enter_deep_sleep(int wdt_off, int pc_wake_up, int rtc_off, const struct wakeup_pin* wakers);
/* Enter deep power down.
* NOTE : entering deep power down implies a lot of side effects. I'll try to list them all here
volatile uint32_t reset; /* 0x08 : reset Register 0 (-/W) */
volatile uint32_t status; /* 0x0C : status Register 0 (R/-) */
};
+#define LPC_STLOGIC_EDGE_FALLING(x) 0x00
+#define LPC_STLOGIC_EDGE_RISING(x) (0x01 << (x))
+#define LPC_STL(x) (0x01 << (x))
+
+
struct lpc_sys_config
{
volatile uint32_t sys_mem_remap; /* 0x000 System memory remap (R/W) */
#define LPC_POWER_DOWN_SYSPLL (1 << 7)
#define LPC_POWER_DOWN_COPARATOR (1 << 15)
-#define LPC_DEEP_SLEEP_CFG_NOWDTLOCK_BOD_ON 0x0000FFF7
-#define LPC_DEEP_SLEEP_CFG_NOWDTLOCK_BOD_OFF 0x0000FFFF
+#define LPC_DEEP_SLEEP_CFG_WD_OFF_BOD_ON 0x0000FFF7
+#define LPC_DEEP_SLEEP_CFG_WD_OFF_BOD_OFF 0x0000FFFF
+#define LPC_DEEP_SLEEP_CFG_WD_ON_BOD_ON 0x0000FFB7
+#define LPC_DEEP_SLEEP_CFG_WD_ON_BOD_OFF 0x0000FFBF
#define LPC_MAIN_CLK_SRC_IRC_OSC 0x00
#define LPC_MAIN_CLK_SRC_PLL_IN 0x01