Added support for STMicroelectronics BlueNRG-1 and BlueNRG-2 SoC
Added configuration files and flash loaders. Change-Id: I768eb3626f4e0eadb206bef90a867cc146fe8c75 Signed-off-by: Michele Sardo <msmttchr@gmail.com> Reviewed-on: http://openocd.zylin.com/4226 Tested-by: jenkins Reviewed-by: Tomas Vanek <vanekt@fbl.cz>riscv-compliance-dev
parent
8f1f912a7d
commit
cb2f21bf36
|
@ -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
|
|
@ -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 <stdint.h>
|
||||
|
||||
/* 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");
|
||||
}
|
|
@ -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,
|
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <target/algorithm.h>
|
||||
#include <target/armv7m.h>
|
||||
#include <target/cortex_m.h>
|
||||
#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 <target#> */
|
||||
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,
|
||||
};
|
|
@ -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,
|
||||
|
|
|
@ -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]
|
|
@ -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]
|
|
@ -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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue