Commit | Line | Data |
---|---|---|
b2441318 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
8c11bffa | 2 | /* |
1da177e4 LT |
3 | * Rescue code, made to reside at the beginning of the |
4 | * flash-memory. when it starts, it checks a partition | |
5 | * table at the first sector after the rescue sector. | |
6 | * the partition table was generated by the product builder | |
7 | * script and contains offsets, lengths, types and checksums | |
8 | * for each partition that this code should check. | |
9 | * | |
10 | * If any of the checksums fail, we assume the flash is so | |
25985edc | 11 | * corrupt that we can't use it to boot into the ftp flash |
1da177e4 LT |
12 | * loader, and instead we initialize the serial port to |
13 | * receive a flash-loader and new flash image. we dont include | |
14 | * any flash code here, but just accept a certain amount of | |
15 | * bytes from the serial port and jump into it. the downloaded | |
16 | * code is put in the cache. | |
17 | * | |
18 | * The partitiontable is designed so that it is transparent to | |
19 | * code execution - it has a relative branch opcode in the | |
20 | * beginning that jumps over it. each entry contains extra | |
21 | * data so we can add stuff later. | |
22 | * | |
23 | * Partition table format: | |
24 | * | |
25 | * Code transparency: | |
8c11bffa | 26 | * |
1da177e4 LT |
27 | * 2 bytes [opcode 'nop'] |
28 | * 2 bytes [opcode 'di'] | |
29 | * 4 bytes [opcode 'ba <offset>', 8-bit or 16-bit version] | |
30 | * 2 bytes [opcode 'nop', delay slot] | |
31 | * | |
8c11bffa JN |
32 | * Table validation (at +10): |
33 | * | |
1da177e4 LT |
34 | * 2 bytes [magic/version word for partitiontable - 0xef, 0xbe] |
35 | * 2 bytes [length of all entries plus the end marker] | |
36 | * 4 bytes [checksum for the partitiontable itself] | |
37 | * | |
8c11bffa JN |
38 | * Entries, each with the following format, last has offset -1: |
39 | * | |
1da177e4 LT |
40 | * 4 bytes [offset in bytes, from start of flash] |
41 | * 4 bytes [length in bytes of partition] | |
42 | * 4 bytes [checksum, simple longword sum] | |
43 | * 2 bytes [partition type] | |
44 | * 2 bytes [flags, only bit 0 used, ro/rw = 1/0] | |
45 | * 16 bytes [reserved for future use] | |
46 | * | |
47 | * End marker | |
48 | * | |
49 | * 4 bytes [-1] | |
8c11bffa | 50 | * |
1da177e4 | 51 | * 10 bytes [0, padding] |
8c11bffa | 52 | * |
1da177e4 LT |
53 | * Bit 0 in flags signifies RW or RO. The rescue code only bothers |
54 | * to check the checksum for RO partitions, since the others will | |
55 | * change their data without updating the checksums. A 1 in bit 0 | |
56 | * means RO, 0 means RW. That way, it is possible to set a partition | |
57 | * in RO mode initially, and later mark it as RW, since you can always | |
58 | * write 0's to the flash. | |
59 | * | |
60 | * During the wait for serial input, the status LED will flash so the | |
61 | * user knows something went wrong. | |
8c11bffa JN |
62 | * |
63 | * Copyright (C) 1999-2007 Axis Communications AB | |
1da177e4 LT |
64 | */ |
65 | ||
8c11bffa JN |
66 | #ifdef CONFIG_ETRAX_AXISFLASHMAP |
67 | ||
1da177e4 | 68 | #define ASSEMBLER_MACROS_ONLY |
556dcee7 | 69 | #include <arch/sv_addr_ag.h> |
1da177e4 LT |
70 | |
71 | ;; The partitiontable is looked for at the first sector after the boot | |
72 | ;; sector. Sector size is 65536 bytes in all flashes we use. | |
8c11bffa | 73 | |
1da177e4 LT |
74 | #define PTABLE_START CONFIG_ETRAX_PTABLE_SECTOR |
75 | #define PTABLE_MAGIC 0xbeef | |
76 | ||
77 | ;; The normal Etrax100 on-chip boot ROM does serial boot at 0x380000f0. | |
99bb22bd JN |
78 | ;; That is not where we put our downloaded serial boot-code. |
79 | ;; The length is enough for downloading code that loads the rest | |
80 | ;; of itself (after having setup the DRAM etc). | |
81 | ;; It is the same length as the on-chip ROM loads, so the same | |
82 | ;; host loader can be used to load a rescued product as well as | |
83 | ;; one booted through the Etrax serial boot code. | |
8c11bffa | 84 | |
1da177e4 LT |
85 | #define CODE_START 0x40000000 |
86 | #define CODE_LENGTH 784 | |
87 | ||
88 | #ifdef CONFIG_ETRAX_RESCUE_SER0 | |
89 | #define SERXOFF R_SERIAL0_XOFF | |
90 | #define SERBAUD R_SERIAL0_BAUD | |
91 | #define SERRECC R_SERIAL0_REC_CTRL | |
92 | #define SERRDAT R_SERIAL0_REC_DATA | |
93 | #define SERSTAT R_SERIAL0_STATUS | |
94 | #endif | |
95 | #ifdef CONFIG_ETRAX_RESCUE_SER1 | |
96 | #define SERXOFF R_SERIAL1_XOFF | |
97 | #define SERBAUD R_SERIAL1_BAUD | |
98 | #define SERRECC R_SERIAL1_REC_CTRL | |
99 | #define SERRDAT R_SERIAL1_REC_DATA | |
100 | #define SERSTAT R_SERIAL1_STATUS | |
101 | #endif | |
102 | #ifdef CONFIG_ETRAX_RESCUE_SER2 | |
103 | #define SERXOFF R_SERIAL2_XOFF | |
104 | #define SERBAUD R_SERIAL2_BAUD | |
105 | #define SERRECC R_SERIAL2_REC_CTRL | |
106 | #define SERRDAT R_SERIAL2_REC_DATA | |
107 | #define SERSTAT R_SERIAL2_STATUS | |
8c11bffa | 108 | #endif |
1da177e4 LT |
109 | #ifdef CONFIG_ETRAX_RESCUE_SER3 |
110 | #define SERXOFF R_SERIAL3_XOFF | |
111 | #define SERBAUD R_SERIAL3_BAUD | |
112 | #define SERRECC R_SERIAL3_REC_CTRL | |
113 | #define SERRDAT R_SERIAL3_REC_DATA | |
114 | #define SERSTAT R_SERIAL3_STATUS | |
115 | #endif | |
116 | ||
117 | #define NOP_DI 0xf025050f | |
118 | #define RAM_INIT_MAGIC 0x56902387 | |
119 | ||
120 | .text | |
8c11bffa | 121 | |
1da177e4 LT |
122 | ;; This is the entry point of the rescue code |
123 | ;; 0x80000000 if loaded in flash (as it should be) | |
8c11bffa | 124 | ;; Since etrax actually starts at address 2 when booting from flash, we |
1da177e4 | 125 | ;; put a nop (2 bytes) here first so we dont accidentally skip the di |
7cf32cad | 126 | |
8c11bffa | 127 | nop |
1da177e4 LT |
128 | di |
129 | ||
130 | jump in_cache ; enter cached area instead | |
7cf32cad MS |
131 | in_cache: |
132 | ||
1da177e4 | 133 | |
8c11bffa JN |
134 | ;; First put a jump test to give a possibility of upgrading the |
135 | ;; rescue code without erasing/reflashing the sector. | |
136 | ;; We put a longword of -1 here and if it is not -1, we jump using | |
137 | ;; the value as jump target. Since we can always change 1's to 0's | |
138 | ;; without erasing the sector, it is possible to add new | |
1da177e4 LT |
139 | ;; code after this and altering the jumptarget in an upgrade. |
140 | ||
141 | jtcd: move.d [jumptarget], $r0 | |
142 | cmp.d 0xffffffff, $r0 | |
143 | beq no_newjump | |
144 | nop | |
8c11bffa | 145 | |
1da177e4 LT |
146 | jump [$r0] |
147 | ||
8c11bffa | 148 | jumptarget: |
1da177e4 | 149 | .dword 0xffffffff ; can be overwritten later to insert new code |
8c11bffa | 150 | |
1da177e4 | 151 | no_newjump: |
8c11bffa | 152 | #ifdef CONFIG_ETRAX_ETHERNET |
1da177e4 LT |
153 | ;; Start MII clock to make sure it is running when tranceiver is reset |
154 | move.d 0x3, $r0 ; enable = on, phy = mii_clk | |
155 | move.d $r0, [R_NETWORK_GEN_CONFIG] | |
156 | #endif | |
8c11bffa | 157 | |
1da177e4 | 158 | ;; We need to setup the bus registers before we start using the DRAM |
66ab3a74 | 159 | #include "../../../arch-v10/lib/dram_init.S" |
1da177e4 LT |
160 | |
161 | ;; we now should go through the checksum-table and check the listed | |
162 | ;; partitions for errors. | |
8c11bffa | 163 | |
1da177e4 LT |
164 | move.d PTABLE_START, $r3 |
165 | move.d [$r3], $r0 | |
166 | cmp.d NOP_DI, $r0 ; make sure the nop/di is there... | |
167 | bne do_rescue | |
168 | nop | |
8c11bffa | 169 | |
1da177e4 LT |
170 | ;; skip the code transparency block (10 bytes). |
171 | ||
172 | addq 10, $r3 | |
8c11bffa | 173 | |
1da177e4 | 174 | ;; check for correct magic |
8c11bffa | 175 | |
1da177e4 LT |
176 | move.w [$r3+], $r0 |
177 | cmp.w PTABLE_MAGIC, $r0 | |
178 | bne do_rescue ; didn't recognize - trig rescue | |
179 | nop | |
180 | ||
181 | ;; check for correct ptable checksum | |
182 | ||
183 | movu.w [$r3+], $r2 ; ptable length | |
184 | move.d $r2, $r8 ; save for later, length of total ptable | |
185 | addq 28, $r8 ; account for the rest | |
186 | move.d [$r3+], $r4 ; ptable checksum | |
187 | move.d $r3, $r1 | |
188 | jsr checksum ; r1 source, r2 length, returns in r0 | |
189 | ||
190 | cmp.d $r0, $r4 | |
191 | bne do_rescue ; didn't match - trig rescue | |
192 | nop | |
8c11bffa | 193 | |
1da177e4 LT |
194 | ;; ptable is ok. validate each entry. |
195 | ||
196 | moveq -1, $r7 | |
8c11bffa | 197 | |
1da177e4 LT |
198 | ploop: move.d [$r3+], $r1 ; partition offset (from ptable start) |
199 | bne notfirst ; check if it is the partition containing ptable | |
200 | nop ; yes.. | |
201 | move.d $r8, $r1 ; for its checksum check, skip the ptable | |
202 | move.d [$r3+], $r2 ; partition length | |
203 | sub.d $r8, $r2 ; minus the ptable length | |
204 | ba bosse | |
205 | nop | |
8c11bffa | 206 | notfirst: |
1da177e4 LT |
207 | cmp.d -1, $r1 ; the end of the ptable ? |
208 | beq flash_ok ; if so, the flash is validated | |
209 | move.d [$r3+], $r2 ; partition length | |
210 | bosse: move.d [$r3+], $r5 ; checksum | |
211 | move.d [$r3+], $r4 ; type and flags | |
212 | addq 16, $r3 ; skip the reserved bytes | |
213 | btstq 16, $r4 ; check ro flag | |
214 | bpl ploop ; rw partition, skip validation | |
215 | nop | |
216 | btstq 17, $r4 ; check bootable flag | |
217 | bpl 1f | |
218 | nop | |
219 | move.d $r1, $r7 ; remember boot partition offset | |
8c11bffa | 220 | 1: |
1da177e4 | 221 | add.d PTABLE_START, $r1 |
8c11bffa | 222 | |
1da177e4 | 223 | jsr checksum ; checksum the partition |
8c11bffa | 224 | |
1da177e4 LT |
225 | cmp.d $r0, $r5 |
226 | beq ploop ; checksums matched, go to next entry | |
227 | nop | |
228 | ||
229 | ;; otherwise fall through to the rescue code. | |
8c11bffa | 230 | |
1da177e4 LT |
231 | do_rescue: |
232 | ;; setup port PA and PB default initial directions and data | |
233 | ;; (so we can flash LEDs, and so that DTR and others are set) | |
8c11bffa | 234 | |
1da177e4 LT |
235 | move.b CONFIG_ETRAX_DEF_R_PORT_PA_DIR, $r0 |
236 | move.b $r0, [R_PORT_PA_DIR] | |
237 | move.b CONFIG_ETRAX_DEF_R_PORT_PA_DATA, $r0 | |
238 | move.b $r0, [R_PORT_PA_DATA] | |
8c11bffa | 239 | |
1da177e4 LT |
240 | move.b CONFIG_ETRAX_DEF_R_PORT_PB_DIR, $r0 |
241 | move.b $r0, [R_PORT_PB_DIR] | |
242 | move.b CONFIG_ETRAX_DEF_R_PORT_PB_DATA, $r0 | |
243 | move.b $r0, [R_PORT_PB_DATA] | |
244 | ||
245 | ;; setup the serial port at 115200 baud | |
8c11bffa | 246 | |
1da177e4 | 247 | moveq 0, $r0 |
8c11bffa | 248 | move.d $r0, [SERXOFF] |
1da177e4 LT |
249 | |
250 | move.b 0x99, $r0 | |
8c11bffa | 251 | move.b $r0, [SERBAUD] ; 115.2kbaud for both transmit and receive |
1da177e4 | 252 | |
8c11bffa JN |
253 | move.b 0x40, $r0 ; rec enable |
254 | move.b $r0, [SERRECC] | |
1da177e4 LT |
255 | |
256 | moveq 0, $r1 ; "timer" to clock out a LED red flash | |
257 | move.d CODE_START, $r3 ; destination counter | |
258 | movu.w CODE_LENGTH, $r4; length | |
8c11bffa | 259 | |
1da177e4 LT |
260 | wait_ser: |
261 | addq 1, $r1 | |
262 | #ifndef CONFIG_ETRAX_NO_LEDS | |
263 | #ifdef CONFIG_ETRAX_PA_LEDS | |
264 | move.b CONFIG_ETRAX_DEF_R_PORT_PA_DATA, $r2 | |
265 | #endif | |
266 | #ifdef CONFIG_ETRAX_PB_LEDS | |
267 | move.b CONFIG_ETRAX_DEF_R_PORT_PB_DATA, $r2 | |
268 | #endif | |
269 | move.d (1 << CONFIG_ETRAX_LED1R) | (1 << CONFIG_ETRAX_LED2R), $r0 | |
270 | btstq 16, $r1 | |
271 | bpl 1f | |
272 | nop | |
273 | or.d $r0, $r2 ; set bit | |
274 | ba 2f | |
275 | nop | |
276 | 1: not $r0 ; clear bit | |
277 | and.d $r0, $r2 | |
8c11bffa | 278 | 2: |
1da177e4 | 279 | #ifdef CONFIG_ETRAX_PA_LEDS |
8c11bffa JN |
280 | move.b $r2, [R_PORT_PA_DATA] |
281 | #endif | |
1da177e4 | 282 | #ifdef CONFIG_ETRAX_PB_LEDS |
8c11bffa | 283 | move.b $r2, [R_PORT_PB_DATA] |
1da177e4 | 284 | #endif |
1da177e4 | 285 | #endif |
8c11bffa | 286 | |
1da177e4 | 287 | ;; check if we got something on the serial port |
8c11bffa | 288 | |
1da177e4 LT |
289 | move.b [SERSTAT], $r0 |
290 | btstq 0, $r0 ; data_avail | |
291 | bpl wait_ser | |
292 | nop | |
293 | ||
294 | ;; got something - copy the byte and loop | |
295 | ||
296 | move.b [SERRDAT], $r0 | |
297 | move.b $r0, [$r3+] | |
8c11bffa | 298 | |
1da177e4 LT |
299 | subq 1, $r4 ; decrease length |
300 | bne wait_ser | |
301 | nop | |
302 | ||
303 | ;; jump into downloaded code | |
304 | ||
8c11bffa JN |
305 | move.d RAM_INIT_MAGIC, $r8 ; Tell next product that DRAM is |
306 | ; initialized | |
1da177e4 LT |
307 | jump CODE_START |
308 | ||
309 | flash_ok: | |
310 | ;; check r7, which contains either -1 or the partition to boot from | |
311 | ||
312 | cmp.d -1, $r7 | |
313 | bne 1f | |
314 | nop | |
315 | move.d PTABLE_START, $r7; otherwise use the ptable start | |
316 | 1: | |
8c11bffa JN |
317 | move.d RAM_INIT_MAGIC, $r8 ; Tell next product that DRAM is |
318 | ; initialized | |
1da177e4 LT |
319 | jump $r7 ; boot! |
320 | ||
321 | ||
322 | ;; Helper subroutines | |
323 | ||
324 | ;; Will checksum by simple addition | |
325 | ;; r1 - source | |
326 | ;; r2 - length in bytes | |
327 | ;; result will be in r0 | |
328 | checksum: | |
329 | moveq 0, $r0 | |
7cf32cad MS |
330 | moveq CONFIG_ETRAX_FLASH1_SIZE, $r6 |
331 | ||
99bb22bd JN |
332 | ;; If the first physical flash memory is exceeded wrap to the |
333 | ;; second one | |
7cf32cad MS |
334 | btstq 26, $r1 ; Are we addressing first flash? |
335 | bpl 1f | |
336 | nop | |
337 | clear.d $r6 | |
338 | ||
339 | 1: test.d $r6 ; 0 = no wrapping | |
340 | beq 2f | |
341 | nop | |
342 | lslq 20, $r6 ; Convert MB to bytes | |
343 | sub.d $r1, $r6 | |
344 | ||
345 | 2: addu.b [$r1+], $r0 | |
346 | subq 1, $r6 ; Flash memory left | |
347 | beq 3f | |
348 | subq 1, $r2 ; Length left | |
349 | bne 2b | |
1da177e4 LT |
350 | nop |
351 | ret | |
352 | nop | |
7cf32cad MS |
353 | |
354 | 3: move.d MEM_CSE1_START, $r1 ; wrap to second flash | |
355 | ba 2b | |
356 | nop | |
8c11bffa JN |
357 | |
358 | #endif |