1 | /** |
---|
2 | * @file |
---|
3 | * |
---|
4 | * @ingroup mpc55xx |
---|
5 | * |
---|
6 | * @brief MPC55XX flash memory support. |
---|
7 | * |
---|
8 | * I set my MMU up to map what will finally be in flash into RAM and at the |
---|
9 | * same time I map the flash to a different location. When the software |
---|
10 | * is tested I can use this to copy the RAM version of the program into |
---|
11 | * the flash and when I reboot I'm running out of flash. |
---|
12 | * |
---|
13 | * I use a flag word located after the boot configuration half-word to |
---|
14 | * indicate that the MMU should be left alone, and I don't include the RCHW |
---|
15 | * or that flag in my call to this routine. |
---|
16 | * |
---|
17 | * There are obviously other uses for this. |
---|
18 | **/ |
---|
19 | |
---|
20 | /* |
---|
21 | * Copyright (c) 2009-2011 |
---|
22 | * HD Associates, Inc. |
---|
23 | * 18 Main Street |
---|
24 | * Pepperell, MA 01463 |
---|
25 | * USA |
---|
26 | * dufault@hda.com |
---|
27 | * |
---|
28 | * The license and distribution terms for this file may be found in the file |
---|
29 | * LICENSE in this distribution or at http://www.rtems.com/license/LICENSE. |
---|
30 | */ |
---|
31 | |
---|
32 | #include <errno.h> |
---|
33 | #include <sys/types.h> |
---|
34 | #include <mpc55xx/regs.h> |
---|
35 | #include <mpc55xx/mpc55xx.h> |
---|
36 | |
---|
37 | #include <libcpu/powerpc-utility.h> |
---|
38 | #include <rtems/powerpc/registers.h> |
---|
39 | |
---|
40 | /* Set up the memory ranges for the flash on |
---|
41 | * the MPC5553, MPC5554, MPC5566 and MPC5567. |
---|
42 | * I check if it is an unknown CPU and return an error. |
---|
43 | * |
---|
44 | * These CPUS have a low, mid, and high space of memory. |
---|
45 | * |
---|
46 | * Only the low space really needs a table like this, but for simplicity |
---|
47 | * I do low, mid, and high the same way. |
---|
48 | */ |
---|
49 | struct range { /* A memory range. */ |
---|
50 | uint32_t lower; |
---|
51 | uint32_t upper; |
---|
52 | }; |
---|
53 | |
---|
54 | /* The ranges of the memory banks for the low space. All the |
---|
55 | * chips I'm looking at share this low format, identified by LAS=6: |
---|
56 | * 2 16K banks, |
---|
57 | * 2 48K banks, |
---|
58 | * 2 64K banks. |
---|
59 | */ |
---|
60 | static const struct range lsel_ranges[] = { |
---|
61 | { 0, (1*16 )*1024 - 1}, |
---|
62 | {(1*16 )*1024, (2*16 )*1024 - 1}, |
---|
63 | {(2*16 )*1024, (2*16 + 1*48 )*1024 - 1}, |
---|
64 | {(2*16 + 1*48 )*1024, (2*16 + 2*48 )*1024 - 1}, |
---|
65 | {(2*16 + 2*48 )*1024, (2*16 + 2*48 + 1*64)*1024 - 1}, |
---|
66 | {(2*16 + 2*48 + 1*64)*1024, (2*16 + 2*48 + 2*64)*1024 - 1}, |
---|
67 | }; |
---|
68 | |
---|
69 | /* The ranges of the memory blocks for the mid banks, 2 128K banks. |
---|
70 | * Again, all the chips share this, identified by MAS=0. |
---|
71 | */ |
---|
72 | #define MBSTART ((2*16+2*48+2*64)*1024) |
---|
73 | static const struct range msel_ranges[] = { |
---|
74 | {MBSTART , MBSTART + 1*128*1024 - 1}, |
---|
75 | {MBSTART + 1*128*1024, MBSTART + 2*128*1024 - 1}, |
---|
76 | }; |
---|
77 | |
---|
78 | /* The ranges of the memory blocks for the high banks. |
---|
79 | * There are N 128K banks, where N <= 20, |
---|
80 | * and is identified by looking at the SIZE field. |
---|
81 | * |
---|
82 | * This could benefit from being redone to save a few bytes |
---|
83 | * and provide for bigger flash spaces. |
---|
84 | */ |
---|
85 | #define HBSTART (MBSTART+2*128*1024) |
---|
86 | static const struct range hbsel_ranges[] = { |
---|
87 | {HBSTART , HBSTART + 1*128*1024 - 1}, |
---|
88 | {HBSTART + 1*128*1024, HBSTART + 2*128*1024 - 1}, |
---|
89 | {HBSTART + 2*128*1024, HBSTART + 3*128*1024 - 1}, |
---|
90 | {HBSTART + 3*128*1024, HBSTART + 4*128*1024 - 1}, |
---|
91 | {HBSTART + 4*128*1024, HBSTART + 5*128*1024 - 1}, |
---|
92 | {HBSTART + 5*128*1024, HBSTART + 6*128*1024 - 1}, |
---|
93 | {HBSTART + 6*128*1024, HBSTART + 7*128*1024 - 1}, |
---|
94 | {HBSTART + 7*128*1024, HBSTART + 8*128*1024 - 1}, |
---|
95 | {HBSTART + 8*128*1024, HBSTART + 9*128*1024 - 1}, |
---|
96 | {HBSTART + 9*128*1024, HBSTART + 10*128*1024 - 1}, |
---|
97 | {HBSTART + 10*128*1024, HBSTART + 11*128*1024 - 1}, |
---|
98 | {HBSTART + 11*128*1024, HBSTART + 12*128*1024 - 1}, |
---|
99 | {HBSTART + 12*128*1024, HBSTART + 13*128*1024 - 1}, |
---|
100 | {HBSTART + 13*128*1024, HBSTART + 14*128*1024 - 1}, |
---|
101 | {HBSTART + 14*128*1024, HBSTART + 15*128*1024 - 1}, |
---|
102 | {HBSTART + 15*128*1024, HBSTART + 16*128*1024 - 1}, |
---|
103 | {HBSTART + 16*128*1024, HBSTART + 17*128*1024 - 1}, |
---|
104 | {HBSTART + 17*128*1024, HBSTART + 18*128*1024 - 1}, |
---|
105 | {HBSTART + 18*128*1024, HBSTART + 19*128*1024 - 1}, |
---|
106 | {HBSTART + 19*128*1024, HBSTART + 20*128*1024 - 1}, |
---|
107 | }; |
---|
108 | |
---|
109 | /* Set bits in a bitmask to indicate which banks are |
---|
110 | * within the range "first" and "last". |
---|
111 | */ |
---|
112 | static void |
---|
113 | range_set( |
---|
114 | uint32_t first, |
---|
115 | uint32_t last, |
---|
116 | int *p_bits, |
---|
117 | const struct range *pr, |
---|
118 | int n_range |
---|
119 | ) |
---|
120 | { |
---|
121 | int i; |
---|
122 | int bits = 0; |
---|
123 | for (i = 0; i < n_range; i++) { |
---|
124 | /* If the upper limit is less than "first" or the lower limit |
---|
125 | * is greater than "last" then the block is not in range. |
---|
126 | */ |
---|
127 | if ( !(pr[i].upper < first || pr[i].lower > last)) { |
---|
128 | bits |= (1 << i); /* This block is in the range, set the bit. */ |
---|
129 | } |
---|
130 | |
---|
131 | } |
---|
132 | *p_bits = bits; |
---|
133 | } |
---|
134 | |
---|
135 | #define N(ARG) (sizeof(ARG)/sizeof(ARG[0])) |
---|
136 | |
---|
137 | /** Return the size of the on-chip flash |
---|
138 | * verifying that this is a device that we know about. |
---|
139 | * @return 0 for OK, non-zero for error: |
---|
140 | * - MPC55XX_FLASH_VERIFY_ERR for LAS not 6 or MAS not 0. |
---|
141 | * @note This is overriding what verify means! |
---|
142 | * - MPC55XX_FLASH_SIZE_ERR Not a chip I've checked against the manual, |
---|
143 | * athat is, SIZE not 5, 7, or 11. |
---|
144 | */ |
---|
145 | int |
---|
146 | mpc55xx_flash_size( |
---|
147 | uint32_t *p_size /**< The size is returned here. */ |
---|
148 | ) |
---|
149 | { |
---|
150 | /* On the MPC5553, MPC5554, MPC5566, and MP5567 the |
---|
151 | * low address space LAS field is 0x6 and all have |
---|
152 | * six blocks sized 2*16k, 2*48k, and 2*64k. |
---|
153 | * |
---|
154 | * All the mid and high address spaces have 128K blocks. |
---|
155 | * |
---|
156 | * The mid address space MAS size field is 0 for the above machines, |
---|
157 | * and they all have 2 128K blocks. |
---|
158 | * |
---|
159 | * For the high address space we look at the |
---|
160 | * size field to figure out the size. The SIZE field is: |
---|
161 | * |
---|
162 | * 5 for 1.5MB (MPC5553) |
---|
163 | * 7 for 2MB (MPC5554, MPC5567) |
---|
164 | * 11 for 3MB (MPC5566) |
---|
165 | */ |
---|
166 | int hblocks; /* The number of blocks in the high address space. */ |
---|
167 | |
---|
168 | /* Verify the configuration matches one of the chips that I've checked out. |
---|
169 | */ |
---|
170 | if (FLASH.MCR.B.LAS != 6 || FLASH.MCR.B.MAS != 0) { |
---|
171 | return MPC55XX_FLASH_VERIFY_ERR; |
---|
172 | } |
---|
173 | |
---|
174 | switch(FLASH.MCR.B.SIZE) { |
---|
175 | case 5: |
---|
176 | hblocks = 8; |
---|
177 | break; |
---|
178 | |
---|
179 | case 7: |
---|
180 | hblocks = 12; |
---|
181 | break; |
---|
182 | |
---|
183 | case 11: |
---|
184 | hblocks = 20; |
---|
185 | break; |
---|
186 | |
---|
187 | default: |
---|
188 | return MPC55XX_FLASH_SIZE_ERR; |
---|
189 | } |
---|
190 | |
---|
191 | /* The first two banks are 256K. |
---|
192 | * The high block has "hblocks" 128K blocks. |
---|
193 | */ |
---|
194 | *p_size = 256*1024 + 256*1024 + hblocks * 128*1024; |
---|
195 | return 0; |
---|
196 | } |
---|
197 | |
---|
198 | /* Unlock the flash blocks if "p_locked" points to something that is 0. |
---|
199 | * If it is a NULL pointer then we aren't allowed to do the unlock. |
---|
200 | */ |
---|
201 | static int |
---|
202 | unlock_once(int lsel, int msel, int hbsel, int *p_locked) |
---|
203 | { |
---|
204 | union LMLR_tag lmlr; |
---|
205 | union SLMLR_tag slmlr; |
---|
206 | union HLR_tag hlr; |
---|
207 | rtems_interrupt_level level; |
---|
208 | |
---|
209 | /* If we're already locked return. |
---|
210 | */ |
---|
211 | if (p_locked && (*p_locked == 1)) { |
---|
212 | return 0; |
---|
213 | } |
---|
214 | |
---|
215 | /* Do we have to lock something in the low or mid block? |
---|
216 | */ |
---|
217 | rtems_interrupt_disable(level); |
---|
218 | lmlr = FLASH.LMLR; |
---|
219 | if ((lsel || msel) && (lmlr.B.LME == 0)) { |
---|
220 | union LMLR_tag lmlr_unlock; |
---|
221 | lmlr_unlock.B.LLOCK=~lsel; |
---|
222 | lmlr_unlock.B.MLOCK=~msel; |
---|
223 | lmlr_unlock.B.SLOCK=1; |
---|
224 | |
---|
225 | if (lmlr.B.LLOCK != lmlr_unlock.B.LLOCK || |
---|
226 | lmlr.B.MLOCK != lmlr_unlock.B.MLOCK) { |
---|
227 | if (p_locked == 0) { |
---|
228 | rtems_interrupt_enable(level); |
---|
229 | return MPC55XX_FLASH_LOCK_ERR; |
---|
230 | } else { |
---|
231 | *p_locked = 1; |
---|
232 | } |
---|
233 | FLASH.LMLR.R = 0xA1A11111; /* Unlock. */ |
---|
234 | FLASH.LMLR = lmlr_unlock; |
---|
235 | } |
---|
236 | } |
---|
237 | rtems_interrupt_enable(level); |
---|
238 | |
---|
239 | rtems_interrupt_disable(level); |
---|
240 | slmlr = FLASH.SLMLR; |
---|
241 | if ((lsel || msel) && (slmlr.B.SLE == 0)) { |
---|
242 | union SLMLR_tag slmlr_unlock; |
---|
243 | slmlr_unlock.B.SLLOCK=~lsel; |
---|
244 | slmlr_unlock.B.SMLOCK=~msel; |
---|
245 | slmlr_unlock.B.SSLOCK=1; |
---|
246 | |
---|
247 | if (slmlr.B.SLLOCK != slmlr_unlock.B.SLLOCK || |
---|
248 | slmlr.B.SMLOCK != slmlr_unlock.B.SMLOCK) { |
---|
249 | if (p_locked == 0) { |
---|
250 | rtems_interrupt_enable(level); |
---|
251 | return MPC55XX_FLASH_LOCK_ERR; |
---|
252 | } else { |
---|
253 | *p_locked = 1; |
---|
254 | } |
---|
255 | FLASH.SLMLR.R = 0xC3C33333; /* Unlock. */ |
---|
256 | FLASH.SLMLR = slmlr_unlock; |
---|
257 | } |
---|
258 | } |
---|
259 | rtems_interrupt_enable(level); |
---|
260 | |
---|
261 | /* Do we have to unlock something in the high block? |
---|
262 | */ |
---|
263 | rtems_interrupt_disable(level); |
---|
264 | hlr = FLASH.HLR; |
---|
265 | if (hbsel && (hlr.B.HBE == 0)) { |
---|
266 | union HLR_tag hlr_unlock; |
---|
267 | hlr_unlock.B.HBLOCK = ~hbsel; |
---|
268 | |
---|
269 | if (hlr.B.HBLOCK != hlr_unlock.B.HBLOCK) { |
---|
270 | if (p_locked == 0) { |
---|
271 | return MPC55XX_FLASH_LOCK_ERR; |
---|
272 | rtems_interrupt_enable(level); |
---|
273 | } else { |
---|
274 | *p_locked = 1; |
---|
275 | } |
---|
276 | FLASH.HLR.R = 0xB2B22222; /* Unlock. */ |
---|
277 | FLASH.HLR = hlr_unlock; |
---|
278 | } |
---|
279 | } |
---|
280 | rtems_interrupt_enable(level); |
---|
281 | |
---|
282 | return 0; |
---|
283 | } |
---|
284 | |
---|
285 | static inline uint32_t |
---|
286 | tsize(int i) |
---|
287 | { |
---|
288 | return 1 << (10 + 2 * i); |
---|
289 | } |
---|
290 | |
---|
291 | static int |
---|
292 | addr_map( |
---|
293 | int to_phys, /* If 1 lookup physical else lookup mapped. */ |
---|
294 | const void *addr, /* The address to look up. */ |
---|
295 | uint32_t *p_result /* Result is here. */ |
---|
296 | ) |
---|
297 | { |
---|
298 | uint32_t u_addr = (uint32_t)addr; |
---|
299 | uint32_t mas0, mas1, mas2, mas3; |
---|
300 | uint32_t start, end; |
---|
301 | rtems_interrupt_level level; |
---|
302 | int i; |
---|
303 | |
---|
304 | for (i = 0; i < 32; i++) { |
---|
305 | mas0 = 0x10000000 | (i << 16); |
---|
306 | rtems_interrupt_disable(level); |
---|
307 | PPC_SET_SPECIAL_PURPOSE_REGISTER(FSL_EIS_MAS0, mas0); |
---|
308 | asm volatile("tlbre"); |
---|
309 | mas1 = PPC_SPECIAL_PURPOSE_REGISTER(FSL_EIS_MAS1); |
---|
310 | mas2 = PPC_SPECIAL_PURPOSE_REGISTER(FSL_EIS_MAS2); |
---|
311 | mas3 = PPC_SPECIAL_PURPOSE_REGISTER(FSL_EIS_MAS3); |
---|
312 | rtems_interrupt_enable(level); |
---|
313 | |
---|
314 | if (mas1 & 0x80000000) { |
---|
315 | /* Valid. */ |
---|
316 | start = (to_phys ? mas2 : mas3) & 0xFFFFF000; |
---|
317 | end = start + tsize((mas1 >> 8) & 0x0000000F); |
---|
318 | /* Are we within range? |
---|
319 | */ |
---|
320 | if (start <= u_addr && end >= u_addr) { |
---|
321 | uint32_t offset = (to_phys ? mas3 : mas2) & 0xFFFFF000; |
---|
322 | *p_result = u_addr - offset; |
---|
323 | return 0; |
---|
324 | } |
---|
325 | } |
---|
326 | } |
---|
327 | |
---|
328 | /* Not found in a TLB. |
---|
329 | */ |
---|
330 | return ESRCH; |
---|
331 | } |
---|
332 | |
---|
333 | /** Return the physical address corresponding to a mapped address. |
---|
334 | @return 0 if OK, ESRCH if not found in TLB1. |
---|
335 | **/ |
---|
336 | int |
---|
337 | mpc55xx_physical_address( |
---|
338 | const void *addr, /**< Mapped address. */ |
---|
339 | uint32_t *p_result /**< Result returned here. */ |
---|
340 | ) |
---|
341 | { |
---|
342 | return addr_map(1, addr, p_result); |
---|
343 | } |
---|
344 | |
---|
345 | /** Return the mapped address corresponding to a mapped address. |
---|
346 | @return 0 if OK, ESRCH if not found in TLB1. |
---|
347 | **/ |
---|
348 | int |
---|
349 | mpc55xx_mapped_address( |
---|
350 | const void *addr, /**< Mapped address. */ |
---|
351 | uint32_t *p_result /**< Result returned here. */ |
---|
352 | ) |
---|
353 | { |
---|
354 | return addr_map(0, addr, p_result); |
---|
355 | } |
---|
356 | |
---|
357 | /** |
---|
358 | * Copy memory from an address into the flash when flash is relocated |
---|
359 | * If programming fails the address that it failed at can be returned. |
---|
360 | @note At end of operation the flash may be left writable. |
---|
361 | * Use mpc55xx_flash_read_only() to set read-only. |
---|
362 | @return Zero for OK, non-zero for error: |
---|
363 | * - ESRCH Can't lookup where something lives. |
---|
364 | * - EPERM Attempt to write to non-writable flash. |
---|
365 | * - ETXTBSY Attempt to flash overlapping regions. |
---|
366 | * - MPC55XX_FLASH_CONFIG_ERR for LAS not 6 or MAS not 0. |
---|
367 | * - MPC55XX_FLASH_SIZE_ERR for SIZE not 5, 7, or 11. |
---|
368 | * - MPC55XX_FLASH_RANGE_ERR for illegal access: |
---|
369 | * - first or first+last outside of flash; |
---|
370 | * - first not on a mod(8) boundary; |
---|
371 | * - nbytes not multiple of 8. |
---|
372 | * - MPC55XX_FLASH_ERASE_ERR Erase requested but failed. |
---|
373 | * - MPC55XX_FLASH_PROGRAM_ERR Program requested but failed. |
---|
374 | * - MPC55XX_FLASH_NOT_BLANK_ERR Blank check requested but not blank. |
---|
375 | * - MPC55XX_FLASH_VERIFY_ERR Verify requested but failed. |
---|
376 | * - MPC55XX_FLASH_LOCK_ERR Unlock requested but failed. |
---|
377 | **/ |
---|
378 | |
---|
379 | int |
---|
380 | mpc55xx_flash_copy_op( |
---|
381 | void *dest, /**< An address in the flash to copy to. */ |
---|
382 | const void *src, /**< An address in memory to copy from. */ |
---|
383 | size_t nbytes, /**< The number of bytes to copy. */ |
---|
384 | uint32_t opmask, /**< Bitmask of operations to perform. |
---|
385 | * - MPC55XX_FLASH_UNLOCK: Unlock the blocks. |
---|
386 | * - MPC55XX_FLASH_ERASE: Erase the blocks. |
---|
387 | * - MPC55XX_FLASH_BLANK_CHECK: Verify the blocks are blank. |
---|
388 | * - MPC55XX_FLASH_PROGRAM: Program the FLASH. |
---|
389 | * - MPC55XX_FLASH_VERIFY: Verify the regions match. |
---|
390 | **/ |
---|
391 | uint32_t *p_fail /**< If not NULL then the address where the operation |
---|
392 | * failed is returned here. |
---|
393 | **/ |
---|
394 | ) |
---|
395 | { |
---|
396 | uint32_t udest, usrc, flash_size; |
---|
397 | int r; |
---|
398 | int peg; /* Program or Erase Good - Did it work? */ |
---|
399 | |
---|
400 | int lsel; /* Low block select bits. */ |
---|
401 | int msel; /* Mid block select bits. */ |
---|
402 | int hbsel; /* High block select bits. */ |
---|
403 | |
---|
404 | int s_lsel; /* Source Low block select bits. */ |
---|
405 | int s_msel; /* Source Mid block select bits. */ |
---|
406 | int s_hbsel; /* Source High block select bits. */ |
---|
407 | |
---|
408 | int unlocked = 0; |
---|
409 | int *p_unlocked; |
---|
410 | int i; |
---|
411 | int nwords; /* The number of 32 bit words to write. */ |
---|
412 | volatile uint32_t *flash; /* Where the flash is mapped in. */ |
---|
413 | volatile uint32_t *memory; /* What to copy into flash. */ |
---|
414 | uint32_t offset; /* Where the FLASH is mapped into memory. */ |
---|
415 | rtems_interrupt_level level; |
---|
416 | |
---|
417 | if ( (r = mpc55xx_flash_size(&flash_size))) { |
---|
418 | return r; |
---|
419 | } |
---|
420 | |
---|
421 | /* Get where the flash is mapped in. |
---|
422 | */ |
---|
423 | offset = mpc55xx_flash_address(); |
---|
424 | |
---|
425 | udest = ((uint32_t)dest) - offset; |
---|
426 | if ( (r = mpc55xx_physical_address(src, &usrc)) ) { |
---|
427 | return r; |
---|
428 | } |
---|
429 | |
---|
430 | /* Verify that the address being programmed is in flash and that it is |
---|
431 | * a multiple of 64 bits. |
---|
432 | * Someone else can remove the 64-bit restriction. |
---|
433 | */ |
---|
434 | if (udest > flash_size || |
---|
435 | udest + nbytes > flash_size || |
---|
436 | (udest & 0x7) != 0 || |
---|
437 | (nbytes & 0x7) != 0) { |
---|
438 | return MPC55XX_FLASH_RANGE_ERR; |
---|
439 | } |
---|
440 | |
---|
441 | if (opmask == 0) { |
---|
442 | return 0; |
---|
443 | } |
---|
444 | |
---|
445 | /* If we're going to do a write-style operation the flash must be writable. |
---|
446 | */ |
---|
447 | if ((opmask & |
---|
448 | (MPC55XX_FLASH_UNLOCK | MPC55XX_FLASH_ERASE | MPC55XX_FLASH_PROGRAM)) && |
---|
449 | !mpc55xx_flash_writable() |
---|
450 | ) { |
---|
451 | return EPERM; |
---|
452 | } |
---|
453 | |
---|
454 | /* If we aren't allowed to unlock then set the pointer to zero. |
---|
455 | * That is how "unlock_once" decides we can't unlock. |
---|
456 | */ |
---|
457 | p_unlocked = (opmask & MPC55XX_FLASH_UNLOCK) ? &unlocked : 0; |
---|
458 | |
---|
459 | /* Set up the bit masks for the blocks to program or erase. |
---|
460 | */ |
---|
461 | range_set(udest, udest + nbytes, &lsel, lsel_ranges, N( lsel_ranges)); |
---|
462 | range_set(udest, udest + nbytes, &msel, msel_ranges, N( msel_ranges)); |
---|
463 | range_set(udest, udest + nbytes, &hbsel, hbsel_ranges, N(hbsel_ranges)); |
---|
464 | |
---|
465 | range_set(usrc, usrc + nbytes, &s_lsel, lsel_ranges, N( lsel_ranges)); |
---|
466 | range_set(usrc, usrc + nbytes, &s_msel, msel_ranges, N( msel_ranges)); |
---|
467 | range_set(usrc, usrc + nbytes, &s_hbsel, hbsel_ranges, N(hbsel_ranges)); |
---|
468 | |
---|
469 | /* Are we attempting overlapping flash? |
---|
470 | */ |
---|
471 | if ((lsel & s_lsel) | (msel & s_msel) | (hbsel & s_hbsel)) { |
---|
472 | return ETXTBSY; |
---|
473 | } |
---|
474 | |
---|
475 | nwords = nbytes / 4; |
---|
476 | flash = (volatile uint32_t *)dest; |
---|
477 | memory = (volatile uint32_t *)src; |
---|
478 | |
---|
479 | /* In the following sections any "Step N" notes refer to |
---|
480 | * the steps in "13.4.2.3 Flash Programming" in the reference manual. |
---|
481 | * XXX Do parts of this neeed to be protected by interrupt locks? |
---|
482 | */ |
---|
483 | |
---|
484 | if (opmask & MPC55XX_FLASH_ERASE) { /* Erase. */ |
---|
485 | if ( (r = unlock_once(lsel, msel, hbsel, p_unlocked)) ) { |
---|
486 | return r; |
---|
487 | } |
---|
488 | |
---|
489 | rtems_interrupt_disable(level); |
---|
490 | FLASH.MCR.B.ERS = 1; /* Step 1: Select erase. */ |
---|
491 | |
---|
492 | FLASH.LMSR.B.LSEL = lsel; /* Step 2: Select blocks to be erased. */ |
---|
493 | FLASH.LMSR.B.MSEL = msel; |
---|
494 | FLASH.HSR.B.HBSEL = hbsel; |
---|
495 | |
---|
496 | flash[0] = 1; /* Step 3: Write to any address in the flash |
---|
497 | * (the "erase interlock write)". |
---|
498 | */ |
---|
499 | FLASH.MCR.B.EHV = 1; /* Step 4: Enable high V to start erase. */ |
---|
500 | rtems_interrupt_enable(level); |
---|
501 | while (FLASH.MCR.B.DONE == 0) { /* Step 5: Wait until done. */ |
---|
502 | } |
---|
503 | rtems_interrupt_disable(level); |
---|
504 | peg = FLASH.MCR.B.PEG; /* Save result. */ |
---|
505 | FLASH.MCR.B.EHV = 0; /* Disable high voltage. */ |
---|
506 | FLASH.MCR.B.ERS = 0; /* De-select erase. */ |
---|
507 | rtems_interrupt_enable(level); |
---|
508 | if (peg == 0) { |
---|
509 | return MPC55XX_FLASH_ERASE_ERR; /* Flash erase failed. */ |
---|
510 | } |
---|
511 | } |
---|
512 | |
---|
513 | if (opmask & MPC55XX_FLASH_BLANK_CHECK) { /* Verify blank. */ |
---|
514 | for (i = 0; i < nwords; i++) { |
---|
515 | if (flash[i] != 0xffffffff) { |
---|
516 | if (p_fail) { |
---|
517 | *p_fail = (uint32_t)(flash + i); |
---|
518 | } |
---|
519 | return MPC55XX_FLASH_NOT_BLANK_ERR; /* Not blank. */ |
---|
520 | } |
---|
521 | } |
---|
522 | } |
---|
523 | |
---|
524 | /* Program. |
---|
525 | */ |
---|
526 | if (opmask & MPC55XX_FLASH_PROGRAM) { |
---|
527 | int chunk = 0; /* Used to collect programming into 256 bit chunks. */ |
---|
528 | |
---|
529 | if ( (r = unlock_once(lsel, msel, hbsel, p_unlocked)) ) { |
---|
530 | return r; |
---|
531 | } |
---|
532 | FLASH.MCR.B.PGM = 1; /* Step 1 */ |
---|
533 | |
---|
534 | rtems_interrupt_disable(level); |
---|
535 | |
---|
536 | for (i = 0; i < nwords; i += 2) { |
---|
537 | flash[i] = memory[i]; /* Step 2 */ |
---|
538 | flash[i + 1] = memory[i + 1]; /* Always program in min 64 bits. */ |
---|
539 | |
---|
540 | /* Step 3 is "write additional words" */ |
---|
541 | |
---|
542 | /* Try to program in chunks of 256 bits. |
---|
543 | * Collect the 64 bit writes into 256 bit ones: |
---|
544 | */ |
---|
545 | chunk++; |
---|
546 | if (chunk == 4) { |
---|
547 | /* Collected 4 64-bits for a 256 bit chunk. */ |
---|
548 | FLASH.MCR.B.EHV = 1; /* Step 4: Enable high V. */ |
---|
549 | |
---|
550 | rtems_interrupt_enable(level); |
---|
551 | while (FLASH.MCR.B.DONE == 0) { /* Step 5: Wait until done. */ |
---|
552 | } |
---|
553 | rtems_interrupt_disable(level); |
---|
554 | |
---|
555 | peg = FLASH.MCR.B.PEG; /* Step 6: Save result. */ |
---|
556 | FLASH.MCR.B.EHV = 0; /* Step 7: Disable high V. */ |
---|
557 | if (peg == 0) { |
---|
558 | FLASH.MCR.B.PGM = 0; |
---|
559 | rtems_interrupt_enable(level); |
---|
560 | if (p_fail) { |
---|
561 | *p_fail = (uint32_t)(flash + i); |
---|
562 | } |
---|
563 | return MPC55XX_FLASH_PROGRAM_ERR; /* Programming failed. */ |
---|
564 | } |
---|
565 | chunk = 0; /* Reset chunk counter. */ |
---|
566 | } |
---|
567 | /* Step 8: Back to step 2. */ |
---|
568 | } |
---|
569 | |
---|
570 | if (!chunk) { |
---|
571 | FLASH.MCR.B.PGM = 0; |
---|
572 | rtems_interrupt_enable(level); |
---|
573 | } else { |
---|
574 | /* If there is anything left in that last chunk flush it out: |
---|
575 | */ |
---|
576 | FLASH.MCR.B.EHV = 1; |
---|
577 | |
---|
578 | rtems_interrupt_enable(level); |
---|
579 | while (FLASH.MCR.B.DONE == 0) { /* Wait until done. */ |
---|
580 | } |
---|
581 | rtems_interrupt_disable(level); |
---|
582 | |
---|
583 | peg = FLASH.MCR.B.PEG; /* Save result. */ |
---|
584 | FLASH.MCR.B.EHV = 0; /* Disable high voltage. */ |
---|
585 | FLASH.MCR.B.PGM = 0; |
---|
586 | rtems_interrupt_enable(level); |
---|
587 | |
---|
588 | if (peg == 0) { |
---|
589 | if (p_fail) { |
---|
590 | *p_fail = (uint32_t)(flash + i); |
---|
591 | } |
---|
592 | return MPC55XX_FLASH_PROGRAM_ERR; /* Programming failed. */ |
---|
593 | } |
---|
594 | } |
---|
595 | } |
---|
596 | |
---|
597 | if (opmask & MPC55XX_FLASH_VERIFY) { /* Verify memory matches. */ |
---|
598 | for (i = 0; i < nwords; i++) { |
---|
599 | if (flash[i] != memory[i]) { |
---|
600 | if (p_fail) { /* Return the failed address. */ |
---|
601 | *p_fail = (uint32_t)(flash + i); |
---|
602 | } |
---|
603 | return MPC55XX_FLASH_VERIFY_ERR; /* Verification failed. */ |
---|
604 | } |
---|
605 | } |
---|
606 | } |
---|
607 | |
---|
608 | return 0; |
---|
609 | } |
---|
610 | |
---|
611 | /** Simple flash copy with a signature that matches memcpy. |
---|
612 | @note At end of operation the flash may be left writable. |
---|
613 | * Use mpc55xx_flash_read_only() to set read-only. |
---|
614 | @return Zero for OK, non-zero for error. |
---|
615 | * see flash_copy_op() for possible errors. |
---|
616 | **/ |
---|
617 | int |
---|
618 | mpc55xx_flash_copy( |
---|
619 | void *dest, /**< An address in the flash to copy to. */ |
---|
620 | const void *src, /**< An address in the flash copy from. */ |
---|
621 | size_t nbytes /**< The number of bytes to copy. */ |
---|
622 | ) |
---|
623 | { |
---|
624 | return mpc55xx_flash_copy_op(dest, src, nbytes, |
---|
625 | (MPC55XX_FLASH_UNLOCK | |
---|
626 | MPC55XX_FLASH_ERASE | |
---|
627 | MPC55XX_FLASH_BLANK_CHECK | |
---|
628 | MPC55XX_FLASH_PROGRAM | |
---|
629 | MPC55XX_FLASH_VERIFY ), 0); |
---|
630 | } |
---|
631 | |
---|
632 | /** Make the flash read-write. |
---|
633 | @note This assumes the flash is mapped by TLB1 entry 1. |
---|
634 | */ |
---|
635 | void |
---|
636 | mpc55xx_flash_set_read_write(void) |
---|
637 | { |
---|
638 | rtems_interrupt_level level; |
---|
639 | rtems_interrupt_disable(level); |
---|
640 | PPC_SET_SPECIAL_PURPOSE_REGISTER( FSL_EIS_MAS0, 0x10010000); |
---|
641 | asm volatile("tlbre"); |
---|
642 | PPC_SET_SPECIAL_PURPOSE_REGISTER_BITS(FSL_EIS_MAS3, 0x0000000C); |
---|
643 | asm volatile("tlbwe"); |
---|
644 | rtems_interrupt_enable(level); |
---|
645 | } |
---|
646 | |
---|
647 | /** Make the flash read-only. |
---|
648 | @note This assumes the flash is mapped by TLB1 entry 1. |
---|
649 | */ |
---|
650 | void |
---|
651 | mpc55xx_flash_set_read_only(void) |
---|
652 | { |
---|
653 | rtems_interrupt_level level; |
---|
654 | rtems_interrupt_disable(level); |
---|
655 | PPC_SET_SPECIAL_PURPOSE_REGISTER( FSL_EIS_MAS0, 0x10010000); |
---|
656 | asm volatile("tlbre"); |
---|
657 | PPC_CLEAR_SPECIAL_PURPOSE_REGISTER_BITS(FSL_EIS_MAS3, 0x0000000C); |
---|
658 | asm volatile("tlbwe"); |
---|
659 | rtems_interrupt_enable(level); |
---|
660 | } |
---|
661 | |
---|
662 | /** See if the flash is writable. |
---|
663 | * @note This assumes the flash is mapped by TLB1 entry 1. |
---|
664 | * @note It needs to be writable by both user and supervisor. |
---|
665 | */ |
---|
666 | int |
---|
667 | mpc55xx_flash_writable(void) |
---|
668 | { |
---|
669 | uint32_t mas3; |
---|
670 | rtems_interrupt_level level; |
---|
671 | |
---|
672 | rtems_interrupt_disable(level); |
---|
673 | PPC_SET_SPECIAL_PURPOSE_REGISTER( FSL_EIS_MAS0, 0x10010000); |
---|
674 | asm volatile("tlbre"); |
---|
675 | mas3 = PPC_SPECIAL_PURPOSE_REGISTER(FSL_EIS_MAS3); |
---|
676 | rtems_interrupt_enable(level); |
---|
677 | |
---|
678 | return ((mas3 & 0x0000000C) == 0x0000000C) ? 1 : 0; |
---|
679 | } |
---|
680 | |
---|
681 | /** Return the address where the flash is mapped in. |
---|
682 | @note This assumes the flash is mapped by TLB1 entry 1. |
---|
683 | **/ |
---|
684 | uint32_t |
---|
685 | mpc55xx_flash_address(void) |
---|
686 | { |
---|
687 | uint32_t mas2; |
---|
688 | rtems_interrupt_level level; |
---|
689 | |
---|
690 | rtems_interrupt_disable(level); |
---|
691 | PPC_SET_SPECIAL_PURPOSE_REGISTER( FSL_EIS_MAS0, 0x10010000); |
---|
692 | asm volatile("tlbre"); |
---|
693 | mas2 = PPC_SPECIAL_PURPOSE_REGISTER(FSL_EIS_MAS2); |
---|
694 | rtems_interrupt_enable(level); |
---|
695 | |
---|
696 | return mas2 & 0xFFFFF000; |
---|
697 | } |
---|