jtag: linuxgpiod: drop extra parenthesis
[openocd.git] / src / flash / nor / stm32lx.c
index 061ccb9e996256de044df03a64442403eb3b81f3..1459e942d1e27007e07b27789067ade9600aace7 100644 (file)
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
 /***************************************************************************
  *   Copyright (C) 2005 by Dominic Rath                                    *
  *   Dominic.Rath@gmx.de                                                   *
@@ -7,21 +9,6 @@
  *                                                                         *
  *   Copyright (C) 2011 by Clement Burin des Roziers                       *
  *   clement.burin-des-roziers@hikob.com                                   *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program is distributed in the hope that it will be useful,       *
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
- *   GNU General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
  ***************************************************************************/
 
 #ifdef HAVE_CONFIG_H
@@ -103,8 +90,11 @@ 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_lock(struct flash_bank *bank);
+static int stm32lx_unlock(struct flash_bank *bank);
 static int stm32lx_mass_erase(struct flash_bank *bank);
 static int stm32lx_wait_until_bsy_clear_timeout(struct flash_bank *bank, int timeout);
+static int stm32lx_update_part_info(struct flash_bank *bank, uint16_t flash_size_in_kb);
 
 struct stm32lx_rev {
        uint16_t rev;
@@ -127,26 +117,40 @@ struct stm32lx_part_info {
 };
 
 struct stm32lx_flash_bank {
-       int probed;
+       bool probed;
        uint32_t idcode;
        uint32_t user_bank_size;
        uint32_t flash_base;
 
-       const struct stm32lx_part_info *part_info;
+       struct stm32lx_part_info part_info;
 };
 
 static const struct stm32lx_rev stm32_416_revs[] = {
-       { 0x1000, "A" }, { 0x1008, "Y" }, { 0x1018, "X" }, { 0x1038, "W" },
-       { 0x1078, "V" },
+       { 0x1000, "A" }, { 0x1008, "Y" }, { 0x1038, "W" }, { 0x1078, "V" },
 };
 static const struct stm32lx_rev stm32_417_revs[] = {
-       { 0x1000, "A" }, { 0x1008, "Z" },
+       { 0x1000, "A" }, { 0x1008, "Z" }, { 0x1018, "Y" }, { 0x1038, "X" }
+};
+static const struct stm32lx_rev stm32_425_revs[] = {
+       { 0x1000, "A" }, { 0x2000, "B" }, { 0x2008, "Y" }, { 0x2018, "1, X" },
 };
 static const struct stm32lx_rev stm32_427_revs[] = {
-       { 0x1018, "A" },
+       { 0x1000, "A" }, { 0x1018, "Y" }, { 0x1038, "X" }, { 0x10f8, "V" },
+};
+static const struct stm32lx_rev stm32_429_revs[] = {
+       { 0x1000, "A" }, { 0x1018, "Z" },
 };
 static const struct stm32lx_rev stm32_436_revs[] = {
-       { 0x1000, "A" }, { 0x1008, "Z" }, { 0x1018, "Y" },
+       { 0x1000, "A" }, { 0x1008, "Z" }, { 0x1018, "Y" }, { 0x1038, "X" },
+};
+static const struct stm32lx_rev stm32_437_revs[] = {
+       { 0x1000, "A" },
+};
+static const struct stm32lx_rev stm32_447_revs[] = {
+       { 0x1000, "A" }, { 0x2000, "B" }, { 0x2008, "Z" },
+};
+static const struct stm32lx_rev stm32_457_revs[] = {
+       { 0x1000, "A" }, { 0x1008, "Z" },
 };
 
 static const struct stm32lx_part_info stm32lx_parts[] = {
@@ -154,7 +158,7 @@ static const struct stm32lx_part_info stm32lx_parts[] = {
                .id                                     = 0x416,
                .revs                           = stm32_416_revs,
                .num_revs                       = ARRAY_SIZE(stm32_416_revs),
-               .device_str                     = "STM32L1xx (Low/Medium Density)",
+               .device_str                     = "STM32L1xx (Cat.1 - Low/Medium Density)",
                .page_size                      = 256,
                .pages_per_sector       = 16,
                .max_flash_size_kb      = 128,
@@ -166,7 +170,7 @@ static const struct stm32lx_part_info stm32lx_parts[] = {
                .id                                     = 0x417,
                .revs                           = stm32_417_revs,
                .num_revs                       = ARRAY_SIZE(stm32_417_revs),
-               .device_str                     = "STM32L0xx",
+               .device_str                     = "STM32L0xx (Cat. 3)",
                .page_size                      = 128,
                .pages_per_sector       = 32,
                .max_flash_size_kb      = 64,
@@ -174,24 +178,47 @@ static const struct stm32lx_part_info stm32lx_parts[] = {
                .flash_base                     = 0x40022000,
                .fsize_base                     = 0x1FF8007C,
        },
+       {
+               .id                                     = 0x425,
+               .revs                           = stm32_425_revs,
+               .num_revs                       = ARRAY_SIZE(stm32_425_revs),
+               .device_str                     = "STM32L0xx (Cat. 2)",
+               .page_size                      = 128,
+               .pages_per_sector       = 32,
+               .max_flash_size_kb      = 32,
+               .has_dual_banks         = false,
+               .flash_base                     = 0x40022000,
+               .fsize_base                     = 0x1FF8007C,
+       },
        {
                .id                                     = 0x427,
                .revs                           = stm32_427_revs,
                .num_revs                       = ARRAY_SIZE(stm32_427_revs),
-               .device_str                     = "STM32L1xx (Medium+ Density)",
+               .device_str                     = "STM32L1xx (Cat.3 - Medium+ Density)",
                .page_size                      = 256,
                .pages_per_sector       = 16,
                .max_flash_size_kb      = 256,
-               .first_bank_size_kb     = 192,
-               .has_dual_banks         = true,
+               .has_dual_banks         = false,
                .flash_base                     = 0x40023C00,
                .fsize_base                     = 0x1FF800CC,
        },
+       {
+               .id                                     = 0x429,
+               .revs                           = stm32_429_revs,
+               .num_revs                       = ARRAY_SIZE(stm32_429_revs),
+               .device_str                     = "STM32L1xx (Cat.2)",
+               .page_size                      = 256,
+               .pages_per_sector       = 16,
+               .max_flash_size_kb      = 128,
+               .has_dual_banks         = false,
+               .flash_base                     = 0x40023C00,
+               .fsize_base                     = 0x1FF8004C,
+       },
        {
                .id                                     = 0x436,
                .revs                           = stm32_436_revs,
                .num_revs                       = ARRAY_SIZE(stm32_436_revs),
-               .device_str                     = "STM32L1xx (Medium+/High Density)",
+               .device_str                     = "STM32L1xx (Cat.4/Cat.3 - Medium+/High Density)",
                .page_size                      = 256,
                .pages_per_sector       = 16,
                .max_flash_size_kb      = 384,
@@ -202,15 +229,42 @@ static const struct stm32lx_part_info stm32lx_parts[] = {
        },
        {
                .id                                     = 0x437,
-               .device_str                     = "STM32L1xx (Medium+/High Density)",
+               .revs                           = stm32_437_revs,
+               .num_revs                       = ARRAY_SIZE(stm32_437_revs),
+               .device_str                     = "STM32L1xx (Cat.5/Cat.6)",
                .page_size                      = 256,
                .pages_per_sector       = 16,
                .max_flash_size_kb      = 512,
-               .first_bank_size_kb     = 256,
+               .first_bank_size_kb     = 0,            /* determined in runtime */
                .has_dual_banks         = true,
                .flash_base                     = 0x40023C00,
                .fsize_base                     = 0x1FF800CC,
        },
+       {
+               .id                                     = 0x447,
+               .revs                           = stm32_447_revs,
+               .num_revs                       = ARRAY_SIZE(stm32_447_revs),
+               .device_str                     = "STM32L0xx (Cat.5)",
+               .page_size                      = 128,
+               .pages_per_sector       = 32,
+               .max_flash_size_kb      = 192,
+               .first_bank_size_kb     = 0,            /* determined in runtime */
+               .has_dual_banks         = false,        /* determined in runtime */
+               .flash_base                     = 0x40022000,
+               .fsize_base                     = 0x1FF8007C,
+       },
+       {
+               .id                                     = 0x457,
+               .revs                           = stm32_457_revs,
+               .num_revs                       = ARRAY_SIZE(stm32_457_revs),
+               .device_str                     = "STM32L0xx (Cat.1)",
+               .page_size                      = 128,
+               .pages_per_sector       = 32,
+               .max_flash_size_kb      = 16,
+               .has_dual_banks         = false,
+               .flash_base                     = 0x40022000,
+               .fsize_base                     = 0x1FF8007C,
+       },
 };
 
 /* flash bank stm32lx <base> <size> 0 0 <target#>
@@ -225,44 +279,77 @@ FLASH_BANK_COMMAND_HANDLER(stm32lx_flash_bank_command)
        stm32lx_info = calloc(1, sizeof(*stm32lx_info));
 
        /* Check allocation */
-       if (stm32lx_info == NULL) {
+       if (!stm32lx_info) {
                LOG_ERROR("failed to allocate bank structure");
                return ERROR_FAIL;
        }
 
        bank->driver_priv = stm32lx_info;
 
-       stm32lx_info->probed = 0;
+       stm32lx_info->probed = false;
        stm32lx_info->user_bank_size = bank->size;
 
        /* the stm32l erased value is 0x00 */
-       bank->default_padded_value = 0x00;
+       bank->default_padded_value = bank->erased_value = 0x00;
 
        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)
+       if (retval != ERROR_OK)
                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;
+       if (retval == ERROR_OK)
+               command_print(CMD, "stm32lx mass erase complete");
+       else
+               command_print(CMD, "stm32lx mass erase failed");
 
-               command_print(CMD_CTX, "stm32lx mass erase complete");
-       } else {
-               command_print(CMD_CTX, "stm32lx mass erase failed");
-       }
+       return retval;
+}
+
+COMMAND_HANDLER(stm32lx_handle_lock_command)
+{
+       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 (retval != ERROR_OK)
+               return retval;
+
+       retval = stm32lx_lock(bank);
+
+       if (retval == ERROR_OK)
+               command_print(CMD, "STM32Lx locked, takes effect after power cycle.");
+       else
+               command_print(CMD, "STM32Lx lock failed");
+
+       return retval;
+}
+
+COMMAND_HANDLER(stm32lx_handle_unlock_command)
+{
+       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 (retval != ERROR_OK)
+               return retval;
+
+       retval = stm32lx_unlock(bank);
+
+       if (retval == ERROR_OK)
+               command_print(CMD, "STM32Lx unlocked, takes effect after power cycle.");
+       else
+               command_print(CMD, "STM32Lx unlock failed");
 
        return retval;
 }
@@ -284,7 +371,7 @@ static int stm32lx_protect_check(struct flash_bank *bank)
        if (retval != ERROR_OK)
                return retval;
 
-       for (int i = 0; i < bank->num_sectors; i++) {
+       for (unsigned int i = 0; i < bank->num_sectors; i++) {
                if (wrpr & (1 << i))
                        bank->sectors[i].is_protected = 1;
                else
@@ -293,7 +380,8 @@ static int stm32lx_protect_check(struct flash_bank *bank)
        return ERROR_OK;
 }
 
-static int stm32lx_erase(struct flash_bank *bank, int first, int last)
+static int stm32lx_erase(struct flash_bank *bank, unsigned int first,
+               unsigned int last)
 {
        int retval;
 
@@ -310,7 +398,7 @@ static int stm32lx_erase(struct flash_bank *bank, int first, int last)
        /*
         * Loop over the selected sectors and erase them
         */
-       for (int i = first; i <= last; i++) {
+       for (unsigned int i = first; i <= last; i++) {
                retval = stm32lx_erase_sector(bank, i);
                if (retval != ERROR_OK)
                        return retval;
@@ -319,51 +407,34 @@ static int stm32lx_erase(struct flash_bank *bank, int first, int last)
        return ERROR_OK;
 }
 
-static int stm32lx_protect(struct flash_bank *bank, int set, int first,
-               int last)
-{
-       LOG_WARNING("protection of the STM32L flash is not implemented");
-       return ERROR_OK;
-}
-
 static int stm32lx_write_half_pages(struct flash_bank *bank, const uint8_t *buffer,
                uint32_t offset, uint32_t count)
 {
        struct target *target = bank->target;
        struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
 
-       uint32_t hp_nb = stm32lx_info->part_info->page_size / 2;
-       uint32_t buffer_size = 16384;
+       uint32_t hp_nb = stm32lx_info->part_info.page_size / 2;
+       uint32_t buffer_size = (16384 / hp_nb) * hp_nb; /* must be multiple of hp_nb */
        struct working_area *write_algorithm;
        struct working_area *source;
        uint32_t address = bank->base + offset;
 
-       struct reg_param reg_params[3];
+       struct reg_param reg_params[5];
        struct armv7m_algorithm armv7m_info;
 
        int retval = ERROR_OK;
 
-       /* see contib/loaders/flash/stm32lx.S for src */
-
        static const uint8_t stm32lx_flash_write_code[] = {
-               /* write_word: */
-               0x00, 0x23,             /* movs r3, #0 */
-               0x04, 0xe0,             /* b test_done */
-
-               /* write_word: */
-               0x51, 0xf8, 0x04, 0xcb, /* ldr ip, [r1], #4 */
-               0x40, 0xf8, 0x04, 0xcb, /* str ip, [r0], #4 */
-               0x01, 0x33,             /* adds r3, #1 */
-
-               /* test_done: */
-               0x93, 0x42,             /* cmp r3, r2 */
-               0xf8, 0xd3,             /* bcc write_word */
-               0x00, 0xbe,             /* bkpt 0 */
+#include "../../../contrib/loaders/flash/stm32/stm32lx.inc"
        };
 
        /* Make sure we're performing a half-page aligned write. */
+       if (offset % hp_nb) {
+               LOG_ERROR("The offset must be %" PRIu32 "B-aligned but it is %" PRIi32 "B)", hp_nb, offset);
+               return ERROR_FAIL;
+       }
        if (count % hp_nb) {
-               LOG_ERROR("The byte count must be %" PRIu32 "B-aligned but count is %" PRIi32 "B)", hp_nb, count);
+               LOG_ERROR("The byte count must be %" PRIu32 "B-aligned but count is %" PRIu32 "B)", hp_nb, count);
                return ERROR_FAIL;
        }
 
@@ -372,7 +443,7 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, const uint8_t *buff
                        &write_algorithm) != ERROR_OK) {
                LOG_DEBUG("no working area for block memory writes");
                return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
-       };
+       }
 
        /* Write the flashing code */
        retval = target_write_buffer(target,
@@ -391,13 +462,16 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, const uint8_t *buff
                else
                        buffer_size /= 2;
 
-               if (buffer_size <= stm32lx_info->part_info->page_size) {
+               if (buffer_size <= stm32lx_info->part_info.page_size) {
                        /* we already allocated the writing code, but failed to get a
                         * buffer, free the algorithm */
                        target_free_working_area(target, write_algorithm);
 
                        LOG_WARNING("no large enough working area available, can't do block memory writes");
                        return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+               } else {
+                       /* Make sure we're still asking for an integral number of half-pages */
+                       buffer_size -= buffer_size % hp_nb;
                }
        }
 
@@ -406,6 +480,8 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, const uint8_t *buff
        init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
        init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
        init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
+       init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);
+       init_reg_param(&reg_params[4], "r4", 32, PARAM_OUT);
 
        /* Enable half-page write */
        retval = stm32lx_enable_write_half_page(bank);
@@ -416,11 +492,13 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, const uint8_t *buff
                destroy_reg_param(&reg_params[0]);
                destroy_reg_param(&reg_params[1]);
                destroy_reg_param(&reg_params[2]);
+               destroy_reg_param(&reg_params[3]);
+               destroy_reg_param(&reg_params[4]);
                return retval;
        }
 
        struct armv7m_common *armv7m = target_to_armv7m(target);
-       if (armv7m == NULL) {
+       if (!armv7m) {
 
                /* something is very wrong if armv7m is NULL */
                LOG_ERROR("unable to get armv7m target");
@@ -446,12 +524,16 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, const uint8_t *buff
                buf_set_u32(reg_params[0].value, 0, 32, address);
                /* The source address of the copy (R1) */
                buf_set_u32(reg_params[1].value, 0, 32, source->address);
-               /* The length of the copy (R2) */
-               buf_set_u32(reg_params[2].value, 0, 32, this_count / 4);
+               /* The number of half pages to copy (R2) */
+               buf_set_u32(reg_params[2].value, 0, 32, this_count / hp_nb);
+               /* The size in byes of a half page (R3) */
+               buf_set_u32(reg_params[3].value, 0, 32, hp_nb);
+               /* The flash base address (R4) */
+               buf_set_u32(reg_params[4].value, 0, 32, stm32lx_info->flash_base);
 
                /* 5: Execute the bunch of code */
-               retval = target_run_algorithm(target, 0, NULL, sizeof(reg_params)
-                               / sizeof(*reg_params), reg_params,
+               retval = target_run_algorithm(target, 0, NULL,
+                               ARRAY_SIZE(reg_params), reg_params,
                                write_algorithm->address, 0, 10000, &armv7m_info);
                if (retval != ERROR_OK)
                        break;
@@ -484,7 +566,7 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, const uint8_t *buff
                 * is reduced by 50% using this slower method.
                 */
 
-               LOG_WARNING("couldn't use loader, falling back to page memory writes");
+               LOG_WARNING("Couldn't use loader, falling back to page memory writes");
 
                while (count > 0) {
                        uint32_t this_count;
@@ -515,6 +597,8 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, const uint8_t *buff
        destroy_reg_param(&reg_params[0]);
        destroy_reg_param(&reg_params[1]);
        destroy_reg_param(&reg_params[2]);
+       destroy_reg_param(&reg_params[3]);
+       destroy_reg_param(&reg_params[4]);
 
        return retval;
 }
@@ -525,7 +609,7 @@ static int stm32lx_write(struct flash_bank *bank, const uint8_t *buffer,
        struct target *target = bank->target;
        struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
 
-       uint32_t hp_nb = stm32lx_info->part_info->page_size / 2;
+       uint32_t hp_nb = stm32lx_info->part_info.page_size / 2;
        uint32_t halfpages_number;
        uint32_t bytes_remaining = 0;
        uint32_t address = bank->base + offset;
@@ -546,7 +630,7 @@ static int stm32lx_write(struct flash_bank *bank, const uint8_t *buffer,
        if (retval != ERROR_OK)
                return retval;
 
-       /* first we need to write any unaligned head bytes upto
+       /* first we need to write any unaligned head bytes up to
         * the next 128 byte page */
 
        if (offset % hp_nb)
@@ -633,16 +717,13 @@ reset_pg_and_lock:
 
 static int stm32lx_read_id_code(struct target *target, uint32_t *id)
 {
-       /* read stm32 device id register */
-       int retval = target_read_u32(target, DBGMCU_IDCODE, id);
-       if (retval != ERROR_OK)
-               return retval;
-
-       /* STM32L0 parts will have 0 there, try reading the L0's location for
-        * DBG_IDCODE in case this is an L0 part. */
-       if (*id == 0)
+       struct armv7m_common *armv7m = target_to_armv7m(target);
+       int retval;
+       if (armv7m->arm.arch == ARM_ARCH_V6M)
                retval = target_read_u32(target, DBGMCU_IDCODE_L0, id);
-
+       else
+       /* read stm32 device id register */
+               retval = target_read_u32(target, DBGMCU_IDCODE, id);
        return retval;
 }
 
@@ -650,14 +731,13 @@ static int stm32lx_probe(struct flash_bank *bank)
 {
        struct target *target = bank->target;
        struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
-       int i;
        uint16_t flash_size_in_kb;
        uint32_t device_id;
        uint32_t base_address = FLASH_BANK0_ADDRESS;
        uint32_t second_bank_base;
+       unsigned int n;
 
-       stm32lx_info->probed = 0;
-       stm32lx_info->part_info = NULL;
+       stm32lx_info->probed = false;
 
        int retval = stm32lx_read_id_code(bank->target, &device_id);
        if (retval != ERROR_OK)
@@ -667,20 +747,24 @@ static int stm32lx_probe(struct flash_bank *bank)
 
        LOG_DEBUG("device id = 0x%08" PRIx32 "", device_id);
 
-       for (unsigned int n = 0; n < ARRAY_SIZE(stm32lx_parts); n++) {
-               if ((device_id & 0xfff) == stm32lx_parts[n].id)
-                       stm32lx_info->part_info = &stm32lx_parts[n];
+       for (n = 0; n < ARRAY_SIZE(stm32lx_parts); n++) {
+               if ((device_id & 0xfff) == stm32lx_parts[n].id) {
+                       stm32lx_info->part_info = stm32lx_parts[n];
+                       break;
+               }
        }
 
-       if (!stm32lx_info->part_info) {
-               LOG_WARNING("Cannot identify target as a STM32L family.");
+       if (n == ARRAY_SIZE(stm32lx_parts)) {
+               LOG_ERROR("Cannot identify target as an STM32 L0 or L1 family device.");
                return ERROR_FAIL;
+       } else {
+               LOG_INFO("Device: %s", stm32lx_info->part_info.device_str);
        }
 
-       stm32lx_info->flash_base = stm32lx_info->part_info->flash_base;
+       stm32lx_info->flash_base = stm32lx_info->part_info.flash_base;
 
        /* Get the flash size from target. */
-       retval = target_read_u16(target, stm32lx_info->part_info->fsize_base,
+       retval = target_read_u16(target, stm32lx_info->part_info.fsize_base,
                        &flash_size_in_kb);
 
        /* 0x436 devices report their flash size as a 0 or 1 code indicating 384K
@@ -693,40 +777,51 @@ static int stm32lx_probe(struct flash_bank *bank)
                        flash_size_in_kb = 256;
        }
 
+       /* 0x429 devices only use the lowest 8 bits of the flash size register */
+       if (retval == ERROR_OK && (device_id & 0xfff) == 0x429) {
+               flash_size_in_kb &= 0xff;
+       }
+
        /* Failed reading flash size or flash size invalid (early silicon),
         * default to max target family */
        if (retval != ERROR_OK || flash_size_in_kb == 0xffff || flash_size_in_kb == 0) {
                LOG_WARNING("STM32L flash size failed, probe inaccurate - assuming %dk flash",
-                       stm32lx_info->part_info->max_flash_size_kb);
-               flash_size_in_kb = stm32lx_info->part_info->max_flash_size_kb;
-       } else if (flash_size_in_kb > stm32lx_info->part_info->max_flash_size_kb) {
+                       stm32lx_info->part_info.max_flash_size_kb);
+               flash_size_in_kb = stm32lx_info->part_info.max_flash_size_kb;
+       } else if (flash_size_in_kb > stm32lx_info->part_info.max_flash_size_kb) {
                LOG_WARNING("STM32L probed flash size assumed incorrect since FLASH_SIZE=%dk > %dk, - assuming %dk flash",
-                       flash_size_in_kb, stm32lx_info->part_info->max_flash_size_kb,
-                       stm32lx_info->part_info->max_flash_size_kb);
-               flash_size_in_kb = stm32lx_info->part_info->max_flash_size_kb;
+                       flash_size_in_kb, stm32lx_info->part_info.max_flash_size_kb,
+                       stm32lx_info->part_info.max_flash_size_kb);
+               flash_size_in_kb = stm32lx_info->part_info.max_flash_size_kb;
        }
 
-       if (stm32lx_info->part_info->has_dual_banks) {
+       /* Overwrite default dual-bank configuration */
+       retval = stm32lx_update_part_info(bank, flash_size_in_kb);
+       if (retval != ERROR_OK)
+               return ERROR_FAIL;
+
+       if (stm32lx_info->part_info.has_dual_banks) {
                /* Use the configured base address to determine if this is the first or second flash bank.
                 * Verify that the base address is reasonably correct and determine the flash bank size
                 */
                second_bank_base = base_address +
-                       stm32lx_info->part_info->first_bank_size_kb * 1024;
+                       stm32lx_info->part_info.first_bank_size_kb * 1024;
                if (bank->base == second_bank_base || !bank->base) {
                        /* This is the second bank  */
                        base_address = second_bank_base;
                        flash_size_in_kb = flash_size_in_kb -
-                               stm32lx_info->part_info->first_bank_size_kb;
+                               stm32lx_info->part_info.first_bank_size_kb;
                } else if (bank->base == base_address) {
                        /* This is the first bank */
-                       flash_size_in_kb = stm32lx_info->part_info->first_bank_size_kb;
+                       flash_size_in_kb = stm32lx_info->part_info.first_bank_size_kb;
                } else {
-                       LOG_WARNING("STM32L flash bank base address config is incorrect."
-                                   " 0x%" PRIx32 " but should rather be 0x%" PRIx32 " or 0x%" PRIx32,
+                       LOG_WARNING("STM32L flash bank base address config is incorrect. "
+                                       TARGET_ADDR_FMT " but should rather be 0x%" PRIx32
+                                       " or 0x%" PRIx32,
                                                bank->base, base_address, second_bank_base);
                        return ERROR_FAIL;
                }
-               LOG_INFO("STM32L flash has dual banks. Bank (%d) size is %dkb, base address is 0x%" PRIx32,
+               LOG_INFO("STM32L flash has dual banks. Bank (%u) size is %dkb, base address is 0x%" PRIx32,
                                bank->bank_number, flash_size_in_kb, base_address);
        } else {
                LOG_INFO("STM32L flash size is %dkb, base address is 0x%" PRIx32, flash_size_in_kb, base_address);
@@ -740,30 +835,27 @@ static int stm32lx_probe(struct flash_bank *bank)
        }
 
        /* calculate numbers of sectors (4kB per sector) */
-       int num_sectors = (flash_size_in_kb * 1024) / FLASH_SECTOR_SIZE;
+       unsigned int num_sectors = (flash_size_in_kb * 1024) / FLASH_SECTOR_SIZE;
 
-       if (bank->sectors) {
-               free(bank->sectors);
-               bank->sectors = NULL;
-       }
+       free(bank->sectors);
 
        bank->size = flash_size_in_kb * 1024;
        bank->base = base_address;
        bank->num_sectors = num_sectors;
        bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
-       if (bank->sectors == NULL) {
+       if (!bank->sectors) {
                LOG_ERROR("failed to allocate bank sectors");
                return ERROR_FAIL;
        }
 
-       for (i = 0; i < num_sectors; i++) {
+       for (unsigned int i = 0; i < num_sectors; i++) {
                bank->sectors[i].offset = i * FLASH_SECTOR_SIZE;
                bank->sectors[i].size = FLASH_SECTOR_SIZE;
                bank->sectors[i].is_erased = -1;
-               bank->sectors[i].is_protected = 1;
+               bank->sectors[i].is_protected = -1;
        }
 
-       stm32lx_info->probed = 1;
+       stm32lx_info->probed = true;
 
        return ERROR_OK;
 }
@@ -778,96 +870,33 @@ static int stm32lx_auto_probe(struct flash_bank *bank)
        return stm32lx_probe(bank);
 }
 
-static int stm32lx_erase_check(struct flash_bank *bank)
-{
-       struct target *target = bank->target;
-       const int buffer_size = 4096;
-       int i;
-       uint32_t nBytes;
-       int retval = ERROR_OK;
-
-       if (bank->target->state != TARGET_HALTED) {
-               LOG_ERROR("Target not halted");
-               return ERROR_TARGET_NOT_HALTED;
-       }
-
-       uint8_t *buffer = malloc(buffer_size);
-       if (buffer == NULL) {
-               LOG_ERROR("failed to allocate read buffer");
-               return ERROR_FAIL;
-       }
-
-       for (i = 0; i < bank->num_sectors; i++) {
-               uint32_t j;
-               bank->sectors[i].is_erased = 1;
-
-               /* Loop chunk by chunk over the sector */
-               for (j = 0; j < bank->sectors[i].size; j += buffer_size) {
-                       uint32_t chunk;
-                       chunk = buffer_size;
-                       if (chunk > (j - bank->sectors[i].size))
-                               chunk = (j - bank->sectors[i].size);
-
-                       retval = target_read_memory(target, bank->base
-                                       + bank->sectors[i].offset + j, 4, chunk / 4, buffer);
-                       if (retval != ERROR_OK)
-                               break;
-
-                       for (nBytes = 0; nBytes < chunk; nBytes++) {
-                               if (buffer[nBytes] != 0x00) {
-                                       bank->sectors[i].is_erased = 0;
-                                       break;
-                               }
-                       }
-               }
-               if (retval != ERROR_OK)
-                       break;
-       }
-       free(buffer);
-
-       return retval;
-}
-
 /* This method must return a string displaying information about the bank */
-static int stm32lx_get_info(struct flash_bank *bank, char *buf, int buf_size)
+static int stm32lx_get_info(struct flash_bank *bank, struct command_invocation *cmd)
 {
        struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
+       const struct stm32lx_part_info *info = &stm32lx_info->part_info;
+       uint16_t rev_id = stm32lx_info->idcode >> 16;
+       const char *rev_str = NULL;
 
        if (!stm32lx_info->probed) {
                int retval = stm32lx_probe(bank);
                if (retval != ERROR_OK) {
-                       snprintf(buf, buf_size,
-                               "Unable to find bank information.");
+                       command_print_sameline(cmd, "Unable to find bank information.");
                        return retval;
                }
        }
 
-       const struct stm32lx_part_info *info = stm32lx_info->part_info;
-
-       if (info) {
-               const char *rev_str = NULL;
-               uint16_t rev_id = stm32lx_info->idcode >> 16;
-
-               for (unsigned int i = 0; i < info->num_revs; i++)
-                       if (rev_id == info->revs[i].rev)
-                               rev_str = info->revs[i].str;
-
-               if (rev_str != NULL) {
-                       snprintf(buf, buf_size,
-                               "%s - Rev: %s",
-                               stm32lx_info->part_info->device_str, rev_str);
-               } else {
-                       snprintf(buf, buf_size,
-                               "%s - Rev: unknown (0x%04x)",
-                               stm32lx_info->part_info->device_str, rev_id);
-               }
+       for (unsigned int i = 0; i < info->num_revs; i++)
+               if (rev_id == info->revs[i].rev)
+                       rev_str = info->revs[i].str;
 
-               return ERROR_OK;
+       if (rev_str) {
+               command_print_sameline(cmd, "%s - Rev: %s", info->device_str, rev_str);
        } else {
-               snprintf(buf, buf_size, "Cannot identify target as a STM32Lx");
-
-               return ERROR_FAIL;
+               command_print_sameline(cmd, "%s - Rev: unknown (0x%04x)", info->device_str, rev_id);
        }
+
+       return ERROR_OK;
 }
 
 static const struct command_registration stm32lx_exec_command_handlers[] = {
@@ -878,6 +907,20 @@ static const struct command_registration stm32lx_exec_command_handlers[] = {
                .usage = "bank_id",
                .help = "Erase entire flash device. including available EEPROM",
        },
+       {
+               .name = "lock",
+               .handler = stm32lx_handle_lock_command,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id",
+               .help = "Increase the readout protection to Level 1.",
+       },
+       {
+               .name = "unlock",
+               .handler = stm32lx_handle_unlock_command,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id",
+               .help = "Lower the readout protection from Level 1 to 0.",
+       },
        COMMAND_REGISTRATION_DONE
 };
 
@@ -892,19 +935,19 @@ static const struct command_registration stm32lx_command_handlers[] = {
        COMMAND_REGISTRATION_DONE
 };
 
-struct flash_driver stm32lx_flash = {
+const struct flash_driver stm32lx_flash = {
                .name = "stm32lx",
                .commands = stm32lx_command_handlers,
                .flash_bank_command = stm32lx_flash_bank_command,
                .erase = stm32lx_erase,
-               .protect = stm32lx_protect,
                .write = stm32lx_write,
                .read = default_flash_read,
                .probe = stm32lx_probe,
                .auto_probe = stm32lx_auto_probe,
-               .erase_check = stm32lx_erase_check,
+               .erase_check = default_flash_blank_check,
                .protect_check = stm32lx_protect_check,
                .info = stm32lx_get_info,
+               .free_driver_priv = default_flash_free_driver_priv,
 };
 
 /* Static methods implementation */
@@ -1062,7 +1105,7 @@ static int stm32lx_erase_sector(struct flash_bank *bank, int sector)
        if (retval != ERROR_OK)
                return retval;
 
-       for (int page = 0; page < (int)stm32lx_info->part_info->pages_per_sector;
+       for (int page = 0; page < (int)stm32lx_info->part_info.pages_per_sector;
                        page++) {
                reg32 = FLASH_PECR__PROG | FLASH_PECR__ERASE;
                retval = target_write_u32(target,
@@ -1075,7 +1118,7 @@ static int stm32lx_erase_sector(struct flash_bank *bank, int sector)
                        return retval;
 
                uint32_t addr = bank->base + bank->sectors[sector].offset + (page
-                               * stm32lx_info->part_info->page_size);
+                               * stm32lx_info->part_info.page_size);
                retval = target_write_u32(target, addr, 0x0);
                if (retval != ERROR_OK)
                        return retval;
@@ -1211,33 +1254,37 @@ static int stm32lx_obl_launch(struct flash_bank *bank)
        return tries ? ERROR_OK : ERROR_FAIL;
 }
 
-static int stm32lx_mass_erase(struct flash_bank *bank)
+static int stm32lx_lock(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 */
        /* set the RDP protection level to 1 */
        retval = target_write_u32(target, OPTION_BYTES_ADDRESS, OPTION_BYTE_0_PR1);
        if (retval != ERROR_OK)
                return retval;
 
-       retval = stm32lx_obl_launch(bank);
-       if (retval != ERROR_OK)
-               return retval;
+       return ERROR_OK;
+}
+
+static int stm32lx_unlock(struct flash_bank *bank)
+{
+       int retval;
+       struct target *target = bank->target;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
 
        retval = stm32lx_unlock_options_bytes(bank);
        if (retval != ERROR_OK)
@@ -1252,6 +1299,35 @@ static int stm32lx_mass_erase(struct flash_bank *bank)
        if (retval != ERROR_OK)
                return retval;
 
+       return ERROR_OK;
+}
+
+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_lock(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = stm32lx_obl_launch(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = stm32lx_unlock(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
        retval = stm32lx_obl_launch(bank);
        if (retval != ERROR_OK)
                return retval;
@@ -1266,3 +1342,22 @@ static int stm32lx_mass_erase(struct flash_bank *bank)
 
        return ERROR_OK;
 }
+
+static int stm32lx_update_part_info(struct flash_bank *bank, uint16_t flash_size_in_kb)
+{
+       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
+
+       switch (stm32lx_info->part_info.id) {
+       case 0x447: /* STM32L0xx (Cat.5) devices */
+               if (flash_size_in_kb == 192 || flash_size_in_kb == 128) {
+                       stm32lx_info->part_info.first_bank_size_kb = flash_size_in_kb / 2;
+                       stm32lx_info->part_info.has_dual_banks = true;
+               }
+               break;
+       case 0x437: /* STM32L1xx (Cat.5/Cat.6) */
+               stm32lx_info->part_info.first_bank_size_kb = flash_size_in_kb / 2;
+               break;
+       }
+
+       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)