diff --git a/core/embed/sys/powerctl/npm1300/npm1300.c b/core/embed/sys/powerctl/npm1300/npm1300.c
new file mode 100644
index 0000000000..fb2bc95c1c
--- /dev/null
+++ b/core/embed/sys/powerctl/npm1300/npm1300.c
@@ -0,0 +1,772 @@
+/*
+ * 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
+
+#include "npm1300.h"
+#include "npm1300_defs.h"
+
+#ifdef KERNEL_MODE
+
+// Default timeout for all I2C operations
+#define NPM1300_I2C_TIMEOUT 10
+
+// Maximum number of consecutive I2C errors after we report a fatal error
+#define NPM1300_I2C_ERROR_LIMIT 3
+
+// Delay inserted between the ADC trigger and the readout [ms]
+#define NPM1300_ADC_READOUT_DELAY 300
+
+// NPM1300 FSM states
+typedef enum {
+ NPM1300_STATE_IDLE = 0,
+ NPM1300_STATE_CHARGING_ENABLE,
+ NPM1300_STATE_CHARGING_DISABLE,
+ NPM1300_STATE_CHARGING_LIMIT,
+ NPM1300_STATE_ADC_TRIGGER,
+ NPM1300_STATE_ADC_WAIT,
+ NPM1300_STATE_ADC_READOUT,
+} npm1300_fsm_state_t;
+
+typedef struct {
+ uint8_t adc_gp0_result_lsbs;
+ uint8_t adc_vbat_result_msb;
+ uint8_t adc_nt_result_msb;
+ uint8_t adc_temp_result_msb;
+ uint8_t adc_vsys_result_msb;
+ uint8_t adc_gp1_result_lsbs;
+ uint8_t adc_vbat2_result_msb;
+ uint8_t adc_ibat_meas_status;
+
+} npm1300_adc_regs_t;
+
+typedef struct {
+ uint8_t bchg_iset_msb;
+ uint8_t bchg_iset_lsb;
+
+} npm1300_chlimit_regs_t;
+
+// NPM1300 PMIC driver state
+typedef struct {
+ // Set if the PMIC driver is initialized
+ bool initialized;
+
+ // I2C bus where the PMIC is connected
+ i2c_bus_t* i2c_bus;
+ // Number of consecutive I2C errors
+ int i2c_errors;
+ // Storage for the pending I2C packet
+ i2c_packet_t pending_i2c_packet;
+
+ // Timer used for waiting for the ADC conversion
+ systimer_t* timer;
+
+ // Content of RTSCAUSE register read during driver initialization
+ uint8_t restart_cause;
+
+ // Current state of the FSM
+ npm1300_fsm_state_t state;
+
+ // ADC register (global buffer used for ADC measurements)
+ npm1300_adc_regs_t adc_regs;
+ // Charging limit registers (global buffer used for charging limit)
+ npm1300_chlimit_regs_t chlimit_regs;
+
+ // Discharge current limit [mA]
+ uint16_t i_limit;
+
+ // Charge current limit [mA]
+ uint16_t i_charge; // written value
+ uint16_t i_charge_requested; // requested value
+ uint16_t i_charge_set; // value beeing written
+
+ // Set if the charging is enabled
+ bool charging;
+ bool charging_requested;
+
+ // Request flags for ADC measurements
+ bool adc_trigger_requested;
+ bool adc_readout_requested;
+
+ // Report callback used for asynchronous measurements
+ npm1300_report_callback_t report_callback;
+ void* report_callback_context;
+
+} npm1300_driver_t;
+
+// PMIC driver instance
+npm1300_driver_t g_npm1300_driver = {
+ .initialized = false,
+};
+
+// forward declarations
+static void npm1300_timer_callback(void* context);
+static void npm1300_i2c_callback(void* context, i2c_packet_t* packet);
+static void npm1300_fsm_continue(npm1300_driver_t* drv);
+
+// Writes a value to the NPM1300 register
+//
+// This function is used only during driver initialization because
+// it's synchronous and blocks the execution.
+static bool npm1300_set_reg(i2c_bus_t* bus, uint16_t addr, uint8_t value) {
+ i2c_op_t ops[] = {
+ {
+ .flags = I2C_FLAG_TX | I2C_FLAG_EMBED,
+ .size = 3,
+ .data = {addr >> 8, addr & 0xFF, value},
+ },
+ };
+
+ i2c_packet_t pkt = {
+ .address = NPM1300_I2C_ADDRESS,
+ .timeout = NPM1300_I2C_TIMEOUT,
+ .op_count = ARRAY_LENGTH(ops),
+ .ops = ops,
+ };
+
+ if (I2C_STATUS_OK != i2c_bus_submit_and_wait(bus, &pkt)) {
+ return false;
+ }
+
+ return true;
+}
+
+// Reads a value from the NPM1300 register
+//
+// This function is used only during driver initialization because
+// it's synchronous and blocks the execution.
+static bool npm1300_get_reg(i2c_bus_t* bus, uint16_t addr, uint8_t* data) {
+ i2c_op_t ops[] = {
+ {
+ .flags = I2C_FLAG_TX | I2C_FLAG_EMBED,
+ .size = 2,
+ .data = {addr >> 8, addr & 0xFF},
+ },
+ {
+ .flags = I2C_FLAG_RX,
+ .size = sizeof(*data),
+ .ptr = data,
+ },
+ };
+
+ i2c_packet_t pkt = {
+ .address = NPM1300_I2C_ADDRESS,
+ .timeout = NPM1300_I2C_TIMEOUT,
+ .op_count = ARRAY_LENGTH(ops),
+ .ops = ops,
+ };
+
+ if (I2C_STATUS_OK != i2c_bus_submit_and_wait(bus, &pkt)) {
+ return false;
+ }
+
+ return true;
+}
+
+// Initializes the NPM1300 driver to the default state
+static bool npm1300_initialize(i2c_bus_t* bus, uint16_t i_charge,
+ uint16_t i_limit) {
+ uint16_t bchg_iset = i_charge / 2; // 2mA steps
+ uint16_t bchg_iset_discharge = (i_limit * 100) / 323; // 3.23mA steps
+ uint16_t die_temp_stop = 360; // 110°C
+ uint16_t die_temp_resume = 372; // 100°C
+ uint16_t ntc_cold = 749; // 0°C
+ uint16_t ntc_cool = 658; // 10°C
+ uint16_t ntc_warm = 337; // 45°C
+ uint16_t ntc_hot = 237; // 60°C
+
+ struct {
+ uint16_t addr;
+ uint8_t value;
+ } table[] = {
+ {NPM1300_SCRATCH0, 0x00},
+ {NPM1300_SCRATCH1, 0x00},
+ // SYSREG
+ {NPM1300_VBUSINILIM0, NPM1300_VBUSINILIM0_500MA},
+ {NPM1300_VBUSSUSPEND, 0x00},
+ {NPM1300_TASKUPDATEILIMSW, NPM1300_TASKUPDATEILIM_SELVBUSILIM0},
+ // LOADSW/LDO
+ {NPM1300_LDSW1GPISEL, 0x00},
+ {NPM1300_LDSW2GPISEL, 0x00},
+ {NPM1300_TASKLDSW1CLR, 0x01},
+ {NPM1300_TASKLDSW2CLR, 0x01},
+ // BUCK regulators
+ // TODO
+ // ADC settings
+ {NPM1300_ADCNTCRSEL, NPM1300_ADCNTCRSEL_10K},
+ {NPM1300_ADCCONFIG, 0x00},
+ {NPM1300_ADCIBATMEASEN, NPM1300_ADCIBATMEASEN_IBATMEASENABLE},
+ // Charger settings
+ {NPM1300_BCHGVTERM, NPM1300_BCHGVTERM_3V65},
+ {NPM1300_BCHGVTERMR, NPM1300_BCHGVTERM_3V60},
+ {NPM1300_BCHGVTRICKLESEL, NPM1300_BCHGVTRICKLESEL_2V5},
+ {NPM1300_BCHGITERMSEL, NPM1300_BCHGITERMSEL_SEL10},
+ {NPM1300_BCHGISETMSB, bchg_iset >> 1},
+ {NPM1300_BCHGISETLSB, bchg_iset & 1},
+ {NPM1300_BCHGISETDISCHARGEMSB, bchg_iset_discharge >> 1},
+ {NPM1300_BCHGISETDISCHARGELSB, bchg_iset_discharge & 1},
+ {NPM1300_BCHGDISABLECLR, NPM1300_BCHGDISABLECLR_USENTC},
+ {NPM1300_BCHGDISABLECLR, NPM1300_BCHGDISABLECLR_ENABLERCHRG},
+ {NPM1300_BCHGCONFIG, 0},
+ // Disable charging
+ {NPM1300_BCHGENABLECLR, NPM1300_BCHGENABLECLR_DISABLECHG},
+ // NTC thresholds
+ {NPM1300_NTCCOLD, ntc_cold >> 2},
+ {NPM1300_NTCCOLDLSB, ntc_cold & 0x3},
+ {NPM1300_NTCCOOL, ntc_cool >> 2},
+ {NPM1300_NTCCOOLLSB, ntc_cool & 0x3},
+ {NPM1300_NTCWARM, ntc_warm >> 2},
+ {NPM1300_NTCWARMLSB, ntc_warm & 0x3},
+ {NPM1300_NTCHOT, ntc_hot >> 2},
+ {NPM1300_NTCHOTLSB, ntc_hot & 0x3},
+ // Die tempererature thresholds
+ {NPM1300_DIETEMPSTOP, die_temp_stop >> 2},
+ {NPM1300_DIETEMPSTOPLSB, die_temp_stop & 0x03},
+ {NPM1300_DIETEMPRESUME, die_temp_resume >> 2},
+ {NPM1300_DIETEMPRESUMELSB, die_temp_resume & 0x03},
+ // LEDS
+ {NPM1300_LEDDRV0MODESEL, NPM1300_LEDDRVMODESEL_ERROR},
+ {NPM1300_LEDDRV1MODESEL, NPM1300_LEDDRVMODESEL_CHARGING},
+ {NPM1300_LEDDRV2MODESEL, NPM1300_LEDDRVMODESEL_NOTUSED},
+ // GPIO
+ {NPM1300_GPIOMODE0, NPM1300_GPIOMODE_GPIINPUT},
+ {NPM1300_GPIOMODE1, NPM1300_GPIOMODE_GPIINPUT},
+ {NPM1300_GPIOMODE2, NPM1300_GPIOMODE_GPIINPUT},
+ {NPM1300_GPIOMODE3, NPM1300_GPIOMODE_GPIINPUT},
+ {NPM1300_GPIOMODE4, NPM1300_GPIOMODE_GPIINPUT},
+ // POF
+ {NPM1300_POFCONFIG, 0x00},
+ // TIMER
+ {NPM1300_TIMERCLR, 0x01},
+ // Ship and hibernate mode
+ // {NPM1300_SHPHLDCONFIG, .. },
+ // {NPM1300_TASKSHPHLDCFGSTROBE, 0x01},
+
+ // TODO automatic temp measurement during charging
+ };
+
+ for (int i = 0; i < sizeof(table) / sizeof(table[0]); i++) {
+ if (!npm1300_set_reg(bus, table[i].addr, table[i].value)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool npm1300_init(void) {
+ npm1300_driver_t* drv = &g_npm1300_driver;
+
+ if (drv->initialized) {
+ return true;
+ }
+
+ memset(drv, 0, sizeof(npm1300_driver_t));
+
+ drv->i_charge = NPM1300_CHARGING_LIMIT_DEFAULT; // mA
+ drv->i_limit = 500; // mA (268mA-1340mA)
+
+ drv->i_charge_set = drv->i_charge;
+ drv->i_charge_requested = drv->i_charge;
+
+ drv->i2c_bus = i2c_bus_open(NPM1300_I2C_INSTANCE);
+ if (drv->i2c_bus == NULL) {
+ goto cleanup;
+ }
+
+ drv->timer = systimer_create(npm1300_timer_callback, drv);
+ if (drv->timer == NULL) {
+ goto cleanup;
+ }
+
+ if (!npm1300_get_reg(drv->i2c_bus, NPM1300_RSTCAUSE, &drv->restart_cause)) {
+ goto cleanup;
+ }
+
+ if (!npm1300_initialize(drv->i2c_bus, drv->i_charge, drv->i_limit)) {
+ goto cleanup;
+ }
+
+ drv->initialized = true;
+
+ return true;
+
+cleanup:
+ npm1300_deinit();
+ return false;
+}
+
+void npm1300_deinit(void) {
+ npm1300_driver_t* drv = &g_npm1300_driver;
+
+ i2c_bus_close(drv->i2c_bus);
+ systimer_delete(drv->timer);
+
+ memset(drv, 0, sizeof(npm1300_driver_t));
+}
+
+bool npm1300_shipmode(void) {
+ npm1300_driver_t* drv = &g_npm1300_driver;
+
+ if (!drv->initialized) {
+ return false;
+ }
+
+ // TODO
+
+ return true;
+}
+
+int npm1300_get_charging_limit(void) {
+ npm1300_driver_t* drv = &g_npm1300_driver;
+
+ if (!drv->initialized) {
+ return 0;
+ }
+
+ return drv->i_charge_requested;
+}
+
+bool npm1300_set_charging_limit(int i_charge) {
+ npm1300_driver_t* drv = &g_npm1300_driver;
+
+ if (!drv->initialized) {
+ return false;
+ }
+
+ if (i_charge < NPM1300_CHARGING_LIMIT_MIN ||
+ i_charge > NPM1300_CHARGING_LIMIT_MAX) {
+ // The value is out of range
+ return false;
+ }
+
+ irq_key_t irq_key = irq_lock();
+ drv->i_charge_requested = i_charge;
+ npm1300_fsm_continue(drv);
+ irq_unlock(irq_key);
+
+ return true;
+}
+
+bool npm1300_set_charging(bool enable) {
+ npm1300_driver_t* drv = &g_npm1300_driver;
+
+ if (!drv->initialized) {
+ return false;
+ }
+
+ irq_key_t irq_key = irq_lock();
+ drv->charging_requested = enable;
+ npm1300_fsm_continue(drv);
+ irq_unlock(irq_key);
+
+ return true;
+}
+
+uint8_t npm1300_restart_cause(void) {
+ npm1300_driver_t* drv = &g_npm1300_driver;
+
+ if (!drv->initialized) {
+ return 0;
+ }
+
+ return drv->restart_cause;
+}
+
+bool npm1300_measure(npm1300_report_callback_t callback, void* context) {
+ npm1300_driver_t* drv = &g_npm1300_driver;
+
+ if (!drv->initialized) {
+ return false;
+ }
+
+ irq_key_t irq_key = irq_lock();
+
+ if (drv->report_callback != NULL && callback != NULL) {
+ // Cannot start another measurement while the previous one is in progress
+ irq_unlock(irq_key);
+ return false;
+ }
+
+ drv->report_callback = callback;
+ drv->report_callback_context = context;
+
+ if (drv->report_callback != NULL) {
+ drv->adc_trigger_requested = true;
+ npm1300_fsm_continue(drv);
+ }
+
+ irq_unlock(irq_key);
+
+ return true;
+}
+
+// Synchronous measurement context structure
+// (used internally within the `npm1300_measure_sync` function)
+typedef struct {
+ // Set when the measurement is done
+ volatile bool done;
+ // Report structure where the measurement is stored
+ npm1300_report_t* report;
+} npm1300_sync_measure_t;
+
+// Callback for the synchronous measurement
+static void npm1300_sync_measure_callback(void* context,
+ npm1300_report_t* report) {
+ npm1300_sync_measure_t* ctx = (npm1300_sync_measure_t*)context;
+ *ctx->report = *report;
+ ctx->done = true;
+}
+
+bool npm1300_measure_sync(npm1300_report_t* report) {
+ npm1300_sync_measure_t measure = {
+ .done = false,
+ .report = report,
+ };
+
+ // Start asynchronous measurement
+ if (!npm1300_measure(npm1300_sync_measure_callback, &measure)) {
+ return false;
+ }
+
+ // Wait for the measurement to finish
+ while (!measure.done) {
+ __WFI();
+ }
+
+ return true;
+}
+
+// Prepares PMIC report from the last readout of the ADC values
+// stored in `drv->adc_regs`
+//
+// This function is called in the irq context.
+static void npm1300_calculate_report(npm1300_driver_t* drv,
+ npm1300_report_t* report) {
+ memset(report, 0, sizeof(npm1300_report_t));
+
+ npm1300_adc_regs_t* r = &drv->adc_regs;
+
+ // Gather measured values from the ADC registers
+
+ uint16_t vbat_adc =
+ (r->adc_vbat_result_msb << 2) + (r->adc_gp0_result_lsbs & 0x03);
+
+ uint16_t ntc_adc =
+ (r->adc_nt_result_msb << 2) + ((r->adc_gp0_result_lsbs >> 2) & 0x3);
+
+ uint16_t die_adc =
+ (r->adc_temp_result_msb << 2) + ((r->adc_gp0_result_lsbs >> 4) & 0x3);
+
+ uint16_t vsys_adc =
+ (r->adc_vsys_result_msb << 2) + ((r->adc_gp0_result_lsbs >> 6) & 0x03);
+
+ uint16_t ibat_adc =
+ (r->adc_vbat2_result_msb << 2) + ((r->adc_gp1_result_lsbs >> 4) & 0x03);
+
+ // IBAT_MEAS_STATUS register isn't well documented in the NPM1300 datasheet.
+ // The following is based partially on observation.
+ //
+ // 00100 - discharge
+ // 01000 - usb powered, not charging
+ // 01100 - charge trickle
+ // 01110 - charge cool
+ // 01111 - charge normal
+ // 1XXXX - invalid value, start measure again
+
+ // bool ibat_invalid = (ibat_meas_status & 0x10) != 0;
+ bool ibat_discharging = ((r->adc_ibat_meas_status >> 2) & 0x03) == 1;
+ bool ibat_charging = ((r->adc_ibat_meas_status >> 2) & 0x03) == 3;
+
+ if (ibat_discharging) {
+ report->ibat = ((int)ibat_adc * drv->i_limit) / 1250;
+ } else if (ibat_charging) {
+ report->ibat = -((int)ibat_adc * drv->i_charge) / 800;
+ } else {
+ report->ibat = 0;
+ }
+
+ report->vbat = (vbat_adc * 5.0) / 1023;
+ float beta = 3380;
+ report->ntc_temp =
+ 1 / (1 / 298.15 - (1 / beta) * logf(1024.0 / ntc_adc - 1)) - 298.15 +
+ 25.0;
+ report->die_temp = 394.67 - 0.7926 * die_adc;
+ report->vsys = (vsys_adc * 6.375) / 1023;
+ report->ibat_meas_status = r->adc_ibat_meas_status;
+}
+
+// I2C operation for writing constant value to the npm1300 register
+#define NPM_WRITE_CONST(reg, value) \
+ { \
+ .flags = I2C_FLAG_TX | I2C_FLAG_EMBED | I2C_FLAG_START, .size = 3, \
+ .data = {(reg) >> 8, (reg) & 0xFF, (value)}, \
+ }
+
+// I2C operations for the value of specified uint8_t field
+// in `g_npm1300_driver` structure into npm1300 register
+#define NPM_WRITE_FIELD(reg, field) \
+ { \
+ .flags = I2C_FLAG_TX | I2C_FLAG_EMBED | I2C_FLAG_START, \
+ .size = 2, \
+ .data = {(reg) >> 8, (reg) & 0xFF}, \
+ }, \
+ { \
+ .flags = I2C_FLAG_TX, .size = 1, .ptr = &g_npm1300_driver.field, \
+ }
+
+// I2C operations for reading npm1300 register into the specified
+// field in `g_npm1300_driver` structure
+#define NPM_READ_FIELD(reg, field) \
+ { \
+ .flags = I2C_FLAG_TX | I2C_FLAG_EMBED | I2C_FLAG_START, \
+ .size = 2, \
+ .data = {(reg) >> 8, (reg) & 0xFF}, \
+ }, \
+ { \
+ .flags = I2C_FLAG_RX, .size = 1, .ptr = &g_npm1300_driver.field, \
+ }
+
+// I2C operations for enabling of the charging
+static const i2c_op_t npm1300_ops_charging_enable[] = {
+ NPM_WRITE_CONST(NPM1300_BCHGENABLESET, NPM1300_BCHGENABLESET_ENABLECHG),
+};
+
+// I2C operations for disabling of the charging
+static const i2c_op_t npm1300_ops_charging_disable[] = {
+ NPM_WRITE_CONST(NPM1300_BCHGENABLECLR, NPM1300_BCHGENABLECLR_DISABLECHG),
+};
+
+// I2C operations for setting of the charging limit from
+// `g_npm1300_driver.chlimit_regs` structure
+static const i2c_op_t npm1300_ops_charging_limit[] = {
+ NPM_WRITE_FIELD(NPM1300_BCHGISETMSB, chlimit_regs.bchg_iset_msb),
+ NPM_WRITE_FIELD(NPM1300_BCHGISETLSB, chlimit_regs.bchg_iset_lsb),
+};
+
+// I2C operations for setting of the charging limit from
+// `g_npm1300_driver.chlimit_regs` structure together with
+// disabling and re-enabling of the charging
+static const i2c_op_t npm1300_ops_charging_limit_reenable[] = {
+ NPM_WRITE_CONST(NPM1300_BCHGENABLECLR, NPM1300_BCHGENABLECLR_DISABLECHG),
+ NPM_WRITE_FIELD(NPM1300_BCHGISETMSB, chlimit_regs.bchg_iset_msb),
+ NPM_WRITE_FIELD(NPM1300_BCHGISETLSB, chlimit_regs.bchg_iset_lsb),
+ NPM_WRITE_CONST(NPM1300_BCHGENABLESET, NPM1300_BCHGENABLESET_ENABLECHG),
+};
+
+// I2C operations for triggering of the ADC measurements
+static const i2c_op_t npm1300_ops_adc_trigger[] = {
+ NPM_WRITE_CONST(NPM1300_TASKVBATMEASURE, 1),
+ NPM_WRITE_CONST(NPM1300_TASKVSYSMEASURE, 1),
+ NPM_WRITE_CONST(NPM1300_TASKNTCMEASURE, 1),
+ NPM_WRITE_CONST(NPM1300_TASKTEMPMEASURE, 1),
+};
+
+// I2C operations for readout of the ADC values into the
+// `g_npm1300_driver.adc_regs` structure
+static const i2c_op_t npm1300_ops_adc_readout[] = {
+ NPM_WRITE_CONST(NPM1300_TASKUPDATEILIMSW,
+ NPM1300_TASKUPDATEILIM_SELVBUSILIM0),
+ NPM_READ_FIELD(NPM1300_ADCGP0RESULTLSBS, adc_regs.adc_gp0_result_lsbs),
+ NPM_READ_FIELD(NPM1300_ADCVBATRESULTMSB, adc_regs.adc_vbat_result_msb),
+ NPM_READ_FIELD(NPM1300_ADCNTCRESULTMSB, adc_regs.adc_nt_result_msb),
+ NPM_READ_FIELD(NPM1300_ADCTEMPRESULTMSB, adc_regs.adc_temp_result_msb),
+ NPM_READ_FIELD(NPM1300_ADCVSYSRESULTMSB, adc_regs.adc_vsys_result_msb),
+ NPM_READ_FIELD(NPM1300_ADCGP1RESULTLSBS, adc_regs.adc_gp1_result_lsbs),
+ NPM_READ_FIELD(NPM1300_ADCVBAT2RESULTMSB, adc_regs.adc_vbat2_result_msb),
+ NPM_READ_FIELD(NPM1300_ADCIBATMEASSTATUS, adc_regs.adc_ibat_meas_status),
+};
+
+#define npm1300_i2c_submit(drv, ops) \
+ _npm1300_i2c_submit(drv, ops, ARRAY_LENGTH(ops))
+
+// helper function for submitting I2C operations
+static void _npm1300_i2c_submit(npm1300_driver_t* drv, const i2c_op_t* ops,
+ size_t op_count) {
+ i2c_packet_t* pkt = &drv->pending_i2c_packet;
+
+ memset(pkt, 0, sizeof(i2c_packet_t));
+ pkt->address = NPM1300_I2C_ADDRESS;
+ pkt->context = drv;
+ pkt->callback = npm1300_i2c_callback;
+ pkt->timeout = NPM1300_I2C_TIMEOUT;
+ pkt->ops = (i2c_op_t*)ops;
+ pkt->op_count = op_count;
+
+ i2c_status_t status = i2c_bus_submit(drv->i2c_bus, pkt);
+
+ if (status != I2C_STATUS_OK) {
+ // This should never happen
+ error_shutdown("npm1300 I2C submit error");
+ }
+}
+
+// npm1300 driver timer callback invoked when `drv->timer` expires.
+//
+// This function is called in the irq context.
+static void npm1300_timer_callback(void* context) {
+ npm1300_driver_t* drv = (npm1300_driver_t*)context;
+
+ switch (drv->state) {
+ case NPM1300_STATE_ADC_WAIT:
+ // The ADC conversion is done, read the values
+ drv->adc_readout_requested = true;
+ drv->state = NPM1300_STATE_IDLE;
+ break;
+
+ default:
+ // we should never get here
+ drv->state = NPM1300_STATE_IDLE;
+ break;
+ }
+
+ npm1300_fsm_continue(drv);
+}
+
+// npm1300 driver I2C completion callback invoked when
+// `drv->pending_i2c_packet` is completed
+//
+// This function is called in the irq context.
+static void npm1300_i2c_callback(void* context, i2c_packet_t* packet) {
+ npm1300_driver_t* drv = (npm1300_driver_t*)context;
+
+ if (packet->status != I2C_STATUS_OK) {
+ drv->i2c_errors++;
+
+ if (drv->i2c_errors > NPM1300_I2C_ERROR_LIMIT) {
+ error_shutdown("npm1300 I2C error");
+ }
+
+ drv->state = NPM1300_STATE_IDLE;
+
+ // I2C operation will be retried until it succeeds or
+ // the error limit is reached
+ npm1300_fsm_continue(drv);
+ return;
+ }
+
+ // If the I2C operation was successful, reset the error counter
+ drv->i2c_errors = 0;
+
+ switch (drv->state) {
+ case NPM1300_STATE_CHARGING_ENABLE:
+ drv->charging = true;
+ drv->state = NPM1300_STATE_IDLE;
+ break;
+
+ case NPM1300_STATE_CHARGING_DISABLE:
+ drv->charging = false;
+ drv->state = NPM1300_STATE_IDLE;
+ break;
+
+ case NPM1300_STATE_CHARGING_LIMIT:
+ drv->i_charge = drv->i_charge_set;
+ drv->state = NPM1300_STATE_IDLE;
+ break;
+
+ case NPM1300_STATE_ADC_TRIGGER:
+ drv->adc_trigger_requested = false;
+ systimer_set(drv->timer, NPM1300_ADC_READOUT_DELAY);
+ drv->state = NPM1300_STATE_ADC_WAIT;
+ break;
+
+ case NPM1300_STATE_ADC_READOUT:
+ drv->adc_readout_requested = false;
+
+ npm1300_report_t report;
+ npm1300_calculate_report(drv, &report);
+
+ // Invoke report callback
+ npm1300_report_callback_t report_callback = drv->report_callback;
+ void* report_callback_context = drv->report_callback_context;
+
+ // Clear the report callback before invoking it
+ // to allow the new measurement to be scheduled in the callback
+ drv->report_callback = NULL;
+ drv->report_callback_context = NULL;
+
+ if (report_callback != NULL) {
+ report_callback(report_callback_context, &report);
+ }
+
+ drv->state = NPM1300_STATE_IDLE;
+ break;
+
+ default:
+ // we should never get here
+ drv->state = NPM1300_STATE_IDLE;
+ break;
+ }
+
+ npm1300_fsm_continue(drv);
+}
+
+// npm1300 driver FSM continuation function that decides what to do next
+//
+// 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) {
+ return;
+ }
+
+ // The order of the following conditions defines the priority
+
+ if (drv->i_charge != drv->i_charge_requested) {
+ // Change charging limit
+ uint16_t bchg_iset = drv->i_charge / 2; // 2mA steps
+ drv->chlimit_regs.bchg_iset_msb = bchg_iset >> 1;
+ drv->chlimit_regs.bchg_iset_lsb = bchg_iset & 1;
+ drv->i_charge_set = drv->i_charge_requested;
+
+ if (drv->charging) {
+ // When charging is enabled, we need to disable it first
+ // and then re-enable it after changing the limit
+ npm1300_i2c_submit(drv, npm1300_ops_charging_limit_reenable);
+ } else {
+ npm1300_i2c_submit(drv, npm1300_ops_charging_limit);
+ }
+ drv->state = NPM1300_STATE_CHARGING_LIMIT;
+ } else if (drv->charging != drv->charging_requested) {
+ // Change charging state
+ if (drv->charging_requested) {
+ npm1300_i2c_submit(drv, npm1300_ops_charging_enable);
+ drv->state = NPM1300_STATE_CHARGING_ENABLE;
+ } else {
+ npm1300_i2c_submit(drv, npm1300_ops_charging_disable);
+ drv->state = NPM1300_STATE_CHARGING_DISABLE;
+ }
+ } else if (drv->adc_readout_requested) {
+ // Read ADC values
+ npm1300_i2c_submit(drv, npm1300_ops_adc_readout);
+ drv->state = NPM1300_STATE_ADC_READOUT;
+ } else if (drv->adc_trigger_requested) {
+ // Trigger ADC conversion
+ npm1300_i2c_submit(drv, npm1300_ops_adc_trigger);
+ drv->state = NPM1300_STATE_ADC_TRIGGER;
+ }
+}
+
+#endif // KERNEL_MODE
diff --git a/core/embed/sys/powerctl/npm1300/npm1300.h b/core/embed/sys/powerctl/npm1300/npm1300.h
new file mode 100644
index 0000000000..4b7a47fa90
--- /dev/null
+++ b/core/embed/sys/powerctl/npm1300/npm1300.h
@@ -0,0 +1,101 @@
+/*
+ * 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_NPM1300_H
+#define TREZORHAL_NPM1300_H
+
+#include
+
+// Charging current limits
+// - range of np1300 is 32-800mA
+// - used battery limit is 180mA
+#define NPM1300_CHARGING_LIMIT_MIN 32 // mA
+#define NPM1300_CHARGING_LIMIT_MAX 800 // mA // !@# TODO: set to 180mA
+#define NPM1300_CHARGING_LIMIT_DEFAULT 180 // mA
+
+typedef struct {
+ // Battery voltage [V]
+ float vbat;
+ // System voltage [V]
+ float vsys;
+ // Battery current [mA]
+ // - positive value means discharging
+ // - negative value means charging
+ float ibat;
+ // NTC temperature [°C]
+ float ntc_temp;
+ // Die temperature [°C]
+ float die_temp;
+ // IBAT_MEAS_STATUS register value
+ // (for debugging purposes, see the NPM1300 datasheet)
+ uint8_t ibat_meas_status;
+
+} npm1300_report_t;
+
+typedef void (*npm1300_report_callback_t)(void* context,
+ npm1300_report_t* report);
+
+// Initializes NPM1300 PMIC driver
+bool npm1300_init(void);
+
+// Deinitializes NPM1300 PMIC driver
+void npm1300_deinit(void);
+
+// Gets the cause of the last restart
+uint8_t npm1300_restart_cause(void);
+
+// Switches the device to the ship mode
+bool npm1300_shipmode(void);
+
+// Starts the asynchronous measurement
+//
+// The measurement is started as soon as possible and finished in
+// hundreds of milliseconds. The result is reported using the callback.
+//
+// The function returns `false` if the measurement cannot be started
+// (e.g. because the previous measurement is still in progress or
+// the the driver is not initialized).
+bool npm1300_measure(npm1300_report_callback_t callback, void* context);
+
+// Synchroneous version of the `pmic_measure` function.
+//
+// Use only for testing purposes, as it blocks the execution until
+// the measurement is done.
+//
+// Returns `true` if the measurement was successful and the report
+// is stored in the `report` structure.
+bool npm1300_measure_sync(npm1300_report_t* report);
+
+// Enables or disables the charging.
+//
+// The function returns `false` if the operation cannot be performed.
+bool npm1300_set_charging(bool enable);
+
+// Sets the charging current limit [mA].
+//
+// The current value must be in the range defined by the
+// `NPM1300_CHARGING_LIMIT_MIN` and `NPM1300_CHARGING_LIMIT_MAX` constants.
+//
+// The function returns `false` if the operation cannot be performed.
+bool npm1300_set_charging_limit(int i_charge);
+
+// Gets the charging current limit [mA].
+int npm1300_get_charging_limit(void);
+
+#endif // TREZORHAL_NPM1300_H
diff --git a/core/embed/sys/powerctl/npm1300/npm1300_defs.h b/core/embed/sys/powerctl/npm1300/npm1300_defs.h
new file mode 100644
index 0000000000..8def16cf1a
--- /dev/null
+++ b/core/embed/sys/powerctl/npm1300/npm1300_defs.h
@@ -0,0 +1,319 @@
+/*
+ * 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_NPM1300_DEFS_H
+#define TREZORHAL_NPM1300_DEFS_H
+
+// I2C address of the NPM1300 on the I2C bus.
+#define NPM1300_I2C_ADDRESS 0x6B
+
+// Event and interrupt registers
+
+#define NPM1300_TASKSWRESET 0x0001
+#define NPM1300_EVENTSADCSET 0x0002
+#define NPM1300_EVENTSADCCLR 0x0003
+#define NPM1300_INTENEVENTSADCSET 0x0004
+#define NPM1300_INTENEVENTSADCCLR 0x0005
+#define NPM1300_EVENTSBCHARGER0SET 0x0006
+#define NPM1300_EVENTSBCHARGER0CLR 0x0007
+#define NPM1300_INTENEVENTSBCHARGER0SET 0x0008
+#define NPM1300_INTENEVENTSBCHARGER0CLR 0x0009
+#define NPM1300_EVENTSBCHARGER1SET 0x000A
+#define NPM1300_EVENTSBCHARGER1CLR 0x000B
+#define NPM1300_INTENEVENTSBCHARGER1SET 0x000C
+#define NPM1300_INTENEVENTSBCHARGER1CLR 0x000D
+#define NPM1300_EVENTSBCHARGER2SET 0x000E
+#define NPM1300_EVENTSBCHARGER2CLR 0x000F
+#define NPM1300_INTENEVENTSBCHARGER2SET 0x0010
+#define NPM1300_INTENEVENTSBCHARGER2CLR 0x0011
+#define NPM1300_EVENTSSHPHLDSET 0x0012
+#define NPM1300_EVENTSSHPHLDCLR 0x0013
+#define NPM1300_INTENEVENTSSHPHLDSET 0x0014
+#define NPM1300_INTENEVENTSSHPHLDCLR 0x0015
+#define NPM1300_EVENTSVBUSIN0SET 0x0016
+#define NPM1300_EVENTSVBUSIN0CLR 0x0017
+#define NPM1300_INTENEVENTSVBUSIN0SET 0x0018
+#define NPM1300_INTENEVENTSVBUSIN0CLR 0x0019
+#define NPM1300_EVENTSVBUSIN1SET 0x001A
+#define NPM1300_EVENTSVBUSIN1CLR 0x001B
+#define NPM1300_INTENEVENTSVBUSIN1SET 0x001C
+#define NPM1300_INTENEVENTSVBUSIN1CLR 0x001D
+#define NPM1300_EVENTSGPIOSET 0x0022
+#define NPM1300_EVENTSGPIOCLR 0x0023
+#define NPM1300_INTENEVENTSGPIOSET 0x0024
+#define NPM1300_INTENEVENTSGPIOCLR 0x0025
+
+// SYSREG registers
+
+#define NPM1300_TASKUPDATEILIMSW 0x0200
+#define NPM1300_VBUSINILIM0 0x0201
+#define NPM1300_VBUSSUSPEND 0x0203
+#define NPM1300_USBCDETECTSTATUS 0x0205
+#define NPM1300_VBUSINSTATUS 0x0207
+
+// Charger registers
+
+#define NPM1300_TASKRELEASEERR 0x0300
+#define NPM1300_TASKCLEARCHGERR 0x0301
+#define NPM1300_TASKCLEARSAFETYTIMER 0x0302
+#define NPM1300_BCHGENABLESET 0x0304
+#define NPM1300_BCHGENABLECLR 0x0305
+#define NPM1300_BCHGDISABLESET 0x0306
+#define NPM1300_BCHGDISABLECLR 0x0307
+#define NPM1300_BCHGISETMSB 0x0308
+#define NPM1300_BCHGISETLSB 0x0309
+#define NPM1300_BCHGISETDISCHARGEMSB 0x030A
+#define NPM1300_BCHGISETDISCHARGELSB 0x030B
+#define NPM1300_BCHGVTERM 0x030C
+#define NPM1300_BCHGVTERMR 0x030D
+#define NPM1300_BCHGVTRICKLESEL 0x030E
+#define NPM1300_BCHGITERMSEL 0x030F
+#define NPM1300_NTCCOLD 0x0310
+#define NPM1300_NTCCOLDLSB 0x0311
+#define NPM1300_NTCCOOL 0x0312
+#define NPM1300_NTCCOOLLSB 0x0313
+#define NPM1300_NTCWARM 0x0314
+#define NPM1300_NTCWARMLSB 0x0315
+#define NPM1300_NTCHOT 0x0316
+#define NPM1300_NTCHOTLSB 0x0317
+#define NPM1300_DIETEMPSTOP 0x0318
+#define NPM1300_DIETEMPSTOPLSB 0x0319
+#define NPM1300_DIETEMPRESUME 0x031A
+#define NPM1300_DIETEMPRESUMELSB 0x031B
+#define NPM1300_BCHGILIMSTATUS 0x032D
+#define NPM1300_NTCSTATUS 0x0332
+#define NPM1300_DIETEMPSTATUS 0x0333
+#define NPM1300_BCHGCHARGESTATUS 0x0334
+#define NPM1300_BCHGERRREASON 0x0336
+#define NPM1300_BCHGERRSENSOR 0x0337
+#define NPM1300_BCHGCONFIG 0x033C
+
+// Buck regulator registers
+
+#define NPM1300_BUCK1ENASET 0x0400
+#define NPM1300_BUCK1ENACLR 0x0401
+#define NPM1300_BUCK2ENASET 0x0402
+#define NPM1300_BUCK2ENACLR 0x0403
+#define NPM1300_BUCK1PWMSET 0x0404
+#define NPM1300_BUCK1PWMCLR 0x0405
+#define NPM1300_BUCK2PWMSET 0x0406
+#define NPM1300_BUCK2PWMCLR 0x0407
+#define NPM1300_BUCK1NORMVOUT 0x0408
+#define NPM1300_BUCK1RETVOUT 0x0409
+#define NPM1300_BUCK2NORMVOUT 0x040A
+#define NPM1300_BUCK2RETVOUT 0x040B
+#define NPM1300_BUCKENCTRL 0x040C
+#define NPM1300_BUCKVRETCTRL 0x040D
+#define NPM1300_BUCKPWMCTRL 0x040E
+#define NPM1300_BUCKSWCTRLSEL 0x040F
+#define NPM1300_BUCK1VOUTSTATUS 0x0410
+#define NPM1300_BUCK2VOUTSTATUS 0x0411
+#define NPM1300_BUCKCTRL0 0x0415
+#define NPM1300_BUCKSTATUS 0x0434
+
+// System monitor registers
+
+#define NPM1300_TASKVBATMEASURE 0x0500
+#define NPM1300_TASKNTCMEASURE 0x0501
+#define NPM1300_TASKTEMPMEASURE 0x0502
+#define NPM1300_TASKVSYSMEASURE 0x0503
+#define NPM1300_TASKIBATMEASURE 0x0506
+#define NPM1300_TASKVBUS7MEASURE 0x0507
+#define NPM1300_TASKDELAYEDVBATMEASURE 0x0508
+#define NPM1300_ADCCONFIG 0x0509
+#define NPM1300_ADCNTCRSEL 0x050A
+#define NPM1300_ADCAUTOTIMCONF 0x050B
+#define NPM1300_TASKAUTOTIMUPDATE 0x050C
+#define NPM1300_ADCDELTIMCONF 0x050D
+#define NPM1300_ADCIBATMEASSTATUS 0x0510
+#define NPM1300_ADCVBATRESULTMSB 0x0511
+#define NPM1300_ADCNTCRESULTMSB 0x0512
+#define NPM1300_ADCTEMPRESULTMSB 0x0513
+#define NPM1300_ADCVSYSRESULTMSB 0x0514
+#define NPM1300_ADCGP0RESULTLSBS 0x0515
+#define NPM1300_ADCVBAT0RESULTMSB 0x0516
+#define NPM1300_ADCVBAT1RESULTMSB 0x0517
+#define NPM1300_ADCVBAT2RESULTMSB 0x0518
+#define NPM1300_ADCVBAT3RESULTMSB 0x0519
+#define NPM1300_ADCGP1RESULTLSBS 0x051A
+#define NPM1300_ADCIBATMEASEN 0x0524
+
+// GPIO registers
+
+#define NPM1300_GPIOMODE0 0x0600
+#define NPM1300_GPIOMODE1 0x0601
+#define NPM1300_GPIOMODE2 0x0602
+#define NPM1300_GPIOMODE3 0x0603
+#define NPM1300_GPIOMODE4 0x0604
+#define NPM1300_GPIODRIVE0 0x0605
+#define NPM1300_GPIODRIVE1 0x0606
+#define NPM1300_GPIODRIVE2 0x0607
+#define NPM1300_GPIODRIVE3 0x0608
+#define NPM1300_GPIODRIVE4 0x0609
+#define NPM1300_GPIOPUEN0 0x060A
+#define NPM1300_GPIOPUEN1 0x060B
+#define NPM1300_GPIOPUEN2 0x060C
+#define NPM1300_GPIOPUEN3 0x060D
+#define NPM1300_GPIOPUEN4 0x060E
+#define NPM1300_GPIOPDEN0 0x060F
+#define NPM1300_GPIOPDEN1 0x0610
+#define NPM1300_GPIOPDEN2 0x0611
+#define NPM1300_GPIOPDEN3 0x0612
+#define NPM1300_GPIOPDEN4 0x0613
+#define NPM1300_GPIOOPENDRAIN0 0x0614
+#define NPM1300_GPIOOPENDRAIN1 0x0615
+#define NPM1300_GPIOOPENDRAIN2 0x0616
+#define NPM1300_GPIOOPENDRAIN3 0x0617
+#define NPM1300_GPIOOPENDRAIN4 0x0618
+#define NPM1300_GPIODEBOUNCE0 0x0619
+#define NPM1300_GPIODEBOUNCE1 0x061A
+#define NPM1300_GPIODEBOUNCE2 0x061B
+#define NPM1300_GPIODEBOUNCE3 0x061C
+#define NPM1300_GPIODEBOUNCE4 0x061D
+#define NPM1300_GPIOSTATUS 0x061E
+
+// Timer registers
+
+#define NPM1300_TIMERSET 0x0700
+#define NPM1300_TIMERCLR 0x0701
+#define NPM1300_TIMERTARGETSTROBE 0x0703
+#define NPM1300_WATCHDOGKICK 0x0704
+#define NPM1300_TIMERCONFIG 0x0705
+#define NPM1300_TIMERSTATUS 0x0706
+#define NPM1300_TIMERHIBYTE 0x0708
+#define NPM1300_TIMERMIDBYTE 0x0709
+#define NPM1300_TIMERLOBYTE 0x070A
+
+// LOADSW/LDO registers
+
+#define NPM1300_TASKLDSW1SET 0x0800
+#define NPM1300_TASKLDSW1CLR 0x0801
+#define NPM1300_TASKLDSW2SET 0x0802
+#define NPM1300_TASKLDSW2CLR 0x0803
+#define NPM1300_LDSWSTATUS 0x0804
+#define NPM1300_LDSW1GPISEL 0x0805
+#define NPM1300_LDSW2GPISEL 0x0806
+#define NPM1300_LDSWCONFIG 0x0807
+#define NPM1300_LDSW1LDOSEL 0x0808
+#define NPM1300_LDSW2LDOSEL 0x0809
+#define NPM1300_LDSW1VOUTSEL 0x080C
+#define NPM1300_LDSW2VOUTSEL 0x080D
+
+// POF registers
+
+#define NPM1300_POFCONFIG 0x0900
+
+// LED registers
+
+#define NPM1300_LEDDRV0MODESEL 0x0A00
+#define NPM1300_LEDDRV1MODESEL 0x0A01
+#define NPM1300_LEDDRV2MODESEL 0x0A02
+#define NPM1300_LEDDRV0SET 0x0A03
+#define NPM1300_LEDDRV0CLR 0x0A04
+#define NPM1300_LEDDRV1SET 0x0A05
+#define NPM1300_LEDDRV1CLR 0x0A06
+#define NPM1300_LEDDRV2SET 0x0A07
+#define NPM1300_LEDDRV2CLR 0x0A08
+
+// Reset and hibernation mode registers
+
+#define NPM1300_TASKENTERHIBERNATE 0x0B00
+#define NPM1300_TASKSHPHLDCFGSTROBE 0x0B01
+#define NPM1300_TASKENTERSHIPMODE 0x0B02
+#define NPM1300_TASKRESETCFG 0x0B03
+#define NPM1300_SHPHLDCONFIG 0x0B04
+#define NPM1300_SHPHLDSTATUS 0x0B05
+#define NPM1300_LPRESETCONFIG 0x0B06
+
+// Reset and error registers
+
+#define NPM1300_TASKCLRERRLOG 0x0E00
+#define NPM1300_SCRATCH0 0x0E01
+#define NPM1300_SCRATCH1 0x0E02
+#define NPM1300_RSTCAUSE 0x0E03
+#define NPM1300_CHARGERERRREASON 0x0E04
+#define NPM1300_CHARGERERRSENSOR 0x0E05
+
+// ------------------------------------------------------
+
+// TASKSWRESET
+#define NPM1300_TASKSWRESET_TRIGGER 0x01
+
+// TASKUPDATEILIM
+#define NPM1300_TASKUPDATEILIM_SELVBUSILIM0 1
+
+// VBUSINILIM0
+#define NPM1300_VBUSINILIM0_500MA 5 // Default
+
+// BCHGENABLESET
+#define NPM1300_BCHGENABLESET_ENABLECHG 0x01
+
+// BCHGENABLECLR
+#define NPM1300_BCHGENABLECLR_DISABLECHG 0x01
+
+// BGCDISABLECLR
+#define NPM1300_BCHGDISABLECLR_ENABLERCHRG 0x01
+#define NPM1300_BCHGDISABLECLR_USENTC 0x02
+
+// BCHGVTERM, BCHGVTERMR
+#define NPM1300_BCHGVTERM_3V60 2 // Default
+#define NPM1300_BCHGVTERM_3V65 3
+
+// ADCCONFIG
+#define NPM1300_ADCCONFIG_AUTOENABLE 0x01
+#define NPM1300_ADCCONFIG_BURSTMODE 0x02
+
+// ADCNTCRSEL
+#define NPM1300_ADCNTCRSEL_HI_Z 0
+#define NPM1300_ADCNTCRSEL_10K 1 // Default
+#define NPM1300_ADCNTCRSEL_47K 2
+#define NPM1300_ADCNTCRSEL_100K 3
+
+// ADCIBATMEASEN
+#define NPM1300_ADCIBATMEASEN_IBATMEASENABLE 0x01
+
+// BCHGVTRICKLESEL
+#define NPM1300_BCHGVTRICKLESEL_2V9 0 // Default
+#define NPM1300_BCHGVTRICKLESEL_2V5 1
+
+// BCHGITERMSEL
+#define NPM1300_BCHGITERMSEL_SEL10 0 // Default
+#define NPM1300_BCHGITERMSEL_SEL20 1
+
+// LEDDRVxMODESEL
+
+#define NPM1300_LEDDRVMODESEL_ERROR 0x00
+#define NPM1300_LEDDRVMODESEL_CHARGING 0x01
+#define NPM1300_LEDDRVMODESEL_HOST 0x02
+#define NPM1300_LEDDRVMODESEL_NOTUSED 0x03
+
+// GPIOMODEx
+
+#define NPM1300_GPIOMODE_GPIINPUT 0x00
+#define NPM1300_GPIOMODE_GPILOGIC1 0x01
+#define NPM1300_GPIOMODE_GPILOGIC0 0x02
+#define NPM1300_GPIOMODE_GPIEVENTRISE 0x03
+#define NPM1300_GPIOMODE_GPIEVENTFALL 0x04
+#define NPM1300_GPIOMODE_GPOIRQ 0x05
+#define NPM1300_GPIOMODE_GPORESET 0x06
+#define NPM1300_GPIOMODE_GPOPLW 0x07
+#define NPM1300_GPIOMODE_GPOLOGIC1 0x08
+#define NPM1300_GPIOMODE_GPOLOGIC9 0x09
+
+#endif // TREZORHAL_NPM1300_DEFS_H
diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revA.py b/core/site_scons/models/T3W1/trezor_t3w1_revA.py
index 3d35ffc592..2790c6f62b 100644
--- a/core/site_scons/models/T3W1/trezor_t3w1_revA.py
+++ b/core/site_scons/models/T3W1/trezor_t3w1_revA.py
@@ -117,6 +117,8 @@ def configure(
"USE_RESET_TO_BOOT=1",
]
+ sources += ["embed/sys/powerctl/npm1300/npm1300.c"]
+
env.get("ENV")["LINKER_SCRIPT"] = linker_script
defs = env.get("CPPDEFINES_IMPLICIT")