diff --git a/README b/README
index 30443d37c7e3b1fd76db350e1c8e068a5e618bf8..00e83bbf4aafdf2eaf34adcf8e853c9b8fb6ac67 100644
--- a/README
+++ b/README
@@ -123,12 +123,12 @@ EJTAG, NDS32, XScale, Intel Quark.
 Flash drivers
 -------------
 
-ADUC702x, AT91SAM, ATH79, AVR, CFI, DSP5680xx, EFM32, EM357, eSi-TSMC,
-FM3, FM4, Kinetis, LPC8xx/LPC1xxx/LPC2xxx/LPC541xx, LPC2900, LPCSPIFI,
-Marvell QSPI, Milandr, NIIET, NuMicro, PIC32mx, PSoC4, PSoC5LP, SiM3x,
-Stellaris, STM32, STMSMI, STR7x, STR9x, nRF51; NAND controllers of
-AT91SAM9, LPC3180, LPC32xx, i.MX31, MXC, NUC910, Orion/Kirkwood,
-S3C24xx, S3C6400, XMC1xxx, XMC4xxx.
+ADUC702x, AT91SAM, ATH79, AVR, CFI, DSP5680xx, EFM32, EM357, eSi-TSMC, FM3,
+FM4, Freedom E SPI, Kinetis, LPC8xx/LPC1xxx/LPC2xxx/LPC541xx, LPC2900,
+LPCSPIFI, Marvell QSPI, Milandr, NIIET, NuMicro, PIC32mx, PSoC4, PSoC5LP,
+SiM3x, Stellaris, STM32, STMSMI, STR7x, STR9x, nRF51; NAND controllers of
+AT91SAM9, LPC3180, LPC32xx, i.MX31, MXC, NUC910, Orion/Kirkwood, S3C24xx,
+S3C6400, XMC1xxx, XMC4xxx.
 
 
 ==================
diff --git a/contrib/loaders/flash/fespi/Makefile b/contrib/loaders/flash/fespi/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..4d2ab51d7619336fa7283b38eebda7f31acb45e1
--- /dev/null
+++ b/contrib/loaders/flash/fespi/Makefile
@@ -0,0 +1,28 @@
+BIN2C = ../../../../src/helper/bin2char.sh
+
+CROSS_COMPILE ?= riscv64-unknown-elf-
+
+CC=$(CROSS_COMPILE)gcc
+OBJCOPY=$(CROSS_COMPILE)objcopy
+OBJDUMP=$(CROSS_COMPILE)objdump
+
+CFLAGS = -march=rv32i -mabi=ilp32 -x assembler-with-cpp -nostdlib -nostartfiles
+
+all: fespi.inc
+
+.PHONY: clean
+
+%.elf: %.S
+	$(CC) $(CFLAGS) $< -o $@
+
+%.lst: %.elf
+	$(OBJDUMP) -S $< > $@
+
+%.bin: %.elf
+	$(OBJCOPY) -Obinary $< $@
+
+%.inc: %.bin
+	$(BIN2C) < $< > $@
+
+clean:
+	-rm -f *.elf *.lst *.bin *.inc
diff --git a/contrib/loaders/flash/fespi/fespi.S b/contrib/loaders/flash/fespi/fespi.S
new file mode 100644
index 0000000000000000000000000000000000000000..d68e65ef81d1816bb17b102f3c9b113ac43ae288
--- /dev/null
+++ b/contrib/loaders/flash/fespi/fespi.S
@@ -0,0 +1,99 @@
+#define SPIFLASH_READ_STATUS	0x05 // Read Status Register
+#define SPIFLASH_BSY_BIT		0x00000001 // WIP Bit of SPI SR on SMI SR
+
+// Register offsets
+#define FESPI_REG_FMT             0x40
+#define FESPI_REG_TXFIFO          0x48
+#define FESPI_REG_RXFIFO          0x4c
+#define FESPI_REG_IP              0x74
+
+// Fields
+#define FESPI_IP_TXWM             0x1
+#define FESPI_FMT_DIR(x)          (((x) & 0x1) << 3)
+
+// To enter, jump to the start of command_table (ie. offset 0).
+//      a0 - FESPI base address
+//      a1 - start address of buffer
+
+// The buffer contains a "program" in byte sequences. The first byte in a
+// sequence determines the operation. Some operation will read more data from
+// the program, while some will not. The operation byte is the offset into
+// command_table, so eg. 4 means exit, 8 means transmit, and so on.
+
+		.global _start
+_start:
+command_table:
+		j       main            // 0
+		ebreak                  // 4
+		j       tx              // 8
+		j       txwm_wait       // 12
+		j       write_reg       // 16
+		j		wip_wait		// 20
+		j		set_dir			// 24
+
+// Execute the program.
+main:
+		lbu     t0, 0(a1)
+		addi    a1, a1, 1
+		la      t1, command_table
+		add     t0, t0, t1
+		jr      t0
+
+// Read 1 byte the contains the number of bytes to transmit. Then read those
+// bytes from the program and transmit them one by one.
+tx:
+		lbu     t1, 0(a1)       // read number of bytes to transmit
+		addi    a1, a1, 1
+1:      lw      t0, FESPI_REG_TXFIFO(a0)        // wait for FIFO clear
+		bltz    t0, 1b
+		lbu     t0, 0(a1)       // Load byte to write
+		sw      t0, FESPI_REG_TXFIFO(a0)
+		addi    a1, a1, 1
+		addi    t1, t1, -1
+		bgtz    t1, 1b
+		j       main
+
+// Wait until TXWM is set.
+txwm_wait:
+1:      lw      t0, FESPI_REG_IP(a0)
+		andi    t0, t0, FESPI_IP_TXWM
+		beqz    t0, 1b
+		j       main
+
+// Read 1 byte that contains the offset of the register to write, and 1 byte
+// that contains the data to write.
+write_reg:
+		lbu     t0, 0(a1)       // read register to write
+		add     t0, t0, a0
+		lbu     t1, 1(a1)       // read value to write
+		addi    a1, a1, 2
+		sw      t1, 0(t0)
+		j       main
+
+wip_wait:
+		li		a2, SPIFLASH_READ_STATUS
+		jal		txrx_byte
+		// discard first result
+1:		li		a2, 0
+		jal		txrx_byte
+		andi	t0, a2, SPIFLASH_BSY_BIT
+		bnez	t0, 1b
+		j		main
+
+txrx_byte:	// transmit the byte in a2, receive a bit into a2
+		lw      t0, FESPI_REG_TXFIFO(a0)        // wait for FIFO clear
+		bltz    t0, txrx_byte
+		sw      a2, FESPI_REG_TXFIFO(a0)
+1:		lw		a2, FESPI_REG_RXFIFO(a0)
+		bltz	a2, 1b
+		ret
+
+set_dir:
+		lw		t0, FESPI_REG_FMT(a0)
+		li		t1, ~(FESPI_FMT_DIR(0xFFFFFFFF))
+		and		t0, t0, t1
+		lbu     t1, 0(a1)       // read value to OR in
+		addi    a1, a1, 1
+		or		t0, t0, t1
+		sw		t0, FESPI_REG_FMT(a0)
+		j		main
diff --git a/contrib/loaders/flash/fespi/fespi.inc b/contrib/loaders/flash/fespi/fespi.inc
new file mode 100644
index 0000000000000000000000000000000000000000..768bdc5981e78ca2831098a7c211b746722605bc
--- /dev/null
+++ b/contrib/loaders/flash/fespi/fespi.inc
@@ -0,0 +1,15 @@
+/* Autogenerated with ../../../../src/helper/bin2char.sh */
+0x6f,0x00,0xc0,0x01,0x73,0x00,0x10,0x00,0x6f,0x00,0xc0,0x02,0x6f,0x00,0x00,0x05,
+0x6f,0x00,0xc0,0x05,0x6f,0x00,0x00,0x07,0x6f,0x00,0x00,0x0a,0x83,0xc2,0x05,0x00,
+0x93,0x85,0x15,0x00,0x17,0x03,0x00,0x00,0x13,0x03,0xc3,0xfd,0xb3,0x82,0x62,0x00,
+0x67,0x80,0x02,0x00,0x03,0xc3,0x05,0x00,0x93,0x85,0x15,0x00,0x83,0x22,0x85,0x04,
+0xe3,0xce,0x02,0xfe,0x83,0xc2,0x05,0x00,0x23,0x24,0x55,0x04,0x93,0x85,0x15,0x00,
+0x13,0x03,0xf3,0xff,0xe3,0x44,0x60,0xfe,0x6f,0xf0,0x5f,0xfc,0x83,0x22,0x45,0x07,
+0x93,0xf2,0x12,0x00,0xe3,0x8c,0x02,0xfe,0x6f,0xf0,0x5f,0xfb,0x83,0xc2,0x05,0x00,
+0xb3,0x82,0xa2,0x00,0x03,0xc3,0x15,0x00,0x93,0x85,0x25,0x00,0x23,0xa0,0x62,0x00,
+0x6f,0xf0,0xdf,0xf9,0x13,0x06,0x50,0x00,0xef,0x00,0x80,0x01,0x13,0x06,0x00,0x00,
+0xef,0x00,0x00,0x01,0x93,0x72,0x16,0x00,0xe3,0x9a,0x02,0xfe,0x6f,0xf0,0x1f,0xf8,
+0x83,0x22,0x85,0x04,0xe3,0xce,0x02,0xfe,0x23,0x24,0xc5,0x04,0x03,0x26,0xc5,0x04,
+0xe3,0x4e,0x06,0xfe,0x67,0x80,0x00,0x00,0x83,0x22,0x05,0x04,0x13,0x03,0x70,0xff,
+0xb3,0xf2,0x62,0x00,0x03,0xc3,0x05,0x00,0x93,0x85,0x15,0x00,0xb3,0xe2,0x62,0x00,
+0x23,0x20,0x55,0x04,0x6f,0xf0,0x9f,0xf4,
diff --git a/doc/openocd.texi b/doc/openocd.texi
index 8cc3f4ad53f777b0c2073394bb0b1121dbfd7cd1..bede1c8c9ec3fac63c3aa5f7b648dcf4aa215423 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -5365,6 +5365,17 @@ flash bank flash2 ath79 0x20000000 0 0 0 $_TARGETNAME cs2
 
 @end deffn
 
+@deffn {Flash Driver} fespi
+@cindex Freedom E SPI
+@cindex fespi
+
+SiFive's Freedom E SPI controller, used in HiFive and other boards.
+
+@example
+flash bank $_FLASHNAME fespi 0x20000000 0 0 0 $_TARGETNAME
+@end example
+@end deffn
+
 @subsection Internal Flash (Microcontrollers)
 
 @deffn {Flash Driver} aduc702x
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index 7c353c4adb4d0f2c0f7622cb9d9523634844e052..bbdc312b5e5a6b265361e87b756d700e0fb7fbe9 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 1c456ad59c846109dd29d41ab2581204ab3605d8..d1bbb2771e302397e576054ee3779e49929871a9 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 0000000000000000000000000000000000000000..a07972edd525265ab98ac3fab864eeec9611aaa6
--- /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
+};