--- /dev/null
+/* 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