*
*************************************************************************** */
+/* PMU unit */
#include "lib/stdint.h"
#include "core/lpc_regs.h"
#include "core/lpc_core.h"
#include "core/system.h"
+#include "core/watchdog.h"
#include "core/pmu.h"
#include "lib/errno.h"
-
-/* PMU unit */
+/* Read (read_gp_retain()) or Write (save_gp_retain()) to the general purpose
+ * retain registers.
+ * There are four 32 bits general purpose retain registers on the LPC82x uC.
+ * Data written to these registers is retained even across uC resets or
+ * reprogrammations so long as the uC remains powered.
+ * These functions read (or write) "nb" contiguous "values" from (or to) "offset"
+ * general purpose registers.
+ * - "values" is a user provided buffer for the data read from (or to be written to)
+ * the general purpose registers
+ * - "nb" is the buffer size.
+ * - "offset" is the first (starting at 0) general purpose register to be read
+ * from (or written to)
+ * "offset + nb" must be <= 4
+ * The data is always placed or found in the first word of "values".
+ */
int save_gp_retain(uint32_t* values, uint8_t nb, uint8_t offset)
{
struct lpc_pm_unit* pmu = LPC_PMU;
* - do not enter "deep" sleep or power-down mode
* - if "wake_sources_is_all_intrs" is set to 1, also wake up from unconfigured
* interrupt sources
+ * This function does not turn off systick if it is activated, so the system will
+ * wake up on next tick.
* This function returns after the next interrupt or event.
*/
void enter_sleep(int wake_sources_is_all_intrs)
{
struct syst_ctrl_block_regs* scb = LPC_SCB;
+ struct lpc_pm_unit* pmu = LPC_PMU;
/* Configure SCR for sleep mode */
if (wake_sources_is_all_intrs == 1) {
} else {
scb->scr = 0;
}
- wfe();
+ pmu->power_ctrl = LPC_PM_DEFAULT;
+ wfi();
}
+/* Enter permanent sleep state
+ * - Return to sleep mode after interrupt or event
+ * - do not enter "deep" sleep or power-down mode
+ * - if "wake_sources_is_all_intrs" is set to 1, also wake up from unconfigured
+ * interrupt sources
+ * This function does not turn off systick if it is activated.
+ * This function does not returns after the next interrupt or event, unless a call to
+ * exit_sleep() happened in an interrupt handler.
+ */
+void enter_continuous_sleep(int wake_sources_is_all_intrs)
+{
+ struct syst_ctrl_block_regs* scb = LPC_SCB;
+ struct lpc_pm_unit* pmu = LPC_PMU;
+
+ /* Configure SCR for continuous sleep mode */
+ if (wake_sources_is_all_intrs == 1) {
+ /* Wakeup from any interrupt, even those not enabled */
+ scb->scr = SCB_SCR_SEVONPEND | SCB_SCR_SLEEPONEXIT;
+ } else {
+ scb->scr = SCB_SCR_SLEEPONEXIT;
+ }
+ pmu->power_ctrl = LPC_PM_DEFAULT;
+ wfi();
+}
+
+/* Exit permanent sleep state
+ * Can be called from an interrupt handler to get back to thread mode at the end of
+ * the handler execution.
+ */
+void exit_sleep(void)
+{
+ struct syst_ctrl_block_regs* scb = LPC_SCB;
+ scb->scr &= ~SCB_SCR_SLEEPONEXIT;
+}
/* Enter deep sleep.
* NOTE : entering deep sleep implies a lot of side effects. I'll try to list them all here
* so this can be done right.
- *
+ * - The PLL is stopped, system must switch back to IRC before entering deep sleep
+ * - The IRC is running, but it's output is disabled.
+ * - Memory retains it's state
+ * - Flash stays ON
+ * - The peripherals will be stopped
+ * - Systick will be stopped
+ * - If using the Self Wake-Up timer as wake-up source then it must be turned ON before
+ * calling the function, and it MUST use either the 10KHZ clock or the external pin clock.
*/
-void enter_deep_sleep(void)
+void enter_deep_sleep(uint32_t wakeup_freq_sel, uint32_t self_wakeup)
{
struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
+ struct syst_ctrl_block_regs* scb = LPC_SCB;
+ struct lpc_pm_unit* pmu = LPC_PMU;
+ uint32_t tmp = ~(0);
/* Ask for the same clock status when waking up */
sys_config->powerdown_wake_cfg = sys_config->powerdown_run_cfg;
- /* Set deep_sleep config */
+
+ /* Set deep_sleep config (Watchdog and BrownOut detection running ?) */
if (system_brown_out_detection_enabled()) {
- sys_config->powerdown_sleep_cfg = LPC_DEEP_SLEEP_CFG_NOWDTLOCK_BOD_ON;
- } else {
- sys_config->powerdown_sleep_cfg = LPC_DEEP_SLEEP_CFG_NOWDTLOCK_BOD_OFF;
+ tmp &= ~(LPC_POWER_DOWN_BOD);
+ }
+ if (watchdog_get_timer_val()) {
+ tmp &= ~(LPC_POWER_DOWN_WDT_OSC);
}
+ sys_config->powerdown_sleep_cfg = tmp;
+
+ /* Use Self Wake-Up timer to wake-up ? */
+ if (self_wakeup != 0) {
+ self_wakeup_timer_start(self_wakeup);
+ }
+
+ /* Turn off PLL and move to IRC */
+ clock_config(FREQ_SEL_IRC);
+
/* Enter deep sleep */
- /* FIXME */
+ pmu->power_ctrl = LPC_PM_DEEP_SLEEP;
+ scb->scr = SCB_SCR_SLEEPDEEP;
+
+ wfi();
+
+ pmu->power_ctrl = LPC_PM_DEFAULT;
+ scb->scr = 0;
+
+ /* Go back to PLL */
+ clock_config(wakeup_freq_sel);
+}
+
+
+
+/* Prevent deep power down entry */
+void prevent_deep_power_down(void)
+{
+ struct lpc_pm_unit* pmu = LPC_PMU;
+ pmu->power_ctrl |= LPC_PM_NO_DPD;
}
* - do not enter "deep" sleep or power-down mode
* - if "wake_sources_is_all_intrs" is set to 1, also wake up from unconfigured
* interrupt sources
+ * This function does not turn off systick if it is activated, so the system will
+ * wake up on next tick.
* This function returns after the next interrupt or event.
*/
void enter_sleep(int wake_sources_is_all_intrs);
+/* Enter permanent sleep state
+ * - Return to sleep mode after interrupt or event
+ * - do not enter "deep" sleep or power-down mode
+ * - if "wake_sources_is_all_intrs" is set to 1, also wake up from unconfigured
+ * interrupt sources
+ * This function does not turn off systick if it is activated.
+ * This function does not returns after the next interrupt or event, unless a call to
+ * exit_sleep() happened in an interrupt handler.
+ */
+void enter_continuous_sleep(int wake_sources_is_all_intrs);
+
+/* Exit permanent sleep state
+ * Can be called from an interrupt handler to get back to thread mode at the end of
+ * the handler execution.
+ */
+void exit_sleep(void);
/* Enter deep sleep.
* NOTE : entering deep sleep implies a lot of 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
+ * so this can be done right.
+ * - The PLL is stopped, system must switch back to IRC before entering deep sleep
+ * - The IRC is running, but it's output is disabled.
+ * - Memory retains it's state
+ * - Flash stays ON
+ * - The peripherals will be stopped
+ * - Systick will be stopped
+ * - If using the Self Wake-Up timer as wake-up source then it must be turned ON before
+ * calling the function, and it MUST use either the 10KHZ clock or the external pin clock.
*/
-void enter_deep_sleep(void);
+void enter_deep_sleep(uint32_t wakeup_freq_sel, uint32_t self_wakeup);
+
+
+/* Prevent deep power down entry */
+void prevent_deep_power_down(void);
/* Read from or write to the general purpose retain registers
* Values in these registers are kept over resets, whatever the reset source, so long as
* the part remains powered. (Even accross watchdog resets or flashing the device !)
+ *
+ * Read (read_gp_retain()) or Write (save_gp_retain()) to the general purpose
+ * retain registers.
+ * There are four 32 bits general purpose retain registers on the LPC82x uC.
+ * Data written to these registers is retained even across uC resets or
+ * reprogrammations so long as the uC remains powered.
+ * These functions read (or write) "nb" contiguous "values" from (or to) general purpose
+ * registers, starting from "offset" general purpose register.
+ * - "values" is a user provided buffer for the data read from (or to be written to)
+ * the general purpose registers
+ * - "nb" is the buffer size.
+ * - "offset" is the first (starting at 0) general purpose register to be read
+ * from (or written to)
+ * "offset + nb" must be <= 4
*/
int save_gp_retain(uint32_t* values, uint8_t nb, uint8_t offset);
int read_gp_retain(uint32_t* values, uint8_t nb, uint8_t offset);