contrib: replace the GPLv2-or-later license tag
[fw/openocd] / contrib / loaders / flash / mrvlqspi_write.S
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 /***************************************************************************
4  *   Copyright (C) 2014 by Mahavir Jain <mjain@marvell.com>                *
5  *                                                                         *
6  *   Adapted from (contrib/loaders/flash/lpcspifi_write.S):                *
7  *   Copyright (C) 2012 by George Harris                                   *
8  *   george@luminairecoffee.com                                            *
9  ***************************************************************************/
10
11         .text
12         .syntax unified
13         .cpu cortex-m3
14         .thumb
15         .thumb_func
16
17 /*
18  * For compilation:
19  * arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c contrib/loaders/flash/mrvlqspi_write.S
20  * arm-none-eabi-objcopy -O binary mrvlqspi_write.o code.bin
21  * Copy code.bin into mrvlqspi flash driver
22  */
23
24 /*
25  * Params :
26  * r0 = workarea start, status (out)
27  * r1 = workarea end
28  * r2 = target address (offset from flash base)
29  * r3 = count (bytes)
30  * r4 = page size
31  * r5 = qspi base address
32  * Clobbered:
33  * r7 - rp
34  * r8 - wp, tmp
35  * r9 - send/receive data
36  * r10 - current page end address
37  */
38
39 #define CNTL    0x0
40 #define CONF    0x4
41 #define DOUT    0x8
42 #define DIN     0xc
43 #define INSTR   0x10
44 #define ADDR    0x14
45 #define RDMODE  0x18
46 #define HDRCNT  0x1c
47 #define DINCNT  0x20
48
49 #define SS_EN (1 << 0)
50 #define XFER_RDY (1 << 1)
51 #define RFIFO_EMPTY (1 << 4)
52 #define WFIFO_EMPTY (1 << 6)
53 #define WFIFO_FULL (1 << 7)
54 #define FIFO_FLUSH (1 << 9)
55 #define RW_EN (1 << 13)
56 #define XFER_STOP (1 << 14)
57 #define XFER_START (1 << 15)
58
59 #define INS_WRITE_ENABLE 0x06
60 #define INS_READ_STATUS 0x05
61 #define INS_PAGE_PROGRAM 0x02
62
63 init:
64         mov.w   r10, #0x00
65 find_next_page_boundary:
66         add     r10, r4         /* Increment to the next page */
67         cmp     r10, r2
68         /* If we have not reached the next page boundary after the target address, keep going */
69         bls     find_next_page_boundary
70 write_enable:
71         /* Flush read/write fifos */
72         bl      flush_fifo
73
74         /* Instruction byte 1 */
75         movs    r8, #0x1
76         str     r8, [r5, #HDRCNT]
77
78         /* Set write enable instruction */
79         movs    r8, #INS_WRITE_ENABLE
80         str     r8, [r5, #INSTR]
81
82         movs    r9, #0x1
83         bl      start_tx
84         bl      stop_tx
85 page_program:
86         /* Instruction byte 1, Addr byte 3 */
87         movs    r8, #0x31
88         str     r8, [r5, #HDRCNT]
89         /* Todo: set addr and data pin to single */
90 write_address:
91         mov     r8, r2
92         str     r8, [r5, #ADDR]
93         /* Set page program instruction */
94         movs    r8, #INS_PAGE_PROGRAM
95         str     r8, [r5, #INSTR]
96         /* Start write transfer */
97         movs    r9, #0x1
98         bl      start_tx
99 wait_fifo:
100         ldr     r8, [r0]        /* read the write pointer */
101         cmp     r8, #0          /* if it's zero, we're gonzo */
102         beq     exit
103         ldr     r7, [r0, #4]    /* read the read pointer */
104         cmp     r7, r8          /* wait until they are not equal */
105         beq     wait_fifo
106 write:
107         ldrb    r9, [r7], #0x01 /* Load one byte from the FIFO, increment the read pointer by 1 */
108         bl      write_data      /* send the byte to the flash chip */
109
110         cmp     r7, r1          /* wrap the read pointer if it is at the end */
111         it      cs
112         addcs   r7, r0, #8      /* skip loader args */
113         str     r7, [r0, #4]    /* store the new read pointer */
114         subs    r3, r3, #1      /* decrement count */
115         cmp     r3, #0          /* Exit if we have written everything */
116         beq     write_wait
117         add     r2, #1          /* Increment flash address by 1 */
118         cmp     r10, r2         /* See if we have reached the end of a page */
119         bne     wait_fifo       /* If not, keep writing bytes */
120 write_wait:
121         bl      stop_tx         /* Otherwise, end the command and keep going w/ the next page */
122         add     r10, r4         /* Move up the end-of-page address by the page size*/
123 check_flash_busy:               /* Wait for the flash to finish the previous page write */
124         /* Flush read/write fifos */
125         bl      flush_fifo
126         /* Instruction byte 1 */
127         movs    r8, #0x1
128         str     r8, [r5, #HDRCNT]
129         /* Continuous data in of status register */
130         movs    r8, #0x0
131         str     r8, [r5, #DINCNT]
132         /* Set write enable instruction */
133         movs    r8, #INS_READ_STATUS
134         str     r8, [r5, #INSTR]
135         /* Start read transfer */
136         movs    r9, #0x0
137         bl      start_tx
138 wait_flash_busy:
139         bl      read_data
140         and.w   r9, r9, #0x1
141         cmp     r9, #0x0
142         bne.n   wait_flash_busy
143         bl      stop_tx
144         cmp     r3, #0
145         bne.n   write_enable    /* If it is done, start a new page write */
146         b       exit            /* All data written, exit */
147
148 write_data:                     /* Send/receive 1 byte of data over QSPI */
149         ldr     r8, [r5, #CNTL]
150         lsls    r8, r8, #24
151         bmi.n   write_data
152         str     r9, [r5, #DOUT]
153         bx      lr
154
155 read_data:                      /* Read 1 byte of data over QSPI */
156         ldr     r8, [r5, #CNTL]
157         lsls    r8, r8, #27
158         bmi.n   read_data
159         ldr     r9, [r5, #DIN]
160         bx      lr
161
162 flush_fifo:                     /* Flush read write fifos */
163         ldr     r8, [r5, #CONF]
164         orr.w   r8, r8, #FIFO_FLUSH
165         str     r8, [r5, #CONF]
166 flush_reset:
167         ldr     r8, [r5, #CONF]
168         lsls    r8, r8, #22
169         bmi.n   flush_reset
170         bx      lr
171
172 start_tx:
173         ldr     r8, [r5, #CNTL]
174         orr.w   r8, r8, #SS_EN
175         str     r8, [r5, #CNTL]
176 xfer_rdy:
177         ldr     r8, [r5, #CNTL]
178         lsls    r8, r8, #30
179         bpl.n   xfer_rdy
180         ldr     r8, [r5, #CONF]
181         bfi     r8, r9, #13, #1
182         orr.w   r8, r8, #XFER_START
183         str     r8, [r5, #CONF]
184         bx lr
185
186 stop_tx:
187         ldr     r8, [r5, #CNTL]
188         lsls    r8, r8, #30
189         bpl.n   stop_tx
190 wfifo_wait:
191         ldr     r8, [r5, #CNTL]
192         lsls    r8, r8, #25
193         bpl.n   wfifo_wait
194         ldr     r8, [r5, #CONF]
195         orr.w   r8, r8, #XFER_STOP
196         str     r8, [r5, #CONF]
197 xfer_start:
198         ldr     r8, [r5, #CONF]
199         lsls    r8, r8, #16
200         bmi.n   xfer_start
201 ss_disable:
202         # Disable SS_EN
203         ldr     r8, [r5, #CNTL]
204         bic.w   r8, r8, #SS_EN
205         str     r8, [r5, #CNTL]
206 wait:
207         ldr     r8, [r5, #CNTL]
208         lsls    r8, r8, #30
209         bpl.n   wait
210         bx      lr
211
212 error:
213         movs    r0, #0
214         str     r0, [r2, #4]    /* set rp = 0 on error */
215 exit:
216         mov     r0, r6
217         bkpt    #0x00
218
219         .end