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 <afaerber@suse.de>
Reviewed-on: http://openocd.zylin.com/3418
Reviewed-by: Freddie Chopin <freddie.chopin@gmail.com>
Tested-by: jenkins
__archive__
Andreas Färber 2016-04-17 19:26:30 +02:00 committed by Freddie Chopin
parent edf2cdc80b
commit 44d2c7b416
16 changed files with 840 additions and 1 deletions

2
README
View File

@ -130,7 +130,7 @@ ADUC702x, AT91SAM, AVR, CFI, DSP5680xx, EFM32, EM357, FM3, FM4, Kinetis,
LPC8xx/LPC1xxx/LPC2xxx/LPC541xx, LPC2900, LPCSPIFI, Marvell QSPI, LPC8xx/LPC1xxx/LPC2xxx/LPC541xx, LPC2900, LPCSPIFI, Marvell QSPI,
Milandr, NIIET, NuMicro, PIC32mx, PSoC4, SiM3x, Stellaris, STM32, STMSMI, Milandr, NIIET, NuMicro, PIC32mx, PSoC4, SiM3x, Stellaris, STM32, STMSMI,
STR7x, STR9x, nRF51; NAND controllers of AT91SAM9, LPC3180, LPC32xx, 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.
================== ==================

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -5977,6 +5977,11 @@ the flash clock.
@end deffn @end deffn
@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 @deffn {Flash Driver} xmc4xxx
All members of the XMC4xxx microcontroller family from Infineon. All members of the XMC4xxx microcontroller family from Infineon.
This driver does not require the chip and bus width to be specified. This driver does not require the chip and bus width to be specified.

View File

@ -53,6 +53,7 @@ NOR_DRIVERS = \
str9xpec.c \ str9xpec.c \
tms470.c \ tms470.c \
virtual.c \ virtual.c \
xmc1xxx.c \
xmc4xxx.c xmc4xxx.c
noinst_HEADERS = \ noinst_HEADERS = \

View File

@ -65,6 +65,7 @@ extern struct flash_driver str9x_flash;
extern struct flash_driver str9xpec_flash; extern struct flash_driver str9xpec_flash;
extern struct flash_driver tms470_flash; extern struct flash_driver tms470_flash;
extern struct flash_driver virtual_flash; extern struct flash_driver virtual_flash;
extern struct flash_driver xmc1xxx_flash;
extern struct flash_driver xmc4xxx_flash; extern struct flash_driver xmc4xxx_flash;
/** /**
@ -115,6 +116,7 @@ static struct flash_driver *flash_drivers[] = {
&str9xpec_flash, &str9xpec_flash,
&tms470_flash, &tms470_flash,
&virtual_flash, &virtual_flash,
&xmc1xxx_flash,
&xmc4xxx_flash, &xmc4xxx_flash,
NULL, NULL,
}; };

549
src/flash/nor/xmc1xxx.c Normal file
View File

@ -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 <helper/binarybuffer.h>
#include <target/algorithm.h>
#include <target/armv7m.h>
#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(&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);
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(&reg_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(&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);
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(&reg_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(&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);
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(&reg_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,
};

View File

@ -9,6 +9,7 @@ source [find interface/jlink.cfg]
transport select swd transport select swd
set CHIPNAME xmc1100 set CHIPNAME xmc1100
set WORKAREASIZE 0x4000
source [find target/xmc1xxx.cfg] source [find target/xmc1xxx.cfg]
reset_config srst_only srst_nogate reset_config srst_only srst_nogate

View File

@ -9,6 +9,7 @@ source [find interface/jlink.cfg]
transport select swd transport select swd
set CHIPNAME xmc1100 set CHIPNAME xmc1100
set WORKAREASIZE 0x4000
source [find target/xmc1xxx.cfg] source [find target/xmc1xxx.cfg]
reset_config srst_only srst_nogate reset_config srst_only srst_nogate

View File

@ -24,4 +24,17 @@ swj_newdap $_CHIPNAME cpu -irlen 4 -expected-id $_CPU_SWD_TAPID
set _TARGETNAME $_CHIPNAME.cpu set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME cortex_m -endian little -chain-position $_TARGETNAME 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 adapter_khz 1000