From 6060545458f6863710d576fc4bd2512d34f88f89 Mon Sep 17 00:00:00 2001 From: Kevin Gillespie Date: Thu, 14 Jul 2016 13:00:23 -0500 Subject: [PATCH] max32xxx: Support for MAX32XXX devices. Adding flash programming support for Maxim Integrated MAX32XXX devices. Change-Id: I5b0f57a885f9d813240e4bc2d9f765b743e1cfc3 Signed-off-by: Kevin Gillespie Reviewed-on: http://openocd.zylin.com/3543 Tested-by: jenkins Reviewed-by: Ismail H. KOSE Reviewed-by: Tomas Vanek Reviewed-by: Andreas Fritiofson --- contrib/loaders/Makefile | 1 + contrib/loaders/flash/max32xxx/Makefile | 19 + contrib/loaders/flash/max32xxx/max32xxx.inc | 6 + contrib/loaders/flash/max32xxx/max32xxx.s | 70 ++ src/flash/nor/Makefile.am | 1 + src/flash/nor/drivers.c | 2 + src/flash/nor/max32xxx.c | 997 ++++++++++++++++++++ tcl/target/max32620.cfg | 28 + tcl/target/max32625.cfg | 28 + tcl/target/max3263x.cfg | 28 + 10 files changed, 1180 insertions(+) create mode 100644 contrib/loaders/flash/max32xxx/Makefile create mode 100644 contrib/loaders/flash/max32xxx/max32xxx.inc create mode 100644 contrib/loaders/flash/max32xxx/max32xxx.s create mode 100644 src/flash/nor/max32xxx.c create mode 100644 tcl/target/max32620.cfg create mode 100644 tcl/target/max32625.cfg create mode 100644 tcl/target/max3263x.cfg diff --git a/contrib/loaders/Makefile b/contrib/loaders/Makefile index a9a27706d6..0a637aff59 100644 --- a/contrib/loaders/Makefile +++ b/contrib/loaders/Makefile @@ -12,6 +12,7 @@ ARM_CROSS_COMPILE ?= arm-none-eabi- arm_dirs = \ flash/fm4 \ flash/kinetis_ke \ + flash/max32xxx \ flash/xmc1xxx \ debug/xscale diff --git a/contrib/loaders/flash/max32xxx/Makefile b/contrib/loaders/flash/max32xxx/Makefile new file mode 100644 index 0000000000..8f3f9242e4 --- /dev/null +++ b/contrib/loaders/flash/max32xxx/Makefile @@ -0,0 +1,19 @@ +BIN2C = ../../../../src/helper/bin2char.sh + +CROSS_COMPILE ?= arm-none-eabi- +AS = $(CROSS_COMPILE)as +OBJCOPY = $(CROSS_COMPILE)objcopy + +all: max32xxx.inc + +%.elf: %.s + $(AS) $< -o $@ + +%.bin: %.elf + $(OBJCOPY) -Obinary $< $@ + +%.inc: %.bin + $(BIN2C) < $< > $@ + +clean: + -rm -f *.elf *.bin *.inc diff --git a/contrib/loaders/flash/max32xxx/max32xxx.inc b/contrib/loaders/flash/max32xxx/max32xxx.inc new file mode 100644 index 0000000000..442165d0d1 --- /dev/null +++ b/contrib/loaders/flash/max32xxx/max32xxx.inc @@ -0,0 +1,6 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0xdf,0xf8,0x44,0x40,0xd0,0xf8,0x00,0x80,0xb8,0xf1,0x00,0x0f,0x1a,0xd0,0x47,0x68, +0x47,0x45,0xf7,0xd0,0x22,0x60,0x02,0xf1,0x04,0x02,0x57,0xf8,0x04,0x8b,0xc4,0xf8, +0x30,0x80,0xa5,0x68,0x45,0xf0,0x01,0x05,0xa5,0x60,0xd4,0xf8,0x08,0x80,0x18,0xf0, +0x01,0x0f,0xfa,0xd1,0x8f,0x42,0x28,0xbf,0x00,0xf1,0x08,0x07,0x47,0x60,0x01,0x3b, +0x03,0xb1,0xdf,0xe7,0x00,0xbe,0x00,0xbf,0x00,0x00,0x00,0x40, diff --git a/contrib/loaders/flash/max32xxx/max32xxx.s b/contrib/loaders/flash/max32xxx/max32xxx.s new file mode 100644 index 0000000000..f5306d6c5d --- /dev/null +++ b/contrib/loaders/flash/max32xxx/max32xxx.s @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2016 by Maxim Integrated * + * Kevin Gillespie . * + ***************************************************************************/ + +.text +.syntax unified +.cpu cortex-m3 +.thumb +.thumb_func + +/* + * Params : + * r0 = workarea start + * r1 = workarea end + * r2 = target address + * r3 = count (32bit words) + * r4 = pFLASH_CTRL_BASE + * + * Clobbered: + * r5 = FLASHWRITECMD + * r7 - rp + * r8 - wp, tmp + */ + +write: + +wait_fifo: +ldr r8, [r0, #0] /* read wp */ +cmp r8, #0 /* abort if wp == 0 */ +beq exit +ldr r7, [r0, #4] /* read rp */ +cmp r7, r8 /* wait until rp != wp */ +beq wait_fifo + +mainloop: +str r2, [r4, #0x00] /* FLSH_ADDR - write address */ +add r2, r2, #4 /* increment target address */ +ldr r8, [r7], #4 +str r8, [r4, #0x30] /* FLSH_DATA0 - write data */ +ldr r5, [r4, #0x08] /* FLSH_CN */ +orr r5, r5, #1 +str r5, [r4, #0x08] /* FLSH_CN - enable write */ +busy: +ldr r8, [r4, #0x08] /* FLSH_CN */ +tst r8, #1 +bne busy + +cmp r7, r1 /* wrap rp at end of buffer */ +it cs +addcs r7, r0, #8 /* skip loader args */ +str r7, [r0, #4] /* store rp */ +subs r3, r3, #1 /* decrement word count */ +cbz r3, exit /* loop if not done */ +b wait_fifo +exit: +bkpt diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 13e589cd02..3839d0a8dd 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -35,6 +35,7 @@ NOR_DRIVERS = \ %D%/lpc288x.c \ %D%/lpc2900.c \ %D%/lpcspifi.c \ + %D%/max32xxx.c \ %D%/mdr.c \ %D%/msp432.c \ %D%/mrvlqspi.c \ diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 196717f462..2251b965a6 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -48,6 +48,7 @@ extern struct flash_driver lpc2000_flash; extern struct flash_driver lpc288x_flash; extern struct flash_driver lpc2900_flash; extern struct flash_driver lpcspifi_flash; +extern struct flash_driver max32xxx_flash; extern struct flash_driver mdr_flash; extern struct flash_driver mrvlqspi_flash; extern struct flash_driver msp432_flash; @@ -112,6 +113,7 @@ static struct flash_driver *flash_drivers[] = { &lpc288x_flash, &lpc2900_flash, &lpcspifi_flash, + &max32xxx_flash, &mdr_flash, &mrvlqspi_flash, &msp432_flash, diff --git a/src/flash/nor/max32xxx.c b/src/flash/nor/max32xxx.c new file mode 100644 index 0000000000..ca037de4d5 --- /dev/null +++ b/src/flash/nor/max32xxx.c @@ -0,0 +1,997 @@ +/*************************************************************************** + * Copyright (C) 2016 by Maxim Integrated * + * Kevin Gillespie * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include +#include + +/* Register Addresses */ +#define FLSH_ADDR 0x000 +#define FLSH_CLKDIV 0x004 +#define FLSH_CN 0x008 +#define PR1E_ADDR 0x00C +#define PR2S_ADDR 0x010 +#define PR2E_ADDR 0x014 +#define PR3S_ADDR 0x018 +#define PR3E_ADDR 0x01C +#define FLSH_MD 0x020 +#define FLSH_INT 0x024 +#define FLSH_DATA0 0x030 +#define FLSH_DATA1 0x034 +#define FLSH_DATA2 0x038 +#define FLSH_DATA3 0x03C +#define FLSH_BL_CTRL 0x170 +#define FLSH_PROT 0x300 + +#define ARM_PID_REG 0xE00FFFE0 +#define MAX326XX_ID_REG 0x40000838 + +/* Register settings */ +#define FLSH_INT_AF 0x00000002 + +#define FLSH_CN_UNLOCK_MASK 0xF0000000 +#define FLSH_CN_UNLOCK_VALUE 0x20000000 + +#define FLSH_CN_PEND 0x01000000 + +#define FLSH_CN_ERASE_CODE_MASK 0x0000FF00 +#define FLSH_CN_ERASE_CODE_PGE 0x00005500 +#define FLSH_CN_ERASE_CODE_ME 0x0000AA00 + +#define FLSH_CN_PGE 0x00000004 +#define FLSH_CN_ME 0x00000002 +#define FLSH_CN_WR 0x00000001 +#define FLASH_BL_CTRL_23 0x00020000 +#define FLASH_BL_CTRL_IFREN 0x00000001 + +#define ARM_PID_DEFAULT_CM3 0xB4C3 +#define ARM_PID_DEFAULT_CM4 0xB4C4 +#define MAX326XX_ID 0x4D + +static int max32xxx_mass_erase(struct flash_bank *bank); + +struct max32xxx_flash_bank { + int probed; + int max326xx; + unsigned int flash_size; + unsigned int flc_base; + unsigned int sector_size; + unsigned int clkdiv_value; + unsigned int int_state; + unsigned int burst_size_bits; +}; + +/* see contib/loaders/flash/max32xxx/max32xxx.s for src */ +static const uint8_t write_code[] = { +#include "../../contrib/loaders/flash/max32xxx/max32xxx.inc" +}; + +/* Config Command: flash bank name driver base size chip_width bus_width target [driver_option] + flash bank max32xxx 0 0 [burst_bits] + */ +FLASH_BANK_COMMAND_HANDLER(max32xxx_flash_bank_command) +{ + struct max32xxx_flash_bank *info; + + if (CMD_ARGC < 9) { + LOG_WARNING("incomplete flash bank max32xxx configuration: 0 0 [burst_bits]"); + return ERROR_FLASH_BANK_INVALID; + } + + info = calloc(sizeof(struct max32xxx_flash_bank), 1); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], info->flash_size); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], info->flc_base); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[7], info->sector_size); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[8], info->clkdiv_value); + + if (CMD_ARGC > 9) + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[9], info->burst_size_bits); + else + info->burst_size_bits = 32; + + info->int_state = 0; + bank->driver_priv = info; + return ERROR_OK; +} + +static int get_info(struct flash_bank *bank, char *buf, int buf_size) +{ + int printed; + struct max32xxx_flash_bank *info = bank->driver_priv; + + if (info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + printed = snprintf(buf, buf_size, "\nMaxim Integrated max32xxx flash driver\n"); + buf += printed; + buf_size -= printed; + return ERROR_OK; +} + +/*************************************************************************** +* flash operations +***************************************************************************/ + +static int max32xxx_flash_op_pre(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct max32xxx_flash_bank *info = bank->driver_priv; + uint32_t flsh_cn; + uint32_t bootloader; + + /* Check if the flash controller is busy */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + if (flsh_cn & (FLSH_CN_PEND | FLSH_CN_ERASE_CODE_MASK | FLSH_CN_PGE | + FLSH_CN_ME | FLSH_CN_WR)) + return ERROR_FLASH_BUSY; + + /* Refresh flash controller timing */ + target_write_u32(target, info->flc_base + FLSH_CLKDIV, info->clkdiv_value); + + /* Clear and disable flash programming interrupts */ + target_read_u32(target, info->flc_base + FLSH_INT, &info->int_state); + target_write_u32(target, info->flc_base + FLSH_INT, 0x00000000); + + /* Clear the lower bit in the bootloader configuration register in case flash page 0 has been replaced */ + if (target_read_u32(target, info->flc_base + FLSH_BL_CTRL, &bootloader) != ERROR_OK) { + LOG_ERROR("Read failure on FLSH_BL_CTRL"); + return ERROR_FAIL; + } + if (bootloader & FLASH_BL_CTRL_23) { + LOG_WARNING("FLSH_BL_CTRL indicates BL mode 2 or mode 3."); + if (bootloader & FLASH_BL_CTRL_IFREN) { + LOG_WARNING("Flash page 0 swapped out, attempting to swap back in for programming"); + bootloader &= ~(FLASH_BL_CTRL_IFREN); + if (target_write_u32(target, info->flc_base + FLSH_BL_CTRL, bootloader) != ERROR_OK) { + LOG_ERROR("Write failure on FLSH_BL_CTRL"); + return ERROR_FAIL; + } + if (target_read_u32(target, info->flc_base + FLSH_BL_CTRL, &bootloader) != ERROR_OK) { + LOG_ERROR("Read failure on FLSH_BL_CTRL"); + return ERROR_FAIL; + } + if (bootloader & FLASH_BL_CTRL_IFREN) { + /* Bummer */ + LOG_ERROR("Unable to swap flash page 0 back in. Writes to page 0 will fail."); + } + } + } + + /* Unlock flash */ + flsh_cn &= ~FLSH_CN_UNLOCK_MASK; + flsh_cn |= FLSH_CN_UNLOCK_VALUE; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + /* Confirm flash is unlocked */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + if ((flsh_cn & FLSH_CN_UNLOCK_VALUE) != FLSH_CN_UNLOCK_VALUE) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int max32xxx_flash_op_post(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct max32xxx_flash_bank *info = bank->driver_priv; + uint32_t flsh_cn; + + /* Restore flash programming interrupts */ + target_write_u32(target, info->flc_base + FLSH_INT, info->int_state); + + /* Lock flash */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn &= ~FLSH_CN_UNLOCK_MASK; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + return ERROR_OK; +} + +static int max32xxx_protect_check(struct flash_bank *bank) +{ + struct max32xxx_flash_bank *info = bank->driver_priv; + struct target *target = bank->target; + int i; + uint32_t temp_reg; + + if (info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + if (!info->max326xx) { + for (i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_protected = -1; + + return ERROR_FLASH_OPER_UNSUPPORTED; + } + + /* Check the protection */ + for (i = 0; i < bank->num_sectors; i++) { + if (i%32 == 0) + target_read_u32(target, info->flc_base + FLSH_PROT + ((i/32)*4), &temp_reg); + + if (temp_reg & (0x1 << i%32)) + bank->sectors[i].is_protected = 1; + else + bank->sectors[i].is_protected = 0; + } + return ERROR_OK; +} + +static int max32xxx_erase(struct flash_bank *bank, int first, int last) +{ + int banknr; + uint32_t flsh_cn, flsh_int; + struct max32xxx_flash_bank *info = bank->driver_priv; + struct target *target = bank->target; + int retval; + int retry; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + if ((first < 0) || (last < first) || (last >= bank->num_sectors)) + return ERROR_FLASH_SECTOR_INVALID; + + if ((first == 0) && (last == (bank->num_sectors - 1))) + return max32xxx_mass_erase(bank); + + /* Prepare to issue flash operation */ + retval = max32xxx_flash_op_pre(bank); + + if (retval != ERROR_OK) + return retval; + + int erased = 0; + for (banknr = first; banknr <= last; banknr++) { + + /* Check the protection */ + if (bank->sectors[banknr].is_protected == 1) { + LOG_WARNING("Flash sector %d is protected", banknr); + continue; + } else + erased = 1; + + /* Address is first word in page */ + target_write_u32(target, info->flc_base + FLSH_ADDR, banknr * info->sector_size); + + /* Write page erase code */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn |= FLSH_CN_ERASE_CODE_PGE; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + /* Issue page erase command */ + flsh_cn |= 0x4; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + /* Wait until erase complete */ + retry = 1000; + do { + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); + + if (retry <= 0) { + LOG_ERROR("Timed out waiting for flash page erase @ 0x%08x", + banknr * info->sector_size); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Check access violations */ + target_read_u32(target, info->flc_base + FLSH_INT, &flsh_int); + if (flsh_int & FLSH_INT_AF) { + LOG_ERROR("Error erasing flash page %i", banknr); + target_write_u32(target, info->flc_base + FLSH_INT, 0); + max32xxx_flash_op_post(bank); + return ERROR_FLASH_OPERATION_FAILED; + } + + bank->sectors[banknr].is_erased = 1; + } + + if (!erased) { + LOG_ERROR("All pages protected %d to %d", first, last); + max32xxx_flash_op_post(bank); + return ERROR_FAIL; + } + + if (max32xxx_flash_op_post(bank) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int max32xxx_protect(struct flash_bank *bank, int set, int first, int last) +{ + struct max32xxx_flash_bank *info = bank->driver_priv; + struct target *target = bank->target; + int page; + uint32_t temp_reg; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + if (!info->max326xx) + return ERROR_FLASH_OPER_UNSUPPORTED; + + if ((first < 0) || (last < first) || (last >= bank->num_sectors)) + return ERROR_FLASH_SECTOR_INVALID; + + /* Setup the protection on the pages given */ + for (page = first; page <= last; page++) { + if (set) { + /* Set the write/erase bit for this page */ + target_read_u32(target, info->flc_base + FLSH_PROT + (page/32), &temp_reg); + temp_reg |= (0x1 << page%32); + target_write_u32(target, info->flc_base + FLSH_PROT + (page/32), temp_reg); + bank->sectors[page].is_protected = 1; + } else { + /* Clear the write/erase bit for this page */ + target_read_u32(target, info->flc_base + FLSH_PROT + (page/32), &temp_reg); + temp_reg &= ~(0x1 << page%32); + target_write_u32(target, info->flc_base + FLSH_PROT + (page/32), temp_reg); + bank->sectors[page].is_protected = 0; + } + } + + return ERROR_OK; +} + +static int max32xxx_write_block(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t wcount) +{ + struct max32xxx_flash_bank *info = bank->driver_priv; + struct target *target = bank->target; + uint32_t buffer_size = 16384; + struct working_area *source; + struct working_area *write_algorithm; + uint32_t address = bank->base + offset; + struct reg_param reg_params[5]; + struct armv7m_algorithm armv7m_info; + int retval = ERROR_OK; + /* power of two, and multiple of word size */ + static const unsigned buf_min = 128; + + /* for small buffers it's faster not to download an algorithm */ + if (wcount * 4 < buf_min) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + + LOG_DEBUG("(bank=%p buffer=%p offset=%08" PRIx32 " wcount=%08" PRIx32 "", + bank, buffer, offset, wcount); + + /* flash write code */ + if (target_alloc_working_area(target, sizeof(write_code), &write_algorithm) != ERROR_OK) { + LOG_DEBUG("no working area for block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + /* plus a buffer big enough for this data */ + if (wcount * 4 < buffer_size) + buffer_size = wcount * 4; + + /* memory buffer */ + while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) { + buffer_size /= 2; + + if (buffer_size <= buf_min) { + target_free_working_area(target, write_algorithm); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + LOG_DEBUG("retry target_alloc_working_area(%s, size=%u)", + target_name(target), (unsigned) buffer_size); + } + + target_write_buffer(target, write_algorithm->address, sizeof(write_code), + write_code); + + armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_info.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); + init_reg_param(®_params[4], "r4", 32, PARAM_OUT); + + buf_set_u32(reg_params[0].value, 0, 32, source->address); + buf_set_u32(reg_params[1].value, 0, 32, source->address + source->size); + buf_set_u32(reg_params[2].value, 0, 32, address); + buf_set_u32(reg_params[3].value, 0, 32, wcount); + buf_set_u32(reg_params[4].value, 0, 32, info->flc_base); + retval = target_run_flash_async_algorithm(target, buffer, wcount, 4, 0, NULL, + 5, reg_params, source->address, source->size, write_algorithm->address, 0, &armv7m_info); + + if (retval == ERROR_FLASH_OPERATION_FAILED) + LOG_ERROR("error %d executing max32xxx flash write algorithm", retval); + + target_free_working_area(target, write_algorithm); + target_free_working_area(target, source); + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + destroy_reg_param(®_params[3]); + destroy_reg_param(®_params[4]); + return retval; +} + +static int max32xxx_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct max32xxx_flash_bank *info = bank->driver_priv; + struct target *target = bank->target; + uint32_t flsh_cn, flsh_int; + uint32_t address = offset; + uint32_t remaining = count; + uint32_t words_remaining; + int retval; + int retry; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + LOG_DEBUG("bank=%p buffer=%p offset=%08" PRIx32 " count=%08" PRIx32 "", + bank, buffer, offset, count); + + if (info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + if (offset & 0x3) { + LOG_WARNING("offset size must be word aligned"); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + if (offset + count > bank->size) + return ERROR_FLASH_DST_OUT_OF_BANK; + + /* Prepare to issue flash operation */ + retval = max32xxx_flash_op_pre(bank); + + if (retval != ERROR_OK) + return retval; + + if (remaining >= 4) { + /* write in 32-bit units */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn &= 0xF7FFFFFF; + flsh_cn |= 0x00000010; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + /* try using a block write */ + words_remaining = remaining / 4; + retval = max32xxx_write_block(bank, buffer, offset, words_remaining); + + if (retval != ERROR_OK) { + if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + LOG_DEBUG("writing flash word-at-a-time"); + else { + max32xxx_flash_op_post(bank); + return ERROR_FLASH_OPERATION_FAILED; + } + } else { + /* all 32-bit words have been written */ + buffer += words_remaining * 4; + address += words_remaining * 4; + remaining -= words_remaining * 4; + } + } + + if ((remaining >= 4) && ((address & 0x1F) != 0)) { + /* write in 32-bit units until we are 128-bit aligned */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn &= 0xF7FFFFFF; + flsh_cn |= 0x00000010; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + while ((remaining >= 4) && ((address & 0x1F) != 0)) { + target_write_u32(target, info->flc_base + FLSH_ADDR, address); + target_write_buffer(target, info->flc_base + FLSH_DATA0, 4, buffer); + flsh_cn |= 0x00000001; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + /* Wait until flash operation is complete */ + retry = 10; + + do { + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); + + if (retry <= 0) { + LOG_ERROR("Timed out waiting for flash write @ 0x%08x", address); + return ERROR_FLASH_OPERATION_FAILED; + } + + buffer += 4; + address += 4; + remaining -= 4; + } + } + + if ((info->burst_size_bits == 128) && (remaining >= 16)) { + /* write in 128-bit bursts while we can */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + + flsh_cn &= 0xFFFFFFEF; + flsh_cn |= 0x08000000; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + target_write_u32(target, info->flc_base + FLSH_ADDR, address); + + while (remaining >= 16) { + if ((address & 0xFFF) == 0) + LOG_DEBUG("Writing @ 0x%08x", address); + + target_write_buffer(target, info->flc_base + FLSH_DATA0, 16, buffer); + flsh_cn |= 0x00000001; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + /* Wait until flash operation is complete */ + retry = 10; + + do { + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); + + if (retry <= 0) { + LOG_ERROR("Timed out waiting for flash write @ 0x%08x", address); + return ERROR_FLASH_OPERATION_FAILED; + } + + buffer += 16; + address += 16; + remaining -= 16; + } + } + + if (remaining >= 4) { + + /* write in 32-bit units while we can */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn &= 0xF7FFFFFF; + flsh_cn |= 0x00000010; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + while (remaining >= 4) { + target_write_u32(target, info->flc_base + FLSH_ADDR, address); + target_write_buffer(target, info->flc_base + FLSH_DATA0, 4, buffer); + flsh_cn |= 0x00000001; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + /* Wait until flash operation is complete */ + retry = 10; + + do { + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); + + if (retry <= 0) { + LOG_ERROR("Timed out waiting for flash write @ 0x%08x", address); + return ERROR_FLASH_OPERATION_FAILED; + } + + buffer += 4; + address += 4; + remaining -= 4; + } + } + + if (remaining > 0) { + /* write remaining bytes in a 32-bit unit */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn &= 0xF7FFFFFF; + flsh_cn |= 0x00000010; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + uint8_t last_word[4] = {0xff, 0xff, 0xff, 0xff}; + int i = 0; + + while (remaining > 0) { + last_word[i++] = *buffer; + buffer++; + remaining--; + } + + target_write_u32(target, info->flc_base + FLSH_ADDR, address); + target_write_buffer(target, info->flc_base + FLSH_DATA0, 4, last_word); + flsh_cn |= 0x00000001; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + /* Wait until flash operation is complete */ + retry = 10; + + do { + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); + + if (retry <= 0) { + LOG_ERROR("Timed out waiting for flash write @ 0x%08x", address); + return ERROR_FLASH_OPERATION_FAILED; + } + } + + /* Check access violations */ + target_read_u32(target, info->flc_base + FLSH_INT, &flsh_int); + if (flsh_int & FLSH_INT_AF) { + LOG_ERROR("Flash Error writing 0x%x bytes at 0x%08x", count, offset); + max32xxx_flash_op_post(bank); + return ERROR_FLASH_OPERATION_FAILED; + } + + if (max32xxx_flash_op_post(bank) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int max32xxx_probe(struct flash_bank *bank) +{ + struct max32xxx_flash_bank *info = bank->driver_priv; + struct target *target = bank->target; + uint32_t arm_id[2]; + uint16_t arm_pid; + + if (bank->sectors) { + free(bank->sectors); + bank->sectors = NULL; + } + + /* provide this for the benefit of the NOR flash framework */ + bank->size = info->flash_size; + bank->num_sectors = info->flash_size / info->sector_size; + bank->sectors = calloc(bank->num_sectors, sizeof(struct flash_sector)); + + for (int i = 0; i < bank->num_sectors; i++) { + bank->sectors[i].offset = i * info->sector_size; + bank->sectors[i].size = info->sector_size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = -1; + } + + /* Probe to determine if this part is in the max326xx family */ + info->max326xx = 0; + target_read_u32(target, ARM_PID_REG, &arm_id[0]); + target_read_u32(target, ARM_PID_REG+4, &arm_id[1]); + arm_pid = (arm_id[1] << 8) + arm_id[0]; + LOG_DEBUG("arm_pid = 0x%x", arm_pid); + + if ((arm_pid == ARM_PID_DEFAULT_CM3) || arm_pid == ARM_PID_DEFAULT_CM4) { + uint32_t max326xx_id; + target_read_u32(target, MAX326XX_ID_REG, &max326xx_id); + LOG_DEBUG("max326xx_id = 0x%x", max326xx_id); + max326xx_id = ((max326xx_id & 0xFF000000) >> 24); + if (max326xx_id == MAX326XX_ID) + info->max326xx = 1; + } + LOG_DEBUG("info->max326xx = %d", info->max326xx); + + /* Initialize the protection bits for each flash page */ + if (max32xxx_protect_check(bank) == ERROR_FLASH_OPER_UNSUPPORTED) + LOG_WARNING("Flash protection not supported on this device"); + + info->probed = 1; + return ERROR_OK; +} + +static int max32xxx_mass_erase(struct flash_bank *bank) +{ + struct target *target = NULL; + struct max32xxx_flash_bank *info = NULL; + uint32_t flsh_cn, flsh_int; + int retval; + int retry; + info = bank->driver_priv; + target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + int not_protected = 0; + for (int i = 0; i < bank->num_sectors; i++) { + if (bank->sectors[i].is_protected == 1) + LOG_WARNING("Flash sector %d is protected", i); + else + not_protected = 1; + } + + if (!not_protected) { + LOG_ERROR("All pages protected"); + return ERROR_FAIL; + } + + /* Prepare to issue flash operation */ + retval = max32xxx_flash_op_pre(bank); + + if (retval != ERROR_OK) + return retval; + + /* Write mass erase code */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn |= FLSH_CN_ERASE_CODE_ME; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + /* Issue mass erase command */ + flsh_cn |= 0x2; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + /* Wait until erase complete */ + retry = 1000; + do { + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); + + if (retry <= 0) { + LOG_ERROR("Timed out waiting for flash mass erase"); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Check access violations */ + target_read_u32(target, info->flc_base + FLSH_INT, &flsh_int); + if (flsh_int & FLSH_INT_AF) { + LOG_ERROR("Error mass erasing"); + target_write_u32(target, info->flc_base + FLSH_INT, 0); + return ERROR_FLASH_OPERATION_FAILED; + } + + if (max32xxx_flash_op_post(bank) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +COMMAND_HANDLER(max32xxx_handle_mass_erase_command) +{ + int i; + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + + if (CMD_ARGC < 1) { + command_print(CMD_CTX, "max32xxx mass_erase "); + return ERROR_OK; + } + + if (ERROR_OK != retval) + return retval; + + if (max32xxx_mass_erase(bank) == ERROR_OK) { + /* set all sectors as erased */ + for (i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_erased = 1; + + command_print(CMD_CTX, "max32xxx mass erase complete"); + } else + command_print(CMD_CTX, "max32xxx mass erase failed"); + + return ERROR_OK; +} + +COMMAND_HANDLER(max32xxx_handle_protection_set_command) +{ + struct flash_bank *bank; + int retval; + struct max32xxx_flash_bank *info; + uint32_t addr, len; + + if (CMD_ARGC != 3) { + command_print(CMD_CTX, "max32xxx protection_set "); + return ERROR_OK; + } + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + info = bank->driver_priv; + + /* Convert the range to the page numbers */ + if (1 != sscanf(CMD_ARGV[1], "0x%"SCNx32, &addr)) { + LOG_WARNING("Error parsing address"); + command_print(CMD_CTX, "max32xxx protection_set "); + return ERROR_FAIL; + } + /* Mask off the top portion on the address */ + addr = (addr & 0x0FFFFFFF); + + if (1 != sscanf(CMD_ARGV[2], "0x%"SCNx32, &len)) { + LOG_WARNING("Error parsing length"); + command_print(CMD_CTX, "max32xxx protection_set "); + return ERROR_FAIL; + } + + /* Check the address is in the range of the flash */ + if ((addr+len) >= info->flash_size) + return ERROR_FLASH_SECTOR_INVALID; + + if (len == 0) + return ERROR_OK; + + /* Convert the address and length to the page boundaries */ + addr = addr - (addr % info->sector_size); + if (len % info->sector_size) + len = len + info->sector_size - (len % info->sector_size); + + /* Convert the address and length to page numbers */ + addr = (addr / info->sector_size); + len = addr + (len / info->sector_size) - 1; + + if (max32xxx_protect(bank, 1, addr, len) == ERROR_OK) + command_print(CMD_CTX, "max32xxx protection set complete"); + else + command_print(CMD_CTX, "max32xxx protection set failed"); + + return ERROR_OK; +} + +COMMAND_HANDLER(max32xxx_handle_protection_clr_command) +{ + struct flash_bank *bank; + int retval; + struct max32xxx_flash_bank *info; + uint32_t addr, len; + + if (CMD_ARGC != 3) { + command_print(CMD_CTX, "max32xxx protection_clr "); + return ERROR_OK; + } + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + info = bank->driver_priv; + + /* Convert the range to the page numbers */ + if (1 != sscanf(CMD_ARGV[1], "0x%"SCNx32, &addr)) { + LOG_WARNING("Error parsing address"); + command_print(CMD_CTX, "max32xxx protection_clr "); + return ERROR_FAIL; + } + /* Mask off the top portion on the address */ + addr = (addr & 0x0FFFFFFF); + + if (1 != sscanf(CMD_ARGV[2], "0x%"SCNx32, &len)) { + LOG_WARNING("Error parsing length"); + command_print(CMD_CTX, "max32xxx protection_clr "); + return ERROR_FAIL; + } + + /* Check the address is in the range of the flash */ + if ((addr+len) >= info->flash_size) + return ERROR_FLASH_SECTOR_INVALID; + + if (len == 0) + return ERROR_OK; + + /* Convert the address and length to the page boundaries */ + addr = addr - (addr % info->sector_size); + if (len % info->sector_size) + len = len + info->sector_size - (len % info->sector_size); + + /* Convert the address and length to page numbers */ + addr = (addr / info->sector_size); + len = addr + (len / info->sector_size) - 1; + + if (max32xxx_protect(bank, 0, addr, len) == ERROR_OK) + command_print(CMD_CTX, "max32xxx protection clear complete"); + else + command_print(CMD_CTX, "max32xxx protection clear failed"); + + return ERROR_OK; +} + +COMMAND_HANDLER(max32xxx_handle_protection_check_command) +{ + struct flash_bank *bank; + int retval; + struct max32xxx_flash_bank *info; + int i; + + if (CMD_ARGC < 1) { + command_print(CMD_CTX, "max32xxx protection_check "); + return ERROR_OK; + } + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + info = bank->driver_priv; + + /* Update the protection array */ + retval = max32xxx_protect_check(bank); + if (ERROR_OK != retval) { + LOG_WARNING("Error updating the protection array"); + return retval; + } + + LOG_WARNING("s: a:
p:"); + for (i = 0; i < bank->num_sectors; i += 4) { + LOG_WARNING("s:%03d a:0x%06x p:%d | s:%03d a:0x%06x p:%d | s:%03d a:0x%06x p:%d | s:%03d a:0x%06x p:%d", + (i+0), (i+0)*info->sector_size, bank->sectors[(i+0)].is_protected, + (i+1), (i+1)*info->sector_size, bank->sectors[(i+1)].is_protected, + (i+2), (i+2)*info->sector_size, bank->sectors[(i+2)].is_protected, + (i+3), (i+3)*info->sector_size, bank->sectors[(i+3)].is_protected); + } + + return ERROR_OK; +} + +static const struct command_registration max32xxx_exec_command_handlers[] = { + { + .name = "mass_erase", + .handler = max32xxx_handle_mass_erase_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "mass erase flash", + }, + { + .name = "protection_set", + .handler = max32xxx_handle_protection_set_command, + .mode = COMMAND_EXEC, + .usage = "bank_id addr size", + .help = "set flash protection for address range", + }, + { + .name = "protection_clr", + .handler = max32xxx_handle_protection_clr_command, + .mode = COMMAND_EXEC, + .usage = "bank_id addr size", + .help = "clear flash protection for address range", + }, + { + .name = "protection_check", + .handler = max32xxx_handle_protection_check_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "check flash protection", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration max32xxx_command_handlers[] = { + { + .name = "max32xxx", + .mode = COMMAND_EXEC, + .help = "max32xxx flash command group", + .chain = max32xxx_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver max32xxx_flash = { + .name = "max32xxx", + .commands = max32xxx_command_handlers, + .flash_bank_command = max32xxx_flash_bank_command, + .erase = max32xxx_erase, + .protect = max32xxx_protect, + .write = max32xxx_write, + .read = default_flash_read, + .probe = max32xxx_probe, + .auto_probe = max32xxx_probe, + .erase_check = default_flash_blank_check, + .protect_check = max32xxx_protect_check, + .info = get_info, +}; diff --git a/tcl/target/max32620.cfg b/tcl/target/max32620.cfg new file mode 100644 index 0000000000..80cb25a475 --- /dev/null +++ b/tcl/target/max32620.cfg @@ -0,0 +1,28 @@ +# Maxim Integrated MAX32620 OpenOCD target configuration file +# www.maximintegrated.com + +# adapter speed +adapter_khz 4000 + +# reset pin configuration +reset_config srst_only + +if {[using_jtag]} { + jtag newtap max32620 cpu -irlen 4 -irmask 0xf -expected-id 0x4ba00477 -ignore-version + jtag newtap maxtest tap -irlen 4 -irmask 0xf -ircapture 0x1 -ignore-version +} else { + swd newdap max32620 cpu -irlen 4 -irmask 0xf -expected-id 0x2ba01477 -ignore-version +} + +# target configuration +target create max32620.cpu cortex_m -chain-position max32620.cpu +max32620.cpu configure -work-area-phys 0x20005000 -work-area-size 0x2000 + +# Config Command: flash bank name driver base size chip_width bus_width target [driver_options] +# flash bank max32xxx 0 0 +# max32620 flash base address 0x00000000 +# max32620 flash size 0x200000 (2MB) +# max32620 FLC base address 0x40002000 +# max32620 sector (page) size 0x2000 (8kB) +# max32620 clock speed 96 (MHz) +flash bank max32620.flash max32xxx 0x00000000 0x200000 0 0 max32620.cpu 0x40002000 0x2000 96 diff --git a/tcl/target/max32625.cfg b/tcl/target/max32625.cfg new file mode 100644 index 0000000000..7182b235fb --- /dev/null +++ b/tcl/target/max32625.cfg @@ -0,0 +1,28 @@ +# Maxim Integrated MAX32625 OpenOCD target configuration file +# www.maximintegrated.com + +# adapter speed +adapter_khz 4000 + +# reset pin configuration +reset_config srst_only + +if {[using_jtag]} { + jtag newtap max32625 cpu -irlen 4 -irmask 0xf -expected-id 0x4ba00477 -ignore-version + jtag newtap maxtest tap -irlen 4 -irmask 0xf -ircapture 0x1 -expected-id 0x07f71197 -ignore-version +} else { + swd newdap max32625 cpu -irlen 4 -irmask 0xf -expected-id 0x2ba01477 -ignore-version +} + +# target configuration +target create max32625.cpu cortex_m -chain-position max32625.cpu +max32625.cpu configure -work-area-phys 0x20005000 -work-area-size 0x2000 + +# Config Command: flash bank name driver base size chip_width bus_width target [driver_options] +# flash bank max32xxx 0 0 +# max32625 flash base address 0x00000000 +# max32625 flash size 0x80000 (512k) +# max32625 FLC base address 0x40002000 +# max32625 sector (page) size 0x2000 (8kB) +# max32625 clock speed 96 (MHz) +flash bank max32625.flash max32xxx 0x00000000 0x80000 0 0 max32625.cpu 0x40002000 0x2000 96 diff --git a/tcl/target/max3263x.cfg b/tcl/target/max3263x.cfg new file mode 100644 index 0000000000..f23b0b64da --- /dev/null +++ b/tcl/target/max3263x.cfg @@ -0,0 +1,28 @@ +# Maxim Integrated MAX3263X OpenOCD target configuration file +# www.maximintegrated.com + +# adapter speed +adapter_khz 4000 + +# reset pin configuration +reset_config srst_only + +if {[using_jtag]} { + jtag newtap max3263x cpu -irlen 4 -irmask 0xf -expected-id 0x4ba00477 -ignore-version + jtag newtap maxtest tap -irlen 4 -irmask 0xf -ircapture 0x1 -expected-id 0x07f76197 -ignore-version +} else { + swd newdap max3263x cpu -irlen 4 -irmask 0xf -expected-id 0x2ba01477 -ignore-version +} + +# target configuration +target create max3263x.cpu cortex_m -chain-position max3263x.cpu +max3263x.cpu configure -work-area-phys 0x20005000 -work-area-size 0x2000 + +# Config Command: flash bank name driver base size chip_width bus_width target [driver_options] +# flash bank max32xxx 0 0 +# max3263x flash base address 0x00000000 +# max3263x flash size 0x200000 (2MB) +# max3263x FLC base address 0x40002000 +# max3263x sector (page) size 0x2000 (8kB) +# max3263x clock speed 96 (MHz) +flash bank max3263x.flash max32xxx 0x00000000 0x200000 0 0 max3263x.cpu 0x40002000 0x2000 96 -- 2.30.2