diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/flash/nor/Makefile.am | 1 | ||||
-rw-r--r-- | src/flash/nor/drivers.c | 2 | ||||
-rw-r--r-- | src/flash/nor/xmc1xxx.c | 549 |
3 files changed, 552 insertions, 0 deletions
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index b5fab1f1..3386211e 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 51a40184..e39b37e4 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 00000000..bb2ec127 --- /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 <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, +}; |