flash/nor/sh_qspi: Add SH QSPI driver

Add driver for the SH QSPI controller. This SPI controller is often
connected to the boot SPI NOR flash on R-Car Gen2 platforms.

Add the following two lines to board TCL file to bind the driver on
R-Car Gen2 SoC and make SRAM work area available:

  flash bank flash0 sh_qspi 0xe6b10000 0 0 0 ${_TARGETNAME}0 cs0
  ${_TARGETNAME}0 configure -work-area-phys 0xe6300000 -work-area-virt 0xe6300000 -work-area-size 0x10000 -work-area-backup 0

To install mainline U-Boot on the board, use the following procedure:

  proc update_uboot {} {
    # SPL
    flash erase_sector 0 0x0 0x0
    flash write_bank 0 /u-boot/spl/u-boot-spl.bin 0x0
    # U-Boot
    flash erase_sector 0 0x5 0x6
    flash write_bank 0 /u-boot/u-boot.img 0x140000
  }

Change-Id: Ief22f61e93bcabae37f6e371156dece6c4be3459
Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
---
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>
This commit is contained in:
Marek Vasut 2019-04-02 16:44:18 +02:00 committed by Oleksij Rempel
parent d612baacaa
commit 8b72657001
7 changed files with 1308 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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*)
}
}

View 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 \

View 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,

912
src/flash/nor/sh_qspi.c Normal file
View File

@ -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,
};