This is an automated email from Gerrit. Rémi PRUD'HOMME (prudhomme.r...@gmail.com) just uploaded a new patch set to Gerrit, which you can find at http://openocd.zylin.com/2753
-- gerrit commit 0b8e7d1785f70575f03621b56abbca4776889373 Author: Rémi PRUD’HOMME <prudhomme.r...@gmail.com> Date: Wed Apr 29 15:38:52 2015 +0200 [new flash driver] flash/nor/stm32f7x is a new flash driver for the new STM serie STM32F7xx (Cortex M7). The flash driver has been tested on a eval board STM32756G-EVAL and STM32F746-DISCO. This driver programm the flash in the 32 bit mode, and implement memory barrier (lds) between two flash words programming. Change-Id: Ia99504b600bdeabc6f9a1cac9c0c2d7aa2494878 Signed-off-by: Rémi PRUD'HOMME <prudhomme.r...@gmail.com> diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 878fc26..d246134 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -33,6 +33,7 @@ NOR_DRIVERS = \ stm32f1x.c \ stm32f2x.c \ stm32lx.c \ + stm32f7x.c \ str7x.c \ str9x.c \ str9xpec.c \ diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index fead797..ead3715 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -40,6 +40,7 @@ extern struct flash_driver str9xpec_flash; extern struct flash_driver stm32f1x_flash; extern struct flash_driver stm32f2x_flash; extern struct flash_driver stm32lx_flash; +extern struct flash_driver stm32f7x_flash; extern struct flash_driver tms470_flash; extern struct flash_driver ocl_flash; extern struct flash_driver pic32mx_flash; @@ -83,6 +84,7 @@ static struct flash_driver *flash_drivers[] = { &stm32f1x_flash, &stm32f2x_flash, &stm32lx_flash, + &stm32f7x_flash, &tms470_flash, &ocl_flash, &pic32mx_flash, diff --git a/src/flash/nor/stm32f7x.c b/src/flash/nor/stm32f7x.c new file mode 100755 index 0000000..d2153f0 --- /dev/null +++ b/src/flash/nor/stm32f7x.c @@ -0,0 +1,1058 @@ +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include <helper/binarybuffer.h> +#include <target/algorithm.h> +#include <target/armv7m.h> + +/* + * To reduce testing complexity and dangers of regressions, + * a seperate file is used for stm32F7xx series. + */ + +/* Erase time can be as high as 1000ms, 10x this and it's toast... */ +#define FLASH_ERASE_TIMEOUT 10000 +#define FLASH_WRITE_TIMEOUT 5 + +/* rm 0351 */ +#define FLASH_ACR 0x00 +#define FLASH_KEYR 0x04 +#define FLASH_OPTKEYR 0x08 +#define FLASH_SR 0x0C +#define FLASH_CR 0x10 +#define FLASH_OPTR 0x14 +#define FLASH_OPTCR1 0x18 + +/* FLASH_ACR bits */ +#define FLASH_ACR__LATENCY (1<<0) +#define FLASH_ACR__ARTEN (1<<9) +#define FLASH_ACR__ARTRST (1<<11) + +/* FLASH_CR register bits */ +#define FLASH_PG (1 << 0) +#define FLASH_SER (1 << 1) +#define FLASH_MER (1 << 2) +#define FLASH_PSNB (1 << 3) +#define FLASH_PSIZE (1 << 8) +#define FLASH_START (1 << 16) +#define FLASH_EOPIE (1 << 24) +#define FLASH_ERRIE (1 << 25) +#define FLASH_LOCK (1 << 31) + +#define FLASH_PSIZE_8 (0 << 8) +#define FLASH_PSIZE_16 (1 << 8) +#define FLASH_PSIZE_32 (2 << 8) +#define FLASH_PSIZE_64 (3 << 8) + +#define FLASH_SNB(a) ((((a) >= 12) ? 0x10 | ((a) - 12) : (a)) << 3) + + +/* FLASH_SR register bits */ +#define FLASH_BSY (1 << 16) /* Operation in progres */ +#define FLASH_PGSERR (1 << 7) /* Programming sequence error */ +#define FLASH_PGPERR (1 << 6) /* Programming parallelism error */ +#define FLASH_PGAERR (1 << 5) /* Programming alignment error */ +#define FLASH_WRPERR (1 << 4) /* Write protection error */ +#define FLASH_PROGERR (1 << 3) /* Write protection error */ +#define FLASH_OPERR (1 << 1) /* Operation error */ +#define FLASH_EOP (1 << 0) /* End of operation */ + +#define FLASH_ERROR (FLASH_PROGERR | FLASH_PGSERR | FLASH_PGPERR | FLASH_PGAERR | FLASH_WRPERR | FLASH_OPERR) + +/* STM32_FLASH_OPTR register bits */ +#define OPT_LOCK (1 << 0) +#define OPT_START (1 << 1) + +/* STM32_FLASH_OBR bit definitions (reading) */ +#define OPT_ERROR 0 +#define OPT_READOUT 1 +#define OPT_RDWDGSW 2 +#define OPT_RDRSTSTOP 3 +#define OPT_RDRSTSTDBY 4 + +/* register unlock keys */ +#define KEY1 0x45670123 +#define KEY2 0xCDEF89AB + +/* option register unlock key */ +#define OPTKEY1 0x08192A3B +#define OPTKEY2 0x4C5D6E7F + +/* option bytes */ +#define OPTION_BYTES_ADDRESS 0x1FFC000 + +#define OPTION_BYTE_0_PR1 0x015500AA +#define OPTION_BYTE_0_PR0 0x01FF0011 + +#define DBGMCU_IDCODE_REGISTER 0xE0042000 +#define FLASH_BANK0_ADDRESS 0x08000000 + +#define FLASH_BASE 0x40023C00 + + +struct stm32f7x_rev { + uint16_t rev; + const char *str; +}; + +struct stm32x_options { + uint8_t RDP; + uint8_t user_options; + uint32_t protection; +}; + +struct stm32f7x_part_info { + uint16_t id; + const char *device_str; + const struct stm32f7x_rev *revs; + size_t num_revs; + unsigned int page_size; + unsigned int pages_per_sector; + uint16_t max_flash_size_kb; + uint16_t user_bank_size; + uint32_t flash_base; /* Flash controller registers location */ + uint32_t fsize_base; /* Location of FSIZE register */ +}; + +struct stm32f7x_flash_bank { + int probed; + uint32_t idcode; + uint32_t user_bank_size; + uint32_t flash_base; /* Address of flash memory */ + struct stm32x_options option_bytes; + const struct stm32f7x_part_info *part_info; +}; + +static const struct stm32f7x_rev stm32_449_revs[] = { + { 0x1000, "A" }, { 0x1001, "Z" }, +}; + +static const struct stm32f7x_part_info stm32f7x_parts[] = { + { + .id = 0x449, + .revs = stm32_449_revs, + .num_revs = ARRAY_SIZE(stm32_449_revs), + .device_str = "STM32F7xx 1M", + .page_size = 256, /* 256 KB */ + .pages_per_sector = 256, + .max_flash_size_kb = 1024, + .flash_base = 0x40023C00, + .fsize_base = 0x1FF0F442, + }, +}; + +static int stm32x_unlock_reg(struct flash_bank *bank); +static int stm32x_probe(struct flash_bank *bank); + +/* flash bank stm32x <base> <size> 0 0 <target#> */ + +FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command) +{ + struct stm32f7x_flash_bank *stm32x_info; + + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + stm32x_info = malloc(sizeof(struct stm32f7x_flash_bank)); + bank->driver_priv = stm32x_info; + + stm32x_info->probed = 0; + stm32x_info->user_bank_size = bank->size; + stm32x_info->flash_base = FLASH_BASE; + + return ERROR_OK; +} + +static inline int stm32x_get_flash_status(struct flash_bank *bank, uint32_t *status) +{ + struct target *target = bank->target; + struct stm32f7x_flash_bank *stm32x_info = bank->driver_priv; + + return target_read_u32(target, stm32x_info->flash_base + FLASH_SR, status); +} + +static int stm32x_wait_status_busy(struct flash_bank *bank, int timeout) +{ + struct target *target = bank->target; + struct stm32f7x_part_info *stm32x_info = bank->driver_priv; + uint32_t status; + int retval = ERROR_OK; + + /* wait for busy to clear */ + for (;;) { + retval = stm32x_get_flash_status(bank, &status); + if (retval != ERROR_OK) { + LOG_INFO("wait_status_busy, target_read_u32 : error : remote address 0x%x", stm32x_info->flash_base); + return retval; + } + + if ((status & FLASH_BSY) == 0) + break; + + if (timeout-- <= 0) { + LOG_INFO("wait_status_busy, time out expired"); + return ERROR_FAIL; + } + alive_sleep(1); + } + + if (status & FLASH_WRPERR) { + LOG_INFO("wait_status_busy, WRPERR : error : remote address 0x%x", stm32x_info->flash_base); + retval = ERROR_FAIL; + } + + /* Clear but report errors */ + if (status & FLASH_ERROR) { + /* If this operation fails, we ignore it and report the original + * retval + */ + target_write_u32(target, stm32x_info->flash_base + FLASH_SR, + status & FLASH_ERROR); + } + return retval; +} + +static int stm32x_unlock_reg(struct flash_bank *bank) +{ + uint32_t ctrl; + struct stm32f7x_flash_bank *stm32x_info = bank->driver_priv; + struct target *target = bank->target; + + /* first check if not already unlocked + * otherwise writing on STM32_FLASH_KEYR will fail + */ + int retval = target_read_u32(target, stm32x_info->flash_base + FLASH_CR, &ctrl); + if (retval != ERROR_OK) + return retval; + + if ((ctrl & FLASH_LOCK) == 0) + return ERROR_OK; + + /* unlock flash registers */ + retval = target_write_u32(target, stm32x_info->flash_base + FLASH_KEYR, KEY1); + if (retval != ERROR_OK) + return retval; + + retval = target_write_u32(target, stm32x_info->flash_base + FLASH_KEYR, KEY2); + if (retval != ERROR_OK) + return retval; + + retval = target_read_u32(target, stm32x_info->flash_base + FLASH_CR, &ctrl); + if (retval != ERROR_OK) + return retval; + + if (ctrl & FLASH_LOCK) { + LOG_ERROR("flash not unlocked STM32_FLASH_CR: %" PRIx32, ctrl); + return ERROR_TARGET_FAILURE; + } + + return ERROR_OK; +} + +static int stm32x_unlock_option_reg(struct flash_bank *bank) +{ + uint32_t ctrl; + struct stm32f7x_flash_bank *stm32x_info = bank->driver_priv; + struct target *target = bank->target; + + int retval = target_read_u32(target, stm32x_info->flash_base + FLASH_OPTR, &ctrl); + if (retval != ERROR_OK) + return retval; + + if ((ctrl & OPT_LOCK) == 0) + return ERROR_OK; + + /* unlock option registers */ + retval = target_write_u32(target, stm32x_info->flash_base + FLASH_OPTKEYR, OPTKEY1); + if (retval != ERROR_OK) + return retval; + + retval = target_write_u32(target, stm32x_info->flash_base + FLASH_OPTKEYR, OPTKEY2); + if (retval != ERROR_OK) + return retval; + + retval = target_read_u32(target, stm32x_info->flash_base + FLASH_OPTR, &ctrl); + if (retval != ERROR_OK) + return retval; + + if (ctrl & OPT_LOCK) { + LOG_ERROR("options not unlocked STM32_FLASH_OPTCR: %" PRIx32, ctrl); + return ERROR_TARGET_FAILURE; + } + + return ERROR_OK; +} + +static int stm32x_read_options(struct flash_bank *bank) +{ + uint32_t optiondata; + struct stm32f7x_flash_bank *stm32x_info = NULL; + struct target *target = bank->target; + + stm32x_info = bank->driver_priv; + + /* read current option bytes */ + int retval = target_read_u32(target, stm32x_info->flash_base + FLASH_OPTR, &optiondata); + if (retval != ERROR_OK) + return retval; + + stm32x_info->option_bytes.user_options = optiondata & 0xec; + stm32x_info->option_bytes.RDP = (optiondata >> 8) & 0xff; + stm32x_info->option_bytes.protection = (optiondata >> 16) & 0xfff; + + if (stm32x_info->option_bytes.RDP != 0xAA) + LOG_INFO("Device Security Bit Set"); + + return ERROR_OK; +} + +static int stm32x_write_options(struct flash_bank *bank) +{ + struct stm32f7x_flash_bank *stm32x_info = NULL; + struct target *target = bank->target; + uint32_t optiondata; + + stm32x_info = bank->driver_priv; + + int retval = stm32x_unlock_option_reg(bank); + if (retval != ERROR_OK) + return retval; + + /* rebuild option data */ + optiondata = stm32x_info->option_bytes.user_options; + buf_set_u32((uint8_t *)&optiondata, 8, 8, stm32x_info->option_bytes.RDP); + buf_set_u32((uint8_t *)&optiondata, 16, 12, stm32x_info->option_bytes.protection); + + /* program options */ + retval = target_write_u32(target, stm32x_info->flash_base + FLASH_OPTR, optiondata); + if (retval != ERROR_OK) + return retval; + + /* start programming cycle */ + retval = target_write_u32(target, stm32x_info->flash_base + FLASH_OPTR, optiondata | OPT_START); + if (retval != ERROR_OK) + return retval; + + /* wait for completion */ + retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + /* relock registers */ + retval = target_write_u32(target, stm32x_info->flash_base + FLASH_OPTR, optiondata | OPT_LOCK); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int stm32x_protect_check(struct flash_bank *bank) +{ + struct stm32f7x_flash_bank *stm32x_info = bank->driver_priv; + + /* read write protection settings */ + int retval = stm32x_read_options(bank); + if (retval != ERROR_OK) { + LOG_DEBUG("unable to read option bytes"); + return retval; + } + + for (int i = 0; i < bank->num_sectors; i++) { + if (stm32x_info->option_bytes.protection & (1 << i)) + bank->sectors[i].is_protected = 0; + else + bank->sectors[i].is_protected = 1; + } + + return ERROR_OK; +} + +static int stm32x_erase(struct flash_bank *bank, int first, int last) +{ + struct target *target = bank->target; + struct stm32f7x_flash_bank *stm32x_info = bank->driver_priv; + int i; + int retval; + + assert(first < bank->num_sectors); + assert(last < bank->num_sectors); + + if (bank->target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + retval = stm32x_unlock_reg(bank); + if (retval != ERROR_OK) + return retval; + + /* + Sector Erase + To erase a sector, follow the procedure below: + 1. Check that no Flash memory operation is ongoing by checking the BSY bit in the + FLASH_SR register + 2. Set the SER bit and select the sector + you wish to erase (SNB) in the FLASH_CR register + 3. Set the STRT bit in the FLASH_CR register + 4. Wait for the BSY bit to be cleared + */ + + for (i = first; i <= last; i++) { + LOG_DEBUG("erase sector %d", i); + retval = target_write_u32(target, + stm32x_info->flash_base + FLASH_CR, FLASH_SER | FLASH_SNB(i) | FLASH_START); + if (retval != ERROR_OK) { + LOG_ERROR("erase sector error %d", i); + return retval; + } + + retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); + if (retval != ERROR_OK) { + LOG_ERROR("erase time-out error sector %d", i); + return retval; + } + bank->sectors[i].is_erased = 1; + } + + retval = target_write_u32(target, stm32x_info->flash_base + FLASH_CR, FLASH_LOCK); + if (retval != ERROR_OK) { + LOG_ERROR("error during the lock of flash"); + return retval; + } + + return ERROR_OK; +} + +static int stm32x_protect(struct flash_bank *bank, int set, int first, int last) +{ + struct target *target = bank->target; + struct stm32f7x_flash_bank *stm32x_info = bank->driver_priv; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + LOG_INFO("stm32x_protect, target_*_u32 : error : remote address 0x%x", stm32x_info->flash_base); + + /* read protection settings */ + int retval = stm32x_read_options(bank); + if (retval != ERROR_OK) { + LOG_DEBUG("unable to read option bytes"); + return retval; + } + + for (int i = first; i <= last; i++) { + + if (set) + stm32x_info->option_bytes.protection &= ~(1 << i); + else + stm32x_info->option_bytes.protection |= (1 << i); + } + + retval = stm32x_write_options(bank); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + uint32_t buffer_size = 16384; + struct working_area *write_algorithm; + struct working_area *source; + uint32_t address = bank->base + offset; + struct reg_param reg_params[4]; + struct armv7m_algorithm armv7m_info; + struct stm32f7x_flash_bank *stm32x_info = bank->driver_priv; + + int retval = ERROR_OK; + + /* see flash/smt32_flash_32.c for src */ + static const uint8_t stm32x_flash_write_code[] = { + 0x43, 0xF6, 0x10, 0x44, /* movw r4, #15376 */ + 0xC4, 0xF2, 0x02, 0x04, /* movt r4, 16386 */ + 0x25, 0x68, /* ldr r5, [r4, #0] */ + 0x45, 0xF4, 0x00, 0x76, /* orr r6, r5, #512 */ + 0x46, 0xF0, 0x01, 0x05, /* orr r5, r6, #1 */ + 0x25, 0x60, /* str r5, [r4, #0] */ + 0x4F, 0xF0, 0x00, 0x07, /* mov r7, #0 */ + 0xA1, 0xF1, 0x04, 0x01, /* sub r1, r1, #4 */ + 0x00, 0xF1, 0x08, 0x0C, /* add ip, r0, #8 */ + 0x43, 0xF6, 0x0C, 0x46, /* movw r6, #15372 */ + 0xC4, 0xF2, 0x02, 0x06, /* movt r6, 16386 */ + 0x25, 0xE0, /* b .L2 */ + /* .L5: */ + 0x04, 0x68, /* ldr r4, [r0, #0] */ + 0x1C, 0xB9, /* cbnz r4, .L10 */ + 0x4F, 0xF0, 0x00, 0x03, /* mov r3, #0 */ + 0x43, 0x60, /* str r3, [r0, #4] */ + 0x21, 0xE0, /* b .L4 */ + /* .L10: */ + 0x45, 0x68, /* ldr r5, [r0, #4] */ + 0x04, 0x68, /* ldr r4, [r0, #0] */ + 0xA5, 0x42, /* cmp r5, r4 */ + 0xF5, 0xD0, /* beq .L5 */ + 0x44, 0x68, /* ldr r4, [r0, #4] */ + 0x24, 0x68, /* ldr r4, [r4, #0] */ + 0x42, 0xF8, 0x04, 0x4B, /* str r4, [r2], #4 */ + 0x44, 0x68, /* ldr r4, [r0, #4] */ + 0x8C, 0x42, /* cmp r4, r1 */ + 0x2F, 0xBF, /* iteee cs */ + 0xC0, 0xF8, 0x04, 0xC0, /* strcs ip, [r0, #4] */ + 0x44, 0x68, /* ldrcc r4, [r0, #4] */ + 0x04, 0x34, /* addcc r4, r4, #4 */ + 0x44, 0x60, /* strcc r4, [r0, #4] */ + 0xBF, 0xF3, 0x4F, 0x8F, /* dsb */ + /* .L8: */ + 0x34, 0x68, /* ldr r4, [r6, #0] */ + 0x14, 0xF4, 0x80, 0x3F, /* tst r4, #65536 */ + 0xFB, 0xD1, /* bne .L8 */ + 0x34, 0x68, /* ldr r4, [r6, #0] */ + 0x14, 0xF0, 0x01, 0x0F, /* tst r4, #1 */ + 0x03, 0xD0, /* beq .L9 */ + 0x34, 0x68, /* ldr r4, [r6, #0] */ + 0x44, 0xF0, 0x01, 0x04, /* orr r4, r4, #1 */ + 0x34, 0x60, /* str r4, [r6, #0] */ + /* .L9: */ + 0x07, 0xF1, 0x01, 0x07, /* add r7, r7, #1 */ + /* .L2: */ + 0x9F, 0x42, /* cmp r7, r3 */ + 0xDD, 0xD1, /* bne .L10 */ + /* .L4: */ + 0x43, 0xF6, 0x0C, 0x40, /* movw r0, #15372 */ + 0xC4, 0xF2, 0x02, 0x00, /* movt r0, 16386 */ + 0x00, 0x68, /* ldr r0, [r0, #0] */ + 0x00, 0xBE, /* bkpt #0x00 */ + }; + + + if (target_alloc_working_area(target, sizeof(stm32x_flash_write_code), + &write_algorithm) != ERROR_OK) { + LOG_WARNING("no working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + retval = target_write_buffer(target, write_algorithm->address, + sizeof(stm32x_flash_write_code), + stm32x_flash_write_code); + if (retval != ERROR_OK) + return retval; + + /* memory buffer */ + while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) { + buffer_size /= 2; + if (buffer_size <= 256) { + /* 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; + } + } + + LOG_DEBUG("target_alloc_working_area_try : buffer_size -> 0x%x", buffer_size); + + armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_info.core_mode = ARM_MODE_THREAD; + + init_reg_param(®_params[0], "r0", 32, PARAM_IN_OUT); /* buffer start, status (out) */ + init_reg_param(®_params[1], "r1", 32, PARAM_OUT); /* buffer end */ + init_reg_param(®_params[2], "r2", 32, PARAM_OUT); /* target address */ + init_reg_param(®_params[3], "r3", 32, PARAM_OUT); /* count (word-32bit) */ + + buf_set_u32(reg_params[0].value, 0, 32, source->address); + buf_set_u32(reg_params[1].value, 0, 32, source->address + source->size); + buf_set_u32(reg_params[2].value, 0, 32, address); + buf_set_u32(reg_params[3].value, 0, 32, count); + + retval = target_run_flash_async_algorithm(target, + buffer, + count, + 4, + 0, NULL, + 4, reg_params, + source->address, source->size, + write_algorithm->address, 0, + &armv7m_info); + + if (retval == ERROR_FLASH_OPERATION_FAILED) { + LOG_INFO("error executing stm32f7x flash write algorithm"); + + uint32_t error = buf_get_u32(reg_params[0].value, 0, 32) & FLASH_ERROR; + + if (error & FLASH_WRPERR) + LOG_ERROR("flash memory write protected"); + + if (error != 0) { + LOG_ERROR("flash write failed = %08" PRIx32, error); + /* Clear but report errors */ + target_write_u32(target, stm32x_info->flash_base + FLASH_SR, error); + retval = ERROR_FAIL; + } + } + + target_free_working_area(target, source); + target_free_working_area(target, write_algorithm); + + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + destroy_reg_param(®_params[3]); + return retval; +} + +static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct stm32f7x_flash_bank *stm32x_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t address = bank->base + offset; + uint32_t bytes_written = 0; + uint32_t count_written = count; + int retval; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + retval = stm32x_unlock_reg(bank); + if (retval != ERROR_OK) + return retval; + + if (address & 0x3) { + retval = target_write_u32(target, stm32x_info->flash_base + FLASH_CR, FLASH_PG | FLASH_PSIZE_8); + if (retval != ERROR_OK) + return retval; + + for (unsigned int i = 0; i < (4 - (address & 0x3)); i++) { + retval = target_write_u8(target, address + i, buffer[i]); + + if (retval != ERROR_OK) + return retval; + + offset++; + count_written--; + bytes_written++; + } + } + + uint32_t words_remaining = (count_written / 4); + uint32_t bytes_remaining = (count_written & 0x00000003); + + /* multiple half words (4-byte) to be programmed? */ + if (words_remaining > 0) { + /* try using a block write */ + retval = stm32x_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"); + } + } else { + buffer += words_remaining * 4; + address += words_remaining * 4 + bytes_written; + words_remaining = 0; + } + } + + if ((retval != ERROR_OK) && (retval != ERROR_TARGET_RESOURCE_NOT_AVAILABLE)) + return retval; + + /* + Standard programming + The Flash memory programming sequence is as follows: + 1. Check that no main Flash memory operation is ongoing by checking the BSY bit in the + FLASH_SR register. + 2. Set the PG bit in the FLASH_CR register + 3. Perform the data write operation(s) to the desired memory address (inside main + memory block or OTP area): + – – Half-word access in case of x16 parallelism + – Word access in case of x32 parallelism + – + 4. + Byte access in case of x8 parallelism + Double word access in case of x64 parallelism + Wait for the BSY bit to be cleared + */ + if (bytes_remaining) { + retval = target_write_u32(target, stm32x_info->flash_base + FLASH_CR, FLASH_PG | FLASH_PSIZE_8); + if (retval != ERROR_OK) + return retval; + + LOG_DEBUG("bytes_remaining %d", bytes_remaining); + for (unsigned int i = 0; i < bytes_remaining; i++) { + retval = target_write_u8(target, address+i, buffer[bytes_written+i-2]); + if (retval != ERROR_OK) + return retval; + } + + retval = stm32x_wait_status_busy(bank, FLASH_WRITE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + } + + return target_write_u32(target, stm32x_info->flash_base + FLASH_CR, FLASH_LOCK); +} + +static void setup_sector(struct flash_bank *bank, int start, int num, int size) +{ + for (int i = start; i < (start + num) ; i++) { + assert(i < bank->num_sectors); + bank->sectors[i].offset = bank->size; + bank->sectors[i].size = size; + bank->size += bank->sectors[i].size; + } +} + +static int stm32x_read_id_code(struct flash_bank *bank, uint32_t *id) +{ + /* read stm32 device id register */ + int retval = target_read_u32(bank->target, DBGMCU_IDCODE_REGISTER, id); + if (retval != ERROR_OK) + return retval; + return ERROR_OK; +} + +static int stm32x_probe(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct stm32f7x_flash_bank *stm32x_info = bank->driver_priv; + int i; + uint16_t flash_size_in_kb; + uint32_t device_id; + uint32_t base_address = FLASH_BANK0_ADDRESS; + + stm32x_info->probed = 0; + + int retval = stm32x_read_id_code(bank, &device_id); + if (retval != ERROR_OK) + return retval; + + stm32x_info->idcode = device_id; + + LOG_DEBUG("device id = 0x%08" PRIx32 "", device_id); + + for (unsigned int n = 0; n < ARRAY_SIZE(stm32f7x_parts); n++) { + if ((device_id & 0xfff) == stm32f7x_parts[n].id) + stm32x_info->part_info = &stm32f7x_parts[n]; + } + if (!stm32x_info->part_info) { + LOG_WARNING("Cannot identify target as a STM32F7xx family."); + return ERROR_FAIL; + } + + /* get flash size from target. */ + retval = target_read_u16(target, stm32x_info->part_info->fsize_base, &flash_size_in_kb); + LOG_INFO("flash size probed value %d", flash_size_in_kb); + + /* if the user sets the size manually then ignore the probed value + * this allows us to work around devices that have a invalid flash size register value */ + if (stm32x_info->user_bank_size) { + LOG_INFO("ignoring flash probed value, using configured bank size"); + flash_size_in_kb = stm32x_info->user_bank_size / 1024; + } else if (flash_size_in_kb == 0xffff) { + /* die flash size */ + flash_size_in_kb = stm32x_info->part_info->max_flash_size_kb; + } + + /* did we assign flash size? */ + assert(flash_size_in_kb != 0xffff); + + /* calculate numbers of pages */ + int num_pages = (flash_size_in_kb / 256) + 4; + + /* check that calculation result makes sense */ + assert(num_pages > 0); + + if (bank->sectors) { + free(bank->sectors); + bank->sectors = NULL; + } + + bank->base = base_address; + bank->num_sectors = num_pages; + bank->sectors = malloc(sizeof(struct flash_sector) * num_pages); + bank->size = 0; + + /* fixed memory */ + setup_sector(bank, 0, 4, 32 * 1024); + setup_sector(bank, 4, 1, 128 * 1024); + + /* dynamic memory */ + setup_sector(bank, 4 + 1, MIN(8, num_pages) - 5, 256 * 1024); + + for (i = 0; i < num_pages; i++) { + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 0; + } + + stm32x_info->probed = 1; + return ERROR_OK; +} + +static int stm32x_auto_probe(struct flash_bank *bank) +{ + struct stm32f7x_flash_bank *stm32x_info = bank->driver_priv; + + if (stm32x_info->probed) + return ERROR_OK; + return stm32x_probe(bank); +} + +/* This method must return a string displaying information about the bank */ +static int stm32x_get_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct stm32f7x_flash_bank *stm32x_info = bank->driver_priv; + const struct stm32f7x_part_info *info = stm32x_info->part_info; + + if (!stm32x_info->probed) { + int retval = stm32x_probe(bank); + if (retval != ERROR_OK) { + snprintf(buf, buf_size, "Unable to find bank information."); + return retval; + } + } + + if (info) { + const char *rev_str = NULL; + uint16_t rev_id = stm32x_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", + stm32x_info->part_info->device_str, rev_str); + } else { + snprintf(buf, buf_size, + "%s - Rev: unknown (0x%04x)", + stm32x_info->part_info->device_str, rev_id); + } + + return ERROR_OK; + } else { + snprintf(buf, buf_size, "Cannot identify target as a STM32F7x"); + return ERROR_FAIL; + } + return ERROR_OK; +} + +COMMAND_HANDLER(stm32x_handle_lock_command) +{ + struct target *target = NULL; + struct stm32f7x_flash_bank *stm32f7x_info = NULL; + + 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; + + stm32f7x_info = bank->driver_priv; + target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (stm32x_read_options(bank) != ERROR_OK) { + command_print(CMD_CTX, "%s failed to read options", + bank->driver->name); + return ERROR_OK; + } + + /* set readout protection */ + stm32f7x_info->option_bytes.RDP = 0; + + if (stm32x_write_options(bank) != ERROR_OK) { + command_print(CMD_CTX, "%s failed to lock device", + bank->driver->name); + return ERROR_OK; + } + + command_print(CMD_CTX, "%s locked", bank->driver->name); + + return ERROR_OK; +} + +COMMAND_HANDLER(stm32x_handle_unlock_command) +{ + struct target *target = NULL; + struct stm32f7x_flash_bank *stm32x_info = NULL; + + 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; + + stm32x_info = bank->driver_priv; + target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (stm32x_read_options(bank) != ERROR_OK) { + command_print(CMD_CTX, "%s failed to read options", bank->driver->name); + return ERROR_OK; + } + + /* clear readout protection and complementary option bytes + * this will also force a device unlock if set */ + stm32x_info->option_bytes.RDP = 0xAA; + + if (stm32x_write_options(bank) != ERROR_OK) { + command_print(CMD_CTX, "%s failed to unlock device", bank->driver->name); + return ERROR_OK; + } + + command_print(CMD_CTX, "%s unlocked.\n" + "INFO: a reset or power cycle is required " + "for the new settings to take effect.", bank->driver->name); + + return ERROR_OK; +} + +static int stm32x_mass_erase(struct flash_bank *bank) +{ + int retval; + struct target *target = bank->target; + struct stm32f7x_flash_bank *stm32x_info = NULL; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + stm32x_info = bank->driver_priv; + + retval = stm32x_unlock_reg(bank); + if (retval != ERROR_OK) + return retval; + + /* mass erase flash memory */ + retval = target_write_u32(target, stm32x_info->flash_base + FLASH_CR, FLASH_MER); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, stm32x_info->flash_base + FLASH_CR, + FLASH_MER | FLASH_START); + if (retval != ERROR_OK) + return retval; + + retval = stm32x_wait_status_busy(bank, 30000); + if (retval != ERROR_OK) + return retval; + + retval = target_write_u32(target, stm32x_info->flash_base + FLASH_CR, FLASH_LOCK); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +COMMAND_HANDLER(stm32x_handle_mass_erase_command) +{ + int i; + + if (CMD_ARGC < 1) { + command_print(CMD_CTX, "stm32f7x mass_erase <bank>"); + 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 = stm32x_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, "stm32f7x mass erase complete"); + } else { + command_print(CMD_CTX, "stm32f7x mass erase failed"); + } + + return retval; +} + +static const struct command_registration stm32x_exec_command_handlers[] = { + { + .name = "lock", + .handler = stm32x_handle_lock_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Lock entire flash device.", + }, + { + .name = "unlock", + .handler = stm32x_handle_unlock_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Unlock entire protected flash device.", + }, + { + .name = "mass_erase", + .handler = stm32x_handle_mass_erase_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Erase entire flash device.", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration stm32x_command_handlers[] = { + { + .name = "stm32f7x", + .mode = COMMAND_ANY, + .help = "stm32f7x flash command group", + .usage = "", + .chain = stm32x_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver stm32f7x_flash = { + .name = "stm32f7x", + .commands = stm32x_command_handlers, + .flash_bank_command = stm32x_flash_bank_command, + .erase = stm32x_erase, + .protect = stm32x_protect, + .write = stm32x_write, + .read = default_flash_read, + .probe = stm32x_probe, + .auto_probe = stm32x_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = stm32x_protect_check, + .info = stm32x_get_info, +}; -- ------------------------------------------------------------------------------ One dashboard for servers and applications across Physical-Virtual-Cloud Widest out-of-the-box monitoring support with 50+ applications Performance metrics, stats and reports that give you Actionable Insights Deep dive visibility with transaction tracing using APM Insight. http://ad.doubleclick.net/ddm/clk/290420510;117567292;y _______________________________________________ OpenOCD-devel mailing list OpenOCD-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openocd-devel