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 34f91ce1eab3713105a8892a6ceeb3704cdb51c1..12bafa2c5c7fe18a7598324fc9fc498540ac5cd8 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 551f389de967243fd336c143aed6e3e4dff19d97..fb43a438dca5fff3a0f937c0112877edd8ee0889 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,
+};

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)