From: Andreas Färber Date: Sun, 17 Apr 2016 17:26:30 +0000 (+0200) Subject: flash/nor: Add Infineon XMC1000 flash driver X-Git-Tag: v0.10.0-rc1~208 X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=commitdiff_plain;h=44d2c7b416fe3efe57a168dd58690b4e8d4effe0 flash/nor: Add Infineon XMC1000 flash driver The XMC1000 family uses a very different flash interface from XMC4000. Tested on XMC 2Go and XMC1100 Boot Kit. Change-Id: I3edaed420ef1c0fb89fdf221022c8b04163d41b3 Signed-off-by: Andreas Färber Reviewed-on: http://openocd.zylin.com/3418 Reviewed-by: Freddie Chopin Tested-by: jenkins --- diff --git a/README b/README index a6b4c5bd01..11ea2db3b2 100644 --- a/README +++ b/README @@ -130,7 +130,7 @@ ADUC702x, AT91SAM, AVR, CFI, DSP5680xx, EFM32, EM357, FM3, FM4, Kinetis, LPC8xx/LPC1xxx/LPC2xxx/LPC541xx, LPC2900, LPCSPIFI, Marvell QSPI, Milandr, NIIET, NuMicro, PIC32mx, PSoC4, SiM3x, Stellaris, STM32, STMSMI, STR7x, STR9x, nRF51; NAND controllers of AT91SAM9, LPC3180, LPC32xx, -i.MX31, MXC, NUC910, Orion/Kirkwood, S3C24xx, S3C6400, XMC4xxx. +i.MX31, MXC, NUC910, Orion/Kirkwood, S3C24xx, S3C6400, XMC1xxx, XMC4xxx. ================== diff --git a/contrib/loaders/flash/xmc1xxx/Makefile b/contrib/loaders/flash/xmc1xxx/Makefile new file mode 100644 index 0000000000..066466efdb --- /dev/null +++ b/contrib/loaders/flash/xmc1xxx/Makefile @@ -0,0 +1,30 @@ +BIN2C = ../../../../src/helper/bin2char.sh + +CROSS_COMPILE ?= arm-none-eabi- + +CC=$(CROSS_COMPILE)gcc +OBJCOPY=$(CROSS_COMPILE)objcopy +OBJDUMP=$(CROSS_COMPILE)objdump + +all: erase.inc erase_check.inc write.inc + +.PHONY: clean + +.INTERMEDIATE: erase.elf erase_check.elf write.elf + +erase.elf erase_check.elf write.elf: xmc1xxx.S + +%.elf: %.S + $(CC) -static -nostartfiles $< -o $@ + +%.lst: %.elf + $(OBJDUMP) -S $< > $@ + +%.bin: %.elf + $(OBJCOPY) -Obinary $< $@ + +%.inc: %.bin + $(BIN2C) < $< > $@ + +clean: + -rm -f *.elf *.lst *.bin *.inc diff --git a/contrib/loaders/flash/xmc1xxx/erase.S b/contrib/loaders/flash/xmc1xxx/erase.S new file mode 100644 index 0000000000..e5a4808fcc --- /dev/null +++ b/contrib/loaders/flash/xmc1xxx/erase.S @@ -0,0 +1,53 @@ +/* + * Infineon XMC1000 flash sectors erase + * + * Copyright (c) 2016 Andreas Färber + * + * Based on XMC1100 AA-Step Reference Manual + * + * License: GPL-2.0+ + */ + +#include "xmc1xxx.S" + +#define DUMMY_VALUE 0x42 + + .macro erase_page, nvmbase, addr, tmp, tmp2 + + movs \tmp, #DUMMY_VALUE + str \tmp, [\addr] + + busy_wait \nvmbase, \tmp, \tmp2 + + .endm + + + .macro erase, nvmbase, addr, end, tmp, tmp2 + + movs \tmp, #NVMPROG_ACTION_PAGE_ERASE_CONTINUOUS + strh \tmp, [\nvmbase, #NVMPROG] +2001: + erase_page \nvmbase, \addr, \tmp, \tmp2 + + movs \tmp, #(NVM_PAGE_SIZE - 1) + adds \tmp, \tmp, #1 + add \addr, \addr, \tmp + cmp \addr, \end + blt 2001b + + movs \tmp, #NVMPROG_ACTION_IDLE + strh \tmp, [\nvmbase, #NVMPROG] + + .endm + + + /* + * r0 = 0x40050000 + * r1 = e.g. 0x10001000 + * r2 = e.g. 0x10011000 + * NVMPROG.ACTION = 0x00 + */ +erase: + erase r0, r1, r2, r3, r4 + + bkpt #0 diff --git a/contrib/loaders/flash/xmc1xxx/erase.inc b/contrib/loaders/flash/xmc1xxx/erase.inc new file mode 100644 index 0000000000..b33e57d1f5 --- /dev/null +++ b/contrib/loaders/flash/xmc1xxx/erase.inc @@ -0,0 +1,4 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0xa2,0x23,0x83,0x80,0x42,0x23,0x0b,0x60,0x03,0x88,0x01,0x24,0x23,0x40,0xa3,0x42, +0xfa,0xd0,0xff,0x23,0x01,0x33,0x19,0x44,0x91,0x42,0xf3,0xdb,0x00,0x23,0x83,0x80, +0x00,0xbe, diff --git a/contrib/loaders/flash/xmc1xxx/erase_check.S b/contrib/loaders/flash/xmc1xxx/erase_check.S new file mode 100644 index 0000000000..6c993443a9 --- /dev/null +++ b/contrib/loaders/flash/xmc1xxx/erase_check.S @@ -0,0 +1,67 @@ +/* + * Infineon XMC1000 flash sector erase check + * + * Copyright (c) 2016 Andreas Färber + * + * Based on XMC1100 AA-Step Reference Manual + * + * License: GPL-2.0+ + */ + +#include "xmc1xxx.S" + + .macro verify_block, nvmbase, addr, tmp, tmp2 + + movs \tmp, #0x00 + mvns \tmp, \tmp + str \tmp, [\addr, #0x0] + str \tmp, [\addr, #0x4] + str \tmp, [\addr, #0x8] + str \tmp, [\addr, #0xC] + + busy_wait \nvmbase, \tmp, \tmp2 + + .endm + + + .macro erase_check, nvmbase, addr, end, tmp, tmp2 + + ldrh \tmp, [\nvmbase, #NVMCONF] + movs \tmp2, #NVMCONF_HRLEV_MASK + mvns \tmp2, \tmp2 + ands \tmp, \tmp, \tmp2 + movs \tmp2, #NVMCONF_HRLEV_HRE + orrs \tmp, \tmp, \tmp2 + strh \tmp, [\nvmbase, #NVMCONF] + + movs \tmp, #NVMPROG_ACTION_VERIFY_CONTINUOUS + strh \tmp, [\nvmbase, #NVMPROG] +2001: + verify_block \nvmbase, \addr, \tmp, \tmp2 + + ldrh \tmp, [\nvmbase, #NVMSTATUS] + movs \tmp2, #NVMSTATUS_VERR_MASK + ands \tmp, \tmp, \tmp2 + cmp \tmp, #NVMSTATUS_VERR_NOFAIL + bne 2010f + + adds \addr, \addr, #NVM_BLOCK_SIZE + cmp \addr, \end + blt 2001b +2010: + movs \tmp, #NVMPROG_ACTION_IDLE + strh \tmp, [\nvmbase, #NVMPROG] + + .endm + + + /* + * r0 = 0x40050000 + * r1 = e.g. 0x10001000 + * r2 = e.g. 0x10002000 + * NVMPROG.ACTION = 0x00 + */ +erase_check: + erase_check r0, r1, r2, r3, r4 + + bkpt #0 diff --git a/contrib/loaders/flash/xmc1xxx/erase_check.inc b/contrib/loaders/flash/xmc1xxx/erase_check.inc new file mode 100644 index 0000000000..8fc8e0b5d5 --- /dev/null +++ b/contrib/loaders/flash/xmc1xxx/erase_check.inc @@ -0,0 +1,5 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0x03,0x89,0x06,0x24,0xe4,0x43,0x23,0x40,0x04,0x24,0x23,0x43,0x03,0x81,0xe0,0x23, +0x83,0x80,0x00,0x23,0xdb,0x43,0x0b,0x60,0x4b,0x60,0x8b,0x60,0xcb,0x60,0x03,0x88, +0x01,0x24,0x23,0x40,0xa3,0x42,0xfa,0xd0,0x03,0x88,0x0c,0x24,0x23,0x40,0x00,0x2b, +0x02,0xd1,0x10,0x31,0x91,0x42,0xec,0xdb,0x00,0x23,0x83,0x80,0x00,0xbe, diff --git a/contrib/loaders/flash/xmc1xxx/write.S b/contrib/loaders/flash/xmc1xxx/write.S new file mode 100644 index 0000000000..640f6ca960 --- /dev/null +++ b/contrib/loaders/flash/xmc1xxx/write.S @@ -0,0 +1,58 @@ +/* + * Infineon XMC1000 flash write + * + * Copyright (c) 2016 Andreas Färber + * + * Based on XMC1100 AA-Step Reference Manual + * + * License: GPL-2.0+ + */ + +#include "xmc1xxx.S" + + .macro write_block, nvmbase, dest, src, tmp, tmp2 + + ldr \tmp, [\src, #0x0] + str \tmp, [\dest, #0x0] + ldr \tmp, [\src, #0x4] + str \tmp, [\dest, #0x4] + ldr \tmp, [\src, #0x8] + str \tmp, [\dest, #0x8] + ldr \tmp, [\src, #0xc] + str \tmp, [\dest, #0xc] + + busy_wait \nvmbase, \tmp, \tmp2 + + .endm + + + .macro write, nvmbase, dest, src, count, tmp, tmp2 + + movs \tmp, #NVMPROG_ACTION_WRITE_CONTINUOUS + strh \tmp, [\nvmbase, #NVMPROG] +1001: + write_block \nvmbase, \dest, \src, \tmp, \tmp2 + + adds \dest, \dest, #NVM_BLOCK_SIZE + adds \src, \src, #NVM_BLOCK_SIZE + subs \count, \count, #1 + cmp \count, #0 + bgt 1001b + + movs \tmp, #NVMPROG_ACTION_IDLE + strh \tmp, [\nvmbase, #NVMPROG] + + .endm + + + /* + * r0 = 0x40050000 + * r1 = e.g. 0x10001000 + * r2 = e.g. 0x20000000 + * r3 = e.g. 1 + * NVMPROG.ACTION = 0x00 + */ +write: + write r0, r1, r2, r3, r4, r5 + + bkpt #0 diff --git a/contrib/loaders/flash/xmc1xxx/write.inc b/contrib/loaders/flash/xmc1xxx/write.inc new file mode 100644 index 0000000000..8272bb7e7d --- /dev/null +++ b/contrib/loaders/flash/xmc1xxx/write.inc @@ -0,0 +1,4 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0xa1,0x24,0x84,0x80,0x14,0x68,0x0c,0x60,0x54,0x68,0x4c,0x60,0x94,0x68,0x8c,0x60, +0xd4,0x68,0xcc,0x60,0x04,0x88,0x01,0x25,0x2c,0x40,0xac,0x42,0xfa,0xd0,0x10,0x31, +0x10,0x32,0x01,0x3b,0x00,0x2b,0xed,0xdc,0x00,0x24,0x84,0x80,0x00,0xbe, diff --git a/contrib/loaders/flash/xmc1xxx/xmc1xxx.S b/contrib/loaders/flash/xmc1xxx/xmc1xxx.S new file mode 100644 index 0000000000..dfe7d3f41e --- /dev/null +++ b/contrib/loaders/flash/xmc1xxx/xmc1xxx.S @@ -0,0 +1,46 @@ +/* + * Infineon XMC1000 flash + * + * Copyright (c) 2016 Andreas Färber + * + * Based on XMC1100 AA-Step Reference Manual + * + * License: GPL-2.0+ + */ + + .text + .syntax unified + .cpu cortex-m0 + .thumb + .thumb_func + +#define NVMSTATUS 0x00 +#define NVMPROG 0x04 +#define NVMCONF 0x08 + +#define NVMSTATUS_BUSY (1 << 0) +#define NVMSTATUS_VERR_NOFAIL (0x0 << 2) +#define NVMSTATUS_VERR_MASK (0x3 << 2) + +#define NVMPROG_ACTION_IDLE 0x00 +#define NVMPROG_ACTION_WRITE_CONTINUOUS 0xA1 +#define NVMPROG_ACTION_PAGE_ERASE_CONTINUOUS 0xA2 +#define NVMPROG_ACTION_VERIFY_CONTINUOUS 0xE0 + +#define NVMCONF_HRLEV_NR (0x0 << 1) +#define NVMCONF_HRLEV_HRE (0x2 << 1) +#define NVMCONF_HRLEV_MASK (0x3 << 1) + +#define NVM_WORD_SIZE 4 +#define NVM_BLOCK_SIZE (4 * NVM_WORD_SIZE) +#define NVM_PAGE_SIZE (16 * NVM_BLOCK_SIZE) + + .macro busy_wait, nvmbase, tmp, tmp2 +1: + ldrh \tmp, [\nvmbase, #NVMSTATUS] + movs \tmp2, #NVMSTATUS_BUSY + ands \tmp, \tmp, \tmp2 + cmp \tmp, \tmp2 + beq 1b + + .endm diff --git a/doc/openocd.texi b/doc/openocd.texi index 8e7c7f8a70..e839191b23 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -5977,6 +5977,11 @@ the flash clock. @end deffn @end deffn +@deffn {Flash Driver} xmc1xxx +All members of the XMC1xxx microcontroller family from Infineon. +This driver does not require the chip and bus width to be specified. +@end deffn + @deffn {Flash Driver} xmc4xxx All members of the XMC4xxx microcontroller family from Infineon. This driver does not require the chip and bus width to be specified. diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index b5fab1f120..3386211ee6 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -53,6 +53,7 @@ NOR_DRIVERS = \ str9xpec.c \ tms470.c \ virtual.c \ + xmc1xxx.c \ xmc4xxx.c noinst_HEADERS = \ diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 51a401840e..e39b37e4b6 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -65,6 +65,7 @@ extern struct flash_driver str9x_flash; extern struct flash_driver str9xpec_flash; extern struct flash_driver tms470_flash; extern struct flash_driver virtual_flash; +extern struct flash_driver xmc1xxx_flash; extern struct flash_driver xmc4xxx_flash; /** @@ -115,6 +116,7 @@ static struct flash_driver *flash_drivers[] = { &str9xpec_flash, &tms470_flash, &virtual_flash, + &xmc1xxx_flash, &xmc4xxx_flash, NULL, }; diff --git a/src/flash/nor/xmc1xxx.c b/src/flash/nor/xmc1xxx.c new file mode 100644 index 0000000000..bb2ec1272a --- /dev/null +++ b/src/flash/nor/xmc1xxx.c @@ -0,0 +1,549 @@ +/* + * XMC1000 flash driver + * + * Copyright (c) 2016 Andreas Färber + * + * License: GPL-2.0+ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include +#include +#include + +#define FLASH_BASE 0x10000000 +#define PAU_BASE 0x40000000 +#define SCU_BASE 0x40010000 +#define NVM_BASE 0x40050000 + +#define FLASH_CS0 (FLASH_BASE + 0xf00) + +#define PAU_FLSIZE (PAU_BASE + 0x404) + +#define SCU_IDCHIP (SCU_BASE + 0x004) + +#define NVMSTATUS (NVM_BASE + 0x00) +#define NVMPROG (NVM_BASE + 0x04) +#define NVMCONF (NVM_BASE + 0x08) + +#define NVMSTATUS_BUSY (1 << 0) +#define NVMSTATUS_VERR_MASK (0x3 << 2) + +#define NVMPROG_ACTION_OPTYPE_IDLE_VERIFY (0 << 0) +#define NVMPROG_ACTION_OPTYPE_WRITE (1 << 0) +#define NVMPROG_ACTION_OPTYPE_PAGE_ERASE (2 << 0) + +#define NVMPROG_ACTION_ONE_SHOT_ONCE (1 << 4) +#define NVMPROG_ACTION_ONE_SHOT_CONTINUOUS (2 << 4) + +#define NVMPROG_ACTION_VERIFY_EACH (1 << 6) +#define NVMPROG_ACTION_VERIFY_NO (2 << 6) +#define NVMPROG_ACTION_VERIFY_ARRAY (3 << 6) + +#define NVMPROG_ACTION_IDLE 0x00 +#define NVMPROG_ACTION_MASK 0xff + +#define NVM_WORD_SIZE 4 +#define NVM_BLOCK_SIZE (4 * NVM_WORD_SIZE) +#define NVM_PAGE_SIZE (16 * NVM_BLOCK_SIZE) + +struct xmc1xxx_flash_bank { + bool probed; +}; + +static int xmc1xxx_nvm_set_idle(struct target *target) +{ + return target_write_u16(target, NVMPROG, NVMPROG_ACTION_IDLE); +} + +static int xmc1xxx_nvm_check_idle(struct target *target) +{ + uint16_t val; + int retval; + + retval = target_read_u16(target, NVMPROG, &val); + if (retval != ERROR_OK) + return retval; + if ((val & NVMPROG_ACTION_MASK) != NVMPROG_ACTION_IDLE) { + LOG_WARNING("NVMPROG.ACTION"); + retval = xmc1xxx_nvm_set_idle(target); + } + + return retval; +} + +static int xmc1xxx_erase(struct flash_bank *bank, int first, int last) +{ + struct target *target = bank->target; + struct working_area *workarea; + struct reg_param reg_params[3]; + struct armv7m_algorithm armv7m_algo; + unsigned i; + int retval, sector; + const uint8_t erase_code[] = { +#include "../../../contrib/loaders/flash/xmc1xxx/erase.inc" + }; + + LOG_DEBUG("Infineon XMC1000 erase sectors %d to %d", first, last); + + if (bank->target->state != TARGET_HALTED) { + LOG_WARNING("Cannot communicate... target not halted."); + return ERROR_TARGET_NOT_HALTED; + } + + retval = xmc1xxx_nvm_check_idle(target); + if (retval != ERROR_OK) + return retval; + + retval = target_alloc_working_area(target, sizeof(erase_code), + &workarea); + if (retval != ERROR_OK) { + LOG_ERROR("No working area available."); + retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + goto err_alloc_code; + } + retval = target_write_buffer(target, workarea->address, + sizeof(erase_code), erase_code); + if (retval != ERROR_OK) + goto err_write_code; + + armv7m_algo.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_algo.core_mode = ARM_MODE_THREAD; + + 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); + + buf_set_u32(reg_params[0].value, 0, 32, NVM_BASE); + buf_set_u32(reg_params[1].value, 0, 32, bank->base + + bank->sectors[first].offset); + buf_set_u32(reg_params[2].value, 0, 32, bank->base + + bank->sectors[last].offset + bank->sectors[last].size); + + retval = target_run_algorithm(target, + 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + workarea->address, 0, + 1000, &armv7m_algo); + if (retval != ERROR_OK) { + LOG_ERROR("Error executing flash sector erase " + "programming algorithm"); + retval = xmc1xxx_nvm_set_idle(target); + if (retval != ERROR_OK) + LOG_WARNING("Couldn't restore NVMPROG.ACTION"); + retval = ERROR_FLASH_OPERATION_FAILED; + goto err_run; + } + + for (sector = first; sector <= last; sector++) + bank->sectors[sector].is_erased = 1; + +err_run: + for (i = 0; i < ARRAY_SIZE(reg_params); i++) + destroy_reg_param(®_params[i]); + +err_write_code: + target_free_working_area(target, workarea); + +err_alloc_code: + return retval; +} + +static int xmc1xxx_erase_check(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct working_area *workarea; + struct reg_param reg_params[3]; + struct armv7m_algorithm armv7m_algo; + uint16_t val; + unsigned i; + int retval, sector; + const uint8_t erase_check_code[] = { +#include "../../../contrib/loaders/flash/xmc1xxx/erase_check.inc" + }; + + if (bank->target->state != TARGET_HALTED) { + LOG_WARNING("Cannot communicate... target not halted."); + return ERROR_TARGET_NOT_HALTED; + } + + retval = target_alloc_working_area(target, sizeof(erase_check_code), + &workarea); + if (retval != ERROR_OK) { + LOG_ERROR("No working area available."); + retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + goto err_alloc_code; + } + retval = target_write_buffer(target, workarea->address, + sizeof(erase_check_code), erase_check_code); + if (retval != ERROR_OK) + goto err_write_code; + + armv7m_algo.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_algo.core_mode = ARM_MODE_THREAD; + + 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); + + buf_set_u32(reg_params[0].value, 0, 32, NVM_BASE); + + for (sector = 0; sector < bank->num_sectors; sector++) { + uint32_t start = bank->base + bank->sectors[sector].offset; + buf_set_u32(reg_params[1].value, 0, 32, start); + buf_set_u32(reg_params[2].value, 0, 32, start + bank->sectors[sector].size); + + retval = xmc1xxx_nvm_check_idle(target); + if (retval != ERROR_OK) + goto err_nvmprog; + + LOG_DEBUG("Erase-checking 0x%08" PRIx32, start); + retval = target_run_algorithm(target, + 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + workarea->address, 0, + 1000, &armv7m_algo); + if (retval != ERROR_OK) { + LOG_ERROR("Error executing flash sector erase check " + "programming algorithm"); + retval = xmc1xxx_nvm_set_idle(target); + if (retval != ERROR_OK) + LOG_WARNING("Couldn't restore NVMPROG.ACTION"); + retval = ERROR_FLASH_OPERATION_FAILED; + goto err_run; + } + + retval = target_read_u16(target, NVMSTATUS, &val); + if (retval != ERROR_OK) { + LOG_ERROR("Couldn't read NVMSTATUS"); + goto err_nvmstatus; + } + bank->sectors[sector].is_erased = (val & NVMSTATUS_VERR_MASK) ? 0 : 1; + } + +err_nvmstatus: +err_run: +err_nvmprog: + for (i = 0; i < ARRAY_SIZE(reg_params); i++) + destroy_reg_param(®_params[i]); + +err_write_code: + target_free_working_area(target, workarea); + +err_alloc_code: + return retval; +} + +static int xmc1xxx_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t byte_count) +{ + struct target *target = bank->target; + struct working_area *code_workarea, *data_workarea; + struct reg_param reg_params[4]; + struct armv7m_algorithm armv7m_algo; + uint32_t block_count = DIV_ROUND_UP(byte_count, NVM_BLOCK_SIZE); + unsigned i; + int retval; + const uint8_t write_code[] = { +#include "../../../contrib/loaders/flash/xmc1xxx/write.inc" + }; + + LOG_DEBUG("Infineon XMC1000 write at 0x%08" PRIx32 " (%" PRId32 " bytes)", + offset, byte_count); + + if (offset & (NVM_BLOCK_SIZE - 1)) { + LOG_ERROR("offset 0x%" PRIx32 " breaks required block alignment", + offset); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + if (byte_count & (NVM_BLOCK_SIZE - 1)) { + LOG_WARNING("length %" PRId32 " is not block aligned, rounding up", + byte_count); + } + + if (target->state != TARGET_HALTED) { + LOG_WARNING("Cannot communicate... target not halted."); + return ERROR_TARGET_NOT_HALTED; + } + + retval = target_alloc_working_area(target, sizeof(write_code), + &code_workarea); + if (retval != ERROR_OK) { + LOG_ERROR("No working area available for write code."); + retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + goto err_alloc_code; + } + retval = target_write_buffer(target, code_workarea->address, + sizeof(write_code), write_code); + if (retval != ERROR_OK) + goto err_write_code; + + retval = target_alloc_working_area(target, MAX(NVM_BLOCK_SIZE, + MIN(block_count * NVM_BLOCK_SIZE, target_get_working_area_avail(target))), + &data_workarea); + if (retval != ERROR_OK) { + LOG_ERROR("No working area available for write data."); + retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + goto err_alloc_data; + } + + armv7m_algo.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_algo.core_mode = ARM_MODE_THREAD; + + 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_OUT); + + buf_set_u32(reg_params[0].value, 0, 32, NVM_BASE); + + while (byte_count > 0) { + uint32_t blocks = MIN(block_count, data_workarea->size / NVM_BLOCK_SIZE); + uint32_t addr = bank->base + offset; + + LOG_DEBUG("copying %" PRId32 " bytes to SRAM 0x%08" PRIx32, + MIN(blocks * NVM_BLOCK_SIZE, byte_count), + data_workarea->address); + + retval = target_write_buffer(target, data_workarea->address, + MIN(blocks * NVM_BLOCK_SIZE, byte_count), buffer); + if (retval != ERROR_OK) { + LOG_ERROR("Error writing data buffer"); + retval = ERROR_FLASH_OPERATION_FAILED; + goto err_write_data; + } + if (byte_count < blocks * NVM_BLOCK_SIZE) { + retval = target_write_memory(target, + data_workarea->address + byte_count, 1, + blocks * NVM_BLOCK_SIZE - byte_count, + &bank->default_padded_value); + if (retval != ERROR_OK) { + LOG_ERROR("Error writing data padding"); + retval = ERROR_FLASH_OPERATION_FAILED; + goto err_write_pad; + } + } + + LOG_DEBUG("writing 0x%08" PRIx32 "-0x%08" PRIx32 " (%" PRId32 "x)", + addr, addr + blocks * NVM_BLOCK_SIZE - 1, blocks); + + retval = xmc1xxx_nvm_check_idle(target); + if (retval != ERROR_OK) + goto err_nvmprog; + + buf_set_u32(reg_params[1].value, 0, 32, addr); + buf_set_u32(reg_params[2].value, 0, 32, data_workarea->address); + buf_set_u32(reg_params[3].value, 0, 32, blocks); + + retval = target_run_algorithm(target, + 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + code_workarea->address, 0, + 5 * 60 * 1000, &armv7m_algo); + if (retval != ERROR_OK) { + LOG_ERROR("Error executing flash write " + "programming algorithm"); + retval = xmc1xxx_nvm_set_idle(target); + if (retval != ERROR_OK) + LOG_WARNING("Couldn't restore NVMPROG.ACTION"); + retval = ERROR_FLASH_OPERATION_FAILED; + goto err_run; + } + + block_count -= blocks; + offset += blocks * NVM_BLOCK_SIZE; + buffer += blocks * NVM_BLOCK_SIZE; + byte_count -= MIN(blocks * NVM_BLOCK_SIZE, byte_count); + } + +err_run: +err_nvmprog: +err_write_pad: +err_write_data: + for (i = 0; i < ARRAY_SIZE(reg_params); i++) + destroy_reg_param(®_params[i]); + + target_free_working_area(target, data_workarea); +err_alloc_data: +err_write_code: + target_free_working_area(target, code_workarea); + +err_alloc_code: + return retval; +} + +static int xmc1xxx_protect_check(struct flash_bank *bank) +{ + uint32_t nvmconf; + int i, num_protected, retval; + + if (bank->target->state != TARGET_HALTED) { + LOG_WARNING("Cannot communicate... target not halted."); + return ERROR_TARGET_NOT_HALTED; + } + + retval = target_read_u32(bank->target, NVMCONF, &nvmconf); + if (retval != ERROR_OK) { + LOG_ERROR("Cannot read NVMCONF register."); + return retval; + } + LOG_DEBUG("NVMCONF = %08" PRIx32, nvmconf); + + num_protected = (nvmconf >> 4) & 0xff; + + for (i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_protected = (i < num_protected) ? 1 : 0; + + return ERROR_OK; +} + +static int xmc1xxx_get_info_command(struct flash_bank *bank, char *buf, int buf_size) +{ + uint32_t chipid[8]; + int i, retval; + + if (bank->target->state != TARGET_HALTED) { + LOG_WARNING("Cannot communicate... target not halted."); + return ERROR_TARGET_NOT_HALTED; + } + + /* Obtain the 8-word Chip Identification Number */ + for (i = 0; i < 7; i++) { + retval = target_read_u32(bank->target, FLASH_CS0 + i * 4, &chipid[i]); + if (retval != ERROR_OK) { + LOG_ERROR("Cannot read CS0 register %i.", i); + return retval; + } + LOG_DEBUG("ID[%d] = %08" PRIX32, i, chipid[i]); + } + retval = target_read_u32(bank->target, SCU_BASE + 0x000, &chipid[7]); + if (retval != ERROR_OK) { + LOG_ERROR("Cannot read DBGROMID register."); + return retval; + } + LOG_DEBUG("ID[7] = %08" PRIX32, chipid[7]); + + snprintf(buf, buf_size, "XMC%" PRIx32 "00 %X flash %uKB ROM %uKB SRAM %uKB", + (chipid[0] >> 12) & 0xff, + 0xAA + (chipid[7] >> 28) - 1, + (((chipid[6] >> 12) & 0x3f) - 1) * 4, + (((chipid[4] >> 8) & 0x3f) * 256) / 1024, + (((chipid[5] >> 8) & 0x1f) * 256 * 4) / 1024); + + return ERROR_OK; +} + +static int xmc1xxx_probe(struct flash_bank *bank) +{ + struct xmc1xxx_flash_bank *xmc_bank = bank->driver_priv; + uint32_t flash_addr = bank->base; + uint32_t idchip, flsize; + int i, retval; + + if (xmc_bank->probed) + return ERROR_OK; + + if (bank->target->state != TARGET_HALTED) { + LOG_WARNING("Cannot communicate... target not halted."); + return ERROR_TARGET_NOT_HALTED; + } + + retval = target_read_u32(bank->target, SCU_IDCHIP, &idchip); + if (retval != ERROR_OK) { + LOG_ERROR("Cannot read IDCHIP register."); + return retval; + } + + if ((idchip & 0xffff0000) != 0x10000) { + LOG_ERROR("IDCHIP register does not match XMC1xxx."); + return ERROR_FAIL; + } + + LOG_DEBUG("IDCHIP = %08" PRIx32, idchip); + + retval = target_read_u32(bank->target, PAU_FLSIZE, &flsize); + if (retval != ERROR_OK) { + LOG_ERROR("Cannot read FLSIZE register."); + return retval; + } + + bank->num_sectors = 1 + ((flsize >> 12) & 0x3f) - 1; + bank->size = bank->num_sectors * 4 * 1024; + bank->sectors = calloc(bank->num_sectors, + sizeof(struct flash_sector)); + for (i = 0; i < bank->num_sectors; i++) { + if (i == 0) { + bank->sectors[i].size = 0x200; + bank->sectors[i].offset = 0xE00; + flash_addr += 0x1000; + } else { + bank->sectors[i].size = 4 * 1024; + bank->sectors[i].offset = flash_addr - bank->base; + flash_addr += bank->sectors[i].size; + } + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = -1; + } + + xmc_bank->probed = true; + + return ERROR_OK; +} + +static int xmc1xxx_auto_probe(struct flash_bank *bank) +{ + struct xmc1xxx_flash_bank *xmc_bank = bank->driver_priv; + + if (xmc_bank->probed) + return ERROR_OK; + + return xmc1xxx_probe(bank); +} + +FLASH_BANK_COMMAND_HANDLER(xmc1xxx_flash_bank_command) +{ + struct xmc1xxx_flash_bank *xmc_bank; + + xmc_bank = malloc(sizeof(struct xmc1xxx_flash_bank)); + if (!xmc_bank) + return ERROR_FLASH_OPERATION_FAILED; + + xmc_bank->probed = false; + + bank->driver_priv = xmc_bank; + + return ERROR_OK; +} + +static const struct command_registration xmc1xxx_exec_command_handlers[] = { + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration xmc1xxx_command_handlers[] = { + { + .name = "xmc1xxx", + .mode = COMMAND_ANY, + .help = "xmc1xxx flash command group", + .usage = "", + .chain = xmc1xxx_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver xmc1xxx_flash = { + .name = "xmc1xxx", + .commands = xmc1xxx_command_handlers, + .flash_bank_command = xmc1xxx_flash_bank_command, + .info = xmc1xxx_get_info_command, + .probe = xmc1xxx_probe, + .auto_probe = xmc1xxx_auto_probe, + .protect_check = xmc1xxx_protect_check, + .read = default_flash_read, + .erase = xmc1xxx_erase, + .erase_check = xmc1xxx_erase_check, + .write = xmc1xxx_write, +}; diff --git a/tcl/board/xmc-2go.cfg b/tcl/board/xmc-2go.cfg index 664426307c..90dbf43660 100644 --- a/tcl/board/xmc-2go.cfg +++ b/tcl/board/xmc-2go.cfg @@ -9,6 +9,7 @@ source [find interface/jlink.cfg] transport select swd set CHIPNAME xmc1100 +set WORKAREASIZE 0x4000 source [find target/xmc1xxx.cfg] reset_config srst_only srst_nogate diff --git a/tcl/board/xmc1100-boot-kit.cfg b/tcl/board/xmc1100-boot-kit.cfg index 4c83fd3ace..5e7c607344 100644 --- a/tcl/board/xmc1100-boot-kit.cfg +++ b/tcl/board/xmc1100-boot-kit.cfg @@ -9,6 +9,7 @@ source [find interface/jlink.cfg] transport select swd set CHIPNAME xmc1100 +set WORKAREASIZE 0x4000 source [find target/xmc1xxx.cfg] reset_config srst_only srst_nogate diff --git a/tcl/target/xmc1xxx.cfg b/tcl/target/xmc1xxx.cfg index 0a0e47eeac..d3123c4376 100644 --- a/tcl/target/xmc1xxx.cfg +++ b/tcl/target/xmc1xxx.cfg @@ -24,4 +24,17 @@ swj_newdap $_CHIPNAME cpu -irlen 4 -expected-id $_CPU_SWD_TAPID set _TARGETNAME $_CHIPNAME.cpu target create $_TARGETNAME cortex_m -endian little -chain-position $_TARGETNAME +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x4000 +} + +$_TARGETNAME configure -work-area-phys 0x20000000 \ + -work-area-size $_WORKAREASIZE \ + -work-area-backup 0 + +set _FLASHNAME $_CHIPNAME.flash +flash bank $_FLASHNAME xmc1xxx 0x10000000 0 0 0 $_TARGETNAME + adapter_khz 1000