1
0
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:
kopecdav 2024-12-09 11:16:18 +01:00
parent f10dc86da2
commit e3845a6420
4 changed files with 1648 additions and 67 deletions

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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, &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, &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, &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

View File

@ -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);