flash/nor/sh_qspi: Add SH QSPI driver 43/5143/17
authorMarek Vasut <marek.vasut@gmail.com>
Tue, 2 Apr 2019 14:44:18 +0000 (16:44 +0200)
committerOleksij Rempel <linux@rempel-privat.de>
Wed, 22 Jan 2020 05:50:20 +0000 (05:50 +0000)
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>
---
V2: - Add Makefile and linker script for the SH QSPI IO algorithm
    - Include the algorithm code instead of hard-coding it
Reviewed-on: http://openocd.zylin.com/5143
Tested-by: jenkins
Reviewed-by: Oleksij Rempel <linux@rempel-privat.de>
contrib/loaders/flash/sh_qspi/Makefile [new file with mode: 0644]
contrib/loaders/flash/sh_qspi/sh_qspi.S [new file with mode: 0644]
contrib/loaders/flash/sh_qspi/sh_qspi.inc [new file with mode: 0644]
contrib/loaders/flash/sh_qspi/sh_qspi.ld [new file with mode: 0644]
src/flash/nor/Makefile.am
src/flash/nor/drivers.c
src/flash/nor/sh_qspi.c [new file with mode: 0644]

diff --git a/contrib/loaders/flash/sh_qspi/Makefile b/contrib/loaders/flash/sh_qspi/Makefile
new file mode 100644 (file)
index 0000000..2bfbad1
--- /dev/null
@@ -0,0 +1,37 @@
+CROSS_COMPILE=arm-linux-gnueabihf-
+BIN2C = ../../../../src/helper/bin2char.sh
+
+TGT = sh_qspi
+ASRC += sh_qspi.S
+LDS = sh_qspi.ld
+
+OBJS += $(ASRC:.S=.o)
+
+CC=$(CROSS_COMPILE)gcc
+OBJCOPY=$(CROSS_COMPILE)objcopy
+OBJDUMP=$(CROSS_COMPILE)objdump
+LD=$(CROSS_COMPILE)ld
+NM=$(CROSS_COMPILE)nm
+SIZE=$(CROSS_COMPILE)size
+
+CFLAGS=-Os -Wall -nostartfiles -marm -nostdinc -ffreestanding -mabi=aapcs-linux -mword-relocations -fno-pic -mno-unaligned-access -ffunction-sections -fdata-sections -fno-common -msoft-float -pipe -march=armv7-a -mtune=generic-armv7-a
+LDFLAGS=-T$(LDS) -nostdlib -Map=$(TGT).map
+
+all: $(TGT).inc
+
+%.o: %.S
+       $(CC) $(CFLAGS) -c $^ -o $@
+
+$(TGT).elf: $(OBJS)
+       $(LD) $(LDFLAGS) $^ -o $@
+
+$(TGT).bin: $(TGT).elf
+       $(OBJCOPY) $< -O binary $@
+       $(NM) -n $(TGT).elf > $(TGT).sym
+       $(SIZE) $(TGT).elf
+
+$(TGT).inc: $(TGT).bin
+       $(BIN2C) < $< > $@
+
+clean:
+       rm -rf *.elf *.hex *.map *.o *.disasm *.sym
diff --git a/contrib/loaders/flash/sh_qspi/sh_qspi.S b/contrib/loaders/flash/sh_qspi/sh_qspi.S
new file mode 100644 (file)
index 0000000..78eb1e8
--- /dev/null
@@ -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/contrib/loaders/flash/sh_qspi/sh_qspi.inc b/contrib/loaders/flash/sh_qspi/sh_qspi.inc
new file mode 100644 (file)
index 0000000..ca91392
--- /dev/null
@@ -0,0 +1,37 @@
+/* Autogenerated with ../../../../src/helper/bin2char.sh */
+0xff,0x74,0xc2,0xe3,0x0f,0x76,0xc7,0xe3,0x02,0x81,0x02,0xe2,0x02,0x01,0x58,0xe3,
+0x0a,0x00,0x00,0x0a,0x32,0x00,0x00,0xeb,0x6c,0x00,0x00,0xeb,0x07,0x80,0x86,0xe0,
+0x1c,0x80,0x80,0xe5,0x42,0x00,0x00,0xeb,0x00,0x40,0xa0,0xe3,0x01,0x50,0xa0,0xe1,
+0x07,0x60,0xa0,0xe1,0x3e,0x00,0x00,0xeb,0x39,0x00,0x00,0xeb,0x27,0x00,0x00,0xea,
+0x01,0x8c,0xa0,0xe3,0x02,0x02,0x12,0xe3,0x02,0x8c,0xa0,0x13,0x08,0x00,0x57,0xe1,
+0x07,0x80,0xa0,0xb1,0xcc,0x41,0x9f,0xe5,0x60,0x50,0x4f,0xe2,0x05,0x40,0x84,0xe0,
+0x00,0x50,0xa0,0xe3,0x01,0x60,0xa0,0xe3,0x1d,0x00,0x00,0xeb,0x1c,0x60,0x80,0xe5,
+0x2f,0x00,0x00,0xeb,0x2a,0x00,0x00,0xeb,0x19,0x00,0x00,0xeb,0x53,0x00,0x00,0xeb,
+0x1c,0x60,0x80,0xe5,0x2a,0x00,0x00,0xeb,0x01,0x40,0xa0,0xe1,0x00,0x50,0xa0,0xe3,
+0x08,0x60,0xa0,0xe1,0x26,0x00,0x00,0xeb,0x21,0x00,0x00,0xeb,0x88,0x41,0x9f,0xe5,
+0xa8,0x50,0x4f,0xe2,0x05,0x40,0x84,0xe0,0x00,0x50,0xa0,0xe3,0x02,0x60,0xa0,0xe3,
+0x0b,0x00,0x00,0xeb,0x1c,0x60,0x80,0xe5,0x1d,0x00,0x00,0xeb,0x18,0x00,0x00,0xeb,
+0x01,0xd0,0x0d,0xe2,0x01,0x00,0x5d,0xe3,0xf3,0xff,0xff,0x0a,0x08,0x10,0x81,0xe0,
+0x08,0x30,0x83,0xe0,0x08,0x70,0x47,0xe0,0x00,0x00,0x57,0xe3,0xda,0xff,0xff,0x1a,
+0x70,0x00,0x20,0xe1,0x08,0xc0,0xa0,0xe3,0x00,0xc0,0xc0,0xe5,0x84,0xc0,0x0e,0xe3,
+0xb0,0xc1,0xc0,0xe1,0x08,0xc0,0xd0,0xe5,0xc0,0xc0,0x8c,0xe3,0x18,0xc0,0xc0,0xe5,
+0x18,0xc0,0xd0,0xe5,0xc0,0xc0,0xcc,0xe3,0x18,0xc0,0xc0,0xe5,0x00,0xc0,0xa0,0xe3,
+0x08,0xc0,0xc0,0xe5,0x00,0xc0,0xd0,0xe5,0x40,0xc0,0x8c,0xe3,0x00,0xc0,0xc0,0xe5,
+0x0e,0xf0,0xa0,0xe1,0x00,0xc0,0xd0,0xe5,0x40,0xc0,0xcc,0xe3,0x00,0xc0,0xc0,0xe5,
+0x0e,0xf0,0xa0,0xe1,0x18,0xd0,0x90,0xe5,0x37,0xd0,0x8d,0xe3,0x20,0xb0,0xa0,0xe3,
+0x20,0x00,0x56,0xe3,0x37,0xd0,0xcd,0xb3,0x01,0xb0,0xa0,0xb3,0x18,0xd0,0x80,0xe5,
+0x03,0xc0,0xd0,0xe5,0x20,0x00,0x1c,0xe3,0xfc,0xff,0xff,0x0a,0x0b,0xc0,0xa0,0xe1,
+0x00,0xd0,0xa0,0xe3,0x00,0x00,0x54,0xe3,0x04,0x00,0x00,0x0a,0x01,0xd0,0xd4,0xe4,
+0x04,0xd0,0xc0,0xe5,0x01,0xc0,0x5c,0xe2,0xfb,0xff,0xff,0x1a,0x02,0x00,0x00,0xea,
+0x04,0xd0,0xc0,0xe5,0x01,0xc0,0x5c,0xe2,0xfc,0xff,0xff,0x1a,0x03,0xc0,0xd0,0xe5,
+0x80,0x00,0x1c,0xe3,0xfc,0xff,0xff,0x0a,0x0b,0xc0,0xa0,0xe1,0x00,0x00,0x55,0xe3,
+0x04,0x00,0x00,0x0a,0x04,0xd0,0xd0,0xe5,0x01,0xd0,0xc5,0xe4,0x01,0xc0,0x5c,0xe2,
+0xfb,0xff,0xff,0x1a,0x02,0x00,0x00,0xea,0x04,0xd0,0xd0,0xe5,0x01,0xc0,0x5c,0xe2,
+0xfc,0xff,0xff,0x1a,0x0b,0x60,0x56,0xe0,0xd9,0xff,0xff,0x1a,0x0e,0xf0,0xa0,0xe1,
+0x58,0x40,0x9f,0xe5,0x77,0x5f,0x4f,0xe2,0x05,0x40,0x84,0xe0,0xff,0xc6,0x02,0xe2,
+0x2c,0xca,0xa0,0xe1,0x00,0xc0,0xc4,0xe5,0x03,0xc0,0xa0,0xe1,0x04,0xc0,0xc4,0xe5,
+0x2c,0xc4,0xa0,0xe1,0x03,0xc0,0xc4,0xe5,0x2c,0xc4,0xa0,0xe1,0x02,0xc0,0xc4,0xe5,
+0x2c,0xc4,0xa0,0xe1,0x01,0xc0,0xc4,0xe5,0x2c,0xc4,0xa0,0xe1,0x00,0x50,0xa0,0xe3,
+0x04,0x60,0xa0,0xe3,0x01,0x01,0x12,0xe3,0x05,0x60,0xa0,0x13,0x0e,0xf0,0xa0,0xe1,
+0x05,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x21,0x02,0x00,0x00,0x20,0x02,0x00,0x00,
+0x23,0x02,0x00,0x00,
diff --git a/contrib/loaders/flash/sh_qspi/sh_qspi.ld b/contrib/loaders/flash/sh_qspi/sh_qspi.ld
new file mode 100644 (file)
index 0000000..2683c52
--- /dev/null
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SECTIONS
+{
+       . = 0x0;
+       . = ALIGN(4);
+       .text : {
+               sh_qspi.o (.text*)
+               *(.text*)
+       }
+}
index 34f91ce..12bafa2 100644 (file)
@@ -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 \
index 551f389..fb43a43 100644 (file)
@@ -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;
@@ -136,6 +137,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 (file)
index 0000000..931b0b1
--- /dev/null
@@ -0,0 +1,912 @@
+/* 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/bits.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>
+
+/* 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 != ERROR_OK)
+               return ret;
+
+       /* Set SSL signal level */
+       ret = target_write_u8(target, info->io_base + SH_QSPI_SSLP, 0x00);
+       if (ret != ERROR_OK)
+               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 != ERROR_OK)
+               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 != ERROR_OK)
+               return ret;
+
+       /* Disable Dummy Data Transmission */
+       ret = target_write_u8(target, info->io_base + SH_QSPI_SPDCR, 0x00);
+       if (ret != ERROR_OK)
+               return ret;
+
+       /* Set clock delay value */
+       ret = target_write_u8(target, info->io_base + SH_QSPI_SPCKD, 0x00);
+       if (ret != ERROR_OK)
+               return ret;
+
+       /* Set SSL negation delay value */
+       ret = target_write_u8(target, info->io_base + SH_QSPI_SSLND, 0x00);
+       if (ret != ERROR_OK)
+               return ret;
+
+       /* Set next-access delay value */
+       ret = target_write_u8(target, info->io_base + SH_QSPI_SPND, 0x00);
+       if (ret != ERROR_OK)
+               return ret;
+
+       /* Set equence command */
+       ret = target_write_u16(target, info->io_base + SH_QSPI_SPCMD0,
+                              SPCMD_INIT2);
+       if (ret != ERROR_OK)
+               return ret;
+
+       /* Reset transfer and receive Buffer */
+       ret = target_read_u8(target, info->io_base + SH_QSPI_SPBFCR, &val);
+       if (ret != ERROR_OK)
+               return ret;
+
+       val |= SPBFCR_TXRST | SPBFCR_RXRST;
+
+       ret = target_write_u8(target, info->io_base + SH_QSPI_SPBFCR, val);
+       if (ret != ERROR_OK)
+               return ret;
+
+       /* Clear transfer and receive Buffer control bit */
+       ret = target_read_u8(target, info->io_base + SH_QSPI_SPBFCR, &val);
+       if (ret != ERROR_OK)
+               return ret;
+
+       val &= ~(SPBFCR_TXRST | SPBFCR_RXRST);
+
+       ret = target_write_u8(target, info->io_base + SH_QSPI_SPBFCR, val);
+       if (ret != ERROR_OK)
+               return ret;
+
+       /* Set equence control method. Use equence0 only */
+       ret = target_write_u8(target, info->io_base + SH_QSPI_SPSCR, 0x00);
+       if (ret != ERROR_OK)
+               return ret;
+
+       /* Enable SPI function */
+       ret = target_read_u8(target, info->io_base + SH_QSPI_SPCR, &val);
+       if (ret != ERROR_OK)
+               return ret;
+
+       val |= SPCR_SPE;
+
+       return target_write_u8(target, info->io_base + SH_QSPI_SPCR, val);
+}
+
+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 != ERROR_OK)
+               return ret;
+
+       /* Set command */
+       ret = target_write_u16(target, info->io_base + SH_QSPI_SPCMD0,
+                              SPCMD_INIT1);
+       if (ret != ERROR_OK)
+               return ret;
+
+       /* Reset transfer and receive Buffer */
+       ret = target_read_u8(target, info->io_base + SH_QSPI_SPBFCR, &val);
+       if (ret != ERROR_OK)
+               return ret;
+
+       val |= SPBFCR_TXRST | SPBFCR_RXRST;
+
+       ret = target_write_u8(target, info->io_base + SH_QSPI_SPBFCR, val);
+       if (ret != ERROR_OK)
+               return ret;
+
+       /* Clear transfer and receive Buffer control bit */
+       ret = target_read_u8(target, info->io_base + SH_QSPI_SPBFCR, &val);
+       if (ret != ERROR_OK)
+               return ret;
+
+       val &= ~(SPBFCR_TXRST | SPBFCR_RXRST);
+
+       ret = target_write_u8(target, info->io_base + SH_QSPI_SPBFCR, val);
+       if (ret != ERROR_OK)
+               return ret;
+
+       /* Set equence control method. Use equence0 only */
+       ret = target_write_u8(target, info->io_base + SH_QSPI_SPSCR, 0x00);
+       if (ret != ERROR_OK)
+               return ret;
+
+       /* Enable SPI function */
+       ret = target_read_u8(target, info->io_base + SH_QSPI_SPCR, &val);
+       if (ret != ERROR_OK)
+               return ret;
+
+       val |= SPCR_SPE;
+
+       return target_write_u8(target, info->io_base + SH_QSPI_SPCR, val);
+}
+
+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 != ERROR_OK)
+               return ret;
+
+       val &= ~SPCR_SPE;
+
+       return target_write_u8(target, info->io_base + SH_QSPI_SPCR, val);
+}
+
+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 != ERROR_OK)
+                       return ret;
+
+               if (!set)
+                       val = ~val;
+
+               if ((val & mask) == mask)
+                       return ERROR_OK;
+
+               alive_sleep(1);
+       } while (timeval_ms() < endtime);
+
+       LOG_ERROR("timeout");
+       return ERROR_TIMEOUT_REACHED;
+}
+
+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 != ERROR_OK)
+                       return ret;
+
+               ret = target_write_u32(target, info->io_base + SH_QSPI_SPBMUL0,
+                                      nbyte);
+               if (ret != ERROR_OK)
+                       return ret;
+
+               ret = target_read_u8(target, info->io_base + SH_QSPI_SPBFCR,
+                                    &val);
+               if (ret != ERROR_OK)
+                       return ret;
+
+               val &= ~(SPBFCR_TXTRG | SPBFCR_RXTRG);
+
+               ret = target_write_u8(target, info->io_base + SH_QSPI_SPBFCR,
+                                     val);
+               if (ret != ERROR_OK)
+                       return ret;
+       }
+
+       while (nbyte > 0) {
+               ret = sh_qspi_wait_for_bit(bank, SH_QSPI_SPSR, SPSR_SPTEF,
+                                               true, 1000);
+               if (ret != ERROR_OK)
+                       return ret;
+
+               tdata = outlen ? *dout++ : 0;
+               ret = target_write_u8(target, info->io_base + SH_QSPI_SPDR,
+                                     tdata);
+               if (ret != ERROR_OK)
+                       return ret;
+
+               ret = sh_qspi_wait_for_bit(bank, SH_QSPI_SPSR, SPSR_SPRFF,
+                                               true, 1000);
+               if (ret != ERROR_OK)
+                       return ret;
+
+               ret = target_read_u8(target, info->io_base + SH_QSPI_SPDR,
+                                    &rdata);
+               if (ret != ERROR_OK)
+                       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;
+
+       return sh_qspi_xfer_common(bank, &dout, 1, NULL, 0, 1, 1);
+}
+
+/* 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_TIMEOUT_REACHED;
+}
+
+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 */
+       return wait_till_ready(bank, 3000);
+}
+
+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 uint8_t sh_qspi_io_code[] = {
+#include "../../../contrib/loaders/flash/sh_qspi/sh_qspi.inc"
+       };
+       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_write_buffer(target, info->io_algorithm->address,
+                           sizeof(sh_qspi_io_code), sh_qspi_io_code);
+
+       /*
+        * Try to allocate as big work area buffer as possible, start
+        * with 32 kiB and count down. If there is less than 256 Bytes
+        * of work area available, abort.
+        */
+       info->buffer_size = 32768;
+       while (true) {
+               ret = target_alloc_working_area_try(target, info->buffer_size,
+                                                   &info->source);
+               if (ret == ERROR_OK)
+                       return ret;
+
+               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,
+};