diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-03 07:20:11 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-04 10:13:28 +0200 |
commit | 27de4e1fb8bcdbdd8cb882fc0d7a2c152b4e027a (patch) | |
tree | def0664dcddc53fd5d599b455c64f76ca2293606 /bsps/shared/dev/flash | |
parent | bsps: Move config macros to RTEMS_BSP_CONFIGURE (diff) | |
download | rtems-27de4e1fb8bcdbdd8cb882fc0d7a2c152b4e027a.tar.bz2 |
bsps: Move libchip to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps/shared/dev/flash')
-rw-r--r-- | bsps/shared/dev/flash/am29lv160.c | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/bsps/shared/dev/flash/am29lv160.c b/bsps/shared/dev/flash/am29lv160.c new file mode 100644 index 0000000000..5cfaae4f24 --- /dev/null +++ b/bsps/shared/dev/flash/am29lv160.c @@ -0,0 +1,473 @@ +/* + * RTEMS Project (http://www.rtems.org/) + * + * Copyright 2007 Chris Johns (chrisj@rtems.org) + */ +/** + * Provide flash support for the AM26LV160 device. + * + * The M29W160D is the same device. + */ + +#include <stdio.h> +#include <errno.h> +#include <string.h> + +#include <rtems.h> + +#include <libchip/am29lv160.h> + +#ifndef AM26LV160_ERROR_TRACE +#define AM26LV160_ERROR_TRACE (0) +#endif + +/** + * Boot blocks at the top + */ +const rtems_fdisk_segment_desc rtems_am29lv160t_segments[4] = +{ + { + .count = 31, + .segment = 0, + .offset = 0x00000000, + .size = RTEMS_FDISK_KBYTES (64) + }, + { + .count = 1, + .segment = 31, + .offset = 0x001f0000, + .size = RTEMS_FDISK_KBYTES (32) + }, + { + .count = 2, + .segment = 32, + .offset = 0x001f8000, + .size = RTEMS_FDISK_KBYTES (8) + }, + { + .count = 1, + .segment = 34, + .offset = 0x001fc000, + .size = RTEMS_FDISK_KBYTES (16) + } +}; + +/** + * Boot blocks at the bottom. + */ +const rtems_fdisk_segment_desc rtems_am29lv160b_segments[] = +{ + { + .count = 1, + .segment = 0, + .offset = 0x00000000, + .size = RTEMS_FDISK_KBYTES (16) + }, + { + . count = 2, + .segment = 1, + .offset = 0x00004000, + .size = RTEMS_FDISK_KBYTES (8) + }, + { + .count = 1, + .segment = 3, + .offset = 0x00008000, + .size = RTEMS_FDISK_KBYTES (32) + }, + { + .count = 31, + .segment = 4, + .offset = 0x00010000, + .size = RTEMS_FDISK_KBYTES (64) + } +}; + +static int +rtems_am29lv160_blank (const rtems_fdisk_segment_desc* sd, + uint32_t device, + uint32_t segment, + uint32_t offset, + uint32_t size) +{ + const rtems_am29lv160_config* ac = &rtems_am29lv160_configuration[device]; + volatile uint8_t* seg_8 = ac->base; + volatile uint32_t* seg_32; + uint32_t count; + + offset += sd->offset + (segment - sd->segment) * sd->size; + + seg_8 += offset; + + count = offset & (sizeof (uint32_t) - 1); + size -= count; + + while (count--) + if (*seg_8++ != 0xff) + { +#if AM26LV160_ERROR_TRACE + printf ("AM26LV160: blank check error: %p = 0x%02x\n", + seg_8 - 1, *(seg_8 - 1)); +#endif + return EIO; + } + + seg_32 = (volatile uint32_t*) seg_8; + + count = size / sizeof (uint32_t); + size -= count * sizeof (uint32_t); + + while (count--) + if (*seg_32++ != 0xffffffff) + { +#if AM26LV160_ERROR_TRACE + printf ("AM26LV160: blank check error: %p = 0x%08lx\n", + seg_32 - 1, *(seg_32 - 1)); +#endif + return EIO; + } + + seg_8 = (volatile uint8_t*) seg_32; + + while (size--) + if (*seg_8++ != 0xff) + { +#if AM26LV160_ERROR_TRACE + printf ("AM26LV160: blank check error: %p = 0x%02x\n", + seg_8 - 1, *(seg_8 - 1)); +#endif + return EIO; + } + + return 0; +} + +static int +rtems_am29lv160_verify (const rtems_fdisk_segment_desc* sd, + uint32_t device, + uint32_t segment, + uint32_t offset, + const void* buffer, + uint32_t size) +{ + const rtems_am29lv160_config* ac = &rtems_am29lv160_configuration[device]; + const uint8_t* addr = ac->base; + + addr += (sd->offset + (segment - sd->segment) * sd->size) + offset; + + if (memcmp (addr, buffer, size) != 0) + return EIO; + + return 0; +} + +static int +rtems_am29lv160_toggle_wait_8 (volatile uint8_t* status) +{ + while (1) + { + volatile uint8_t status1 = *status; + volatile uint8_t status2 = *status; + + if (((status1 ^ status2) & (1 << 6)) == 0) + return 0; + + if ((status1 & (1 << 5)) != 0) + { + status1 = *status; + status2 = *status; + + if (((status1 ^ status2) & (1 << 6)) == 0) + return 0; + +#if AM26LV160_ERROR_TRACE + printf ("AM26LV160: error bit detected: %p = 0x%04x\n", + status, status1); +#endif + + *status = 0xf0; + return EIO; + } + } +} + +static int +rtems_am29lv160_toggle_wait_16 (volatile uint16_t* status) +{ + while (1) + { + volatile uint16_t status1 = *status; + volatile uint16_t status2 = *status; + + if (((status1 ^ status2) & (1 << 6)) == 0) + return 0; + + if ((status1 & (1 << 5)) != 0) + { + status1 = *status; + status2 = *status; + + if (((status1 ^ status2) & (1 << 6)) == 0) + return 0; + +#if AM26LV160_ERROR_TRACE + printf ("AM26LV160: error bit detected: %p = 0x%04x/0x%04x\n", + status, status1, status2); +#endif + + *status = 0xf0; + return EIO; + } + } +} + +static int +rtems_am29lv160_write_data_8 (volatile uint8_t* base, + uint32_t offset, + const uint8_t* data, + uint32_t size) +{ + volatile uint8_t* seg = base + offset; + rtems_interrupt_level level; + + /* + * Issue a reset. + */ + *base = 0xf0; + + while (size) + { + rtems_interrupt_disable (level); + *(base + 0xaaa) = 0xaa; + *(base + 0x555) = 0x55; + *(base + 0xaaa) = 0xa0; + *seg = *data++; + rtems_interrupt_enable (level); + if (rtems_am29lv160_toggle_wait_8 (seg++) != 0) + return EIO; + size--; + } + + /* + * Issue a reset. + */ + *base = 0xf0; + + return 0; +} + +static int +rtems_am29lv160_write_data_16 (volatile uint16_t* base, + uint32_t offset, + const uint16_t* data, + uint32_t size) +{ + volatile uint16_t* seg = base + (offset / 2); + rtems_interrupt_level level; + + size /= 2; + + /* + * Issue a reset. + */ + *base = 0xf0; + + while (size) + { + rtems_interrupt_disable (level); + *(base + 0x555) = 0xaa; + *(base + 0x2aa) = 0x55; + *(base + 0x555) = 0xa0; + *seg = *data++; + rtems_interrupt_enable (level); + if (rtems_am29lv160_toggle_wait_16 (seg++) != 0) + return EIO; + size--; + } + + /* + * Issue a reset. + */ + *base = 0xf0; + + return 0; +} + +static int +rtems_am29lv160_read (const rtems_fdisk_segment_desc* sd, + uint32_t device, + uint32_t segment, + uint32_t offset, + void* buffer, + uint32_t size) +{ + unsigned char* addr = + rtems_am29lv160_configuration[device].base + + sd->offset + ((segment - sd->segment) * sd->size) + offset; + memcpy (buffer, addr, size); + return 0; +} + +/* + * @todo Fix the odd alignment and odd sizes. + */ +static int +rtems_am29lv160_write (const rtems_fdisk_segment_desc* sd, + uint32_t device, + uint32_t segment, + uint32_t offset, + const void* buffer, + uint32_t size) +{ + int ret = rtems_am29lv160_verify (sd, device, segment, offset, buffer, size); + + if (ret != 0) + { + const rtems_am29lv160_config* ac = &rtems_am29lv160_configuration[device]; + uint32_t soffset; + + soffset = offset + sd->offset + ((segment - sd->segment) * sd->size); + + if (offset & 1) + printf ("rtems_am29lv160_write: offset is odd\n"); + + if (size & 1) + printf ("rtems_am29lv160_write: size is odd\n"); + + if (ac->bus_8bit) + ret = rtems_am29lv160_write_data_8 (ac->base, soffset, buffer, size); + else + ret = rtems_am29lv160_write_data_16 (ac->base, soffset, buffer, size); + + /* + * Verify the write worked. + */ + if (ret == 0) + { + ret = rtems_am29lv160_verify (sd, device, segment, offset, buffer, size); +#if AM26LV160_ERROR_TRACE + if (ret) + printf ("AM26LV160: verify failed: %ld-%ld-%08lx: s=%ld\n", + device, segment, offset, size); +#endif + } + } + + return ret; +} + +static int +rtems_am29lv160_erase (const rtems_fdisk_segment_desc* sd, + uint32_t device, + uint32_t segment) +{ + int ret = rtems_am29lv160_blank (sd, device, segment, 0, sd->size); + if (ret != 0) + { + const rtems_am29lv160_config* ac = &rtems_am29lv160_configuration[device]; + uint32_t offset; + rtems_interrupt_level level; + + offset = sd->offset + ((segment - sd->segment) * sd->size); + + if (ac->bus_8bit) + { + volatile uint8_t* base = ac->base; + volatile uint8_t* seg = base + offset; + + /* + * Issue a reset. + */ + rtems_interrupt_disable (level); + *base = 0xf0; + *(base + 0xaaa) = 0xaa; + *(base + 0x555) = 0x55; + *(base + 0xaaa) = 0x80; + *(base + 0xaaa) = 0xaa; + *(base + 0x555) = 0x55; + *seg = 0x30; + rtems_interrupt_enable (level); + + ret = rtems_am29lv160_toggle_wait_8 (seg); + + /* + * Issue a reset. + */ + *base = 0xf0; + } + else + { + volatile uint16_t* base = ac->base; + volatile uint16_t* seg = base + (offset / 2); + + /* + * Issue a reset. + */ + rtems_interrupt_disable (level); + *base = 0xf0; + *(base + 0x555) = 0xaa; + *(base + 0x2aa) = 0x55; + *(base + 0x555) = 0x80; + *(base + 0x555) = 0xaa; + *(base + 0x2aa) = 0x55; + *seg = 0x30; + rtems_interrupt_enable (level); + + ret = rtems_am29lv160_toggle_wait_16 (seg); + + /* + * Issue a reset. + */ + *base = 0xf0; + } + + /* + * Check the erase worked. + */ + if (ret == 0) + { + ret = rtems_am29lv160_blank (sd, device, segment, 0, sd->size); +#if AM26LV160_ERROR_TRACE + if (ret) + printf ("AM26LV160: erase failed: %ld-%ld\n", device, segment); +#endif + } + } + + return ret; +} + +static int +rtems_am29lv160_erase_device (const rtems_fdisk_device_desc* dd, + uint32_t device) +{ + uint32_t segment; + + for (segment = 0; segment < dd->segment_count; segment++) + { + uint32_t seg_segment; + + for (seg_segment = 0; + seg_segment < dd->segments[segment].count; + seg_segment++) + { + int ret = rtems_am29lv160_erase (&dd->segments[segment], + device, + segment + seg_segment); + if (ret) + return ret; + } + } + + return 0; +} + +const rtems_fdisk_driver_handlers rtems_am29lv160_handlers = +{ + .read = rtems_am29lv160_read, + .write = rtems_am29lv160_write, + .blank = rtems_am29lv160_blank, + .verify = rtems_am29lv160_verify, + .erase = rtems_am29lv160_erase, + .erase_device = rtems_am29lv160_erase_device +}; |