refactor(core): make wireless charging driver optional
What changed, and why it matters
This commit reorganizes how the wireless charging chip driver is included in the Trezor firmware. Previously, the driver was always compiled in for certain hardware models; now it is an optional feature that is enabled only for boards that actually have wireless charging. There is no indication this fixes a security bug—it is a build-system and hardware-support cleanup.
No security action required; treat as a normal refactoring/build cleanup. Continue standard review and testing.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change introduces a new Cargo feature wireless_stwlc38 and a C preprocessor macro USE_WIRELESS_CHARGER. The STWLC38 wireless-charger source files and all calls to stwlc38_* are now guarded by this feature/macro. Board configuration files for T3W1 revA/B/C explicitly enable the wireless driver, while other boards can omit it. The suspend framework no longer directly suspends/resumes PMIC and STWLC38; instead it relies on the power manager to handle its sub-drivers. The production-test wireless-charging command is also gated on USE_WIRELESS_CHARGER rather than USE_POWER_MANAGER.
Changed components
core/embed/io/power_manager build and runtime codecore/embed/io/suspend/stm32u5/suspend.ccore/embed/models/T3W1 board configurationscore/embed/projects/prodtest/cmd/prodtest_wpc.cInspect captured patch +55 / −19
diff --git a/core/embed/io/Cargo.toml b/core/embed/io/Cargo.toml
index 58240570..f61ef39a 100644
--- a/core/embed/io/Cargo.toml
+++ b/core/embed/io/Cargo.toml
@@ -54,6 +54,7 @@ hw_jpeg_decoder = []
lockable_bootloader = ["sec/lockable_bootloader"]
pmic = ["sys/i2c_bus"]
power_manager = ["pmic", "sys/i2c_bus"]
+wireless_stwlc38 = ["sys/i2c_bus"]
production = ["sec/production"]
raspi_emulator = []
rgb_led = []
diff --git a/core/embed/io/power_manager/build.rs b/core/embed/io/power_manager/build.rs
index 174bf7b0..7391509c 100644
--- a/core/embed/io/power_manager/build.rs
+++ b/core/embed/io/power_manager/build.rs
@@ -23,6 +23,19 @@ pub fn def_module(lib: &mut CLibrary) -> Result<()> {
lib.add_source("power_manager/power_manager_poll.c");
+ // --- Optional wireless charger ----------------------------------------
+ // Not present in the emulator (the STWLC38 driver needs the STM32 HAL).
+ if cfg!(feature = "wireless_stwlc38") && cfg!(not(feature = "emulator")) {
+ lib.add_define("USE_WIRELESS_CHARGER", Some("1"));
+ if cfg!(feature = "mcu_stm32u5") {
+ lib.add_sources([
+ "power_manager/stwlc38/stwlc38.c",
+ "power_manager/stwlc38/stwlc38_patching.c",
+ ]);
+ }
+ }
+
+ // --- Power manager backend --------------------------------------------
if cfg!(feature = "emulator") {
lib.add_source("power_manager/unix/power_manager.c");
} else if cfg!(feature = "mcu_stm32u5") {
@@ -33,8 +46,6 @@ pub fn def_module(lib: &mut CLibrary) -> Result<()> {
"power_manager/battery/battery.c",
"power_manager/battery/fuel_gauge.c",
"power_manager/battery/battery_model.c",
- "power_manager/stwlc38/stwlc38.c",
- "power_manager/stwlc38/stwlc38_patching.c",
]);
} else {
bail_unsupported!();
diff --git a/core/embed/io/power_manager/stm32u5/power_manager.c b/core/embed/io/power_manager/stm32u5/power_manager.c
index 782dc769..9aadad13 100644
--- a/core/embed/io/power_manager/stm32u5/power_manager.c
+++ b/core/embed/io/power_manager/stm32u5/power_manager.c
@@ -63,11 +63,18 @@ pm_status_t pm_init(bool inherit_state) {
memset(drv, 0, sizeof(pm_driver_t));
// Initialize hardware subsystems
- if (!pmic_init() || !stwlc38_init()) {
+ if (!pmic_init()) {
pm_deinit();
return PM_ERROR;
}
+#ifdef USE_WIRELESS_CHARGER
+ if (!stwlc38_init()) {
+ pm_deinit();
+ return PM_ERROR;
+ }
+#endif
+
if (!pm_poll_init()) {
pm_deinit();
return PM_ERROR;
@@ -202,7 +209,9 @@ void pm_deinit(void) {
}
pmic_deinit();
+#ifdef USE_WIRELESS_CHARGER
stwlc38_deinit();
+#endif
drv->initialized = false;
}
@@ -630,6 +639,12 @@ bool pm_driver_suspend(void) {
irq_unlock(irq_key);
+ // Suspend the sub-drivers owned by the power manager.
+ pmic_suspend();
+#ifdef USE_WIRELESS_CHARGER
+ stwlc38_suspend();
+#endif
+
return true;
}
@@ -701,6 +716,12 @@ bool pm_usb_is_connected(void) {
}
bool pm_driver_resume(void) {
+ // Resume the sub-drivers owned by the power manager.
+#ifdef USE_WIRELESS_CHARGER
+ stwlc38_resume();
+#endif
+ pmic_resume();
+
pm_driver_t* drv = &g_pm;
if (!drv->initialized) {
@@ -748,6 +769,12 @@ bool pm_driver_is_suspended(void) {
suspended = drv->suspended;
irq_unlock(irq_key);
+ // The power manager is only fully suspended once its sub-drivers are too.
+ suspended = suspended && pmic_is_suspended();
+#ifdef USE_WIRELESS_CHARGER
+ suspended = suspended && stwlc38_is_suspended();
+#endif
+
return suspended;
}
diff --git a/core/embed/io/power_manager/stm32u5/power_monitoring.c b/core/embed/io/power_manager/stm32u5/power_monitoring.c
index e945c919..832092b0 100644
--- a/core/embed/io/power_manager/stm32u5/power_monitoring.c
+++ b/core/embed/io/power_manager/stm32u5/power_monitoring.c
@@ -77,7 +77,9 @@ void pm_pmic_data_ready(void* context, pmic_report_t* report) {
memcpy(&drv->pmic_data, report, sizeof(pmic_report_t));
// Get wireless charger data
+#ifdef USE_WIRELESS_CHARGER
stwlc38_get_report(&drv->wireless_data);
+#endif
#ifdef USE_TELEMETRY
// Update telemetry with the current battery temperature
diff --git a/core/embed/io/suspend/stm32u5/suspend.c b/core/embed/io/suspend/stm32u5/suspend.c
index ca4ed821..cea8f142 100644
--- a/core/embed/io/suspend/stm32u5/suspend.c
+++ b/core/embed/io/suspend/stm32u5/suspend.c
@@ -26,8 +26,6 @@
#include <sec/suspend_io.h>
#include <sys/irq.h>
-#include <../../power_manager/stwlc38/stwlc38.h>
-#include <io/pmic.h>
#include <io/power_manager.h>
#ifdef USE_RGB_LED
@@ -147,21 +145,15 @@ wakeup_flags_t system_suspend(void) {
return wakeup_flags;
}
-static void background_tasks_suspend(void) {
- pm_driver_suspend();
- pmic_suspend();
- stwlc38_suspend();
-}
+// The power manager owns its sub-drivers (PMIC, wireless charger); it
+// suspends/resumes them internally, so the suspend framework only deals with
+// the pm_driver_* interface and stays unaware of which power hardware exists.
+static void background_tasks_suspend(void) { pm_driver_suspend(); }
static bool background_tasks_suspended(void) {
- return pmic_is_suspended() && stwlc38_is_suspended() &&
- pm_driver_is_suspended();
+ return pm_driver_is_suspended();
}
-static void background_tasks_resume(void) {
- stwlc38_resume();
- pmic_resume();
- pm_driver_resume();
-}
+static void background_tasks_resume(void) { pm_driver_resume(); }
#endif // defined(KERNEL_MODE) && !defined(SECMON)
diff --git a/core/embed/models/T3W1/boards/revA.toml b/core/embed/models/T3W1/boards/revA.toml
index dc74a5b4..1f0f185c 100644
--- a/core/embed/models/T3W1/boards/revA.toml
+++ b/core/embed/models/T3W1/boards/revA.toml
@@ -25,6 +25,7 @@ panel = "io/touch_panel_lx250a2410a"
[rgb_led]
[power_manager]
+wireless = "io/wireless_stwlc38"
[nfc]
diff --git a/core/embed/models/T3W1/boards/revB.toml b/core/embed/models/T3W1/boards/revB.toml
index ceba657f..915da087 100644
--- a/core/embed/models/T3W1/boards/revB.toml
+++ b/core/embed/models/T3W1/boards/revB.toml
@@ -25,6 +25,7 @@ panel = "io/touch_panel_lx250a2410a"
[rgb_led]
[power_manager]
+wireless = "io/wireless_stwlc38"
[nfc]
diff --git a/core/embed/models/T3W1/boards/revC.toml b/core/embed/models/T3W1/boards/revC.toml
index d5a1fa48..b939ec04 100644
--- a/core/embed/models/T3W1/boards/revC.toml
+++ b/core/embed/models/T3W1/boards/revC.toml
@@ -25,6 +25,7 @@ panel = "io/touch_panel_lx250a2410a"
[rgb_led]
[power_manager]
+wireless = "io/wireless_stwlc38"
[nfc]
diff --git a/core/embed/projects/prodtest/cmd/prodtest_wpc.c b/core/embed/projects/prodtest/cmd/prodtest_wpc.c
index 891181f7..84b5a10e 100644
--- a/core/embed/projects/prodtest/cmd/prodtest_wpc.c
+++ b/core/embed/projects/prodtest/cmd/prodtest_wpc.c
@@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifdef USE_POWER_MANAGER
+#ifdef USE_WIRELESS_CHARGER
#include <trezor_rtl.h>
@@ -183,4 +183,4 @@ PRODTEST_CLI_CMD(
.args = ""
);
-#endif // USE_POWER_MANAGER
+#endif // USE_WIRELESS_CHARGER
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.