40953acf685e95d15b2f93931921b155e3e2ba51
[fw/openocd] / contrib / loaders / flash / stmqspi / stmqspi_write.S
1 /***************************************************************************
2  *   Copyright (C) 2016 - 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 - QSPI io_base
30  * r8 - fifo start
31  * r9 - fifo end + 1
32
33  * Clobbered:
34  * r4 - rp
35  * r5 - address of QSPI_DR
36  * r7 - tmp
37  * r10 - single 0x0 / dual 0x1
38  */
39
40 #include "../../../../src/flash/nor/stmqspi.h"
41
42         .macro  qspi_abort
43         movs    r5, #(1<<SPI_ABORT)                     /* abort bit mask */
44         ldr             r7, [r3, #QSPI_CR]                      /* get QSPI_CR register */
45         orrs    r7, r7, r5                                      /* set abort bit */
46         str             r7, [r3, #QSPI_CR]                      /* store new CR register */
47         .endm
48
49         .macro  wait_busy
50 0:
51         ldr             r7, [r3, #QSPI_SR]                      /* load status */
52         lsrs    r7, r7, #(SPI_BUSY+1)           /* shift BUSY into C */
53         bcs             0b                                                      /* loop until BUSY cleared */
54         movs    r7, #(1<<SPI_TCF)                       /* TCF bitmask */
55         str             r7, [r3, #QSPI_FCR]                     /* clear TCF flag */
56         .endm
57
58 start:
59         subs    r0, r0, #1                                      /* decrement count for DLR */
60         subs    r1, r1, #1                                      /* page size mask and for DLR */
61         ldr             r4, rp                                          /* load rp */
62         ldr             r7, [r3, #QSPI_CR]                      /* get QSPI_CR register */
63         lsls    r7, r7, #(31-SPI_DUAL_FLASH)    /* clear higher order bits */
64         lsrs    r7, r7, #31                                     /* DUAL_FLASH bit into bit 0 */
65         mov             r10, r7                                         /* save in r10 */
66 wip_loop:
67         qspi_abort                                                      /* start in clean state */
68         movs    r5, #QSPI_DR                            /* load QSPI_DR address offset */
69         adds    r5, r5, r3                                      /* address of QSPI_DR */
70         wait_busy
71         mov             r7, r10                                         /* get dual bit */
72         str             r7, [r3, #QSPI_DLR]                     /* one or two (for dual) bytes */
73         ldr             r7, ccr_read_status                     /* CCR for status read */
74         str             r7, [r3, #QSPI_CCR]                     /* initiate status read */
75         ldr             r7, [r3, #QSPI_SR]                      /* wait for command startup */
76         ldrb    r7, [r5]                                        /* get first status register */
77         lsrs    r7, r7, #(SPIFLASH_BSY+1)       /* if first flash busy, */
78         bcs             wip_loop                                        /* then poll again */
79         mov             r7, r10                                         /* get dual bit */
80         tst             r7, r7                                          /* dual mode ? */
81         beq             write_enable                            /* not dual, then ok */
82         ldrb    r7, [r5]                                        /* get second status register */
83         lsrs    r7, r7, #(SPIFLASH_BSY+1)       /* if second flash busy, */
84         bcs             wip_loop                                        /* then poll again */
85 write_enable:
86         tst             r0, r0                                          /* test residual count */
87         bmi             exit                                            /* if negative, then finished */
88         wait_busy
89         ldr             r7, ccr_write_enable            /* CCR for write enable */
90         str             r7, [r3, #QSPI_CCR]                     /* initiate write enable */
91         wait_busy
92         mov             r7, r10                                         /* get dual bit */
93         str             r7, [r3, #QSPI_DLR]                     /* one or two (for dual) bytes */
94         ldr             r7, ccr_read_status                     /* CCR for status read */
95         str             r7, [r3, #QSPI_CCR]                     /* initiate status read */
96         ldr             r7, [r3, #QSPI_SR]                      /* wait for command startup */
97         ldrb    r7, [r5]                                        /* get first status register */
98         lsrs    r7, r7, #(SPIFLASH_WE+1)        /* if first flash not */
99         bcc             error                                           /* write enabled, then error */
100         mov             r7, r10                                         /* get dual bit */
101         tst             r7, r7                                          /* dual mode ? */
102         beq             start_write                                     /* not dual, then ok */
103         ldrb    r7, [r5]                                        /* get second status register */
104         lsrs    r7, r7, #(SPIFLASH_WE+1)        /* if second flash not */
105         bcc             error                                           /* write enabled, then error */
106 start_write:
107         wait_busy
108         mov             r7, r2                                          /* get current start address */
109         orrs    r7, r7, r1                                      /* end of current page */
110         subs    r7, r7, r2                                      /* count-1 to end of page */
111         cmp             r7, r0                                          /* if this count <= remaining */
112         bls             write_dlr                                       /* then write to end of page */
113         mov             r7, r0                                          /* else write all remaining */
114 write_dlr:
115         str             r7, [r3, #QSPI_DLR]                     /* size-1 in DLR register */
116         ldr             r7, ccr_page_write                      /* CCR for page write */
117         str             r7, [r3, #QSPI_CCR]                     /* initiate transfer */
118         str             r2, [r3, #QSPI_AR]                      /* store SPI start address */
119         ldr             r7, [r3, #QSPI_SR]                      /* wait for command startup */
120 write_loop:
121         ldr             r7, wp                                          /* get wp */
122         cmp             r7, #0                                          /* if wp equals 0 */
123         beq             exit                                            /* then abort */
124         cmp             r4, r7                                          /* check if fifo empty */
125         beq             write_loop                                      /* wait until not empty */
126         ldrb    r7, [r4, #0]                            /* read next byte */
127         strb    r7, [r5]                                        /* write next byte to DR */
128         adds    r4, r4, #1                                      /* increment internal rp */
129         cmp             r4, r9                                          /* internal rp beyond end? */
130         blo             upd_write                                       /* if no, then ok */
131         mov             r4, r8                                          /* else wrap around */
132 upd_write:
133         adr             r7, rp                                          /* get address of rp */
134         str             r4, [r7]                                        /* store updated rp */
135         adds    r2, r2, #1                                      /* increment address */
136         subs    r0, r0, #1                                      /* decrement (count-1) */
137         bmi             page_end                                        /* stop if no data left */
138         tst             r2, r1                                          /* page end ? */
139         bne             write_loop                                      /* if not, then next byte */
140 page_end:
141         ldr             r7, [r3, #QSPI_SR]                      /* load status */
142         lsrs    r7, r7, #(SPI_TCF+1)            /* shift TCF into C */
143         bcc             page_end                                        /* loop until TCF set */
144         bal             wip_loop                                        /* then next page */
145
146 error:
147         movs    r0, #0                                          /* return 0xFFFFFFFF */
148         subs    r0, r0, #2                                      /* for error */
149 exit:
150         adds    r0, r0, #1                                      /* increment count due to the -1 */
151         qspi_abort                                                      /* to idle state */
152
153         .align  2                                                       /* align to word, bkpt is 4 words */
154         bkpt    #0                                                      /* before code end for exit_point */
155         .align  2                                                       /* align to word */
156
157         .space  4                                                       /* not used */
158 ccr_read_status:
159         .space  4                                                       /* QSPI_CCR value for READ_STATUS command */
160         .space  4                                                       /* not used */
161         .space  4                                                       /* not used */
162
163         .space  4                                                       /* not used */
164 ccr_write_enable:
165         .space  4                                                       /* QSPI_CCR value for WRITE_ENABLE command */
166         .space  4                                                       /* not used */
167         .space  4                                                       /* not used */
168
169         .space  4                                                       /* not used */
170 ccr_page_write:
171         .space  4                                                       /* QSPI_CCR value for PAGE_PROG command */
172         .space  4                                                       /* not used */
173         .space  4                                                       /* not used */
174
175         .equ wp, .                                                      /* wp, uint32_t */
176         .equ rp, wp + 4                                         /* rp, uint32_t */
177         .equ buffer, rp + 4                                     /* buffer follows right away */