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