forked from auracaster/openocd
flash/nor/bl602: add bl602 flash driver
Initial release of Flash bank driver for Bouffalo chips. The driver currently supports BL602, BL702, BL702L series of chips. Similar SFlash core is inside of BL808, BL606P and BL616 series, so those might be supported in future as well. With adapter speed set to 8000, it can reach speed 140 KiB/s. Since chips have eXecute In Place support, and they also require boot config in Flash at offset 0x0, it's required to have properly crafted linker script, so OpenOCD knows where to write firmware through gdb. There is required flash bank parameter, which specifies the chip type. This is required because BL702 and BL702L have same TAP ID CODE, and there are no usable indicators to use for automatic chip type recognition in the chip. Change-Id: Id57336d447be3c608b39ba3ed143527bfdc0af98 Signed-off-by: Marek Kraus <gamelaster@outlook.com> Reviewed-on: https://review.openocd.org/c/openocd/+/8527 Tested-by: jenkins Reviewed-by: Tomas Vanek <vanekt@fbl.cz> Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
This commit is contained in:
committed by
Antonio Borneo
parent
3e86eaaf7d
commit
df2eb07086
@@ -22,6 +22,7 @@ NOR_DRIVERS = \
|
||||
%D%/atsamv.c \
|
||||
%D%/atsame5.c \
|
||||
%D%/avrf.c \
|
||||
%D%/bl602.c \
|
||||
%D%/bluenrg-x.c \
|
||||
%D%/cc3220sf.c \
|
||||
%D%/cc26xx.c \
|
||||
|
||||
760
src/flash/nor/bl602.c
Normal file
760
src/flash/nor/bl602.c
Normal file
@@ -0,0 +1,760 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2024 by Marek Kraus *
|
||||
* gamelaster@gami.ee *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "imp.h"
|
||||
#include <jtag/jtag.h>
|
||||
#include <target/algorithm.h>
|
||||
#include "spi.h"
|
||||
#include "target/riscv/opcodes.h"
|
||||
#include "target/riscv/gdb_regs.h"
|
||||
|
||||
/*
|
||||
* Flash bank driver for Bouffalo chips with BL602-like flash controller
|
||||
*
|
||||
* Supported chips (series): BL602, BL702 and BL702L
|
||||
* Not supported but compatible chips (series): BL616, BL606P and BL808
|
||||
*
|
||||
* SFlash supports both direct access and eXecute In Place (XIP) via L1 cache (L1C).
|
||||
* If XIP is configured and direct access is needed,
|
||||
* then the state needs to be saved and some settings need to be reset for it to work properly.
|
||||
* The only way to do this is to save/restore the entire SFlash core configuration, which has not been tested.
|
||||
* Because of this, on every OpenOCD flash probe, GPIO and SFlash will be reconfigured, resulting in XIP not working.
|
||||
* So it's recommended to reset the device after every OpenOCD flash operation.
|
||||
*
|
||||
* QSPI write is not supported yet.
|
||||
*/
|
||||
|
||||
// SFlash CFG definitions
|
||||
#define SFLASH_CFG_SIZE 84
|
||||
#define SFLASH_CFG_BUSY_BIT_POS 0x2D
|
||||
#define SFLASH_CFG_BUSY_INDEX_POS 0x2A
|
||||
#define SFLASH_CFG_BUSY_READ_REG_LEN_POS 0x33
|
||||
#define SFLASH_CFG_FAST_READ_CMD_POS 0x18
|
||||
#define SFLASH_CFG_JEDEC_ID_CMD_DMY_CLK_POS 0x09
|
||||
#define SFLASH_CFG_JEDEC_ID_CMD_POS 0x08
|
||||
#define SFLASH_CFG_PAGE_PROGRAM_CMD_POS 0x15
|
||||
#define SFLASH_CFG_PAGE_SIZE_POS 0x0E
|
||||
#define SFLASH_CFG_READ_STATUS_REG1_POS 0x34
|
||||
#define SFLASH_CFG_SECTOR_ERASE_CMD_POS 0x11
|
||||
#define SFLASH_CFG_SECTOR_SIZE_POS 0x0C
|
||||
#define SFLASH_CFG_TIME_ERASE_SECTOR_POS 0x48
|
||||
#define SFLASH_CFG_TIME_PAGE_PGM_POS 0x4E
|
||||
#define SFLASH_CFG_WRITE_ENABLE_BIT_POS 0x2B
|
||||
#define SFLASH_CFG_WRITE_ENABLE_CMD_POS 0x14
|
||||
#define SFLASH_CFG_WRITE_ENABLE_INDEX_POS 0x28
|
||||
#define SFLASH_CFG_WRITE_ENABLE_READ_REG_LEN_POS 0x2F
|
||||
|
||||
#define BL602_DEFAULT_TIMEOUT_MS 5000
|
||||
|
||||
enum bflb_series {
|
||||
BFLB_SERIES_BL602,
|
||||
BFLB_SERIES_BL702,
|
||||
BFLB_SERIES_BL702L,
|
||||
};
|
||||
|
||||
struct bl602_part_info {
|
||||
uint32_t idcode;
|
||||
enum bflb_series series;
|
||||
uint32_t romapi_get_jedec_id;
|
||||
uint32_t romapi_sflash_init_gpio;
|
||||
uint32_t romapi_sflash_init;
|
||||
uint32_t romapi_sflash_program;
|
||||
uint32_t romapi_sflash_read;
|
||||
uint32_t romapi_sflash_erase_sector;
|
||||
};
|
||||
|
||||
struct bl602_flash_bank {
|
||||
// flag indicating successful flash probe
|
||||
bool probed;
|
||||
enum bflb_series series;
|
||||
const struct bl602_part_info *part_info;
|
||||
uint8_t sflash_cfg[SFLASH_CFG_SIZE];
|
||||
// detected model of SPI flash
|
||||
const struct flash_device *dev;
|
||||
};
|
||||
|
||||
static const struct bl602_part_info bl602_parts[] = {
|
||||
{
|
||||
.idcode = 0x20000c05,
|
||||
.series = BFLB_SERIES_BL602,
|
||||
.romapi_get_jedec_id = 0x210109bc,
|
||||
.romapi_sflash_init_gpio = 0x21010a20,
|
||||
.romapi_sflash_init = 0x21010980,
|
||||
.romapi_sflash_program = 0x210109b4,
|
||||
.romapi_sflash_read = 0x210109fc,
|
||||
.romapi_sflash_erase_sector = 0x210109a4,
|
||||
},
|
||||
{
|
||||
.idcode = 0x20000e05,
|
||||
.series = BFLB_SERIES_BL702,
|
||||
.romapi_get_jedec_id = 0x210189a4,
|
||||
.romapi_sflash_init_gpio = 0x21018a78,
|
||||
.romapi_sflash_init = 0x21018960,
|
||||
.romapi_sflash_program = 0x2101899c,
|
||||
.romapi_sflash_read = 0x210189d8,
|
||||
.romapi_sflash_erase_sector = 0x2101898c,
|
||||
},
|
||||
{
|
||||
.idcode = 0x20000e05,
|
||||
.series = BFLB_SERIES_BL702L,
|
||||
.romapi_get_jedec_id = 0x21010d10,
|
||||
.romapi_sflash_init_gpio = 0x21010c54,
|
||||
.romapi_sflash_init = 0x21010cc8,
|
||||
.romapi_sflash_program = 0x21010d08,
|
||||
.romapi_sflash_read = 0x21010d44,
|
||||
.romapi_sflash_erase_sector = 0x21010cf8,
|
||||
},
|
||||
};
|
||||
|
||||
static int bl602_call_func(struct target *target, uint32_t func_addr,
|
||||
uint32_t arg_data[], unsigned int n_args, uint32_t *return_data, unsigned int timeout_ms)
|
||||
{
|
||||
static const char * const reg_names[] = { "a0", "a1", "a2", "a3", "a4", "a5" };
|
||||
struct reg_param reg_params[ARRAY_SIZE(reg_names) + 1];
|
||||
unsigned int n_reg_params = 0;
|
||||
|
||||
assert(n_args <= ARRAY_SIZE(reg_names)); // only allow register arguments
|
||||
|
||||
struct working_area *trampoline_algorithm;
|
||||
|
||||
uint32_t trampoline_code[] = {
|
||||
ebreak(),
|
||||
};
|
||||
|
||||
int retval = target_alloc_working_area(target, sizeof(trampoline_code),
|
||||
&trampoline_algorithm);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("No working area available, can't do trampoline");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = target_write_buffer(target, trampoline_algorithm->address,
|
||||
sizeof(trampoline_code), (uint8_t *)trampoline_code);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Writing trampoline code failed, can't do trampoline");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// initialize a0 register, which is used both as arg and also return register.
|
||||
if (!return_data || n_args > 0) {
|
||||
init_reg_param(®_params[0], reg_names[0], 32, PARAM_IN);
|
||||
if (n_args > 0) {
|
||||
buf_set_u32(reg_params[0].value, 0, 32, arg_data[0]);
|
||||
reg_params[0].direction = PARAM_IN_OUT;
|
||||
}
|
||||
n_reg_params++;
|
||||
}
|
||||
// initialize rest of registers, if any
|
||||
for (unsigned int i = 1; i < n_args; ++i) {
|
||||
init_reg_param(®_params[i], reg_names[i], 32, PARAM_OUT);
|
||||
buf_set_u32(reg_params[i].value, 0, 32, arg_data[i]);
|
||||
n_reg_params++;
|
||||
}
|
||||
// set return address to the ebreak instruction in working area
|
||||
init_reg_param(®_params[n_reg_params], "ra", 32, PARAM_OUT);
|
||||
buf_set_u32(reg_params[n_reg_params].value, 0, 32, trampoline_algorithm->address);
|
||||
n_reg_params++;
|
||||
|
||||
retval = target_run_algorithm(target,
|
||||
0, NULL,
|
||||
n_reg_params, reg_params,
|
||||
func_addr,
|
||||
trampoline_algorithm->address,
|
||||
timeout_ms, NULL);
|
||||
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to execute algorithm at 0x%" TARGET_PRIxADDR ": %d",
|
||||
trampoline_algorithm->address, retval);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (return_data)
|
||||
*return_data = buf_get_u32(reg_params[0].value, 0, 32);
|
||||
|
||||
cleanup:
|
||||
for (unsigned int i = 0; i < n_reg_params; i++)
|
||||
destroy_reg_param(®_params[i]);
|
||||
|
||||
target_free_working_area(target, trampoline_algorithm);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int bl602_call_romapi_func(struct target *target, uint32_t romapi_func_addr,
|
||||
uint32_t arg_data[], unsigned int n_args, uint32_t *return_data, unsigned int timeout_ms)
|
||||
{
|
||||
uint32_t func_addr;
|
||||
|
||||
int retval = target_read_u32(target, romapi_func_addr, &func_addr);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
return bl602_call_func(target, func_addr, arg_data, n_args, return_data, timeout_ms);
|
||||
}
|
||||
|
||||
static int bl602_alloc_bounce_buffer(struct flash_bank *bank,
|
||||
struct working_area **working_area, uint32_t count)
|
||||
{
|
||||
struct bl602_flash_bank *priv = bank->driver_priv;
|
||||
struct target *target = bank->target;
|
||||
|
||||
unsigned int avail_pages = target_get_working_area_avail(target) / priv->dev->pagesize;
|
||||
if (avail_pages == 0) {
|
||||
LOG_ERROR("Not enough space for bounce buffer. Can't continue");
|
||||
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/* We try to allocate working area rounded down to device page size,
|
||||
* at least 1 page, at most the write data size */
|
||||
unsigned int chunk_size = MIN(MAX(avail_pages - 1, 1) * priv->dev->pagesize, count);
|
||||
int retval = target_alloc_working_area(target, chunk_size, working_area);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Could not allocate bounce buffer for flash manipulation. Can't continue");
|
||||
return retval;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Allocated flash bounce buffer @" TARGET_ADDR_FMT, (*working_area)->address);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int bl602_alloc_sflash_cfg(struct flash_bank *bank,
|
||||
struct working_area **working_area)
|
||||
{
|
||||
struct bl602_flash_bank *priv = bank->driver_priv;
|
||||
struct target *target = bank->target;
|
||||
|
||||
int retval = target_alloc_working_area(target, sizeof(priv->sflash_cfg),
|
||||
working_area);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("No working area available, can't alloc sflash cfg");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = target_write_buffer(target, (*working_area)->address,
|
||||
sizeof(priv->sflash_cfg), priv->sflash_cfg);
|
||||
if (retval != ERROR_OK)
|
||||
target_free_working_area(target, *working_area);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int bl602_flash_gpio_init(struct flash_bank *bank)
|
||||
{
|
||||
struct bl602_flash_bank *priv = bank->driver_priv;
|
||||
struct target *target = bank->target;
|
||||
const struct bl602_part_info *part_info = priv->part_info;
|
||||
|
||||
uint32_t flash_pin_cfg = 0;
|
||||
|
||||
// Read device info from eFuse
|
||||
if (part_info->series == BFLB_SERIES_BL702 ||
|
||||
part_info->series == BFLB_SERIES_BL702L) {
|
||||
// Read device_info register
|
||||
uint32_t device_info;
|
||||
int retval = target_read_u32(target, 0x40007074, &device_info);
|
||||
if (retval != ERROR_OK || device_info == 0x0) {
|
||||
/* device_info being 0x0 shouldn't happen, since eFuse is loaded in BootROM,
|
||||
* but better to handle it */
|
||||
LOG_ERROR("Failed to read device info from eFuse");
|
||||
return retval;
|
||||
}
|
||||
|
||||
uint32_t flash_cfg = (device_info >> 26) & 0x03;
|
||||
uint32_t sf_swap_cfg = (device_info >> 22) & 0x03;
|
||||
uint32_t sf_reverse_cfg = (device_info >> 29) & 0x01; // BL702L only
|
||||
if (part_info->series == BFLB_SERIES_BL702) {
|
||||
flash_pin_cfg = (flash_cfg << 2) | sf_swap_cfg;
|
||||
} else { // BL702L
|
||||
flash_pin_cfg = sf_swap_cfg + (sf_reverse_cfg != 0 ? 5 : 1);
|
||||
}
|
||||
} else if (part_info->series == BFLB_SERIES_BL602) {
|
||||
// Structure is not known.
|
||||
uint32_t sw_usage;
|
||||
int retval = target_read_u32(target, 0x40007010, &sw_usage);
|
||||
if (retval != ERROR_OK || sw_usage == 0x0) {
|
||||
/* sw_usage being 0x0 shouldn't happen, since eFuse is loaded in BootROM,
|
||||
* but better to handle it */
|
||||
LOG_ERROR("Failed to read sw usage from eFuse");
|
||||
return retval;
|
||||
}
|
||||
|
||||
// We only assume it is sf_swap_cfg
|
||||
uint32_t sf_swap_cfg = (sw_usage >> 16) & 0x03;
|
||||
flash_pin_cfg = sf_swap_cfg;
|
||||
}
|
||||
|
||||
// initialize flash GPIOs
|
||||
uint32_t args[] = {
|
||||
flash_pin_cfg,
|
||||
1, // restoreDefault
|
||||
};
|
||||
|
||||
int retval = bl602_call_romapi_func(target, part_info->romapi_sflash_init_gpio,
|
||||
args, ARRAY_SIZE(args), NULL, BL602_DEFAULT_TIMEOUT_MS);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to invoke spi flash gpio init function");
|
||||
return retval;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static const uint8_t bl602_sflash_ctrl_cfg[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
/* The structure of SF_Ctrl_Cfg_Type is same as on BL602 and BL702,
|
||||
* but BL702L's BootROM was compiled without -fshort-enums flag.
|
||||
* That caused that first three fields (enums) are 4 bytes instead of 1 byte. */
|
||||
static const uint8_t bl702l_sflash_ctrl_cfg[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||
0xe1, 0xfd, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00,
|
||||
0x00, 0x00, 0x01, 0x21,
|
||||
};
|
||||
|
||||
static int bl602_flash_init(struct flash_bank *bank)
|
||||
{
|
||||
struct bl602_flash_bank *priv = bank->driver_priv;
|
||||
struct target *target = bank->target;
|
||||
const struct bl602_part_info *part_info = priv->part_info;
|
||||
|
||||
int retval = bl602_flash_gpio_init(bank);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
// initialize SFlash peripheral
|
||||
// structure for this can be found in official SDK by name SF_Ctrl_Cfg_Type
|
||||
const uint8_t *sflash_ctrl_cfg = NULL;
|
||||
uint16_t sflash_ctrl_cfg_size = 0;
|
||||
|
||||
switch (part_info->series) {
|
||||
case BFLB_SERIES_BL602:
|
||||
case BFLB_SERIES_BL702:
|
||||
sflash_ctrl_cfg = bl602_sflash_ctrl_cfg;
|
||||
sflash_ctrl_cfg_size = ARRAY_SIZE(bl602_sflash_ctrl_cfg);
|
||||
break;
|
||||
case BFLB_SERIES_BL702L:
|
||||
sflash_ctrl_cfg = bl702l_sflash_ctrl_cfg;
|
||||
sflash_ctrl_cfg_size = ARRAY_SIZE(bl702l_sflash_ctrl_cfg);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("No SFlash Control Config available for this chip");
|
||||
return ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
struct working_area *sflash_ctrl_cfg_area;
|
||||
retval = target_alloc_working_area(target, sflash_ctrl_cfg_size,
|
||||
&sflash_ctrl_cfg_area);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_WARNING("No working area available, can't init SFlash");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = target_write_buffer(target, sflash_ctrl_cfg_area->address,
|
||||
sflash_ctrl_cfg_size, sflash_ctrl_cfg);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to write SFlash Control CFG into working area");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
uint32_t args[] = {
|
||||
sflash_ctrl_cfg_area->address,
|
||||
};
|
||||
|
||||
retval = bl602_call_romapi_func(target, part_info->romapi_sflash_init,
|
||||
args, ARRAY_SIZE(args), NULL, BL602_DEFAULT_TIMEOUT_MS);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to invoke SFlash init function");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
target_free_working_area(target, sflash_ctrl_cfg_area);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int bl602_flash_read_id(struct flash_bank *bank, uint32_t *jedec_id)
|
||||
{
|
||||
struct bl602_flash_bank *priv = bank->driver_priv;
|
||||
struct target *target = bank->target;
|
||||
const struct bl602_part_info *part_info = priv->part_info;
|
||||
|
||||
struct working_area *data_area;
|
||||
int retval = target_alloc_working_area(target, sizeof(uint32_t) + SFLASH_CFG_SIZE,
|
||||
&data_area);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("No working area available, can't read flash id");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = target_write_buffer(target, data_area->address + sizeof(uint32_t),
|
||||
SFLASH_CFG_SIZE, priv->sflash_cfg);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to write data required for flash id read");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
uint32_t args[2] = {
|
||||
data_area->address + 4, // SFlash CFG
|
||||
data_area->address, // JEDEC ID pointer
|
||||
};
|
||||
|
||||
retval = bl602_call_romapi_func(target, part_info->romapi_get_jedec_id,
|
||||
args, ARRAY_SIZE(args), NULL, BL602_DEFAULT_TIMEOUT_MS);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to invoke get jedec id function");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
retval = target_read_u32(target, data_area->address, jedec_id);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to read flash id from target");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*jedec_id &= 0x00FFFFFF;
|
||||
|
||||
cleanup:
|
||||
target_free_working_area(target, data_area);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int bl602_erase(struct flash_bank *bank, unsigned int first,
|
||||
unsigned int last)
|
||||
{
|
||||
struct bl602_flash_bank *priv = bank->driver_priv;
|
||||
const struct bl602_part_info *part_info = priv->part_info;
|
||||
struct target *target = bank->target;
|
||||
struct working_area *sflash_cfg_area = NULL;
|
||||
|
||||
if (target->state != TARGET_HALTED) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
int retval = bl602_alloc_sflash_cfg(bank, &sflash_cfg_area);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
uint32_t return_value;
|
||||
|
||||
for (unsigned int sector = first; sector <= last; sector++) {
|
||||
LOG_INFO("Erasing sector %d", sector);
|
||||
uint32_t args[] = {
|
||||
sflash_cfg_area->address,
|
||||
sector,
|
||||
};
|
||||
retval = bl602_call_romapi_func(target, part_info->romapi_sflash_erase_sector,
|
||||
args, ARRAY_SIZE(args), &return_value, BL602_DEFAULT_TIMEOUT_MS);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to invoke flash erase code on target");
|
||||
break;
|
||||
}
|
||||
if (return_value != 0) {
|
||||
LOG_ERROR("Erase flash function returned wrong value: %02X", return_value);
|
||||
retval = ERROR_FAIL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
target_free_working_area(target, sflash_cfg_area);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int bl602_write(struct flash_bank *bank, const uint8_t *buffer,
|
||||
uint32_t offset, uint32_t count)
|
||||
{
|
||||
struct bl602_flash_bank *priv = bank->driver_priv;
|
||||
const struct bl602_part_info *part_info = priv->part_info;
|
||||
struct target *target = bank->target;
|
||||
struct working_area *bounce_area = NULL;
|
||||
struct working_area *sflash_cfg_area = NULL;
|
||||
|
||||
LOG_DEBUG("bank->size=0x%x offset=0x%08" PRIx32 " count=0x%08" PRIx32,
|
||||
bank->size, offset, count);
|
||||
|
||||
if (target->state != TARGET_HALTED) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
if (offset + count > priv->dev->size_in_bytes) {
|
||||
LOG_WARNING("Write past end of flash. Extra data discarded");
|
||||
count = priv->dev->size_in_bytes - offset;
|
||||
}
|
||||
|
||||
int retval = bl602_alloc_sflash_cfg(bank, &sflash_cfg_area);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = bl602_alloc_bounce_buffer(bank, &bounce_area, count);
|
||||
if (retval != ERROR_OK)
|
||||
goto cleanup;
|
||||
|
||||
unsigned int chunk_size = bounce_area->size;
|
||||
uint32_t return_value;
|
||||
|
||||
while (count > 0) {
|
||||
uint32_t write_size = count > chunk_size ? chunk_size : count;
|
||||
LOG_INFO("Writing %d bytes to offset 0x%" PRIx32, write_size, offset);
|
||||
retval = target_write_buffer(target, bounce_area->address, write_size, buffer);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Could not load data into target bounce buffer");
|
||||
goto cleanup;
|
||||
}
|
||||
uint32_t args[] = {
|
||||
sflash_cfg_area->address,
|
||||
0x0, // io_mode
|
||||
offset,
|
||||
bounce_area->address,
|
||||
write_size,
|
||||
};
|
||||
retval = bl602_call_romapi_func(target, part_info->romapi_sflash_program,
|
||||
args, ARRAY_SIZE(args), &return_value, BL602_DEFAULT_TIMEOUT_MS);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to invoke flash programming code on target");
|
||||
goto cleanup;
|
||||
}
|
||||
if (return_value != 0) {
|
||||
LOG_ERROR("Write flash function returned wrong value: %02X", return_value);
|
||||
retval = ERROR_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
buffer += write_size;
|
||||
offset += write_size;
|
||||
count -= write_size;
|
||||
}
|
||||
|
||||
|
||||
cleanup:
|
||||
target_free_working_area(target, bounce_area);
|
||||
target_free_working_area(target, sflash_cfg_area);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int bl602_read(struct flash_bank *bank,
|
||||
uint8_t *buffer, uint32_t offset, uint32_t count)
|
||||
{
|
||||
struct bl602_flash_bank *priv = bank->driver_priv;
|
||||
const struct bl602_part_info *part_info = priv->part_info;
|
||||
struct target *target = bank->target;
|
||||
struct working_area *bounce_area = NULL;
|
||||
struct working_area *sflash_cfg_area = NULL;
|
||||
|
||||
if (target->state != TARGET_HALTED) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
if (offset + count > priv->dev->size_in_bytes) {
|
||||
LOG_WARNING("Read past end of flash");
|
||||
count = priv->dev->size_in_bytes - offset;
|
||||
}
|
||||
|
||||
int retval = bl602_alloc_sflash_cfg(bank, &sflash_cfg_area);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = bl602_alloc_bounce_buffer(bank, &bounce_area, count);
|
||||
if (retval != ERROR_OK)
|
||||
goto cleanup;
|
||||
|
||||
unsigned int chunk_size = bounce_area->size;
|
||||
|
||||
while (count > 0) {
|
||||
uint32_t read_size = count > chunk_size ? chunk_size : count;
|
||||
LOG_DEBUG("Read %d bytes from offset 0x%" PRIx32, read_size, offset);
|
||||
uint32_t args[] = {
|
||||
sflash_cfg_area->address,
|
||||
0x0, // io_mode
|
||||
false, // continous_read
|
||||
offset,
|
||||
bounce_area->address,
|
||||
read_size,
|
||||
};
|
||||
uint32_t return_value;
|
||||
retval = bl602_call_romapi_func(target, part_info->romapi_sflash_read,
|
||||
args, ARRAY_SIZE(args), &return_value, BL602_DEFAULT_TIMEOUT_MS);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to invoke flash read code on target");
|
||||
break;
|
||||
}
|
||||
if (return_value != 0) {
|
||||
LOG_ERROR("Read flash function returned wrong value: %02X", return_value);
|
||||
retval = ERROR_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
retval = target_read_buffer(target, bounce_area->address, read_size, buffer);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Could not load data from target bounce buffer");
|
||||
break;
|
||||
}
|
||||
|
||||
buffer += read_size;
|
||||
offset += read_size;
|
||||
count -= read_size;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
target_free_working_area(target, bounce_area);
|
||||
target_free_working_area(target, sflash_cfg_area);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int bl602_probe(struct flash_bank *bank)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct bl602_flash_bank *priv = bank->driver_priv;
|
||||
|
||||
if (!target_was_examined(target)) {
|
||||
LOG_ERROR("Target not examined yet");
|
||||
return ERROR_TARGET_NOT_EXAMINED;
|
||||
}
|
||||
|
||||
priv->probed = false;
|
||||
|
||||
for (unsigned int n = 0; n < ARRAY_SIZE(bl602_parts); n++) {
|
||||
const struct bl602_part_info *part_info = &bl602_parts[n];
|
||||
if (priv->series == part_info->series) {
|
||||
if (target->tap->idcode != part_info->idcode) {
|
||||
LOG_ERROR("TAP IDCODE is not valid for selected chip type");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
priv->part_info = part_info;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!priv->part_info) {
|
||||
LOG_ERROR("Cannot identify target as an BL602 family device");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
int retval = bl602_flash_init(bank);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Initialization of flash failed");
|
||||
return retval;
|
||||
}
|
||||
|
||||
uint32_t jedec_id;
|
||||
retval = bl602_flash_read_id(bank, &jedec_id);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Cannot identify flash JEDEC ID");
|
||||
return retval;
|
||||
}
|
||||
|
||||
priv->dev = NULL;
|
||||
for (const struct flash_device *p = flash_devices; p->name ; p++) {
|
||||
if (p->device_id == jedec_id) {
|
||||
priv->dev = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!priv->dev) {
|
||||
LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", jedec_id);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
LOG_INFO("Found flash device '%s' (ID 0x%08" PRIx32 ")", priv->dev->name,
|
||||
priv->dev->device_id);
|
||||
|
||||
// set correct size value
|
||||
bank->size = priv->dev->size_in_bytes;
|
||||
bank->num_sectors = bank->size / priv->dev->sectorsize;
|
||||
bank->write_start_alignment = 8;
|
||||
bank->write_end_alignment = 8;
|
||||
|
||||
bank->sectors = alloc_block_array(0, priv->dev->sectorsize, bank->num_sectors);
|
||||
if (!bank->sectors)
|
||||
return ERROR_FAIL;
|
||||
|
||||
priv->sflash_cfg[SFLASH_CFG_PAGE_PROGRAM_CMD_POS] = priv->dev->pprog_cmd;
|
||||
priv->sflash_cfg[SFLASH_CFG_PAGE_SIZE_POS] = priv->dev->pagesize & 0xFF;
|
||||
priv->sflash_cfg[SFLASH_CFG_PAGE_SIZE_POS + 1] = (priv->dev->pagesize >> 8) & 0xFF;
|
||||
priv->sflash_cfg[SFLASH_CFG_FAST_READ_CMD_POS] = priv->dev->read_cmd;
|
||||
priv->sflash_cfg[SFLASH_CFG_SECTOR_SIZE_POS] = priv->dev->sectorsize / 1024;
|
||||
priv->sflash_cfg[SFLASH_CFG_SECTOR_ERASE_CMD_POS] = priv->dev->erase_cmd;
|
||||
|
||||
priv->probed = true;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int bl602_auto_probe(struct flash_bank *bank)
|
||||
{
|
||||
struct bl602_flash_bank *priv = bank->driver_priv;
|
||||
|
||||
if (priv->probed)
|
||||
return ERROR_OK;
|
||||
|
||||
return bl602_probe(bank);
|
||||
}
|
||||
|
||||
/*
|
||||
* flash bank bl602 <base> 0 0 0 <target#> <chip_type>
|
||||
*/
|
||||
FLASH_BANK_COMMAND_HANDLER(bl602_flash_bank_command)
|
||||
{
|
||||
if (CMD_ARGC < 7)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
struct bl602_flash_bank *priv;
|
||||
priv = malloc(sizeof(struct bl602_flash_bank));
|
||||
priv->probed = false;
|
||||
priv->part_info = NULL;
|
||||
|
||||
if (strcmp(CMD_ARGV[6], "bl602") == 0) {
|
||||
priv->series = BFLB_SERIES_BL602;
|
||||
} else if (strcmp(CMD_ARGV[6], "bl702") == 0) {
|
||||
priv->series = BFLB_SERIES_BL702;
|
||||
} else if (strcmp(CMD_ARGV[6], "bl702l") == 0) {
|
||||
priv->series = BFLB_SERIES_BL702L;
|
||||
} else {
|
||||
LOG_ERROR("Unknown BL602 chip type: %s", CMD_ARGV[6]);
|
||||
free(priv);
|
||||
return ERROR_FLASH_BANK_INVALID;
|
||||
}
|
||||
|
||||
// initialize default sflash_cfg fields
|
||||
priv->sflash_cfg[SFLASH_CFG_JEDEC_ID_CMD_POS] = 0x9F;
|
||||
priv->sflash_cfg[SFLASH_CFG_JEDEC_ID_CMD_DMY_CLK_POS] = 0x0;
|
||||
priv->sflash_cfg[SFLASH_CFG_TIME_PAGE_PGM_POS] = 200;
|
||||
priv->sflash_cfg[SFLASH_CFG_WRITE_ENABLE_CMD_POS] = 0x06;
|
||||
priv->sflash_cfg[SFLASH_CFG_WRITE_ENABLE_INDEX_POS] = 0;
|
||||
priv->sflash_cfg[SFLASH_CFG_WRITE_ENABLE_READ_REG_LEN_POS] = 1;
|
||||
priv->sflash_cfg[SFLASH_CFG_WRITE_ENABLE_BIT_POS] = 1;
|
||||
priv->sflash_cfg[SFLASH_CFG_READ_STATUS_REG1_POS] = 0x05;
|
||||
priv->sflash_cfg[SFLASH_CFG_BUSY_INDEX_POS] = 0;
|
||||
priv->sflash_cfg[SFLASH_CFG_BUSY_READ_REG_LEN_POS] = 1;
|
||||
priv->sflash_cfg[SFLASH_CFG_BUSY_BIT_POS] = 0;
|
||||
priv->sflash_cfg[SFLASH_CFG_TIME_ERASE_SECTOR_POS] = 1000 & 0xFF;
|
||||
priv->sflash_cfg[SFLASH_CFG_TIME_ERASE_SECTOR_POS + 1] = (1000 >> 8) & 0xFF;
|
||||
|
||||
// set up driver_priv
|
||||
bank->driver_priv = priv;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
const struct flash_driver bl602_flash = {
|
||||
.name = "bl602",
|
||||
.flash_bank_command = bl602_flash_bank_command,
|
||||
.erase = bl602_erase,
|
||||
.write = bl602_write,
|
||||
.read = bl602_read,
|
||||
.probe = bl602_probe,
|
||||
.auto_probe = bl602_auto_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.free_driver_priv = default_flash_free_driver_priv
|
||||
};
|
||||
@@ -251,6 +251,7 @@ extern const struct flash_driver ath79_flash;
|
||||
extern const struct flash_driver atsame5_flash;
|
||||
extern const struct flash_driver atsamv_flash;
|
||||
extern const struct flash_driver avr_flash;
|
||||
extern const struct flash_driver bl602_flash;
|
||||
extern const struct flash_driver bluenrgx_flash;
|
||||
extern const struct flash_driver cc26xx_flash;
|
||||
extern const struct flash_driver cc3220sf_flash;
|
||||
|
||||
@@ -30,6 +30,7 @@ static const struct flash_driver * const flash_drivers[] = {
|
||||
&atsame5_flash,
|
||||
&atsamv_flash,
|
||||
&avr_flash,
|
||||
&bl602_flash,
|
||||
&bluenrgx_flash,
|
||||
&cc26xx_flash,
|
||||
&cc3220sf_flash,
|
||||
|
||||
Reference in New Issue
Block a user