1
0
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:
cepetr 2025-06-20 14:07:04 +02:00 committed by cepetr
parent 995caca9c7
commit bec455c9e5
7 changed files with 206 additions and 32 deletions

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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.
//

View File

@ -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

View File

@ -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)