diff options
author | Øyvind Harboe <oyvind.harboe@zylin.com> | 2011-01-31 12:32:07 +0100 |
---|---|---|
committer | Øyvind Harboe <oyvind.harboe@zylin.com> | 2011-02-04 10:55:43 +0100 |
commit | 5ca7cbe2d232fcaff32598af1ef5e962e704c004 (patch) | |
tree | ec445005777a989b8b0ab61fcaa9f9d30db92a66 /src/flash/nor/stm32f2xxx.c | |
parent | 75cdbff5aa93d93e414cb22d413f41fb38a076bb (diff) | |
download | openocd+libswd-5ca7cbe2d232fcaff32598af1ef5e962e704c004.tar.gz openocd+libswd-5ca7cbe2d232fcaff32598af1ef5e962e704c004.tar.bz2 openocd+libswd-5ca7cbe2d232fcaff32598af1ef5e962e704c004.tar.xz openocd+libswd-5ca7cbe2d232fcaff32598af1ef5e962e704c004.zip |
stm32x: add support for STM32F20x
ready for wider testing and comments on basic erase + programming.
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
Diffstat (limited to 'src/flash/nor/stm32f2xxx.c')
-rw-r--r-- | src/flash/nor/stm32f2xxx.c | 712 |
1 files changed, 712 insertions, 0 deletions
diff --git a/src/flash/nor/stm32f2xxx.c b/src/flash/nor/stm32f2xxx.c new file mode 100644 index 00000000..2ccf5317 --- /dev/null +++ b/src/flash/nor/stm32f2xxx.c @@ -0,0 +1,712 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * Copyright (C) 2008 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * Copyright (C) 2011 Øyvind Harboe * + * oyvind.harboe@zylin.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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include <helper/binarybuffer.h> +#include <target/algorithm.h> +#include <target/armv7m.h> + +/* Regarding performance: + * + * Short story - it might be best to leave the performance at + * current levels. + * + * You may see a jump in speed if you change to using + * 32bit words for the block programming. + * + * Its a shame you cannot use the double word as its + * even faster - but you require external VPP for that mode. + * + * Having said all that 16bit writes give us the widest vdd + * operating range, so may be worth adding a note to that effect. + * + */ + + +/* Danger!!!! The STM32F1xxxx and STM32F2xxxx series actually have + * quite different flash controllers. + * + * What's more scary is that the names of the registers and their + * addresses are the same, but the actual bits and what they do are + * can be very different. + * + * To reduce testing complexity and dangers of regressions, + * a seperate file is used for stm32fx2222. + * + * 1mByte part with 4 x 16, 1 x 64, 7 x 128kBytes sectors + * + * What's the protection page size??? + * + * Tested with STM3220F-EVAL board. + * + * STM32F21xx series for reference. + * + * RM0033 + * http://www.st.com/internet/mcu/product/250192.jsp + * + * PM0059 + * www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/PROGRAMMING_MANUAL/CD00233952.pdf + * + * STM32F1xxx series - notice that this code was copy, pasted and knocked + * into a stm32f2xxx driver, so in case something has been converted or + * bugs haven't been fixed, here are the original manuals: + * + * RM0008 - Reference manual + * + * RM0042, the Flash programming manual for low-, medium- high-density and + * connectivity line STM32F10xxx devices + * + * PM0068, the Flash programming manual for XL-density STM32F10xxx devices. + * + */ + + // Erase time can be as high as 1000ms, 10x this and it's toast... +#define FLASH_ERASE_TIMEOUT 10000 +#define FLASH_WRITE_TIMEOUT 5 + + +#define STM32_FLASH_BASE 0x40023c00 +#define STM32_FLASH_ACR 0x40023c00 +#define STM32_FLASH_KEYR 0x40023c04 +#define STM32_FLASH_OPTKEYR 0x40023c08 +#define STM32_FLASH_SR 0x40023c0C +#define STM32_FLASH_CR 0x40023c10 +#define STM32_FLASH_OPTCR 0x40023c14 +#define STM32_FLASH_OBR 0x40023c1C + + + +/* option byte location */ + +#define STM32_OB_RDP 0x1FFFF800 +#define STM32_OB_USER 0x1FFFF802 +#define STM32_OB_DATA0 0x1FFFF804 +#define STM32_OB_DATA1 0x1FFFF806 +#define STM32_OB_WRP0 0x1FFFF808 +#define STM32_OB_WRP1 0x1FFFF80A +#define STM32_OB_WRP2 0x1FFFF80C +#define STM32_OB_WRP3 0x1FFFF80E + +/* FLASH_CR register bits */ + +#define FLASH_PG (1 << 0) +#define FLASH_SER (1 << 1) +#define FLASH_MER (1 << 2) +#define FLASH_STRT (1 << 16) +#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) << 3) +#define FLASH_LOCK (1 << 31) + +/* FLASH_SR register bits */ + +#define FLASH_BSY (1 << 16) +#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_OPERR (1 << 1) // Operation error + +#define FLASH_ERROR (FLASH_PGSERR | FLASH_PGPERR | FLASH_PGAERR| FLASH_WRPERR| FLASH_OPERR) + +/* 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 +#define OPT_BFB2 5 /* dual flash bank only */ + +/* register unlock keys */ + +#define KEY1 0x45670123 +#define KEY2 0xCDEF89AB + +struct stm32x_flash_bank +{ + struct working_area *write_algorithm; + int probed; +}; + + +/* flash bank stm32x <base> <size> 0 0 <target#> + */ +FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command) +{ + struct stm32x_flash_bank *stm32x_info; + + if (CMD_ARGC < 6) + { + LOG_WARNING("incomplete flash_bank stm32x configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + stm32x_info = malloc(sizeof(struct stm32x_flash_bank)); + bank->driver_priv = stm32x_info; + + stm32x_info->write_algorithm = NULL; + stm32x_info->probed = 0; + + return ERROR_OK; +} + +static inline int stm32x_get_flash_reg(struct flash_bank *bank, uint32_t reg) +{ + return reg; +} + +static inline int stm32x_get_flash_status(struct flash_bank *bank, uint32_t *status) +{ + struct target *target = bank->target; + return target_read_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_SR), status); +} + +static int stm32x_wait_status_busy(struct flash_bank *bank, int timeout) +{ + struct target *target = bank->target; + uint32_t status; + int retval = ERROR_OK; + + /* wait for busy to clear */ + for (;;) + { + retval = stm32x_get_flash_status(bank, &status); + if (retval != ERROR_OK) + return retval; + LOG_DEBUG("status: 0x%" PRIx32 "", status); + if ((status & FLASH_BSY) == 0) + break; + if (timeout-- <= 0) + { + LOG_ERROR("timed out waiting for flash"); + return ERROR_FAIL; + } + alive_sleep(1); + } + + + if (status & FLASH_WRPERR) + { + LOG_ERROR("stm32x device protected"); + 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_get_flash_reg(bank, STM32_FLASH_SR), + status & FLASH_ERROR); + } + return retval; +} + +static int stm32x_unlock_reg(struct target *target) +{ + /* unlock flash registers */ + int retval = target_write_u32(target, STM32_FLASH_KEYR, KEY1); + if (retval != ERROR_OK) + return retval; + + retval = target_write_u32(target, STM32_FLASH_KEYR, KEY2); + if (retval != ERROR_OK) + return retval; + return ERROR_OK; +} + +static int stm32x_protect_check(struct flash_bank *bank) +{ + return ERROR_OK; +} + +static int stm32x_erase(struct flash_bank *bank, int first, int last) +{ + struct target *target = bank->target; + int i; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + int retval; + retval = stm32x_unlock_reg(target); + 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 (out of the 12 sectors in the main memory block) + 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++) + { + retval = target_write_u32(target, + stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_SER | FLASH_SNB(i) | FLASH_STRT); + if (retval != ERROR_OK) + return retval; + + retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + bank->sectors[i].is_erased = 1; + } + + retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int stm32x_protect(struct flash_bank *bank, int set, int first, int last) +{ + return ERROR_OK; +} + +static int stm32x_write_block(struct flash_bank *bank, uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct stm32x_flash_bank *stm32x_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t buffer_size = 16384; + struct working_area *source; + uint32_t address = bank->base + offset; + struct reg_param reg_params[5]; + struct armv7m_algorithm armv7m_info; + int retval = ERROR_OK; + + /* see contib/loaders/flash/stm32x.s for src */ + + static const uint16_t stm32x_flash_write_code_16[] = { +// 00000000 <write>: + 0x4b07, // ldr r3, [pc, #28] (20 <STM32_PROG16>) + 0x6123, // str r3, [r4, #16] + 0xf830, 0x3b02, //ldrh.w r3, [r0], #2 + 0xf821, 0x3b02, //strh.w r3, [r1], #2 + + //0000000c <busy>: + 0x68e3, //ldr r3, [r4, #12] +0xf413, 0x3f80, // tst.w r3, #65536 ; 0x10000 +0xd0fb, //beq.n c <busy> +0xf013, 0x0ff0, //tst.w r3, #240 ; 0xf0 +0xd101, //bne.n 1e <exit> +0x3a01, //subs r2, #1 +0xd1f0, //bne.n 0 <write> + //0000001e <exit>: + 0xbe00, // bkpt 0x0000 + + //00000020 <STM32_PROG16>: + 0x0101, 0x0000, // .word 0x00000101 + + }; + + // Flip endian + uint8_t stm32x_flash_write_code[sizeof(stm32x_flash_write_code_16)*2]; + for (unsigned i = 0; i < sizeof(stm32x_flash_write_code_16) / 2; i++) + { + stm32x_flash_write_code[i*2 + 0] = stm32x_flash_write_code_16[i] & 0xff; + stm32x_flash_write_code[i*2 + 1] = (stm32x_flash_write_code_16[i] >> 8) & 0xff; + } + + if (target_alloc_working_area(target, sizeof(stm32x_flash_write_code), + &stm32x_info->write_algorithm) != ERROR_OK) + { + LOG_WARNING("no working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + }; + + if ((retval = target_write_buffer(target, stm32x_info->write_algorithm->address, + sizeof(stm32x_flash_write_code), + (uint8_t*)stm32x_flash_write_code)) != 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) + { + /* if we already allocated the writing code, but failed to get a + * buffer, free the algorithm */ + if (stm32x_info->write_algorithm) + target_free_working_area(target, stm32x_info->write_algorithm); + + LOG_WARNING("no large enough working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + }; + + armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_info.core_mode = ARMV7M_MODE_ANY; + + init_reg_param(®_params[0], "r0", 32, PARAM_OUT); + init_reg_param(®_params[1], "r1", 32, PARAM_OUT); + init_reg_param(®_params[2], "r2", 32, PARAM_OUT); + init_reg_param(®_params[3], "r3", 32, PARAM_IN_OUT); + init_reg_param(®_params[4], "r4", 32, PARAM_OUT); + + while (count > 0) + { + uint32_t thisrun_count = (count > (buffer_size / 2)) ? + (buffer_size / 2) : count; + + if ((retval = target_write_buffer(target, source->address, + thisrun_count * 2, buffer)) != ERROR_OK) + break; + + 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, thisrun_count); + // R3 is a return value only + buf_set_u32(reg_params[4].value, 0, 32, STM32_FLASH_BASE); + + if ((retval = target_run_algorithm(target, 0, NULL, + sizeof(reg_params) / sizeof(*reg_params), + reg_params, + stm32x_info->write_algorithm->address, + 0, + 10000, &armv7m_info)) != ERROR_OK) + { + LOG_ERROR("error executing stm32x flash write algorithm"); + break; + } + + uint32_t error = buf_get_u32(reg_params[3].value, 0, 32) & FLASH_ERROR; + + if (error & FLASH_WRPERR) + { + LOG_ERROR("flash memory write protected"); + } + + if (error != 0) + { + LOG_ERROR("flash write failed = %08x", error); + /* Clear but report errors */ + target_write_u32(target, STM32_FLASH_SR, error); + retval = ERROR_FAIL; + break; + } + + buffer += thisrun_count * 2; + address += thisrun_count * 2; + count -= thisrun_count; + } + + target_free_working_area(target, source); + target_free_working_area(target, stm32x_info->write_algorithm); + + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + destroy_reg_param(®_params[3]); + destroy_reg_param(®_params[4]); + + return retval; +} + +static int stm32x_write(struct flash_bank *bank, uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + uint32_t words_remaining = (count / 2); + uint32_t bytes_remaining = (count & 0x00000001); + uint32_t address = bank->base + offset; + uint32_t bytes_written = 0; + int retval; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (offset & 0x1) + { + LOG_WARNING("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + retval = stm32x_unlock_reg(target); + if (retval != ERROR_OK) + return retval; + + /* multiple half words (2-byte) to be programmed? */ + if (words_remaining > 0) + { + /* try using a block write */ + if ((retval = stm32x_write_block(bank, buffer, offset, words_remaining)) != 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 * 2; + address += words_remaining * 2; + 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 + */ + while (words_remaining > 0) + { + uint16_t value; + memcpy(&value, buffer + bytes_written, sizeof(uint16_t)); + + retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), + FLASH_PG | FLASH_PSIZE_16); + if (retval != ERROR_OK) + return retval; + + retval = target_write_u16(target, address, value); + if (retval != ERROR_OK) + return retval; + + retval = stm32x_wait_status_busy(bank, FLASH_WRITE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + bytes_written += 2; + words_remaining--; + address += 2; + } + + if (bytes_remaining) + { + retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), + FLASH_PG | FLASH_PSIZE_8); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, address, buffer[bytes_written]); + 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, STM32_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++) + { + bank->sectors[i].offset = bank->size; + bank->sectors[i].size = size; + bank->size += bank->sectors[i].size; + } +} + +static int stm32x_probe(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct stm32x_flash_bank *stm32x_info = bank->driver_priv; + int i; + uint16_t num_pages; + uint32_t device_id; + uint32_t base_address = 0x08000000; + + stm32x_info->probed = 0; + + /* read stm32 device id register */ + int retval = target_read_u32(target, 0xE0042000, &device_id); + if (retval != ERROR_OK) + return retval; + LOG_INFO("device id = 0x%08" PRIx32 "", device_id); + + /* get flash size from target. */ + retval = target_read_u16(target, 0x1FFFF7E0, &num_pages); + if (retval != ERROR_OK) + { + LOG_WARNING("failed reading flash size, default to max target family"); + /* failed reading flash size, default to max target family */ + num_pages = 0xffff; + } + + if ((device_id & 0x7ff) != 0x411) + { + LOG_WARNING("Cannot identify target as a STM32 family, try the other STM32 drivers."); + return ERROR_FAIL; + } + + /* sectors sizes vary, handle this in a different code path + * than the rest. + */ + // Uhhh.... what to use here? + + /* calculate numbers of pages*/ + num_pages = 4 + 1 + 7; + + 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; + setup_sector(bank, 0, 4, 16 * 1024); + setup_sector(bank, 4, 1, 64 * 1024); + setup_sector(bank, 4+1, 7, 128 * 1024); + + for (i = 0; i < num_pages; i++) + { + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 0; + } + + LOG_INFO("flash size = %dkBytes", bank->size / 1024); + + stm32x_info->probed = 1; + + return ERROR_OK; +} + +static int stm32x_auto_probe(struct flash_bank *bank) +{ + struct stm32x_flash_bank *stm32x_info = bank->driver_priv; + if (stm32x_info->probed) + return ERROR_OK; + return stm32x_probe(bank); +} + +static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct target *target = bank->target; + uint32_t device_id; + int printed; + + /* read stm32 device id register */ + int retval = target_read_u32(target, 0xE0042000, &device_id); + if (retval != ERROR_OK) + return retval; + + if ((device_id & 0x7ff) == 0x411) + { + printed = snprintf(buf, buf_size, "stm32x (1mByte part) - Rev: "); + buf += printed; + buf_size -= printed; + + switch (device_id >> 16) + { + case 0x1000: + snprintf(buf, buf_size, "A"); + break; + + case 0x2000: + snprintf(buf, buf_size, "B"); + break; + + case 0x1001: + snprintf(buf, buf_size, "Z"); + break; + + default: + snprintf(buf, buf_size, "unknown"); + break; + } + } + else + { + snprintf(buf, buf_size, "Cannot identify target as a stm32x\n"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static const struct command_registration stm32x_exec_command_handlers[] = { + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration stm32x_command_handlers[] = { + { + .name = "stm32f2xxx", + .mode = COMMAND_ANY, + .help = "stm32f2xxx flash command group", + .chain = stm32x_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver stm32xf2xxx_flash = { + .name = "stm32f2xxx", + .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_mem_blank_check, + .protect_check = stm32x_protect_check, + .info = get_stm32x_info, +}; |