aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTim Newsome <tim@sifive.com>2019-01-01 12:37:34 -0800
committerTomas Vanek <vanekt@fbl.cz>2019-01-11 19:50:09 +0000
commit31ea7037b3df7e567a9eb34d28947d118c4cada1 (patch)
tree5230e15a186f1915f3e1d5dd729c11b8c3f3c519 /src
parent42d8fa899c6a6f136daa761e7718003f08f723e7 (diff)
downloadriscv-openocd-31ea7037b3df7e567a9eb34d28947d118c4cada1.zip
riscv-openocd-31ea7037b3df7e567a9eb34d28947d118c4cada1.tar.gz
riscv-openocd-31ea7037b3df7e567a9eb34d28947d118c4cada1.tar.bz2
Add flash support for SiFive's Freedom E platforms
Valgrind and Clang Static Analyzer have no complaints about this change. Change-Id: I7757615ec52448372bdc57729cdf97c7016d97e8 Signed-off-by: Tim Newsome <tim@sifive.com> Reviewed-on: http://openocd.zylin.com/4656 Tested-by: jenkins Reviewed-by: Philipp Guehring <pg@futureware.at> Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Diffstat (limited to 'src')
-rw-r--r--src/flash/nor/Makefile.am1
-rw-r--r--src/flash/nor/drivers.c2
-rw-r--r--src/flash/nor/fespi.c1053
3 files changed, 1056 insertions, 0 deletions
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index 7c353c4..bbdc312 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -27,6 +27,7 @@ NOR_DRIVERS = \
%D%/em357.c \
%D%/esirisc_flash.c \
%D%/faux.c \
+ %D%/fespi.c \
%D%/fm3.c \
%D%/fm4.c \
%D%/jtagspi.c \
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index 1c456ad..d1bbb27 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -42,6 +42,7 @@ extern struct flash_driver esirisc_flash;
extern struct flash_driver faux_flash;
extern struct flash_driver fm3_flash;
extern struct flash_driver fm4_flash;
+extern struct flash_driver fespi_flash;
extern struct flash_driver jtagspi_flash;
extern struct flash_driver kinetis_flash;
extern struct flash_driver kinetis_ke_flash;
@@ -109,6 +110,7 @@ static struct flash_driver *flash_drivers[] = {
&faux_flash,
&fm3_flash,
&fm4_flash,
+ &fespi_flash,
&jtagspi_flash,
&kinetis_flash,
&kinetis_ke_flash,
diff --git a/src/flash/nor/fespi.c b/src/flash/nor/fespi.c
new file mode 100644
index 0000000..a07972e
--- /dev/null
+++ b/src/flash/nor/fespi.c
@@ -0,0 +1,1053 @@
+/***************************************************************************
+ * Copyright (C) 2010 by Antonio Borneo <borneo.antonio@gmail.com> *
+ * Modified by Megan Wachs <megan@sifive.com> from the original stmsmi.c *
+ * *
+ * 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/>. *
+ ***************************************************************************/
+
+/* The Freedom E SPI controller is a SPI bus controller
+ * specifically designed for SPI Flash Memories on Freedom E platforms.
+ *
+ * Two working modes are available:
+ * - SW mode: the SPI is controlled by SW. Any custom commands can be sent
+ * on the bus. Writes are only possible in this mode.
+ * - HW mode: Memory content is directly
+ * accessible in CPU memory space. CPU can read and execute memory content.
+ */
+
+/* ATTENTION:
+ * To have flash memory mapped in CPU memory space, the controller
+ * must have "HW mode" enabled.
+ * 1) The command "reset init" has to initialize the controller and put
+ * it in HW mode (this is actually the default out of reset for Freedom E systems).
+ * 2) every command in this file have to return to prompt in HW mode. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include "spi.h"
+#include <jtag/jtag.h>
+#include <helper/time_support.h>
+#include <target/algorithm.h>
+#include "target/riscv/riscv.h"
+
+/* Register offsets */
+
+#define FESPI_REG_SCKDIV 0x00
+#define FESPI_REG_SCKMODE 0x04
+#define FESPI_REG_CSID 0x10
+#define FESPI_REG_CSDEF 0x14
+#define FESPI_REG_CSMODE 0x18
+
+#define FESPI_REG_DCSSCK 0x28
+#define FESPI_REG_DSCKCS 0x2a
+#define FESPI_REG_DINTERCS 0x2c
+#define FESPI_REG_DINTERXFR 0x2e
+
+#define FESPI_REG_FMT 0x40
+#define FESPI_REG_TXFIFO 0x48
+#define FESPI_REG_RXFIFO 0x4c
+#define FESPI_REG_TXCTRL 0x50
+#define FESPI_REG_RXCTRL 0x54
+
+#define FESPI_REG_FCTRL 0x60
+#define FESPI_REG_FFMT 0x64
+
+#define FESPI_REG_IE 0x70
+#define FESPI_REG_IP 0x74
+
+/* Fields */
+
+#define FESPI_SCK_POL 0x1
+#define FESPI_SCK_PHA 0x2
+
+#define FESPI_FMT_PROTO(x) ((x) & 0x3)
+#define FESPI_FMT_ENDIAN(x) (((x) & 0x1) << 2)
+#define FESPI_FMT_DIR(x) (((x) & 0x1) << 3)
+#define FESPI_FMT_LEN(x) (((x) & 0xf) << 16)
+
+/* TXCTRL register */
+#define FESPI_TXWM(x) ((x) & 0xffff)
+/* RXCTRL register */
+#define FESPI_RXWM(x) ((x) & 0xffff)
+
+#define FESPI_IP_TXWM 0x1
+#define FESPI_IP_RXWM 0x2
+
+#define FESPI_FCTRL_EN 0x1
+
+#define FESPI_INSN_CMD_EN 0x1
+#define FESPI_INSN_ADDR_LEN(x) (((x) & 0x7) << 1)
+#define FESPI_INSN_PAD_CNT(x) (((x) & 0xf) << 4)
+#define FESPI_INSN_CMD_PROTO(x) (((x) & 0x3) << 8)
+#define FESPI_INSN_ADDR_PROTO(x) (((x) & 0x3) << 10)
+#define FESPI_INSN_DATA_PROTO(x) (((x) & 0x3) << 12)
+#define FESPI_INSN_CMD_CODE(x) (((x) & 0xff) << 16)
+#define FESPI_INSN_PAD_CODE(x) (((x) & 0xff) << 24)
+
+/* Values */
+
+#define FESPI_CSMODE_AUTO 0
+#define FESPI_CSMODE_HOLD 2
+#define FESPI_CSMODE_OFF 3
+
+#define FESPI_DIR_RX 0
+#define FESPI_DIR_TX 1
+
+#define FESPI_PROTO_S 0
+#define FESPI_PROTO_D 1
+#define FESPI_PROTO_Q 2
+
+#define FESPI_ENDIAN_MSB 0
+#define FESPI_ENDIAN_LSB 1
+
+
+/* Timeout in ms */
+#define FESPI_CMD_TIMEOUT (100)
+#define FESPI_PROBE_TIMEOUT (100)
+#define FESPI_MAX_TIMEOUT (3000)
+
+
+struct fespi_flash_bank {
+ int probed;
+ target_addr_t ctrl_base;
+ const struct flash_device *dev;
+};
+
+struct fespi_target {
+ char *name;
+ uint32_t tap_idcode;
+ uint32_t ctrl_base;
+};
+
+/* TODO !!! What is the right naming convention here? */
+static const struct fespi_target target_devices[] = {
+ /* name, tap_idcode, ctrl_base */
+ { "Freedom E300 SPI Flash", 0x10e31913 , 0x10014000 },
+ { NULL, 0, 0 }
+};
+
+FLASH_BANK_COMMAND_HANDLER(fespi_flash_bank_command)
+{
+ struct fespi_flash_bank *fespi_info;
+
+ LOG_DEBUG("%s", __func__);
+
+ if (CMD_ARGC < 6)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ fespi_info = malloc(sizeof(struct fespi_flash_bank));
+ if (fespi_info == NULL) {
+ LOG_ERROR("not enough memory");
+ return ERROR_FAIL;
+ }
+
+ bank->driver_priv = fespi_info;
+ fespi_info->probed = 0;
+ fespi_info->ctrl_base = 0;
+ if (CMD_ARGC >= 7) {
+ int temp;
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[6], temp);
+ fespi_info->ctrl_base = (uint32_t) temp;
+ LOG_DEBUG("ASSUMING FESPI device at ctrl_base = 0x%" TARGET_PRIxADDR,
+ fespi_info->ctrl_base);
+ }
+
+ return ERROR_OK;
+}
+
+static int fespi_read_reg(struct flash_bank *bank, uint32_t *value, target_addr_t address)
+{
+ struct target *target = bank->target;
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+
+ int result = target_read_u32(target, fespi_info->ctrl_base + address, value);
+ if (result != ERROR_OK) {
+ LOG_ERROR("fespi_read_reg() error at 0x%" TARGET_PRIxADDR,
+ fespi_info->ctrl_base + address);
+ return result;
+ }
+ return ERROR_OK;
+}
+
+static int fespi_write_reg(struct flash_bank *bank, target_addr_t address, uint32_t value)
+{ \
+ struct target *target = bank->target;
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+
+ int result = target_write_u32(target, fespi_info->ctrl_base + address, value);
+ if (result != ERROR_OK) {
+ LOG_ERROR("fespi_write_reg() error writing 0x%x to 0x%" TARGET_PRIxADDR,
+ value, fespi_info->ctrl_base + address);
+ return result;
+ }
+ return ERROR_OK;
+}
+
+static int fespi_disable_hw_mode(struct flash_bank *bank)
+{
+ uint32_t fctrl;
+ if (fespi_read_reg(bank, &fctrl, FESPI_REG_FCTRL) != ERROR_OK)
+ return ERROR_FAIL;
+ return fespi_write_reg(bank, FESPI_REG_FCTRL, fctrl & ~FESPI_FCTRL_EN);
+}
+
+static int fespi_enable_hw_mode(struct flash_bank *bank)
+{
+ uint32_t fctrl;
+ if (fespi_read_reg(bank, &fctrl, FESPI_REG_FCTRL) != ERROR_OK)
+ return ERROR_FAIL;
+ return fespi_write_reg(bank, FESPI_REG_FCTRL, fctrl | FESPI_FCTRL_EN);
+}
+
+static int fespi_set_dir(struct flash_bank *bank, bool dir)
+{
+ uint32_t fmt;
+ if (fespi_read_reg(bank, &fmt, FESPI_REG_FMT) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return fespi_write_reg(bank, FESPI_REG_FMT,
+ (fmt & ~(FESPI_FMT_DIR(0xFFFFFFFF))) | FESPI_FMT_DIR(dir));
+}
+
+static int fespi_txwm_wait(struct flash_bank *bank)
+{
+ int64_t start = timeval_ms();
+
+ while (1) {
+ uint32_t ip;
+ if (fespi_read_reg(bank, &ip, FESPI_REG_IP) != ERROR_OK)
+ return ERROR_FAIL;
+ if (ip & FESPI_IP_TXWM)
+ break;
+ int64_t now = timeval_ms();
+ if (now - start > 1000) {
+ LOG_ERROR("ip.txwm didn't get set.");
+ return ERROR_TARGET_TIMEOUT;
+ }
+ }
+
+ return ERROR_OK;
+}
+
+static int fespi_tx(struct flash_bank *bank, uint8_t in)
+{
+ int64_t start = timeval_ms();
+
+ while (1) {
+ uint32_t txfifo;
+ if (fespi_read_reg(bank, &txfifo, FESPI_REG_TXFIFO) != ERROR_OK)
+ return ERROR_FAIL;
+ if (!(txfifo >> 31))
+ break;
+ int64_t now = timeval_ms();
+ if (now - start > 1000) {
+ LOG_ERROR("txfifo stayed negative.");
+ return ERROR_TARGET_TIMEOUT;
+ }
+ }
+
+ return fespi_write_reg(bank, FESPI_REG_TXFIFO, in);
+}
+
+static int fespi_rx(struct flash_bank *bank, uint8_t *out)
+{
+ int64_t start = timeval_ms();
+ uint32_t value;
+
+ while (1) {
+ if (fespi_read_reg(bank, &value, FESPI_REG_RXFIFO) != ERROR_OK)
+ return ERROR_FAIL;
+ if (!(value >> 31))
+ break;
+ int64_t now = timeval_ms();
+ if (now - start > 1000) {
+ LOG_ERROR("rxfifo didn't go positive (value=0x%x).", value);
+ return ERROR_TARGET_TIMEOUT;
+ }
+ }
+
+ if (out)
+ *out = value & 0xff;
+
+ return ERROR_OK;
+}
+
+/* TODO!!! Why don't we need to call this after writing? */
+static int fespi_wip(struct flash_bank *bank, int timeout)
+{
+ int64_t endtime;
+
+ fespi_set_dir(bank, FESPI_DIR_RX);
+
+ if (fespi_write_reg(bank, FESPI_REG_CSMODE, FESPI_CSMODE_HOLD) != ERROR_OK)
+ return ERROR_FAIL;
+ endtime = timeval_ms() + timeout;
+
+ fespi_tx(bank, SPIFLASH_READ_STATUS);
+ if (fespi_rx(bank, NULL) != ERROR_OK)
+ return ERROR_FAIL;
+
+ do {
+ alive_sleep(1);
+
+ fespi_tx(bank, 0);
+ uint8_t rx;
+ if (fespi_rx(bank, &rx) != ERROR_OK)
+ return ERROR_FAIL;
+ if ((rx & SPIFLASH_BSY_BIT) == 0) {
+ if (fespi_write_reg(bank, FESPI_REG_CSMODE, FESPI_CSMODE_AUTO) != ERROR_OK)
+ return ERROR_FAIL;
+ fespi_set_dir(bank, FESPI_DIR_TX);
+ return ERROR_OK;
+ }
+ } while (timeval_ms() < endtime);
+
+ LOG_ERROR("timeout");
+ return ERROR_FAIL;
+}
+
+static int fespi_erase_sector(struct flash_bank *bank, int sector)
+{
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+ int retval;
+
+ retval = fespi_tx(bank, SPIFLASH_WRITE_ENABLE);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = fespi_txwm_wait(bank);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if (fespi_write_reg(bank, FESPI_REG_CSMODE, FESPI_CSMODE_HOLD) != ERROR_OK)
+ return ERROR_FAIL;
+ retval = fespi_tx(bank, fespi_info->dev->erase_cmd);
+ if (retval != ERROR_OK)
+ return retval;
+ sector = bank->sectors[sector].offset;
+ retval = fespi_tx(bank, sector >> 16);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = fespi_tx(bank, sector >> 8);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = fespi_tx(bank, sector);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = fespi_txwm_wait(bank);
+ if (retval != ERROR_OK)
+ return retval;
+ if (fespi_write_reg(bank, FESPI_REG_CSMODE, FESPI_CSMODE_AUTO) != ERROR_OK)
+ return ERROR_FAIL;
+
+ retval = fespi_wip(bank, FESPI_MAX_TIMEOUT);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int fespi_erase(struct flash_bank *bank, int first, int last)
+{
+ struct target *target = bank->target;
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+ int retval = ERROR_OK;
+ int sector;
+
+ LOG_DEBUG("%s: from sector %d to sector %d", __func__, first, last);
+
+ if (target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if ((first < 0) || (last < first) || (last >= bank->num_sectors)) {
+ LOG_ERROR("Flash sector invalid");
+ return ERROR_FLASH_SECTOR_INVALID;
+ }
+
+ if (!(fespi_info->probed)) {
+ LOG_ERROR("Flash bank not probed");
+ return ERROR_FLASH_BANK_NOT_PROBED;
+ }
+
+ for (sector = first; sector <= last; sector++) {
+ if (bank->sectors[sector].is_protected) {
+ LOG_ERROR("Flash sector %d protected", sector);
+ return ERROR_FAIL;
+ }
+ }
+
+ if (fespi_info->dev->erase_cmd == 0x00)
+ return ERROR_FLASH_OPER_UNSUPPORTED;
+
+ if (fespi_write_reg(bank, FESPI_REG_TXCTRL, FESPI_TXWM(1)) != ERROR_OK)
+ return ERROR_FAIL;
+ retval = fespi_txwm_wait(bank);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("WM Didn't go high before attempting.");
+ return retval;
+ }
+
+ /* Disable Hardware accesses*/
+ if (fespi_disable_hw_mode(bank) != ERROR_OK)
+ return ERROR_FAIL;
+
+ /* poll WIP */
+ retval = fespi_wip(bank, FESPI_PROBE_TIMEOUT);
+ if (retval != ERROR_OK)
+ goto done;
+
+ for (sector = first; sector <= last; sector++) {
+ retval = fespi_erase_sector(bank, sector);
+ if (retval != ERROR_OK)
+ goto done;
+ keep_alive();
+ }
+
+ /* Switch to HW mode before return to prompt */
+done:
+ if (fespi_enable_hw_mode(bank) != ERROR_OK)
+ return ERROR_FAIL;
+ return retval;
+}
+
+static int fespi_protect(struct flash_bank *bank, int set,
+ int first, int last)
+{
+ int sector;
+
+ for (sector = first; sector <= last; sector++)
+ bank->sectors[sector].is_protected = set;
+ return ERROR_OK;
+}
+
+static int slow_fespi_write_buffer(struct flash_bank *bank,
+ const uint8_t *buffer, uint32_t offset, uint32_t len)
+{
+ uint32_t ii;
+
+ if (offset & 0xFF000000) {
+ LOG_ERROR("FESPI interface does not support greater than 3B addressing, can't write to offset 0x%x",
+ offset);
+ return ERROR_FAIL;
+ }
+
+ /* TODO!!! assert that len < page size */
+
+ fespi_tx(bank, SPIFLASH_WRITE_ENABLE);
+ fespi_txwm_wait(bank);
+
+ if (fespi_write_reg(bank, FESPI_REG_CSMODE, FESPI_CSMODE_HOLD) != ERROR_OK)
+ return ERROR_FAIL;
+
+ fespi_tx(bank, SPIFLASH_PAGE_PROGRAM);
+
+ fespi_tx(bank, offset >> 16);
+ fespi_tx(bank, offset >> 8);
+ fespi_tx(bank, offset);
+
+ for (ii = 0; ii < len; ii++)
+ fespi_tx(bank, buffer[ii]);
+
+ fespi_txwm_wait(bank);
+
+ if (fespi_write_reg(bank, FESPI_REG_CSMODE, FESPI_CSMODE_AUTO) != ERROR_OK)
+ return ERROR_FAIL;
+
+ keep_alive();
+
+ return ERROR_OK;
+}
+
+static const uint8_t algorithm_bin[] = {
+#include "../../../contrib/loaders/flash/fespi/fespi.inc"
+};
+#define STEP_EXIT 4
+#define STEP_TX 8
+#define STEP_TXWM_WAIT 12
+#define STEP_WRITE_REG 16
+#define STEP_WIP_WAIT 20
+#define STEP_SET_DIR 24
+#define STEP_NOP 0xff
+
+struct algorithm_steps {
+ unsigned size;
+ unsigned used;
+ uint8_t **steps;
+};
+
+static struct algorithm_steps *as_new(void)
+{
+ struct algorithm_steps *as = calloc(1, sizeof(struct algorithm_steps));
+ as->size = 8;
+ as->steps = malloc(as->size * sizeof(as->steps[0]));
+ return as;
+}
+
+static struct algorithm_steps *as_delete(struct algorithm_steps *as)
+{
+ for (unsigned step = 0; step < as->used; step++) {
+ free(as->steps[step]);
+ as->steps[step] = NULL;
+ }
+ free(as->steps);
+ free(as);
+ return NULL;
+}
+
+static int as_empty(struct algorithm_steps *as)
+{
+ for (unsigned s = 0; s < as->used; s++) {
+ if (as->steps[s][0] != STEP_NOP)
+ return 0;
+ }
+ return 1;
+}
+
+/* Return size of compiled program. */
+static unsigned as_compile(struct algorithm_steps *as, uint8_t *target,
+ unsigned target_size)
+{
+ unsigned offset = 0;
+ bool finish_early = false;
+ for (unsigned s = 0; s < as->used && !finish_early; s++) {
+ unsigned bytes_left = target_size - offset;
+ switch (as->steps[s][0]) {
+ case STEP_NOP:
+ break;
+ case STEP_TX:
+ {
+ unsigned size = as->steps[s][1];
+ if (size + 3 > bytes_left) {
+ finish_early = true;
+ break;
+ }
+ memcpy(target + offset, as->steps[s], size + 2);
+ offset += size + 2;
+ break;
+ }
+ case STEP_WRITE_REG:
+ if (4 > bytes_left) {
+ finish_early = true;
+ break;
+ }
+ memcpy(target + offset, as->steps[s], 3);
+ offset += 3;
+ break;
+ case STEP_SET_DIR:
+ if (3 > bytes_left) {
+ finish_early = true;
+ break;
+ }
+ memcpy(target + offset, as->steps[s], 2);
+ offset += 2;
+ break;
+ case STEP_TXWM_WAIT:
+ case STEP_WIP_WAIT:
+ if (2 > bytes_left) {
+ finish_early = true;
+ break;
+ }
+ memcpy(target + offset, as->steps[s], 1);
+ offset += 1;
+ break;
+ default:
+ assert(0);
+ }
+ if (!finish_early)
+ as->steps[s][0] = STEP_NOP;
+ }
+ assert(offset + 1 <= target_size);
+ target[offset++] = STEP_EXIT;
+
+ LOG_DEBUG("%d-byte program:", offset);
+ for (unsigned i = 0; i < offset;) {
+ char buf[80];
+ for (unsigned x = 0; i < offset && x < 16; x++, i++)
+ sprintf(buf + x*3, "%02x ", target[i]);
+ LOG_DEBUG("%s", buf);
+ }
+
+ return offset;
+}
+
+static void as_add_step(struct algorithm_steps *as, uint8_t *step)
+{
+ if (as->used == as->size) {
+ as->size *= 2;
+ as->steps = realloc(as->steps, sizeof(as->steps[0]) * as->size);
+ LOG_DEBUG("Increased size to 0x%x", as->size);
+ }
+ as->steps[as->used] = step;
+ as->used++;
+}
+
+static void as_add_tx(struct algorithm_steps *as, unsigned count, const uint8_t *data)
+{
+ LOG_DEBUG("count=%d", count);
+ while (count > 0) {
+ unsigned step_count = MIN(count, 255);
+ uint8_t *step = malloc(step_count + 2);
+ step[0] = STEP_TX;
+ step[1] = step_count;
+ memcpy(step + 2, data, step_count);
+ as_add_step(as, step);
+ data += step_count;
+ count -= step_count;
+ }
+}
+
+static void as_add_tx1(struct algorithm_steps *as, uint8_t byte)
+{
+ uint8_t data[1];
+ data[0] = byte;
+ as_add_tx(as, 1, data);
+}
+
+static void as_add_write_reg(struct algorithm_steps *as, uint8_t offset, uint8_t data)
+{
+ uint8_t *step = malloc(3);
+ step[0] = STEP_WRITE_REG;
+ step[1] = offset;
+ step[2] = data;
+ as_add_step(as, step);
+}
+
+static void as_add_txwm_wait(struct algorithm_steps *as)
+{
+ uint8_t *step = malloc(1);
+ step[0] = STEP_TXWM_WAIT;
+ as_add_step(as, step);
+}
+
+static void as_add_wip_wait(struct algorithm_steps *as)
+{
+ uint8_t *step = malloc(1);
+ step[0] = STEP_WIP_WAIT;
+ as_add_step(as, step);
+}
+
+static void as_add_set_dir(struct algorithm_steps *as, bool dir)
+{
+ uint8_t *step = malloc(2);
+ step[0] = STEP_SET_DIR;
+ step[1] = FESPI_FMT_DIR(dir);
+ as_add_step(as, step);
+}
+
+/* This should write something less than or equal to a page.*/
+static int steps_add_buffer_write(struct algorithm_steps *as,
+ const uint8_t *buffer, uint32_t chip_offset, uint32_t len)
+{
+ if (chip_offset & 0xFF000000) {
+ LOG_ERROR("FESPI interface does not support greater than 3B addressing, can't write to offset 0x%x",
+ chip_offset);
+ return ERROR_FAIL;
+ }
+
+ as_add_tx1(as, SPIFLASH_WRITE_ENABLE);
+ as_add_txwm_wait(as);
+ as_add_write_reg(as, FESPI_REG_CSMODE, FESPI_CSMODE_HOLD);
+
+ uint8_t setup[] = {
+ SPIFLASH_PAGE_PROGRAM,
+ chip_offset >> 16,
+ chip_offset >> 8,
+ chip_offset,
+ };
+ as_add_tx(as, sizeof(setup), setup);
+
+ as_add_tx(as, len, buffer);
+ as_add_txwm_wait(as);
+ as_add_write_reg(as, FESPI_REG_CSMODE, FESPI_CSMODE_AUTO);
+
+ /* fespi_wip() */
+ as_add_set_dir(as, FESPI_DIR_RX);
+ as_add_write_reg(as, FESPI_REG_CSMODE, FESPI_CSMODE_HOLD);
+ as_add_wip_wait(as);
+ as_add_write_reg(as, FESPI_REG_CSMODE, FESPI_CSMODE_AUTO);
+ as_add_set_dir(as, FESPI_DIR_TX);
+
+ return ERROR_OK;
+}
+
+static int steps_execute(struct algorithm_steps *as,
+ struct flash_bank *bank, struct working_area *algorithm_wa,
+ struct working_area *data_wa)
+{
+ struct target *target = bank->target;
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+ uint32_t ctrl_base = fespi_info->ctrl_base;
+ int xlen = riscv_xlen(target);
+
+ struct reg_param reg_params[2];
+ init_reg_param(&reg_params[0], "a0", xlen, PARAM_OUT);
+ init_reg_param(&reg_params[1], "a1", xlen, PARAM_OUT);
+ buf_set_u64(reg_params[0].value, 0, xlen, ctrl_base);
+ buf_set_u64(reg_params[1].value, 0, xlen, data_wa->address);
+
+ int retval = ERROR_OK;
+ while (!as_empty(as)) {
+ keep_alive();
+ uint8_t *data_buf = malloc(data_wa->size);
+ unsigned bytes = as_compile(as, data_buf, data_wa->size);
+ retval = target_write_buffer(target, data_wa->address, bytes,
+ data_buf);
+ free(data_buf);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Failed to write data to 0x%" TARGET_PRIxADDR ": %d",
+ data_wa->address, retval);
+ goto exit;
+ }
+
+ retval = target_run_algorithm(target, 0, NULL, 2, reg_params,
+ algorithm_wa->address, algorithm_wa->address + 4,
+ 10000, NULL);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Failed to execute algorithm at 0x%" TARGET_PRIxADDR ": %d",
+ algorithm_wa->address, retval);
+ goto exit;
+ }
+ }
+
+exit:
+ destroy_reg_param(&reg_params[1]);
+ destroy_reg_param(&reg_params[0]);
+ return retval;
+}
+
+static int fespi_write(struct flash_bank *bank, const uint8_t *buffer,
+ uint32_t offset, uint32_t count)
+{
+ struct target *target = bank->target;
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+ uint32_t cur_count, page_size, page_offset;
+ int sector;
+ int retval = ERROR_OK;
+
+ LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32,
+ __func__, offset, count);
+
+ if (target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (offset + count > fespi_info->dev->size_in_bytes) {
+ LOG_WARNING("Write past end of flash. Extra data discarded.");
+ count = fespi_info->dev->size_in_bytes - offset;
+ }
+
+ /* Check sector protection */
+ for (sector = 0; sector < bank->num_sectors; sector++) {
+ /* Start offset in or before this sector? */
+ /* End offset in or behind this sector? */
+ if ((offset <
+ (bank->sectors[sector].offset + bank->sectors[sector].size))
+ && ((offset + count - 1) >= bank->sectors[sector].offset)
+ && bank->sectors[sector].is_protected) {
+ LOG_ERROR("Flash sector %d protected", sector);
+ return ERROR_FAIL;
+ }
+ }
+
+ struct working_area *algorithm_wa;
+ if (target_alloc_working_area(target, sizeof(algorithm_bin),
+ &algorithm_wa) != ERROR_OK) {
+ LOG_WARNING("Couldn't allocate %zd-byte working area.",
+ sizeof(algorithm_bin));
+ algorithm_wa = NULL;
+ } else {
+ retval = target_write_buffer(target, algorithm_wa->address,
+ sizeof(algorithm_bin), algorithm_bin);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Failed to write code to 0x%" TARGET_PRIxADDR ": %d",
+ algorithm_wa->address, retval);
+ target_free_working_area(target, algorithm_wa);
+ algorithm_wa = NULL;
+ }
+ }
+
+ struct working_area *data_wa = NULL;
+ unsigned data_wa_size = 2 * count;
+ while (1) {
+ if (data_wa_size < 128) {
+ LOG_WARNING("Couldn't allocate data working area.");
+ target_free_working_area(target, algorithm_wa);
+ algorithm_wa = NULL;
+ }
+ if (target_alloc_working_area_try(target, data_wa_size, &data_wa) ==
+ ERROR_OK) {
+ break;
+ }
+
+ data_wa_size /= 2;
+ }
+
+ /* If no valid page_size, use reasonable default. */
+ page_size = fespi_info->dev->pagesize ?
+ fespi_info->dev->pagesize : SPIFLASH_DEF_PAGESIZE;
+
+ fespi_txwm_wait(bank);
+
+ /* Disable Hardware accesses*/
+ if (fespi_disable_hw_mode(bank) != ERROR_OK)
+ return ERROR_FAIL;
+
+ struct algorithm_steps *as = as_new();
+
+ /* poll WIP */
+ retval = fespi_wip(bank, FESPI_PROBE_TIMEOUT);
+ if (retval != ERROR_OK)
+ goto err;
+
+ page_offset = offset % page_size;
+ /* central part, aligned words */
+ while (count > 0) {
+ /* clip block at page boundary */
+ if (page_offset + count > page_size)
+ cur_count = page_size - page_offset;
+ else
+ cur_count = count;
+
+ if (algorithm_wa)
+ retval = steps_add_buffer_write(as, buffer, offset, cur_count);
+ else
+ retval = slow_fespi_write_buffer(bank, buffer, offset, cur_count);
+ if (retval != ERROR_OK)
+ goto err;
+
+ page_offset = 0;
+ buffer += cur_count;
+ offset += cur_count;
+ count -= cur_count;
+ }
+
+ if (algorithm_wa)
+ retval = steps_execute(as, bank, algorithm_wa, data_wa);
+
+err:
+ if (algorithm_wa) {
+ target_free_working_area(target, data_wa);
+ target_free_working_area(target, algorithm_wa);
+ }
+
+ as_delete(as);
+
+ /* Switch to HW mode before return to prompt */
+ if (fespi_enable_hw_mode(bank) != ERROR_OK)
+ return ERROR_FAIL;
+ return retval;
+}
+
+/* Return ID of flash device */
+/* On exit, SW mode is kept */
+static int fespi_read_flash_id(struct flash_bank *bank, uint32_t *id)
+{
+ struct target *target = bank->target;
+ int retval;
+
+ if (target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ fespi_txwm_wait(bank);
+
+ /* poll WIP */
+ retval = fespi_wip(bank, FESPI_PROBE_TIMEOUT);
+ if (retval != ERROR_OK)
+ return retval;
+
+ fespi_set_dir(bank, FESPI_DIR_RX);
+
+ /* Send SPI command "read ID" */
+ if (fespi_write_reg(bank, FESPI_REG_CSMODE, FESPI_CSMODE_HOLD) != ERROR_OK)
+ return ERROR_FAIL;
+
+ fespi_tx(bank, SPIFLASH_READ_ID);
+ /* Send dummy bytes to actually read the ID.*/
+ fespi_tx(bank, 0);
+ fespi_tx(bank, 0);
+ fespi_tx(bank, 0);
+
+ /* read ID from Receive Register */
+ *id = 0;
+ if (fespi_rx(bank, NULL) != ERROR_OK)
+ return ERROR_FAIL;
+ uint8_t rx;
+ if (fespi_rx(bank, &rx) != ERROR_OK)
+ return ERROR_FAIL;
+ *id = rx;
+ if (fespi_rx(bank, &rx) != ERROR_OK)
+ return ERROR_FAIL;
+ *id |= (rx << 8);
+ if (fespi_rx(bank, &rx) != ERROR_OK)
+ return ERROR_FAIL;
+ *id |= (rx << 16);
+
+ if (fespi_write_reg(bank, FESPI_REG_CSMODE, FESPI_CSMODE_AUTO) != ERROR_OK)
+ return ERROR_FAIL;
+
+ fespi_set_dir(bank, FESPI_DIR_TX);
+
+ return ERROR_OK;
+}
+
+static int fespi_probe(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+ struct flash_sector *sectors;
+ uint32_t id = 0; /* silence uninitialized warning */
+ const struct fespi_target *target_device;
+ int retval;
+ uint32_t sectorsize;
+
+ if (fespi_info->probed)
+ free(bank->sectors);
+ fespi_info->probed = 0;
+
+ if (fespi_info->ctrl_base == 0) {
+ for (target_device = target_devices ; target_device->name ; ++target_device)
+ if (target_device->tap_idcode == target->tap->idcode)
+ break;
+
+ if (!target_device->name) {
+ LOG_ERROR("Device ID 0x%" PRIx32 " is not known as FESPI capable",
+ target->tap->idcode);
+ return ERROR_FAIL;
+ }
+
+ fespi_info->ctrl_base = target_device->ctrl_base;
+
+ LOG_DEBUG("Valid FESPI on device %s at address 0x%" PRIx32,
+ target_device->name, bank->base);
+
+ } else {
+ LOG_DEBUG("Assuming FESPI as specified at address 0x%" TARGET_PRIxADDR
+ " with ctrl at 0x%x", fespi_info->ctrl_base, bank->base);
+ }
+
+ /* read and decode flash ID; returns in SW mode */
+ if (fespi_write_reg(bank, FESPI_REG_TXCTRL, FESPI_TXWM(1)) != ERROR_OK)
+ return ERROR_FAIL;
+ fespi_set_dir(bank, FESPI_DIR_TX);
+
+ /* Disable Hardware accesses*/
+ if (fespi_disable_hw_mode(bank) != ERROR_OK)
+ return ERROR_FAIL;
+
+ retval = fespi_read_flash_id(bank, &id);
+
+ if (fespi_enable_hw_mode(bank) != ERROR_OK)
+ return ERROR_FAIL;
+ if (retval != ERROR_OK)
+ return retval;
+
+ fespi_info->dev = NULL;
+ for (const struct flash_device *p = flash_devices; p->name ; p++)
+ if (p->device_id == id) {
+ fespi_info->dev = p;
+ break;
+ }
+
+ if (!fespi_info->dev) {
+ LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", id);
+ return ERROR_FAIL;
+ }
+
+ LOG_INFO("Found flash device \'%s\' (ID 0x%08" PRIx32 ")",
+ fespi_info->dev->name, fespi_info->dev->device_id);
+
+ /* Set correct size value */
+ bank->size = fespi_info->dev->size_in_bytes;
+
+ if (bank->size <= (1UL << 16))
+ LOG_WARNING("device needs 2-byte addresses - not implemented");
+ if (bank->size > (1UL << 24))
+ LOG_WARNING("device needs paging or 4-byte addresses - not implemented");
+
+ /* if no sectors, treat whole bank as single sector */
+ sectorsize = fespi_info->dev->sectorsize ?
+ fespi_info->dev->sectorsize : fespi_info->dev->size_in_bytes;
+
+ /* create and fill sectors array */
+ bank->num_sectors = fespi_info->dev->size_in_bytes / sectorsize;
+ sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
+ if (sectors == NULL) {
+ LOG_ERROR("not enough memory");
+ return ERROR_FAIL;
+ }
+
+ for (int sector = 0; sector < bank->num_sectors; sector++) {
+ sectors[sector].offset = sector * sectorsize;
+ sectors[sector].size = sectorsize;
+ sectors[sector].is_erased = -1;
+ sectors[sector].is_protected = 0;
+ }
+
+ bank->sectors = sectors;
+ fespi_info->probed = 1;
+ return ERROR_OK;
+}
+
+static int fespi_auto_probe(struct flash_bank *bank)
+{
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+ if (fespi_info->probed)
+ return ERROR_OK;
+ return fespi_probe(bank);
+}
+
+static int fespi_protect_check(struct flash_bank *bank)
+{
+ /* Nothing to do. Protection is only handled in SW. */
+ return ERROR_OK;
+}
+
+static int get_fespi_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+
+ if (!(fespi_info->probed)) {
+ snprintf(buf, buf_size,
+ "\nFESPI flash bank not probed yet\n");
+ return ERROR_OK;
+ }
+
+ snprintf(buf, buf_size, "\nFESPI flash information:\n"
+ " Device \'%s\' (ID 0x%08" PRIx32 ")\n",
+ fespi_info->dev->name, fespi_info->dev->device_id);
+
+ return ERROR_OK;
+}
+
+struct flash_driver fespi_flash = {
+ .name = "fespi",
+ .flash_bank_command = fespi_flash_bank_command,
+ .erase = fespi_erase,
+ .protect = fespi_protect,
+ .write = fespi_write,
+ .read = default_flash_read,
+ .probe = fespi_probe,
+ .auto_probe = fespi_auto_probe,
+ .erase_check = default_flash_blank_check,
+ .protect_check = fespi_protect_check,
+ .info = get_fespi_info,
+ .free_driver_priv = default_flash_free_driver_priv
+};