AnasSarkiz/usb-c-pd-brushed-motor
USB-C PD-powered motor controller with protected input, selectable 5/9/12 V buck supply, MCU qualification, PWM H-bridge drive, overvoltage sensing, regenerative energy-dump resistors, and safety interlocks.
- Version
- 0.0.1-a48-cloud-pipeline9-unrouted-c41d79a-305b1e7a
- License
- unset
- Stars
- 0
firmware/stusb4500_nvm.c
#include "stusb4500_nvm.h"
#define NVM_MAX_AGE_US 20000u
#define NVM_MAX_POLLS 64u
struct nvm_transaction { const struct stusb_bus *bus; uint32_t started_us; };
struct nvm_byte { uint8_t address, byte; };
struct nvm_sector { uint8_t index, *bytes; };
static bool late(const struct nvm_transaction *transaction) {
return transaction->bus->now_us(transaction->bus->context) -
transaction->started_us > NVM_MAX_AGE_US;
}
static enum stusb_nvm_result read_checked(struct stusb_read *transfer,
const struct nvm_transaction *transaction) {
if (late(transaction)) return STUSB_NVM_LATE;
transfer->deadline_us = transaction->started_us + NVM_MAX_AGE_US;
if (!transaction->bus->read(transaction->bus->context, transfer)) return STUSB_NVM_IO_ERROR;
return late(transaction) ? STUSB_NVM_LATE : STUSB_NVM_VERIFIED;
}
static enum stusb_nvm_result write_checked(struct stusb_write *transfer,
const struct nvm_transaction *transaction) {
if (late(transaction)) return STUSB_NVM_LATE;
transfer->deadline_us = transaction->started_us + NVM_MAX_AGE_US;
if (!transaction->bus->write(transaction->bus->context, transfer)) return STUSB_NVM_IO_ERROR;
return late(transaction) ? STUSB_NVM_LATE : STUSB_NVM_VERIFIED;
}
static enum stusb_nvm_result write_byte(struct nvm_byte command,
const struct nvm_transaction *transaction) {
struct stusb_write transfer = {.register_address=command.address, .byte_count=1, .bytes=&command.byte};
return write_checked(&transfer, transaction);
}
/* Proposed3us minimum reset hold, in addition to I2C wire time. A finite
* iteration ceiling avoids hanging if the required monotonic timer stops.
* Exact target timing and manufacturer reset constraints remain to qualify. */
static enum stusb_nvm_result hold_reset(const struct nvm_transaction *transaction) {
const uint32_t reset_us = transaction->bus->now_us(transaction->bus->context);
for (unsigned spin=0; spin<256; ++spin) {
if (late(transaction)) return STUSB_NVM_LATE;
if (transaction->bus->now_us(transaction->bus->context) - reset_us >= 3u)
return STUSB_NVM_VERIFIED;
}
return STUSB_NVM_TIMER_STALLED;
}
static enum stusb_nvm_result read_sector(const struct nvm_sector *sector,
const struct nvm_transaction *transaction) {
enum stusb_nvm_result result = write_byte((struct nvm_byte){0x96,0xc0}, transaction);
if (result != STUSB_NVM_VERIFIED) return result;
result = write_byte((struct nvm_byte){0x97,0x00}, transaction); /* READ only. */
if (result != STUSB_NVM_VERIFIED) return result;
result = write_byte((struct nvm_byte){0x96,(uint8_t)(0xd0u|sector->index)}, transaction);
if (result != STUSB_NVM_VERIFIED) return result;
uint8_t status = 0;
struct stusb_read poll = {.register_address=0x96, .byte_count=1, .bytes=&status};
bool complete = false;
for (unsigned attempt=0; attempt<NVM_MAX_POLLS; ++attempt) {
result = read_checked(&poll, transaction);
if (result != STUSB_NVM_VERIFIED) return result;
if (!(status & 0x10u)) { complete = true; break; }
}
if (!complete) return STUSB_NVM_BUSY_TIMEOUT;
if ((status & 0x47u) != (uint8_t)(0x40u|sector->index)) return STUSB_NVM_CONTROLLER_STATE;
struct stusb_read sector_read = {.register_address=0x53, .byte_count=8, .bytes=sector->bytes};
result = read_checked(§or_read, transaction);
if (result != STUSB_NVM_VERIFIED) return result;
result = write_byte((struct nvm_byte){0x96,0x00}, transaction);
return result == STUSB_NVM_VERIFIED ? hold_reset(transaction) : result;
}
enum stusb_nvm_result stusb_nvm_verify(const struct stusb_bus *bus,
const struct stusb_nvm_check *check) {
if (!check || !check->state) return STUSB_NVM_INVALID_ARGUMENT;
struct stusb_nvm_state *state = check->state;
if (state->faulted) return STUSB_NVM_LATCHED;
state->read_complete = state->image_matches = false;
for (unsigned i=0; i<STUSB_NVM_BYTES; ++i) state->observed_image.bytes[i] = 0;
enum stusb_nvm_result result = STUSB_NVM_INVALID_ARGUMENT;
if (!bus || !bus->read || !bus->write || !bus->now_us || !check->expected_image ||
check->expected_image == &state->observed_image) goto failed;
result = STUSB_NVM_UNSAFE_PRECONDITION;
if (!check->power_path_inhibited || !check->bridge_inhibited ||
!check->provenance_invalidated) goto failed;
const struct nvm_transaction transaction = {.bus=bus, .started_us=check->started_us};
result = write_byte((struct nvm_byte){0x95,0x47}, &transaction);
if (result != STUSB_NVM_VERIFIED) goto failed;
result = write_byte((struct nvm_byte){0x96,0x00}, &transaction);
if (result != STUSB_NVM_VERIFIED) goto failed;
result = hold_reset(&transaction);
if (result != STUSB_NVM_VERIFIED) goto failed;
result = write_byte((struct nvm_byte){0x96,0xc0}, &transaction);
if (result != STUSB_NVM_VERIFIED) goto failed;
for (uint8_t sector=0; sector<5; ++sector) {
const struct nvm_sector sector_read = {.index=sector,
.bytes=&state->observed_image.bytes[sector*8]};
result = read_sector(§or_read, &transaction);
if (result != STUSB_NVM_VERIFIED) goto failed;
}
const uint8_t exit_controls[2] = {0x40,0x00};
struct stusb_write exit_write = {.register_address=0x96, .byte_count=2, .bytes=exit_controls};
result = write_checked(&exit_write, &transaction);
if (result != STUSB_NVM_VERIFIED) goto failed;
result = write_byte((struct nvm_byte){0x95,0x00}, &transaction);
if (result != STUSB_NVM_VERIFIED) goto failed;
state->read_complete = true;
result = STUSB_NVM_MISMATCH;
for (unsigned i=0; i<STUSB_NVM_BYTES; ++i)
if (state->observed_image.bytes[i] != check->expected_image->bytes[i]) goto failed;
state->image_matches = true;
return STUSB_NVM_VERIFIED;
failed:
state->faulted = true;
state->image_matches = false;
return result;
}