micropython/ports/renesas-ra/powerctrl.c

307 lines
11 KiB
C

/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2013-2018 Damien P. George
* Copyright (c) 2022 Renesas Electronics Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/runtime.h"
#include "py/mperrno.h"
#include "py/mphal.h"
#include "hal_data.h"
#include "rtc.h"
#include "powerctrl.h"
#if 0
// Sleep Mode
// At power on, by default sleep is set as the low-power mode. Sleep
// mode is the most convenient low-power mode available, as it does
// not require any special configuration (other than configuring and
// enabling a suitable interrupt or event to wake the MCU from sleep)
// to return to normal program-execution mode.
// The states of the SRAM, the processor registers, and the hardware
// peripherals are all maintained in sleep mode, and the time needed
// to enter and wake from sleep is minimal. Any interrupt causes the
// MCU device to wake from sleep mode, including the Systick interrupt
// used by the RTOS scheduler.
// Software Standby Mode
// In software-standby mode, the CPU, as well as most of the on-chip
// peripheral functions and all of the internal oscillators, are
// stopped.
// The contents of the CPU internal registers and SRAM data, the states
// of on-chip peripheral functions, and I/O Ports are all retained.
// Software-standby mode allows significant reduction in power
// consumption, because most of the oscillators are stopped in this mode.
// Like sleep mode, standby mode requires an interrupt or event be
// configured and enabled to wake up.
// Snooze Mode
// Snooze mode can be used with some MCU peripherals to execute basic
// tasks while keeping the MCU in a low-power state.
// Many core peripherals and all clocks can be selected to run during
// Snooze, allowing for more flexible low-power configuration than
// Software Standby mode. To enable Snooze, select "Software Standby
// mode with Snooze mode enabled" for the "Low Power Mode" configuration
// option.
// Snooze mode settings (including entry/exit sources) are available
// under "Standby Options".
// Deep Software Standby Mode
// Deep Software Standby Mode is only available on some MCU devices.
// The MCU always wakes from Deep Software Standby Mode by going
// through reset, either by the negation of the reset pin or by one of
// the wakeup sources configurable in the "Deep Standby Options"
// configuration group.
#endif
#if defined(USE_FSP_LPM)
// LPM_MODE_SLEEP: Sleep mode
// LPM_MODE_STANDBY: Software Standby mode
// LPM_MODE_STANDBY_SNOOZE: Software Standby mode with Snooze mode enabled
// LPM_MODE_DEEP: Deep Software Standby mode
lpm_instance_ctrl_t g_lpm_sleep_ctrl;
const lpm_cfg_t g_lpm_sleep_cfg = {
.low_power_mode = LPM_MODE_SLEEP,
.snooze_cancel_sources = LPM_SNOOZE_CANCEL_SOURCE_NONE,
.standby_wake_sources = (lpm_standby_wake_source_t)0,
.snooze_request_source = LPM_SNOOZE_REQUEST_RTC_PERIOD,
.snooze_end_sources = (lpm_snooze_end_t)0,
.dtc_state_in_snooze = LPM_SNOOZE_DTC_DISABLE,
#if BSP_FEATURE_LPM_HAS_SBYCR_OPE
.output_port_enable = 0,
#endif
#if BSP_FEATURE_LPM_HAS_DEEP_STANDBY
.io_port_state = 0,
.power_supply_state = 0,
.deep_standby_cancel_source = (lpm_deep_standby_cancel_source_t)0,
.deep_standby_cancel_edge = (lpm_deep_standby_cancel_edge_t)0,
#endif
.p_extend = NULL,
};
const lpm_instance_t g_lpm_sleep = {
.p_api = &g_lpm_on_lpm,
.p_ctrl = &g_lpm_sleep_ctrl,
.p_cfg = &g_lpm_sleep_cfg
};
lpm_instance_ctrl_t g_lpm_deep_ctrl;
const lpm_cfg_t g_lpm_deep_cfg = {
.low_power_mode = LPM_MODE_DEEP,
.snooze_cancel_sources =
LPM_SNOOZE_CANCEL_SOURCE_NONE,
.standby_wake_sources =
(lpm_standby_wake_source_t)0,
.snooze_request_source =
LPM_SNOOZE_REQUEST_RXD0_FALLING,
.snooze_end_sources =
(lpm_snooze_end_t)0,
.dtc_state_in_snooze =
LPM_SNOOZE_DTC_DISABLE,
#if BSP_FEATURE_LPM_HAS_SBYCR_OPE
.output_port_enable = LPM_OUTPUT_PORT_ENABLE_RETAIN,
#endif
#if BSP_FEATURE_LPM_HAS_DEEP_STANDBY
.io_port_state = LPM_IO_PORT_NO_CHANGE,
.power_supply_state = LPM_POWER_SUPPLY_DEEPCUT0,
.deep_standby_cancel_source = LPM_DEEP_STANDBY_CANCEL_SOURCE_RTC_INTERVAL | LPM_DEEP_STANDBY_CANCEL_SOURCE_RTC_ALARM | (lpm_deep_standby_cancel_source_t)0,
.deep_standby_cancel_edge = (lpm_deep_standby_cancel_edge_t)0,
#endif
.p_extend = NULL,
};
const lpm_instance_t g_lpm_deep = {
.p_api = &g_lpm_on_lpm,
.p_ctrl = &g_lpm_deep_ctrl,
.p_cfg = &g_lpm_deep_cfg
};
lpm_instance_ctrl_t g_lpm_standby_ctrl;
const lpm_cfg_t g_lpm_standby_cfg = {
.low_power_mode = LPM_MODE_STANDBY,
.snooze_cancel_sources =
LPM_SNOOZE_CANCEL_SOURCE_NONE,
.standby_wake_sources =
LPM_STANDBY_WAKE_SOURCE_RTCALM | LPM_STANDBY_WAKE_SOURCE_RTCPRD | (lpm_standby_wake_source_t)0,
.snooze_request_source = LPM_SNOOZE_REQUEST_RXD0_FALLING,
.snooze_end_sources = (lpm_snooze_end_t)0,
.dtc_state_in_snooze = LPM_SNOOZE_DTC_DISABLE,
#if BSP_FEATURE_LPM_HAS_SBYCR_OPE
.output_port_enable = LPM_OUTPUT_PORT_ENABLE_RETAIN,
#endif
#if BSP_FEATURE_LPM_HAS_DEEP_STANDBY
.io_port_state = LPM_IO_PORT_NO_CHANGE,
.power_supply_state = LPM_POWER_SUPPLY_DEEPCUT0,
.deep_standby_cancel_source = LPM_DEEP_STANDBY_CANCEL_SOURCE_RTC_INTERVAL | (lpm_deep_standby_cancel_source_t)0,
.deep_standby_cancel_edge = (lpm_deep_standby_cancel_edge_t)0,
#endif
.p_extend = NULL,
};
const lpm_instance_t g_lpm_standby = {
.p_api = &g_lpm_on_lpm,
.p_ctrl = &g_lpm_standby_ctrl,
.p_cfg = &g_lpm_standby_cfg
};
#endif
NORETURN void powerctrl_mcu_reset(void) {
#if BSP_TZ_SECURE_BUILD
R_BSP_NonSecureEnter();
#else
NVIC_SystemReset();
#endif
while (1) {
;
}
}
NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) {
while (1) {
;
}
}
// static __attribute__((naked)) void branch_to_bootloader(uint32_t r0, uint32_t
// bl_addr) {
// }
void powerctrl_check_enter_bootloader(void) {
}
void powerctrl_enter_sleep_mode(void) {
// start trandition to RA MCU sleep mode
#if defined(USE_FSP_LPM)
fsp_err_t err;
err = R_LPM_Open(&g_lpm_sleep_ctrl, &g_lpm_sleep_cfg);
if (err != FSP_SUCCESS) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Can't enter sleep mode"));
}
err = R_LPM_LowPowerModeEnter(&g_lpm_sleep_ctrl);
if (err != FSP_SUCCESS) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Can't enter sleep mode"));
}
#endif
}
void powerctrl_enter_stop_mode(void) {
// Disable IRQs so that the IRQ that wakes the device from stop mode is not
// executed until after the clocks are reconfigured
uint32_t irq_state = disable_irq();
#if defined(MICROPY_BOARD_ENTER_STOP)
MICROPY_BOARD_ENTER_STOP
#endif
// start trandition to RA MCU sleep mode
#if defined(USE_FSP_LPM)
fsp_err_t err;
err = R_LPM_Open(&g_lpm_standby_ctrl, &g_lpm_standby_cfg);
if (err != FSP_SUCCESS) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Can't enter stop mode"));
}
err = R_LPM_LowPowerModeEnter(&g_lpm_standby_ctrl);
if (err != FSP_SUCCESS) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Can't enter stop mode"));
}
#endif
// reconfigure the system clock after waking up
#if defined(MICROPY_BOARD_LEAVE_STOP)
MICROPY_BOARD_LEAVE_STOP
#endif
// Enable IRQs now that all clocks are reconfigured
enable_irq(irq_state);
}
NORETURN void powerctrl_enter_standby_mode(void) {
rtc_init_finalise();
#if defined(MICROPY_BOARD_ENTER_STANDBY)
MICROPY_BOARD_ENTER_STANDBY
#endif
// start trandition to RA MCU deep software standby mode via software standby mode
#if defined(USE_FSP_LPM)
fsp_err_t err;
err = R_LPM_Open(&g_lpm_deep_ctrl, &g_lpm_deep_cfg);
/* Handle any errors. This function should be defined by the user. */
if (err != FSP_SUCCESS) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Can't enter stop mode"));
}
/* Check the Deep Software Standby Reset Flag. */
if (1U == R_SYSTEM->RSTSR0_b.DPSRSTF) {
/* Clear the IOKEEP bit to allow I/O Port use. */
err = R_LPM_IoKeepClear(&g_lpm_deep_ctrl);
if (err != FSP_SUCCESS) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Can't enter stop mode"));
}
}
/* Add user code here. */
/* Reconfigure the module to set the IOKEEP bit before entering deep software standby. */
err = R_LPM_LowPowerReconfigure(&g_lpm_deep_ctrl, &g_lpm_deep_cfg);
if (err != FSP_SUCCESS) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Can't enter stop mode"));
}
err = R_LPM_LowPowerModeEnter(&g_lpm_deep_ctrl);
/* Code after R_LPM_LowPowerModeEnter when using Deep Software Standby never be executed.
* Deep software standby exits by resetting the MCU. */
if (err != FSP_SUCCESS) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Can't enter stop mode"));
}
#endif
// We need to clear the PWR wake-up-flag before entering standby, since
// the flag may have been set by a previous wake-up event. Furthermore,
// we need to disable the wake-up sources while clearing this flag, so
// that if a source is active it does actually wake the device.
// See section 5.3.7 of RM0090.
// save RTC interrupts
// disable register write protection
// disable RTC interrupts
// clear RTC wake-up flags
// enable previously-enabled RTC interrupts
// enable register write protection
// enter standby mode
// we never return; MCU is reset on exit from standby
powerctrl_mcu_reset();
}