1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-03-20 18:16:05 +00:00

feat(core): introduce hibernate mode

[no changelog]
This commit is contained in:
cepetr 2025-02-13 15:43:29 +01:00 committed by cepetr
parent 79d14b8907
commit de73b38aae
11 changed files with 175 additions and 4 deletions

View File

@ -547,3 +547,40 @@ Example:
optiga-counter-read
OK 0E
```
### powerctl-suspend
Enters low-power mode.
In low-power mode, the CPU retains its state, including SRAM content.
The device can be woken by pressing the power button and will continue
operation from the point where it was suspended.
Example:
```
powerctl-suspend
# Suspending the device to low-power mode...
# Press the POWER button to resume.
....
# Resumed to active mode.
OK
```
### powerctl-hibernate
Enters Hibernate mode.
In Hibernate mode, the CPU is powered off, and only the VBAT domain remains
active. The device can be woken by pressing the power button, triggering
a full boot sequence.
Hibernate mode can only be entered if the device is not connected to a USB or
wireless charger.
Example:
```
powerctl-hibernate
# Hibernating the the device...
# Device is powered externally, hibernation is not possible.
OK
```

View File

@ -43,6 +43,23 @@ static void prodtest_powerctl_suspend(cli_t* cli) {
cli_ok(cli, "");
}
static void prodtest_powerctl_hibernate(cli_t* cli) {
if (cli_arg_count(cli) > 0) {
cli_error_arg_count(cli);
return;
}
cli_trace(cli, "Hibernating the the device...");
if (!powerctl_hibernate()) {
cli_error(cli, CLI_ERROR, "Failed to hibernate.");
return;
}
cli_trace(cli, "Device is powered externally, hibernation is not possible.");
cli_ok(cli, "");
}
// clang-format off
PRODTEST_CLI_CMD(
@ -52,4 +69,12 @@ PRODTEST_CLI_CMD(
.args = ""
);
PRODTEST_CLI_CMD(
.name = "powerctl-hibernate",
.func = prodtest_powerctl_hibernate,
.info = "Hibernate the device into a near power-off state",
.args = ""
);
#endif // USE_POWERCTL

View File

@ -155,6 +155,10 @@ static void show_welcome_screen(void) {
}
static void drivers_init(void) {
#ifdef USE_POWERCTL
powerctl_init();
#endif
display_init(DISPLAY_RESET_CONTENT);
#ifdef USE_STORAGE_HWKEY

View File

@ -49,10 +49,30 @@ typedef struct {
} powerctl_status_t;
// Gets the current power status.
void powerctl_get_status(powerctl_status_t* status);
//
// Returns `true` if the status was successfully retrieved.
bool powerctl_get_status(powerctl_status_t* status);
// Enters low-power mode
//
// In low-power mode, the CPU retains its state, including SRAM content.
// The device can be woken by pressing the power button and will continue
// operation from the point where it was suspended.
void powerctl_suspend(void);
// Enters Hibernate mode.
//
// In Hibernate mode, the CPU is powered off, and only the VBAT domain remains
// active. The device can be woken by pressing the power button, triggering
// a full boot sequence.
//
// Hibernate mode can only be entered if the device is not connected to a USB or
// wireless charger. If the device is charging, the function returns `true`,
// and the device state remains unchanged. If the function succeeds, it does
// not return.
//
// Returns `false` if the operation fails (likely due to uninitialized power
// management).
bool powerctl_hibernate(void);
#endif // TREZORHAL_POWERCTL_H

View File

@ -63,7 +63,21 @@ void npm1300_deinit(void);
// Gets the cause of the last restart
uint8_t npm1300_restart_cause(void);
// Switches the device to the ship mode
// Switches the device to ship mode.
//
// In tge ship mode, the CPU is powered off, and only the VBAT domain remains
// active. The device can be woken by pressing the power button, triggering
// a full boot sequence.
//
// Ship mode can only be entered if the device is not connected to a USB or
// wireless charger. If the device is charging, the function returns `true`,
// and the device state remains unchanged.
//
// If the function succeeds, the device will not be powered off immediately,
// but after some time (typically a few milliseconds).
//
// Returns `false` if the operation fails (likely due to uninitialized power
// management).
bool npm1300_enter_shipmode(void);
// Starts the asynchronous measurement

View File

@ -21,6 +21,7 @@
#include <trezor_rtl.h>
#include <sys/powerctl.h>
#include <sys/systick.h>
#include "../npm1300/npm1300.h"
#include "../stwlc38/stwlc38.h"
@ -77,16 +78,35 @@ void powerctl_deinit(void) {
drv->initialized = false;
}
void powerctl_get_status(powerctl_status_t* status) {
bool powerctl_get_status(powerctl_status_t* status) {
powerctl_driver_t* drv = &g_powerctl_driver;
memset(status, 0, sizeof(powerctl_status_t));
if (!drv->initialized) {
return;
return false;
}
// TODO
return true;
}
bool powerctl_hibernate(void) {
powerctl_driver_t* drv = &g_powerctl_driver;
if (!drv->initialized) {
return false;
}
if (!npm1300_enter_shipmode()) {
return false;
}
// Wait for the device to power off
systick_delay_ms(50);
return true;
}
#endif // KERNEL_MODE

View File

@ -726,6 +726,15 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args,
case SYSCALL_POWERCTL_SUSPEND: {
powerctl_suspend();
} break;
case SYSCALL_POWERCTL_HIBERNATE: {
args[0] = powerctl_hibernate();
}
case SYSCALL_POWERCTL_GET_STATUS: {
powerctl_status_t *status = (powerctl_status_t *)args[0];
args[0] = powerctl_get_status__verified(status);
}
#endif
#ifdef USE_HW_JPEG_DECODER

View File

@ -147,6 +147,8 @@ typedef enum {
SYSCALL_BLE_READ,
SYSCALL_POWERCTL_SUSPEND,
SYSCALL_POWERCTL_HIBERNATE,
SYSCALL_POWERCTL_GET_STATUS,
SYSCALL_JPEGDEC_OPEN,
SYSCALL_JPEGDEC_CLOSE,

View File

@ -685,6 +685,14 @@ uint32_t ble_read(uint8_t *data, uint16_t len) {
void powerctl_suspend(void) { syscall_invoke0(SYSCALL_POWERCTL_SUSPEND); }
bool powerctl_hibernate(void) {
return (bool)syscall_invoke0(SYSCALL_POWERCTL_HIBERNATE);
}
bool powerctl_get_status(powerctl_status_t *status) {
return (bool)syscall_invoke1((uint32_t)status, SYSCALL_POWERCTL_GET_STATUS);
}
#endif // USE_POWERCTL
// =============================================================================

View File

@ -783,6 +783,28 @@ access_violation:
// ---------------------------------------------------------------------
#ifdef USE_POWERCTL
bool powerctl_get_status__verified(powerctl_status_t *status) {
if (!probe_write_access(status, sizeof(*status))) {
goto access_violation;
}
powerctl_status_t status_copy = {0};
bool retval = powerctl_get_status(&status_copy);
*status = status_copy;
return retval;
access_violation:
apptask_access_violation();
return false;
}
#endif
// ---------------------------------------------------------------------
#ifdef USE_HW_JPEG_DECODER
jpegdec_state_t jpegdec_process__verified(jpegdec_input_t *input) {

View File

@ -202,6 +202,16 @@ secbool ble_read__verified(uint8_t *data, size_t len);
#endif
// ---------------------------------------------------------------------
#ifdef USE_POWERCTL
#include <sys/powerctl.h>
bool powerctl_get_status__verified(powerctl_status_t *status);
#endif
// ---------------------------------------------------------------------
#ifdef USE_HW_JPEG_DECODER