stm32: add mass erase support for STM32L 19/2319/4
authorRémi PRUD'HOMME <prudhomme.remi@gmail.com>
Wed, 1 Oct 2014 21:43:03 +0000 (22:43 +0100)
committerSpencer Oliver <spen@spen-soft.co.uk>
Mon, 6 Oct 2014 18:41:30 +0000 (18:41 +0000)
The mass erase for STM32L was lack because the procedure is more complex
than the procedure for the STM32F4xx.

The reference manual RM0038 (L100 subfamily) page 79 is more accurate
than the reference manual for the STM32L0xx. On the L0, the mass-erase
erase also the EEPROM. This is a limit to mass erase on L0.

The mass erase procedure is a command of telnet interface.

Tested on Discovery L053 and Discovery L100.

Change-Id: I6a1d7a3669789aea89c59a006ab2d883f3d827ca
Signed-off-by: Rémi PRUD'HOMME <prudhomme.remi@gmail.com>
Reviewed-on: http://openocd.zylin.com/2319
Tested-by: jenkins
Reviewed-by: Andrey Yurovsky <yurovsky@gmail.com>
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
src/flash/nor/stm32lx.c

index dc1a0c40c27aa5edef686b1fbba2dc0361c0597e..e5b66cf7fdc7d72a8e8223f602faa976dd3001a3 100644 (file)
 #define FLASH_SECTOR_SIZE 4096
 #define FLASH_BANK0_ADDRESS 0x08000000
 
 #define FLASH_SECTOR_SIZE 4096
 #define FLASH_BANK0_ADDRESS 0x08000000
 
+/* option bytes */
+#define OPTION_BYTES_ADDRESS 0x1FF80000
+
+#define OPTION_BYTE_0_PR1 0x015500AA
+#define OPTION_BYTE_0_PR0 0x01FF0011
+
 static int stm32lx_unlock_program_memory(struct flash_bank *bank);
 static int stm32lx_lock_program_memory(struct flash_bank *bank);
 static int stm32lx_enable_write_half_page(struct flash_bank *bank);
 static int stm32lx_erase_sector(struct flash_bank *bank, int sector);
 static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank);
 static int stm32lx_unlock_program_memory(struct flash_bank *bank);
 static int stm32lx_lock_program_memory(struct flash_bank *bank);
 static int stm32lx_enable_write_half_page(struct flash_bank *bank);
 static int stm32lx_erase_sector(struct flash_bank *bank, int sector);
 static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank);
+static int stm32lx_mass_erase(struct flash_bank *bank);
+static int stm32lx_wait_status_busy(struct flash_bank *bank, int timeout);
 
 struct stm32lx_rev {
        uint16_t rev;
 
 struct stm32lx_rev {
        uint16_t rev;
@@ -233,6 +241,32 @@ FLASH_BANK_COMMAND_HANDLER(stm32lx_flash_bank_command)
        return ERROR_OK;
 }
 
        return ERROR_OK;
 }
 
+COMMAND_HANDLER(stm32lx_handle_mass_erase_command)
+{
+       int i;
+
+       if (CMD_ARGC < 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       struct flash_bank *bank;
+       int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (ERROR_OK != retval)
+               return retval;
+
+       retval = stm32lx_mass_erase(bank);
+       if (retval == ERROR_OK) {
+               /* set all sectors as erased */
+               for (i = 0; i < bank->num_sectors; i++)
+                       bank->sectors[i].is_erased = 1;
+
+               command_print(CMD_CTX, "stm32lx mass erase complete");
+       } else {
+               command_print(CMD_CTX, "stm32lx mass erase failed");
+       }
+
+       return retval;
+}
+
 static int stm32lx_protect_check(struct flash_bank *bank)
 {
        int retval;
 static int stm32lx_protect_check(struct flash_bank *bank)
 {
        int retval;
@@ -273,6 +307,9 @@ static int stm32lx_erase(struct flash_bank *bank, int first, int last)
                return ERROR_TARGET_NOT_HALTED;
        }
 
                return ERROR_TARGET_NOT_HALTED;
        }
 
+       if ((first == 0) && (last == (bank->num_sectors - 1)))
+               return stm32lx_mass_erase(bank);
+
        /*
         * Loop over the selected sectors and erase them
         */
        /*
         * Loop over the selected sectors and erase them
         */
@@ -325,7 +362,6 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, const uint8_t *buff
                0x93, 0x42,             /* cmp r3, r2 */
                0xf8, 0xd3,             /* bcc write_word */
                0x00, 0xbe,             /* bkpt 0 */
                0x93, 0x42,             /* cmp r3, r2 */
                0xf8, 0xd3,             /* bcc write_word */
                0x00, 0xbe,             /* bkpt 0 */
-
        };
 
        /* Make sure we're performing a half-page aligned write. */
        };
 
        /* Make sure we're performing a half-page aligned write. */
@@ -809,7 +845,6 @@ static int stm32lx_get_info(struct flash_bank *bank, char *buf, int buf_size)
                }
        }
 
                }
        }
 
-
        const struct stm32lx_part_info *info = stm32lx_info->part_info;
 
        if (info) {
        const struct stm32lx_part_info *info = stm32lx_info->part_info;
 
        if (info) {
@@ -839,6 +874,13 @@ static int stm32lx_get_info(struct flash_bank *bank, char *buf, int buf_size)
 }
 
 static const struct command_registration stm32lx_exec_command_handlers[] = {
 }
 
 static const struct command_registration stm32lx_exec_command_handlers[] = {
+       {
+               .name = "mass_erase",
+               .handler = stm32lx_handle_mass_erase_command,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id",
+               .help = "Erase entire flash device. including available EEPROM",
+       },
        COMMAND_REGISTRATION_DONE
 };
 
        COMMAND_REGISTRATION_DONE
 };
 
@@ -1053,6 +1095,14 @@ static int stm32lx_erase_sector(struct flash_bank *bank, int sector)
        return ERROR_OK;
 }
 
        return ERROR_OK;
 }
 
+static inline int stm32lx_get_flash_status(struct flash_bank *bank, uint32_t *status)
+{
+       struct target *target = bank->target;
+       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
+
+       return target_read_u32(target, stm32lx_info->flash_base + FLASH_SR, status);
+}
+
 static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank)
 {
        struct target *target = bank->target;
 static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank)
 {
        struct target *target = bank->target;
@@ -1063,8 +1113,7 @@ static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank)
 
        /* wait for busy to clear */
        for (;;) {
 
        /* wait for busy to clear */
        for (;;) {
-               retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_SR,
-                               &status);
+               retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_SR, &status);
                if (retval != ERROR_OK)
                        return retval;
 
                if (retval != ERROR_OK)
                        return retval;
 
@@ -1089,3 +1138,127 @@ static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank)
 
        return retval;
 }
 
        return retval;
 }
+
+static int stm32lx_unlock_options_bytes(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
+       int retval;
+       uint32_t reg32;
+
+       /*
+       * Unlocking the options bytes is done by unlocking the PECR,
+       * then by writing the 2 FLASH_PEKEYR to the FLASH_OPTKEYR register
+       */
+
+       /* check flash is not already unlocked */
+       retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR, &reg32);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if ((reg32 & FLASH_PECR__OPTLOCK) == 0)
+               return ERROR_OK;
+
+       if ((reg32 & FLASH_PECR__PELOCK) != 0) {
+
+               retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PEKEYR, PEKEY1);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PEKEYR, PEKEY2);
+               if (retval != ERROR_OK)
+                       return retval;
+       }
+
+       /* To unlock the PECR write the 2 OPTKEY to the FLASH_OPTKEYR register */
+       retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_OPTKEYR, OPTKEY1);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_OPTKEYR, OPTKEY2);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return ERROR_OK;
+}
+
+static int stm32lx_wait_status_busy(struct flash_bank *bank, int timeout)
+{
+       struct target *target = bank->target;
+       uint32_t status;
+
+       int retval = ERROR_OK;
+       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
+
+       /* wait for busy to clear */
+       for (;;) {
+
+               retval = stm32lx_get_flash_status(bank, &status);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               LOG_DEBUG("status: 0x%" PRIx32 "", status);
+               if ((status & FLASH_SR__BSY) == 0)
+                       break;
+
+               if (timeout-- <= 0) {
+                       LOG_ERROR("timed out waiting for flash");
+                       return ERROR_FAIL;
+               }
+               alive_sleep(1);
+       }
+
+       if (status & FLASH_SR__WRPERR) {
+               LOG_ERROR("stm32lx device protected");
+               retval = ERROR_FAIL;
+       }
+
+       /* Clear but report errors */
+       if (status & FLASH_SR__OPTVERR) {
+               /* If this operation fails, we ignore it and report the original retval */
+               target_write_u32(target, stm32lx_info->flash_base + FLASH_SR, status & FLASH_SR__OPTVERR);
+       }
+
+       return retval;
+}
+
+static int stm32lx_mass_erase(struct flash_bank *bank)
+{
+       int retval;
+       struct target *target = bank->target;
+       struct stm32lx_flash_bank *stm32lx_info = NULL;
+       uint32_t reg32;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       stm32lx_info = bank->driver_priv;
+
+       retval = stm32lx_unlock_options_bytes(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* mass erase flash memory, write 0x015500AA option byte address 0*/
+       /* pass the RDP privilege to 1 */
+       retval = target_write_u32(target, OPTION_BYTES_ADDRESS, OPTION_BYTE_0_PR1);
+
+       /* restore the RDP privilege to 0 */
+       retval = target_write_u32(target, OPTION_BYTES_ADDRESS, OPTION_BYTE_0_PR0);
+
+       /* the mass erase occur when the privilege go back to 0 */
+       retval = stm32lx_wait_status_busy(bank, 30000);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR, &reg32);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PECR, reg32 | FLASH_PECR__OPTLOCK);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return ERROR_OK;
+}

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)