aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/loaders/flash/sh_qspi/Makefile37
-rw-r--r--contrib/loaders/flash/sh_qspi/sh_qspi.S306
-rw-r--r--contrib/loaders/flash/sh_qspi/sh_qspi.inc37
-rw-r--r--contrib/loaders/flash/sh_qspi/sh_qspi.ld13
-rw-r--r--src/flash/nor/Makefile.am1
-rw-r--r--src/flash/nor/drivers.c2
-rw-r--r--src/flash/nor/sh_qspi.c912
7 files changed, 1308 insertions, 0 deletions
diff --git a/contrib/loaders/flash/sh_qspi/Makefile b/contrib/loaders/flash/sh_qspi/Makefile
new file mode 100644
index 00000000..2bfbad1b
--- /dev/null
+++ b/contrib/loaders/flash/sh_qspi/Makefile
@@ -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
index 00000000..78eb1e81
--- /dev/null
+++ b/contrib/loaders/flash/sh_qspi/sh_qspi.S
@@ -0,0 +1,306 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * SH QSPI (Quad SPI) driver
+ * Copyright (C) 2019 Marek Vasut <marek.vasut@gmail.com>
+ */
+
+#define BIT(n) (1UL << (n))
+/* SH QSPI register bit masks <REG>_<BIT> */
+#define SPCR_MSTR 0x08
+#define SPCR_SPE 0x40
+#define SPSR_SPRFF 0x80
+#define SPSR_SPTEF 0x20
+#define SPPCR_IO3FV 0x04
+#define SPPCR_IO2FV 0x02
+#define SPPCR_IO1FV 0x01
+#define SPBDCR_RXBC0 BIT(0)
+#define SPCMD_SCKDEN BIT(15)
+#define SPCMD_SLNDEN BIT(14)
+#define SPCMD_SPNDEN BIT(13)
+#define SPCMD_SSLKP BIT(7)
+#define SPCMD_BRDV0 BIT(2)
+#define SPCMD_INIT1 SPCMD_SCKDEN | SPCMD_SLNDEN | \
+ SPCMD_SPNDEN | SPCMD_SSLKP | \
+ SPCMD_BRDV0
+#define SPCMD_INIT2 SPCMD_SPNDEN | SPCMD_SSLKP | \
+ SPCMD_BRDV0
+#define SPBFCR_TXRST BIT(7)
+#define SPBFCR_RXRST BIT(6)
+#define SPBFCR_TXTRG 0x30
+#define SPBFCR_RXTRG 0x07
+
+/* SH QSPI register set */
+#define SH_QSPI_SPCR 0x00
+#define SH_QSPI_SSLP 0x01
+#define SH_QSPI_SPPCR 0x02
+#define SH_QSPI_SPSR 0x03
+#define SH_QSPI_SPDR 0x04
+#define SH_QSPI_SPSCR 0x08
+#define SH_QSPI_SPSSR 0x09
+#define SH_QSPI_SPBR 0x0a
+#define SH_QSPI_SPDCR 0x0b
+#define SH_QSPI_SPCKD 0x0c
+#define SH_QSPI_SSLND 0x0d
+#define SH_QSPI_SPND 0x0e
+#define SH_QSPI_DUMMY0 0x0f
+#define SH_QSPI_SPCMD0 0x10
+#define SH_QSPI_SPCMD1 0x12
+#define SH_QSPI_SPCMD2 0x14
+#define SH_QSPI_SPCMD3 0x16
+#define SH_QSPI_SPBFCR 0x18
+#define SH_QSPI_DUMMY1 0x19
+#define SH_QSPI_SPBDCR 0x1a
+#define SH_QSPI_SPBMUL0 0x1c
+#define SH_QSPI_SPBMUL1 0x20
+#define SH_QSPI_SPBMUL2 0x24
+#define SH_QSPI_SPBMUL3 0x28
+
+.syntax unified
+.arm
+.text
+
+.macro wait_for_spsr, spsrbit
+ 1: ldrb r12, [r0, #SH_QSPI_SPSR]
+ tst r12, \spsrbit
+ beq 1b
+.endm
+
+.macro sh_qspi_xfer
+ bl sh_qspi_cs_activate
+ str r6, [r0, SH_QSPI_SPBMUL0]
+ bl sh_qspi_xfer_common
+ bl sh_qspi_cs_deactivate
+.endm
+
+.macro sh_qspi_write_enable
+ ldr r4, =SPIFLASH_WRITE_ENABLE
+ adr r5, _start
+ add r4, r5
+ mov r5, #0x0
+ mov r6, #0x1
+ sh_qspi_xfer
+.endm
+
+.macro sh_qspi_wait_till_ready
+ 1: ldr r4, =SPIFLASH_READ_STATUS
+ adr r5, _start
+ add r4, r5
+ mov r5, #0x0
+ mov r6, #0x2
+ sh_qspi_xfer
+ and r13, #0x1
+ cmp r13, #0x1
+ beq 1b
+.endm
+
+/*
+ * r0: controller base address
+ * r1: data buffer base address
+ * r2: BIT(31) -- page program (not read)
+ * BIT(30) -- 4-byte address (not 3-byte)
+ * BIT(29) -- 512-byte page (not 256-byte)
+ * BIT(27:20) -- SF command
+ * BIT(19:0) -- amount of data to read/write
+ * r3: SF target address
+ *
+ * r7: data size
+ * r8: page size
+ *
+ * r14: lr, link register
+ * r15: pc, program counter
+ *
+ * Clobber: r4, r5, r6, r7, r8
+ */
+
+.global _start
+_start:
+ bic r7, r2, #0xff000000
+ bic r7, r7, #0x00f00000
+
+ and r8, r2, #(1 << 31)
+ cmp r8, #(1 << 31)
+ beq do_page_program
+
+/* fast read */
+
+ bl sh_qspi_cs_activate
+
+ bl sh_qspi_setup_command
+ add r8, r6, r7
+ str r8, [r0, SH_QSPI_SPBMUL0]
+ bl sh_qspi_xfer_common
+
+ mov r4, #0x0
+ mov r5, r1
+ mov r6, r7
+ bl sh_qspi_xfer_common
+
+ bl sh_qspi_cs_deactivate
+
+ b end
+
+do_page_program:
+
+ mov r8, #0x100
+ tst r2, (1 << 29)
+ movne r8, #0x200
+
+do_pp_next_page:
+ /* Check if less then page bytes left. */
+ cmp r7, r8
+ movlt r8, r7
+
+ sh_qspi_write_enable
+
+ bl sh_qspi_cs_activate
+
+ bl sh_qspi_setup_command
+ str r6, [r0, SH_QSPI_SPBMUL0]
+ bl sh_qspi_xfer_common
+
+ mov r4, r1
+ mov r5, #0x0
+ mov r6, r8
+
+ bl sh_qspi_xfer_common
+
+ bl sh_qspi_cs_deactivate
+
+ sh_qspi_wait_till_ready
+
+ add r1, r8
+ add r3, r8
+ sub r7, r8
+ cmp r7, #0
+
+ bne do_pp_next_page
+
+end:
+ bkpt #0
+
+sh_qspi_cs_activate:
+ /* Set master mode only */
+ mov r12, #SPCR_MSTR
+ strb r12, [r0, SH_QSPI_SPCR]
+
+ /* Set command */
+ mov r12, #SPCMD_INIT1
+ strh r12, [r0, SH_QSPI_SPCMD0]
+
+ /* Reset transfer and receive Buffer */
+ ldrb r12, [r0, SH_QSPI_SPSCR]
+ orr r12, #(SPBFCR_TXRST | SPBFCR_RXRST)
+ strb r12, [r0, SH_QSPI_SPBFCR]
+
+ /* Clear transfer and receive Buffer control bit */
+ ldrb r12, [r0, SH_QSPI_SPBFCR]
+ bic r12, #(SPBFCR_TXRST | SPBFCR_RXRST)
+ strb r12, [r0, SH_QSPI_SPBFCR]
+
+ /* Set sequence control method. Use sequence0 only */
+ mov r12, #0x00
+ strb r12, [r0, SH_QSPI_SPSCR]
+
+ /* Enable SPI function */
+ ldrb r12, [r0, SH_QSPI_SPCR]
+ orr r12, #SPCR_SPE
+ strb r12, [r0, SH_QSPI_SPCR]
+
+ mov pc, lr
+
+sh_qspi_cs_deactivate:
+ /* Disable SPI function */
+ ldrb r12, [r0, SH_QSPI_SPCR]
+ bic r12, #SPCR_SPE
+ strb r12, [r0, SH_QSPI_SPCR]
+
+ mov pc, lr
+
+/*
+ * r0, controller base address
+ * r4, tx buffer
+ * r5, rx buffer
+ * r6, xfer len, non-zero
+ *
+ * Upon exit, r13 contains the last byte in SPDR
+ *
+ * Clobber: r11, r12, r13
+ */
+sh_qspi_xfer_common:
+prepcopy:
+ ldr r13, [r0, #SH_QSPI_SPBFCR]
+ orr r13, #(SPBFCR_TXTRG | SPBFCR_RXTRG)
+ mov r11, #32
+ cmp r6, #32
+
+ biclt r13, #(SPBFCR_TXTRG | SPBFCR_RXTRG)
+ movlt r11, #1
+
+copy:
+ str r13, [r0, #SH_QSPI_SPBFCR]
+
+ wait_for_spsr SPSR_SPTEF
+
+ mov r12, r11
+ mov r13, #0
+ cmp r4, #0
+ beq 3f
+
+2: ldrb r13, [r4], #1
+ strb r13, [r0, #SH_QSPI_SPDR]
+ subs r12, #1
+ bne 2b
+ b 4f
+
+3: strb r13, [r0, #SH_QSPI_SPDR]
+ subs r12, #1
+ bne 3b
+
+4: wait_for_spsr SPSR_SPRFF
+
+ mov r12, r11
+ cmp r5, #0
+ beq 6f
+
+5: ldrb r13, [r0, #SH_QSPI_SPDR]
+ strb r13, [r5], #1
+ subs r12, #1
+ bne 5b
+ b 7f
+
+6: ldrb r13, [r0, #SH_QSPI_SPDR]
+ subs r12, #1
+ bne 6b
+
+7: subs r6, r11
+ bne prepcopy
+
+ mov pc, lr
+
+sh_qspi_setup_command:
+ ldr r4, =SPIFLASH_SCRATCH_DATA
+ adr r5, _start
+ add r4, r5
+ and r12, r2, #0x0ff00000
+ lsr r12, #20
+ strb r12, [r4]
+ mov r12, r3
+ strb r12, [r4, #4]
+ lsr r12, #8
+ strb r12, [r4, #3]
+ lsr r12, #8
+ strb r12, [r4, #2]
+ lsr r12, #8
+ strb r12, [r4, #1]
+ lsr r12, #8
+ mov r5, #0x0
+ mov r6, #0x4
+ tst r2, (1 << 30)
+ movne r6, #0x5
+
+ mov pc, lr
+
+SPIFLASH_READ_STATUS: .byte 0x05 /* Read Status Register */
+SPIFLASH_WRITE_ENABLE: .byte 0x06 /* Write Enable */
+SPIFLASH_NOOP: .byte 0x00
+SPIFLASH_SCRATCH_DATA: .byte 0x00, 0x0, 0x0, 0x0, 0x0
diff --git a/contrib/loaders/flash/sh_qspi/sh_qspi.inc b/contrib/loaders/flash/sh_qspi/sh_qspi.inc
new file mode 100644
index 00000000..ca913923
--- /dev/null
+++ b/contrib/loaders/flash/sh_qspi/sh_qspi.inc
@@ -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
index 00000000..2683c520
--- /dev/null
+++ b/contrib/loaders/flash/sh_qspi/sh_qspi.ld
@@ -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*)
+ }
+}
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index 34f91ce1..12bafa2c 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -51,6 +51,7 @@ NOR_DRIVERS = \
%D%/psoc4.c \
%D%/psoc5lp.c \
%D%/psoc6.c \
+ %D%/sh_qspi.c \
%D%/sim3x.c \
%D%/spi.c \
%D%/stmsmi.c \
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index 551f389d..fb43a438 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -66,6 +66,7 @@ extern const struct flash_driver psoc5lp_flash;
extern const struct flash_driver psoc5lp_eeprom_flash;
extern const struct flash_driver psoc5lp_nvl_flash;
extern const struct flash_driver psoc6_flash;
+extern const struct flash_driver sh_qspi_flash;
extern const struct flash_driver sim3x_flash;
extern const struct flash_driver stellaris_flash;
extern const struct flash_driver stm32f1x_flash;
@@ -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
index 00000000..931b0b17
--- /dev/null
+++ b/src/flash/nor/sh_qspi.c
@@ -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,
+};