aboutsummaryrefslogtreecommitdiff
path: root/src/flash/nor/xmc1xxx.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/flash/nor/xmc1xxx.c')
-rw-r--r--src/flash/nor/xmc1xxx.c549
1 files changed, 549 insertions, 0 deletions
diff --git a/src/flash/nor/xmc1xxx.c b/src/flash/nor/xmc1xxx.c
new file mode 100644
index 0000000..bb2ec12
--- /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(&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,
+};