From 1803cf62d7cda8018a07b6576ce72da1a6fa9409 Mon Sep 17 00:00:00 2001 From: kopecdav Date: Thu, 2 Jan 2025 17:45:37 +0100 Subject: [PATCH] refactor(core/prodtest): Put patch_and_update function to separate source file --- core/embed/sys/powerctl/stwlc38/stwlc38.c | 451 +----------------- .../sys/powerctl/stwlc38/stwlc38_internal.h | 103 ++++ .../sys/powerctl/stwlc38/stwlc38_patching.c | 414 ++++++++++++++++ .../models/T3W1/trezor_t3w1_revA.py | 1 + 4 files changed, 520 insertions(+), 449 deletions(-) create mode 100644 core/embed/sys/powerctl/stwlc38/stwlc38_internal.h create mode 100644 core/embed/sys/powerctl/stwlc38/stwlc38_patching.c diff --git a/core/embed/sys/powerctl/stwlc38/stwlc38.c b/core/embed/sys/powerctl/stwlc38/stwlc38.c index 92d684bf1f..d8864b7b29 100644 --- a/core/embed/sys/powerctl/stwlc38/stwlc38.c +++ b/core/embed/sys/powerctl/stwlc38/stwlc38.c @@ -25,84 +25,14 @@ #include #include -#include "nvm_data.h" #include "stwlc38.h" +#include "stwlc38_internal.h" #include "stwlc38_defs.h" #ifdef KERNEL_MODE -// !@# TODO: put following constants the board file -#define STWLC38_INT_PIN GPIO_PIN_15 -#define STWLC38_INT_PORT GPIOG -#define STWLC38_INT_PIN_CLK_ENA __HAL_RCC_GPIOG_CLK_ENABLE -#define STWLC38_EXTI_INTERRUPT_GPIOSEL EXTI_GPIOG -#define STWLC38_EXTI_INTERRUPT_LINE EXTI_LINE_15 -#define STWLC38_EXTI_INTERRUPT_NUM EXTI15_IRQn -#define STWLC38_EXTI_INTERRUPT_HANDLER EXTI15_IRQHandler -#define STWLC38_ENB_PIN GPIO_PIN_3 -#define STWLC37_ENB_PORT GPIOD -#define STWLC38_ENB_PIN_CLK_ENA __HAL_RCC_GPIOD_CLK_ENABLE - -// Period of the report readout [ms] -#define STWLC38_REPORT_READOUT_INTERVAL_MS 500 - -// STWLC38 FSM states -typedef enum { - STWLC38_STATE_POWER_DOWN = 0, - STWLC38_STATE_IDLE, - STWLC38_STATE_VOUT_ENABLE, - STWLC38_STATE_VOUT_DISABLE, - STWLC38_STATE_REPORT_READOUT, -} stwlc38_fsm_state_t; - -typedef struct { - // Rectified voltage [mV] - uint16_t vrect; - // Main LDO voltage output [mV] - uint16_t vout; - // Output current [mA] - uint16_t icur; - // Chip temperature [°C * 10] - uint16_t tmeas; - // Operating frequency [kHz] - uint16_t opfreq; - // NTC Temperature [°C * 10] - uint16_t ntc; - // RX Int Status 0 - uint8_t status0; - -} stwlc38_report_regs_t; - -// STWLC38 driver state -typedef struct { - // Set if the driver is initialized - bool initialized; - - // I2C bus where the STWLC38 is connected - i2c_bus_t *i2c_bus; - // Storage for the pending I2C packet - i2c_packet_t pending_i2c_packet; - // Report register (global buffer used for report readout) - stwlc38_report_regs_t report_regs; - // Timer used for periodic report readout - systimer_t *timer; - - // Main LDO output current state - bool vout_enabled; - // Main LDO output requested state - bool vout_enabled_requested; - // Flags set if report readout is scheduled - bool report_readout_requested; - - // Current report - stwlc38_report_t report; - // Current state of the FSM - stwlc38_fsm_state_t state; - -} stwlc38_driver_t; - // STWLC38 driver instance -static stwlc38_driver_t g_stwlc38_driver = { +stwlc38_driver_t g_stwlc38_driver = { .initialized = false, }; @@ -142,95 +72,6 @@ static void stwlc38_timer_callback(void *context); static void stwlc38_i2c_callback(void *context, i2c_packet_t *packet); static void stwlc38_fsm_continue(stwlc38_driver_t *drv); -static i2c_status_t stwlc38_write_fw_register(i2c_bus_t *i2c_bus, - uint16_t address, uint8_t value); -static i2c_status_t stwlc38_write_hw_register(i2c_bus_t *i2c_bus, - uint32_t address, uint8_t vaule); -static i2c_status_t stwlc38_read_fw_register(i2c_bus_t *i2c_bus, - uint16_t address, uint8_t *data); -static i2c_status_t stwlc38_write_n_bytes(i2c_bus_t *i2c_bus, uint16_t address, - uint8_t *data, size_t size); -static i2c_status_t stwlc38_read_n_bytes(i2c_bus_t *i2c_bus, uint16_t address, - uint8_t *data, size_t size); -static i2c_status_t stwlc38_nvm_write_sector(i2c_bus_t *i2c_bus, - const uint8_t *data, size_t size, - uint8_t sec_idx); -static i2c_status_t stwlc38_nvm_write_bulk(i2c_bus_t *i2c_bus, - const uint8_t *data, size_t size, - uint8_t sec_idx); - -bool stwlc38_patch_and_config() { - stwlc38_driver_t *drv = &g_stwlc38_driver; - i2c_status_t status; - - if (!drv->initialized) { - return false; - } - - // Check op mode - uint8_t reg; - status = - stwlc38_read_fw_register(drv->i2c_bus, STWLC38_FWREG_OP_MODE_REG, ®); - if (status != I2C_STATUS_OK) { - return false; - } - - if (reg != OP_MODE_SA) { - return false; - } - - // Reset and disable NVM loading - status = - stwlc38_write_fw_register(drv->i2c_bus, STWLC38_FWREG_SYS_CMD_REG, 0x40); - if (status != I2C_STATUS_OK) { - return false; - } - - systick_delay_ms(STWLC38_RESET_DELAY_MS); - - // Check op mode again - status = - stwlc38_read_fw_register(drv->i2c_bus, STWLC38_FWREG_OP_MODE_REG, ®); - if (status != I2C_STATUS_OK) { - return false; - } - - if (reg != OP_MODE_SA) { - return false; - } - - // Unlock NVM - status = - stwlc38_write_fw_register(drv->i2c_bus, STWLC38_FWREG_NVM_PWD_REG, 0xC5); - if (status != I2C_STATUS_OK) { - return false; - } - - // Write patch to NVM - status = stwlc38_nvm_write_bulk(drv->i2c_bus, patch_data, NVM_PATCH_SIZE, - STWLC38_NVM_PATCH_START_SECTOR_INDEX); - if (status != I2C_STATUS_OK) { - return false; - } - - // Write config to NVM - status = stwlc38_nvm_write_bulk(drv->i2c_bus, cfg_data, NVM_CFG_SIZE, - STWLC38_NVM_CFG_START_SECTOR_INDEX); - if (status != I2C_STATUS_OK) { - return false; - } - - // Reset stwlc38 - status = - stwlc38_write_hw_register(drv->i2c_bus, STWLC38_HWREG_RESET_REG, 0x01); - if (status != I2C_STATUS_OK) { - return false; - } - - systick_delay_ms(STWLC38_RESET_DELAY_MS); - - return true; -} void stwlc38_deinit(void) { stwlc38_driver_t *drv = &g_stwlc38_driver; @@ -347,61 +188,6 @@ bool stwlc38_enable_vout(bool enable) { return true; } -bool stwlc38_read_chip_info(stwlc38_chip_info_t *chip_info) { - stwlc38_driver_t *drv = &g_stwlc38_driver; - - if (!drv->initialized) { - return false; - } - - systimer_key_t lock = systimer_suspend(drv->timer); - - uint8_t raw_data[16]; - - // Read first block of chip information (Address 0x0000 - 0x000F) - i2c_status_t ret = stwlc38_read_n_bytes( - drv->i2c_bus, STWLC38_FWREG_CHIP_ID_REG, (uint8_t *)&raw_data, 15); - if (ret != I2C_STATUS_OK) { - systimer_resume(drv->timer, lock); - return false; - } - - // Parse raw data into chip info structure - chip_info->chip_id = (uint16_t)((raw_data[1] << 8) + raw_data[0]); - chip_info->chip_rev = raw_data[2]; - chip_info->cust_id = raw_data[3]; - chip_info->rom_id = (uint16_t)((raw_data[5] << 8) + raw_data[4]); - chip_info->patch_id = (uint16_t)((raw_data[7] << 8) + raw_data[6]); - chip_info->cfg_id = (uint16_t)((raw_data[11] << 8) + raw_data[10]); - chip_info->pe_id = (uint16_t)((raw_data[13] << 8) + raw_data[12]); - chip_info->op_mode = raw_data[14]; - - // Read second block of chip information - device ID (Address 0x0010 - 0x001F) - ret = stwlc38_read_n_bytes(drv->i2c_bus, STWLC38_FWREG_DEVICE_ID_REG, - (uint8_t *)&raw_data, 16); - if (ret != I2C_STATUS_OK) { - systimer_resume(drv->timer, lock); - return false; - } - - memcpy(&(chip_info->device_id), raw_data, 16); - - // Read third block of chip information - system error (Address 0x002C - - // 0x002F) - ret = stwlc38_read_n_bytes(drv->i2c_bus, STWLC38_FWREG_SYS_ERR_REG, - (uint8_t *)&raw_data, 4); - if (ret != I2C_STATUS_OK) { - systimer_resume(drv->timer, lock); - return false; - } - - memcpy(&(chip_info->sys_err), raw_data, 4); - - systimer_resume(drv->timer, lock); - - return true; -} - // I2C operations for readout of the current state into the // `g_stwlc38.state` structure static const i2c_op_t stwlc38_ops_report_readout[] = { @@ -573,237 +359,4 @@ static void stwlc38_fsm_continue(stwlc38_driver_t *drv) { } } -static i2c_status_t stwlc38_write_fw_register(i2c_bus_t *i2c_bus, - uint16_t address, uint8_t value) { - i2c_status_t status; - - i2c_op_t op[] = { - { - .flags = I2C_FLAG_TX | I2C_FLAG_EMBED, - .size = 3, - .data = {(address) >> 8, (address) & 0xFF, value}, - }, - }; - - i2c_packet_t i2c_pkt = { - .address = STWLC38_I2C_ADDRESS, - .ops = (i2c_op_t *)&op, - .op_count = ARRAY_LENGTH(op), - }; - - status = i2c_bus_submit_and_wait(i2c_bus, &i2c_pkt); - - return status; -} - -static i2c_status_t stwlc38_read_fw_register(i2c_bus_t *i2c_bus, - uint16_t address, uint8_t *data) { - i2c_op_t op[] = { - { - .flags = I2C_FLAG_TX | I2C_FLAG_EMBED, - .size = 2, - .data = {address >> 8, address & 0xFF}, - }, - { - .flags = I2C_FLAG_RX, - .size = 1, - .ptr = data, - }, - }; - - i2c_packet_t i2c_pkt = { - .address = STWLC38_I2C_ADDRESS, - .ops = (i2c_op_t *)&op, - .op_count = ARRAY_LENGTH(op), - }; - - i2c_status_t status = i2c_bus_submit_and_wait(i2c_bus, &i2c_pkt); - - return status; -} - -static i2c_status_t stwlc38_write_hw_register(i2c_bus_t *i2c_bus, - uint32_t address, uint8_t value) { - i2c_op_t op[] = { - { - .flags = I2C_FLAG_TX | I2C_FLAG_EMBED, - .size = 4, - .data = - { - (STWLC38_HWREG_CUT_ID_REG && 0xFF000000) >> 24, - (STWLC38_HWREG_CUT_ID_REG && 0x00FF0000) >> 16, - (STWLC38_HWREG_CUT_ID_REG && 0x0000FF00) >> 8, - (STWLC38_HWREG_CUT_ID_REG && 0x000000FF), - }, - }, - { - .flags = I2C_FLAG_TX | I2C_FLAG_EMBED, - .size = 1, - .data = {value}, - }, - }; - - i2c_packet_t i2c_pkt = { - .address = STWLC38_I2C_ADDRESS, - .ops = (i2c_op_t *)&op, - .op_count = ARRAY_LENGTH(op), - }; - - i2c_status_t status = i2c_bus_submit_and_wait(i2c_bus, &i2c_pkt); - return status; -} - -static i2c_status_t stwlc38_write_n_bytes(i2c_bus_t *i2c_bus, uint16_t address, - uint8_t *data, size_t size) { - i2c_op_t op[] = { - { - .flags = I2C_FLAG_TX | I2C_FLAG_EMBED, - .size = 2, - .data = {(address) >> 8, (address) & 0xFF}, - }, - { - .flags = I2C_FLAG_TX, - .size = size, - .ptr = data, - }, - }; - - i2c_packet_t i2c_pkt = { - .address = STWLC38_I2C_ADDRESS, - .ops = (i2c_op_t *)&op, - .op_count = ARRAY_LENGTH(op), - }; - - i2c_status_t status = i2c_bus_submit_and_wait(i2c_bus, &i2c_pkt); - return status; -} - -static i2c_status_t stwlc38_read_n_bytes(i2c_bus_t *i2c_bus, uint16_t address, - uint8_t *data, size_t size) { - i2c_op_t op[] = { - { - .flags = I2C_FLAG_TX | I2C_FLAG_EMBED, - .size = 2, - .data = {(address) >> 8, (address) & 0xFF}, - }, - { - .flags = I2C_FLAG_RX, - .size = (uint16_t) size, - .ptr = data, - }, - }; - - i2c_packet_t i2c_pkt = { - .address = STWLC38_I2C_ADDRESS, - .ops = (i2c_op_t *)&op, - .op_count = ARRAY_LENGTH(op), - }; - - i2c_status_t status = i2c_bus_submit_and_wait(i2c_bus, &i2c_pkt); - return status; -} - -static i2c_status_t stwlc38_nvm_write_sector(i2c_bus_t *i2c_bus, - const uint8_t *data, size_t size, - uint8_t sec_idx) { - int32_t ret; - int32_t i; - int32_t timeout = 1; - uint8_t reg; - - ret = stwlc38_write_fw_register(i2c_bus, STWLC38_FWREG_NVM_SEC_IDX_REG, - sec_idx); - if (ret != I2C_STATUS_OK) { - return ret; - } - - ret = stwlc38_write_fw_register(i2c_bus, STWLC38_FWREG_SYS_CMD_REG, 0x10); - if (ret != I2C_STATUS_OK) { - return ret; - } - - size_t remaining = size; - int8_t chunk = 0; - while (remaining > 0) { - if (remaining > STWLC38_MAX_WRITE_CHUNK) { - ret = stwlc38_write_n_bytes( - i2c_bus, - STWLC38_FWREG_AUX_DATA_00_REG + chunk * STWLC38_MAX_WRITE_CHUNK, - ((uint8_t *)(data)) + chunk * STWLC38_MAX_WRITE_CHUNK, - STWLC38_MAX_WRITE_CHUNK); - if (ret != I2C_STATUS_OK) { - return ret; - } - - remaining -= STWLC38_MAX_WRITE_CHUNK; - - } else { - ret = stwlc38_write_n_bytes( - i2c_bus, - STWLC38_FWREG_AUX_DATA_00_REG + chunk * STWLC38_MAX_WRITE_CHUNK, - ((uint8_t *)data) + chunk * STWLC38_MAX_WRITE_CHUNK, remaining); - if (ret != I2C_STATUS_OK) { - return ret; - } - - break; - } - - chunk++; - } - - ret = stwlc38_write_fw_register(i2c_bus, STWLC38_FWREG_SYS_CMD_REG, 0x04); - if (ret != I2C_STATUS_OK) { - return ret; - } - - for (i = 0; i < STWLC38_NVM_WRITE_TIMEOUT; i++) { - systick_delay_ms(STWLC38_NVM_WRITE_INTERVAL_MS); - - ret = stwlc38_read_fw_register(i2c_bus, STWLC38_FWREG_SYS_CMD_REG, ®); - if (ret != I2C_STATUS_OK) { - return ret; - } - - if ((reg & 0x04) == 0) { - timeout = 0; - break; - } - } - - ret = stwlc38_write_fw_register(i2c_bus, STWLC38_FWREG_SYS_CMD_REG, 0x20); - if (ret != I2C_STATUS_OK) { - return ret; - } - - return timeout ? I2C_STATUS_TIMEOUT : I2C_STATUS_OK; -} - -static i2c_status_t stwlc38_nvm_write_bulk(i2c_bus_t *i2c_bus, - const uint8_t *data, size_t size, - uint8_t sec_idx) { - int32_t ret; - size_t remaining = size; - int32_t to_write_now = 0; - int32_t written_already = 0; - - while (remaining > 0) { - to_write_now = remaining > STWLC38_NVM_SECTOR_BYTE_SIZE - ? STWLC38_NVM_SECTOR_BYTE_SIZE - : remaining; - - ret = stwlc38_nvm_write_sector(i2c_bus, data + written_already, - to_write_now, sec_idx); - if (ret != I2C_STATUS_OK) { - return ret; - } - - remaining -= to_write_now; - written_already += to_write_now; - sec_idx++; - } - - return I2C_STATUS_OK; -} - #endif // KERNEL_MODE diff --git a/core/embed/sys/powerctl/stwlc38/stwlc38_internal.h b/core/embed/sys/powerctl/stwlc38/stwlc38_internal.h new file mode 100644 index 0000000000..18d37078bf --- /dev/null +++ b/core/embed/sys/powerctl/stwlc38/stwlc38_internal.h @@ -0,0 +1,103 @@ + +/* + * 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_STWLC38_INTERNAL_H +#define TREZORHAL_STWLC38_INTERNAL_H + +#include +#include + +#ifdef KERNEL_MODE + +// !@# TODO: put following constants the board file +#define STWLC38_INT_PIN GPIO_PIN_15 +#define STWLC38_INT_PORT GPIOG +#define STWLC38_INT_PIN_CLK_ENA __HAL_RCC_GPIOG_CLK_ENABLE +#define STWLC38_EXTI_INTERRUPT_GPIOSEL EXTI_GPIOG +#define STWLC38_EXTI_INTERRUPT_LINE EXTI_LINE_15 +#define STWLC38_EXTI_INTERRUPT_NUM EXTI15_IRQn +#define STWLC38_EXTI_INTERRUPT_HANDLER EXTI15_IRQHandler +#define STWLC38_ENB_PIN GPIO_PIN_3 +#define STWLC37_ENB_PORT GPIOD +#define STWLC38_ENB_PIN_CLK_ENA __HAL_RCC_GPIOD_CLK_ENABLE + +// Period of the report readout [ms] +#define STWLC38_REPORT_READOUT_INTERVAL_MS 500 + +// STWLC38 FSM states +typedef enum { + STWLC38_STATE_POWER_DOWN = 0, + STWLC38_STATE_IDLE, + STWLC38_STATE_VOUT_ENABLE, + STWLC38_STATE_VOUT_DISABLE, + STWLC38_STATE_REPORT_READOUT, +} stwlc38_fsm_state_t; + +typedef struct { + // Rectified voltage [mV] + uint16_t vrect; + // Main LDO voltage output [mV] + uint16_t vout; + // Output current [mA] + uint16_t icur; + // Chip temperature [°C * 10] + uint16_t tmeas; + // Operating frequency [kHz] + uint16_t opfreq; + // NTC Temperature [°C * 10] + uint16_t ntc; + // RX Int Status 0 + uint8_t status0; + +} stwlc38_report_regs_t; + +// STWLC38 driver state +typedef struct { + // Set if the driver is initialized + bool initialized; + + // I2C bus where the STWLC38 is connected + i2c_bus_t *i2c_bus; + // Storage for the pending I2C packet + i2c_packet_t pending_i2c_packet; + // Report register (global buffer used for report readout) + stwlc38_report_regs_t report_regs; + // Timer used for periodic report readout + systimer_t *timer; + + // Main LDO output current state + bool vout_enabled; + // Main LDO output requested state + bool vout_enabled_requested; + // Flags set if report readout is scheduled + bool report_readout_requested; + + // Current report + stwlc38_report_t report; + // Current state of the FSM + stwlc38_fsm_state_t state; + +} stwlc38_driver_t; + +extern stwlc38_driver_t g_stwlc38_driver; + +#endif // KERNEL_MODE + +#endif // TREZORHAL_STWLC38_INTERNAL_H \ No newline at end of file diff --git a/core/embed/sys/powerctl/stwlc38/stwlc38_patching.c b/core/embed/sys/powerctl/stwlc38/stwlc38_patching.c new file mode 100644 index 0000000000..6768446e3b --- /dev/null +++ b/core/embed/sys/powerctl/stwlc38/stwlc38_patching.c @@ -0,0 +1,414 @@ +/* + * 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 . + */ + +#ifdef KERNEL_MODE + +#include +#include + +#include +#include +#include +#include + +#include "nvm_data.h" +#include "stwlc38.h" +#include "stwlc38_internal.h" +#include "stwlc38_defs.h" + +static i2c_status_t stwlc38_write_fw_register(i2c_bus_t *i2c_bus, + uint16_t address, uint8_t value); +static i2c_status_t stwlc38_write_hw_register(i2c_bus_t *i2c_bus, + uint32_t address, uint8_t vaule); +static i2c_status_t stwlc38_read_fw_register(i2c_bus_t *i2c_bus, + uint16_t address, uint8_t *data); +static i2c_status_t stwlc38_write_n_bytes(i2c_bus_t *i2c_bus, uint16_t address, + uint8_t *data, size_t size); +static i2c_status_t stwlc38_read_n_bytes(i2c_bus_t *i2c_bus, uint16_t address, + uint8_t *data, size_t size); +static i2c_status_t stwlc38_nvm_write_sector(i2c_bus_t *i2c_bus, + const uint8_t *data, size_t size, + uint8_t sec_idx); +static i2c_status_t stwlc38_nvm_write_bulk(i2c_bus_t *i2c_bus, + const uint8_t *data, size_t size, + uint8_t sec_idx); + +bool stwlc38_patch_and_config() { + stwlc38_driver_t *drv = &g_stwlc38_driver; + i2c_status_t status; + + if (!drv->initialized) { + return false; + } + + // Check op mode + uint8_t reg; + status = + stwlc38_read_fw_register(drv->i2c_bus, STWLC38_FWREG_OP_MODE_REG, ®); + if (status != I2C_STATUS_OK) { + return false; + } + + if (reg != OP_MODE_SA) { + return false; + } + + // Reset and disable NVM loading + status = + stwlc38_write_fw_register(drv->i2c_bus, STWLC38_FWREG_SYS_CMD_REG, 0x40); + if (status != I2C_STATUS_OK) { + return false; + } + + systick_delay_ms(STWLC38_RESET_DELAY_MS); + + // Check op mode again + status = + stwlc38_read_fw_register(drv->i2c_bus, STWLC38_FWREG_OP_MODE_REG, ®); + if (status != I2C_STATUS_OK) { + return false; + } + + if (reg != OP_MODE_SA) { + return false; + } + + // Unlock NVM + status = + stwlc38_write_fw_register(drv->i2c_bus, STWLC38_FWREG_NVM_PWD_REG, 0xC5); + if (status != I2C_STATUS_OK) { + return false; + } + + // Write patch to NVM + status = stwlc38_nvm_write_bulk(drv->i2c_bus, patch_data, NVM_PATCH_SIZE, + STWLC38_NVM_PATCH_START_SECTOR_INDEX); + if (status != I2C_STATUS_OK) { + return false; + } + + // Write config to NVM + status = stwlc38_nvm_write_bulk(drv->i2c_bus, cfg_data, NVM_CFG_SIZE, + STWLC38_NVM_CFG_START_SECTOR_INDEX); + if (status != I2C_STATUS_OK) { + return false; + } + + // Reset stwlc38 + status = + stwlc38_write_hw_register(drv->i2c_bus, STWLC38_HWREG_RESET_REG, 0x01); + if (status != I2C_STATUS_OK) { + return false; + } + + systick_delay_ms(STWLC38_RESET_DELAY_MS); + + return true; +} + +bool stwlc38_read_chip_info(stwlc38_chip_info_t *chip_info) { + stwlc38_driver_t *drv = &g_stwlc38_driver; + + if (!drv->initialized) { + return false; + } + + systimer_key_t lock = systimer_suspend(drv->timer); + + uint8_t raw_data[16]; + + // Read first block of chip information (Address 0x0000 - 0x000F) + i2c_status_t ret = stwlc38_read_n_bytes( + drv->i2c_bus, STWLC38_FWREG_CHIP_ID_REG, (uint8_t *)&raw_data, 15); + if (ret != I2C_STATUS_OK) { + systimer_resume(drv->timer, lock); + return false; + } + + // Parse raw data into chip info structure + chip_info->chip_id = (uint16_t)((raw_data[1] << 8) + raw_data[0]); + chip_info->chip_rev = raw_data[2]; + chip_info->cust_id = raw_data[3]; + chip_info->rom_id = (uint16_t)((raw_data[5] << 8) + raw_data[4]); + chip_info->patch_id = (uint16_t)((raw_data[7] << 8) + raw_data[6]); + chip_info->cfg_id = (uint16_t)((raw_data[11] << 8) + raw_data[10]); + chip_info->pe_id = (uint16_t)((raw_data[13] << 8) + raw_data[12]); + chip_info->op_mode = raw_data[14]; + + // Read second block of chip information - device ID (Address 0x0010 - 0x001F) + ret = stwlc38_read_n_bytes(drv->i2c_bus, STWLC38_FWREG_DEVICE_ID_REG, + (uint8_t *)&raw_data, 16); + if (ret != I2C_STATUS_OK) { + systimer_resume(drv->timer, lock); + return false; + } + + memcpy(&(chip_info->device_id), raw_data, 16); + + // Read third block of chip information - system error (Address 0x002C - + // 0x002F) + ret = stwlc38_read_n_bytes(drv->i2c_bus, STWLC38_FWREG_SYS_ERR_REG, + (uint8_t *)&raw_data, 4); + if (ret != I2C_STATUS_OK) { + systimer_resume(drv->timer, lock); + return false; + } + + memcpy(&(chip_info->sys_err), raw_data, 4); + + systimer_resume(drv->timer, lock); + + return true; +} + +static i2c_status_t stwlc38_write_fw_register(i2c_bus_t *i2c_bus, + uint16_t address, uint8_t value) { + i2c_status_t status; + + i2c_op_t op[] = { + { + .flags = I2C_FLAG_TX | I2C_FLAG_EMBED, + .size = 3, + .data = {(address) >> 8, (address) & 0xFF, value}, + }, + }; + + i2c_packet_t i2c_pkt = { + .address = STWLC38_I2C_ADDRESS, + .ops = (i2c_op_t *)&op, + .op_count = ARRAY_LENGTH(op), + }; + + status = i2c_bus_submit_and_wait(i2c_bus, &i2c_pkt); + + return status; +} + +static i2c_status_t stwlc38_read_fw_register(i2c_bus_t *i2c_bus, + uint16_t address, uint8_t *data) { + i2c_op_t op[] = { + { + .flags = I2C_FLAG_TX | I2C_FLAG_EMBED, + .size = 2, + .data = {address >> 8, address & 0xFF}, + }, + { + .flags = I2C_FLAG_RX, + .size = 1, + .ptr = data, + }, + }; + + i2c_packet_t i2c_pkt = { + .address = STWLC38_I2C_ADDRESS, + .ops = (i2c_op_t *)&op, + .op_count = ARRAY_LENGTH(op), + }; + + i2c_status_t status = i2c_bus_submit_and_wait(i2c_bus, &i2c_pkt); + + return status; +} + +static i2c_status_t stwlc38_write_hw_register(i2c_bus_t *i2c_bus, + uint32_t address, uint8_t value) { + i2c_op_t op[] = { + { + .flags = I2C_FLAG_TX | I2C_FLAG_EMBED, + .size = 4, + .data = + { + (STWLC38_HWREG_CUT_ID_REG && 0xFF000000) >> 24, + (STWLC38_HWREG_CUT_ID_REG && 0x00FF0000) >> 16, + (STWLC38_HWREG_CUT_ID_REG && 0x0000FF00) >> 8, + (STWLC38_HWREG_CUT_ID_REG && 0x000000FF), + }, + }, + { + .flags = I2C_FLAG_TX | I2C_FLAG_EMBED, + .size = 1, + .data = {value}, + }, + }; + + i2c_packet_t i2c_pkt = { + .address = STWLC38_I2C_ADDRESS, + .ops = (i2c_op_t *)&op, + .op_count = ARRAY_LENGTH(op), + }; + + i2c_status_t status = i2c_bus_submit_and_wait(i2c_bus, &i2c_pkt); + return status; +} + +static i2c_status_t stwlc38_write_n_bytes(i2c_bus_t *i2c_bus, uint16_t address, + uint8_t *data, size_t size) { + i2c_op_t op[] = { + { + .flags = I2C_FLAG_TX | I2C_FLAG_EMBED, + .size = 2, + .data = {(address) >> 8, (address) & 0xFF}, + }, + { + .flags = I2C_FLAG_TX, + .size = size, + .ptr = data, + }, + }; + + i2c_packet_t i2c_pkt = { + .address = STWLC38_I2C_ADDRESS, + .ops = (i2c_op_t *)&op, + .op_count = ARRAY_LENGTH(op), + }; + + i2c_status_t status = i2c_bus_submit_and_wait(i2c_bus, &i2c_pkt); + return status; +} + +static i2c_status_t stwlc38_read_n_bytes(i2c_bus_t *i2c_bus, uint16_t address, + uint8_t *data, size_t size) { + i2c_op_t op[] = { + { + .flags = I2C_FLAG_TX | I2C_FLAG_EMBED, + .size = 2, + .data = {(address) >> 8, (address) & 0xFF}, + }, + { + .flags = I2C_FLAG_RX, + .size = (uint16_t) size, + .ptr = data, + }, + }; + + i2c_packet_t i2c_pkt = { + .address = STWLC38_I2C_ADDRESS, + .ops = (i2c_op_t *)&op, + .op_count = ARRAY_LENGTH(op), + }; + + i2c_status_t status = i2c_bus_submit_and_wait(i2c_bus, &i2c_pkt); + return status; +} + +static i2c_status_t stwlc38_nvm_write_sector(i2c_bus_t *i2c_bus, + const uint8_t *data, size_t size, + uint8_t sec_idx) { + int32_t ret; + int32_t i; + int32_t timeout = 1; + uint8_t reg; + + ret = stwlc38_write_fw_register(i2c_bus, STWLC38_FWREG_NVM_SEC_IDX_REG, + sec_idx); + if (ret != I2C_STATUS_OK) { + return ret; + } + + ret = stwlc38_write_fw_register(i2c_bus, STWLC38_FWREG_SYS_CMD_REG, 0x10); + if (ret != I2C_STATUS_OK) { + return ret; + } + + size_t remaining = size; + int8_t chunk = 0; + while (remaining > 0) { + if (remaining > STWLC38_MAX_WRITE_CHUNK) { + ret = stwlc38_write_n_bytes( + i2c_bus, + STWLC38_FWREG_AUX_DATA_00_REG + chunk * STWLC38_MAX_WRITE_CHUNK, + ((uint8_t *)(data)) + chunk * STWLC38_MAX_WRITE_CHUNK, + STWLC38_MAX_WRITE_CHUNK); + if (ret != I2C_STATUS_OK) { + return ret; + } + + remaining -= STWLC38_MAX_WRITE_CHUNK; + + } else { + ret = stwlc38_write_n_bytes( + i2c_bus, + STWLC38_FWREG_AUX_DATA_00_REG + chunk * STWLC38_MAX_WRITE_CHUNK, + ((uint8_t *)data) + chunk * STWLC38_MAX_WRITE_CHUNK, remaining); + if (ret != I2C_STATUS_OK) { + return ret; + } + + break; + } + + chunk++; + } + + ret = stwlc38_write_fw_register(i2c_bus, STWLC38_FWREG_SYS_CMD_REG, 0x04); + if (ret != I2C_STATUS_OK) { + return ret; + } + + for (i = 0; i < STWLC38_NVM_WRITE_TIMEOUT; i++) { + systick_delay_ms(STWLC38_NVM_WRITE_INTERVAL_MS); + + ret = stwlc38_read_fw_register(i2c_bus, STWLC38_FWREG_SYS_CMD_REG, ®); + if (ret != I2C_STATUS_OK) { + return ret; + } + + if ((reg & 0x04) == 0) { + timeout = 0; + break; + } + } + + ret = stwlc38_write_fw_register(i2c_bus, STWLC38_FWREG_SYS_CMD_REG, 0x20); + if (ret != I2C_STATUS_OK) { + return ret; + } + + return timeout ? I2C_STATUS_TIMEOUT : I2C_STATUS_OK; +} + +static i2c_status_t stwlc38_nvm_write_bulk(i2c_bus_t *i2c_bus, + const uint8_t *data, size_t size, + uint8_t sec_idx) { + int32_t ret; + size_t remaining = size; + int32_t to_write_now = 0; + int32_t written_already = 0; + + while (remaining > 0) { + to_write_now = remaining > STWLC38_NVM_SECTOR_BYTE_SIZE + ? STWLC38_NVM_SECTOR_BYTE_SIZE + : remaining; + + ret = stwlc38_nvm_write_sector(i2c_bus, data + written_already, + to_write_now, sec_idx); + if (ret != I2C_STATUS_OK) { + return ret; + } + + remaining -= to_write_now; + written_already += to_write_now; + sec_idx++; + } + + return I2C_STATUS_OK; +} + + +#endif // KERNEL_MODE \ No newline at end of file diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revA.py b/core/site_scons/models/T3W1/trezor_t3w1_revA.py index 569cebfbcb..97a0b0b627 100644 --- a/core/site_scons/models/T3W1/trezor_t3w1_revA.py +++ b/core/site_scons/models/T3W1/trezor_t3w1_revA.py @@ -156,6 +156,7 @@ def configure( sources += [ "embed/sys/powerctl/npm1300/npm1300.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",