openocd/src/flash/nor/cc3220sf.c

488 lines
13 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later */
/***************************************************************************
* Copyright (C) 2017 by Texas Instruments, Inc. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include "cc3220sf.h"
#include <helper/binarybuffer.h>
#include <helper/time_support.h>
#include <target/algorithm.h>
#include <target/armv7m.h>
#define FLASH_TIMEOUT 5000
struct cc3220sf_bank {
bool probed;
struct armv7m_algorithm armv7m_info;
};
static int cc3220sf_mass_erase(struct flash_bank *bank)
{
struct target *target = bank->target;
bool done;
long long start_ms;
long long elapsed_ms;
uint32_t value;
int retval = ERROR_OK;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/* Set starting address to erase to zero */
retval = target_write_u32(target, FMA_REGISTER_ADDR, 0);
if (retval != ERROR_OK)
return retval;
/* Write the MERASE bit of the FMC register */
retval = target_write_u32(target, FMC_REGISTER_ADDR, FMC_MERASE_VALUE);
if (retval != ERROR_OK)
return retval;
/* Poll the MERASE bit until the mass erase is complete */
done = false;
start_ms = timeval_ms();
while (!done) {
retval = target_read_u32(target, FMC_REGISTER_ADDR, &value);
if (retval != ERROR_OK)
return retval;
if ((value & FMC_MERASE_BIT) == 0) {
/* Bit clears when mass erase is finished */
done = true;
} else {
elapsed_ms = timeval_ms() - start_ms;
if (elapsed_ms > 500)
keep_alive();
if (elapsed_ms > FLASH_TIMEOUT)
break;
}
}
if (!done) {
/* Mass erase timed out waiting for confirmation */
return ERROR_FAIL;
}
return retval;
}
FLASH_BANK_COMMAND_HANDLER(cc3220sf_flash_bank_command)
{
struct cc3220sf_bank *cc3220sf_bank;
if (CMD_ARGC < 6)
return ERROR_COMMAND_SYNTAX_ERROR;
cc3220sf_bank = malloc(sizeof(struct cc3220sf_bank));
if (!cc3220sf_bank)
return ERROR_FAIL;
/* Initialize private flash information */
cc3220sf_bank->probed = false;
/* Finish initialization of flash bank */
bank->driver_priv = cc3220sf_bank;
bank->next = NULL;
return ERROR_OK;
}
static int cc3220sf_erase(struct flash_bank *bank, unsigned int first,
unsigned int last)
{
struct target *target = bank->target;
bool done;
long long start_ms;
long long elapsed_ms;
uint32_t address;
uint32_t value;
int retval = ERROR_OK;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/* Do a mass erase if user requested all sectors of flash */
if ((first == 0) && (last == (bank->num_sectors - 1))) {
/* Request mass erase of flash */
return cc3220sf_mass_erase(bank);
}
/* Erase requested sectors one by one */
for (unsigned int i = first; i <= last; i++) {
/* Determine address of sector to erase */
address = FLASH_BASE_ADDR + i * FLASH_SECTOR_SIZE;
/* Set starting address to erase */
retval = target_write_u32(target, FMA_REGISTER_ADDR, address);
if (retval != ERROR_OK)
return retval;
/* Write the ERASE bit of the FMC register */
retval = target_write_u32(target, FMC_REGISTER_ADDR, FMC_ERASE_VALUE);
if (retval != ERROR_OK)
return retval;
/* Poll the ERASE bit until the erase is complete */
done = false;
start_ms = timeval_ms();
while (!done) {
retval = target_read_u32(target, FMC_REGISTER_ADDR, &value);
if (retval != ERROR_OK)
return retval;
if ((value & FMC_ERASE_BIT) == 0) {
/* Bit clears when mass erase is finished */
done = true;
} else {
elapsed_ms = timeval_ms() - start_ms;
if (elapsed_ms > 500)
keep_alive();
if (elapsed_ms > FLASH_TIMEOUT)
break;
}
}
if (!done) {
/* Sector erase timed out waiting for confirmation */
return ERROR_FAIL;
}
}
return retval;
}
static int cc3220sf_write(struct flash_bank *bank, const uint8_t *buffer,
uint32_t offset, uint32_t count)
{
struct target *target = bank->target;
struct cc3220sf_bank *cc3220sf_bank = bank->driver_priv;
struct working_area *algo_working_area;
struct working_area *buffer_working_area;
struct reg_param reg_params[3];
uint32_t algo_base_address;
uint32_t algo_buffer_address;
uint32_t algo_buffer_size;
uint32_t address;
uint32_t remaining;
uint32_t words;
uint32_t result;
int retval = ERROR_OK;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/* Obtain working area to use for flash helper algorithm */
retval = target_alloc_working_area(target, sizeof(cc3220sf_algo),
&algo_working_area);
if (retval != ERROR_OK)
return retval;
/* Obtain working area to use for flash buffer */
retval = target_alloc_working_area(target,
target_get_working_area_avail(target), &buffer_working_area);
if (retval != ERROR_OK) {
target_free_working_area(target, algo_working_area);
return retval;
}
algo_base_address = algo_working_area->address;
algo_buffer_address = buffer_working_area->address;
algo_buffer_size = buffer_working_area->size;
/* Make sure buffer size is a multiple of 32 word (0x80 byte) chunks */
/* (algo runs more efficiently if it operates on 32 words at a time) */
if (algo_buffer_size > 0x80)
algo_buffer_size &= ~0x7f;
/* Write flash helper algorithm into target memory */
retval = target_write_buffer(target, algo_base_address,
sizeof(cc3220sf_algo), cc3220sf_algo);
if (retval != ERROR_OK) {
target_free_working_area(target, algo_working_area);
target_free_working_area(target, buffer_working_area);
return retval;
}
/* Initialize the ARMv7m specific info to run the algorithm */
cc3220sf_bank->armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
cc3220sf_bank->armv7m_info.core_mode = ARM_MODE_THREAD;
/* Initialize register params for flash helper algorithm */
init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
init_reg_param(&reg_params[2], "r2", 32, PARAM_IN_OUT);
/* Prepare to write to flash */
address = FLASH_BASE_ADDR + offset;
remaining = count;
/* The flash hardware can only write complete words to flash. If
* an unaligned address is passed in, we must do a read-modify-write
* on a word with enough bytes to align the rest of the buffer. And
* if less than a whole word remains at the end, we must also do a
* read-modify-write on a final word to finish up.
*/
/* Do one word write to align address on 32-bit boundary if needed */
if (0 != (address & 0x3)) {
uint8_t head[4];
/* Get starting offset for data to write (will be 1 to 3) */
uint32_t head_offset = address & 0x03;
/* Get the aligned address to write this first word to */
uint32_t head_address = address & 0xfffffffc;
/* Retrieve what is already in flash at the head address */
retval = target_read_buffer(target, head_address, sizeof(head), head);
if (retval == ERROR_OK) {
/* Substitute in the new data to write */
while ((remaining > 0) && (head_offset < 4)) {
head[head_offset] = *buffer;
head_offset++;
address++;
buffer++;
remaining--;
}
}
if (retval == ERROR_OK) {
/* Helper parameters are passed in registers R0-R2 */
/* Set start of data buffer, address to write to, and word count */
buf_set_u32(reg_params[0].value, 0, 32, algo_buffer_address);
buf_set_u32(reg_params[1].value, 0, 32, head_address);
buf_set_u32(reg_params[2].value, 0, 32, 1);
/* Write head value into buffer to flash */
retval = target_write_buffer(target, algo_buffer_address,
sizeof(head), head);
}
if (retval == ERROR_OK) {
/* Execute the flash helper algorithm */
retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
algo_base_address, 0, FLASH_TIMEOUT,
&cc3220sf_bank->armv7m_info);
if (retval != ERROR_OK)
LOG_ERROR("cc3220sf: Flash algorithm failed to run");
/* Check that the head value was written to flash */
result = buf_get_u32(reg_params[2].value, 0, 32);
if (result != 0) {
retval = ERROR_FAIL;
LOG_ERROR("cc3220sf: Flash operation failed");
}
}
}
/* Check if there's data at end of buffer that isn't a full word */
uint32_t tail_count = remaining & 0x03;
/* Adjust remaining so it is a multiple of whole words */
remaining -= tail_count;
while ((retval == ERROR_OK) && (remaining > 0)) {
/* Set start of data buffer and address to write to */
buf_set_u32(reg_params[0].value, 0, 32, algo_buffer_address);
buf_set_u32(reg_params[1].value, 0, 32, address);
/* Download data to write into memory buffer */
if (remaining >= algo_buffer_size) {
/* Fill up buffer with data to flash */
retval = target_write_buffer(target, algo_buffer_address,
algo_buffer_size, buffer);
if (retval != ERROR_OK)
break;
/* Count to write is in 32-bit words */
words = algo_buffer_size / 4;
/* Bump variables to next data */
address += algo_buffer_size;
buffer += algo_buffer_size;
remaining -= algo_buffer_size;
} else {
/* Fill buffer with what's left of the data */
retval = target_write_buffer(target, algo_buffer_address,
remaining, buffer);
if (retval != ERROR_OK)
break;
/* Calculate the final word count to write */
words = remaining / 4;
if (0 != (remaining % 4))
words++;
/* Bump variables to any final data */
address += remaining;
buffer += remaining;
remaining = 0;
}
/* Set number of words to write */
buf_set_u32(reg_params[2].value, 0, 32, words);
/* Execute the flash helper algorithm */
retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
algo_base_address, 0, FLASH_TIMEOUT,
&cc3220sf_bank->armv7m_info);
if (retval != ERROR_OK) {
LOG_ERROR("cc3220sf: Flash algorithm failed to run");
break;
}
/* Check that all words were written to flash */
result = buf_get_u32(reg_params[2].value, 0, 32);
if (result != 0) {
retval = ERROR_FAIL;
LOG_ERROR("cc3220sf: Flash operation failed");
break;
}
keep_alive();
}
/* Do one word write for any final bytes less than a full word */
if ((retval == ERROR_OK) && (tail_count != 0)) {
uint8_t tail[4];
/* Set starting byte offset for data to write */
uint32_t tail_offset = 0;
/* Retrieve what is already in flash at the tail address */
retval = target_read_buffer(target, address, sizeof(tail), tail);
if (retval == ERROR_OK) {
/* Substitute in the new data to write */
while (tail_count > 0) {
tail[tail_offset] = *buffer;
tail_offset++;
buffer++;
tail_count--;
}
}
if (retval == ERROR_OK) {
/* Set start of data buffer, address to write to, and word count */
buf_set_u32(reg_params[0].value, 0, 32, algo_buffer_address);
buf_set_u32(reg_params[1].value, 0, 32, address);
buf_set_u32(reg_params[2].value, 0, 32, 1);
/* Write tail value into buffer to flash */
retval = target_write_buffer(target, algo_buffer_address,
sizeof(tail), tail);
}
if (retval == ERROR_OK) {
/* Execute the flash helper algorithm */
retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
algo_base_address, 0, FLASH_TIMEOUT,
&cc3220sf_bank->armv7m_info);
if (retval != ERROR_OK)
LOG_ERROR("cc3220sf: Flash algorithm failed to run");
/* Check that the tail was written to flash */
result = buf_get_u32(reg_params[2].value, 0, 32);
if (result != 0) {
retval = ERROR_FAIL;
LOG_ERROR("cc3220sf: Flash operation failed");
}
}
}
/* Free resources */
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
destroy_reg_param(&reg_params[2]);
target_free_working_area(target, algo_working_area);
target_free_working_area(target, buffer_working_area);
return retval;
}
static int cc3220sf_probe(struct flash_bank *bank)
{
struct cc3220sf_bank *cc3220sf_bank = bank->driver_priv;
uint32_t base;
uint32_t size;
unsigned int num_sectors;
base = FLASH_BASE_ADDR;
size = FLASH_NUM_SECTORS * FLASH_SECTOR_SIZE;
num_sectors = FLASH_NUM_SECTORS;
free(bank->sectors);
bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
if (!bank->sectors)
return ERROR_FAIL;
bank->base = base;
bank->size = size;
bank->write_start_alignment = 0;
bank->write_end_alignment = 0;
bank->num_sectors = num_sectors;
for (unsigned int i = 0; i < num_sectors; i++) {
bank->sectors[i].offset = i * FLASH_SECTOR_SIZE;
bank->sectors[i].size = FLASH_SECTOR_SIZE;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 0;
}
/* We've successfully recorded the stats on this flash bank */
cc3220sf_bank->probed = true;
/* If we fall through to here, then all went well */
return ERROR_OK;
}
static int cc3220sf_auto_probe(struct flash_bank *bank)
{
struct cc3220sf_bank *cc3220sf_bank = bank->driver_priv;
int retval = ERROR_OK;
if (!cc3220sf_bank->probed)
retval = cc3220sf_probe(bank);
return retval;
}
static int cc3220sf_info(struct flash_bank *bank, struct command_invocation *cmd)
{
command_print_sameline(cmd, "CC3220SF with 1MB internal flash\n");
return ERROR_OK;
}
const struct flash_driver cc3220sf_flash = {
.name = "cc3220sf",
.flash_bank_command = cc3220sf_flash_bank_command,
.erase = cc3220sf_erase,
.write = cc3220sf_write,
.read = default_flash_read,
.probe = cc3220sf_probe,
.auto_probe = cc3220sf_auto_probe,
.erase_check = default_flash_blank_check,
.info = cc3220sf_info,
.free_driver_priv = default_flash_free_driver_priv,
};