src: add loader src description
[openocd.git] / src / flash / nor / stellaris.c
index 0ae65dc8ac195242cfc79f42ecebf1e21111d450..069ee8c1c111a86722b11ca7b82519934c598c47 100644 (file)
@@ -22,7 +22,7 @@
  ***************************************************************************/
 
 /***************************************************************************
-* STELLARIS is tested on LM3S811, LM3S6965
+* STELLARIS flash is tested on LM3S811, LM3S6965, LM3s3748, more.
 ***************************************************************************/
 #ifdef HAVE_CONFIG_H
 #include "config.h"
@@ -36,8 +36,7 @@
 
 #define DID0_VER(did0) ((did0 >> 28)&0x07)
 
-static int stellaris_read_part_info(struct flash_bank *bank);
-
+static void stellaris_read_clock_info(struct flash_bank *bank);
 static int stellaris_mass_erase(struct flash_bank *bank);
 
 static struct {
@@ -256,20 +255,16 @@ FLASH_BANK_COMMAND_HANDLER(stellaris_flash_bank_command)
        return ERROR_OK;
 }
 
-static int stellaris_info(struct flash_bank *bank, char *buf, int buf_size)
+static int get_stellaris_info(struct flash_bank *bank, char *buf, int buf_size)
 {
        int printed, device_class;
        struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
 
-       stellaris_read_part_info(bank);
-
        if (stellaris_info->did1 == 0)
-       {
-               printed = snprintf(buf, buf_size, "Cannot identify target as a Stellaris\n");
-               buf += printed;
-               buf_size -= printed;
-               return ERROR_FLASH_OPERATION_FAILED;
-       }
+               return ERROR_FLASH_BANK_NOT_PROBED;
+
+       /* Read main and master clock freqency register */
+       stellaris_read_clock_info(bank);
 
        if (DID0_VER(stellaris_info->did0) > 0)
        {
@@ -495,7 +490,8 @@ static int stellaris_read_part_info(struct flash_bank *bank)
        fam = (did1 >> 24) & 0xF;
        if (((ver != 0) && (ver != 1)) || (fam != 0))
        {
-               LOG_WARNING("Unknown did1 version/family, cannot positively identify target as a Stellaris");
+               LOG_WARNING("Unknown did1 version/family.");
+               return ERROR_FLASH_OPERATION_FAILED;
        }
 
        /* For Sandstorm, Fury, DustDevil:  current data sheets say IOSC
@@ -535,9 +531,10 @@ static int stellaris_read_part_info(struct flash_bank *bank)
                default:
                        LOG_WARNING("Unknown did0 class");
                }
-       default:
                break;
+       default:
                LOG_WARNING("Unknown did0 version");
+               break;
        }
 
        for (i = 0; StellarisParts[i].partno; i++)
@@ -554,22 +551,12 @@ static int stellaris_read_part_info(struct flash_bank *bank)
        stellaris_info->num_lockbits = 1 + (stellaris_info->dc0 & 0xFFFF);
        stellaris_info->num_pages = 2 *(1 + (stellaris_info->dc0 & 0xFFFF));
        stellaris_info->pagesize = 1024;
-       bank->size = 1024 * stellaris_info->num_pages;
        stellaris_info->pages_in_lockregion = 2;
 
-       /* provide this for the benefit of the higher flash driver layers */
-       bank->num_sectors = stellaris_info->num_pages;
-       bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
-       for (i = 0; i < bank->num_sectors; i++)
-       {
-               bank->sectors[i].offset = i * stellaris_info->pagesize;
-               bank->sectors[i].size = stellaris_info->pagesize;
-               bank->sectors[i].is_erased = -1;
-               bank->sectors[i].is_protected = -1;
-       }
-
-       /* Read main and master clock freqency register */
-       stellaris_read_clock_info(bank);
+       /* REVISIT for at least Tempest parts, read NVMSTAT.FWB too.
+        * That exposes a 32-word Flash Write Buffer ... enabling
+        * writes of more than one word at a time.
+        */
 
        return ERROR_OK;
 }
@@ -586,11 +573,7 @@ static int stellaris_protect_check(struct flash_bank *bank)
        unsigned page;
 
        if (stellaris->did1 == 0)
-       {
-               status = stellaris_read_part_info(bank);
-               if (status < 0)
-                       return status;
-       }
+               return ERROR_FLASH_BANK_NOT_PROBED;
 
        for (i = 0; i < (unsigned) bank->num_sectors; i++)
                bank->sectors[i].is_protected = -1;
@@ -642,15 +625,7 @@ static int stellaris_erase(struct flash_bank *bank, int first, int last)
        }
 
        if (stellaris_info->did1 == 0)
-       {
-               stellaris_read_part_info(bank);
-       }
-
-       if (stellaris_info->did1 == 0)
-       {
-               LOG_WARNING("Cannot identify target as Stellaris");
-               return ERROR_FLASH_OPERATION_FAILED;
-       }
+               return ERROR_FLASH_BANK_NOT_PROBED;
 
        if ((first < 0) || (last < first) || (last >= (int)stellaris_info->num_pages))
        {
@@ -670,6 +645,10 @@ static int stellaris_erase(struct flash_bank *bank, int first, int last)
        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.
+        */
+
        for (banknr = first; banknr <= last; banknr++)
        {
                /* Address is first word in page */
@@ -714,31 +693,23 @@ static int stellaris_protect(struct flash_bank *bank, int set, int first, int la
 
        if (!set)
        {
-               LOG_ERROR("Can't unprotect write-protected pages.");
-               /* except by the "recover locked device" procedure ... */
+               LOG_ERROR("Hardware doesn't suppport page-level unprotect. "
+                       "Try the 'recover' command.");
                return ERROR_INVALID_ARGUMENTS;
        }
 
+       if (stellaris_info->did1 == 0)
+               return ERROR_FLASH_BANK_NOT_PROBED;
+
        /* 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 sectors.");
+               LOG_ERROR("Can't protect unaligned or out-of-range pages.");
                return ERROR_FLASH_SECTOR_INVALID;
        }
 
-       if (stellaris_info->did1 == 0)
-       {
-               stellaris_read_part_info(bank);
-       }
-
-       if (stellaris_info->did1 == 0)
-       {
-               LOG_WARNING("Cannot identify target as an Stellaris MCU");
-               return ERROR_FLASH_OPERATION_FAILED;
-       }
-
        /* Refresh flash controller timing */
        stellaris_read_clock_info(bank);
        stellaris_set_flash_timing(bank);
@@ -764,6 +735,10 @@ static int stellaris_protect(struct flash_bank *bank, int set, int first, int la
        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.
+        */
+
        LOG_DEBUG("fmppe 0x%" PRIx32 "",fmppe);
        target_write_u32(target, SCB_BASE | FMPPE, fmppe);
 
@@ -773,6 +748,8 @@ static int stellaris_protect(struct flash_bank *bank, int set, int first, int la
        /* Write commit command */
        /* REVISIT safety check, since this cannot be undone
         * except by the "Recover a locked device" procedure.
+        * REVISIT DustDevil-A0 parts have an erratum making FMPPE commits
+        * inadvisable ... it makes future mass erase operations fail.
         */
        LOG_WARNING("Flash protection cannot be removed once commited, commit is NOT executed !");
        /* target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_COMT); */
@@ -796,6 +773,8 @@ static int stellaris_protect(struct flash_bank *bank, int set, int first, int la
        return ERROR_OK;
 }
 
+/* see contib/loaders/flash/stellaris.s for src */
+
 static const uint8_t stellaris_write_code[] =
 {
 /*
@@ -828,8 +807,7 @@ static const uint8_t stellaris_write_code[] =
        0x04,0x36,                      /* adds r6, r6, #4 */
        0x96,0x42,                      /* cmp  r6, r2 */
        0xF4,0xD1,                      /* bne  mainloop */
-                                               /* exit: */
-       0xFE,0xE7,                      /* b exit */
+       0x00,0xBE,              /* bkpt #0 */
 /* pFLASH_CTRL_BASE: */
        0x00,0xD0,0x0F,0x40,    /* .word        0x400FD000 */
 /* FLASHWRITECMD: */
@@ -840,7 +818,7 @@ static int stellaris_write_block(struct flash_bank *bank,
                uint8_t *buffer, uint32_t offset, uint32_t wcount)
 {
        struct target *target = bank->target;
-       uint32_t buffer_size = 8192;
+       uint32_t buffer_size = 16384;
        struct working_area *source;
        struct working_area *write_algorithm;
        uint32_t address = bank->base + offset;
@@ -848,37 +826,44 @@ static int stellaris_write_block(struct flash_bank *bank,
        struct armv7m_algorithm armv7m_info;
        int retval = ERROR_OK;
 
+       /* power of two, and multiple of word size */
+       static const unsigned buf_min = 128;
+
+       /* for small buffers it's faster not to download an algorithm */
+       if (wcount * 4 < buf_min)
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+
        LOG_DEBUG("(bank=%p buffer=%p offset=%08" PRIx32 " wcount=%08" PRIx32 "",
                        bank, buffer, offset, wcount);
 
        /* flash write code */
        if (target_alloc_working_area(target, sizeof(stellaris_write_code), &write_algorithm) != ERROR_OK)
        {
-               LOG_WARNING("no working area available, can't do block memory writes");
+               LOG_DEBUG("no working area for block memory writes");
                return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        };
 
-       target_write_buffer(target, write_algorithm->address,
-                       sizeof(stellaris_write_code),
-                       (uint8_t *) stellaris_write_code);
+       /* plus a buffer big enough for this data */
+       if (wcount * 4 < buffer_size)
+               buffer_size = wcount * 4;
 
        /* memory buffer */
-       while (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK)
+       while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK)
        {
-               LOG_DEBUG("called target_alloc_working_area(target=%p buffer_size=%08" PRIx32 " source=%p)",
-                               target, buffer_size, source);
                buffer_size /= 2;
-               if (buffer_size <= 256)
+               if (buffer_size <= buf_min)
                {
-                       /* if we already allocated the writing code, but failed to get a buffer, free the algorithm */
-                       if (write_algorithm)
-                               target_free_working_area(target, write_algorithm);
-
-                       LOG_WARNING("no large enough working area available, can't do block memory writes");
+                       target_free_working_area(target, write_algorithm);
                        return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
                }
+               LOG_DEBUG("retry target_alloc_working_area(%s, size=%u)",
+                               target_name(target), (unsigned) buffer_size);
        };
 
+       retval = target_write_buffer(target, write_algorithm->address,
+                       sizeof(stellaris_write_code),
+                       (uint8_t *) stellaris_write_code);
+
        armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
        armv7m_info.core_mode = ARMV7M_MODE_ANY;
 
@@ -895,11 +880,19 @@ static int stellaris_write_block(struct flash_bank *bank,
                buf_set_u32(reg_params[0].value, 0, 32, source->address);
                buf_set_u32(reg_params[1].value, 0, 32, address);
                buf_set_u32(reg_params[2].value, 0, 32, 4*thisrun_count);
-               LOG_INFO("Algorithm flash write %" PRIi32 " words to 0x%" PRIx32 ", %" PRIi32 " remaining", thisrun_count, address, (wcount - thisrun_count));
-               LOG_DEBUG("Algorithm flash write %" PRIi32 " words to 0x%" PRIx32 ", %" PRIi32 " remaining", thisrun_count, address, (wcount - thisrun_count));
-               if ((retval = target_run_algorithm(target, 0, NULL, 3, reg_params, write_algorithm->address, write_algorithm->address + sizeof(stellaris_write_code)-10, 10000, &armv7m_info)) != ERROR_OK)
+               LOG_DEBUG("Algorithm flash write %u words to 0x%" PRIx32
+                               ", %u remaining",
+                               (unsigned) thisrun_count, address,
+                               (unsigned) (wcount - thisrun_count));
+               retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
+                               write_algorithm->address,
+                               0,
+                               10000, &armv7m_info);
+               if (retval != ERROR_OK)
                {
-                       LOG_ERROR("error executing stellaris flash write algorithm");
+                       LOG_ERROR("error %d executing stellaris "
+                                       "flash write algorithm",
+                                       retval);
                        retval = ERROR_FLASH_OPERATION_FAILED;
                        break;
                }
@@ -909,6 +902,10 @@ static int stellaris_write_block(struct flash_bank *bank,
                wcount -= thisrun_count;
        }
 
+       /* REVISIT we could speed up writing multi-section images by
+        * not freeing the initialized write_algorithm this way.
+        */
+
        target_free_working_area(target, write_algorithm);
        target_free_working_area(target, source);
 
@@ -940,15 +937,7 @@ static int stellaris_write(struct flash_bank *bank, uint8_t *buffer, uint32_t of
                        bank, buffer, offset, count);
 
        if (stellaris_info->did1 == 0)
-       {
-               stellaris_read_part_info(bank);
-       }
-
-       if (stellaris_info->did1 == 0)
-       {
-               LOG_WARNING("Cannot identify target as a Stellaris processor");
-               return ERROR_FLASH_OPERATION_FAILED;
-       }
+               return ERROR_FLASH_BANK_NOT_PROBED;
 
        if (offset & 0x3)
        {
@@ -967,17 +956,21 @@ static int stellaris_write(struct flash_bank *bank, uint8_t *buffer, uint32_t of
        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.
+        */
+
        /* multiple words to be programmed? */
        if (words_remaining > 0)
        {
                /* try using a block write */
-               if ((retval = stellaris_write_block(bank, buffer, offset, words_remaining)) != ERROR_OK)
+               retval = stellaris_write_block(bank, buffer, offset,
+                               words_remaining);
+               if (retval != ERROR_OK)
                {
                        if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
                        {
-                               /* if block write failed (no sufficient working area),
-                                * we use normal (slow) single dword accesses */
-                               LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
+                               LOG_DEBUG("writing flash word-at-a-time");
                        }
                        else if (retval == ERROR_FLASH_OPERATION_FAILED)
                        {
@@ -1056,26 +1049,42 @@ static int stellaris_write(struct flash_bank *bank, uint8_t *buffer, uint32_t of
 
 static int stellaris_probe(struct flash_bank *bank)
 {
-       /* we can't probe on an stellaris
-        * if this is an stellaris, it has the configured flash
+       struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
+       int retval;
+
+       /* If this is a stellaris chip, it has flash; probe() is just
+        * to figure out how much is present.  Only do it once.
         */
+       if (stellaris_info->did1 != 0)
+               return ERROR_OK;
 
-       if (bank->target->state != TARGET_HALTED)
+       /* stellaris_read_part_info() already handled error checking and
+        * reporting.  Note that it doesn't write, so we don't care about
+        * whether the target is halted or not.
+        */
+       retval = stellaris_read_part_info(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (bank->sectors)
        {
-               LOG_ERROR("Target not halted");
-               return ERROR_TARGET_NOT_HALTED;
+               free(bank->sectors);
+               bank->sectors = NULL;
        }
 
-       /* stellaris_read_part_info() already takes care about error checking and reporting */
-       return stellaris_read_part_info(bank);
-}
+       /* provide this for the benefit of the NOR flash framework */
+       bank->size = 1024 * stellaris_info->num_pages;
+       bank->num_sectors = stellaris_info->num_pages;
+       bank->sectors = calloc(bank->num_sectors, sizeof(struct flash_sector));
+       for (int i = 0; i < bank->num_sectors; i++)
+       {
+               bank->sectors[i].offset = i * stellaris_info->pagesize;
+               bank->sectors[i].size = stellaris_info->pagesize;
+               bank->sectors[i].is_erased = -1;
+               bank->sectors[i].is_protected = -1;
+       }
 
-static int stellaris_auto_probe(struct flash_bank *bank)
-{
-       struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
-       if (stellaris_info->did1)
-               return ERROR_OK;
-       return stellaris_probe(bank);
+       return retval;
 }
 
 static int stellaris_mass_erase(struct flash_bank *bank)
@@ -1094,15 +1103,7 @@ static int stellaris_mass_erase(struct flash_bank *bank)
        }
 
        if (stellaris_info->did1 == 0)
-       {
-               stellaris_read_part_info(bank);
-       }
-
-       if (stellaris_info->did1 == 0)
-       {
-               LOG_WARNING("Cannot identify target as Stellaris");
-               return ERROR_FLASH_OPERATION_FAILED;
-       }
+               return ERROR_FLASH_BANK_NOT_PROBED;
 
        /* Refresh flash controller timing */
        stellaris_read_clock_info(bank);
@@ -1112,6 +1113,10 @@ static int stellaris_mass_erase(struct flash_bank *bank)
        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.
+        */
+
        target_write_u32(target, FLASH_FMA, 0);
        target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_MERASE);
        /* Wait until erase complete */
@@ -1171,19 +1176,85 @@ COMMAND_HANDLER(stellaris_handle_mass_erase_command)
        return ERROR_OK;
 }
 
+/**
+ * Perform the Stellaris "Recovering a 'Locked' Device procedure.
+ * This performs a mass erase and then restores all nonvolatile registers
+ * (including USER_* registers and flash lock bits) to their defaults.
+ * Accordingly, flash can be reprogrammed, and JTAG can be used.
+ *
+ * NOTE that DustDevil parts (at least rev A0 silicon) have errata which
+ * can affect this operation if flash protection has been enabled.
+ */
+COMMAND_HANDLER(stellaris_handle_recover_command)
+{
+       struct flash_bank *bank;
+       int retval;
+
+       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* REVISIT ... it may be worth sanity checking that the AP is
+        * inactive before we start.  ARM documents that switching a DP's
+        * mode while it's active can cause fault modes that need a power
+        * cycle to recover.
+        */
+
+       /* assert SRST */
+       if (!(jtag_get_reset_config() & RESET_HAS_SRST)) {
+               LOG_ERROR("Can't recover Stellaris flash without SRST");
+               return ERROR_FAIL;
+       }
+       jtag_add_reset(0, 1);
+
+       for (int i = 0; i < 5; i++) {
+               retval = dap_to_swd(bank->target);
+               if (retval != ERROR_OK)
+                       goto done;
+
+               retval = dap_to_jtag(bank->target);
+               if (retval != ERROR_OK)
+                       goto done;
+       }
+
+       /* de-assert SRST */
+       jtag_add_reset(0, 0);
+       retval = jtag_execute_queue();
+
+       /* wait 400+ msec ... OK, "1+ second" is simpler */
+       usleep(1000);
+
+       /* USER INTERVENTION required for the power cycle
+        * Restarting OpenOCD is likely needed because of mode switching.
+        */
+       LOG_INFO("USER ACTION:  "
+               "power cycle Stellaris chip, then restart OpenOCD.");
+
+done:
+       return retval;
+}
+
 static const struct command_registration stellaris_exec_command_handlers[] = {
        {
                .name = "mass_erase",
-               .handler = &stellaris_handle_mass_erase_command,
+               .handler = stellaris_handle_mass_erase_command,
                .mode = COMMAND_EXEC,
+               .usage = "bank_id",
                .help = "erase entire device",
        },
+       {
+               .name = "recover",
+               .handler = stellaris_handle_recover_command,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id",
+               .help = "recover (and erase) locked device",
+       },
        COMMAND_REGISTRATION_DONE
 };
 static const struct command_registration stellaris_command_handlers[] = {
        {
                .name = "stellaris",
-               .mode = COMMAND_ANY,
+               .mode = COMMAND_EXEC,
                .help = "Stellaris flash command group",
                .chain = stellaris_exec_command_handlers,
        },
@@ -1197,9 +1268,10 @@ struct flash_driver stellaris_flash = {
        .erase = stellaris_erase,
        .protect = stellaris_protect,
        .write = stellaris_write,
+       .read = default_flash_read,
        .probe = stellaris_probe,
-       .auto_probe = stellaris_auto_probe,
+       .auto_probe = stellaris_probe,
        .erase_check = default_flash_mem_blank_check,
        .protect_check = stellaris_protect_check,
-       .info = stellaris_info,
+       .info = get_stellaris_info,
 };

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)