[f3bf21f2] | 1 | /* |
---|
| 2 | * $Id$ |
---|
| 3 | * |
---|
| 4 | * RTEMS Project (http://www.rtems.org/) |
---|
| 5 | * |
---|
| 6 | * Copyright 2007 Chris Johns (chrisj@rtems.org) |
---|
| 7 | */ |
---|
| 8 | /** |
---|
| 9 | * Provide flash support for the AM26LV160 device. |
---|
| 10 | * |
---|
| 11 | * The M29W160D is the same device. |
---|
| 12 | */ |
---|
| 13 | |
---|
| 14 | #include <stdio.h> |
---|
| 15 | #include <errno.h> |
---|
| 16 | |
---|
| 17 | #include <rtems.h> |
---|
| 18 | |
---|
| 19 | #include <libchip/am29lv160.h> |
---|
| 20 | |
---|
[a61cc6c] | 21 | #ifndef AM26LV160_ERROR_TRACE |
---|
[f3bf21f2] | 22 | #define AM26LV160_ERROR_TRACE (0) |
---|
[a61cc6c] | 23 | #endif |
---|
[f3bf21f2] | 24 | |
---|
| 25 | /** |
---|
| 26 | * Boot blocks at the top |
---|
| 27 | */ |
---|
| 28 | const rtems_fdisk_segment_desc rtems_am29lv160t_segments[4] = |
---|
| 29 | { |
---|
| 30 | { |
---|
| 31 | count: 31, |
---|
| 32 | segment: 0, |
---|
| 33 | offset: 0x00000000, |
---|
| 34 | size: RTEMS_FDISK_KBYTES (64) |
---|
| 35 | }, |
---|
| 36 | { |
---|
| 37 | count: 1, |
---|
| 38 | segment: 31, |
---|
| 39 | offset: 0x001f0000, |
---|
| 40 | size: RTEMS_FDISK_KBYTES (32) |
---|
| 41 | }, |
---|
| 42 | { |
---|
| 43 | count: 2, |
---|
| 44 | segment: 32, |
---|
| 45 | offset: 0x001f8000, |
---|
| 46 | size: RTEMS_FDISK_KBYTES (8) |
---|
| 47 | }, |
---|
| 48 | { |
---|
| 49 | count: 1, |
---|
| 50 | segment: 34, |
---|
| 51 | offset: 0x001fc000, |
---|
| 52 | size: RTEMS_FDISK_KBYTES (16) |
---|
| 53 | } |
---|
| 54 | }; |
---|
| 55 | |
---|
| 56 | /** |
---|
| 57 | * Boot blocks at the bottom. |
---|
| 58 | */ |
---|
| 59 | const rtems_fdisk_segment_desc rtems_am29lv160b_segments[] = |
---|
| 60 | { |
---|
| 61 | { |
---|
| 62 | count: 1, |
---|
| 63 | segment: 0, |
---|
| 64 | offset: 0x00000000, |
---|
| 65 | size: RTEMS_FDISK_KBYTES (16) |
---|
| 66 | }, |
---|
| 67 | { |
---|
| 68 | count: 2, |
---|
| 69 | segment: 1, |
---|
| 70 | offset: 0x00004000, |
---|
| 71 | size: RTEMS_FDISK_KBYTES (8) |
---|
| 72 | }, |
---|
| 73 | { |
---|
| 74 | count: 1, |
---|
| 75 | segment: 3, |
---|
| 76 | offset: 0x00008000, |
---|
| 77 | size: RTEMS_FDISK_KBYTES (32) |
---|
| 78 | }, |
---|
| 79 | { |
---|
| 80 | count: 31, |
---|
| 81 | segment: 4, |
---|
| 82 | offset: 0x00010000, |
---|
| 83 | size: RTEMS_FDISK_KBYTES (64) |
---|
| 84 | } |
---|
| 85 | }; |
---|
| 86 | |
---|
| 87 | static int |
---|
| 88 | rtems_am29lv160_blank (const rtems_fdisk_segment_desc* sd, |
---|
| 89 | uint32_t device, |
---|
| 90 | uint32_t segment, |
---|
| 91 | uint32_t offset, |
---|
| 92 | uint32_t size) |
---|
| 93 | { |
---|
| 94 | const rtems_am29lv160_config* ac = &rtems_am29lv160_configuration[device]; |
---|
| 95 | volatile uint8_t* seg_8 = ac->base; |
---|
| 96 | volatile uint32_t* seg_32; |
---|
| 97 | uint32_t count; |
---|
| 98 | |
---|
| 99 | offset += sd->offset + (segment - sd->segment) * sd->size; |
---|
| 100 | |
---|
| 101 | seg_8 += offset; |
---|
[3495c57] | 102 | |
---|
[f3bf21f2] | 103 | count = offset & (sizeof (uint32_t) - 1); |
---|
| 104 | size -= count; |
---|
| 105 | |
---|
| 106 | while (count--) |
---|
| 107 | if (*seg_8++ != 0xff) |
---|
| 108 | { |
---|
| 109 | #if AM26LV160_ERROR_TRACE |
---|
[a61cc6c] | 110 | printf ("AM26LV160: blank check error: %p = 0x%02x\n", |
---|
[f3bf21f2] | 111 | seg_8 - 1, *(seg_8 - 1)); |
---|
| 112 | #endif |
---|
| 113 | return EIO; |
---|
| 114 | } |
---|
[3495c57] | 115 | |
---|
[f3bf21f2] | 116 | seg_32 = (volatile uint32_t*) seg_8; |
---|
[3495c57] | 117 | |
---|
| 118 | count = size / sizeof (uint32_t); |
---|
[f3bf21f2] | 119 | size -= count * sizeof (uint32_t); |
---|
[3495c57] | 120 | |
---|
[f3bf21f2] | 121 | while (count--) |
---|
| 122 | if (*seg_32++ != 0xffffffff) |
---|
| 123 | { |
---|
| 124 | #if AM26LV160_ERROR_TRACE |
---|
[a61cc6c] | 125 | printf ("AM26LV160: blank check error: %p = 0x%08lx\n", |
---|
[f3bf21f2] | 126 | seg_32 - 1, *(seg_32 - 1)); |
---|
| 127 | #endif |
---|
| 128 | return EIO; |
---|
| 129 | } |
---|
[3495c57] | 130 | |
---|
[f3bf21f2] | 131 | seg_8 = (volatile uint8_t*) seg_32; |
---|
| 132 | |
---|
| 133 | while (size--) |
---|
| 134 | if (*seg_8++ != 0xff) |
---|
| 135 | { |
---|
| 136 | #if AM26LV160_ERROR_TRACE |
---|
[a61cc6c] | 137 | printf ("AM26LV160: blank check error: %p = 0x%02x\n", |
---|
[f3bf21f2] | 138 | seg_8 - 1, *(seg_8 - 1)); |
---|
| 139 | #endif |
---|
| 140 | return EIO; |
---|
| 141 | } |
---|
[3495c57] | 142 | |
---|
[f3bf21f2] | 143 | return 0; |
---|
| 144 | } |
---|
| 145 | |
---|
| 146 | static int |
---|
| 147 | rtems_am29lv160_verify (const rtems_fdisk_segment_desc* sd, |
---|
| 148 | uint32_t device, |
---|
| 149 | uint32_t segment, |
---|
| 150 | uint32_t offset, |
---|
| 151 | const void* buffer, |
---|
| 152 | uint32_t size) |
---|
| 153 | { |
---|
| 154 | const rtems_am29lv160_config* ac = &rtems_am29lv160_configuration[device]; |
---|
| 155 | const uint8_t* addr = ac->base; |
---|
[3495c57] | 156 | |
---|
[f3bf21f2] | 157 | addr += (sd->offset + (segment - sd->segment) * sd->size) + offset; |
---|
| 158 | |
---|
| 159 | if (memcmp (addr, buffer, size) != 0) |
---|
| 160 | return EIO; |
---|
| 161 | |
---|
| 162 | return 0; |
---|
| 163 | } |
---|
| 164 | |
---|
| 165 | static int |
---|
| 166 | rtems_am29lv160_toggle_wait_8 (volatile uint8_t* status) |
---|
| 167 | { |
---|
| 168 | while (1) |
---|
| 169 | { |
---|
| 170 | volatile uint8_t status1 = *status; |
---|
| 171 | volatile uint8_t status2 = *status; |
---|
| 172 | |
---|
| 173 | if (((status1 ^ status2) & (1 << 6)) == 0) |
---|
| 174 | return 0; |
---|
| 175 | |
---|
| 176 | if ((status1 & (1 << 5)) != 0) |
---|
| 177 | { |
---|
| 178 | status1 = *status; |
---|
| 179 | status2 = *status; |
---|
| 180 | |
---|
| 181 | if (((status1 ^ status2) & (1 << 6)) == 0) |
---|
| 182 | return 0; |
---|
[3495c57] | 183 | |
---|
[f3bf21f2] | 184 | #if AM26LV160_ERROR_TRACE |
---|
| 185 | printf ("AM26LV160: error bit detected: %p = 0x%04x\n", |
---|
| 186 | status, status1); |
---|
| 187 | #endif |
---|
| 188 | |
---|
| 189 | *status = 0xf0; |
---|
| 190 | return EIO; |
---|
| 191 | } |
---|
| 192 | } |
---|
| 193 | } |
---|
| 194 | |
---|
| 195 | static int |
---|
| 196 | rtems_am29lv160_toggle_wait_16 (volatile uint16_t* status) |
---|
| 197 | { |
---|
| 198 | while (1) |
---|
| 199 | { |
---|
| 200 | volatile uint16_t status1 = *status; |
---|
| 201 | volatile uint16_t status2 = *status; |
---|
| 202 | |
---|
| 203 | if (((status1 ^ status2) & (1 << 6)) == 0) |
---|
| 204 | return 0; |
---|
| 205 | |
---|
| 206 | if ((status1 & (1 << 5)) != 0) |
---|
| 207 | { |
---|
| 208 | status1 = *status; |
---|
| 209 | status2 = *status; |
---|
| 210 | |
---|
| 211 | if (((status1 ^ status2) & (1 << 6)) == 0) |
---|
| 212 | return 0; |
---|
| 213 | |
---|
| 214 | #if AM26LV160_ERROR_TRACE |
---|
| 215 | printf ("AM26LV160: error bit detected: %p = 0x%04x/0x%04x\n", |
---|
| 216 | status, status1, status2); |
---|
| 217 | #endif |
---|
| 218 | |
---|
| 219 | *status = 0xf0; |
---|
| 220 | return EIO; |
---|
| 221 | } |
---|
| 222 | } |
---|
| 223 | } |
---|
| 224 | |
---|
| 225 | static int |
---|
| 226 | rtems_am29lv160_write_data_8 (volatile uint8_t* base, |
---|
| 227 | uint32_t offset, |
---|
| 228 | const uint8_t* data, |
---|
| 229 | uint32_t size) |
---|
| 230 | { |
---|
| 231 | volatile uint8_t* seg = base + offset; |
---|
| 232 | rtems_interrupt_level level; |
---|
[3495c57] | 233 | |
---|
[f3bf21f2] | 234 | /* |
---|
| 235 | * Issue a reset. |
---|
| 236 | */ |
---|
| 237 | *base = 0xf0; |
---|
[3495c57] | 238 | |
---|
[f3bf21f2] | 239 | while (size) |
---|
| 240 | { |
---|
| 241 | rtems_interrupt_disable (level); |
---|
| 242 | *(base + 0xaaa) = 0xaa; |
---|
| 243 | *(base + 0x555) = 0x55; |
---|
| 244 | *(base + 0xaaa) = 0xa0; |
---|
[3495c57] | 245 | *seg = *data++; |
---|
[f3bf21f2] | 246 | rtems_interrupt_enable (level); |
---|
| 247 | if (rtems_am29lv160_toggle_wait_8 (seg++) != 0) |
---|
| 248 | return EIO; |
---|
| 249 | size--; |
---|
| 250 | } |
---|
| 251 | |
---|
| 252 | /* |
---|
| 253 | * Issue a reset. |
---|
| 254 | */ |
---|
| 255 | *base = 0xf0; |
---|
| 256 | |
---|
| 257 | return 0; |
---|
| 258 | } |
---|
| 259 | |
---|
| 260 | static int |
---|
| 261 | rtems_am29lv160_write_data_16 (volatile uint16_t* base, |
---|
| 262 | uint32_t offset, |
---|
| 263 | const uint16_t* data, |
---|
| 264 | uint32_t size) |
---|
| 265 | { |
---|
| 266 | volatile uint16_t* seg = base + (offset / 2); |
---|
| 267 | rtems_interrupt_level level; |
---|
| 268 | |
---|
| 269 | size /= 2; |
---|
[3495c57] | 270 | |
---|
[f3bf21f2] | 271 | /* |
---|
| 272 | * Issue a reset. |
---|
| 273 | */ |
---|
| 274 | *base = 0xf0; |
---|
[3495c57] | 275 | |
---|
[f3bf21f2] | 276 | while (size) |
---|
| 277 | { |
---|
| 278 | rtems_interrupt_disable (level); |
---|
| 279 | *(base + 0x555) = 0xaa; |
---|
| 280 | *(base + 0x2aa) = 0x55; |
---|
| 281 | *(base + 0x555) = 0xa0; |
---|
| 282 | *seg = *data++; |
---|
| 283 | rtems_interrupt_enable (level); |
---|
| 284 | if (rtems_am29lv160_toggle_wait_16 (seg++) != 0) |
---|
| 285 | return EIO; |
---|
| 286 | size--; |
---|
| 287 | } |
---|
| 288 | |
---|
| 289 | /* |
---|
| 290 | * Issue a reset. |
---|
| 291 | */ |
---|
[3495c57] | 292 | *base = 0xf0; |
---|
[f3bf21f2] | 293 | |
---|
| 294 | return 0; |
---|
| 295 | } |
---|
| 296 | |
---|
| 297 | static int |
---|
| 298 | rtems_am29lv160_read (const rtems_fdisk_segment_desc* sd, |
---|
| 299 | uint32_t device, |
---|
| 300 | uint32_t segment, |
---|
| 301 | uint32_t offset, |
---|
| 302 | void* buffer, |
---|
| 303 | uint32_t size) |
---|
| 304 | { |
---|
| 305 | unsigned char* addr = |
---|
| 306 | rtems_am29lv160_configuration[device].base + |
---|
| 307 | sd->offset + ((segment - sd->segment) * sd->size) + offset; |
---|
| 308 | memcpy (buffer, addr, size); |
---|
| 309 | return 0; |
---|
| 310 | } |
---|
| 311 | |
---|
| 312 | /* |
---|
| 313 | * @todo Fix the odd alignment and odd sizes. |
---|
| 314 | */ |
---|
| 315 | static int |
---|
| 316 | rtems_am29lv160_write (const rtems_fdisk_segment_desc* sd, |
---|
| 317 | uint32_t device, |
---|
| 318 | uint32_t segment, |
---|
| 319 | uint32_t offset, |
---|
| 320 | const void* buffer, |
---|
| 321 | uint32_t size) |
---|
| 322 | { |
---|
| 323 | int ret = rtems_am29lv160_verify (sd, device, segment, offset, buffer, size); |
---|
| 324 | |
---|
| 325 | if (ret != 0) |
---|
| 326 | { |
---|
| 327 | const rtems_am29lv160_config* ac = &rtems_am29lv160_configuration[device]; |
---|
| 328 | uint32_t soffset; |
---|
| 329 | |
---|
| 330 | soffset = offset + sd->offset + ((segment - sd->segment) * sd->size); |
---|
| 331 | |
---|
| 332 | if (offset & 1) |
---|
| 333 | printf ("rtems_am29lv160_write: offset is odd\n"); |
---|
| 334 | |
---|
| 335 | if (size & 1) |
---|
| 336 | printf ("rtems_am29lv160_write: size is odd\n"); |
---|
| 337 | |
---|
| 338 | if (ac->bus_8bit) |
---|
| 339 | ret = rtems_am29lv160_write_data_8 (ac->base, soffset, buffer, size); |
---|
| 340 | else |
---|
| 341 | ret = rtems_am29lv160_write_data_16 (ac->base, soffset, buffer, size); |
---|
| 342 | |
---|
| 343 | /* |
---|
| 344 | * Verify the write worked. |
---|
| 345 | */ |
---|
| 346 | if (ret == 0) |
---|
| 347 | { |
---|
| 348 | ret = rtems_am29lv160_verify (sd, device, segment, offset, buffer, size); |
---|
| 349 | #if AM26LV160_ERROR_TRACE |
---|
| 350 | if (ret) |
---|
| 351 | printf ("AM26LV160: verify failed: %ld-%ld-%08lx: s=%ld\n", |
---|
| 352 | device, segment, offset, size); |
---|
| 353 | #endif |
---|
| 354 | } |
---|
| 355 | } |
---|
| 356 | |
---|
| 357 | return ret; |
---|
| 358 | } |
---|
| 359 | |
---|
| 360 | static int |
---|
| 361 | rtems_am29lv160_erase (const rtems_fdisk_segment_desc* sd, |
---|
| 362 | uint32_t device, |
---|
| 363 | uint32_t segment) |
---|
| 364 | { |
---|
| 365 | int ret = rtems_am29lv160_blank (sd, device, segment, 0, sd->size); |
---|
| 366 | if (ret != 0) |
---|
| 367 | { |
---|
| 368 | const rtems_am29lv160_config* ac = &rtems_am29lv160_configuration[device]; |
---|
| 369 | uint32_t offset; |
---|
| 370 | rtems_interrupt_level level; |
---|
| 371 | |
---|
| 372 | offset = sd->offset + ((segment - sd->segment) * sd->size); |
---|
| 373 | |
---|
| 374 | if (ac->bus_8bit) |
---|
| 375 | { |
---|
| 376 | volatile uint8_t* base = ac->base; |
---|
| 377 | volatile uint8_t* seg = base + offset; |
---|
[3495c57] | 378 | |
---|
[f3bf21f2] | 379 | /* |
---|
| 380 | * Issue a reset. |
---|
| 381 | */ |
---|
| 382 | rtems_interrupt_disable (level); |
---|
[3495c57] | 383 | *base = 0xf0; |
---|
[f3bf21f2] | 384 | *(base + 0xaaa) = 0xaa; |
---|
| 385 | *(base + 0x555) = 0x55; |
---|
| 386 | *(base + 0xaaa) = 0x80; |
---|
| 387 | *(base + 0xaaa) = 0xaa; |
---|
| 388 | *(base + 0x555) = 0x55; |
---|
| 389 | *seg = 0x30; |
---|
| 390 | rtems_interrupt_enable (level); |
---|
[3495c57] | 391 | |
---|
[f3bf21f2] | 392 | ret = rtems_am29lv160_toggle_wait_8 (seg); |
---|
| 393 | |
---|
| 394 | /* |
---|
| 395 | * Issue a reset. |
---|
| 396 | */ |
---|
| 397 | *base = 0xf0; |
---|
| 398 | } |
---|
| 399 | else |
---|
| 400 | { |
---|
| 401 | volatile uint16_t* base = ac->base; |
---|
| 402 | volatile uint16_t* seg = base + (offset / 2); |
---|
| 403 | |
---|
| 404 | /* |
---|
| 405 | * Issue a reset. |
---|
| 406 | */ |
---|
| 407 | rtems_interrupt_disable (level); |
---|
| 408 | *base = 0xf0; |
---|
| 409 | *(base + 0x555) = 0xaa; |
---|
| 410 | *(base + 0x2aa) = 0x55; |
---|
| 411 | *(base + 0x555) = 0x80; |
---|
| 412 | *(base + 0x555) = 0xaa; |
---|
| 413 | *(base + 0x2aa) = 0x55; |
---|
| 414 | *seg = 0x30; |
---|
| 415 | rtems_interrupt_enable (level); |
---|
[3495c57] | 416 | |
---|
[f3bf21f2] | 417 | ret = rtems_am29lv160_toggle_wait_16 (seg); |
---|
| 418 | |
---|
| 419 | /* |
---|
| 420 | * Issue a reset. |
---|
| 421 | */ |
---|
| 422 | *base = 0xf0; |
---|
| 423 | } |
---|
| 424 | |
---|
| 425 | /* |
---|
| 426 | * Check the erase worked. |
---|
| 427 | */ |
---|
| 428 | if (ret == 0) |
---|
| 429 | { |
---|
| 430 | ret = rtems_am29lv160_blank (sd, device, segment, 0, sd->size); |
---|
| 431 | #if AM26LV160_ERROR_TRACE |
---|
| 432 | if (ret) |
---|
| 433 | printf ("AM26LV160: erase failed: %ld-%ld\n", device, segment); |
---|
| 434 | #endif |
---|
| 435 | } |
---|
| 436 | } |
---|
| 437 | |
---|
| 438 | return ret; |
---|
| 439 | } |
---|
| 440 | |
---|
| 441 | static int |
---|
| 442 | rtems_am29lv160_erase_device (const rtems_fdisk_device_desc* dd, |
---|
| 443 | uint32_t device) |
---|
| 444 | { |
---|
| 445 | uint32_t segment; |
---|
| 446 | |
---|
| 447 | for (segment = 0; segment < dd->segment_count; segment++) |
---|
| 448 | { |
---|
| 449 | uint32_t seg_segment; |
---|
[3495c57] | 450 | |
---|
[f3bf21f2] | 451 | for (seg_segment = 0; |
---|
| 452 | seg_segment < dd->segments[segment].count; |
---|
| 453 | seg_segment++) |
---|
| 454 | { |
---|
| 455 | int ret = rtems_am29lv160_erase (&dd->segments[segment], |
---|
| 456 | device, |
---|
| 457 | segment + seg_segment); |
---|
| 458 | if (ret) |
---|
| 459 | return ret; |
---|
| 460 | } |
---|
| 461 | } |
---|
[3495c57] | 462 | |
---|
[f3bf21f2] | 463 | return 0; |
---|
| 464 | } |
---|
| 465 | |
---|
| 466 | const rtems_fdisk_driver_handlers rtems_am29lv160_handlers = |
---|
| 467 | { |
---|
| 468 | read: rtems_am29lv160_read, |
---|
| 469 | write: rtems_am29lv160_write, |
---|
| 470 | blank: rtems_am29lv160_blank, |
---|
| 471 | verify: rtems_am29lv160_verify, |
---|
| 472 | erase: rtems_am29lv160_erase, |
---|
| 473 | erase_device: rtems_am29lv160_erase_device |
---|
| 474 | }; |
---|