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