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