diff --git a/core/embed/sys/power_manager/inc/sys/power_manager.h b/core/embed/sys/power_manager/inc/sys/power_manager.h index 2083f68add..e4d2b39c5e 100644 --- a/core/embed/sys/power_manager/inc/sys/power_manager.h +++ b/core/embed/sys/power_manager/inc/sys/power_manager.h @@ -82,7 +82,7 @@ typedef struct { } pm_report_t; /* Public API functions */ -pm_status_t pm_init(bool turned_on); +pm_status_t pm_init(bool skip_boot_sequence); void pm_deinit(void); pm_status_t pm_get_events(pm_event_t* event_flags); pm_status_t pm_get_state(pm_state_t* state); diff --git a/core/embed/sys/power_manager/stm32u5/power_manager.c b/core/embed/sys/power_manager/stm32u5/power_manager.c index 86a037e30d..09d1dc349c 100644 --- a/core/embed/sys/power_manager/stm32u5/power_manager.c +++ b/core/embed/sys/power_manager/stm32u5/power_manager.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "../../powerctl/npm1300/npm1300.h" #include "../../powerctl/stwlc38/stwlc38.h" @@ -37,7 +38,7 @@ static void pm_shutdown_timer_handler(void* context); // API Implementation -pm_status_t pm_init(bool turned_on) { +pm_status_t pm_init(bool skip_bootup_sequence) { pm_driver_t* drv = &g_pm; if (drv->initialized) { @@ -58,10 +59,41 @@ pm_status_t pm_init(bool turned_on) { PM_FUEL_GAUGE_R_AGGRESSIVE, PM_FUEL_GAUGE_Q_AGGRESSIVE, PM_FUEL_GAUGE_P_INIT); - if (turned_on) { - drv->state = PM_STATE_POWER_SAVE; - } else { + + if(skip_bootup_sequence) { + + // Skip bootup sequence and try to recover the power manages state left + // by the bootloader in backup ram. + + backup_ram_power_manager_data_t pm_recovery_data; + backup_ram_status_t status + = backup_ram_read_power_manager_data(&pm_recovery_data); + + if (status != BACKUP_RAM_OK && + (pm_recovery_data.bootloader_exit_state != PM_STATE_POWER_SAVE && + pm_recovery_data.bootloader_exit_state != PM_STATE_ACTIVE)) { + + drv->state = PM_STATE_POWER_SAVE; + drv->fuel_gauge_request_new_guess = true; + + }else{ + + // Backup RAM contain valid data + drv->state = pm_recovery_data.bootloader_exit_state; + drv->fuel_gauge.soc = pm_recovery_data.soc; + drv->fuel_gauge_request_new_guess = false; + drv->fuel_gauge_initialized = true; + } + + drv->fuel_gauge_initialized = true; + + }else{ + + // Start in lowest state and wait for the bootup sequence to + // finish (call of pm_turn_on()) drv->state = PM_STATE_HIBERNATE; + drv->initialized = false; + } // Disable charging by default @@ -206,12 +238,22 @@ pm_status_t pm_turn_on(void) { irq_key_t irq_key = irq_lock(); - pm_battery_initial_soc_guess(); + // Try to recover SoC from the backup RAM + backup_ram_power_manager_data_t pm_recovery_data; + backup_ram_status_t status = + backup_ram_read_power_manager_data(&pm_recovery_data); + + if(status == BACKUP_RAM_OK && pm_recovery_data.soc != 0.0f) { + drv->fuel_gauge.soc = pm_recovery_data.soc; + } else { + pm_battery_initial_soc_guess(); + } // Set monitoiring timer with longer period systimer_set_periodic(drv->monitoring_timer, PM_TIMER_PERIOD_MS); drv->fuel_gauge_initialized = true; + irq_unlock(irq_key); return PM_OK; diff --git a/core/embed/sys/power_manager/stm32u5/power_manager_internal.h b/core/embed/sys/power_manager/stm32u5/power_manager_internal.h index a1736d4dbb..e7aade2b1e 100644 --- a/core/embed/sys/power_manager/stm32u5/power_manager_internal.h +++ b/core/embed/sys/power_manager/stm32u5/power_manager_internal.h @@ -84,6 +84,7 @@ typedef struct { // Fuel gauge fuel_gauge_state_t fuel_gauge; bool fuel_gauge_initialized; + bool fuel_gauge_request_new_guess; pm_sampling_data_t bat_sampling_buf[PM_BATTERY_SAMPLING_BUF_SIZE]; uint8_t bat_sampling_buf_tail_idx; uint8_t bat_sampling_buf_head_idx; @@ -134,6 +135,7 @@ void pm_pmic_data_ready(void* context, npm1300_report_t* report); void pm_charging_controller(pm_driver_t* drv); void pm_battery_sampling(float vbat, float ibat, float ntc_temp); void pm_battery_initial_soc_guess(void); +void pm_store_power_manager_data(pm_driver_t* drv); // State handlers pm_internal_state_t pm_handle_state_active(pm_driver_t* drv); diff --git a/core/embed/sys/power_manager/stm32u5/power_monitoring.c b/core/embed/sys/power_manager/stm32u5/power_monitoring.c index f715cb25f7..f74c77e60b 100644 --- a/core/embed/sys/power_manager/stm32u5/power_monitoring.c +++ b/core/embed/sys/power_manager/stm32u5/power_monitoring.c @@ -20,6 +20,7 @@ #include #include +#include #include "../../powerctl/fuel_gauge/fuel_gauge.h" #include "../../powerctl/npm1300/npm1300.h" @@ -36,26 +37,43 @@ void pm_monitor_power_sources(void) { // Update fuel gauge state if (drv->fuel_gauge_initialized) { - fuel_gauge_update(&drv->fuel_gauge, PM_BATTERY_SAMPLING_PERIOD_MS, - drv->pmic_data.vbat, drv->pmic_data.ibat, - drv->pmic_data.ntc_temp); - // Ceil the float soc to user friendly integer - uint8_t soc_ceiled_temp = (int)(drv->fuel_gauge.soc_latched * 100 + 0.999f); - if (soc_ceiled_temp != drv->soc_ceiled) { - drv->soc_ceiled = soc_ceiled_temp; - PM_SET_EVENT(drv->event_flags, PM_EVENT_SOC_UPDATED); + if(drv->fuel_gauge_request_new_guess){ + + // Request new single SoC guess based on the latest measurements + fuel_gauge_initial_guess(&drv->fuel_gauge, drv->pmic_data.vbat, + drv->pmic_data.ibat, drv->pmic_data.ntc_temp); + + drv->fuel_gauge_request_new_guess = false; + + }else{ + + fuel_gauge_update(&drv->fuel_gauge, PM_BATTERY_SAMPLING_PERIOD_MS, + drv->pmic_data.vbat, drv->pmic_data.ibat, + drv->pmic_data.ntc_temp); + + // Ceil the float soc to user friendly integer + uint8_t soc_ceiled_temp = (int)(drv->fuel_gauge.soc_latched * 100 + 0.999f); + if (soc_ceiled_temp != drv->soc_ceiled) { + drv->soc_ceiled = soc_ceiled_temp; + PM_SET_EVENT(drv->event_flags, PM_EVENT_SOC_UPDATED); + } + } } else { + pm_battery_sampling(drv->pmic_data.vbat, drv->pmic_data.ibat, drv->pmic_data.ntc_temp); // Battery sampling period, collect data before initial guess is made. fuel_gauge_initial_guess(&drv->fuel_gauge, drv->pmic_data.vbat, drv->pmic_data.ibat, drv->pmic_data.ntc_temp); + } + + // Check USB power source status if (drv->pmic_data.usb_status != 0x0) { if (!drv->usb_connected) { @@ -236,3 +254,17 @@ void pm_battery_initial_soc_guess(void) { fuel_gauge_initial_guess(&drv->fuel_gauge, vbat_g, ibat_g, ntc_temp_g); } + +void pm_store_power_manager_data(pm_driver_t* drv){ + + backup_ram_power_manager_data_t pm_data = {0}; + + // Store the current state of the power manager + pm_data.soc = drv->fuel_gauge.soc; + pm_data.last_capture_timestamp = systick_ms(); + + // Store the data in backup RAM + backup_ram_store_power_manager_data(&pm_data); + +} + diff --git a/core/embed/sys/power_manager/stm32u5/power_states.c b/core/embed/sys/power_manager/stm32u5/power_states.c index 02320818c9..d21f04f97b 100644 --- a/core/embed/sys/power_manager/stm32u5/power_states.c +++ b/core/embed/sys/power_manager/stm32u5/power_states.c @@ -309,6 +309,9 @@ void pm_enter_suspend(pm_driver_t* drv) { } void pm_enter_hibernate(pm_driver_t* drv) { + + pm_store_power_manager_data(drv); + reboot_device(); // Put PMIC into ship mode (ultra-low power) // npm1300_enter_shipmode(); diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revC.py b/core/site_scons/models/T3W1/trezor_t3w1_revC.py index 2c3a60d3f7..6f99d11a01 100644 --- a/core/site_scons/models/T3W1/trezor_t3w1_revC.py +++ b/core/site_scons/models/T3W1/trezor_t3w1_revC.py @@ -251,20 +251,19 @@ def configure( ("USE_OEM_KEYS_CHECK", "1"), ] - if "powerctl" in features_wanted: - sources += [ - "embed/sys/powerctl/npm1300/npm1300.c", - "embed/sys/powerctl/fuel_gauge/fuel_gauge.c", - "embed/sys/powerctl/fuel_gauge/battery_model.c", - "embed/sys/powerctl/stwlc38/stwlc38.c", - "embed/sys/powerctl/stwlc38/stwlc38_patching.c", - "embed/sys/powerctl/stm32u5/powerctl.c", - "embed/sys/powerctl/stm32u5/powerctl_suspend.c", - "embed/sys/powerctl/wakeup_flags.c", - ] - paths += ["embed/sys/powerctl/inc"] - defines += [("USE_POWERCTL", "1")] - features_available.append("powerctl") + sources += [ + "embed/sys/powerctl/npm1300/npm1300.c", + "embed/sys/powerctl/fuel_gauge/fuel_gauge.c", + "embed/sys/powerctl/fuel_gauge/battery_model.c", + "embed/sys/powerctl/stwlc38/stwlc38.c", + "embed/sys/powerctl/stwlc38/stwlc38_patching.c", + "embed/sys/powerctl/stm32u5/powerctl.c", + "embed/sys/powerctl/stm32u5/powerctl_suspend.c", + "embed/sys/powerctl/wakeup_flags.c", + ] + paths += ["embed/sys/powerctl/inc"] + defines += [("USE_POWERCTL", "1")] + features_available.append("powerctl") env.get("ENV")["LINKER_SCRIPT"] = linker_script