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__
parent
edf2cdc80b
commit
44d2c7b416
2
README
2
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.
|
||||
|
||||
|
||||
==================
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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,
|
|
@ -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
|
|
@ -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,
|
|
@ -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
|
|
@ -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,
|
|
@ -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
|
|
@ -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.
|
||||
|
|
|
@ -53,6 +53,7 @@ NOR_DRIVERS = \
|
|||
str9xpec.c \
|
||||
tms470.c \
|
||||
virtual.c \
|
||||
xmc1xxx.c \
|
||||
xmc4xxx.c
|
||||
|
||||
noinst_HEADERS = \
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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(®_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,
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue