flash/nor/stellaris: implement protection statuses and procedures
authorPaul Fertser <fercerpav@gmail.com>
Thu, 21 Aug 2014 11:16:55 +0000 (15:16 +0400)
committerSpencer Oliver <spen@spen-soft.co.uk>
Mon, 6 Oct 2014 18:40:46 +0000 (18:40 +0000)
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>
src/flash/nor/stellaris.c

index 4c819c6195d294946d521e132a8c614bc9b2846e..c4fe3be9edccf61edf6d8effb8529b70649a95e2 100644 (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;
-
-               for (unsigned j = 0; j < 32; j++) {
-                       unsigned k;
 
-                       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));
+       if (stellaris->target_class >= 0x0a || flash_sizek > 64)
+               fmppe_addr = SCB_BASE | FMPPE0;
+       else
+               fmppe_addr = SCB_BASE | FMPPE;
+
+       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;
+
+               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;
+
+       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;
+
+               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);
-       /* Write commit command */
-       target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_COMT);
+               /* 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 erase complete */
-       do {
-               target_read_u32(target, FLASH_FMC, &flash_fmc);
-       } while (flash_fmc & FMC_COMT);
+               /* Wait until commit complete */
+               do {
+                       target_read_u32(target, FLASH_FMC, &flash_fmc);
+               } while (flash_fmc & FMC_COMT);
 
-       /* 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;
+               /* 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;