mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-02 19:40:57 +00:00
feat(core/prodtest): Add stwlc38 update from host feature [no changelog]
This commit is contained in:
parent
f10dc86da2
commit
e3845a6420
@ -971,7 +971,55 @@ void test_pmic(const char *args) {
|
||||
void test_wpc(const char *args) {
|
||||
stwlc38_init();
|
||||
|
||||
if (strcmp(args, "EN") == 0) {
|
||||
if (strcmp(args, "UPDATE") == 0) {
|
||||
vcp_println("Trying to update WLC32 ... ");
|
||||
|
||||
uint32_t update_time_ms = systick_ms();
|
||||
bool status = stwlc38_patch_and_config();
|
||||
update_time_ms = systick_ms() - update_time_ms;
|
||||
|
||||
if (status == false) {
|
||||
vcp_println("Some problem occured");
|
||||
} else {
|
||||
vcp_println("WPC update completed {%d ms}", update_time_ms);
|
||||
vcp_println("OK");
|
||||
}
|
||||
|
||||
} else if (strcmp(args, "CHIP_INFO") == 0) {
|
||||
stwlc38_chip_info_t chip_info;
|
||||
|
||||
if (!stwlc38_read_chip_info(&chip_info)) {
|
||||
vcp_println("ERROR # STWLC38 not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
vcp_println("chip_id 0x%d", chip_info.chip_id);
|
||||
vcp_println("chip_rev 0x%d ", chip_info.chip_rev);
|
||||
vcp_println("cust_id 0x%d ", chip_info.cust_id);
|
||||
vcp_println("rom_id 0x%X ", chip_info.rom_id);
|
||||
vcp_println("patch_id 0x%X ", chip_info.patch_id);
|
||||
vcp_println("cfg_id 0x%X ", chip_info.cfg_id);
|
||||
vcp_println("pe_id 0x%X ", chip_info.pe_id);
|
||||
vcp_println("op_mode 0x%X ", chip_info.op_mode);
|
||||
|
||||
vcp_print("device_id : ");
|
||||
for (uint8_t i = 0; i < sizeof(chip_info.device_id); i++) {
|
||||
vcp_print("%x", chip_info.device_id[i]);
|
||||
}
|
||||
vcp_println("");
|
||||
|
||||
vcp_println("sys_err 0x%X ", chip_info.sys_err);
|
||||
vcp_println(" - core_hard_fault: 0x%X ", chip_info.core_hard_fault);
|
||||
vcp_println(" - nvm_ip_err: 0x%X ", chip_info.nvm_ip_err);
|
||||
vcp_println(" - nvm_boot_err: 0x%X ", chip_info.nvm_boot_err);
|
||||
vcp_println(" - nvm_pe_error: 0x%X ", chip_info.nvm_pe_error);
|
||||
vcp_println(" - nvm_config_err: 0x%X ", chip_info.nvm_config_err);
|
||||
vcp_println(" - nvm_patch_err: 0x%X ", chip_info.nvm_patch_err);
|
||||
vcp_println(" - nvm_prod_info_err: 0x%X ", chip_info.nvm_prod_info_err);
|
||||
|
||||
vcp_println("OK");
|
||||
|
||||
} else if (strcmp(args, "EN") == 0) {
|
||||
if (!stwlc38_enable(true)) {
|
||||
vcp_println("ERROR # STWLC38 not initialized");
|
||||
return;
|
||||
|
1116
core/embed/sys/powerctl/stwlc38/nvm_data.h
Normal file
1116
core/embed/sys/powerctl/stwlc38/nvm_data.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -22,8 +22,10 @@
|
||||
|
||||
#include <io/i2c_bus.h>
|
||||
#include <sys/irq.h>
|
||||
#include <sys/systick.h>
|
||||
#include <sys/systimer.h>
|
||||
|
||||
#include "nvm_data.h"
|
||||
#include "stwlc38.h"
|
||||
#include "stwlc38_defs.h"
|
||||
|
||||
@ -77,13 +79,13 @@ typedef struct {
|
||||
bool initialized;
|
||||
|
||||
// I2C bus where the STWLC38 is connected
|
||||
i2c_bus_t* i2c_bus;
|
||||
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;
|
||||
systimer_t *timer;
|
||||
|
||||
// Main LDO output current state
|
||||
bool vout_enabled;
|
||||
@ -104,13 +106,127 @@ static stwlc38_driver_t g_stwlc38_driver = {
|
||||
.initialized = false,
|
||||
};
|
||||
|
||||
// I2C operation for writing 8-bit constant value to the STWLC38 register
|
||||
#define STWLC_WRITE_CONST8(reg, value) \
|
||||
{ \
|
||||
.flags = I2C_FLAG_TX | I2C_FLAG_EMBED | I2C_FLAG_START, .size = 3, \
|
||||
.data = {(reg) >> 8, (reg) & 0xFF, (value)}, \
|
||||
}
|
||||
|
||||
// I2C operations for reading 16-bit STWLC38 register into the
|
||||
// specified field in `g_stwlc38_driver` structure
|
||||
#define STWLC_READ_FIELD16(reg, field) \
|
||||
{ \
|
||||
.flags = I2C_FLAG_TX | I2C_FLAG_EMBED | I2C_FLAG_START, \
|
||||
.size = 2, \
|
||||
.data = {(reg) >> 8, (reg) & 0xFF}, \
|
||||
}, \
|
||||
{ \
|
||||
.flags = I2C_FLAG_RX, .size = 2, .ptr = &g_stwlc38_driver.field, \
|
||||
}
|
||||
|
||||
// I2C operations for reading 8-bit STWLC38 register into the
|
||||
// specified field in `g_stwlc38_driver` structure
|
||||
#define STWLC_READ_FIELD8(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_stwlc38_driver.field, \
|
||||
}
|
||||
|
||||
// forward declarations
|
||||
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 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, uint16_t size);
|
||||
static i2c_status_t stwlc38_read_n_bytes(i2c_bus_t *i2c_bus, uint16_t address,
|
||||
uint8_t *data, uint16_t size);
|
||||
static int32_t stwlc38_nvm_write_sector(i2c_bus_t *i2c_bus, const uint8_t *data,
|
||||
int32_t len, uint8_t sec_idx);
|
||||
static int32_t stwlc38_nvm_write_bulk(i2c_bus_t *i2c_bus, const uint8_t *data,
|
||||
int32_t len, 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);
|
||||
|
||||
systick_delay_ms(STWLC38_RESET_DELAY_MS);
|
||||
|
||||
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
|
||||
int32_t ret = stwlc38_nvm_write_bulk(drv->i2c_bus, patch_data, NVM_PATCH_SIZE,
|
||||
STWLC38_NVM_PATCH_START_SECTOR_INDEX);
|
||||
if (ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write config to NVM
|
||||
ret = stwlc38_nvm_write_bulk(drv->i2c_bus, cfg_data, NVM_CFG_SIZE,
|
||||
STWLC38_NVM_CFG_START_SECTOR_INDEX);
|
||||
if (ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
stwlc38_driver_t *drv = &g_stwlc38_driver;
|
||||
|
||||
i2c_bus_close(drv->i2c_bus);
|
||||
systimer_delete(drv->timer);
|
||||
@ -118,7 +234,7 @@ void stwlc38_deinit(void) {
|
||||
}
|
||||
|
||||
bool stwlc38_init(void) {
|
||||
stwlc38_driver_t* drv = &g_stwlc38_driver;
|
||||
stwlc38_driver_t *drv = &g_stwlc38_driver;
|
||||
|
||||
if (drv->initialized) {
|
||||
return true;
|
||||
@ -190,7 +306,7 @@ cleanup:
|
||||
}
|
||||
|
||||
bool stwlc38_enable(bool enable) {
|
||||
stwlc38_driver_t* drv = &g_stwlc38_driver;
|
||||
stwlc38_driver_t *drv = &g_stwlc38_driver;
|
||||
|
||||
if (!drv->initialized) {
|
||||
return false;
|
||||
@ -206,7 +322,7 @@ bool stwlc38_enable(bool enable) {
|
||||
}
|
||||
|
||||
bool stwlc38_enable_vout(bool enable) {
|
||||
stwlc38_driver_t* drv = &g_stwlc38_driver;
|
||||
stwlc38_driver_t *drv = &g_stwlc38_driver;
|
||||
|
||||
if (!drv->initialized) {
|
||||
return false;
|
||||
@ -224,37 +340,61 @@ bool stwlc38_enable_vout(bool enable) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// I2C operation for writing 8-bit constant value to the STWLC38 register
|
||||
#define STWLC_WRITE_CONST8(reg, value) \
|
||||
{ \
|
||||
.flags = I2C_FLAG_TX | I2C_FLAG_EMBED | I2C_FLAG_START, .size = 3, \
|
||||
.data = {(reg) >> 8, (reg) & 0xFF, (value)}, \
|
||||
bool stwlc38_read_chip_info(stwlc38_chip_info_t *chip_info) {
|
||||
stwlc38_driver_t *drv = &g_stwlc38_driver;
|
||||
|
||||
if (!drv->initialized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// I2C operations for reading 16-bit STWLC38 register into the
|
||||
// specified field in `g_stwlc38_driver` structure
|
||||
#define STWLC_READ_FIELD16(reg, field) \
|
||||
{ \
|
||||
.flags = I2C_FLAG_TX | I2C_FLAG_EMBED | I2C_FLAG_START, \
|
||||
.size = 2, \
|
||||
.data = {(reg) >> 8, (reg) & 0xFF}, \
|
||||
}, \
|
||||
{ \
|
||||
.flags = I2C_FLAG_RX, .size = 2, .ptr = &g_stwlc38_driver.field, \
|
||||
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;
|
||||
}
|
||||
|
||||
// I2C operations for reading 8-bit STWLC38 register into the
|
||||
// specified field in `g_stwlc38_driver` structure
|
||||
#define STWLC_READ_FIELD8(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_stwlc38_driver.field, \
|
||||
// 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[] = {
|
||||
@ -281,28 +421,27 @@ static const i2c_op_t stwlc38_ops_vout_disable[] = {
|
||||
_stwlc38_i2c_submit(drv, ops, ARRAY_LENGTH(ops))
|
||||
|
||||
// helper function for submitting I2C operations
|
||||
static void _stwlc38_i2c_submit(stwlc38_driver_t* drv, const i2c_op_t* ops,
|
||||
static void _stwlc38_i2c_submit(stwlc38_driver_t *drv, const i2c_op_t *ops,
|
||||
size_t op_count) {
|
||||
i2c_packet_t* pkt = &drv->pending_i2c_packet;
|
||||
i2c_packet_t *pkt = &drv->pending_i2c_packet;
|
||||
|
||||
memset(pkt, 0, sizeof(i2c_packet_t));
|
||||
pkt->address = STWLC38_I2C_ADDRESS;
|
||||
pkt->context = drv;
|
||||
pkt->callback = stwlc38_i2c_callback;
|
||||
pkt->timeout = 0;
|
||||
pkt->ops = (i2c_op_t*)ops;
|
||||
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("STWLC38 I2C submit error");
|
||||
error_shutdown("STWLC32 I2C submit error");
|
||||
}
|
||||
}
|
||||
|
||||
bool stwlc38_get_report(stwlc38_report_t* report) {
|
||||
stwlc38_driver_t* drv = &g_stwlc38_driver;
|
||||
bool stwlc38_get_report(stwlc38_report_t *report) {
|
||||
stwlc38_driver_t *drv = &g_stwlc38_driver;
|
||||
|
||||
if (!drv->initialized) {
|
||||
return false;
|
||||
@ -315,16 +454,16 @@ bool stwlc38_get_report(stwlc38_report_t* report) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void stwlc38_timer_callback(void* context) {
|
||||
stwlc38_driver_t* drv = (stwlc38_driver_t*)context;
|
||||
static void stwlc38_timer_callback(void *context) {
|
||||
stwlc38_driver_t *drv = (stwlc38_driver_t *)context;
|
||||
|
||||
// Schedule the report readout
|
||||
drv->report_readout_requested = true;
|
||||
stwlc38_fsm_continue(drv);
|
||||
}
|
||||
|
||||
static void stwlc38_i2c_callback(void* context, i2c_packet_t* packet) {
|
||||
stwlc38_driver_t* drv = (stwlc38_driver_t*)context;
|
||||
static void stwlc38_i2c_callback(void *context, i2c_packet_t *packet) {
|
||||
stwlc38_driver_t *drv = (stwlc38_driver_t *)context;
|
||||
|
||||
if (packet->status != I2C_STATUS_OK) {
|
||||
memset(&drv->report, 0, sizeof(stwlc38_report_t));
|
||||
@ -384,7 +523,7 @@ static void stwlc38_i2c_callback(void* context, i2c_packet_t* packet) {
|
||||
}
|
||||
|
||||
void STWLC38_EXTI_INTERRUPT_HANDLER(void) {
|
||||
stwlc38_driver_t* drv = &g_stwlc38_driver;
|
||||
stwlc38_driver_t *drv = &g_stwlc38_driver;
|
||||
|
||||
// Clear the EXTI line pending bit
|
||||
__HAL_GPIO_EXTI_CLEAR_FLAG(STWLC38_INT_PIN);
|
||||
@ -397,7 +536,7 @@ void STWLC38_EXTI_INTERRUPT_HANDLER(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static void stwlc38_fsm_continue(stwlc38_driver_t* drv) {
|
||||
static void stwlc38_fsm_continue(stwlc38_driver_t *drv) {
|
||||
// The order of the following conditions defines the priority
|
||||
|
||||
if (drv->state == STWLC38_STATE_POWER_DOWN && drv->report_readout_requested) {
|
||||
@ -427,4 +566,180 @@ 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 | I2C_FLAG_START | I2C_FLAG_STOP,
|
||||
.size = 3,
|
||||
.data = {(address) >> 8, (address) & 0xFF, value}},
|
||||
};
|
||||
|
||||
i2c_packet_t i2c_pkt = {.address = STWLC38_I2C_ADDRESS,
|
||||
.context = NULL,
|
||||
.callback = NULL,
|
||||
.timeout = 0,
|
||||
.ops = (i2c_op_t *)&op,
|
||||
.op_count = 1};
|
||||
|
||||
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 | I2C_FLAG_START,
|
||||
.size = 2,
|
||||
.data = {address >> 8, address & 0xFF}},
|
||||
{.flags = I2C_FLAG_RX | I2C_FLAG_STOP, .size = 1, .ptr = data}};
|
||||
|
||||
i2c_packet_t i2c_pkt = {.address = STWLC38_I2C_ADDRESS,
|
||||
.context = NULL,
|
||||
.callback = NULL,
|
||||
.timeout = 0,
|
||||
.ops = (i2c_op_t *)&op,
|
||||
.op_count = 2};
|
||||
|
||||
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 | I2C_FLAG_START,
|
||||
.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 && 0x0000FF00) >> 8}},
|
||||
{.flags = I2C_FLAG_TX | I2C_FLAG_EMBED | I2C_FLAG_STOP,
|
||||
.size = 1,
|
||||
.data = {value}}};
|
||||
|
||||
i2c_packet_t i2c_pkt = {.address = STWLC38_I2C_ADDRESS,
|
||||
.context = NULL,
|
||||
.callback = NULL,
|
||||
.timeout = 0,
|
||||
.ops = (i2c_op_t *)&op,
|
||||
.op_count = 2};
|
||||
|
||||
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, uint16_t len) {
|
||||
i2c_op_t op[] = {
|
||||
{.flags = I2C_FLAG_TX | I2C_FLAG_EMBED | I2C_FLAG_START,
|
||||
.size = 2,
|
||||
.data = {(address) >> 8, (address) & 0xFF}},
|
||||
{.flags = I2C_FLAG_TX | I2C_FLAG_STOP, .size = len, .ptr = data}};
|
||||
|
||||
i2c_packet_t i2c_pkt = {.address = STWLC38_I2C_ADDRESS,
|
||||
.context = NULL,
|
||||
.callback = NULL,
|
||||
.timeout = 0,
|
||||
.ops = (i2c_op_t *)&op,
|
||||
.op_count = 2};
|
||||
|
||||
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, uint16_t len) {
|
||||
i2c_op_t op[] = {
|
||||
{.flags = I2C_FLAG_TX | I2C_FLAG_EMBED | I2C_FLAG_START,
|
||||
.size = 2,
|
||||
.data = {(address) >> 8, (address) & 0xFF}},
|
||||
{.flags = I2C_FLAG_RX | I2C_FLAG_STOP, .size = len, .ptr = data}};
|
||||
|
||||
i2c_packet_t i2c_pkt = {.address = STWLC38_I2C_ADDRESS,
|
||||
.context = NULL,
|
||||
.callback = NULL,
|
||||
.timeout = 0,
|
||||
.ops = (i2c_op_t *)&op,
|
||||
.op_count = 2};
|
||||
|
||||
i2c_status_t status = i2c_bus_submit_and_wait(i2c_bus, &i2c_pkt);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int32_t stwlc38_nvm_write_sector(i2c_bus_t *i2c_bus, const uint8_t *data,
|
||||
int32_t len, 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);
|
||||
ret |= stwlc38_write_fw_register(i2c_bus, STWLC38_FWREG_SYS_CMD_REG, 0x10);
|
||||
|
||||
int16_t remaining = (int16_t)len;
|
||||
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);
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
||||
chunk++;
|
||||
}
|
||||
|
||||
ret |= stwlc38_write_fw_register(i2c_bus, STWLC38_FWREG_SYS_CMD_REG, 0x04);
|
||||
|
||||
if (ret) 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) return ret;
|
||||
if ((reg & 0x04) == 0) {
|
||||
timeout = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = stwlc38_write_fw_register(i2c_bus, STWLC38_FWREG_SYS_CMD_REG, 0x20);
|
||||
return timeout == 0 ? ret : STWLC38_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
static int32_t stwlc38_nvm_write_bulk(i2c_bus_t *i2c_bus, const uint8_t *data,
|
||||
int32_t len, uint8_t sec_idx) {
|
||||
int32_t ret;
|
||||
int32_t remaining = len;
|
||||
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) return ret;
|
||||
remaining -= to_write_now;
|
||||
written_already += to_write_now;
|
||||
sec_idx++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // KERNEL_MODE
|
||||
|
@ -22,6 +22,112 @@
|
||||
|
||||
#include <trezor_types.h>
|
||||
|
||||
/* Common_Definitions */
|
||||
#define STWLC38_MAX_READ_CHUNK 500U
|
||||
#define STWLC38_MAX_WRITE_CHUNK 250U
|
||||
#define STWLC38_LOG_SIZE 1024U
|
||||
#define STWLC38_GENERAL_POLLING_MS 50U
|
||||
#define STWLC38_GENERAL_TIMEOUT 100U
|
||||
#define STWLC38_RESET_DELAY_MS 50U
|
||||
|
||||
/* NVM Definitions */
|
||||
#define STWLC38_NVM_WRITE_INTERVAL_MS 1U
|
||||
#define STWLC38_NVM_WRITE_TIMEOUT 20U
|
||||
#define STWLC38_NVM_UPDATE_MAX_RETRY 3U
|
||||
|
||||
#define STWLC38_NVM_SECTOR_BYTE_SIZE 256U
|
||||
#define STWLC38_NVM_PATCH_START_SECTOR_INDEX 0U
|
||||
#define STWLC38_NVM_CFG_START_SECTOR_INDEX 126U
|
||||
#define STWLC38_NVM_PATCH_TOTAL_SECTOR 126U
|
||||
#define STWLC38_NVM_CFG_TOTAL_SECTOR 2U
|
||||
#define STWLC38_NVM_PATCH_SIZE \
|
||||
(STWLC38_NVM_PATCH_TOTAL_SECTOR * STWLC38_NVM_SECTOR_BYTE_SIZE)
|
||||
#define STWLC38_NVM_CFG_SIZE \
|
||||
(STWLC38_NVM_CFG_TOTAL_SECTOR * STWLC38_NVM_SECTOR_BYTE_SIZE)
|
||||
|
||||
/* HW Registers */
|
||||
#define STWLC38_OPCODE_WRITE 0xFA
|
||||
#define STWLC38_OPCODE_SIZE 1U
|
||||
|
||||
#define STWLC38_HWREG_CUT_ID_REG 0x2001C002U
|
||||
#define STWLC38_HWREG_RESET_REG 0x2001F200U
|
||||
|
||||
/* FW Registers */
|
||||
#define STWLC38_FWREG_CHIP_ID_REG 0x0000U
|
||||
#define STWLC38_FWREG_OP_MODE_REG 0x000EU
|
||||
#define STWLC38_FWREG_DEVICE_ID_REG 0x0010U
|
||||
#define STWLC38_FWREG_SYS_CMD_REG 0x0020U
|
||||
#define STWLC38_FWREG_NVM_PWD_REG 0x0022U
|
||||
#define STWLC38_FWREG_NVM_SEC_IDX_REG 0x0024U
|
||||
#define STWLC38_FWREG_SYS_ERR_REG 0x002CU
|
||||
#define STWLC38_FWREG_AUX_DATA_00_REG 0x0180U
|
||||
|
||||
#define STWLC38_OK 0x0U
|
||||
#define STWLC38_ERR_BUS_W 0x80000000U
|
||||
#define STWLC38_ERR_BUS_WR 0x80000001U
|
||||
#define STWLC38_ERR_ALLOC_MEM 0x80000002U
|
||||
#define STWLC38_ERR_INVALID_PARAM 0x80000003U
|
||||
#define STWLC38_ERR_TIMEOUT 0x80000004U
|
||||
#define STWLC38_ERR_INVALID_OP_MODE 0x80000005U
|
||||
#define STWLC38_ERR_INVALID_CHIP_ID 0x80000006U
|
||||
#define STWLC38_ERR_NVM_ID_MISMATCH 0x80000007U
|
||||
#define STWLC38_ERR_NVM_DATA_CORRUPTED 0x80000008U
|
||||
|
||||
enum stwlc38_op_mode {
|
||||
OP_MODE_SA = 1,
|
||||
OP_MODE_RX = 2,
|
||||
OP_MODE_TX = 3,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint16_t chip_id; // Chip ID
|
||||
uint8_t chip_rev; // Chip Revision
|
||||
uint8_t cust_id; // Customer ID
|
||||
uint16_t rom_id; // ROM ID
|
||||
uint16_t patch_id; // Patch ID
|
||||
uint16_t cfg_id; // Config ID
|
||||
uint16_t pe_id; // Production ID
|
||||
uint8_t op_mode; // Operation mode
|
||||
uint8_t device_id[16]; // Device ID
|
||||
union {
|
||||
uint32_t sys_err; // System error
|
||||
struct {
|
||||
uint32_t core_hard_fault : 1;
|
||||
uint32_t reserved_bit_1 : 1;
|
||||
uint32_t nvm_ip_err : 1;
|
||||
uint32_t reserved_bit_3 : 1;
|
||||
uint32_t nvm_boot_err : 1;
|
||||
uint32_t reserved_3 : 3;
|
||||
uint32_t nvm_pe_error : 2;
|
||||
uint32_t nvm_config_err : 2;
|
||||
uint32_t nvm_patch_err : 2;
|
||||
uint32_t nvm_prod_info_err : 2;
|
||||
uint32_t reserved_4 : 16;
|
||||
};
|
||||
};
|
||||
} stwlc38_chip_info_t;
|
||||
|
||||
typedef struct {
|
||||
// Powered-up and initialized
|
||||
bool ready;
|
||||
// Providing power to the system
|
||||
bool vout_ready;
|
||||
|
||||
// Rectified voltage [V]
|
||||
float vrect;
|
||||
// Main LDO voltage output [V]
|
||||
float vout;
|
||||
// Output current [mA]
|
||||
float icur;
|
||||
// Chip temperature [°C]
|
||||
float tmeas;
|
||||
// Operating frequency [kHz]
|
||||
uint16_t opfreq;
|
||||
// NTC Temperature [°C]
|
||||
float ntc;
|
||||
|
||||
} stwlc38_report_t;
|
||||
|
||||
// Initializes STWLC38 driver
|
||||
//
|
||||
// After initialization, the STWLC38 is enabled by default.
|
||||
@ -46,26 +152,22 @@ bool stwlc38_enable(bool enable);
|
||||
// Returns true if the main LDO output was successfully enabled or disabled.
|
||||
bool stwlc38_enable_vout(bool enable);
|
||||
|
||||
typedef struct {
|
||||
// Powered-up and initialized
|
||||
bool ready;
|
||||
// Providing power to the system
|
||||
bool vout_ready;
|
||||
// Reads the chip information from the STWLC38
|
||||
//
|
||||
// The chip information is read from the STWLC38 and stored in the provided
|
||||
// chip_info structure.
|
||||
//
|
||||
// Returns true if the chip information was successfully read.
|
||||
bool stwlc38_read_chip_info(stwlc38_chip_info_t* chip_info);
|
||||
|
||||
// Rectified voltage [V]
|
||||
float vrect;
|
||||
// Main LDO voltage output [V]
|
||||
float vout;
|
||||
// Output current [mA]
|
||||
float icur;
|
||||
// Chip temperature [°C]
|
||||
float tmeas;
|
||||
// Operating frequency [kHz]
|
||||
uint16_t opfreq;
|
||||
// NTC Temperature [°C]
|
||||
float ntc;
|
||||
|
||||
} stwlc38_report_t;
|
||||
// Performs the firmware patch and config update on the STWLC38
|
||||
//
|
||||
//
|
||||
// To perform the update, the STWLC38 must be in standalone mode (5V on VOUT
|
||||
// pin).
|
||||
//
|
||||
// Returns true if the firmware and config update was successfully performed.
|
||||
bool stwlc38_patch_and_config();
|
||||
|
||||
// Gets the current report from the STWLC38
|
||||
bool stwlc38_get_report(stwlc38_report_t* status);
|
||||
|
Loading…
Reference in New Issue
Block a user