Files
openocd/src/flash/nor/w600.c
Antonio Borneo 382148e4dd openocd: fix SPDX tag format for files .c
With the old checkpatch we cannot use the correct format for the
SPDX tags in the file .c, in fact the C99 comments are not allowed
and we had to use the block comment.

With the new checkpatch, let's switch to the correct SPDX format.

Change created automatically through the command:
	sed -i \
	's,^/\* *\(SPDX-License-Identifier: .*[^ ]\) *\*/$,// \1,' \
	$(find src/ contrib/ -name \*.c)

Change-Id: I6da16506baa7af718947562505dd49606d124171
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/7153
Tested-by: jenkins
2022-09-18 08:22:01 +00:00

379 lines
9.2 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/***************************************************************************
* Copyright (C) 2018 by Simon Qian *
* SimonQian@SimonQian.com *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include <helper/binarybuffer.h>
#include <target/algorithm.h>
#include <target/armv7m.h>
#define W600_FLASH_SECSIZE 0x1000
#define W600_FLASH_PAGESIZE 0x100
#define W600_FLASH_BASE 0x08000000
#define W600_FLASH_PROTECT_SIZE 0x2000
/* w600 register locations */
#define QFLASH_REGBASE 0X40002000
#define QFLASH_CMD_INFO (QFLASH_REGBASE + 0)
#define QFLASH_CMD_START (QFLASH_REGBASE + 4)
#define QFLASH_BUFFER (QFLASH_REGBASE + 0X200)
#define QFLASH_CMD_READ (1ul << 14)
#define QFLASH_CMD_WRITE 0
#define QFLASH_CMD_ADDR (1ul << 31)
#define QFLASH_CMD_DATA (1ul << 15)
#define QFLASH_CMD_DATALEN(len) (((len) & 0x3FF) << 16)
#define QFLASH_CMD_RDID (QFLASH_CMD_READ | 0x9F)
#define QFLASH_CMD_WREN (QFLASH_CMD_WRITE | 0x06)
#define QFLASH_CMD_WRDI (QFLASH_CMD_WRITE | 0x04)
#define QFLASH_CMD_SE (QFLASH_CMD_WRITE | QFLASH_CMD_ADDR | (1ul << 11) | 0x20)
#define QFLASH_CMD_PP (QFLASH_CMD_WRITE | QFLASH_CMD_ADDR | (1ul << 12) | 0x02)
#define QFLASH_START (1ul << 28)
#define QFLASH_ADDR(addr) (((addr) & 0xFFFFF) << 8)
#define QFLASH_CRM(crm) (((crm) & 0xFF) << 0)
struct w600_flash_param {
uint8_t id;
uint8_t se_delay;
uint8_t pp_delay;
};
static const struct w600_flash_param w600_param[] = {
{
.id = 0x85,
.se_delay = 8,
.pp_delay = 2,
},
{
.id = 0x1C,
.se_delay = 50,
.pp_delay = 1,
},
{
.id = 0xC8,
.se_delay = 45,
.pp_delay = 1,
},
{
.id = 0x0B,
.se_delay = 60,
.pp_delay = 1,
},
{
.id = 0x68,
.se_delay = 50,
.pp_delay = 1,
},
};
struct w600_flash_bank {
bool probed;
uint32_t id;
const struct w600_flash_param *param;
uint32_t register_base;
uint32_t user_bank_size;
};
/* flash bank w600 <base> <size> 0 0 <target#>
*/
FLASH_BANK_COMMAND_HANDLER(w600_flash_bank_command)
{
struct w600_flash_bank *w600_info;
if (CMD_ARGC < 6)
return ERROR_COMMAND_SYNTAX_ERROR;
w600_info = malloc(sizeof(struct w600_flash_bank));
bank->driver_priv = w600_info;
w600_info->probed = false;
w600_info->register_base = QFLASH_REGBASE;
w600_info->user_bank_size = bank->size;
return ERROR_OK;
}
static int w600_get_delay(struct flash_bank *bank, uint32_t cmd)
{
struct w600_flash_bank *w600_info = bank->driver_priv;
if (!w600_info->param)
return 0;
switch (cmd) {
case QFLASH_CMD_SE:
return w600_info->param->se_delay;
case QFLASH_CMD_PP:
return w600_info->param->pp_delay;
default:
return 0;
}
}
static int w600_start_do(struct flash_bank *bank, uint32_t cmd, uint32_t addr,
uint32_t len, int timeout)
{
struct target *target = bank->target;
if (len > 0)
cmd |= QFLASH_CMD_DATALEN(len - 1) | QFLASH_CMD_DATA;
LOG_DEBUG("WRITE CMD: 0x%08" PRIx32 "", cmd);
int retval = target_write_u32(target, QFLASH_CMD_INFO, cmd);
if (retval != ERROR_OK)
return retval;
addr |= QFLASH_START;
LOG_DEBUG("WRITE START: 0x%08" PRIx32 "", addr);
retval = target_write_u32(target, QFLASH_CMD_START, addr);
if (retval != ERROR_OK)
return retval;
LOG_DEBUG("DELAY %dms", timeout);
alive_sleep(timeout);
int retry = 100;
uint32_t status;
for (;;) {
LOG_DEBUG("READ START...");
retval = target_read_u32(target, QFLASH_CMD_START, &status);
if (retval == ERROR_OK)
LOG_DEBUG("READ START: 0x%08" PRIx32 "", status);
else
LOG_DEBUG("READ START FAILED");
if ((retval != ERROR_OK) || (status & QFLASH_START)) {
if (retry-- <= 0) {
LOG_ERROR("timed out waiting for flash");
return ERROR_FAIL;
}
continue;
}
break;
}
return retval;
}
static int w600_write_enable(struct flash_bank *bank)
{
return w600_start_do(bank, QFLASH_CMD_WREN, 0, 0, 0);
}
static int w600_write_disable(struct flash_bank *bank)
{
return w600_start_do(bank, QFLASH_CMD_WRDI, 0, 0, 0);
}
static int w600_start(struct flash_bank *bank, uint32_t cmd, uint32_t addr,
uint32_t len)
{
int retval = w600_write_enable(bank);
if (retval != ERROR_OK)
return retval;
retval = w600_start_do(bank, cmd, addr, len, w600_get_delay(bank, cmd));
if (retval != ERROR_OK)
return retval;
retval = w600_write_disable(bank);
if (retval != ERROR_OK)
return retval;
return retval;
}
static int w600_erase(struct flash_bank *bank, unsigned int first,
unsigned int last)
{
int retval = ERROR_OK;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (first < W600_FLASH_PROTECT_SIZE / W600_FLASH_SECSIZE) {
LOG_ERROR("can not erase protected area");
return ERROR_FAIL;
}
for (unsigned int i = first; i <= last; i++) {
retval = w600_start(bank, QFLASH_CMD_SE,
QFLASH_ADDR(bank->sectors[i].offset), 0);
if (retval != ERROR_OK)
break;
}
return retval;
}
static int w600_write(struct flash_bank *bank, const uint8_t *buffer,
uint32_t offset, uint32_t count)
{
struct target *target = bank->target;
int retval = ERROR_OK;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if ((offset % W600_FLASH_PAGESIZE) != 0) {
LOG_WARNING("offset 0x%" PRIx32 " breaks required %d-byte alignment",
offset, W600_FLASH_PAGESIZE);
return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
}
if ((count % W600_FLASH_PAGESIZE) != 0) {
LOG_WARNING("count 0x%" PRIx32 " breaks required %d-byte alignment",
offset, W600_FLASH_PAGESIZE);
return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
}
while (count > 0) {
retval = target_write_buffer(target, QFLASH_BUFFER, W600_FLASH_PAGESIZE, buffer);
if (retval != ERROR_OK)
break;
retval = w600_start(bank, QFLASH_CMD_PP, QFLASH_ADDR(offset),
W600_FLASH_PAGESIZE);
if (retval != ERROR_OK)
break;
count -= W600_FLASH_PAGESIZE;
offset += W600_FLASH_PAGESIZE;
buffer += W600_FLASH_PAGESIZE;
}
return retval;
}
static int w600_get_flash_id(struct flash_bank *bank, uint32_t *flash_id)
{
struct target *target = bank->target;
int retval = w600_start(bank, QFLASH_CMD_RDID, 0, 4);
if (retval != ERROR_OK)
return retval;
return target_read_u32(target, QFLASH_BUFFER, flash_id);
}
static int w600_probe(struct flash_bank *bank)
{
struct w600_flash_bank *w600_info = bank->driver_priv;
uint32_t flash_size;
uint32_t flash_id;
size_t i;
w600_info->probed = false;
/* read stm32 device id register */
int retval = w600_get_flash_id(bank, &flash_id);
if (retval != ERROR_OK)
return retval;
LOG_INFO("flash_id id = 0x%08" PRIx32 "", flash_id);
w600_info->id = flash_id;
w600_info->param = NULL;
for (i = 0; i < ARRAY_SIZE(w600_param); i++) {
if (w600_param[i].id == (flash_id & 0xFF)) {
w600_info->param = &w600_param[i];
break;
}
}
if (!w600_info->param) {
LOG_ERROR("flash_id not supported for w600");
return ERROR_FAIL;
}
/* if the user sets the size manually then ignore the probed value
* this allows us to work around devices that have a invalid flash size register value */
if (w600_info->user_bank_size) {
LOG_INFO("ignoring flash probed value, using configured bank size");
flash_size = w600_info->user_bank_size;
} else {
flash_size = ((flash_id & 0xFFFFFF) >> 16) & 0xFF;
if ((flash_size != 0x14) && (flash_size != 0x13)) {
LOG_ERROR("w600 flash size failed, probe inaccurate");
return ERROR_FAIL;
}
flash_size = 1 << flash_size;
}
LOG_INFO("flash size = %" PRIu32 " KiB", flash_size / 1024);
/* calculate numbers of pages */
size_t num_pages = flash_size / W600_FLASH_SECSIZE;
/* check that calculation result makes sense */
assert(num_pages > 0);
free(bank->sectors);
bank->sectors = NULL;
bank->base = W600_FLASH_BASE;
bank->size = num_pages * W600_FLASH_SECSIZE;
bank->num_sectors = num_pages;
bank->write_start_alignment = W600_FLASH_PAGESIZE;
bank->write_end_alignment = W600_FLASH_PAGESIZE;
bank->sectors = malloc(sizeof(struct flash_sector) * num_pages);
for (i = 0; i < num_pages; i++) {
bank->sectors[i].offset = i * W600_FLASH_SECSIZE;
bank->sectors[i].size = W600_FLASH_SECSIZE;
bank->sectors[i].is_erased = -1;
/* offset 0 to W600_FLASH_PROTECT_SIZE should be protected */
bank->sectors[i].is_protected = (i < W600_FLASH_PROTECT_SIZE / W600_FLASH_SECSIZE);
}
w600_info->probed = true;
return ERROR_OK;
}
static int w600_auto_probe(struct flash_bank *bank)
{
struct w600_flash_bank *w600_info = bank->driver_priv;
if (w600_info->probed)
return ERROR_OK;
return w600_probe(bank);
}
static int get_w600_info(struct flash_bank *bank, struct command_invocation *cmd)
{
uint32_t flash_id;
/* read w600 device id register */
int retval = w600_get_flash_id(bank, &flash_id);
if (retval != ERROR_OK)
return retval;
command_print_sameline(cmd, "w600 : 0x%08" PRIx32 "", flash_id);
return ERROR_OK;
}
const struct flash_driver w600_flash = {
.name = "w600",
.flash_bank_command = w600_flash_bank_command,
.erase = w600_erase,
.write = w600_write,
.read = default_flash_read,
.probe = w600_probe,
.auto_probe = w600_auto_probe,
.erase_check = default_flash_blank_check,
.info = get_w600_info,
.free_driver_priv = default_flash_free_driver_priv,
};