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:
Paul Fertser 2014-08-21 15:16:55 +04:00 committed by Spencer Oliver
parent 487c57d9a2
commit cdcae765de

View File

@ -107,14 +107,9 @@ struct stellaris_flash_bank {
uint8_t target_class;
uint32_t sramsiz;
uint32_t flshsz;
/* flash geometry */
uint32_t num_pages;
uint32_t pagesize;
uint32_t pages_in_lockregion;
/* nv memory bits */
uint16_t num_lockbits;
/* main clock status */
uint32_t rcc;
@ -524,24 +519,15 @@ static int get_stellaris_info(struct flash_bank *bank, char *buf, int buf_size)
printed = snprintf(buf,
buf_size,
"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),
stellaris_info->mck_desc,
stellaris_info->rcc,
stellaris_info->rcc2);
buf += printed;
buf_size -= printed;
stellaris_info->rcc2,
stellaris_info->pagesize,
stellaris_info->num_pages);
if (stellaris_info->num_lockbits > 0) {
snprintf(buf,
buf_size,
"pagesize: %" PRIi32 ", pages: %d, "
"lockbits: %i, pages per lockbit: %i\n",
stellaris_info->pagesize,
(unsigned) stellaris_info->num_pages,
stellaris_info->num_lockbits,
(unsigned) stellaris_info->pages_in_lockregion);
}
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_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->sramsiz = (1 + (stellaris_info->ssize & 0xFFFF)) / 4;
stellaris_info->pagesize = 1024;
stellaris_info->pages_in_lockregion = 2;
} else if (stellaris_info->target_class == 0xa) { /* Snowflake */
target_read_u32(target, FLASH_FSIZE, &stellaris_info->fsize);
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->num_pages = 2048 * (1 + (stellaris_info->fsize & 0xFFFF)) /
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;
} else {
stellaris_info->num_lockbits = 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->pagesize = 1024;
stellaris_info->pages_in_lockregion = 2;
}
/* 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)
{
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;
unsigned i;
unsigned page;
if (stellaris->did1 == 0)
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++)
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
* 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,
SCB_BASE + (i ? (FMPPE0 + 4 * i) : FMPPE),
&lockbits);
LOG_DEBUG("FMPPE%d = %#8.8x (status %d)", i,
(unsigned) lockbits, status);
if (status != ERROR_OK)
goto done;
if (stellaris->target_class >= 0x0a || flash_sizek > 64)
fmppe_addr = SCB_BASE | FMPPE0;
else
fmppe_addr = SCB_BASE | FMPPE;
for (unsigned j = 0; j < 32; j++) {
unsigned k;
unsigned int page = 0, lockbitnum, lockbitcnt = flash_sizek / 2;
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++) {
if (page >= (unsigned) bank->num_sectors)
goto done;
bank->sectors[page++].is_protected =
!(lockbits & (1 << j));
target_read_u32(target, fmppe_addr, &fmppe);
for (i = 0; i < 32 && lockbitnum + i < lockbitcnt; i++) {
bool protect = !(fmppe & (1 << i));
if (bits_per_page) {
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;
}
@ -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)
{
uint32_t fmppe, flash_fmc, flash_cris;
int lockregion;
struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
struct stellaris_flash_bank *stellaris = bank->driver_priv;
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");
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;
}
if (stellaris_info->did1 == 0)
if (stellaris->did1 == 0)
return ERROR_FLASH_BANK_NOT_PROBED;
if (stellaris_info->target_class == 0x03 &&
!((stellaris_info->did0 >> 8) & 0xFF) &&
!((stellaris_info->did0) & 0xFF)) {
if (stellaris->target_class == 0x03 &&
!((stellaris->did0 >> 8) & 0xFF) &&
!((stellaris->did0) & 0xFF)) {
LOG_ERROR("DustDevil A0 parts can't be unprotected, see errata; refusing to proceed");
return ERROR_FLASH_OPERATION_FAILED;
}
if (stellaris_info->target_class == 0xa) {
LOG_ERROR("Protection on Snowflake is not supported yet");
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.");
if (!bits_per_page && (first % 2 || !(last % 2))) {
LOG_ERROR("Can't protect unaligned pages");
return ERROR_FLASH_SECTOR_INVALID;
}
@ -974,50 +941,60 @@ static int stellaris_protect(struct flash_bank *bank, int set, int first, int la
stellaris_read_clock_info(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 */
target_write_u32(target, FLASH_CIM, 0);
target_write_u32(target, FLASH_MISC, PMISC | AMISC);
/* REVISIT this clobbers state set by any halted firmware ...
* it might want to process those IRQs.
*/
uint32_t flash_sizek = stellaris->pagesize / 1024 *
stellaris->num_pages;
uint32_t fmppe_addr;
LOG_DEBUG("fmppe 0x%" PRIx32 "", fmppe);
target_write_u32(target, SCB_BASE | FMPPE, fmppe);
if (stellaris->target_class >= 0x0a || flash_sizek > 64)
fmppe_addr = SCB_BASE | FMPPE0;
else
fmppe_addr = SCB_BASE | FMPPE;
/* Commit FMPPE */
target_write_u32(target, FLASH_FMA, 1);
/* Write commit command */
target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_COMT);
int page = 0;
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;
/* Wait until erase complete */
do {
target_read_u32(target, FLASH_FMC, &flash_fmc);
} while (flash_fmc & FMC_COMT);
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);
/* Check acess violations */
target_read_u32(target, FLASH_CRIS, &flash_cris);
if (flash_cris & (AMASK)) {
LOG_WARNING("Error setting flash page protection, flash_cris 0x%" PRIx32 "", flash_cris);
target_write_u32(target, FLASH_CRIS, 0);
return ERROR_FLASH_OPERATION_FAILED;
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 */
target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_COMT);
/* Wait until commit complete */
do {
target_read_u32(target, FLASH_FMC, &flash_fmc);
} while (flash_fmc & FMC_COMT);
/* Check access violations */
target_read_u32(target, FLASH_CRIS, &flash_cris);
if (flash_cris & (AMASK)) {
LOG_WARNING("Error setting flash page protection, flash_cris 0x%" PRIx32 "", flash_cris);
target_write_u32(target, FLASH_CRIS, 0);
return ERROR_FLASH_OPERATION_FAILED;
}
fmppe_addr += 4;
}
return ERROR_OK;
@ -1382,6 +1359,9 @@ COMMAND_HANDLER(stellaris_handle_recover_command)
struct flash_bank *bank;
int retval;
if (CMD_ARGC < 1)
return ERROR_COMMAND_SYNTAX_ERROR;
retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (retval != ERROR_OK)
return retval;