stm32h7: Switched to the unified mass erase command, breaking up the implementation cleanly and added progress dots to stop GDB timing out

This commit is contained in:
dragonmux 2022-07-11 23:40:18 -04:00 committed by Piotr Esden-Tempski
parent 980a1f140c
commit 787d66fe95
1 changed files with 94 additions and 145 deletions

View File

@ -37,7 +37,6 @@
#include "target_internal.h" #include "target_internal.h"
#include "cortexm.h" #include "cortexm.h"
static bool stm32h7_cmd_erase_mass(target *t, int argc, const char **argv);
/* static bool stm32h7_cmd_option(target *t, int argc, char *argv[]); */ /* static bool stm32h7_cmd_option(target *t, int argc, char *argv[]); */
static bool stm32h7_uid(target *t, int argc, const char **argv); static bool stm32h7_uid(target *t, int argc, const char **argv);
static bool stm32h7_crc(target *t, int argc, const char **argv); static bool stm32h7_crc(target *t, int argc, const char **argv);
@ -45,29 +44,22 @@ static bool stm32h7_cmd_psize(target *t, int argc, char *argv[]);
static bool stm32h7_cmd_rev(target *t, int argc, const char **argv); static bool stm32h7_cmd_rev(target *t, int argc, const char **argv);
const struct command_s stm32h7_cmd_list[] = { const struct command_s stm32h7_cmd_list[] = {
{"erase_mass", (cmd_handler)stm32h7_cmd_erase_mass, /*{"option", (cmd_handler)stm32h7_cmd_option, "Manipulate option bytes"},*/
"Erase entire flash memory"}, {"psize", (cmd_handler)stm32h7_cmd_psize, "Configure flash write parallelism: (x8|x16|x32|x64(default))"},
/* {"option", (cmd_handler)stm32h7_cmd_option,
"Manipulate option bytes"},*/
{"psize", (cmd_handler)stm32h7_cmd_psize,
"Configure flash write parallelism: (x8|x16|x32|x64(default))"},
{"uid", (cmd_handler)stm32h7_uid, "Print unique device ID"}, {"uid", (cmd_handler)stm32h7_uid, "Print unique device ID"},
{"crc", (cmd_handler)stm32h7_crc, "Print CRC of both banks"}, {"crc", (cmd_handler)stm32h7_crc, "Print CRC of both banks"},
{"revision", (cmd_handler)stm32h7_cmd_rev, {"revision", (cmd_handler)stm32h7_cmd_rev, "Returns the Device ID and Revision"},
"Returns the Device ID and Revision"},
{NULL, NULL, NULL} {NULL, NULL, NULL}
}; };
static int stm32h7_flash_erase(struct target_flash *f, target_addr addr, static int stm32h7_flash_erase(struct target_flash *f, target_addr addr, size_t len);
size_t len); static int stm32h7_flash_write(struct target_flash *f, target_addr dest, const void *src, size_t len);
static int stm32h7_flash_write(struct target_flash *f, static bool stm32h7_mass_erase(target *t);
target_addr dest, const void *src, size_t len);
static const char stm32h7_driver_str[] = "STM32H7"; static const char stm32h7_driver_str[] = "STM32H7";
enum stm32h7_regs enum stm32h7_regs {
{
FLASH_ACR = 0x00, FLASH_ACR = 0x00,
FLASH_KEYR = 0x04, FLASH_KEYR = 0x04,
FLASH_OPTKEYR = 0x08, FLASH_OPTKEYR = 0x08,
@ -169,18 +161,15 @@ struct stm32h7_priv_s {
uint32_t dbg_cr; uint32_t dbg_cr;
}; };
static void stm32h7_add_flash(target *t, static void stm32h7_add_flash(target *t, uint32_t addr, size_t length, size_t blocksize)
uint32_t addr, size_t length, size_t blocksize)
{ {
struct stm32h7_flash *sf = calloc(1, sizeof(*sf)); struct stm32h7_flash *sf = calloc(1, sizeof(*sf));
struct target_flash *f;
if (!sf) { /* calloc failed: heap exhaustion */ if (!sf) { /* calloc failed: heap exhaustion */
DEBUG_WARN("calloc: failed in %s\n", __func__); DEBUG_WARN("calloc: failed in %s\n", __func__);
return; return;
} }
f = &sf->f; struct target_flash *f = &sf->f;
f->start = addr; f->start = addr;
f->length = length; f->length = length;
f->blocksize = blocksize; f->blocksize = blocksize;
@ -237,6 +226,7 @@ bool stm32h7_probe(target *t)
{ {
uint32_t idcode = t->idcode; uint32_t idcode = t->idcode;
if (idcode == ID_STM32H74x || idcode == ID_STM32H7Bx || idcode == ID_STM32H72x) { if (idcode == ID_STM32H74x || idcode == ID_STM32H7Bx || idcode == ID_STM32H72x) {
t->mass_erase = stm32h7_mass_erase;
t->driver = stm32h7_driver_str; t->driver = stm32h7_driver_str;
t->attach = stm32h7_attach; t->attach = stm32h7_attach;
t->detach = stm32h7_detach; t->detach = stm32h7_detach;
@ -255,21 +245,20 @@ bool stm32h7_probe(target *t)
return false; return false;
} }
static bool stm32h7_flash_unlock(target *t, uint32_t addr) static bool stm32h7_flash_unlock(target *t, const uint32_t addr)
{ {
uint32_t regbase = FPEC1_BASE; uint32_t regbase = FPEC1_BASE;
if (addr >= BANK2_START) { if (addr >= BANK2_START)
regbase = FPEC2_BASE; regbase = FPEC2_BASE;
}
while(target_mem_read32(t, regbase + FLASH_SR) & FLASH_SR_BSY) { while (target_mem_read32(t, regbase + FLASH_SR) & FLASH_SR_BSY) {
if(target_check_error(t)) if (target_check_error(t))
return false; return false;
} }
uint32_t sr = target_mem_read32(t, regbase + FLASH_SR) & FLASH_SR_ERROR_MASK; const uint32_t status = target_mem_read32(t, regbase + FLASH_SR) & FLASH_SR_ERROR_MASK;
if (sr) { if (status) {
DEBUG_WARN("%s error 0x%08" PRIx32, __func__, sr); DEBUG_WARN("%s error 0x%08" PRIx32, __func__, status);
target_mem_write32(t, regbase + FLASH_CCR, sr); target_mem_write32(t, regbase + FLASH_CCR, status);
return false; return false;
} }
if (target_mem_read32(t, regbase + FLASH_CR) & FLASH_CR_LOCK) { if (target_mem_read32(t, regbase + FLASH_CR) & FLASH_CR_LOCK) {
@ -277,77 +266,72 @@ static bool stm32h7_flash_unlock(target *t, uint32_t addr)
target_mem_write32(t, regbase + FLASH_KEYR, KEY1); target_mem_write32(t, regbase + FLASH_KEYR, KEY1);
target_mem_write32(t, regbase + FLASH_KEYR, KEY2); target_mem_write32(t, regbase + FLASH_KEYR, KEY2);
} }
if (target_mem_read32(t, regbase + FLASH_CR) & FLASH_CR_LOCK) return !(target_mem_read32(t, regbase + FLASH_CR) & FLASH_CR_LOCK);
return false;
else
return true;
} }
static int stm32h7_flash_erase(struct target_flash *f, target_addr addr, static int stm32h7_flash_erase(struct target_flash *f, target_addr addr, size_t len)
size_t len)
{ {
target *t = f->t; target *t = f->t;
struct stm32h7_flash *sf = (struct stm32h7_flash *)f; struct stm32h7_flash *sf = (struct stm32h7_flash *)f;
if (stm32h7_flash_unlock(t, addr) == false) if (!stm32h7_flash_unlock(t, addr))
return -1; return -1;
/* We come out of reset with HSI 64 MHz. Adapt FLASH_ACR.*/ /* We come out of reset with HSI 64 MHz. Adapt FLASH_ACR.*/
target_mem_write32(t, sf->regbase + FLASH_ACR, 0); target_mem_write32(t, sf->regbase + FLASH_ACR, 0);
addr &= (NUM_SECTOR_PER_BANK * FLASH_SECTOR_SIZE) - 1; addr &= (NUM_SECTOR_PER_BANK * FLASH_SECTOR_SIZE) - 1;
int start_sector = addr / FLASH_SECTOR_SIZE; size_t start_sector = addr / FLASH_SECTOR_SIZE;
int end_sector = (addr + len - 1) / FLASH_SECTOR_SIZE; const size_t end_sector = (addr + len - 1) / FLASH_SECTOR_SIZE;
enum align psize = ((struct stm32h7_flash *)f)->psize; enum align psize = ((struct stm32h7_flash *)f)->psize;
uint32_t sr;
while (start_sector <= end_sector) { while (start_sector <= end_sector) {
uint32_t cr = (psize * FLASH_CR_PSIZE16) | FLASH_CR_SER | uint32_t ctrl_reg = (psize * FLASH_CR_PSIZE16) | FLASH_CR_SER | (start_sector * FLASH_CR_SNB_1);
(start_sector * FLASH_CR_SNB_1); target_mem_write32(t, sf->regbase + FLASH_CR, ctrl_reg);
target_mem_write32(t, sf->regbase + FLASH_CR, cr); ctrl_reg |= FLASH_CR_START;
cr |= FLASH_CR_START; target_mem_write32(t, sf->regbase + FLASH_CR, ctrl_reg);
target_mem_write32(t, sf->regbase + FLASH_CR, cr);
DEBUG_INFO(" started cr %08" PRIx32 " sr %08" PRIx32 "\n", DEBUG_INFO(" started cr %08" PRIx32 " sr %08" PRIx32 "\n",
target_mem_read32(t, sf->regbase + FLASH_CR), target_mem_read32(t, sf->regbase + FLASH_CR),
target_mem_read32(t, sf->regbase + FLASH_SR)); target_mem_read32(t, sf->regbase + FLASH_SR));
uint32_t status = 0;
do { do {
sr = target_mem_read32(t, sf->regbase + FLASH_SR); status = target_mem_read32(t, sf->regbase + FLASH_SR);
if (target_check_error(t)) { if (target_check_error(t)) {
DEBUG_WARN("stm32h7_flash_erase: comm failed\n"); DEBUG_WARN("stm32h7_flash_erase: comm failed\n");
return -1; return -1;
} }
// target_mem_write32(t, H7_IWDG_BASE, 0x0000aaaa); // target_mem_write32(t, H7_IWDG_BASE, 0x0000aaaa);
}while (sr & (FLASH_SR_QW | FLASH_SR_BSY)); } while (status & (FLASH_SR_QW | FLASH_SR_BSY));
if (sr & FLASH_SR_ERROR_MASK) {
DEBUG_WARN("stm32h7_flash_erase: error, sr: %08" PRIx32 "\n", sr); if (status & FLASH_SR_ERROR_MASK) {
DEBUG_WARN("stm32h7_flash_erase: error, sr: %08" PRIx32 "\n", status);
return -1; return -1;
} }
start_sector++; ++start_sector;
} }
return 0; return 0;
} }
static int stm32h7_flash_write(struct target_flash *f, target_addr dest, static int stm32h7_flash_write(struct target_flash *f, target_addr dest, const void *src, size_t len)
const void *src, size_t len)
{ {
target *t = f->t; target *t = f->t;
struct stm32h7_flash *sf = (struct stm32h7_flash *)f; struct stm32h7_flash *sf = (struct stm32h7_flash *)f;
enum align psize = sf->psize; enum align psize = sf->psize;
if (stm32h7_flash_unlock(t, dest) == false) if (!stm32h7_flash_unlock(t, dest))
return -1; return -1;
uint32_t cr = psize * FLASH_CR_PSIZE16; uint32_t cr = psize * FLASH_CR_PSIZE16;
target_mem_write32(t, sf->regbase + FLASH_CR, cr); target_mem_write32(t, sf->regbase + FLASH_CR, cr);
cr |= FLASH_CR_PG; cr |= FLASH_CR_PG;
target_mem_write32(t, sf->regbase + FLASH_CR, cr); target_mem_write32(t, sf->regbase + FLASH_CR, cr);
/* does H7 stall?*/ /* does H7 stall?*/
uint32_t sr_reg = sf->regbase + FLASH_SR; uint32_t status_reg = sf->regbase + FLASH_SR;
uint32_t sr; uint32_t status = 0;
target_mem_write(t, dest, src, len); target_mem_write(t, dest, src, len);
while ((sr = target_mem_read32(t, sr_reg)) & FLASH_SR_BSY) { while ((status = target_mem_read32(t, status_reg)) & FLASH_SR_BSY) {
if(target_check_error(t)) { if(target_check_error(t)) {
DEBUG_WARN("stm32h7_flash_write: BSY comm failed\n"); DEBUG_WARN("stm32h7_flash_write: BSY comm failed\n");
return -1; return -1;
} }
} }
if (sr & FLASH_SR_ERROR_MASK) { if (status & FLASH_SR_ERROR_MASK) {
DEBUG_WARN("stm32h7_flash_write: error sr %08" PRIx32 "\n", sr); DEBUG_WARN("stm32h7_flash_write: error sr %08" PRIx32 "\n", status);
return -1; return -1;
} }
/* Close write windows.*/ /* Close write windows.*/
@ -355,97 +339,62 @@ static int stm32h7_flash_write(struct target_flash *f, target_addr dest,
return 0; return 0;
} }
/* Both banks are erased in parallel.*/ static bool stm32h7_erase_bank(target *const t, const enum align psize,
static bool stm32h7_cmd_erase(target *t, int bank_mask) const uint32_t start_addr, const uint32_t reg_base)
{ {
const char spinner[] = "|/-\\"; if (!stm32h7_flash_unlock(t, start_addr)) {
int spinindex = 0; DEBUG_WARN("mass erase: Unlock bank failed\n");
bool do_bank1 = bank_mask & 1, do_bank2 = bank_mask & 2; return false;
uint32_t cr;
bool result = false;
enum align psize = ALIGN_DWORD;
for (struct target_flash *f = t->flash; f; f = f->next) {
if (f->write == stm32h7_flash_write) {
psize = ((struct stm32h7_flash *)f)->psize;
}
} }
cr = (psize * FLASH_CR_PSIZE16) | FLASH_CR_BER | FLASH_CR_START; /* BER and start can be merged (3.3.10).*/
/* Flash mass erase start instruction */ const uint32_t ctrl_reg = (psize * FLASH_CR_PSIZE16) | FLASH_CR_BER | FLASH_CR_START;
if (do_bank1) { target_mem_write32(t, reg_base + FLASH_CR, ctrl_reg);
if (stm32h7_flash_unlock(t, BANK1_START) == false) { DEBUG_INFO("mass erase of bank started\n");
DEBUG_WARN("ME: Unlock bank1 failed\n"); return true;
goto done;
}
uint32_t regbase = FPEC1_BASE;
/* BER and start can be merged (3.3.10).*/
target_mem_write32(t, regbase + FLASH_CR, cr);
DEBUG_INFO("ME bank1 started\n");
}
if (do_bank2) {
if (stm32h7_flash_unlock(t, BANK2_START) == false) {
DEBUG_WARN("ME: Unlock bank2 failed\n");
goto done;
}
uint32_t regbase = FPEC2_BASE;
/* BER and start can be merged (3.3.10).*/
target_mem_write32(t, regbase + FLASH_CR, cr);
DEBUG_INFO("ME bank2 started\n");
}
/* Read FLASH_SR to poll for QW bit */
if (do_bank1) {
uint32_t regbase = FPEC1_BASE;
while (target_mem_read32(t, regbase + FLASH_SR) & FLASH_SR_QW) {
// target_mem_write32(t, H7_IWDG_BASE, 0x0000aaaa);
tc_printf(t, "\b%c", spinner[spinindex++ % 4]);
if(target_check_error(t)) {
DEBUG_WARN("ME bank1: comm failed\n");
goto done;
}
}
}
if (do_bank2) {
uint32_t regbase = FPEC2_BASE;
while (target_mem_read32(t, regbase + FLASH_SR) & FLASH_SR_QW) {
// target_mem_write32(t, H7_IWDG_BASE 0x0000aaaa);
tc_printf(t, "\b%c", spinner[spinindex++ % 4]);
if(target_check_error(t)) {
DEBUG_WARN("ME bank2: comm failed\n");
goto done;
}
}
}
if (do_bank1) {
/* Check for error */
uint32_t regbase = FPEC1_BASE;
uint32_t sr = target_mem_read32(t, regbase + FLASH_SR);
if (sr & FLASH_SR_ERROR_MASK) {
DEBUG_WARN("ME bank1, error sr %" PRIx32 "\n", sr);
goto done;
}
}
if (do_bank2) {
/* Check for error */
uint32_t regbase = FPEC2_BASE;
uint32_t sr = target_mem_read32(t, regbase + FLASH_SR);
if (sr & FLASH_SR_ERROR_MASK) {
DEBUG_WARN("ME bank2, error: sr %" PRIx32 "\n", sr);
goto done;
}
}
result = true;
done:
tc_printf(t, "\n");
return result;
} }
static bool stm32h7_cmd_erase_mass(target *t, int argc, const char **argv) static bool stm32h7_wait_erase_bank(target *const t, platform_timeout *timeout, const uint32_t reg_base)
{ {
(void)argc; while (target_mem_read32(t, reg_base + FLASH_SR) & FLASH_SR_QW) {
(void)argv; if (target_check_error(t)) {
tc_printf(t, "Erasing flash... This may take a few seconds. "); DEBUG_WARN("mass erase bank: comm failed\n");
return stm32h7_cmd_erase(t, 3); return false;
}
target_print_progress(timeout);
}
return true;
}
static bool stm32h7_check_bank(target *const t, const uint32_t reg_base)
{
uint32_t status = target_mem_read32(t, reg_base + FLASH_SR);
if (status & FLASH_SR_ERROR_MASK)
DEBUG_WARN("mass erase bank: error sr %" PRIx32 "\n", status);
return !(status & FLASH_SR_ERROR_MASK);
}
/* Both banks are erased in parallel.*/
static bool stm32h7_mass_erase(target *t)
{
enum align psize = ALIGN_DWORD;
for (struct target_flash *flash = t->flash; flash; flash = flash->next) {
if (flash->write == stm32h7_flash_write)
psize = ((struct stm32h7_flash *)flash)->psize;
}
/* Send mass erase Flash start instruction */
if (!stm32h7_erase_bank(t, psize, BANK1_START, FPEC1_BASE) ||
stm32h7_erase_bank(t, psize, BANK2_START, FPEC2_BASE))
return false;
platform_timeout timeout;
platform_timeout_set(&timeout, 500);
/* Wait for the banks to finish erasing */
if (!stm32h7_wait_erase_bank(t, &timeout, FPEC1_BASE) ||
!stm32h7_wait_erase_bank(t, &timeout, FPEC2_BASE))
return false;
/* Check the banks for final errors */
return stm32h7_check_bank(t, FPEC1_BASE) && stm32h7_check_bank(t, FPEC2_BASE);
} }
/* Print the Unique device ID. /* Print the Unique device ID.