From: Michele Sardo Date: Tue, 12 Sep 2017 06:51:29 +0000 (+0200) Subject: Added support for STMicroelectronics BlueNRG-1 and BlueNRG-2 SoC X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=cb2f21bf3608f24de5c2e4219626cc464269e830;p=openocd Added support for STMicroelectronics BlueNRG-1 and BlueNRG-2 SoC Added configuration files and flash loaders. Change-Id: I768eb3626f4e0eadb206bef90a867cc146fe8c75 Signed-off-by: Michele Sardo Reviewed-on: http://openocd.zylin.com/4226 Tested-by: jenkins Reviewed-by: Tomas Vanek --- diff --git a/contrib/loaders/flash/bluenrg-x/Makefile b/contrib/loaders/flash/bluenrg-x/Makefile new file mode 100644 index 00000000..1a5cfc01 --- /dev/null +++ b/contrib/loaders/flash/bluenrg-x/Makefile @@ -0,0 +1,27 @@ +BIN2C = ../../../../src/helper/bin2char.sh + +CROSS_COMPILE ?= arm-none-eabi- + +CC=$(CROSS_COMPILE)gcc +OBJCOPY=$(CROSS_COMPILE)objcopy +OBJDUMP=$(CROSS_COMPILE)objdump + +CFLAGS = -c -mthumb -mcpu=cortex-m0 -O3 -g + +all: bluenrg-x_write.inc + +.PHONY: clean + +.INTERMEDIATE: bluenrg-x_write.o + +%.o: %.c + $(CC) $(CFLAGS) -Wall -Wextra -Wa,-adhln=$*.lst $< -o $@ + +%.bin: %.o + $(OBJCOPY) -Obinary $< $@ + +%.inc: %.bin + $(BIN2C) < $< > $@ + +clean: + -rm -f *.o *.lst *.bin *.inc diff --git a/contrib/loaders/flash/bluenrg-x/bluenrg-x_write.c b/contrib/loaders/flash/bluenrg-x/bluenrg-x_write.c new file mode 100644 index 00000000..3dd17b2f --- /dev/null +++ b/contrib/loaders/flash/bluenrg-x/bluenrg-x_write.c @@ -0,0 +1,132 @@ +/* To be built with arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m0 -O3 bluenrgx.c */ +/* Then postprocess output of command "arm-none-eabi-objdump -d bluenrgx.o" to make a C array of bytes */ + +#include + +/* Status Values ----------------------------------------------------------*/ +#define SUCCESS 0 +#define ERR_UNALIGNED 1 +#define ERR_INVALID_ADDRESS 2 +#define ERR_INVALID_TYPE 3 +#define ERR_WRITE_PROTECTED 4 +#define ERR_WRITE_FAILED 5 +#define ERR_ERASE_REQUIRED 6 +#define ERR_VERIFY_FAILED 7 + +/* Flash Controller defines ---------------------------------------------------*/ +#define FLASH_REG_COMMAND ((volatile uint32_t *)0x40100000) +#define FLASH_REG_CONFIG ((volatile uint32_t *)0x40100004) +#define FLASH_REG_IRQSTAT ((volatile uint32_t *)0x40100008) +#define FLASH_REG_IRQMASK ((volatile uint32_t *)0x4010000C) +#define FLASH_REG_IRQRAW ((volatile uint32_t *)0x40100010) +#define FLASH_REG_ADDRESS ((volatile uint32_t *)0x40100018) +#define FLASH_REG_UNLOCKM ((volatile uint32_t *)0x4010001C) +#define FLASH_REG_UNLOCKL ((volatile uint32_t *)0x40100020) +#define FLASH_REG_DATA0 ((volatile uint32_t *)0x40100040) +#define FLASH_REG_DATA1 ((volatile uint32_t *)0x40100044) +#define FLASH_REG_DATA2 ((volatile uint32_t *)0x40100048) +#define FLASH_REG_DATA3 ((volatile uint32_t *)0x4010004C) +#define FLASH_SIZE_REG 0x40100014 + +#define MFB_MASS_ERASE 0x01 +#define MFB_PAGE_ERASE 0x02 + +#define DO_ERASE 0x0100 +#define DO_VERIFY 0x0200 +#define FLASH_CMD_ERASE_PAGE 0x11 +#define FLASH_CMD_MASSERASE 0x22 +#define FLASH_CMD_WRITE 0x33 +#define FLASH_CMD_BURSTWRITE 0xCC +#define FLASH_INT_CMDDONE 0x01 +#define MFB_BOTTOM (0x10040000) +#define MFB_SIZE_B ((16 * (((*(uint32_t *) FLASH_SIZE_REG) + 1) >> 12)) * 1024) +#define MFB_SIZE_W (MFB_SIZE_B/4) +#define MFB_TOP (MFB_BOTTOM+MFB_SIZE_B-1) +#define MFB_PAGE_SIZE_B (2048) +#define MFB_PAGE_SIZE_W (MFB_PAGE_SIZE_B/4) + +#define AREA_ERROR 0x01 +#define AREA_MFB 0x04 + +#define FLASH_WORD_LEN 4 + +typedef struct { + volatile uint8_t *wp; + uint8_t *rp; +} work_area_t; + +/* Flash Commands --------------------------------------------------------*/ +static inline __attribute__((always_inline)) uint32_t flashWrite(uint32_t address, uint8_t **data, + uint32_t writeLength) +{ + uint32_t index, flash_word[4]; + uint8_t i; + + *FLASH_REG_IRQMASK = 0; + for (index = 0; index < writeLength; index += (FLASH_WORD_LEN*4)) { + for (i = 0; i < 4; i++) + flash_word[i] = (*(uint32_t *) (*data + i*4)); + + /* Clear the IRQ flags */ + *FLASH_REG_IRQRAW = 0x0000003F; + /* Load the flash address to write */ + *FLASH_REG_ADDRESS = (uint16_t)((address + index) >> 2); + /* Prepare and load the data to flash */ + *FLASH_REG_DATA0 = flash_word[0]; + *FLASH_REG_DATA1 = flash_word[1]; + *FLASH_REG_DATA2 = flash_word[2]; + *FLASH_REG_DATA3 = flash_word[3]; + /* Flash write command */ + *FLASH_REG_COMMAND = FLASH_CMD_BURSTWRITE; + /* Wait the end of the flash write command */ + while ((*FLASH_REG_IRQRAW & FLASH_INT_CMDDONE) == 0) + ; + *data += (FLASH_WORD_LEN * 4); + } + + return SUCCESS; +} + +__attribute__((naked)) __attribute__((noreturn)) void write(uint8_t *work_area_p, + uint8_t *fifo_end, + uint8_t *target_address, + uint32_t count) +{ + uint32_t retval; + volatile work_area_t *work_area = (work_area_t *) work_area_p; + uint8_t *fifo_start = (uint8_t *) work_area->rp; + + while (count) { + volatile int32_t fifo_linear_size; + + /* Wait for some data in the FIFO */ + while (work_area->rp == work_area->wp) + ; + if (work_area->wp == 0) { + /* Aborted by other party */ + break; + } + if (work_area->rp > work_area->wp) { + fifo_linear_size = fifo_end-work_area->rp; + } else { + fifo_linear_size = (work_area->wp - work_area->rp); + if (fifo_linear_size < 0) + fifo_linear_size = 0; + } + if (fifo_linear_size < 16) { + /* We should never get here */ + continue; + } + + retval = flashWrite((uint32_t) target_address, (uint8_t **) &work_area->rp, fifo_linear_size); + if (retval != SUCCESS) { + work_area->rp = (uint8_t *)retval; + break; + } + target_address += fifo_linear_size; + if (work_area->rp >= fifo_end) + work_area->rp = fifo_start; + count -= fifo_linear_size; + } + __asm("bkpt 0"); +} diff --git a/contrib/loaders/flash/bluenrg-x/bluenrg-x_write.inc b/contrib/loaders/flash/bluenrg-x/bluenrg-x_write.inc new file mode 100644 index 00000000..47f33122 --- /dev/null +++ b/contrib/loaders/flash/bluenrg-x/bluenrg-x_write.inc @@ -0,0 +1,18 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0x05,0x93,0x43,0x68,0x05,0x00,0x07,0x93,0x05,0x9b,0x06,0x91,0x03,0x92,0x35,0x4c, +0x00,0x2b,0x5c,0xd0,0x6a,0x68,0x2b,0x68,0x9a,0x42,0xfb,0xd0,0x2b,0x68,0x00,0x2b, +0x55,0xd0,0x6a,0x68,0x2b,0x68,0x9a,0x42,0x52,0xd9,0x6b,0x68,0x06,0x9a,0xd3,0x1a, +0x09,0x93,0x09,0x9b,0x0f,0x2b,0xed,0xdd,0x00,0x21,0x09,0x9b,0x04,0x93,0x1a,0x1e, +0x29,0x4b,0x19,0x60,0x32,0xd0,0x29,0x4b,0x00,0x20,0x98,0x46,0x28,0x4b,0x6a,0x68, +0x9c,0x46,0x28,0x4b,0x28,0x4e,0x9b,0x46,0x28,0x4b,0x9a,0x46,0x28,0x4b,0x99,0x46, +0x01,0x23,0x51,0x68,0x17,0x68,0x00,0x91,0x91,0x68,0x01,0x91,0xd1,0x68,0x02,0x91, +0x3f,0x21,0x21,0x60,0x03,0x99,0x09,0x18,0x89,0x03,0x09,0x0c,0x31,0x60,0x41,0x46, +0x0f,0x60,0x67,0x46,0x00,0x99,0x39,0x60,0x5f,0x46,0x01,0x99,0x39,0x60,0x57,0x46, +0x02,0x99,0x39,0x60,0x49,0x46,0xcc,0x27,0x0f,0x60,0x21,0x68,0x0b,0x42,0xfc,0xd0, +0x04,0x99,0x10,0x32,0x10,0x30,0x6a,0x60,0x81,0x42,0xda,0xd8,0x03,0x9a,0x09,0x9b, +0x94,0x46,0x9c,0x44,0x63,0x46,0x06,0x9a,0x03,0x93,0x6b,0x68,0x9a,0x42,0x01,0xd8, +0x07,0x9b,0x6b,0x60,0x05,0x9a,0x09,0x9b,0xd3,0x1a,0x05,0x93,0xa2,0xd1,0x00,0xbe, +0x2b,0x68,0x6a,0x68,0x9b,0x1a,0x09,0x93,0x09,0x9b,0x00,0x2b,0xa9,0xda,0x00,0x23, +0x09,0x93,0xa6,0xe7,0x10,0x00,0x10,0x40,0x0c,0x00,0x10,0x40,0x40,0x00,0x10,0x40, +0x44,0x00,0x10,0x40,0x48,0x00,0x10,0x40,0x18,0x00,0x10,0x40,0x4c,0x00,0x10,0x40, +0x00,0x00,0x10,0x40, diff --git a/doc/openocd.texi b/doc/openocd.texi index 9126003b..2f01153f 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -5344,6 +5344,30 @@ The AVR 8-bit microcontrollers from Atmel integrate flash memory. @comment - defines mass_erase ... pointless given flash_erase_address @end deffn +@deffn {Flash Driver} bluenrg-x +STMicroelectronics BlueNRG-1 and BlueNRG-2 Bluetooth low energy wireless system-on-chip. They include ARM Cortex-M0 core and internal flash memory. +The driver automatically recognizes these chips using +the chip identification registers, and autoconfigures itself. + +@example +flash bank $_FLASHNAME bluenrg-x 0 0 0 0 $_TARGETNAME +@end example + +Note that when users ask to erase all the sectors of the flash, a mass erase command is used which is faster than erasing +each single sector one by one. + +@example +flash erase_sector 0 0 79 # It will perform a mass erase on BlueNRG-1 +@end example + +@example +flash erase_sector 0 0 127 # It will perform a mass erase on BlueNRG-2 +@end example + +Triggering a mass erase is also useful when users want to disable readout protection. + +@end deffn + @deffn {Flash Driver} efm32 All members of the EFM32 microcontroller family from Energy Micro include internal flash and use ARM Cortex-M3 cores. The driver automatically recognizes diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 4b74a468..b9353642 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -18,6 +18,7 @@ NOR_DRIVERS = \ %D%/ath79.c \ %D%/atsamv.c \ %D%/avrf.c \ + %D%/bluenrg-x.c \ %D%/cfi.c \ %D%/dsp5680xx_flash.c \ %D%/efm32.c \ diff --git a/src/flash/nor/bluenrg-x.c b/src/flash/nor/bluenrg-x.c new file mode 100644 index 00000000..c4a80804 --- /dev/null +++ b/src/flash/nor/bluenrg-x.c @@ -0,0 +1,549 @@ +/*************************************************************************** + * Copyright (C) 2017 by Michele Sardo * + * msmttchr@gmail.com * + * * + * 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 +#include +#include +#include "imp.h" + +#define FLASH_SIZE_REG (0x40100014) +#define DIE_ID_REG (0x4090001C) +#define JTAG_IDCODE_REG (0x40900028) +#define BLUENRG2_IDCODE (0x0200A041) +#define FLASH_BASE (0x10040000) +#define FLASH_PAGE_SIZE (2048) +#define FLASH_REG_COMMAND (0x40100000) +#define FLASH_REG_IRQRAW (0x40100010) +#define FLASH_REG_ADDRESS (0x40100018) +#define FLASH_REG_DATA (0x40100040) +#define FLASH_CMD_ERASE_PAGE 0x11 +#define FLASH_CMD_MASSERASE 0x22 +#define FLASH_CMD_WRITE 0x33 +#define FLASH_CMD_BURSTWRITE 0xCC +#define FLASH_INT_CMDDONE 0x01 +#define FLASH_WORD_LEN 4 + +struct bluenrgx_flash_bank { + int probed; + uint32_t idcode; + uint32_t die_id; +}; + +static int bluenrgx_protect_check(struct flash_bank *bank) +{ + /* Nothing to do. Protection is only handled in SW. */ + return ERROR_OK; +} + +/* flash_bank bluenrg-x 0 0 0 0 */ +FLASH_BANK_COMMAND_HANDLER(bluenrgx_flash_bank_command) +{ + struct bluenrgx_flash_bank *bluenrgx_info; + /* Create the bank structure */ + bluenrgx_info = calloc(1, sizeof(*bluenrgx_info)); + + /* Check allocation */ + if (bluenrgx_info == NULL) { + LOG_ERROR("failed to allocate bank structure"); + return ERROR_FAIL; + } + + bank->driver_priv = bluenrgx_info; + + bluenrgx_info->probed = 0; + + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + return ERROR_OK; +} + +static int bluenrgx_erase(struct flash_bank *bank, int first, int last) +{ + int retval = ERROR_OK; + struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv; + int num_sectors = (last - first + 1); + int mass_erase = (num_sectors == bank->num_sectors); + struct target *target = bank->target; + uint32_t address, command; + + /* check preconditions */ + if (bluenrgx_info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + /* Disable blue module */ + if (target_write_u32(target, 0x200000c0, 0) != ERROR_OK) { + LOG_ERROR("Blue disable failed"); + return ERROR_FAIL; + } + + if (mass_erase) { + command = FLASH_CMD_MASSERASE; + address = bank->base; + if (target_write_u32(target, FLASH_REG_IRQRAW, 0x3f) != ERROR_OK) { + LOG_ERROR("Register write failed"); + return ERROR_FAIL; + } + + if (target_write_u32(target, FLASH_REG_ADDRESS, address >> 2) != ERROR_OK) { + LOG_ERROR("Register write failed"); + return ERROR_FAIL; + } + + if (target_write_u32(target, FLASH_REG_COMMAND, command) != ERROR_OK) { + LOG_ERROR("Register write failed"); + return ERROR_FAIL; + } + + for (int i = 0; i < 100; i++) { + uint32_t value; + if (target_read_u32(target, FLASH_REG_IRQRAW, &value)) { + LOG_ERROR("Register write failed"); + return ERROR_FAIL; + } + if (value & FLASH_INT_CMDDONE) + break; + if (i == 99) { + LOG_ERROR("Mass erase command failed (timeout)"); + retval = ERROR_FAIL; + } + } + + } else { + command = FLASH_CMD_ERASE_PAGE; + for (int i = first; i <= last; i++) { + address = bank->base+i*FLASH_PAGE_SIZE; + + if (target_write_u32(target, FLASH_REG_IRQRAW, 0x3f) != ERROR_OK) { + LOG_ERROR("Register write failed"); + return ERROR_FAIL; + } + + if (target_write_u32(target, FLASH_REG_ADDRESS, address >> 2) != ERROR_OK) { + LOG_ERROR("Register write failed"); + return ERROR_FAIL; + } + + if (target_write_u32(target, FLASH_REG_COMMAND, command) != ERROR_OK) { + LOG_ERROR("Failed"); + return ERROR_FAIL; + } + + for (int j = 0; j < 100; j++) { + uint32_t value; + if (target_read_u32(target, FLASH_REG_IRQRAW, &value)) { + LOG_ERROR("Register write failed"); + return ERROR_FAIL; + } + if (value & FLASH_INT_CMDDONE) + break; + if (j == 99) { + LOG_ERROR("Erase command failed (timeout)"); + retval = ERROR_FAIL; + } + } + } + } + + return retval; + +} + +static int bluenrgx_protect(struct flash_bank *bank, int set, int first, int last) +{ + /* Protection is only handled in software: no hardware write protection + available in BlueNRG-x devices */ + int sector; + + for (sector = first; sector <= last; sector++) + bank->sectors[sector].is_protected = set; + return ERROR_OK; +} +static int bluenrgx_write_word(struct target *target, uint32_t address_base, uint8_t *values, uint32_t count) +{ + int retval = ERROR_OK; + + retval = target_write_u32(target, FLASH_REG_IRQRAW, 0x3f); + if (retval != ERROR_OK) { + LOG_ERROR("Register write failed, error code: %d", retval); + return retval; + } + + for (uint32_t i = 0; i < count; i++) { + uint32_t address = address_base + i * FLASH_WORD_LEN; + + retval = target_write_u32(target, FLASH_REG_ADDRESS, address >> 2); + if (retval != ERROR_OK) { + LOG_ERROR("Register write failed, error code: %d", retval); + return retval; + } + + retval = target_write_buffer(target, FLASH_REG_DATA, FLASH_WORD_LEN, values + i * FLASH_WORD_LEN); + if (retval != ERROR_OK) { + LOG_ERROR("Register write failed, error code: %d", retval); + return retval; + } + + retval = target_write_u32(target, FLASH_REG_COMMAND, FLASH_CMD_WRITE); + if (retval != ERROR_OK) { + LOG_ERROR("Register write failed, error code: %d", retval); + return retval; + } + + for (int j = 0; j < 100; j++) { + uint32_t reg_value; + retval = target_read_u32(target, FLASH_REG_IRQRAW, ®_value); + + if (retval != ERROR_OK) { + LOG_ERROR("Register read failed, error code: %d", retval); + return retval; + } + + if (reg_value & FLASH_INT_CMDDONE) + break; + + if (j == 99) { + LOG_ERROR("Write command failed (timeout)"); + return ERROR_FAIL; + } + } + } + return retval; +} + +static int bluenrgx_write_bytes(struct target *target, uint32_t address_base, uint8_t *buffer, uint32_t count) +{ + int retval = ERROR_OK; + uint8_t *new_buffer = NULL; + uint32_t pre_bytes = 0, post_bytes = 0, pre_word, post_word, pre_address, post_address; + + if (count == 0) { + /* Just return if there are no bytes to write */ + return retval; + } + + if (address_base & 3) { + pre_bytes = address_base & 3; + pre_address = address_base - pre_bytes; + } + + if ((count + pre_bytes) & 3) { + post_bytes = ((count + pre_bytes + 3) & ~3) - (count + pre_bytes); + post_address = (address_base + count) & ~3; + } + + if (pre_bytes || post_bytes) { + uint32_t old_count = count; + + count = old_count + pre_bytes + post_bytes; + + new_buffer = malloc(count); + + if (new_buffer == NULL) { + LOG_ERROR("odd number of bytes to write and no memory " + "for padding buffer"); + return ERROR_FAIL; + } + + LOG_INFO("Requested number of bytes to write and/or address not word aligned (%" PRIu32 "), extending to %" + PRIu32 " ", old_count, count); + + if (pre_bytes) { + if (target_read_u32(target, pre_address, &pre_word)) { + LOG_ERROR("Memory read failed"); + return ERROR_FAIL; + } + + } + + if (post_bytes) { + if (target_read_u32(target, post_address, &post_word)) { + LOG_ERROR("Memory read failed"); + return ERROR_FAIL; + } + + } + + memcpy(new_buffer, &pre_word, pre_bytes); + memcpy((new_buffer+((pre_bytes+old_count) & ~3)), &post_word, 4); + memcpy(new_buffer+pre_bytes, buffer, old_count); + buffer = new_buffer; + } + + retval = bluenrgx_write_word(target, address_base - pre_bytes, buffer, count/4); + + if (new_buffer) + free(new_buffer); + + return retval; +} + +static int bluenrgx_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + uint32_t buffer_size = 16384 + 8; + struct working_area *write_algorithm; + struct working_area *write_algorithm_sp; + struct working_area *source; + uint32_t address = bank->base + offset; + struct reg_param reg_params[5]; + struct armv7m_algorithm armv7m_info; + int retval = ERROR_OK; + uint32_t pre_size = 0, fast_size = 0, post_size = 0; + uint32_t pre_offset = 0, fast_offset = 0, post_offset = 0; + + /* See contrib/loaders/flash/bluenrg-x/bluenrg-x_write.c for source and + * hints how to generate the data! + */ + static const uint8_t bluenrgx_flash_write_code[] = { +#include "../../../contrib/loaders/flash/bluenrg-x/bluenrg-x_write.inc" + }; + + if ((offset + count) > bank->size) { + LOG_ERROR("Requested write past beyond of flash size: (offset+count) = %d, size=%d", + (offset + count), + bank->size); + return ERROR_FLASH_DST_OUT_OF_BANK; + } + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* We are good here and we need to compute pre_size, fast_size, post_size */ + pre_size = MIN(count, ((offset+0xF) & ~0xF) - offset); + pre_offset = offset; + fast_size = 16*((count - pre_size) / 16); + fast_offset = offset + pre_size; + post_size = (count-pre_size-fast_size) % 16; + post_offset = fast_offset + fast_size; + + LOG_DEBUG("pre_size = %08x, pre_offset=%08x", pre_size, pre_offset); + LOG_DEBUG("fast_size = %08x, fast_offset=%08x", fast_size, fast_offset); + LOG_DEBUG("post_size = %08x, post_offset=%08x", post_size, post_offset); + + /* Program initial chunk not 16 bytes aligned */ + retval = bluenrgx_write_bytes(target, bank->base+pre_offset, (uint8_t *) buffer, pre_size); + if (retval) { + LOG_ERROR("bluenrgx_write_bytes failed %d", retval); + return ERROR_FAIL; + } + + /* Program chunk 16 bytes aligned in fast mode */ + if (fast_size) { + + if (target_alloc_working_area(target, sizeof(bluenrgx_flash_write_code), + &write_algorithm) != ERROR_OK) { + LOG_WARNING("no working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + retval = target_write_buffer(target, write_algorithm->address, + sizeof(bluenrgx_flash_write_code), + bluenrgx_flash_write_code); + if (retval != ERROR_OK) + return retval; + + /* memory buffer */ + if (target_alloc_working_area(target, buffer_size, &source)) { + LOG_WARNING("no large enough working area available"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + /* Stack pointer area */ + if (target_alloc_working_area(target, 64, + &write_algorithm_sp) != ERROR_OK) { + LOG_DEBUG("no working area for write code stack pointer"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_info.core_mode = ARM_MODE_THREAD; + + init_reg_param(®_params[0], "r0", 32, PARAM_IN_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], "sp", 32, PARAM_OUT); + + /* FIFO start address (first two words used for write and read pointers) */ + buf_set_u32(reg_params[0].value, 0, 32, source->address); + /* FIFO end address (first two words used for write and read pointers) */ + buf_set_u32(reg_params[1].value, 0, 32, source->address + source->size); + /* Flash memory address */ + buf_set_u32(reg_params[2].value, 0, 32, address+pre_size); + /* Number of bytes */ + buf_set_u32(reg_params[3].value, 0, 32, fast_size); + /* Stack pointer for program working area */ + buf_set_u32(reg_params[4].value, 0, 32, write_algorithm_sp->address); + + LOG_DEBUG("source->address = %08" TARGET_PRIxADDR, source->address); + LOG_DEBUG("source->address+ source->size = %08" TARGET_PRIxADDR, source->address+source->size); + LOG_DEBUG("write_algorithm_sp->address = %08" TARGET_PRIxADDR, write_algorithm_sp->address); + LOG_DEBUG("address = %08x", address+pre_size); + LOG_DEBUG("count = %08x", count); + + retval = target_run_flash_async_algorithm(target, + buffer+pre_size, + fast_size/16, + 16, /* Block size: we write in block of 16 bytes to enjoy burstwrite speed */ + 0, + NULL, + 5, + reg_params, + source->address, + source->size, + write_algorithm->address, + 0, + &armv7m_info); + + if (retval == ERROR_FLASH_OPERATION_FAILED) { + LOG_ERROR("error executing bluenrg-x flash write algorithm"); + + uint32_t error = buf_get_u32(reg_params[0].value, 0, 32); + + if (error != 0) + LOG_ERROR("flash write failed = %08" PRIx32, error); + } + if (retval == ERROR_OK) { + uint32_t rp; + /* Read back rp and check that is valid */ + retval = target_read_u32(target, source->address+4, &rp); + if (retval == ERROR_OK) { + if ((rp < source->address+8) || (rp > (source->address + source->size))) { + LOG_ERROR("flash write failed = %08" PRIx32, rp); + retval = ERROR_FLASH_OPERATION_FAILED; + } + } + } + target_free_working_area(target, source); + target_free_working_area(target, write_algorithm); + target_free_working_area(target, write_algorithm_sp); + + 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]); + } + + /* Program chunk at end, not addressable by fast burst write algorithm */ + retval = bluenrgx_write_bytes(target, bank->base+post_offset, (uint8_t *) (buffer+pre_size+fast_size), post_size); + if (retval) { + LOG_ERROR("bluenrgx_write_bytes failed %d", retval); + return ERROR_FAIL; + } + return retval; +} + +static int bluenrgx_probe(struct flash_bank *bank) +{ + struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv; + uint32_t idcode, size_info, die_id; + int i; + int retval = target_read_u32(bank->target, JTAG_IDCODE_REG, &idcode); + if (retval != ERROR_OK) + return retval; + retval = target_read_u32(bank->target, FLASH_SIZE_REG, &size_info); + if (retval != ERROR_OK) + return retval; + + retval = target_read_u32(bank->target, DIE_ID_REG, &die_id); + if (retval != ERROR_OK) + return retval; + + bank->size = (size_info + 1) * 4; + bank->base = FLASH_BASE; + bank->num_sectors = bank->size/FLASH_PAGE_SIZE; + bank->sectors = realloc(bank->sectors, sizeof(struct flash_sector) * bank->num_sectors); + + for (i = 0; i < bank->num_sectors; i++) { + bank->sectors[i].offset = i * FLASH_PAGE_SIZE; + bank->sectors[i].size = FLASH_PAGE_SIZE; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 0; + } + + bluenrgx_info->probed = 1; + bluenrgx_info->die_id = die_id; + bluenrgx_info->idcode = idcode; + return ERROR_OK; +} + +static int bluenrgx_auto_probe(struct flash_bank *bank) +{ + struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv; + + if (bluenrgx_info->probed) + return ERROR_OK; + + return bluenrgx_probe(bank); +} + +/* This method must return a string displaying information about the bank */ +static int bluenrgx_get_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv; + int mask_number, cut_number; + char *part_name; + + if (!bluenrgx_info->probed) { + int retval = bluenrgx_probe(bank); + if (retval != ERROR_OK) { + snprintf(buf, buf_size, + "Unable to find bank information."); + return retval; + } + } + + if (bluenrgx_info->idcode == BLUENRG2_IDCODE) + part_name = "BLUENRG-2"; + else + part_name = "BLUENRG-1"; + + mask_number = (bluenrgx_info->die_id >> 4) & 0xF; + cut_number = bluenrgx_info->die_id & 0xF; + + snprintf(buf, buf_size, + "%s - Rev: %d.%d", part_name, mask_number, cut_number); + return ERROR_OK; +} + +struct flash_driver bluenrgx_flash = { + .name = "bluenrg-x", + .flash_bank_command = bluenrgx_flash_bank_command, + .erase = bluenrgx_erase, + .protect = bluenrgx_protect, + .write = bluenrgx_write, + .read = default_flash_read, + .probe = bluenrgx_probe, + .erase_check = default_flash_blank_check, + .protect_check = bluenrgx_protect_check, + .auto_probe = bluenrgx_auto_probe, + .info = bluenrgx_get_info, +}; diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 0e6a7382..a4640a5e 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -31,6 +31,7 @@ extern struct flash_driver at91samd_flash; extern struct flash_driver ath79_flash; extern struct flash_driver atsamv_flash; extern struct flash_driver avr_flash; +extern struct flash_driver bluenrgx_flash; extern struct flash_driver cfi_flash; extern struct flash_driver dsp5680xx_flash; extern struct flash_driver efm32_flash; @@ -88,6 +89,7 @@ static struct flash_driver *flash_drivers[] = { &ath79_flash, &atsamv_flash, &avr_flash, + &bluenrgx_flash, &cfi_flash, &dsp5680xx_flash, &efm32_flash, diff --git a/tcl/board/steval-idb007v1.cfg b/tcl/board/steval-idb007v1.cfg new file mode 100644 index 00000000..24dbd1e9 --- /dev/null +++ b/tcl/board/steval-idb007v1.cfg @@ -0,0 +1,4 @@ +# This is an evaluation board with a single BlueNRG-1 chip. +# http://www.st.com/content/st_com/en/products/evaluation-tools/solution-evaluation-tools/communication-and-connectivity-solution-eval-boards/steval-idb008v1.html +set CHIPNAME bluenrg-1 +source [find target/bluenrg-x.cfg] diff --git a/tcl/board/steval-idb008v1.cfg b/tcl/board/steval-idb008v1.cfg new file mode 100644 index 00000000..3e9d0e5e --- /dev/null +++ b/tcl/board/steval-idb008v1.cfg @@ -0,0 +1,4 @@ +# This is an evaluation board with a single BlueNRG-2 chip. +# http://www.st.com/content/st_com/en/products/evaluation-tools/solution-evaluation-tools/communication-and-connectivity-solution-eval-boards/steval-idb007v1.html +set CHIPNAME bluenrg-2 +source [find target/bluenrg-x.cfg] diff --git a/tcl/target/bluenrg-x.cfg b/tcl/target/bluenrg-x.cfg new file mode 100644 index 00000000..3ae5108c --- /dev/null +++ b/tcl/target/bluenrg-x.cfg @@ -0,0 +1,73 @@ +# +# bluenrg-1/2 devices support only SWD transports. +# + +source [find target/swj-dp.tcl] + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME bluenrg-1 +} + +set _ENDIAN little + +# Work-area is a space in RAM used for flash programming +# By default use 24kB-256bytes +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x5F00 +} + +adapter_khz 4000 + +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + set _CPUTAPID 0x0bb11477 +} + +swj_newdap $_CHIPNAME cpu -expected-id $_CPUTAPID + +set _TARGETNAME $_CHIPNAME.cpu +set WDOG_VALUE 0 +set WDOG_VALUE_SET 0 + +target create $_TARGETNAME cortex_m -endian $_ENDIAN -chain-position $_TARGETNAME + +$_TARGETNAME configure -work-area-phys 0x20000100 -work-area-size $_WORKAREASIZE -work-area-backup 0 + +# flash size will be probed +set _FLASHNAME $_CHIPNAME.flash +flash bank $_FLASHNAME bluenrg-x 0 0 0 0 $_TARGETNAME + +# In BlueNRG-X reset pin is actually a shutdown (power-off), so define reset as none +reset_config none + +if {![using_hla]} { + # if srst is not fitted use SYSRESETREQ to + # perform a soft reset + cortex_m reset_config sysresetreq +} + +$_TARGETNAME configure -event halted { + global WDOG_VALUE + global WDOG_VALUE_SET + # Stop watchdog during halt, if enabled + mem2array value 32 0x40700008 1 + set WDOG_VALUE [expr ($value(0))] + if [expr ($value(0) & (1 << 1))] { + set WDOG_VALUE_SET 1 + mww 0x40700008 [expr ($value(0) & 0xFFFFFFFD)] + } +} +$_TARGETNAME configure -event resumed { + global WDOG_VALUE + global WDOG_VALUE_SET + if [expr $WDOG_VALUE_SET] { + # Restore watchdog enable value after resume + mww 0x40700008 $WDOG_VALUE + set WDOG_VALUE_SET 0 + } +}