flash/nor/stellaris: implement protection statuses and procedures
This should make protection work as expected on all stellaris families, including the latest Tiva C Snowflake. Run-time tested on TM4C123x (Blizzard). Change-Id: Ia017edb119bec32382b08fc037b5bbc02dd9000c Signed-off-by: Paul Fertser <fercerpav@gmail.com> Reviewed-on: http://openocd.zylin.com/2267 Tested-by: jenkins Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
This commit is contained in:
parent
487c57d9a2
commit
cdcae765de
|
@ -107,14 +107,9 @@ struct stellaris_flash_bank {
|
||||||
uint8_t target_class;
|
uint8_t target_class;
|
||||||
|
|
||||||
uint32_t sramsiz;
|
uint32_t sramsiz;
|
||||||
uint32_t flshsz;
|
|
||||||
/* flash geometry */
|
/* flash geometry */
|
||||||
uint32_t num_pages;
|
uint32_t num_pages;
|
||||||
uint32_t pagesize;
|
uint32_t pagesize;
|
||||||
uint32_t pages_in_lockregion;
|
|
||||||
|
|
||||||
/* nv memory bits */
|
|
||||||
uint16_t num_lockbits;
|
|
||||||
|
|
||||||
/* main clock status */
|
/* main clock status */
|
||||||
uint32_t rcc;
|
uint32_t rcc;
|
||||||
|
@ -524,24 +519,15 @@ static int get_stellaris_info(struct flash_bank *bank, char *buf, int buf_size)
|
||||||
printed = snprintf(buf,
|
printed = snprintf(buf,
|
||||||
buf_size,
|
buf_size,
|
||||||
"master clock: %ikHz%s, "
|
"master clock: %ikHz%s, "
|
||||||
"rcc is 0x%" PRIx32 ", rcc2 is 0x%" PRIx32 "\n",
|
"rcc is 0x%" PRIx32 ", rcc2 is 0x%" PRIx32 ", "
|
||||||
|
"pagesize: %" PRIu32 ", pages: %" PRIu32,
|
||||||
(int)(stellaris_info->mck_freq / 1000),
|
(int)(stellaris_info->mck_freq / 1000),
|
||||||
stellaris_info->mck_desc,
|
stellaris_info->mck_desc,
|
||||||
stellaris_info->rcc,
|
stellaris_info->rcc,
|
||||||
stellaris_info->rcc2);
|
stellaris_info->rcc2,
|
||||||
buf += printed;
|
|
||||||
buf_size -= printed;
|
|
||||||
|
|
||||||
if (stellaris_info->num_lockbits > 0) {
|
|
||||||
snprintf(buf,
|
|
||||||
buf_size,
|
|
||||||
"pagesize: %" PRIi32 ", pages: %d, "
|
|
||||||
"lockbits: %i, pages per lockbit: %i\n",
|
|
||||||
stellaris_info->pagesize,
|
stellaris_info->pagesize,
|
||||||
(unsigned) stellaris_info->num_pages,
|
stellaris_info->num_pages);
|
||||||
stellaris_info->num_lockbits,
|
|
||||||
(unsigned) stellaris_info->pages_in_lockregion);
|
|
||||||
}
|
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -782,11 +768,9 @@ static int stellaris_read_part_info(struct flash_bank *bank)
|
||||||
target_read_u32(target, FLASH_FSIZE, &stellaris_info->fsize);
|
target_read_u32(target, FLASH_FSIZE, &stellaris_info->fsize);
|
||||||
target_read_u32(target, FLASH_SSIZE, &stellaris_info->ssize);
|
target_read_u32(target, FLASH_SSIZE, &stellaris_info->ssize);
|
||||||
|
|
||||||
stellaris_info->num_lockbits = 1 + (stellaris_info->fsize & 0xFFFF);
|
|
||||||
stellaris_info->num_pages = 2 * (1 + (stellaris_info->fsize & 0xFFFF));
|
stellaris_info->num_pages = 2 * (1 + (stellaris_info->fsize & 0xFFFF));
|
||||||
stellaris_info->sramsiz = (1 + (stellaris_info->ssize & 0xFFFF)) / 4;
|
stellaris_info->sramsiz = (1 + (stellaris_info->ssize & 0xFFFF)) / 4;
|
||||||
stellaris_info->pagesize = 1024;
|
stellaris_info->pagesize = 1024;
|
||||||
stellaris_info->pages_in_lockregion = 2;
|
|
||||||
} else if (stellaris_info->target_class == 0xa) { /* Snowflake */
|
} else if (stellaris_info->target_class == 0xa) { /* Snowflake */
|
||||||
target_read_u32(target, FLASH_FSIZE, &stellaris_info->fsize);
|
target_read_u32(target, FLASH_FSIZE, &stellaris_info->fsize);
|
||||||
target_read_u32(target, FLASH_SSIZE, &stellaris_info->ssize);
|
target_read_u32(target, FLASH_SSIZE, &stellaris_info->ssize);
|
||||||
|
@ -794,17 +778,11 @@ static int stellaris_read_part_info(struct flash_bank *bank)
|
||||||
stellaris_info->pagesize = (1 << ((stellaris_info->fsize >> 16) & 7)) * 1024;
|
stellaris_info->pagesize = (1 << ((stellaris_info->fsize >> 16) & 7)) * 1024;
|
||||||
stellaris_info->num_pages = 2048 * (1 + (stellaris_info->fsize & 0xFFFF)) /
|
stellaris_info->num_pages = 2048 * (1 + (stellaris_info->fsize & 0xFFFF)) /
|
||||||
stellaris_info->pagesize;
|
stellaris_info->pagesize;
|
||||||
stellaris_info->pages_in_lockregion = 1;
|
|
||||||
|
|
||||||
stellaris_info->num_lockbits = stellaris_info->pagesize * stellaris_info->num_pages /
|
|
||||||
2048;
|
|
||||||
stellaris_info->sramsiz = (1 + (stellaris_info->ssize & 0xFFFF)) / 4;
|
stellaris_info->sramsiz = (1 + (stellaris_info->ssize & 0xFFFF)) / 4;
|
||||||
} else {
|
} else {
|
||||||
stellaris_info->num_lockbits = 1 + (stellaris_info->dc0 & 0xFFFF);
|
|
||||||
stellaris_info->num_pages = 2 * (1 + (stellaris_info->dc0 & 0xFFFF));
|
stellaris_info->num_pages = 2 * (1 + (stellaris_info->dc0 & 0xFFFF));
|
||||||
stellaris_info->sramsiz = (1 + ((stellaris_info->dc0 >> 16) & 0xFFFF)) / 4;
|
stellaris_info->sramsiz = (1 + ((stellaris_info->dc0 >> 16) & 0xFFFF)) / 4;
|
||||||
stellaris_info->pagesize = 1024;
|
stellaris_info->pagesize = 1024;
|
||||||
stellaris_info->pages_in_lockregion = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* REVISIT for at least Tempest parts, read NVMSTAT.FWB too.
|
/* REVISIT for at least Tempest parts, read NVMSTAT.FWB too.
|
||||||
|
@ -822,18 +800,16 @@ static int stellaris_read_part_info(struct flash_bank *bank)
|
||||||
static int stellaris_protect_check(struct flash_bank *bank)
|
static int stellaris_protect_check(struct flash_bank *bank)
|
||||||
{
|
{
|
||||||
struct stellaris_flash_bank *stellaris = bank->driver_priv;
|
struct stellaris_flash_bank *stellaris = bank->driver_priv;
|
||||||
|
struct target *target = bank->target;
|
||||||
|
uint32_t flash_sizek = stellaris->pagesize / 1024 *
|
||||||
|
stellaris->num_pages;
|
||||||
|
uint32_t fmppe_addr;
|
||||||
int status = ERROR_OK;
|
int status = ERROR_OK;
|
||||||
unsigned i;
|
unsigned i;
|
||||||
unsigned page;
|
|
||||||
|
|
||||||
if (stellaris->did1 == 0)
|
if (stellaris->did1 == 0)
|
||||||
return ERROR_FLASH_BANK_NOT_PROBED;
|
return ERROR_FLASH_BANK_NOT_PROBED;
|
||||||
|
|
||||||
if (stellaris->target_class == 0xa) {
|
|
||||||
LOG_WARNING("Assuming flash to be unprotected on Snowflake");
|
|
||||||
return ERROR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < (unsigned) bank->num_sectors; i++)
|
for (i = 0; i < (unsigned) bank->num_sectors; i++)
|
||||||
bank->sectors[i].is_protected = -1;
|
bank->sectors[i].is_protected = -1;
|
||||||
|
|
||||||
|
@ -841,32 +817,32 @@ static int stellaris_protect_check(struct flash_bank *bank)
|
||||||
* to report any pages that we can't write. Ignore the Read Enable
|
* to report any pages that we can't write. Ignore the Read Enable
|
||||||
* register (FMPRE).
|
* register (FMPRE).
|
||||||
*/
|
*/
|
||||||
for (i = 0, page = 0;
|
|
||||||
i < DIV_ROUND_UP(stellaris->num_lockbits, 32u);
|
|
||||||
i++) {
|
|
||||||
uint32_t lockbits;
|
|
||||||
|
|
||||||
status = target_read_u32(bank->target,
|
if (stellaris->target_class >= 0x0a || flash_sizek > 64)
|
||||||
SCB_BASE + (i ? (FMPPE0 + 4 * i) : FMPPE),
|
fmppe_addr = SCB_BASE | FMPPE0;
|
||||||
&lockbits);
|
else
|
||||||
LOG_DEBUG("FMPPE%d = %#8.8x (status %d)", i,
|
fmppe_addr = SCB_BASE | FMPPE;
|
||||||
(unsigned) lockbits, status);
|
|
||||||
if (status != ERROR_OK)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
for (unsigned j = 0; j < 32; j++) {
|
unsigned int page = 0, lockbitnum, lockbitcnt = flash_sizek / 2;
|
||||||
unsigned k;
|
unsigned int bits_per_page = stellaris->pagesize / 2048;
|
||||||
|
/* Every lock bit always corresponds to a 2k region */
|
||||||
|
for (lockbitnum = 0; lockbitnum < lockbitcnt; lockbitnum += 32) {
|
||||||
|
uint32_t fmppe;
|
||||||
|
|
||||||
for (k = 0; k < stellaris->pages_in_lockregion; k++) {
|
target_read_u32(target, fmppe_addr, &fmppe);
|
||||||
if (page >= (unsigned) bank->num_sectors)
|
for (i = 0; i < 32 && lockbitnum + i < lockbitcnt; i++) {
|
||||||
goto done;
|
bool protect = !(fmppe & (1 << i));
|
||||||
bank->sectors[page++].is_protected =
|
if (bits_per_page) {
|
||||||
!(lockbits & (1 << j));
|
bank->sectors[page++].is_protected = protect;
|
||||||
|
i += bits_per_page - 1;
|
||||||
|
} else { /* 1024k pages, every lockbit covers 2 pages */
|
||||||
|
bank->sectors[page++].is_protected = protect;
|
||||||
|
bank->sectors[page++].is_protected = protect;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fmppe_addr += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -930,13 +906,12 @@ static int stellaris_erase(struct flash_bank *bank, int first, int last)
|
||||||
|
|
||||||
static int stellaris_protect(struct flash_bank *bank, int set, int first, int last)
|
static int stellaris_protect(struct flash_bank *bank, int set, int first, int last)
|
||||||
{
|
{
|
||||||
uint32_t fmppe, flash_fmc, flash_cris;
|
struct stellaris_flash_bank *stellaris = bank->driver_priv;
|
||||||
int lockregion;
|
|
||||||
|
|
||||||
struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
|
|
||||||
struct target *target = bank->target;
|
struct target *target = bank->target;
|
||||||
|
uint32_t flash_fmc, flash_cris;
|
||||||
|
unsigned int bits_per_page = stellaris->pagesize / 2048;
|
||||||
|
|
||||||
if (bank->target->state != TARGET_HALTED) {
|
if (target->state != TARGET_HALTED) {
|
||||||
LOG_ERROR("Target not halted");
|
LOG_ERROR("Target not halted");
|
||||||
return ERROR_TARGET_NOT_HALTED;
|
return ERROR_TARGET_NOT_HALTED;
|
||||||
}
|
}
|
||||||
|
@ -947,26 +922,18 @@ static int stellaris_protect(struct flash_bank *bank, int set, int first, int la
|
||||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stellaris_info->did1 == 0)
|
if (stellaris->did1 == 0)
|
||||||
return ERROR_FLASH_BANK_NOT_PROBED;
|
return ERROR_FLASH_BANK_NOT_PROBED;
|
||||||
|
|
||||||
if (stellaris_info->target_class == 0x03 &&
|
if (stellaris->target_class == 0x03 &&
|
||||||
!((stellaris_info->did0 >> 8) & 0xFF) &&
|
!((stellaris->did0 >> 8) & 0xFF) &&
|
||||||
!((stellaris_info->did0) & 0xFF)) {
|
!((stellaris->did0) & 0xFF)) {
|
||||||
LOG_ERROR("DustDevil A0 parts can't be unprotected, see errata; refusing to proceed");
|
LOG_ERROR("DustDevil A0 parts can't be unprotected, see errata; refusing to proceed");
|
||||||
return ERROR_FLASH_OPERATION_FAILED;
|
return ERROR_FLASH_OPERATION_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stellaris_info->target_class == 0xa) {
|
if (!bits_per_page && (first % 2 || !(last % 2))) {
|
||||||
LOG_ERROR("Protection on Snowflake is not supported yet");
|
LOG_ERROR("Can't protect unaligned pages");
|
||||||
return ERROR_FLASH_OPERATION_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* lockregions are 2 pages ... must protect [even..odd] */
|
|
||||||
if ((first < 0) || (first & 1)
|
|
||||||
|| (last < first) || !(last & 1)
|
|
||||||
|| (last >= 2 * stellaris_info->num_lockbits)) {
|
|
||||||
LOG_ERROR("Can't protect unaligned or out-of-range pages.");
|
|
||||||
return ERROR_FLASH_SECTOR_INVALID;
|
return ERROR_FLASH_SECTOR_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -974,45 +941,52 @@ static int stellaris_protect(struct flash_bank *bank, int set, int first, int la
|
||||||
stellaris_read_clock_info(bank);
|
stellaris_read_clock_info(bank);
|
||||||
stellaris_set_flash_timing(bank);
|
stellaris_set_flash_timing(bank);
|
||||||
|
|
||||||
/* convert from pages to lockregions */
|
|
||||||
first /= 2;
|
|
||||||
last /= 2;
|
|
||||||
|
|
||||||
/* FIXME this assumes single FMPPE, for a max of 64K of flash!!
|
|
||||||
* Current parts can be much bigger.
|
|
||||||
*/
|
|
||||||
if (last >= 32) {
|
|
||||||
LOG_ERROR("No support yet for protection > 64K");
|
|
||||||
return ERROR_FLASH_OPERATION_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
target_read_u32(target, SCB_BASE | FMPPE, &fmppe);
|
|
||||||
|
|
||||||
for (lockregion = first; lockregion <= last; lockregion++)
|
|
||||||
fmppe &= ~(1 << lockregion);
|
|
||||||
|
|
||||||
/* Clear and disable flash programming interrupts */
|
/* Clear and disable flash programming interrupts */
|
||||||
target_write_u32(target, FLASH_CIM, 0);
|
target_write_u32(target, FLASH_CIM, 0);
|
||||||
target_write_u32(target, FLASH_MISC, PMISC | AMISC);
|
target_write_u32(target, FLASH_MISC, PMISC | AMISC);
|
||||||
|
|
||||||
/* REVISIT this clobbers state set by any halted firmware ...
|
uint32_t flash_sizek = stellaris->pagesize / 1024 *
|
||||||
* it might want to process those IRQs.
|
stellaris->num_pages;
|
||||||
*/
|
uint32_t fmppe_addr;
|
||||||
|
|
||||||
LOG_DEBUG("fmppe 0x%" PRIx32 "", fmppe);
|
if (stellaris->target_class >= 0x0a || flash_sizek > 64)
|
||||||
target_write_u32(target, SCB_BASE | FMPPE, fmppe);
|
fmppe_addr = SCB_BASE | FMPPE0;
|
||||||
|
else
|
||||||
|
fmppe_addr = SCB_BASE | FMPPE;
|
||||||
|
|
||||||
/* Commit FMPPE */
|
int page = 0;
|
||||||
target_write_u32(target, FLASH_FMA, 1);
|
unsigned int lockbitnum, lockbitcnt = flash_sizek / 2;
|
||||||
|
/* Every lock bit always corresponds to a 2k region */
|
||||||
|
for (lockbitnum = 0; lockbitnum < lockbitcnt; lockbitnum += 32) {
|
||||||
|
uint32_t fmppe;
|
||||||
|
|
||||||
|
target_read_u32(target, fmppe_addr, &fmppe);
|
||||||
|
for (unsigned int i = 0;
|
||||||
|
i < 32 && lockbitnum + i < lockbitcnt;
|
||||||
|
i++) {
|
||||||
|
if (page >= first && page <= last)
|
||||||
|
fmppe &= ~(1 << i);
|
||||||
|
|
||||||
|
if (bits_per_page) {
|
||||||
|
if (!((i + 1) % bits_per_page))
|
||||||
|
page++;
|
||||||
|
} else { /* 1024k pages, every lockbit covers 2 pages */
|
||||||
|
page += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target_write_u32(target, fmppe_addr, fmppe);
|
||||||
|
|
||||||
|
/* Commit FMPPE* */
|
||||||
|
target_write_u32(target, FLASH_FMA, 1 + lockbitnum / 16);
|
||||||
/* Write commit command */
|
/* Write commit command */
|
||||||
target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_COMT);
|
target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_COMT);
|
||||||
|
|
||||||
/* Wait until erase complete */
|
/* Wait until commit complete */
|
||||||
do {
|
do {
|
||||||
target_read_u32(target, FLASH_FMC, &flash_fmc);
|
target_read_u32(target, FLASH_FMC, &flash_fmc);
|
||||||
} while (flash_fmc & FMC_COMT);
|
} while (flash_fmc & FMC_COMT);
|
||||||
|
|
||||||
/* Check acess violations */
|
/* Check access violations */
|
||||||
target_read_u32(target, FLASH_CRIS, &flash_cris);
|
target_read_u32(target, FLASH_CRIS, &flash_cris);
|
||||||
if (flash_cris & (AMASK)) {
|
if (flash_cris & (AMASK)) {
|
||||||
LOG_WARNING("Error setting flash page protection, flash_cris 0x%" PRIx32 "", flash_cris);
|
LOG_WARNING("Error setting flash page protection, flash_cris 0x%" PRIx32 "", flash_cris);
|
||||||
|
@ -1020,6 +994,9 @@ static int stellaris_protect(struct flash_bank *bank, int set, int first, int la
|
||||||
return ERROR_FLASH_OPERATION_FAILED;
|
return ERROR_FLASH_OPERATION_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmppe_addr += 4;
|
||||||
|
}
|
||||||
|
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1382,6 +1359,9 @@ COMMAND_HANDLER(stellaris_handle_recover_command)
|
||||||
struct flash_bank *bank;
|
struct flash_bank *bank;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
|
if (CMD_ARGC < 1)
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
|
||||||
retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
|
retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
Loading…
Reference in New Issue