aboutsummaryrefslogtreecommitdiff
path: root/src/flash
diff options
context:
space:
mode:
authorTim Newsome <tim@sifive.com>2018-06-11 12:08:08 -0700
committerTim Newsome <tim@sifive.com>2018-06-11 12:08:08 -0700
commit17a0523736e25b352f82d62e1b16bd1c2d87646b (patch)
tree9969e4575f44f9f9f0cd3b64e33a2679dc730ed4 /src/flash
parent6766fa1dda84e5b9243a2ab60bca77de92ea178d (diff)
parent06123153f38280608b1e92dcb766b31ade7e4668 (diff)
downloadriscv-openocd-17a0523736e25b352f82d62e1b16bd1c2d87646b.zip
riscv-openocd-17a0523736e25b352f82d62e1b16bd1c2d87646b.tar.gz
riscv-openocd-17a0523736e25b352f82d62e1b16bd1c2d87646b.tar.bz2
Merge branch 'master' into from_upstream
Diffstat (limited to 'src/flash')
-rw-r--r--src/flash/nor/Makefile.am3
-rw-r--r--src/flash/nor/cc3220sf.c529
-rw-r--r--src/flash/nor/cc3220sf.h45
-rw-r--r--src/flash/nor/core.c12
-rw-r--r--src/flash/nor/drivers.c8
-rw-r--r--src/flash/nor/psoc5lp.c1574
-rw-r--r--src/flash/nor/virtual.c5
7 files changed, 2174 insertions, 2 deletions
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index 7121412..db936b8 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -19,6 +19,7 @@ NOR_DRIVERS = \
%D%/atsamv.c \
%D%/avrf.c \
%D%/bluenrg-x.c \
+ %D%/cc3220sf.c \
%D%/cfi.c \
%D%/dsp5680xx_flash.c \
%D%/efm32.c \
@@ -43,6 +44,7 @@ NOR_DRIVERS = \
%D%/ocl.c \
%D%/pic32mx.c \
%D%/psoc4.c \
+ %D%/psoc5lp.c \
%D%/psoc6.c \
%D%/sim3x.c \
%D%/spi.c \
@@ -64,6 +66,7 @@ NOR_DRIVERS = \
NORHEADERS = \
%D%/core.h \
+ %D%/cc3220sf.h \
%D%/cfi.h \
%D%/driver.h \
%D%/imp.h \
diff --git a/src/flash/nor/cc3220sf.c b/src/flash/nor/cc3220sf.c
new file mode 100644
index 0000000..af45743
--- /dev/null
+++ b/src/flash/nor/cc3220sf.c
@@ -0,0 +1,529 @@
+/***************************************************************************
+ * Copyright (C) 2017 by Texas Instruments, Inc. *
+ * *
+ * 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 "imp.h"
+#include "cc3220sf.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_HALTED != target->state) {
+ 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 (ERROR_OK != retval)
+ return retval;
+
+ /* Write the MERASE bit of the FMC register */
+ retval = target_write_u32(target, FMC_REGISTER_ADDR, FMC_MERASE_VALUE);
+ if (ERROR_OK != retval)
+ 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 (ERROR_OK != retval)
+ 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 (NULL == 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, int first, 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_HALTED != target->state) {
+ 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 (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 (ERROR_OK != retval)
+ return retval;
+
+ /* Write the ERASE bit of the FMC register */
+ retval = target_write_u32(target, FMC_REGISTER_ADDR, FMC_ERASE_VALUE);
+ if (ERROR_OK != retval)
+ 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 (ERROR_OK != retval)
+ 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_protect(struct flash_bank *bank, int set, int first,
+ int last)
+{
+ return ERROR_OK;
+}
+
+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_HALTED != target->state) {
+ 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 (ERROR_OK != retval)
+ 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 (ERROR_OK != retval) {
+ 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 (ERROR_OK != retval) {
+ 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 (ERROR_OK == retval) {
+ /* Substitute in the new data to write */
+ while ((remaining > 0) && (head_offset < 4)) {
+ head[head_offset] = *buffer;
+ head_offset++;
+ address++;
+ buffer++;
+ remaining--;
+ }
+ }
+
+ if (ERROR_OK == retval) {
+ /* 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 (ERROR_OK == retval) {
+ /* 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 (ERROR_OK != retval)
+ 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 (0 != result) {
+ 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 ((ERROR_OK == retval) && (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 (ERROR_OK != retval)
+ 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 (ERROR_OK != retval)
+ 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 (ERROR_OK != retval) {
+ 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 (0 != result) {
+ retval = ERROR_FAIL;
+ LOG_ERROR("cc3220sf: Flash operation failed");
+ break;
+ }
+ }
+
+ /* Do one word write for any final bytes less than a full word */
+ if ((ERROR_OK == retval) && (0 != tail_count)) {
+ 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 (ERROR_OK == retval) {
+ /* Substitute in the new data to write */
+ while (tail_count > 0) {
+ tail[tail_offset] = *buffer;
+ tail_offset++;
+ buffer++;
+ tail_count--;
+ }
+ }
+
+ if (ERROR_OK == retval) {
+ /* 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 (ERROR_OK == retval) {
+ /* 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 (ERROR_OK != retval)
+ 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 (0 != result) {
+ 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;
+ int num_sectors;
+ int bank_id;
+
+ bank_id = bank->bank_number;
+
+ if (0 == bank_id) {
+ base = FLASH_BASE_ADDR;
+ size = FLASH_NUM_SECTORS * FLASH_SECTOR_SIZE;
+ num_sectors = FLASH_NUM_SECTORS;
+ } else {
+ /* Invalid bank number somehow */
+ return ERROR_FAIL;
+ }
+
+ if (NULL != bank->sectors) {
+ free(bank->sectors);
+ bank->sectors = NULL;
+ }
+
+ bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
+ if (NULL == 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 (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 (0 != bank->bank_number) {
+ /* Invalid bank number somehow */
+ return ERROR_FAIL;
+ }
+
+ if (!cc3220sf_bank->probed)
+ retval = cc3220sf_probe(bank);
+
+ return retval;
+}
+
+static int cc3220sf_protect_check(struct flash_bank *bank)
+{
+ return ERROR_OK;
+}
+
+static int cc3220sf_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+ int printed;
+
+ printed = snprintf(buf, buf_size, "CC3220SF with 1MB internal flash\n");
+
+ if (printed >= buf_size)
+ return ERROR_BUF_TOO_SMALL;
+
+ return ERROR_OK;
+}
+
+struct flash_driver cc3220sf_flash = {
+ .name = "cc3220sf",
+ .flash_bank_command = cc3220sf_flash_bank_command,
+ .erase = cc3220sf_erase,
+ .protect = cc3220sf_protect,
+ .write = cc3220sf_write,
+ .read = default_flash_read,
+ .probe = cc3220sf_probe,
+ .auto_probe = cc3220sf_auto_probe,
+ .erase_check = default_flash_blank_check,
+ .protect_check = cc3220sf_protect_check,
+ .info = cc3220sf_info,
+ .free_driver_priv = default_flash_free_driver_priv,
+};
diff --git a/src/flash/nor/cc3220sf.h b/src/flash/nor/cc3220sf.h
new file mode 100644
index 0000000..36c17be
--- /dev/null
+++ b/src/flash/nor/cc3220sf.h
@@ -0,0 +1,45 @@
+/***************************************************************************
+ * Copyright (C) 2017 by Texas Instruments, Inc. *
+ * *
+ * 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/>. *
+ ***************************************************************************/
+
+#ifndef OPENOCD_FLASH_NOR_CC3220SF_H
+#define OPENOCD_FLASH_NOR_CC3220SF_H
+
+/* CC3220SF device types */
+#define CC3220_NO_TYPE 0 /* Device type not determined yet */
+#define CC3220_OTHER 1 /* CC3220 variant without flash */
+#define CC3220SF 2 /* CC3220SF variant with flash */
+
+/* Flash parameters */
+#define FLASH_BASE_ADDR 0x01000000
+#define FLASH_SECTOR_SIZE 2048
+#define FLASH_NUM_SECTORS 512
+
+/* CC2200SF flash registers */
+#define FMA_REGISTER_ADDR 0x400FD000
+#define FMC_REGISTER_ADDR 0x400FD008
+#define FMC_DEFAULT_VALUE 0xA4420000
+#define FMC_ERASE_BIT 0x00000002
+#define FMC_MERASE_BIT 0x00000004
+#define FMC_ERASE_VALUE (FMC_DEFAULT_VALUE | FMC_ERASE_BIT)
+#define FMC_MERASE_VALUE (FMC_DEFAULT_VALUE | FMC_MERASE_BIT)
+
+/* Flash helper algorithm for CC3220SF */
+const uint8_t cc3220sf_algo[] = {
+#include "../../../contrib/loaders/flash/cc3220sf/cc3220sf.inc"
+};
+
+#endif /* OPENOCD_FLASH_NOR_CC3220SF_H */
diff --git a/src/flash/nor/core.c b/src/flash/nor/core.c
index f05c68b..4941281 100644
--- a/src/flash/nor/core.c
+++ b/src/flash/nor/core.c
@@ -188,9 +188,17 @@ void flash_free_all_banks(void)
else
LOG_WARNING("Flash driver of %s does not support free_driver_priv()", bank->name);
+ /* For 'virtual' flash driver bank->sectors and bank->prot_blocks pointers are copied from
+ * master flash_bank structure. They point to memory locations allocated by master flash driver
+ * so master driver is responsible for releasing them.
+ * Avoid UB caused by double-free memory corruption if flash bank is 'virtual'. */
+
+ if (strcmp(bank->driver->name, "virtual") != 0) {
+ free(bank->sectors);
+ free(bank->prot_blocks);
+ }
+
free(bank->name);
- free(bank->sectors);
- free(bank->prot_blocks);
free(bank);
bank = next;
}
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index 3d6dab2..a094c20 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -32,6 +32,7 @@ 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 cc3220sf_flash;
extern struct flash_driver cfi_flash;
extern struct flash_driver dsp5680xx_flash;
extern struct flash_driver efm32_flash;
@@ -56,6 +57,9 @@ extern struct flash_driver numicro_flash;
extern struct flash_driver ocl_flash;
extern struct flash_driver pic32mx_flash;
extern struct flash_driver psoc4_flash;
+extern struct flash_driver psoc5lp_flash;
+extern struct flash_driver psoc5lp_eeprom_flash;
+extern struct flash_driver psoc5lp_nvl_flash;
extern struct flash_driver psoc6_flash;
extern struct flash_driver sim3x_flash;
extern struct flash_driver stellaris_flash;
@@ -91,6 +95,7 @@ static struct flash_driver *flash_drivers[] = {
&atsamv_flash,
&avr_flash,
&bluenrgx_flash,
+ &cc3220sf_flash,
&cfi_flash,
&dsp5680xx_flash,
&efm32_flash,
@@ -115,6 +120,9 @@ static struct flash_driver *flash_drivers[] = {
&ocl_flash,
&pic32mx_flash,
&psoc4_flash,
+ &psoc5lp_flash,
+ &psoc5lp_eeprom_flash,
+ &psoc5lp_nvl_flash,
&psoc6_flash,
&sim3x_flash,
&stellaris_flash,
diff --git a/src/flash/nor/psoc5lp.c b/src/flash/nor/psoc5lp.c
new file mode 100644
index 0000000..ae8e3d3
--- /dev/null
+++ b/src/flash/nor/psoc5lp.c
@@ -0,0 +1,1574 @@
+/*
+ * PSoC 5LP flash driver
+ *
+ * Copyright (c) 2016 Andreas Färber
+ *
+ * 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 "imp.h"
+#include <helper/time_support.h>
+#include <target/armv7m.h>
+
+#define PM_ACT_CFG0 0x400043A0
+#define PM_ACT_CFG12 0x400043AC
+#define SPC_CPU_DATA 0x40004720
+#define SPC_SR 0x40004722
+#define PRT1_PC2 0x4000500A
+#define PHUB_CH0_BASIC_CFG 0x40007010
+#define PHUB_CH0_ACTION 0x40007014
+#define PHUB_CH0_BASIC_STATUS 0x40007018
+#define PHUB_CH1_BASIC_CFG 0x40007020
+#define PHUB_CH1_ACTION 0x40007024
+#define PHUB_CH1_BASIC_STATUS 0x40007028
+#define PHUB_CFGMEM0_CFG0 0x40007600
+#define PHUB_CFGMEM0_CFG1 0x40007604
+#define PHUB_CFGMEM1_CFG0 0x40007608
+#define PHUB_CFGMEM1_CFG1 0x4000760C
+#define PHUB_TDMEM0_ORIG_TD0 0x40007800
+#define PHUB_TDMEM0_ORIG_TD1 0x40007804
+#define PHUB_TDMEM1_ORIG_TD0 0x40007808
+#define PHUB_TDMEM1_ORIG_TD1 0x4000780C
+#define PANTHER_DEVICE_ID 0x4008001C
+
+/* NVL is not actually mapped to the Cortex-M address space
+ * As we need a base addess different from other banks in the device
+ * we use the address of NVL programming data in Cypress images */
+#define NVL_META_BASE 0x90000000
+
+#define PM_ACT_CFG12_EN_EE (1 << 4)
+
+#define SPC_KEY1 0xB6
+#define SPC_KEY2 0xD3
+
+#define SPC_LOAD_BYTE 0x00
+#define SPC_LOAD_MULTI_BYTE 0x01
+#define SPC_LOAD_ROW 0x02
+#define SPC_READ_BYTE 0x03
+#define SPC_READ_MULTI_BYTE 0x04
+#define SPC_WRITE_ROW 0x05
+#define SPC_WRITE_USER_NVL 0x06
+#define SPC_PRG_ROW 0x07
+#define SPC_ERASE_SECTOR 0x08
+#define SPC_ERASE_ALL 0x09
+#define SPC_READ_HIDDEN_ROW 0x0A
+#define SPC_PROGRAM_PROTECT_ROW 0x0B
+#define SPC_GET_CHECKSUM 0x0C
+#define SPC_GET_TEMP 0x0E
+#define SPC_READ_VOLATILE_BYTE 0x10
+
+#define SPC_ARRAY_ALL 0x3F
+#define SPC_ARRAY_EEPROM 0x40
+#define SPC_ARRAY_NVL_USER 0x80
+#define SPC_ARRAY_NVL_WO 0xF8
+
+#define SPC_ROW_PROTECTION 0
+
+#define SPC_OPCODE_LEN 3
+
+#define SPC_SR_DATA_READY (1 << 0)
+#define SPC_SR_IDLE (1 << 1)
+
+#define PM_ACT_CFG0_EN_CLK_SPC (1 << 3)
+
+#define PHUB_CHx_BASIC_CFG_EN (1 << 0)
+#define PHUB_CHx_BASIC_CFG_WORK_SEP (1 << 5)
+
+#define PHUB_CHx_ACTION_CPU_REQ (1 << 0)
+
+#define PHUB_CFGMEMx_CFG0 (1 << 7)
+
+#define PHUB_TDMEMx_ORIG_TD0_NEXT_TD_PTR_LAST (0xff << 16)
+#define PHUB_TDMEMx_ORIG_TD0_INC_SRC_ADDR (1 << 24)
+
+#define NVL_3_ECCEN (1 << 3)
+
+#define ROW_SIZE 256
+#define ROW_ECC_SIZE 32
+#define ROWS_PER_SECTOR 64
+#define SECTOR_SIZE (ROWS_PER_SECTOR * ROW_SIZE)
+#define ROWS_PER_BLOCK 256
+#define BLOCK_SIZE (ROWS_PER_BLOCK * ROW_SIZE)
+#define SECTORS_PER_BLOCK (BLOCK_SIZE / SECTOR_SIZE)
+#define EEPROM_ROW_SIZE 16
+#define EEPROM_SECTOR_SIZE (ROWS_PER_SECTOR * EEPROM_ROW_SIZE)
+#define EEPROM_BLOCK_SIZE (ROWS_PER_BLOCK * EEPROM_ROW_SIZE)
+
+#define PART_NUMBER_LEN (17 + 1)
+
+struct psoc5lp_device {
+ uint32_t id;
+ unsigned fam;
+ unsigned speed_mhz;
+ unsigned flash_kb;
+ unsigned eeprom_kb;
+};
+
+/*
+ * Device information collected from datasheets.
+ * Different temperature ranges (C/I/Q/A) may share IDs, not differing otherwise.
+ */
+static const struct psoc5lp_device psoc5lp_devices[] = {
+ /* CY8C58LP Family Datasheet */
+ { .id = 0x2E11F069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E120069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E123069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E124069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E126069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E127069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E117069, .fam = 8, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E118069, .fam = 8, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E119069, .fam = 8, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E11C069, .fam = 8, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E114069, .fam = 8, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E115069, .fam = 8, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E116069, .fam = 8, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E160069, .fam = 8, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+ /* '' */
+ { .id = 0x2E161069, .fam = 8, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+ /* '' */
+ { .id = 0x2E1D2069, .fam = 8, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E1D6069, .fam = 8, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+
+ /* CY8C56LP Family Datasheet */
+ { .id = 0x2E10A069, .fam = 6, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E10D069, .fam = 6, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E10E069, .fam = 6, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E106069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E108069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E109069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E101069, .fam = 6, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E104069, .fam = 6, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ /* '' */
+ { .id = 0x2E105069, .fam = 6, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E128069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ /* '' */
+ { .id = 0x2E122069, .fam = 6, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E129069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E163069, .fam = 6, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E156069, .fam = 6, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E1D3069, .fam = 6, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+
+ /* CY8C54LP Family Datasheet */
+ { .id = 0x2E11A069, .fam = 4, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E16A069, .fam = 4, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E12A069, .fam = 4, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E103069, .fam = 4, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E16C069, .fam = 4, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E102069, .fam = 4, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E148069, .fam = 4, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E155069, .fam = 4, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E16B069, .fam = 4, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E12B069, .fam = 4, .speed_mhz = 67, .flash_kb = 32, .eeprom_kb = 2 },
+ { .id = 0x2E168069, .fam = 4, .speed_mhz = 67, .flash_kb = 32, .eeprom_kb = 2 },
+ { .id = 0x2E178069, .fam = 4, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E15D069, .fam = 4, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E1D4069, .fam = 4, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+
+ /* CY8C52LP Family Datasheet */
+ { .id = 0x2E11E069, .fam = 2, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E12F069, .fam = 2, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E133069, .fam = 2, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E159069, .fam = 2, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E11D069, .fam = 2, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E121069, .fam = 2, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E184069, .fam = 2, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E196069, .fam = 2, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E132069, .fam = 2, .speed_mhz = 67, .flash_kb = 32, .eeprom_kb = 2 },
+ { .id = 0x2E138069, .fam = 2, .speed_mhz = 67, .flash_kb = 32, .eeprom_kb = 2 },
+ { .id = 0x2E13A069, .fam = 2, .speed_mhz = 67, .flash_kb = 32, .eeprom_kb = 2 },
+ { .id = 0x2E152069, .fam = 2, .speed_mhz = 67, .flash_kb = 32, .eeprom_kb = 2 },
+ { .id = 0x2E15F069, .fam = 2, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E15A069, .fam = 2, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E1D5069, .fam = 2, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+};
+
+static void psoc5lp_get_part_number(const struct psoc5lp_device *dev, char *str)
+{
+ strcpy(str, "CY8Cabcdefg-LPxxx");
+
+ str[4] = '5';
+ str[5] = '0' + dev->fam;
+
+ switch (dev->speed_mhz) {
+ case 67:
+ str[6] = '6';
+ break;
+ case 80:
+ str[6] = '8';
+ break;
+ default:
+ str[6] = '?';
+ }
+
+ switch (dev->flash_kb) {
+ case 32:
+ str[7] = '5';
+ break;
+ case 64:
+ str[7] = '6';
+ break;
+ case 128:
+ str[7] = '7';
+ break;
+ case 256:
+ str[7] = '8';
+ break;
+ default:
+ str[7] = '?';
+ }
+
+ /* Package does not matter. */
+ strncpy(str + 8, "xx", 2);
+
+ /* Temperate range cannot uniquely be identified. */
+ str[10] = 'x';
+}
+
+static int psoc5lp_get_device_id(struct target *target, uint32_t *id)
+{
+ int retval;
+
+ retval = target_read_u32(target, PANTHER_DEVICE_ID, id); /* dummy read */
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_read_u32(target, PANTHER_DEVICE_ID, id);
+ return retval;
+}
+
+static int psoc5lp_find_device(struct target *target,
+ const struct psoc5lp_device **device)
+{
+ uint32_t device_id;
+ unsigned i;
+ int retval;
+
+ *device = NULL;
+
+ retval = psoc5lp_get_device_id(target, &device_id);
+ if (retval != ERROR_OK)
+ return retval;
+ LOG_DEBUG("PANTHER_DEVICE_ID = 0x%08" PRIX32, device_id);
+
+ for (i = 0; i < ARRAY_SIZE(psoc5lp_devices); i++) {
+ if (psoc5lp_devices[i].id == device_id) {
+ *device = &psoc5lp_devices[i];
+ return ERROR_OK;
+ }
+ }
+
+ LOG_ERROR("Device 0x%08" PRIX32 " not supported", device_id);
+ return ERROR_FLASH_OPER_UNSUPPORTED;
+}
+
+static int psoc5lp_spc_enable_clock(struct target *target)
+{
+ int retval;
+ uint8_t pm_act_cfg0;
+
+ retval = target_read_u8(target, PM_ACT_CFG0, &pm_act_cfg0);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Cannot read PM_ACT_CFG0");
+ return retval;
+ }
+
+ if (pm_act_cfg0 & PM_ACT_CFG0_EN_CLK_SPC)
+ return ERROR_OK; /* clock already enabled */
+
+ retval = target_write_u8(target, PM_ACT_CFG0, pm_act_cfg0 | PM_ACT_CFG0_EN_CLK_SPC);
+ if (retval != ERROR_OK)
+ LOG_ERROR("Cannot enable SPC clock");
+
+ return retval;
+}
+
+static int psoc5lp_spc_write_opcode(struct target *target, uint8_t opcode)
+{
+ int retval;
+
+ retval = target_write_u8(target, SPC_CPU_DATA, SPC_KEY1);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, SPC_KEY2 + opcode);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, opcode);
+ return retval;
+}
+
+static void psoc5lp_spc_write_opcode_buffer(struct target *target,
+ uint8_t *buf, uint8_t opcode)
+{
+ buf[0] = SPC_KEY1;
+ buf[1] = SPC_KEY2 + opcode;
+ buf[2] = opcode;
+}
+
+static int psoc5lp_spc_busy_wait_data(struct target *target)
+{
+ int64_t endtime;
+ uint8_t sr;
+ int retval;
+
+ retval = target_read_u8(target, SPC_SR, &sr); /* dummy read */
+ if (retval != ERROR_OK)
+ return retval;
+
+ endtime = timeval_ms() + 1000; /* 1 second timeout */
+ do {
+ alive_sleep(1);
+ retval = target_read_u8(target, SPC_SR, &sr);
+ if (retval != ERROR_OK)
+ return retval;
+ if (sr == SPC_SR_DATA_READY)
+ return ERROR_OK;
+ } while (timeval_ms() < endtime);
+
+ return ERROR_FLASH_OPERATION_FAILED;
+}
+
+static int psoc5lp_spc_busy_wait_idle(struct target *target)
+{
+ int64_t endtime;
+ uint8_t sr;
+ int retval;
+
+ retval = target_read_u8(target, SPC_SR, &sr); /* dummy read */
+ if (retval != ERROR_OK)
+ return retval;
+
+ endtime = timeval_ms() + 1000; /* 1 second timeout */
+ do {
+ alive_sleep(1);
+ retval = target_read_u8(target, SPC_SR, &sr);
+ if (retval != ERROR_OK)
+ return retval;
+ if (sr == SPC_SR_IDLE)
+ return ERROR_OK;
+ } while (timeval_ms() < endtime);
+
+ return ERROR_FLASH_OPERATION_FAILED;
+}
+
+static int psoc5lp_spc_load_byte(struct target *target,
+ uint8_t array_id, uint8_t offset, uint8_t value)
+{
+ int retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_LOAD_BYTE);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, array_id);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, offset);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, value);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_spc_load_row(struct target *target,
+ uint8_t array_id, const uint8_t *data, unsigned row_size)
+{
+ unsigned i;
+ int retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_LOAD_ROW);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, array_id);
+ if (retval != ERROR_OK)
+ return retval;
+
+ for (i = 0; i < row_size; i++) {
+ retval = target_write_u8(target, SPC_CPU_DATA, data[i]);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_spc_read_byte(struct target *target,
+ uint8_t array_id, uint8_t offset, uint8_t *data)
+{
+ int retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_READ_BYTE);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, array_id);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, offset);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_data(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_read_u8(target, SPC_CPU_DATA, data);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_spc_write_row(struct target *target,
+ uint8_t array_id, uint16_t row_id, const uint8_t *temp)
+{
+ int retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_WRITE_ROW);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, array_id);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, row_id >> 8);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, row_id & 0xff);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, temp[0]);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, temp[1]);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_spc_write_user_nvl(struct target *target,
+ uint8_t array_id)
+{
+ int retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_WRITE_USER_NVL);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, array_id);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_spc_erase_sector(struct target *target,
+ uint8_t array_id, uint8_t row_id)
+{
+ int retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_ERASE_SECTOR);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, array_id);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, row_id);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_spc_erase_all(struct target *target)
+{
+ int retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_ERASE_ALL);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_spc_read_hidden_row(struct target *target,
+ uint8_t array_id, uint8_t row_id, uint8_t *data)
+{
+ int i, retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_READ_HIDDEN_ROW);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, array_id);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, row_id);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_data(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ for (i = 0; i < ROW_SIZE; i++) {
+ retval = target_read_u8(target, SPC_CPU_DATA, &data[i]);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_spc_get_temp(struct target *target, uint8_t samples,
+ uint8_t *data)
+{
+ int retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_GET_TEMP);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, samples);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_data(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_read_u8(target, SPC_CPU_DATA, &data[0]);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_read_u8(target, SPC_CPU_DATA, &data[1]);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_spc_read_volatile_byte(struct target *target,
+ uint8_t array_id, uint8_t offset, uint8_t *data)
+{
+ int retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_READ_VOLATILE_BYTE);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, array_id);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, offset);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_data(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_read_u8(target, SPC_CPU_DATA, data);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+/*
+ * NV Latch
+ */
+
+struct psoc5lp_nvl_flash_bank {
+ bool probed;
+ const struct psoc5lp_device *device;
+};
+
+static int psoc5lp_nvl_read(struct flash_bank *bank,
+ uint8_t *buffer, uint32_t offset, uint32_t count)
+{
+ int retval;
+
+ retval = psoc5lp_spc_enable_clock(bank->target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ while (count > 0) {
+ retval = psoc5lp_spc_read_byte(bank->target,
+ SPC_ARRAY_NVL_USER, offset, buffer);
+ if (retval != ERROR_OK)
+ return retval;
+ buffer++;
+ offset++;
+ count--;
+ }
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_nvl_erase(struct flash_bank *bank, int first, int last)
+{
+ LOG_WARNING("There is no erase operation for NV Latches");
+ return ERROR_FLASH_OPER_UNSUPPORTED;
+}
+
+static int psoc5lp_nvl_erase_check(struct flash_bank *bank)
+{
+ int i;
+
+ for (i = 0; i < bank->num_sectors; i++)
+ bank->sectors[i].is_erased = 0;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_nvl_write(struct flash_bank *bank,
+ const uint8_t *buffer, uint32_t offset, uint32_t byte_count)
+{
+ struct target *target = bank->target;
+ uint8_t *current_data, val;
+ bool write_required = false, pullup_needed = false, ecc_changed = false;
+ uint32_t i;
+ int retval;
+
+ if (offset != 0 || byte_count != bank->size) {
+ LOG_ERROR("NVL can only be written in whole");
+ return ERROR_FLASH_OPER_UNSUPPORTED;
+ }
+
+ current_data = calloc(1, bank->size);
+ if (!current_data)
+ return ERROR_FAIL;
+ retval = psoc5lp_nvl_read(bank, current_data, offset, byte_count);
+ if (retval != ERROR_OK) {
+ free(current_data);
+ return retval;
+ }
+ for (i = offset; i < byte_count; i++) {
+ if (current_data[i] != buffer[i]) {
+ write_required = true;
+ break;
+ }
+ }
+ if (((buffer[2] & 0x80) == 0x80) && ((current_data[0] & 0x0C) != 0x08))
+ pullup_needed = true;
+ if (((buffer[3] ^ current_data[3]) & 0x08) == 0x08)
+ ecc_changed = true;
+ free(current_data);
+
+ if (!write_required) {
+ LOG_INFO("Unchanged, skipping NVL write");
+ return ERROR_OK;
+ }
+ if (pullup_needed) {
+ retval = target_read_u8(target, PRT1_PC2, &val);
+ if (retval != ERROR_OK)
+ return retval;
+ val &= 0xF0;
+ val |= 0x05;
+ retval = target_write_u8(target, PRT1_PC2, val);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ for (i = offset; i < byte_count; i++) {
+ retval = psoc5lp_spc_load_byte(target,
+ SPC_ARRAY_NVL_USER, i, buffer[i]);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_read_volatile_byte(target,
+ SPC_ARRAY_NVL_USER, i, &val);
+ if (retval != ERROR_OK)
+ return retval;
+ if (val != buffer[i]) {
+ LOG_ERROR("Failed to load NVL byte %" PRIu32 ": "
+ "expected 0x%02" PRIx8 ", read 0x%02" PRIx8,
+ i, buffer[i], val);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ }
+
+ retval = psoc5lp_spc_write_user_nvl(target, SPC_ARRAY_NVL_USER);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if (ecc_changed) {
+ retval = target_call_reset_callbacks(target, RESET_INIT);
+ if (retval != ERROR_OK)
+ LOG_WARNING("Reset failed after enabling or disabling ECC");
+ }
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_nvl_protect_check(struct flash_bank *bank)
+{
+ int i;
+
+ for (i = 0; i < bank->num_sectors; i++)
+ bank->sectors[i].is_protected = -1;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_nvl_get_info_command(struct flash_bank *bank,
+ char *buf, int buf_size)
+{
+ struct psoc5lp_nvl_flash_bank *psoc_nvl_bank = bank->driver_priv;
+ char part_number[PART_NUMBER_LEN];
+
+ psoc5lp_get_part_number(psoc_nvl_bank->device, part_number);
+
+ snprintf(buf, buf_size, "%s", part_number);
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_nvl_probe(struct flash_bank *bank)
+{
+ struct psoc5lp_nvl_flash_bank *psoc_nvl_bank = bank->driver_priv;
+ int retval;
+
+ if (psoc_nvl_bank->probed)
+ return ERROR_OK;
+
+ if (bank->target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ retval = psoc5lp_find_device(bank->target, &psoc_nvl_bank->device);
+ if (retval != ERROR_OK)
+ return retval;
+
+ bank->base = NVL_META_BASE;
+ bank->size = 4;
+ bank->num_sectors = 1;
+ bank->sectors = calloc(bank->num_sectors,
+ sizeof(struct flash_sector));
+ bank->sectors[0].offset = 0;
+ bank->sectors[0].size = 4;
+ bank->sectors[0].is_erased = -1;
+ bank->sectors[0].is_protected = -1;
+
+ psoc_nvl_bank->probed = true;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_nvl_auto_probe(struct flash_bank *bank)
+{
+ struct psoc5lp_nvl_flash_bank *psoc_nvl_bank = bank->driver_priv;
+
+ if (psoc_nvl_bank->probed)
+ return ERROR_OK;
+
+ return psoc5lp_nvl_probe(bank);
+}
+
+FLASH_BANK_COMMAND_HANDLER(psoc5lp_nvl_flash_bank_command)
+{
+ struct psoc5lp_nvl_flash_bank *psoc_nvl_bank;
+
+ psoc_nvl_bank = malloc(sizeof(struct psoc5lp_nvl_flash_bank));
+ if (!psoc_nvl_bank)
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ psoc_nvl_bank->probed = false;
+
+ bank->driver_priv = psoc_nvl_bank;
+
+ return ERROR_OK;
+}
+
+static const struct command_registration psoc5lp_nvl_exec_command_handlers[] = {
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration psoc5lp_nvl_command_handlers[] = {
+ {
+ .name = "psoc5lp_nvl",
+ .mode = COMMAND_ANY,
+ .help = "PSoC 5LP NV Latch command group",
+ .usage = "",
+ .chain = psoc5lp_nvl_exec_command_handlers,
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver psoc5lp_nvl_flash = {
+ .name = "psoc5lp_nvl",
+ .commands = psoc5lp_nvl_command_handlers,
+ .flash_bank_command = psoc5lp_nvl_flash_bank_command,
+ .info = psoc5lp_nvl_get_info_command,
+ .probe = psoc5lp_nvl_probe,
+ .auto_probe = psoc5lp_nvl_auto_probe,
+ .protect_check = psoc5lp_nvl_protect_check,
+ .read = psoc5lp_nvl_read,
+ .erase = psoc5lp_nvl_erase,
+ .erase_check = psoc5lp_nvl_erase_check,
+ .write = psoc5lp_nvl_write,
+};
+
+/*
+ * EEPROM
+ */
+
+struct psoc5lp_eeprom_flash_bank {
+ bool probed;
+ const struct psoc5lp_device *device;
+};
+
+static int psoc5lp_eeprom_erase(struct flash_bank *bank, int first, int last)
+{
+ int i, retval;
+
+ for (i = first; i <= last; i++) {
+ retval = psoc5lp_spc_erase_sector(bank->target,
+ SPC_ARRAY_EEPROM, i);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_eeprom_write(struct flash_bank *bank,
+ const uint8_t *buffer, uint32_t offset, uint32_t byte_count)
+{
+ struct target *target = bank->target;
+ uint8_t temp[2];
+ unsigned row;
+ int retval;
+
+ if (offset % EEPROM_ROW_SIZE != 0) {
+ LOG_ERROR("Writes must be row-aligned, got offset 0x%08" PRIx32,
+ offset);
+ return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+ }
+
+ retval = psoc5lp_spc_get_temp(target, 3, temp);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Unable to read Die temperature");
+ return retval;
+ }
+ LOG_DEBUG("Get_Temp: sign 0x%02" PRIx8 ", magnitude 0x%02" PRIx8,
+ temp[0], temp[1]);
+
+ for (row = offset / EEPROM_ROW_SIZE; byte_count >= EEPROM_ROW_SIZE; row++) {
+ retval = psoc5lp_spc_load_row(target, SPC_ARRAY_EEPROM,
+ buffer, EEPROM_ROW_SIZE);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_write_row(target, SPC_ARRAY_EEPROM,
+ row, temp);
+ if (retval != ERROR_OK)
+ return retval;
+
+ buffer += EEPROM_ROW_SIZE;
+ byte_count -= EEPROM_ROW_SIZE;
+ offset += EEPROM_ROW_SIZE;
+ }
+ if (byte_count > 0) {
+ uint8_t buf[EEPROM_ROW_SIZE];
+
+ memcpy(buf, buffer, byte_count);
+ memset(buf + byte_count, bank->default_padded_value,
+ EEPROM_ROW_SIZE - byte_count);
+
+ LOG_DEBUG("Padding %d bytes", EEPROM_ROW_SIZE - byte_count);
+ retval = psoc5lp_spc_load_row(target, SPC_ARRAY_EEPROM,
+ buf, EEPROM_ROW_SIZE);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_write_row(target, SPC_ARRAY_EEPROM,
+ row, temp);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_eeprom_protect_check(struct flash_bank *bank)
+{
+ int i;
+
+ for (i = 0; i < bank->num_sectors; i++)
+ bank->sectors[i].is_protected = -1;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_eeprom_get_info_command(struct flash_bank *bank, char *buf, int buf_size)
+{
+ struct psoc5lp_eeprom_flash_bank *psoc_eeprom_bank = bank->driver_priv;
+ char part_number[PART_NUMBER_LEN];
+
+ psoc5lp_get_part_number(psoc_eeprom_bank->device, part_number);
+
+ snprintf(buf, buf_size, "%s", part_number);
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_eeprom_probe(struct flash_bank *bank)
+{
+ struct psoc5lp_eeprom_flash_bank *psoc_eeprom_bank = bank->driver_priv;
+ uint32_t flash_addr = bank->base;
+ uint32_t val;
+ int i, retval;
+
+ if (psoc_eeprom_bank->probed)
+ return ERROR_OK;
+
+ if (bank->target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ retval = psoc5lp_find_device(bank->target, &psoc_eeprom_bank->device);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_read_u32(bank->target, PM_ACT_CFG12, &val);
+ if (retval != ERROR_OK)
+ return retval;
+ if (!(val & PM_ACT_CFG12_EN_EE)) {
+ val |= PM_ACT_CFG12_EN_EE;
+ retval = target_write_u32(bank->target, PM_ACT_CFG12, val);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ bank->size = psoc_eeprom_bank->device->eeprom_kb * 1024;
+ bank->num_sectors = DIV_ROUND_UP(bank->size, EEPROM_SECTOR_SIZE);
+ bank->sectors = calloc(bank->num_sectors,
+ sizeof(struct flash_sector));
+ for (i = 0; i < bank->num_sectors; i++) {
+ bank->sectors[i].size = EEPROM_SECTOR_SIZE;
+ bank->sectors[i].offset = flash_addr - bank->base;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = -1;
+
+ flash_addr += bank->sectors[i].size;
+ }
+
+ bank->default_padded_value = bank->erased_value = 0x00;
+
+ psoc_eeprom_bank->probed = true;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_eeprom_auto_probe(struct flash_bank *bank)
+{
+ struct psoc5lp_eeprom_flash_bank *psoc_eeprom_bank = bank->driver_priv;
+
+ if (psoc_eeprom_bank->probed)
+ return ERROR_OK;
+
+ return psoc5lp_eeprom_probe(bank);
+}
+
+FLASH_BANK_COMMAND_HANDLER(psoc5lp_eeprom_flash_bank_command)
+{
+ struct psoc5lp_eeprom_flash_bank *psoc_eeprom_bank;
+
+ psoc_eeprom_bank = malloc(sizeof(struct psoc5lp_eeprom_flash_bank));
+ if (!psoc_eeprom_bank)
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ psoc_eeprom_bank->probed = false;
+ psoc_eeprom_bank->device = NULL;
+
+ bank->driver_priv = psoc_eeprom_bank;
+
+ return ERROR_OK;
+}
+
+static const struct command_registration psoc5lp_eeprom_exec_command_handlers[] = {
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration psoc5lp_eeprom_command_handlers[] = {
+ {
+ .name = "psoc5lp_eeprom",
+ .mode = COMMAND_ANY,
+ .help = "PSoC 5LP EEPROM command group",
+ .usage = "",
+ .chain = psoc5lp_eeprom_exec_command_handlers,
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver psoc5lp_eeprom_flash = {
+ .name = "psoc5lp_eeprom",
+ .commands = psoc5lp_eeprom_command_handlers,
+ .flash_bank_command = psoc5lp_eeprom_flash_bank_command,
+ .info = psoc5lp_eeprom_get_info_command,
+ .probe = psoc5lp_eeprom_probe,
+ .auto_probe = psoc5lp_eeprom_auto_probe,
+ .protect_check = psoc5lp_eeprom_protect_check,
+ .read = default_flash_read,
+ .erase = psoc5lp_eeprom_erase,
+ .erase_check = default_flash_blank_check,
+ .write = psoc5lp_eeprom_write,
+};
+
+/*
+ * Program Flash
+ */
+
+struct psoc5lp_flash_bank {
+ bool probed;
+ const struct psoc5lp_device *device;
+ bool ecc_enabled;
+};
+
+static int psoc5lp_erase(struct flash_bank *bank, int first, int last)
+{
+ struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv;
+ int i, retval;
+
+ if (!psoc_bank->ecc_enabled) {
+ /* Silently avoid erasing sectors twice */
+ if (last >= first + bank->num_sectors / 2) {
+ LOG_DEBUG("Skipping duplicate erase of sectors %d to %d",
+ first + bank->num_sectors / 2, last);
+ last = first + (bank->num_sectors / 2) - 1;
+ }
+ /* Check for any remaining ECC sectors */
+ if (last >= bank->num_sectors / 2) {
+ LOG_WARNING("Skipping erase of ECC region sectors %d to %d",
+ bank->num_sectors / 2, last);
+ last = (bank->num_sectors / 2) - 1;
+ }
+ }
+
+ for (i = first; i <= last; i++) {
+ retval = psoc5lp_spc_erase_sector(bank->target,
+ i / SECTORS_PER_BLOCK, i % SECTORS_PER_BLOCK);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ return ERROR_OK;
+}
+
+/* Derived from core.c:default_flash_blank_check() */
+static int psoc5lp_erase_check(struct flash_bank *bank)
+{
+ struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv;
+ struct target *target = bank->target;
+ int i, retval;
+
+ if (target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ struct target_memory_check_block *block_array;
+ block_array = malloc(bank->num_sectors * sizeof(struct target_memory_check_block));
+ if (block_array == NULL)
+ return ERROR_FAIL;
+
+ for (i = 0; i < bank->num_sectors; i++) {
+ block_array[i].address = bank->base + bank->sectors[i].offset;
+ block_array[i].size = bank->sectors[i].size;
+ block_array[i].result = UINT32_MAX; /* erase state unknown */
+ }
+
+ bool fast_check = true;
+ for (i = 0; i < bank->num_sectors; ) {
+ retval = armv7m_blank_check_memory(target,
+ block_array + i, bank->num_sectors - i,
+ bank->erased_value);
+ if (retval < 1) {
+ /* Run slow fallback if the first run gives no result
+ * otherwise use possibly incomplete results */
+ if (i == 0)
+ fast_check = false;
+ break;
+ }
+ i += retval; /* add number of blocks done this round */
+ }
+
+ if (fast_check) {
+ if (!psoc_bank->ecc_enabled) {
+ int half_sectors = bank->num_sectors / 2;
+ for (i = 0; i < half_sectors / 2; i++)
+ bank->sectors[i].is_erased =
+ (block_array[i].result != 1)
+ ? block_array[i + half_sectors].result
+ : block_array[i].result;
+ } else {
+ for (i = 0; i < bank->num_sectors; i++)
+ bank->sectors[i].is_erased = block_array[i].result;
+ }
+ retval = ERROR_OK;
+ } else {
+ LOG_ERROR("Can't run erase check - add working memory");
+ retval = ERROR_FAIL;
+ }
+ free(block_array);
+
+ return retval;
+}
+
+static int psoc5lp_write(struct flash_bank *bank, const uint8_t *buffer,
+ uint32_t offset, uint32_t byte_count)
+{
+ struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv;
+ struct target *target = bank->target;
+ struct working_area *code_area, *even_row_area, *odd_row_area;
+ uint32_t row_size;
+ uint8_t temp[2], buf[12], ecc_bytes[ROW_ECC_SIZE];
+ unsigned array_id, row;
+ int i, retval;
+
+ if (offset + byte_count > bank->size) {
+ LOG_ERROR("Writing to ECC not supported");
+ return ERROR_FLASH_DST_OUT_OF_BANK;
+ }
+
+ if (offset % ROW_SIZE != 0) {
+ LOG_ERROR("Writes must be row-aligned, got offset 0x%08" PRIx32,
+ offset);
+ return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+ }
+
+ row_size = ROW_SIZE;
+ if (!psoc_bank->ecc_enabled) {
+ row_size += ROW_ECC_SIZE;
+ memset(ecc_bytes, bank->default_padded_value, ROW_ECC_SIZE);
+ }
+
+ retval = psoc5lp_spc_get_temp(target, 3, temp);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Unable to read Die temperature");
+ return retval;
+ }
+ LOG_DEBUG("Get_Temp: sign 0x%02" PRIx8 ", magnitude 0x%02" PRIx8,
+ temp[0], temp[1]);
+
+ assert(target_get_working_area_avail(target) == target->working_area_size);
+ retval = target_alloc_working_area(target,
+ target_get_working_area_avail(target) / 2, &code_area);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Could not allocate working area for program SRAM");
+ return retval;
+ }
+ assert(code_area->address < 0x20000000);
+
+ retval = target_alloc_working_area(target,
+ SPC_OPCODE_LEN + 1 + row_size + 3 + SPC_OPCODE_LEN + 6,
+ &even_row_area);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Could not allocate working area for even row");
+ goto err_alloc_even;
+ }
+ assert(even_row_area->address >= 0x20000000);
+
+ retval = target_alloc_working_area(target, even_row_area->size,
+ &odd_row_area);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Could not allocate working area for odd row");
+ goto err_alloc_odd;
+ }
+ assert(odd_row_area->address >= 0x20000000);
+
+ for (array_id = offset / BLOCK_SIZE; byte_count > 0; array_id++) {
+ for (row = (offset / ROW_SIZE) % ROWS_PER_BLOCK;
+ row < ROWS_PER_BLOCK && byte_count > 0; row++) {
+ bool even_row = (row % 2 == 0);
+ struct working_area *data_area = even_row ? even_row_area : odd_row_area;
+ unsigned len = MIN(ROW_SIZE, byte_count);
+
+ LOG_DEBUG("Writing load command for array %u row %u at 0x%08" TARGET_PRIxADDR,
+ array_id, row, data_area->address);
+
+ psoc5lp_spc_write_opcode_buffer(target, buf, SPC_LOAD_ROW);
+ buf[SPC_OPCODE_LEN] = array_id;
+ retval = target_write_buffer(target, data_area->address, 4, buf);
+ if (retval != ERROR_OK)
+ goto err_write;
+
+ retval = target_write_buffer(target,
+ data_area->address + SPC_OPCODE_LEN + 1,
+ len, buffer);
+ if (retval != ERROR_OK)
+ goto err_write;
+ buffer += len;
+ byte_count -= len;
+ offset += len;
+
+ if (len < ROW_SIZE) {
+ uint8_t padding[ROW_SIZE];
+
+ memset(padding, bank->default_padded_value, ROW_SIZE);
+
+ LOG_DEBUG("Padding %d bytes", ROW_SIZE - len);
+ retval = target_write_buffer(target,
+ data_area->address + SPC_OPCODE_LEN + 1 + len,
+ ROW_SIZE - len, padding);
+ if (retval != ERROR_OK)
+ goto err_write;
+ }
+
+ if (!psoc_bank->ecc_enabled) {
+ retval = target_write_buffer(target,
+ data_area->address + SPC_OPCODE_LEN + 1 + ROW_SIZE,
+ sizeof(ecc_bytes), ecc_bytes);
+ if (retval != ERROR_OK)
+ goto err_write;
+ }
+
+ for (i = 0; i < 3; i++)
+ buf[i] = 0x00; /* 3 NOPs for short delay */
+ psoc5lp_spc_write_opcode_buffer(target, buf + 3, SPC_PRG_ROW);
+ buf[3 + SPC_OPCODE_LEN] = array_id;
+ buf[3 + SPC_OPCODE_LEN + 1] = row >> 8;
+ buf[3 + SPC_OPCODE_LEN + 2] = row & 0xff;
+ memcpy(buf + 3 + SPC_OPCODE_LEN + 3, temp, 2);
+ buf[3 + SPC_OPCODE_LEN + 5] = 0x00; /* padding */
+ retval = target_write_buffer(target,
+ data_area->address + SPC_OPCODE_LEN + 1 + row_size,
+ 12, buf);
+ if (retval != ERROR_OK)
+ goto err_write;
+
+ retval = target_write_u32(target,
+ even_row ? PHUB_CH0_BASIC_STATUS : PHUB_CH1_BASIC_STATUS,
+ (even_row ? 0 : 1) << 8);
+ if (retval != ERROR_OK)
+ goto err_dma;
+
+ retval = target_write_u32(target,
+ even_row ? PHUB_CH0_BASIC_CFG : PHUB_CH1_BASIC_CFG,
+ PHUB_CHx_BASIC_CFG_WORK_SEP | PHUB_CHx_BASIC_CFG_EN);
+ if (retval != ERROR_OK)
+ goto err_dma;
+
+ retval = target_write_u32(target,
+ even_row ? PHUB_CFGMEM0_CFG0 : PHUB_CFGMEM1_CFG0,
+ PHUB_CFGMEMx_CFG0);
+ if (retval != ERROR_OK)
+ goto err_dma;
+
+ retval = target_write_u32(target,
+ even_row ? PHUB_CFGMEM0_CFG1 : PHUB_CFGMEM1_CFG1,
+ ((SPC_CPU_DATA >> 16) << 16) | (data_area->address >> 16));
+ if (retval != ERROR_OK)
+ goto err_dma;
+
+ retval = target_write_u32(target,
+ even_row ? PHUB_TDMEM0_ORIG_TD0 : PHUB_TDMEM1_ORIG_TD0,
+ PHUB_TDMEMx_ORIG_TD0_INC_SRC_ADDR |
+ PHUB_TDMEMx_ORIG_TD0_NEXT_TD_PTR_LAST |
+ ((SPC_OPCODE_LEN + 1 + row_size + 3 + SPC_OPCODE_LEN + 5) & 0xfff));
+ if (retval != ERROR_OK)
+ goto err_dma;
+
+ retval = target_write_u32(target,
+ even_row ? PHUB_TDMEM0_ORIG_TD1 : PHUB_TDMEM1_ORIG_TD1,
+ ((SPC_CPU_DATA & 0xffff) << 16) | (data_area->address & 0xffff));
+ if (retval != ERROR_OK)
+ goto err_dma;
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ goto err_idle;
+
+ retval = target_write_u32(target,
+ even_row ? PHUB_CH0_ACTION : PHUB_CH1_ACTION,
+ PHUB_CHx_ACTION_CPU_REQ);
+ if (retval != ERROR_OK)
+ goto err_dma_action;
+ }
+ }
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+
+err_dma_action:
+err_idle:
+err_dma:
+err_write:
+ target_free_working_area(target, odd_row_area);
+err_alloc_odd:
+ target_free_working_area(target, even_row_area);
+err_alloc_even:
+ target_free_working_area(target, code_area);
+
+ return retval;
+}
+
+static int psoc5lp_protect_check(struct flash_bank *bank)
+{
+ struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv;
+ uint8_t row_data[ROW_SIZE];
+ const unsigned protection_bytes_per_sector = ROWS_PER_SECTOR * 2 / 8;
+ unsigned i, j, k, num_sectors;
+ int retval;
+
+ if (bank->target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ for (i = 0; i < DIV_ROUND_UP(bank->size, BLOCK_SIZE); i++) {
+ retval = psoc5lp_spc_read_hidden_row(bank->target, i,
+ SPC_ROW_PROTECTION, row_data);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* Last flash array may have less rows, but in practice full sectors. */
+ if (i == bank->size / BLOCK_SIZE)
+ num_sectors = (bank->size % BLOCK_SIZE) / SECTOR_SIZE;
+ else
+ num_sectors = SECTORS_PER_BLOCK;
+
+ for (j = 0; j < num_sectors; j++) {
+ int sector_nr = i * SECTORS_PER_BLOCK + j;
+ struct flash_sector *sector = &bank->sectors[sector_nr];
+ struct flash_sector *ecc_sector;
+
+ if (psoc_bank->ecc_enabled)
+ ecc_sector = &bank->sectors[bank->num_sectors + sector_nr];
+ else
+ ecc_sector = &bank->sectors[bank->num_sectors / 2 + sector_nr];
+
+ sector->is_protected = ecc_sector->is_protected = 0;
+ for (k = protection_bytes_per_sector * j;
+ k < protection_bytes_per_sector * (j + 1); k++) {
+ assert(k < protection_bytes_per_sector * SECTORS_PER_BLOCK);
+ LOG_DEBUG("row[%u][%02u] = 0x%02" PRIx8, i, k, row_data[k]);
+ if (row_data[k] != 0x00) {
+ sector->is_protected = ecc_sector->is_protected = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_get_info_command(struct flash_bank *bank, char *buf, int buf_size)
+{
+ struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv;
+ char part_number[PART_NUMBER_LEN];
+ const char *ecc;
+
+ psoc5lp_get_part_number(psoc_bank->device, part_number);
+ ecc = psoc_bank->ecc_enabled ? "ECC enabled" : "ECC disabled";
+
+ snprintf(buf, buf_size, "%s %s", part_number, ecc);
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_probe(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv;
+ uint32_t flash_addr = bank->base;
+ uint8_t nvl[4], temp[2];
+ int i, retval;
+
+ if (target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (!psoc_bank->device) {
+ retval = psoc5lp_find_device(target, &psoc_bank->device);
+ if (retval != ERROR_OK)
+ return retval;
+
+ bank->size = psoc_bank->device->flash_kb * 1024;
+ }
+
+ bank->num_sectors = DIV_ROUND_UP(bank->size, SECTOR_SIZE);
+
+ if (!psoc_bank->probed) {
+ retval = psoc5lp_spc_enable_clock(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* First values read are inaccurate, so do it once now. */
+ retval = psoc5lp_spc_get_temp(target, 3, temp);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Unable to read Die temperature");
+ return retval;
+ }
+
+ bank->sectors = calloc(bank->num_sectors * 2,
+ sizeof(struct flash_sector));
+ for (i = 0; i < bank->num_sectors; i++) {
+ bank->sectors[i].size = SECTOR_SIZE;
+ bank->sectors[i].offset = flash_addr - bank->base;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = -1;
+
+ flash_addr += bank->sectors[i].size;
+ }
+ flash_addr = 0x48000000;
+ for (i = bank->num_sectors; i < bank->num_sectors * 2; i++) {
+ bank->sectors[i].size = ROWS_PER_SECTOR * ROW_ECC_SIZE;
+ bank->sectors[i].offset = flash_addr - bank->base;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = -1;
+
+ flash_addr += bank->sectors[i].size;
+ }
+
+ bank->default_padded_value = bank->erased_value = 0x00;
+
+ psoc_bank->probed = true;
+ }
+
+ retval = psoc5lp_spc_read_byte(target, SPC_ARRAY_NVL_USER, 3, &nvl[3]);
+ if (retval != ERROR_OK)
+ return retval;
+ LOG_DEBUG("NVL[%d] = 0x%02" PRIx8, 3, nvl[3]);
+ psoc_bank->ecc_enabled = nvl[3] & NVL_3_ECCEN;
+
+ if (!psoc_bank->ecc_enabled)
+ bank->num_sectors *= 2;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_auto_probe(struct flash_bank *bank)
+{
+ return psoc5lp_probe(bank);
+}
+
+COMMAND_HANDLER(psoc5lp_handle_mass_erase_command)
+{
+ struct flash_bank *bank;
+ int retval;
+
+ if (CMD_ARGC < 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_erase_all(bank->target);
+ if (retval == ERROR_OK)
+ command_print(CMD_CTX, "PSoC 5LP erase succeeded");
+ else
+ command_print(CMD_CTX, "PSoC 5LP erase failed");
+
+ return retval;
+}
+
+FLASH_BANK_COMMAND_HANDLER(psoc5lp_flash_bank_command)
+{
+ struct psoc5lp_flash_bank *psoc_bank;
+
+ psoc_bank = malloc(sizeof(struct psoc5lp_flash_bank));
+ if (!psoc_bank)
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ psoc_bank->probed = false;
+ psoc_bank->device = NULL;
+
+ bank->driver_priv = psoc_bank;
+
+ return ERROR_OK;
+}
+
+static const struct command_registration psoc5lp_exec_command_handlers[] = {
+ {
+ .name = "mass_erase",
+ .handler = psoc5lp_handle_mass_erase_command,
+ .mode = COMMAND_EXEC,
+ .usage = "bank_id",
+ .help = "Erase all flash data and ECC/configuration bytes, "
+ "all flash protection rows, "
+ "and all row latches in all flash arrays on the device.",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration psoc5lp_command_handlers[] = {
+ {
+ .name = "psoc5lp",
+ .mode = COMMAND_ANY,
+ .help = "PSoC 5LP flash command group",
+ .usage = "",
+ .chain = psoc5lp_exec_command_handlers,
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver psoc5lp_flash = {
+ .name = "psoc5lp",
+ .commands = psoc5lp_command_handlers,
+ .flash_bank_command = psoc5lp_flash_bank_command,
+ .info = psoc5lp_get_info_command,
+ .probe = psoc5lp_probe,
+ .auto_probe = psoc5lp_auto_probe,
+ .protect_check = psoc5lp_protect_check,
+ .read = default_flash_read,
+ .erase = psoc5lp_erase,
+ .erase_check = psoc5lp_erase_check,
+ .write = psoc5lp_write,
+};
diff --git a/src/flash/nor/virtual.c b/src/flash/nor/virtual.c
index d5d688b..15c4bff 100644
--- a/src/flash/nor/virtual.c
+++ b/src/flash/nor/virtual.c
@@ -46,8 +46,13 @@ static void virtual_update_bank_info(struct flash_bank *bank)
bank->bus_width = master_bank->bus_width;
bank->erased_value = master_bank->erased_value;
bank->default_padded_value = master_bank->default_padded_value;
+ bank->write_start_alignment = master_bank->write_start_alignment;
+ bank->write_end_alignment = master_bank->write_end_alignment;
+ bank->minimal_write_gap = master_bank->minimal_write_gap;
bank->num_sectors = master_bank->num_sectors;
bank->sectors = master_bank->sectors;
+ bank->num_prot_blocks = master_bank->num_prot_blocks;
+ bank->prot_blocks = master_bank->prot_blocks;
}
FLASH_BANK_COMMAND_HANDLER(virtual_flash_bank_command)