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;
|
||||
|
||||
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;
|
||||
|
|
Loading…
Reference in New Issue