mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-25 07:58:12 +00:00
feat(core): manage background ops during suspend
[no changelog]
This commit is contained in:
parent
995caca9c7
commit
bec455c9e5
@ -59,6 +59,19 @@ bool pmic_init(void);
|
||||
// Deinitializes PMIC driver
|
||||
void pmic_deinit(void);
|
||||
|
||||
// Suspends driver activity so the CPU can enter low-power mode.
|
||||
//
|
||||
// Suspending may take some time if the driver is currently
|
||||
// performing an operation. Caller may check the status by
|
||||
// pmic_is_suspended().
|
||||
bool pmic_suspend(void);
|
||||
|
||||
// Resumes the driver operation after it has been suspended.
|
||||
bool pmic_resume(void);
|
||||
|
||||
// Checks whether the driver is suspended.
|
||||
bool pmic_is_suspended(void);
|
||||
|
||||
// Gets the cause of the last restart
|
||||
uint8_t pmic_restart_cause(void);
|
||||
|
||||
|
@ -107,6 +107,15 @@ typedef struct {
|
||||
// Current state of the FSM
|
||||
npm1300_fsm_state_t state;
|
||||
|
||||
// Set if the driver was requested to suspend background operations.
|
||||
// IF so, the driver waits until the last operation is finished,
|
||||
// then enters suspended mode.
|
||||
bool suspending;
|
||||
|
||||
// Set if the driver's background operations are suspended.
|
||||
// In suspended mode, the driver does not start any new operations.
|
||||
bool suspended;
|
||||
|
||||
// ADC register (global buffer used for ADC measurements)
|
||||
npm1300_adc_regs_t adc_regs;
|
||||
// Charging limit registers (global buffer used for charging limit)
|
||||
@ -418,6 +427,53 @@ void pmic_deinit(void) {
|
||||
memset(drv, 0, sizeof(npm1300_driver_t));
|
||||
}
|
||||
|
||||
bool pmic_suspend(void) {
|
||||
npm1300_driver_t* drv = &g_npm1300_driver;
|
||||
|
||||
if (!drv->initialized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
irq_key_t irq_key = irq_lock();
|
||||
drv->suspending = true;
|
||||
npm1300_fsm_continue(drv);
|
||||
irq_unlock(irq_key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pmic_resume(void) {
|
||||
npm1300_driver_t* drv = &g_npm1300_driver;
|
||||
|
||||
if (!drv->initialized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
irq_key_t irq_key = irq_lock();
|
||||
drv->suspending = false;
|
||||
drv->suspended = false;
|
||||
npm1300_fsm_continue(drv);
|
||||
irq_unlock(irq_key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pmic_is_suspended(void) {
|
||||
npm1300_driver_t* drv = &g_npm1300_driver;
|
||||
|
||||
if (!drv->initialized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_suspended;
|
||||
|
||||
irq_key_t irq_key = irq_lock();
|
||||
is_suspended = drv->suspended;
|
||||
irq_unlock(irq_key);
|
||||
|
||||
return is_suspended;
|
||||
}
|
||||
|
||||
bool pmic_enter_shipmode(void) {
|
||||
npm1300_driver_t* drv = &g_npm1300_driver;
|
||||
|
||||
@ -830,6 +886,9 @@ static void npm1300_i2c_callback(void* context, i2c_packet_t* packet) {
|
||||
case NPM1300_STATE_CLEAR_EVENTS:
|
||||
drv->clear_events_requested = false;
|
||||
drv->state = NPM1300_STATE_IDLE;
|
||||
#ifdef USE_SUSPEND
|
||||
wakeup_flags_set(WAKEUP_FLAG_POWER);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case NPM1300_STATE_CHARGING_ENABLE:
|
||||
@ -903,10 +962,6 @@ void NPM1300_EXTI_INTERRUPT_HANDLER(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef USE_SUSPEND
|
||||
wakeup_flags_set(WAKEUP_FLAG_POWER);
|
||||
#endif
|
||||
|
||||
drv->clear_events_requested = true;
|
||||
npm1300_fsm_continue(drv);
|
||||
}
|
||||
@ -915,7 +970,7 @@ void NPM1300_EXTI_INTERRUPT_HANDLER(void) {
|
||||
//
|
||||
// This function is called in the irq context or when interrupts are disabled.
|
||||
static void npm1300_fsm_continue(npm1300_driver_t* drv) {
|
||||
if (drv->state != NPM1300_STATE_IDLE) {
|
||||
if (drv->state != NPM1300_STATE_IDLE || drv->suspended) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -971,6 +1026,16 @@ static void npm1300_fsm_continue(npm1300_driver_t* drv) {
|
||||
drv->shipmode_requested = false;
|
||||
drv->state = NPM1300_STATE_ENTER_SHIPMODE;
|
||||
}
|
||||
|
||||
// After processing all requests, check if we need to
|
||||
// suspend the driver
|
||||
if (drv->state == NPM1300_STATE_IDLE) {
|
||||
// No more requests to process
|
||||
if (drv->suspending) {
|
||||
drv->suspending = false;
|
||||
drv->suspended = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // KERNEL_MODE
|
||||
|
@ -215,15 +215,19 @@ pm_status_t pm_get_state(pm_state_t* state) {
|
||||
// - The callback can schedule the next wake-up by calling
|
||||
// rtc_wakeup_timer_start().
|
||||
// - If the callback return with wakeup_flags set, system_suspend() returns.
|
||||
#ifdef USE_RTC
|
||||
void pm_rtc_wakeup_callback(void* context) {
|
||||
// TODO: update fuel gauge state
|
||||
|
||||
// TODO: decide whether to reschedule the next wake-up or wake up the coreapp
|
||||
if (true) {
|
||||
rtc_wakeup_timer_start(0, pm_rtc_wakeup_callback, NULL);
|
||||
// Reschedule the next wake-up
|
||||
rtc_wakeup_timer_start(10, pm_rtc_wakeup_callback, NULL);
|
||||
} else {
|
||||
// Wake up the coreapp
|
||||
wakeup_flags_set(WAKEUP_FLAG_RTC);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
pm_status_t pm_suspend(wakeup_flags_t* wakeup_reason) {
|
||||
pm_driver_t* drv = &g_pm;
|
||||
@ -256,7 +260,9 @@ pm_status_t pm_suspend(wakeup_flags_t* wakeup_reason) {
|
||||
|
||||
wakeup_flags_t wakeup_flags = system_suspend();
|
||||
|
||||
#ifdef USE_RTC
|
||||
rtc_wakeup_timer_stop();
|
||||
#endif
|
||||
|
||||
// TODO: Handle wake-up flags
|
||||
// UNUSED(wakeup_flags);
|
||||
|
@ -156,6 +156,53 @@ cleanup:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool stwlc38_suspend(void) {
|
||||
stwlc38_driver_t *drv = &g_stwlc38_driver;
|
||||
|
||||
if (!drv->initialized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
irq_key_t irq_key = irq_lock();
|
||||
drv->suspending = true;
|
||||
stwlc38_fsm_continue(drv);
|
||||
irq_unlock(irq_key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool stwlc38_resume(void) {
|
||||
stwlc38_driver_t *drv = &g_stwlc38_driver;
|
||||
|
||||
if (!drv->initialized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
irq_key_t irq_key = irq_lock();
|
||||
drv->suspending = false;
|
||||
drv->suspended = false;
|
||||
stwlc38_fsm_continue(drv);
|
||||
irq_unlock(irq_key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool stwlc38_is_suspended(void) {
|
||||
stwlc38_driver_t *drv = &g_stwlc38_driver;
|
||||
|
||||
if (!drv->initialized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_suspended;
|
||||
|
||||
irq_key_t irq_key = irq_lock();
|
||||
is_suspended = drv->suspended;
|
||||
irq_unlock(irq_key);
|
||||
|
||||
return is_suspended;
|
||||
}
|
||||
|
||||
bool stwlc38_enable(bool enable) {
|
||||
stwlc38_driver_t *drv = &g_stwlc38_driver;
|
||||
|
||||
@ -329,8 +376,6 @@ void STWLC38_EXTI_INTERRUPT_HANDLER(void) {
|
||||
}
|
||||
|
||||
if (drv->state == STWLC38_STATE_POWER_DOWN) {
|
||||
// Inform the powerctl module about the WPC
|
||||
// wakeup_flags_set(WAKEUP_FLAGS_WPC);
|
||||
drv->report_readout_requested = true;
|
||||
stwlc38_fsm_continue(drv);
|
||||
}
|
||||
@ -339,30 +384,42 @@ void STWLC38_EXTI_INTERRUPT_HANDLER(void) {
|
||||
static void stwlc38_fsm_continue(stwlc38_driver_t *drv) {
|
||||
// The order of the following conditions defines the priority
|
||||
|
||||
if (drv->state == STWLC38_STATE_POWER_DOWN && drv->report_readout_requested) {
|
||||
// Check if the i2c interface is ready
|
||||
stwlc38_i2c_submit(drv, stwlc38_ops_report_readout);
|
||||
drv->state = STWLC38_STATE_REPORT_READOUT;
|
||||
if (drv->suspended) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (drv->state != STWLC38_STATE_IDLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (drv->vout_enabled != drv->vout_enabled_requested) {
|
||||
// Enable/Disable the main LDO output
|
||||
if (drv->vout_enabled_requested) {
|
||||
stwlc38_i2c_submit(drv, stwlc38_ops_vout_enable);
|
||||
drv->state = STWLC38_STATE_VOUT_ENABLE;
|
||||
} else {
|
||||
stwlc38_i2c_submit(drv, stwlc38_ops_vout_disable);
|
||||
drv->state = STWLC38_STATE_VOUT_DISABLE;
|
||||
if (drv->state == STWLC38_STATE_POWER_DOWN) {
|
||||
if (drv->report_readout_requested) {
|
||||
// Check if the i2c interface is ready
|
||||
stwlc38_i2c_submit(drv, stwlc38_ops_report_readout);
|
||||
drv->state = STWLC38_STATE_REPORT_READOUT;
|
||||
}
|
||||
} else if (drv->state == STWLC38_STATE_IDLE) {
|
||||
if (drv->vout_enabled != drv->vout_enabled_requested) {
|
||||
// Enable/Disable the main LDO output
|
||||
if (drv->vout_enabled_requested) {
|
||||
stwlc38_i2c_submit(drv, stwlc38_ops_vout_enable);
|
||||
drv->state = STWLC38_STATE_VOUT_ENABLE;
|
||||
} else {
|
||||
stwlc38_i2c_submit(drv, stwlc38_ops_vout_disable);
|
||||
drv->state = STWLC38_STATE_VOUT_DISABLE;
|
||||
}
|
||||
} else if (drv->report_readout_requested) {
|
||||
// Read status registers
|
||||
stwlc38_i2c_submit(drv, stwlc38_ops_report_readout);
|
||||
drv->state = STWLC38_STATE_REPORT_READOUT;
|
||||
}
|
||||
}
|
||||
|
||||
// After processing all requests, check if we need to
|
||||
// suspend the driver
|
||||
if (drv->state == STWLC38_STATE_IDLE ||
|
||||
drv->state == STWLC38_STATE_POWER_DOWN) {
|
||||
// No more requests to process
|
||||
if (drv->suspending) {
|
||||
drv->suspending = false;
|
||||
drv->suspended = true;
|
||||
}
|
||||
} else if (drv->report_readout_requested) {
|
||||
// Read status registers
|
||||
stwlc38_i2c_submit(drv, stwlc38_ops_report_readout);
|
||||
drv->state = STWLC38_STATE_REPORT_READOUT;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,6 +90,19 @@ bool stwlc38_init(void);
|
||||
// Deinitializes STWLC38 driver
|
||||
void stwlc38_deinit(void);
|
||||
|
||||
// Suspends driver activity so the CPU can enter low-power mode.
|
||||
//
|
||||
// Suspending may take some time if the driver is currently
|
||||
// performing an operation. Caller may check the status by
|
||||
// stwlc38_is_suspended().
|
||||
bool stwlc38_suspend(void);
|
||||
|
||||
// Resumes the driver operation after it has been suspended.
|
||||
bool stwlc38_resume(void);
|
||||
|
||||
// Checks whether the driver is suspended.
|
||||
bool stwlc38_is_suspended(void);
|
||||
|
||||
// Enables or disables the STWLC38. This can be used to enable/disable
|
||||
// wireless charging functionality.
|
||||
//
|
||||
|
@ -72,6 +72,15 @@ typedef struct {
|
||||
// Timer used for periodic report readout
|
||||
systimer_t *timer;
|
||||
|
||||
// Set if the driver was requested to suspend background operations.
|
||||
// IF so, the driver waits until the last operation is finished,
|
||||
// then enters suspended mode.
|
||||
bool suspending;
|
||||
|
||||
// Set if the driver's background operations are suspended.
|
||||
// In suspended mode, the driver does not start any new operations.
|
||||
bool suspended;
|
||||
|
||||
// Main LDO output current state
|
||||
bool vout_enabled;
|
||||
// Main LDO output requested state
|
||||
|
@ -25,6 +25,9 @@
|
||||
#include <sys/suspend.h>
|
||||
#include <sys/suspend_io.h>
|
||||
|
||||
#include <../../power_manager/stwlc38/stwlc38.h>
|
||||
#include <sys/pmic.h>
|
||||
|
||||
static wakeup_flags_t g_wakeup_flags = 0;
|
||||
|
||||
static void background_tasks_suspend(void);
|
||||
@ -106,10 +109,18 @@ wakeup_flags_t system_suspend(void) {
|
||||
return wakeup_flags;
|
||||
}
|
||||
|
||||
static void background_tasks_suspend(void) {}
|
||||
static void background_tasks_suspend(void) {
|
||||
pmic_suspend();
|
||||
stwlc38_suspend();
|
||||
}
|
||||
|
||||
static bool background_tasks_suspended(void) { return true; }
|
||||
static bool background_tasks_suspended(void) {
|
||||
return pmic_is_suspended() && stwlc38_is_suspended();
|
||||
}
|
||||
|
||||
static void background_tasks_resume(void) {}
|
||||
static void background_tasks_resume(void) {
|
||||
stwlc38_resume();
|
||||
pmic_resume();
|
||||
}
|
||||
|
||||
#endif // defined(KERNEL_MODE) && !defined(SECMON)
|
||||
|
Loading…
Reference in New Issue
Block a user