867a02483063cd51baa278039c458fbfe8f00961
[fw/openocd] / contrib / loaders / flash / stmqspi / stmoctospi_write.S
1 /***************************************************************************
2  *   Copyright (C) 2018 by Andreas Bolsch                                  *
3  *   andreas.bolsch@mni.thm.de                                             *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
17  ***************************************************************************/
18
19         .text
20         .syntax unified
21         .cpu cortex-m0
22         .thumb
23         .thumb_func
24
25 /* Params:
26  * r0 - total count (bytes), remaining bytes (out, 0 means successful)
27  * r1 - flash page size
28  * r2 - address offset into flash
29  * r3 - OCTOSPI io_base
30  * r8 - fifo start
31  * r9 - fifo end + 1
32
33  * Clobbered:
34  * r4 - rp
35  * r5 - address of OCTOSPI_DR
36  * r6 - address of OCTOSPI_CCR
37  * r7 - tmp
38  * r10 - single 0x0 / dual 0x1
39  */
40
41 #include "../../../../src/flash/nor/stmqspi.h"
42
43 #define OCTOSPI_CCR_CCR                                 (OCTOSPI_CCR - OCTOSPI_CCR)
44 #define OCTOSPI_TCR_CCR                                 (OCTOSPI_TCR - OCTOSPI_CCR)
45 #define OCTOSPI_IR_CCR                                  (OCTOSPI_IR - OCTOSPI_CCR)
46
47         .macro  octospi_abort
48         movs    r5, #(1<<SPI_ABORT)                     /* abort bit mask */
49         ldr             r7, [r3, #OCTOSPI_CR]           /* get OCTOSPI CR register */
50         orrs    r7, r7, r5                                      /* set abort bit */
51         str             r7, [r3, #OCTOSPI_CR]           /* store new CR register */
52         .endm
53
54         .macro  wait_busy
55 0:
56         ldr             r7, [r3, #OCTOSPI_SR]           /* load status */
57         lsrs    r7, r7, #(SPI_BUSY+1)           /* shift BUSY into C */
58         bcs             0b                                                      /* loop until BUSY cleared */
59         movs    r7, #(1<<SPI_TCF)                       /* TCF bitmask */
60         str             r7, [r3, #OCTOSPI_FCR]          /* clear TCF flag */
61         .endm
62
63 start:
64         subs    r0, r0, #1                                      /* decrement count for DLR */
65         subs    r1, r1, #1                                      /* page size mask and for DLR */
66         ldr             r4, rp                                          /* load rp */
67         ldr             r7, [r3, #OCTOSPI_CR]           /* get OCTOSPI_CR register */
68         lsls    r7, r7, #(31-SPI_DUAL_FLASH)    /* clear higher order bits */
69         lsrs    r7, r7, #31                                     /* DUAL_FLASH bit into bit 0 */
70         mov             r10, r7                                         /* save in r10 */
71 wip_loop:
72         octospi_abort                                           /* start in clean state */
73         movs    r5, #OCTOSPI_DR                         /* load OCTOSPI_DR address offset */
74         adds    r5, r5, r3                                      /* address of OCTOSPI_DR */
75         movs    r6, #OCTOSPI_CCR-OCTOSPI_DR     /* load OCTOSPI_CCR address offset */
76         adds    r6, r6, r5                                      /* address of OCTOSPI_CCR */
77         wait_busy
78         ldr             r7, cr_read_status                      /* indirect read mode */
79         str             r7, [r3, #OCTOSPI_CR]           /* set mode */
80         mov             r7, r10                                         /* get dual bit */
81         str             r7, [r3, #OCTOSPI_DLR]          /* one or two (for dual) bytes */
82         ldr             r7, ccr_read_status                     /* CCR for status read */
83         str             r7, [r6, #OCTOSPI_CCR_CCR]      /* initiate status read */
84         ldr             r7, tcr_read_status                     /* TCR for status read */
85         str             r7, [r6, #OCTOSPI_TCR_CCR]      /* instruction */
86         ldr             r7, ir_read_status                      /* IR for status read */
87         str             r7, [r6, #OCTOSPI_IR_CCR]       /* instruction */
88         movs    r7, #0                                          /* dummy address */
89         str             r7, [r3, #OCTOSPI_AR]           /* into AR (for 8-line mode) */
90         ldrb    r7, [r5]                                        /* get first status register */
91         lsrs    r7, r7, #(SPIFLASH_BSY+1)       /* if first flash busy, */
92         bcs             wip_loop                                        /* then poll again */
93         mov             r7, r10                                         /* get dual bit */
94         tst             r7, r7                                          /* dual mode ? */
95         beq             write_enable                            /* not dual, then ok */
96         ldrb    r7, [r5]                                        /* get second status register */
97         lsrs    r7, r7, #(SPIFLASH_BSY+1)       /* if second flash busy, */
98         bcs             wip_loop                                        /* then poll again */
99 write_enable:
100         tst             r0, r0                                          /* test residual count */
101         bmi             exit                                            /* if negative, then finished */
102         wait_busy
103         ldr             r7, cr_write_enable                     /* indirect write mode */
104         str             r7, [r3, #OCTOSPI_CR]           /* set mode */
105         ldr             r7, ccr_write_enable            /* CCR for write enable */
106         str             r7, [r6, #OCTOSPI_CCR_CCR]      /* initiate write enable */
107         ldr             r7, tcr_write_enable            /* TCR for write enable */
108         str             r7, [r6, #OCTOSPI_TCR_CCR]      /* write enable instruction */
109         ldr             r7, ir_write_enable                     /* IR for write enable */
110         str             r7, [r6, #OCTOSPI_IR_CCR]       /* instruction */
111         movs    r7, #0                                          /* silicon bug in L5? dummy write */
112         str             r7, [r3, #OCTOSPI_AR]           /* into AR resolves issue */
113         wait_busy
114         ldr             r7, cr_read_status                      /* indirect read mode */
115         str             r7, [r3, #OCTOSPI_CR]           /* set mode */
116         mov             r7, r10                                         /* get dual count */
117         str             r7, [r3, #OCTOSPI_DLR]          /* one or two (for dual) bytes */
118         ldr             r7, ccr_read_status                     /* CCR for status read */
119         str             r7, [r6, #OCTOSPI_CCR_CCR]      /* initiate status read */
120         ldr             r7, tcr_read_status                     /* TCR for status read */
121         str             r7, [r6, #OCTOSPI_TCR_CCR]      /* instruction */
122         ldr             r7, ir_read_status                      /* IR for status read */
123         str             r7, [r6, #OCTOSPI_IR_CCR]       /* instruction */
124         movs    r7, #0                                          /* dummy address */
125         str             r7, [r3, #OCTOSPI_AR]           /* into AR (for 8-line mode) */
126         ldrb    r7, [r5]                                        /* get first status register */
127         lsrs    r7, r7, #(SPIFLASH_WE+1)        /* if first flash not */
128         bcc             error                                           /* write enabled, then error */
129         mov             r7, r10                                         /* get dual bit */
130         tst             r7, r7                                          /* dual mode ? */
131         beq             start_write                                     /* not dual, then ok */
132         ldrb    r7, [r5]                                        /* get second status register */
133         lsrs    r7, r7, #(SPIFLASH_WE+1)        /* if second flash not */
134         bcc             error                                           /* write enabled, then error */
135 start_write:
136         wait_busy
137         ldr             r7, cr_page_write                       /* indirect write mode */
138         str             r7, [r3, #OCTOSPI_CR]           /* set mode */
139         mov             r7, r2                                          /* get current start address */
140         orrs    r7, r7, r1                                      /* end of current page */
141         subs    r7, r7, r2                                      /* count-1 to end of page */
142         cmp             r7, r0                                          /* if this count <= remaining */
143         bls             write_dlr                                       /* then write to end of page */
144         mov             r7, r0                                          /* else write all remaining */
145 write_dlr:
146         str             r7, [r3, #OCTOSPI_DLR]          /* size-1 in DLR register */
147         ldr             r7, ccr_page_write                      /* CCR for page write */
148         str             r7, [r6, #OCTOSPI_CCR_CCR]      /* initiate transfer */
149         ldr             r7, tcr_page_write                      /* TCR for page write */
150         str             r7, [r6, #OCTOSPI_TCR_CCR]      /* instruction */
151         ldr             r7, ir_page_write                       /* IR for page write */
152         str             r7, [r6, #OCTOSPI_IR_CCR]       /* instruction */
153         str             r2, [r3, #OCTOSPI_AR]           /* store SPI start address */
154 write_loop:
155         ldr             r7, wp                                          /* get wp */
156         cmp             r7, #0                                          /* if wp equals 0 */
157         beq             exit                                            /* then abort */
158         cmp             r4, r7                                          /* check if fifo empty */
159         beq             write_loop                                      /* wait until not empty */
160         ldrb    r7, [r4, #0]                            /* read next byte */
161         strb    r7, [r5]                                        /* write next byte to DR */
162         adds    r4, r4, #1                                      /* increment internal rp */
163         cmp             r4, r9                                          /* internal rp beyond end? */
164         blo             upd_write                                       /* if no, then ok */
165         mov             r4, r8                                          /* else wrap around */
166 upd_write:
167         adr             r7, rp                                          /* get address of rp */
168         str             r4, [r7]                                        /* store updated rp */
169         adds    r2, r2, #1                                      /* increment address */
170         subs    r0, r0, #1                                      /* decrement (count-1) */
171         bmi             page_end                                        /* stop if no data left */
172         tst             r2, r1                                          /* page end ? */
173         bne             write_loop                                      /* if not, then next byte */
174 page_end:
175         ldr             r7, [r3, #OCTOSPI_SR]           /* load status */
176         lsrs    r7, r7, #(SPI_TCF+1)            /* shift TCF into C */
177         bcc             page_end                                        /* loop until TCF set */
178         bal             wip_loop                                        /* then next page */
179
180 error:
181         movs    r0, #0                                          /* return 0xFFFFFFFF */
182         subs    r0, r0, #2                                      /* for error */
183 exit:
184         adds    r0, r0, #1                                      /* increment count due to the -1 */
185         octospi_abort                                           /* to idle state */
186         .align  2                                                       /* align to word, bkpt is 4 words */
187         bkpt    #0                                                      /* before code end for exit_point */
188         .align  2                                                       /* align to word */
189
190 cr_read_status:
191         .space  4                                                       /* OCTOSPI_CR value for READ_STATUS command */
192 ccr_read_status:
193         .space  4                                                       /* OCTOSPI_CCR value for READ_STATUS command */
194 tcr_read_status:
195         .space  4                                                       /* OCTOSPI_TCR value for READ_STATUS command */
196 ir_read_status:
197         .space  4                                                       /* OCTOSPI_IR value for READ_STATUS command */
198
199 cr_write_enable:
200         .space  4                                                       /* OCTOSPI_CR value for WRITE_ENABLE command */
201 ccr_write_enable:
202         .space  4                                                       /* OCTOSPI_CCR value for WRITE_ENABLE command */
203 tcr_write_enable:
204         .space  4                                                       /* OCTOSPI_TCR value for WRITE_ENABLE command */
205 ir_write_enable:
206         .space  4                                                       /* OCTOSPI_IR value for WRITE_ENABLE command */
207
208 cr_page_write:
209         .space  4                                                       /* OCTOSPI_CR value for PAGE_PROG command */
210 ccr_page_write:
211         .space  4                                                       /* OCTOSPI_CCR value for PAGE_PROG command */
212 tcr_page_write:
213         .space  4                                                       /* OCTOSPI_TCR value for PAGE_PROG command */
214 ir_page_write:
215         .space  4                                                       /* OCTOSPI_IR value for PAGE_PROG command */
216
217         .equ wp, .                                                      /* wp, uint32_t */
218         .equ rp, wp + 4                                         /* rp, uint32_t */
219         .equ buffer, rp + 4                                     /* buffer follows right away */