openocd/contrib/loaders/flash/sh_qspi/sh_qspi.S

307 lines
5.6 KiB
ArmAsm

/* 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