[prev in list] [next in list] [prev in thread] [next in thread] 

List:       openocd-development
Subject:    [OpenOCD-devel] [PATCH]: 47bb85c flash/nor/sh_qspi: Add SH QSPI driver
From:       gerrit () openocd ! org (gerrit)
Date:       2019-04-29 20:45:54
Message-ID: 20190429204554.78D582522ADD () mail ! openocd ! org
[Download RAW message or body]

This is an automated email from Gerrit.

Marek Vasut (marek.vasut@gmail.com) just uploaded a new patch set to Gerrit, which \
you can find at http://openocd.zylin.com/5143

-- gerrit

commit 47bb85cedb112c568d5c47bdb6d0c147cf256d71
Author: Marek Vasut <marek.vasut@gmail.com>
Date:   Tue Apr 2 16:44:18 2019 +0200

    flash/nor/sh_qspi: Add SH QSPI driver
    
    Add driver for the SH QSPI controller. This SPI controller is often
    connected to the boot SPI NOR flash on R-Car Gen2 platforms.
    
    Add the following two lines to board TCL file to bind the driver on
    R-Car Gen2 SoC and make SRAM work area available:
    
      flash bank flash0 sh_qspi 0xe6b10000 0 0 0 ${_TARGETNAME}0 cs0
      ${_TARGETNAME}0 configure -work-area-phys 0xe6300000 -work-area-virt 0xe6300000 \
-work-area-size 0x10000 -work-area-backup 0  
    To install mainline U-Boot on the board, use the following procedure:
    
      proc update_uboot {} {
        # SPL
        flash erase_sector 0 0x0 0x0
        flash write_bank 0 /u-boot/spl/u-boot-spl.bin 0x0
        # U-Boot
        flash erase_sector 0 0x5 0x6
        flash write_bank 0 /u-boot/u-boot.img 0x140000
      }
    
    Change-Id: Ief22f61e93bcabae37f6e371156dece6c4be3459
    Signed-off-by: Marek Vasut <marek.vasut@gmail.com>

diff --git a/contrib/loaders/flash/sh_qspi.s b/contrib/loaders/flash/sh_qspi.s
new file mode 100644
index 0000000..f83664c
--- /dev/null
+++ b/contrib/loaders/flash/sh_qspi.s
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SH QSPI (Quad SPI) driver
+ * Copyright (C) 2019 Marek Vasut <marek.vasut@gmail.com>
+ */
+
+#define BIT(n)		(1UL << (n))
+/* SH QSPI register bit masks <REG>_<BIT> */
+#define SPCR_MSTR	0x08
+#define SPCR_SPE	0x40
+#define SPSR_SPRFF	0x80
+#define SPSR_SPTEF	0x20
+#define SPPCR_IO3FV	0x04
+#define SPPCR_IO2FV	0x02
+#define SPPCR_IO1FV	0x01
+#define SPBDCR_RXBC0	BIT(0)
+#define SPCMD_SCKDEN	BIT(15)
+#define SPCMD_SLNDEN	BIT(14)
+#define SPCMD_SPNDEN	BIT(13)
+#define SPCMD_SSLKP	BIT(7)
+#define SPCMD_BRDV0	BIT(2)
+#define SPCMD_INIT1	SPCMD_SCKDEN | SPCMD_SLNDEN | \
+			SPCMD_SPNDEN | SPCMD_SSLKP | \
+			SPCMD_BRDV0
+#define SPCMD_INIT2	SPCMD_SPNDEN | SPCMD_SSLKP | \
+			SPCMD_BRDV0
+#define SPBFCR_TXRST	BIT(7)
+#define SPBFCR_RXRST	BIT(6)
+#define SPBFCR_TXTRG	0x30
+#define SPBFCR_RXTRG	0x07
+
+/* SH QSPI register set */
+#define SH_QSPI_SPCR		0x00
+#define SH_QSPI_SSLP		0x01
+#define SH_QSPI_SPPCR		0x02
+#define SH_QSPI_SPSR		0x03
+#define SH_QSPI_SPDR		0x04
+#define SH_QSPI_SPSCR		0x08
+#define SH_QSPI_SPSSR		0x09
+#define SH_QSPI_SPBR		0x0a
+#define SH_QSPI_SPDCR		0x0b
+#define SH_QSPI_SPCKD		0x0c
+#define SH_QSPI_SSLND		0x0d
+#define SH_QSPI_SPND		0x0e
+#define SH_QSPI_DUMMY0		0x0f
+#define SH_QSPI_SPCMD0		0x10
+#define SH_QSPI_SPCMD1		0x12
+#define SH_QSPI_SPCMD2		0x14
+#define SH_QSPI_SPCMD3		0x16
+#define SH_QSPI_SPBFCR		0x18
+#define SH_QSPI_DUMMY1		0x19
+#define SH_QSPI_SPBDCR		0x1a
+#define SH_QSPI_SPBMUL0		0x1c
+#define SH_QSPI_SPBMUL1		0x20
+#define SH_QSPI_SPBMUL2		0x24
+#define SH_QSPI_SPBMUL3		0x28
+
+.syntax unified
+.arm
+.text
+
+.macro wait_for_spsr, spsrbit
+	1:	ldrb	r12, [r0, #SH_QSPI_SPSR]
+		tst	r12, \spsrbit
+		beq	1b
+.endm
+
+.macro sh_qspi_xfer
+	bl	sh_qspi_cs_activate
+	str	r6, [r0, SH_QSPI_SPBMUL0]
+	bl	sh_qspi_xfer_common
+	bl	sh_qspi_cs_deactivate
+.endm
+
+.macro sh_qspi_write_enable
+	ldr	r4,	=SPIFLASH_WRITE_ENABLE
+	adr	r5,	_start
+	add	r4,	r5
+	mov	r5,	#0x0
+	mov	r6,	#0x1
+	sh_qspi_xfer
+.endm
+
+.macro sh_qspi_wait_till_ready
+	1:	ldr	r4,	=SPIFLASH_READ_STATUS
+		adr	r5,	_start
+		add	r4,	r5
+		mov	r5,	#0x0
+		mov	r6,	#0x2
+		sh_qspi_xfer
+		and	r13,	#0x1
+		cmp	r13,	#0x1
+		beq	1b
+.endm
+
+/*
+ * r0: controller base address
+ * r1: data buffer base address
+ * r2: BIT(31) -- page program (not read)
+ *     BIT(30) -- 4-byte address (not 3-byte)
+ *     BIT(29) -- 512-byte page (not 256-byte)
+ *     BIT(27:20) -- SF command
+ *     BIT(19:0)  -- amount of data to read/write
+ * r3: SF target address
+ *
+ * r7: data size
+ * r8: page size
+ *
+ * r14: lr, link register
+ * r15: pc, program counter
+ *
+ * Clobber: r4, r5, r6, r7, r8
+ */
+
+.global _start
+_start:
+	bic	r7,	r2, #0xff000000
+	bic	r7,	r7, #0x00f00000
+
+	and	r8,	r2, #(1 << 31)
+	cmp	r8,	#(1 << 31)
+	beq	do_page_program
+
+/* fast read */
+
+	bl	sh_qspi_cs_activate
+
+	bl	sh_qspi_setup_command
+	add	r8, r6, r7
+	str	r8, [r0, SH_QSPI_SPBMUL0]
+	bl	sh_qspi_xfer_common
+
+	mov	r4,	#0x0
+	mov	r5,	r1
+	mov	r6,	r7
+	bl	sh_qspi_xfer_common
+
+	bl	sh_qspi_cs_deactivate
+
+	b end
+
+do_page_program:
+
+	mov	r8,	#0x100
+	tst	r2,	(1 << 29)
+	movne	r8,	#0x200
+
+do_pp_next_page:
+	/* Check if less then page bytes left. */
+	cmp	r7,	r8
+	movlt	r8,	r7
+
+	sh_qspi_write_enable
+
+	bl	sh_qspi_cs_activate
+
+	bl	sh_qspi_setup_command
+	str	r6, [r0, SH_QSPI_SPBMUL0]
+	bl	sh_qspi_xfer_common
+
+	mov	r4,	r1
+	mov	r5,	#0x0
+	mov	r6,	r8
+
+	bl	sh_qspi_xfer_common
+
+	bl	sh_qspi_cs_deactivate
+
+	sh_qspi_wait_till_ready
+
+	add	r1,	r8
+	add	r3,	r8
+	sub	r7,	r8
+	cmp	r7,	#0
+
+	bne	do_pp_next_page
+
+end:
+	bkpt	#0
+
+sh_qspi_cs_activate:
+	/* Set master mode only */
+	mov	r12,	#SPCR_MSTR
+	strb	r12,	[r0, SH_QSPI_SPCR]
+
+	/* Set command */
+	mov	r12,	#SPCMD_INIT1
+	strh	r12,	[r0, SH_QSPI_SPCMD0]
+
+	/* Reset transfer and receive Buffer */
+	ldrb	r12,	[r0, SH_QSPI_SPSCR]
+	orr	r12,	#(SPBFCR_TXRST | SPBFCR_RXRST)
+	strb	r12,	[r0, SH_QSPI_SPBFCR]
+
+	/* Clear transfer and receive Buffer control bit */
+	ldrb	r12,	[r0, SH_QSPI_SPBFCR]
+	bic	r12,	#(SPBFCR_TXRST | SPBFCR_RXRST)
+	strb	r12,	[r0, SH_QSPI_SPBFCR]
+
+	/* Set sequence control method. Use sequence0 only */
+	mov	r12,	#0x00
+	strb	r12,	[r0, SH_QSPI_SPSCR]
+
+	/* Enable SPI function */
+	ldrb	r12,	[r0, SH_QSPI_SPCR]
+	orr	r12,	#SPCR_SPE
+	strb	r12,	[r0, SH_QSPI_SPCR]
+
+	mov	pc,	lr
+
+sh_qspi_cs_deactivate:
+	/* Disable SPI function */
+	ldrb	r12,	[r0, SH_QSPI_SPCR]
+	bic	r12,	#SPCR_SPE
+	strb	r12,	[r0, SH_QSPI_SPCR]
+
+	mov	pc,	lr
+
+/*
+ * r0, controller base address
+ * r4, tx buffer
+ * r5, rx buffer
+ * r6, xfer len, non-zero
+ *
+ * Upon exit, r13 contains the last byte in SPDR
+ *
+ * Clobber: r11, r12, r13
+ */
+sh_qspi_xfer_common:
+prepcopy:
+	ldr	r13, [r0, #SH_QSPI_SPBFCR]
+	orr	r13, #(SPBFCR_TXTRG | SPBFCR_RXTRG)
+	mov	r11, #32
+	cmp	r6, #32
+
+	biclt	r13, #(SPBFCR_TXTRG | SPBFCR_RXTRG)
+	movlt	r11, #1
+
+copy:
+	str	r13, [r0, #SH_QSPI_SPBFCR]
+
+	wait_for_spsr SPSR_SPTEF
+
+	mov	r12, r11
+	mov	r13, #0
+	cmp	r4, #0
+	beq	3f
+
+2:	ldrb	r13, [r4], #1
+	strb	r13, [r0, #SH_QSPI_SPDR]
+	subs	r12, #1
+	bne	2b
+	b	4f
+
+3:	strb	r13, [r0, #SH_QSPI_SPDR]
+	subs	r12, #1
+	bne	3b
+
+4:	wait_for_spsr SPSR_SPRFF
+
+	mov	r12, r11
+	cmp	r5, #0
+	beq	6f
+
+5:	ldrb	r13, [r0, #SH_QSPI_SPDR]
+	strb	r13, [r5], #1
+	subs	r12, #1
+	bne	5b
+	b	7f
+
+6:	ldrb	r13, [r0, #SH_QSPI_SPDR]
+	subs	r12, #1
+	bne	6b
+
+7:	subs	r6, r11
+	bne	prepcopy
+
+	mov	pc,	lr
+
+sh_qspi_setup_command:
+	ldr	r4,	=SPIFLASH_SCRATCH_DATA
+	adr	r5,	_start
+	add	r4,	r5
+	and	r12,	r2, #0x0ff00000
+	lsr	r12,	#20
+	strb	r12,	[r4]
+	mov	r12,	r3
+	strb	r12,	[r4, #4]
+	lsr	r12,	#8
+	strb	r12,	[r4, #3]
+	lsr	r12,	#8
+	strb	r12,	[r4, #2]
+	lsr	r12,	#8
+	strb	r12,	[r4, #1]
+	lsr	r12,	#8
+	mov	r5,	#0x0
+	mov	r6,	#0x4
+	tst	r2,	(1 << 30)
+	movne	r6,	#0x5
+
+	mov	pc,	lr
+
+SPIFLASH_READ_STATUS:	.byte	0x05 /* Read Status Register */
+SPIFLASH_WRITE_ENABLE:	.byte	0x06 /* Write Enable */
+SPIFLASH_NOOP:		.byte	0x00
+SPIFLASH_SCRATCH_DATA:	.byte	0x00, 0x0, 0x0, 0x0, 0x0
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index 135128e..7818414 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -51,6 +51,7 @@ NOR_DRIVERS = \
 	%D%/psoc4.c \
 	%D%/psoc5lp.c \
 	%D%/psoc6.c \
+	%D%/sh_qspi.c \
 	%D%/sim3x.c \
 	%D%/spi.c \
 	%D%/stmsmi.c \
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index 955d149..1c61587 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -66,6 +66,7 @@ extern const struct flash_driver psoc5lp_flash;
 extern const struct flash_driver psoc5lp_eeprom_flash;
 extern const struct flash_driver psoc5lp_nvl_flash;
 extern const struct flash_driver psoc6_flash;
+extern const struct flash_driver sh_qspi_flash;
 extern const struct flash_driver sim3x_flash;
 extern const struct flash_driver stellaris_flash;
 extern const struct flash_driver stm32f1x_flash;
@@ -135,6 +136,7 @@ static const struct flash_driver * const flash_drivers[] = {
 	&psoc5lp_eeprom_flash,
 	&psoc5lp_nvl_flash,
 	&psoc6_flash,
+	&sh_qspi_flash,
 	&sim3x_flash,
 	&stellaris_flash,
 	&stm32f1x_flash,
diff --git a/src/flash/nor/sh_qspi.c b/src/flash/nor/sh_qspi.c
new file mode 100644
index 0000000..7e566d9
--- /dev/null
+++ b/src/flash/nor/sh_qspi.c
@@ -0,0 +1,1072 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SH QSPI (Quad SPI) driver
+ * Copyright (C) 2019 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * Based on U-Boot SH QSPI driver
+ * Copyright (C) 2013 Renesas Electronics Corporation
+ * Copyright (C) 2013 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include "spi.h"
+#include <helper/binarybuffer.h>
+#include <helper/time_support.h>
+#include <helper/types.h>
+#include <jtag/jtag.h>
+#include <target/algorithm.h>
+#include <target/arm.h>
+#include <target/arm_opcodes.h>
+#include <target/target.h>
+
+#define BIT(n)		(1UL << (n))
+/* SH QSPI register bit masks <REG>_<BIT> */
+#define SPCR_MSTR	0x08
+#define SPCR_SPE	0x40
+#define SPSR_SPRFF	0x80
+#define SPSR_SPTEF	0x20
+#define SPPCR_IO3FV	0x04
+#define SPPCR_IO2FV	0x02
+#define SPPCR_IO1FV	0x01
+#define SPBDCR_RXBC0	BIT(0)
+#define SPCMD_SCKDEN	BIT(15)
+#define SPCMD_SLNDEN	BIT(14)
+#define SPCMD_SPNDEN	BIT(13)
+#define SPCMD_SSLKP	BIT(7)
+#define SPCMD_BRDV0	BIT(2)
+#define SPCMD_INIT1	SPCMD_SCKDEN | SPCMD_SLNDEN | \
+			SPCMD_SPNDEN | SPCMD_SSLKP | \
+			SPCMD_BRDV0
+#define SPCMD_INIT2	SPCMD_SPNDEN | SPCMD_SSLKP | \
+			SPCMD_BRDV0
+#define SPBFCR_TXRST	BIT(7)
+#define SPBFCR_RXRST	BIT(6)
+#define SPBFCR_TXTRG	0x30
+#define SPBFCR_RXTRG	0x07
+
+/* SH QSPI register set */
+#define SH_QSPI_SPCR		0x00
+#define SH_QSPI_SSLP		0x01
+#define SH_QSPI_SPPCR		0x02
+#define SH_QSPI_SPSR		0x03
+#define SH_QSPI_SPDR		0x04
+#define SH_QSPI_SPSCR		0x08
+#define SH_QSPI_SPSSR		0x09
+#define SH_QSPI_SPBR		0x0a
+#define SH_QSPI_SPDCR		0x0b
+#define SH_QSPI_SPCKD		0x0c
+#define SH_QSPI_SSLND		0x0d
+#define SH_QSPI_SPND		0x0e
+#define SH_QSPI_DUMMY0		0x0f
+#define SH_QSPI_SPCMD0		0x10
+#define SH_QSPI_SPCMD1		0x12
+#define SH_QSPI_SPCMD2		0x14
+#define SH_QSPI_SPCMD3		0x16
+#define SH_QSPI_SPBFCR		0x18
+#define SH_QSPI_DUMMY1		0x19
+#define SH_QSPI_SPBDCR		0x1a
+#define SH_QSPI_SPBMUL0		0x1c
+#define SH_QSPI_SPBMUL1		0x20
+#define SH_QSPI_SPBMUL2		0x24
+#define SH_QSPI_SPBMUL3		0x28
+
+struct sh_qspi_flash_bank {
+	const struct flash_device *dev;
+	uint32_t		io_base;
+	int			probed;
+	struct working_area	*io_algorithm;
+	struct working_area	*source;
+	unsigned int		buffer_size;
+};
+
+struct sh_qspi_target {
+	char		*name;
+	uint32_t	tap_idcode;
+	uint32_t	io_base;
+};
+
+static const struct sh_qspi_target target_devices[] = {
+	/* name,	tap_idcode,	io_base */
+	{ "SH QSPI",	0x4ba00477,	0xe6b10000 },
+	{ NULL,		0,		0 }
+};
+
+static int sh_qspi_init(struct flash_bank *bank)
+{
+	struct target *target = bank->target;
+	struct sh_qspi_flash_bank *info = bank->driver_priv;
+	uint8_t val;
+	int ret;
+
+	/* QSPI initialize */
+	/* Set master mode only */
+	ret = target_write_u8(target, info->io_base + SH_QSPI_SPCR, SPCR_MSTR);
+	if (ret)
+		return ret;
+
+	/* Set SSL signal level */
+	ret = target_write_u8(target, info->io_base + SH_QSPI_SSLP, 0x00);
+	if (ret)
+		return ret;
+
+	/* Set MOSI signal value when transfer is in idle state */
+	ret = target_write_u8(target, info->io_base + SH_QSPI_SPPCR,
+			      SPPCR_IO3FV | SPPCR_IO2FV);
+	if (ret)
+		return ret;
+
+	/* Set bit rate. See 58.3.8 Quad Serial Peripheral Interface */
+	ret = target_write_u8(target, info->io_base + SH_QSPI_SPBR, 0x01);
+	if (ret)
+		return ret;
+
+	/* Disable Dummy Data Transmission */
+	ret = target_write_u8(target, info->io_base + SH_QSPI_SPDCR, 0x00);
+	if (ret)
+		return ret;
+
+	/* Set clock delay value */
+	ret = target_write_u8(target, info->io_base + SH_QSPI_SPCKD, 0x00);
+	if (ret)
+		return ret;
+
+	/* Set SSL negation delay value */
+	ret = target_write_u8(target, info->io_base + SH_QSPI_SSLND, 0x00);
+	if (ret)
+		return ret;
+
+	/* Set next-access delay value */
+	ret = target_write_u8(target, info->io_base + SH_QSPI_SPND, 0x00);
+	if (ret)
+		return ret;
+
+	/* Set equence command */
+	ret = target_write_u16(target, info->io_base + SH_QSPI_SPCMD0,
+			       SPCMD_INIT2);
+	if (ret)
+		return ret;
+
+	/* Reset transfer and receive Buffer */
+	ret = target_read_u8(target, info->io_base + SH_QSPI_SPBFCR, &val);
+	if (ret)
+		return ret;
+
+	val |= SPBFCR_TXRST | SPBFCR_RXRST;
+
+	ret = target_write_u8(target, info->io_base + SH_QSPI_SPBFCR, val);
+	if (ret)
+		return ret;
+
+	/* Clear transfer and receive Buffer control bit */
+	ret = target_read_u8(target, info->io_base + SH_QSPI_SPBFCR, &val);
+	if (ret)
+		return ret;
+
+	val &= ~(SPBFCR_TXRST|SPBFCR_RXRST);
+
+	ret = target_write_u8(target, info->io_base + SH_QSPI_SPBFCR, val);
+	if (ret)
+		return ret;
+
+	/* Set equence control method. Use equence0 only */
+	ret = target_write_u8(target, info->io_base + SH_QSPI_SPSCR, 0x00);
+	if (ret)
+		return ret;
+
+	/* Enable SPI function */
+	ret = target_read_u8(target, info->io_base + SH_QSPI_SPCR, &val);
+	if (ret)
+		return ret;
+
+	val |= SPCR_SPE;
+
+	ret = target_write_u8(target, info->io_base + SH_QSPI_SPCR, val);
+	if (ret)
+		return ret;
+
+	return ERROR_OK;
+}
+
+static int sh_qspi_cs_activate(struct flash_bank *bank)
+{
+	struct target *target = bank->target;
+	struct sh_qspi_flash_bank *info = bank->driver_priv;
+	uint8_t val;
+	int ret;
+
+	/* Set master mode only */
+	ret = target_write_u8(target, info->io_base + SH_QSPI_SPCR, SPCR_MSTR);
+	if (ret)
+		return ret;
+
+	/* Set command */
+	ret = target_write_u16(target, info->io_base + SH_QSPI_SPCMD0,
+			       SPCMD_INIT1);
+	if (ret)
+		return ret;
+
+	/* Reset transfer and receive Buffer */
+	ret = target_read_u8(target, info->io_base + SH_QSPI_SPBFCR, &val);
+	if (ret)
+		return ret;
+
+	val |= SPBFCR_TXRST|SPBFCR_RXRST;
+
+	ret = target_write_u8(target, info->io_base + SH_QSPI_SPBFCR, val);
+	if (ret)
+		return ret;
+
+	/* Clear transfer and receive Buffer control bit */
+	ret = target_read_u8(target, info->io_base + SH_QSPI_SPBFCR, &val);
+	if (ret)
+		return ret;
+
+	val &= ~(SPBFCR_TXRST|SPBFCR_RXRST);
+
+	ret = target_write_u8(target, info->io_base + SH_QSPI_SPBFCR, val);
+	if (ret)
+		return ret;
+
+	/* Set equence control method. Use equence0 only */
+	ret = target_write_u8(target, info->io_base + SH_QSPI_SPSCR, 0x00);
+	if (ret)
+		return ret;
+
+	/* Enable SPI function */
+	ret = target_read_u8(target, info->io_base + SH_QSPI_SPCR, &val);
+	if (ret)
+		return ret;
+
+	val |= SPCR_SPE;
+
+	ret = target_write_u8(target, info->io_base + SH_QSPI_SPCR, val);
+	if (ret)
+		return ret;
+
+	return ERROR_OK;
+}
+
+static int sh_qspi_cs_deactivate(struct flash_bank *bank)
+{
+	struct target *target = bank->target;
+	struct sh_qspi_flash_bank *info = bank->driver_priv;
+	uint8_t val;
+	int ret;
+
+	/* Disable SPI Function */
+	ret = target_read_u8(target, info->io_base + SH_QSPI_SPCR, &val);
+	if (ret)
+		return ret;
+
+	val &= ~SPCR_SPE;
+
+	ret = target_write_u8(target, info->io_base + SH_QSPI_SPCR, val);
+	if (ret)
+		return ret;
+
+	return ERROR_OK;
+}
+
+static int sh_qspi_wait_for_bit(struct flash_bank *bank, uint8_t reg,
+				uint32_t mask, bool set,
+				unsigned long timeout)
+{
+	struct target *target = bank->target;
+	struct sh_qspi_flash_bank *info = bank->driver_priv;
+	long long endtime;
+	uint8_t val;
+	int ret;
+
+	endtime = timeval_ms() + timeout;
+	do {
+		ret = target_read_u8(target, info->io_base + reg, &val);
+		if (ret)
+			return ERROR_FAIL;
+
+		if (!set)
+			val = ~val;
+
+		if ((val & mask) == mask)
+			return ERROR_OK;
+
+		alive_sleep(1);
+	} while (timeval_ms() < endtime);
+
+	LOG_ERROR("timeout");
+	return ERROR_FAIL;
+}
+
+static int sh_qspi_xfer_common(struct flash_bank *bank,
+			       const uint8_t *dout, unsigned int outlen,
+			       uint8_t *din, unsigned int inlen,
+			       bool xfer_start, bool xfer_end)
+{
+	struct target *target = bank->target;
+	struct sh_qspi_flash_bank *info = bank->driver_priv;
+	uint8_t tdata, rdata;
+	uint8_t val;
+	unsigned int nbyte = outlen + inlen;
+	int ret = 0;
+
+	if (xfer_start) {
+		ret = sh_qspi_cs_activate(bank);
+		if (ret)
+			return ret;
+
+		ret = target_write_u32(target, info->io_base + SH_QSPI_SPBMUL0,
+				       nbyte);
+		if (ret)
+			return ret;
+
+		ret = target_read_u8(target, info->io_base + SH_QSPI_SPBFCR,
+				     &val);
+		if (ret)
+			return ret;
+
+		val &= ~(SPBFCR_TXTRG | SPBFCR_RXTRG);
+
+		ret = target_write_u8(target, info->io_base + SH_QSPI_SPBFCR,
+				      val);
+		if (ret)
+			return ret;
+	}
+
+	while (nbyte > 0) {
+		ret = sh_qspi_wait_for_bit(bank, SH_QSPI_SPSR, SPSR_SPTEF,
+						true, 1000);
+		if (ret)
+			return ret;
+
+		tdata = outlen ? *dout++ : 0;
+		ret = target_write_u8(target, info->io_base + SH_QSPI_SPDR,
+				      tdata);
+		if (ret)
+			return ret;
+
+		ret = sh_qspi_wait_for_bit(bank, SH_QSPI_SPSR, SPSR_SPRFF,
+						true, 1000);
+		if (ret)
+			return ret;
+
+		ret = target_read_u8(target, info->io_base + SH_QSPI_SPDR,
+				     &rdata);
+		if (ret)
+			return ret;
+		if (!outlen && inlen) {
+			*din++ = rdata;
+			inlen--;
+		}
+
+		if (outlen)
+			outlen--;
+
+		nbyte--;
+	}
+
+	if (xfer_end)
+		return sh_qspi_cs_deactivate(bank);
+	else
+		return ERROR_OK;
+}
+
+/* Send "write enable" command to SPI flash chip. */
+static int sh_qspi_write_enable(struct flash_bank *bank)
+{
+	uint8_t dout = SPIFLASH_WRITE_ENABLE;
+	int ret;
+
+	ret = sh_qspi_xfer_common(bank, &dout, 1, NULL, 0, 1, 1);
+	if (ret != ERROR_OK)
+		return ret;
+
+	return ERROR_OK;
+}
+
+/* Read the status register of the external SPI flash chip. */
+static int read_status_reg(struct flash_bank *bank, uint32_t *status)
+{
+	uint8_t dout = SPIFLASH_READ_STATUS;
+	uint8_t din;
+	int ret;
+
+	ret = sh_qspi_xfer_common(bank, &dout, 1, &din, 1, 1, 1);
+	if (ret != ERROR_OK)
+		return ret;
+
+	*status = din & 0xff;
+
+	return ERROR_OK;
+}
+
+/* check for WIP (write in progress) bit in status register */
+/* timeout in ms */
+static int wait_till_ready(struct flash_bank *bank, int timeout)
+{
+	long long endtime;
+	uint32_t status;
+	int ret;
+
+	endtime = timeval_ms() + timeout;
+	do {
+		/* read flash status register */
+		ret = read_status_reg(bank, &status);
+		if (ret != ERROR_OK)
+			return ret;
+
+		if ((status & SPIFLASH_BSY_BIT) == 0)
+			return ERROR_OK;
+		alive_sleep(1);
+	} while (timeval_ms() < endtime);
+
+	LOG_ERROR("timeout");
+	return ERROR_FAIL;
+}
+
+static int sh_qspi_erase_sector(struct flash_bank *bank, int sector)
+{
+	struct sh_qspi_flash_bank *info = bank->driver_priv;
+	bool addr4b = info->dev->size_in_bytes > (1UL << 24);
+	uint32_t address = (sector * info->dev->sectorsize) <<
+			   (addr4b ? 0 : 8);
+	uint8_t dout[5] = {
+		info->dev->erase_cmd,
+		(address >> 24) & 0xff, (address >> 16) & 0xff,
+		(address >> 8) & 0xff, (address >> 0) & 0xff
+	};
+	unsigned int doutlen = addr4b ? 5 : 4;
+	int ret;
+
+	/* Write Enable */
+	ret = sh_qspi_write_enable(bank);
+	if (ret != ERROR_OK)
+		return ret;
+
+	/* Erase */
+	ret = sh_qspi_xfer_common(bank, dout, doutlen, NULL, 0, 1, 1);
+	if (ret != ERROR_OK)
+		return ret;
+
+	/* Poll status register */
+	ret = wait_till_ready(bank, 3000);
+	if (ret != ERROR_OK)
+		return ret;
+
+	return ERROR_OK;
+}
+
+static int sh_qspi_erase(struct flash_bank *bank, int first, int last)
+{
+	struct target *target = bank->target;
+	struct sh_qspi_flash_bank *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 (!info->probed) {
+		LOG_ERROR("Flash bank not probed");
+		return ERROR_FLASH_BANK_NOT_PROBED;
+	}
+
+	if (info->dev->erase_cmd == 0x00)
+		return ERROR_FLASH_OPER_UNSUPPORTED;
+
+	for (sector = first; sector <= last; sector++) {
+		if (bank->sectors[sector].is_protected) {
+			LOG_ERROR("Flash sector %d protected", sector);
+			return ERROR_FAIL;
+		}
+	}
+
+	for (sector = first; sector <= last; sector++) {
+		retval = sh_qspi_erase_sector(bank, sector);
+		if (retval != ERROR_OK)
+			break;
+		keep_alive();
+	}
+
+	return retval;
+}
+
+static int sh_qspi_write(struct flash_bank *bank, const uint8_t *buffer,
+		       uint32_t offset, uint32_t count)
+{
+	struct target *target = bank->target;
+	struct sh_qspi_flash_bank *info = bank->driver_priv;
+	struct reg_param reg_params[4];
+	struct arm_algorithm arm_algo;
+	uint32_t io_base = (uint32_t)(info->io_base);
+	uint32_t src_base = (uint32_t)(info->source->address);
+	uint32_t chunk;
+	bool addr4b = !!(info->dev->size_in_bytes > (1UL << 24));
+	int ret = ERROR_OK;
+	int sector;
+
+	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 > bank->size) {
+		LOG_WARNING("Write pasts end of flash. Extra data discarded.");
+		count = bank->size - offset;
+	}
+
+	if (offset & 0xff) {
+		LOG_ERROR("sh_qspi_write_page: unaligned write address: %08x",
+			  offset);
+		return ERROR_FAIL;
+	}
+
+	/* 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? */
+		struct flash_sector *bs = &bank->sectors[sector];
+
+		if ((offset < (bs->offset + bs->size)) &&
+		    ((offset + count - 1) >= bs->offset) &&
+		    bs->is_protected) {
+			LOG_ERROR("Flash sector %d protected", sector);
+			return ERROR_FAIL;
+		}
+	}
+
+	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 > bank->size) {
+		LOG_WARNING("Reads past end of flash. Extra data discarded.");
+		count = bank->size - offset;
+	}
+
+	arm_algo.common_magic = ARM_COMMON_MAGIC;
+	arm_algo.core_mode = ARM_MODE_SVC;
+	arm_algo.core_state = ARM_STATE_ARM;
+
+	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_OUT);
+	init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);
+
+	while (count > 0) {
+		chunk = (count > info->buffer_size) ?
+			info->buffer_size : count;
+
+		target_write_buffer(target, info->source->address,
+				    chunk, buffer);
+
+		buf_set_u32(reg_params[0].value, 0, 32, io_base);
+		buf_set_u32(reg_params[1].value, 0, 32, src_base);
+		buf_set_u32(reg_params[2].value, 0, 32,
+				(1 << 31) | (addr4b << 30) |
+				(info->dev->pprog_cmd << 20) | chunk);
+		buf_set_u32(reg_params[3].value, 0, 32, offset);
+
+		ret = target_run_algorithm(target, 0, NULL, 4, reg_params,
+				info->io_algorithm->address,
+				0, 10000, &arm_algo);
+		if (ret != ERROR_OK) {
+			LOG_ERROR("error executing SH QSPI flash IO algorithm");
+			ret = ERROR_FLASH_OPERATION_FAILED;
+			break;
+		}
+
+		buffer += chunk;
+		offset += chunk;
+		count -= chunk;
+	}
+
+	destroy_reg_param(&reg_params[0]);
+	destroy_reg_param(&reg_params[1]);
+	destroy_reg_param(&reg_params[2]);
+	destroy_reg_param(&reg_params[3]);
+
+	return ret;
+}
+
+static int sh_qspi_read(struct flash_bank *bank, uint8_t *buffer,
+			uint32_t offset, uint32_t count)
+{
+	struct target *target = bank->target;
+	struct sh_qspi_flash_bank *info = bank->driver_priv;
+	struct reg_param reg_params[4];
+	struct arm_algorithm arm_algo;
+	uint32_t io_base = (uint32_t)(info->io_base);
+	uint32_t src_base = (uint32_t)(info->source->address);
+	uint32_t chunk;
+	bool addr4b = !!(info->dev->size_in_bytes > (1UL << 24));
+	int ret = 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 > bank->size) {
+		LOG_WARNING("Reads past end of flash. Extra data discarded.");
+		count = bank->size - offset;
+	}
+
+	arm_algo.common_magic = ARM_COMMON_MAGIC;
+	arm_algo.core_mode = ARM_MODE_SVC;
+	arm_algo.core_state = ARM_STATE_ARM;
+
+	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_OUT);
+	init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);
+
+	while (count > 0) {
+		chunk = (count > info->buffer_size) ?
+			info->buffer_size : count;
+
+		buf_set_u32(reg_params[0].value, 0, 32, io_base);
+		buf_set_u32(reg_params[1].value, 0, 32, src_base);
+		buf_set_u32(reg_params[2].value, 0, 32,
+				(addr4b << 30) | (info->dev->read_cmd << 20) |
+				chunk);
+		buf_set_u32(reg_params[3].value, 0, 32, offset);
+
+		ret = target_run_algorithm(target, 0, NULL, 4, reg_params,
+				info->io_algorithm->address,
+				0, 10000, &arm_algo);
+		if (ret != ERROR_OK) {
+			LOG_ERROR("error executing SH QSPI flash IO algorithm");
+			ret = ERROR_FLASH_OPERATION_FAILED;
+			break;
+		}
+
+		target_read_buffer(target, info->source->address,
+				   chunk, buffer);
+
+		buffer += chunk;
+		offset += chunk;
+		count -= chunk;
+	}
+
+	destroy_reg_param(&reg_params[0]);
+	destroy_reg_param(&reg_params[1]);
+	destroy_reg_param(&reg_params[2]);
+	destroy_reg_param(&reg_params[3]);
+
+	return ERROR_OK;
+}
+
+/* Return ID of flash device */
+static int read_flash_id(struct flash_bank *bank, uint32_t *id)
+{
+	struct target *target = bank->target;
+	uint8_t dout = SPIFLASH_READ_ID;
+	uint8_t din[3] = { 0, 0, 0 };
+	int ret;
+
+	if (target->state != TARGET_HALTED) {
+		LOG_ERROR("Target not halted");
+		return ERROR_TARGET_NOT_HALTED;
+	}
+
+	ret = sh_qspi_xfer_common(bank, &dout, 1, din, 3, 1, 1);
+	if (ret != ERROR_OK)
+		return ret;
+
+	*id = (din[0] << 0) | (din[1] << 8) | (din[2] << 16);
+
+	if (*id == 0xffffff) {
+		LOG_ERROR("No SPI flash found");
+		return ERROR_FAIL;
+	}
+
+	return ERROR_OK;
+}
+
+static int sh_qspi_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 sh_qspi_upload_helper(struct flash_bank *bank)
+{
+	struct target *target = bank->target;
+	struct sh_qspi_flash_bank *info = bank->driver_priv;
+
+	/* see contrib/loaders/flash/sh_qspi.s for src */
+	static const uint32_t sh_qspi_io_code[] = {
+		0xe3c274ff,
+		0xe3c7760f,
+		0xe2028102,
+		0xe3580102,
+		0x0a00000a,
+		0xeb000032,
+		0xeb00006c,
+		0xe0868007,
+		0xe580801c,
+		0xeb000042,
+		0xe3a04000,
+		0xe1a05001,
+		0xe1a06007,
+		0xeb00003e,
+		0xeb000039,
+		0xea000027,
+		0xe3a08c01,
+		0xe3120202,
+		0x13a08c02,
+		0xe1570008,
+		0xb1a08007,
+		0xe59f41cc,
+		0xe24f5060,
+		0xe0844005,
+		0xe3a05000,
+		0xe3a06001,
+		0xeb00001d,
+		0xe580601c,
+		0xeb00002f,
+		0xeb00002a,
+		0xeb000019,
+		0xeb000053,
+		0xe580601c,
+		0xeb00002a,
+		0xe1a04001,
+		0xe3a05000,
+		0xe1a06008,
+		0xeb000026,
+		0xeb000021,
+		0xe59f4188,
+		0xe24f50a8,
+		0xe0844005,
+		0xe3a05000,
+		0xe3a06002,
+		0xeb00000b,
+		0xe580601c,
+		0xeb00001d,
+		0xeb000018,
+		0xe20dd001,
+		0xe35d0001,
+		0x0afffff3,
+		0xe0811008,
+		0xe0833008,
+		0xe0477008,
+		0xe3570000,
+		0x1affffda,
+		0xe1200070,
+		0xe3a0c008,
+		0xe5c0c000,
+		0xe30ec084,
+		0xe1c0c1b0,
+		0xe5d0c008,
+		0xe38cc0c0,
+		0xe5c0c018,
+		0xe5d0c018,
+		0xe3ccc0c0,
+		0xe5c0c018,
+		0xe3a0c000,
+		0xe5c0c008,
+		0xe5d0c000,
+		0xe38cc040,
+		0xe5c0c000,
+		0xe1a0f00e,
+		0xe5d0c000,
+		0xe3ccc040,
+		0xe5c0c000,
+		0xe1a0f00e,
+		0xe590d018,
+		0xe38dd037,
+		0xe3a0b020,
+		0xe3560020,
+		0xb3cdd037,
+		0xb3a0b001,
+		0xe580d018,
+		0xe5d0c003,
+		0xe31c0020,
+		0x0afffffc,
+		0xe1a0c00b,
+		0xe3a0d000,
+		0xe3540000,
+		0x0a000004,
+		0xe4d4d001,
+		0xe5c0d004,
+		0xe25cc001,
+		0x1afffffb,
+		0xea000002,
+		0xe5c0d004,
+		0xe25cc001,
+		0x1afffffc,
+		0xe5d0c003,
+		0xe31c0080,
+		0x0afffffc,
+		0xe1a0c00b,
+		0xe3550000,
+		0x0a000004,
+		0xe5d0d004,
+		0xe4c5d001,
+		0xe25cc001,
+		0x1afffffb,
+		0xea000002,
+		0xe5d0d004,
+		0xe25cc001,
+		0x1afffffc,
+		0xe056600b,
+		0x1affffd9,
+		0xe1a0f00e,
+		0xe59f4058,
+		0xe24f5f77,
+		0xe0844005,
+		0xe202c6ff,
+		0xe1a0ca2c,
+		0xe5c4c000,
+		0xe1a0c003,
+		0xe5c4c004,
+		0xe1a0c42c,
+		0xe5c4c003,
+		0xe1a0c42c,
+		0xe5c4c002,
+		0xe1a0c42c,
+		0xe5c4c001,
+		0xe1a0c42c,
+		0xe3a05000,
+		0xe3a06004,
+		0xe3120101,
+		0x13a06005,
+		0xe1a0f00e,
+		0x00000605,
+		0x00000000,
+		0x00000221,
+		0x00000220,
+		0x00000223,
+	};
+	uint8_t code[sizeof(sh_qspi_io_code)];
+	int ret;
+
+	if (info->source)
+		target_free_working_area(target, info->source);
+	if (info->io_algorithm)
+		target_free_working_area(target, info->io_algorithm);
+
+	/* flash write code */
+	if (target_alloc_working_area(target, sizeof(sh_qspi_io_code),
+			&info->io_algorithm) != ERROR_OK) {
+		LOG_WARNING("no working area available, can't do block memory writes");
+		return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+	}
+
+	target_buffer_set_u32_array(target, code, ARRAY_SIZE(sh_qspi_io_code),
+				    sh_qspi_io_code);
+	target_write_buffer(target, info->io_algorithm->address,
+			    sizeof(code), code);
+
+	/* memory buffer */
+	info->buffer_size = 32768;
+	while (true) {
+		ret = target_alloc_working_area_try(target, info->buffer_size,
+						    &info->source);
+		if (ret == ERROR_OK)
+			break;
+
+		info->buffer_size /= 2;
+		if (info->buffer_size <= 256) {
+			target_free_working_area(target, info->io_algorithm);
+
+			LOG_WARNING("no large enough working area available, can't do block memory \
writes"); +			return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+		}
+	}
+
+	return ERROR_OK;
+}
+
+static int sh_qspi_probe(struct flash_bank *bank)
+{
+	struct target *target = bank->target;
+	struct sh_qspi_flash_bank *info = bank->driver_priv;
+	struct flash_sector *sectors;
+	uint32_t id = 0; /* silence uninitialized warning */
+	uint32_t sectorsize;
+	const struct sh_qspi_target *target_device;
+	int ret;
+
+	if (info->probed) {
+		free(bank->sectors);
+	}
+	info->probed = 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",
+			  target->tap->idcode);
+		return ERROR_FAIL;
+	}
+
+	info->io_base = target_device->io_base;
+
+	LOG_DEBUG("Found device %s at address " TARGET_ADDR_FMT,
+		  target_device->name, bank->base);
+
+	ret = sh_qspi_upload_helper(bank);
+	if (ret != ERROR_OK)
+		return ret;
+
+	ret = sh_qspi_init(bank);
+	if (ret != ERROR_OK)
+		return ret;
+
+	ret = read_flash_id(bank, &id);
+	if (ret != ERROR_OK)
+		return ret;
+
+	info->dev = NULL;
+	for (const struct flash_device *p = flash_devices; p->name; p++)
+		if (p->device_id == id) {
+			info->dev = p;
+			break;
+		}
+
+	if (!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 ")",
+		 info->dev->name, info->dev->device_id);
+
+	/* Set correct size value */
+	bank->size = info->dev->size_in_bytes;
+	if (bank->size <= (1UL << 16))
+		LOG_WARNING("device needs 2-byte addresses - not implemented");
+
+	/* if no sectors, treat whole bank as single sector */
+	sectorsize = info->dev->sectorsize ?
+		     info->dev->sectorsize :
+		     info->dev->size_in_bytes;
+
+	/* create and fill sectors array */
+	bank->num_sectors = info->dev->size_in_bytes / sectorsize;
+	sectors = calloc(1, sizeof(*sectors) * bank->num_sectors);
+	if (!sectors) {
+		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 = 0;
+		sectors[sector].is_protected = 0;
+	}
+
+	bank->sectors = sectors;
+	info->probed = 1;
+	return ERROR_OK;
+}
+
+static int sh_qspi_auto_probe(struct flash_bank *bank)
+{
+	struct sh_qspi_flash_bank *info = bank->driver_priv;
+
+	if (info->probed)
+		return ERROR_OK;
+
+	return sh_qspi_probe(bank);
+}
+
+static int sh_qspi_flash_blank_check(struct flash_bank *bank)
+{
+	/* Not implemented */
+	return ERROR_OK;
+}
+
+static int sh_qspi_protect_check(struct flash_bank *bank)
+{
+	/* Not implemented */
+	return ERROR_OK;
+}
+
+static int sh_qspi_get_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+	struct sh_qspi_flash_bank *info = bank->driver_priv;
+
+	if (!info->probed) {
+		snprintf(buf, buf_size,
+			 "\nSH QSPI flash bank not probed yet\n");
+		return ERROR_OK;
+	}
+
+	snprintf(buf, buf_size, "\nSH QSPI flash information:\n"
+		"  Device \'%s\' (ID 0x%08" PRIx32 ")\n",
+		info->dev->name, info->dev->device_id);
+
+	return ERROR_OK;
+}
+
+FLASH_BANK_COMMAND_HANDLER(sh_qspi_flash_bank_command)
+{
+	struct sh_qspi_flash_bank *info;
+
+	LOG_DEBUG("%s", __func__);
+
+	if (CMD_ARGC < 6 || CMD_ARGC > 7)
+		return ERROR_COMMAND_SYNTAX_ERROR;
+
+	if ((CMD_ARGC == 7) && strcmp(CMD_ARGV[6], "cs0")) {
+		LOG_ERROR("Unknown arg: %s", CMD_ARGV[6]);
+		return ERROR_COMMAND_SYNTAX_ERROR;
+	}
+
+	info = calloc(1, sizeof(struct sh_qspi_flash_bank));
+	if (!info) {
+		LOG_ERROR("not enough memory");
+		return ERROR_FAIL;
+	}
+
+	bank->driver_priv = info;
+
+	return ERROR_OK;
+}
+
+const struct flash_driver sh_qspi_flash = {
+	.name			= "sh_qspi",
+	.flash_bank_command	= sh_qspi_flash_bank_command,
+	.erase			= sh_qspi_erase,
+	.protect		= sh_qspi_protect,
+	.write			= sh_qspi_write,
+	.read			= sh_qspi_read,
+	.probe			= sh_qspi_probe,
+	.auto_probe		= sh_qspi_auto_probe,
+	.erase_check		= sh_qspi_flash_blank_check,
+	.protect_check		= sh_qspi_protect_check,
+	.info			= sh_qspi_get_info,
+	.free_driver_priv	= default_flash_free_driver_priv,
+};

-- 


_______________________________________________
OpenOCD-devel mailing list
OpenOCD-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openocd-devel


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic