From 5d64a674791bce6bec8d9982815671cee1016ede Mon Sep 17 00:00:00 2001 From: cepetr Date: Wed, 20 Nov 2024 16:24:20 +0100 Subject: [PATCH] feat(core): introduce powerctl module [no changelog] --- core/SConscript.kernel | 3 +- core/embed/projects/kernel/main.c | 8 ++ core/embed/sys/powerctl/inc/sys/powerctl.h | 58 ++++++++ .../embed/sys/powerctl/inc/sys/wakeup_flags.h | 44 +++++++ core/embed/sys/powerctl/stm32u5/powerctl.c | 92 +++++++++++++ .../sys/powerctl/stm32u5/powerctl_suspend.c | 124 ++++++++++++++++++ core/embed/sys/powerctl/wakeup_flags.c | 46 +++++++ .../models/T3W1/trezor_t3w1_revA.py | 7 +- 8 files changed, 380 insertions(+), 2 deletions(-) create mode 100644 core/embed/sys/powerctl/inc/sys/powerctl.h create mode 100644 core/embed/sys/powerctl/inc/sys/wakeup_flags.h create mode 100644 core/embed/sys/powerctl/stm32u5/powerctl.c create mode 100644 core/embed/sys/powerctl/stm32u5/powerctl_suspend.c create mode 100644 core/embed/sys/powerctl/wakeup_flags.c diff --git a/core/SConscript.kernel b/core/SConscript.kernel index e07f16d1e3..01b449c2ee 100644 --- a/core/SConscript.kernel +++ b/core/SConscript.kernel @@ -295,7 +295,8 @@ env.Replace( '-ffreestanding ' '-fstack-protector-all ' + env.get('ENV')["CPU_CCFLAGS"] + CCFLAGS_MOD, - LINKFLAGS='-T build/kernel/memory.ld -Wl,--gc-sections -Wl,--print-memory-usage -Wl,-Map=build/kernel/kernel.map -Wl,--warn-common', + LINKFLAGS='-T build/kernel/memory.ld -Wl,--gc-sections -Wl,--print-memory-usage ' + ' -Wl,-Map=build/kernel/kernel.map -Wl,--warn-common -Wl,--undefined=__errno', CPPPATH=ALLPATHS, CPPDEFINES=[ 'KERNEL', diff --git a/core/embed/projects/kernel/main.c b/core/embed/projects/kernel/main.c index 2ff4f4f4c0..81cb8c73a5 100644 --- a/core/embed/projects/kernel/main.c +++ b/core/embed/projects/kernel/main.c @@ -55,6 +55,10 @@ #include #endif +#ifdef USE_POWERCTL +#include +#endif + #ifdef USE_PVD #include #endif @@ -104,6 +108,10 @@ static void optiga_log_hex(const char *prefix, const uint8_t *data, #endif void drivers_init() { +#ifdef USE_POWERCTL + powerctl_init(); +#endif + #ifdef USE_TAMPER tamper_init(); #endif diff --git a/core/embed/sys/powerctl/inc/sys/powerctl.h b/core/embed/sys/powerctl/inc/sys/powerctl.h new file mode 100644 index 0000000000..45dfd49dae --- /dev/null +++ b/core/embed/sys/powerctl/inc/sys/powerctl.h @@ -0,0 +1,58 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TREZORHAL_POWERCTL_H +#define TREZORHAL_POWERCTL_H + +// Initializes power control module. +// +// Returns true if the initialization was successful. +bool powerctl_init(void); + +// Deinitializes power control module. +void powerctl_deinit(void); + +typedef enum { + POWER_SOURCE_BATT, + POWER_SOURCE_USB, + POWER_SOURCE_QI, +} power_source_t; + +typedef struct { + // Current power source + power_source_t power_source; + // Set if charging is active + bool charging; + // Battery charge level in percents + // (or -1 if the battery level is unknown) + int charge_level; + // Set if the temperature is too low + bool low_temperature; + // Set if the temperature is too high + bool high_temperature; +} powerctl_status_t; + +// Gets the current power status. +void powerctl_get_status(powerctl_status_t* status); + +// Enters low-power mode +// +void powerctl_suspend(void); + +#endif // TREZORHAL_POWERCTL_H \ No newline at end of file diff --git a/core/embed/sys/powerctl/inc/sys/wakeup_flags.h b/core/embed/sys/powerctl/inc/sys/wakeup_flags.h new file mode 100644 index 0000000000..e8642e7a4f --- /dev/null +++ b/core/embed/sys/powerctl/inc/sys/wakeup_flags.h @@ -0,0 +1,44 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TREZORHAL_WAKEUP_FLAGS_H +#define TREZORHAL_WAKEUP_FLAGS_H + +#include + +// Wakeup flags used to signal the reason of the wakeup +// from the STOP mode + +#define WAKEUP_FLAG_BUTTON 0x01 // Button pressed +#define WAKEUP_FLAG_WPC 0x02 // Wireless power charging event +#define WAKEUP_FLAG_BLE 0x04 // Bluetooth connection event +#define WAKEUP_FLAG_NFC 0x08 // NFC event +#define WAKEUP_FLAG_USB 0x10 // USB event +#define WAKEUP_FLAG_TIMER 0x20 // Timer event + +// Sets the wakeup flag +void wakeup_flags_set(uint16_t flags); + +// Resets all wakeup flags +void wakeup_flags_reset(void); + +// Gets current wakeup flags +uint16_t wakeup_flags_get(void); + +#endif // TREZORHAL_WAKEUP_FLAGS_H diff --git a/core/embed/sys/powerctl/stm32u5/powerctl.c b/core/embed/sys/powerctl/stm32u5/powerctl.c new file mode 100644 index 0000000000..cdea214e1a --- /dev/null +++ b/core/embed/sys/powerctl/stm32u5/powerctl.c @@ -0,0 +1,92 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include + +#include "../npm1300/npm1300.h" +#include "../stwlc38/stwlc38.h" + +#ifdef KERNEL_MODE + +// Power control driver state +typedef struct { + // True if the driver is initialized + bool initialized; + +} powerctl_driver_t; + +// Power control driver instance +static powerctl_driver_t g_powerctl_driver = {.initialized = false}; + +bool powerctl_init(void) { + powerctl_driver_t* drv = &g_powerctl_driver; + + if (drv->initialized) { + return true; + } + + // Initialize PMIC + if (!npm1300_init()) { + goto cleanup; + } + + // Initialize wireless charging + if (!stwlc38_init()) { + goto cleanup; + } + + drv->initialized = true; + + return true; + +cleanup: + stwlc38_deinit(); + npm1300_deinit(); + return false; +} + +void powerctl_deinit(void) { + powerctl_driver_t* drv = &g_powerctl_driver; + + if (!drv->initialized) { + return; + } + + stwlc38_deinit(); + npm1300_deinit(); + + drv->initialized = false; +} + +void 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; + } + + // TODO +} + +#endif // KERNEL_MODE diff --git a/core/embed/sys/powerctl/stm32u5/powerctl_suspend.c b/core/embed/sys/powerctl/stm32u5/powerctl_suspend.c new file mode 100644 index 0000000000..3ea2e47f93 --- /dev/null +++ b/core/embed/sys/powerctl/stm32u5/powerctl_suspend.c @@ -0,0 +1,124 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include +#include +#include +#include + +#ifdef USE_TOUCH +#include +#endif + +#ifdef USE_HAPTIC +#include +#endif + +#ifdef KERNEL_MODE + +static void background_tasks_suspend(void) { + // stwlc38 + // npm1300 + // nrf52 + // ble + // powerctl +} + +static bool background_tasks_suspended(void) { return true; } + +static void background_tasks_resume(void) {} + +void powerctl_suspend(void) { + // Clear all wakeup flags. From this point, any wakeup event that + // sets a wakeup flag causes this function to return. + wakeup_flags_reset(); + + // Deinitialize all drivers that are not required in low-power mode + // (e.g., USB, display, touch, haptic, etc.). + usb_stop(); +#ifdef USE_HAPTIC + haptic_deinit(); +#endif +#ifdef USE_TOUCH + touch_deinit(); +#endif + int backlight_level = display_get_backlight(); + display_deinit(DISPLAY_RESET_CONTENT); + + // In the following loop, the system will attempt to enter low-power mode. + // Low-power mode may be exited for various reasons, but the loop will + // terminate only if a wakeup flag is set, indicating that user interaction + // is required or the user needs to be notified. + + while (wakeup_flags_get() == 0) { + // Notify state machines running in the interrupt context about the + // impending low-power mode. They should complete any pending operations + // and avoid starting new ones. + background_tasks_suspend(); + + // Wait until all state machines are idle and the system is ready to enter + // low-power mode. This loop also exits if any wakeup flag is set + // (e.g., due to a button press). + do { + __WFI(); + + // TODO: Implement a 5-second timeout to trigger a fatal error. + + } while (!background_tasks_suspended() && (wakeup_flags_get() == 0)); + + if (wakeup_flags_get() == 0) { + // Disable interrupts by setting PRIMASK to 1. + // + // The system can wake up, but interrupts will not be processed until + // PRIMASK is cleared again. This is necessary to restore the system clock + // immediately after exiting STOP2 mode. + irq_key_t irq_key = irq_lock(); + + // Enter STOP2 mode + HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI); + + // Recover system clock + SystemInit(); + + irq_unlock(irq_key); + + // At this point, all pending interrupts are processed. + // Some of them may set wakeup flags. + } + + // Resume state machines running in the interrupt context + background_tasks_resume(); + } + + // Reinitialize all drivers that were stopped earlier + display_init(DISPLAY_RESET_CONTENT); + display_set_backlight(backlight_level); +#ifdef USE_TOUCH + touch_init(); +#endif +#ifdef USE_HAPTIC + haptic_init(); +#endif + usb_start(); +} + +#endif // KERNEL_MODE diff --git a/core/embed/sys/powerctl/wakeup_flags.c b/core/embed/sys/powerctl/wakeup_flags.c new file mode 100644 index 0000000000..ae32c5683e --- /dev/null +++ b/core/embed/sys/powerctl/wakeup_flags.c @@ -0,0 +1,46 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#ifdef KERNEL_MODE + +static uint16_t g_wakeup_flags = 0; + +void wakeup_flags_set(uint16_t flags) { + irq_key_t irq_key = irq_lock(); + g_wakeup_flags |= flags; + irq_unlock(irq_key); +} + +void wakeup_flags_reset(void) { + irq_key_t irq_key = irq_lock(); + g_wakeup_flags = 0; + irq_unlock(irq_key); +} + +uint16_t wakeup_flags_get(void) { + irq_key_t irq_key = irq_lock(); + uint16_t flags = g_wakeup_flags; + irq_unlock(irq_key); + return flags; +} + +#endif // KERNEL_MODE diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revA.py b/core/site_scons/models/T3W1/trezor_t3w1_revA.py index 8f463d327e..5b91468693 100644 --- a/core/site_scons/models/T3W1/trezor_t3w1_revA.py +++ b/core/site_scons/models/T3W1/trezor_t3w1_revA.py @@ -136,8 +136,13 @@ def configure( sources += [ "embed/sys/powerctl/npm1300/npm1300.c", - "embed/sys/powerctl/stwlc38/stwlc38.c" + "embed/sys/powerctl/stwlc38/stwlc38.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"] env.get("ENV")["LINKER_SCRIPT"] = linker_script