8435a20453ffff142899a9b9d004046e0a008095
[fw/openocd] / contrib / loaders / flash / lpcspifi_write.S
1 /***************************************************************************
2  *   Copyright (C) 2012 by George Harris                                   *
3  *   george@luminairecoffee.com                                            *
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, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
19  ***************************************************************************/
20
21         .text
22         .syntax unified
23         .cpu cortex-m3
24         .thumb
25         .thumb_func
26
27 /*
28  * Params :
29  * r0 = workarea start, status (out)
30  * r1 = workarea end
31  * r2 = target address (offset from flash base)
32  * r3 = count (bytes)
33  * r4 = page size
34  * Clobbered:
35  * r7 - rp
36  * r8 - wp, tmp
37  * r9 - send/receive data
38  * r10 - temp
39  * r11 - current page end address
40  */
41
42 /*
43  * This code is embedded within: src/flash/nor/lpcspifi.c as a "C" array.
44  *
45  * To rebuild:
46  *   arm-none-eabi-gcc -c lpcspifi_write.S
47  *   arm-none-eabi-objcopy -O binary lpcspifi_write.o lpcspifi_write.bin
48  *   xxd -c 8 -i lpcspifi_write.bin > lpcspifi_write.txt
49  *
50  * Then read and edit this result into the "C" source.
51  */
52
53 #define SSP_BASE_HIGH                           0x4008
54 #define SSP_BASE_LOW                            0x3000
55 #define SSP_CR0_OFFSET                          0x00
56 #define SSP_CR1_OFFSET                          0x04
57 #define SSP_DATA_OFFSET                         0x08
58 #define SSP_CPSR_OFFSET                         0x10
59 #define SSP_SR_OFFSET                           0x0c
60
61 #define SSP_CLOCK_BASE_HIGH             0x4005
62 #define SSP_CLOCK_BASE_LOW                      0x0000
63 #define SSP_BRANCH_CLOCK_BASE_HIGH      0x4005
64 #define SSP_BRANCH_CLOCK_BASE_LOW       0x2000
65 #define SSP_BASE_CLOCK_OFFSET           0x94
66 #define SSP_BRANCH_CLOCK_OFFSET         0x700
67
68 #define IOCONFIG_BASE_HIGH                      0x4008
69 #define IOCONFIG_BASE_LOW                       0x6000
70 #define IOCONFIG_SCK_OFFSET                     0x18c
71 #define IOCONFIG_HOLD_OFFSET            0x190
72 #define IOCONFIG_WP_OFFSET                      0x194
73 #define IOCONFIG_MISO_OFFSET            0x198
74 #define IOCONFIG_MOSI_OFFSET            0x19c
75 #define IOCONFIG_CS_OFFSET                      0x1a0
76
77 #define IO_BASE_HIGH                            0x400f
78 #define IO_BASE_LOW                             0x4000
79 #define IO_CS_OFFSET                            0xab
80 #define IODIR_BASE_HIGH                         0x400f
81 #define IODIR_BASE_LOW                          0x6000
82 #define IO_CS_DIR_OFFSET                        0x14
83
84
85 setup: /* Initialize SSP pins and module */
86         mov.w   r10, #IOCONFIG_BASE_LOW
87         movt    r10, #IOCONFIG_BASE_HIGH
88         mov.w   r8, #0xea
89         str.w   r8, [r10, #IOCONFIG_SCK_OFFSET]         /* Configure SCK pin function */
90         mov.w   r8, #0x40
91         str.w   r8, [r10, #IOCONFIG_HOLD_OFFSET]        /* Configure /HOLD pin function */
92         mov.w   r8, #0x40
93         str.w   r8, [r10, #IOCONFIG_WP_OFFSET]          /* Configure /WP pin function */
94         mov.w   r8, #0xed
95         str.w   r8, [r10, #IOCONFIG_MISO_OFFSET]        /* Configure MISO pin function */
96         mov.w   r8, #0xed
97         str.w   r8, [r10, #IOCONFIG_MOSI_OFFSET]        /* Configure MOSI pin function */
98         mov.w   r8, #0x44
99         str.w   r8, [r10, #IOCONFIG_CS_OFFSET]          /* Configure CS pin function */
100
101         mov.w   r10, #IODIR_BASE_LOW
102         movt    r10, #IODIR_BASE_HIGH
103         mov.w   r8, #0x800
104         str     r8, [r10, #IO_CS_DIR_OFFSET]            /* Set CS as output */
105         mov.w   r10, #IO_BASE_LOW
106         movt    r10, #IO_BASE_HIGH
107         mov.w   r8, #0xff
108         str.w   r8, [r10, #IO_CS_OFFSET]                        /* Set CS high */
109
110         mov.w   r10, #SSP_CLOCK_BASE_LOW
111         movt    r10, #SSP_CLOCK_BASE_HIGH
112         mov.w   r8, #0x0000
113         movt    r8, #0x0100
114         str.w   r8, [r10, #SSP_BASE_CLOCK_OFFSET]       /* Configure SSP0 base clock (use 12 MHz IRC) */
115
116         mov.w   r10, #SSP_BRANCH_CLOCK_BASE_LOW
117         movt    r10, #SSP_BRANCH_CLOCK_BASE_HIGH
118         mov.w   r8, #0x01
119         str.w   r8, [r10, #SSP_BRANCH_CLOCK_OFFSET] /* Configure (enable) SSP0 branch clock */
120
121         mov.w   r10, #SSP_BASE_LOW
122         movt    r10, #SSP_BASE_HIGH
123         mov.w   r8, #0x07
124         str.w   r8, [r10, #SSP_CR0_OFFSET]                      /* Set clock postscale */
125         mov.w   r8, #0x02
126         str.w   r8, [r10, #SSP_CPSR_OFFSET]             /* Set clock prescale */
127         str.w   r8, [r10, #SSP_CR1_OFFSET]                      /* Enable SSP in SPI mode */
128
129         mov.w   r11, #0x00
130 find_next_page_boundary:
131         add     r11, r4                 /* Increment to the next page */
132         cmp     r11, r2
133         /* If we have not reached the next page boundary after the target address, keep going */
134         bls     find_next_page_boundary
135 write_enable:
136         bl              cs_down
137         mov.w   r9, #0x06               /* Send the write enable command */
138         bl              write_data
139         bl              cs_up
140
141         bl              cs_down
142         mov.w   r9, #0x05               /* Get status register */
143         bl              write_data
144         mov.w   r9, #0x00               /* Dummy data to clock in status */
145         bl              write_data
146         bl              cs_up
147
148         tst     r9, #0x02               /* If the WE bit isn't set, we have a problem. */
149         beq     error
150 page_program:
151         bl              cs_down
152         mov.w   r9, #0x02               /* Send the page program command */
153         bl              write_data
154 write_address:
155         lsr     r9, r2, #16     /* Send the current 24-bit write address, MSB first */
156         bl              write_data
157         lsr     r9, r2, #8
158         bl              write_data
159         mov.w   r9, r2
160         bl              write_data
161 wait_fifo:
162         ldr     r8, [r0]                /* read the write pointer */
163         cmp     r8, #0                  /* if it's zero, we're gonzo */
164         beq     exit
165         ldr     r7, [r0, #4]    /* read the read pointer */
166         cmp     r7, r8                  /* wait until they are not equal */
167         beq     wait_fifo
168 write:
169         ldrb    r9, [r7], #0x01 /* Load one byte from the FIFO, increment the read pointer by 1 */
170         bl              write_data              /* send the byte to the flash chip */
171
172         cmp     r7, r1                  /* wrap the read pointer if it is at the end */
173         it      cs
174         addcs   r7, r0, #8              /* skip loader args */
175         str     r7, [r0, #4]    /* store the new read pointer */
176         subs    r3, r3, #1              /* decrement count */
177         cbz             r3, exit                /* Exit if we have written everything */
178
179         add     r2, #1                  /* Increment flash address by 1 */
180         cmp     r11, r2                 /* See if we have reached the end of a page */
181         bne     wait_fifo               /* If not, keep writing bytes */
182         bl              cs_up                   /* Otherwise, end the command and keep going w/ the next page */
183         add     r11, r4                 /* Move up the end-of-page address by the page size*/
184 wait_flash_busy:                        /* Wait for the flash to finish the previous page write */
185         bl              cs_down
186         mov.w   r9, #0x05                                       /* Get status register */
187         bl              write_data
188         mov.w   r9, #0x00                                       /* Dummy data to clock in status */
189         bl              write_data
190         bl              cs_up
191         tst     r9, #0x01                                       /* If it isn't done, keep waiting */
192         bne     wait_flash_busy
193         b               write_enable                            /* If it is done, start a new page write */
194 write_data:                                                     /* Send/receive 1 byte of data over SSP */
195         mov.w   r10, #SSP_BASE_LOW
196         movt    r10, #SSP_BASE_HIGH
197         str.w   r9, [r10, #SSP_DATA_OFFSET]     /* Write supplied data to the SSP data reg */
198 wait_transmit:
199         ldr     r9, [r10, #SSP_SR_OFFSET]       /* Check SSP status */
200         tst     r9, #0x0010                                     /* Check if BSY bit is set */
201         bne     wait_transmit                           /* If still transmitting, keep waiting */
202         ldr     r9, [r10, #SSP_DATA_OFFSET]     /* Load received data */
203         bx              lr                                                      /* Exit subroutine */
204 cs_up:
205         mov.w   r8, #0xff
206         b               cs_write
207 cs_down:
208         mov.w   r8, #0x0000
209 cs_write:
210         mov.w   r10, #IO_BASE_LOW
211         movt    r10, #IO_BASE_HIGH
212         str.w   r8, [r10, #IO_CS_OFFSET]
213         bx              lr
214 error:
215         movs    r0, #0
216         str     r0, [r2, #4]    /* set rp = 0 on error */
217 exit:
218         bl              cs_up                   /* end the command before returning */
219         mov     r0, r6
220         bkpt    #0x00
221
222         .end