diff options
-rw-r--r-- | c/src/ChangeLog | 7 | ||||
-rw-r--r-- | c/src/libchip/Makefile.am | 9 | ||||
-rw-r--r-- | c/src/libchip/flash/am29lv160.c | 472 | ||||
-rw-r--r-- | c/src/libchip/flash/am29lv160.h | 57 | ||||
-rw-r--r-- | c/src/libchip/preinstall.am | 5 | ||||
-rw-r--r-- | c/src/wrapup/Makefile.am | 1 |
6 files changed, 551 insertions, 0 deletions
diff --git a/c/src/ChangeLog b/c/src/ChangeLog index 26f4de6132..6d89344055 100644 --- a/c/src/ChangeLog +++ b/c/src/ChangeLog @@ -1,3 +1,10 @@ +2007-12-14 Chris Johns <chrisj@rtems.org> + + * wrapup/Makefile.am: Add libflash.a to the libbsp library. + * libchip/Makefile.am, libchip/preinstall.am: Add the flash chip support. + * libchip/flash, libchip/flash/am29lv160.c, + libchip/flash/am29lv160.h: New. + 2007-11-26 Joel Sherrill <joel.sherrill@oarcorp.com> * libchip/network/if_fxp.c: Spacing. diff --git a/c/src/libchip/Makefile.am b/c/src/libchip/Makefile.am index 5cfca33da7..243c87d865 100644 --- a/c/src/libchip/Makefile.am +++ b/c/src/libchip/Makefile.am @@ -13,6 +13,15 @@ EXTRA_DIST = noinst_LIBRARIES = noinst_PROGRAMS = +# flash +if LIBCHIP +include_libchip_HEADERS += flash/am29lv160.h + +noinst_LIBRARIES += libflash.a +libflash_a_SOURCES = flash/am29lv160.c +libflash_a_CPPFLAGS = $(AM_CPPFLAGS) +endif + # ide if LIBCHIP include_libchip_HEADERS += ide/ata.h ide/ide_ctrl_cfg.h ide/ide_ctrl.h \ diff --git a/c/src/libchip/flash/am29lv160.c b/c/src/libchip/flash/am29lv160.c new file mode 100644 index 0000000000..dfeb4a2776 --- /dev/null +++ b/c/src/libchip/flash/am29lv160.c @@ -0,0 +1,472 @@ +/* + * $Id$ + * + * 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 <rtems.h> + +#include <libchip/am29lv160.h> + +#define AM26LV160_ERROR_TRACE (0) + +/** + * 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: %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: %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: %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 +}; diff --git a/c/src/libchip/flash/am29lv160.h b/c/src/libchip/flash/am29lv160.h new file mode 100644 index 0000000000..65ffd37d91 --- /dev/null +++ b/c/src/libchip/flash/am29lv160.h @@ -0,0 +1,57 @@ +/* + * $Id$ + * + * RTEMS Project (http://www.rtems.org/) + * + * Copyright 2007 Chris Johns (chrisj@rtems.org) + */ + +/** + * Flash Disk Device Driver. + * + * Am29LV160D 16 Megabit (2M x 8bit) 3.0 Volt-only + * Boot Sctor Flash Memory. + */ + +#if !defined (_RTEMS_AM29LV160_H_) +#define _RTEMS_AM29LV160_H_ + +#include <rtems/flashdisk.h> + +/** + * The segments in the AM29LV160 top boot block device. + */ +#define rtems_am29lv160t_segment_count (4) +extern const rtems_fdisk_segment_desc rtems_am29lv160t_segments[4]; + +/** + * The segments in the AM29LV160 bottom boot block device. + */ +#define rtems_am29lv160b_segment_count (4) +extern const rtems_fdisk_segment_desc rtems_am29lv160b_segments[4]; + +/** + * The segments in the AM29LV160 top boot block device. + */ +extern const rtems_fdisk_driver_handlers rtems_am29lv160_handlers; + +/** + * The device configuration. + */ +typedef struct rtems_am29lv160_config +{ + int bus_8bit; + void* base; +} rtems_am29lv160_config; + +/** + * External reference to the configuration. + */ +extern const rtems_am29lv160_config rtems_am29lv160_configuration[]; + +/** + * External reference to the configuration size + */ +extern uint32_t rtems_am29lv160_configuration_size; + +#endif diff --git a/c/src/libchip/preinstall.am b/c/src/libchip/preinstall.am index d24155a266..d12e43e345 100644 --- a/c/src/libchip/preinstall.am +++ b/c/src/libchip/preinstall.am @@ -35,6 +35,11 @@ $(PROJECT_INCLUDE)/libchip/$(dirstamp): PREINSTALL_DIRS += $(PROJECT_INCLUDE)/libchip/$(dirstamp) endif if LIBCHIP +$(PROJECT_INCLUDE)/libchip/am29lv160.h: flash/am29lv160.h $(PROJECT_INCLUDE)/libchip/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/libchip/am29lv160.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/libchip/am29lv160.h +endif +if LIBCHIP $(PROJECT_INCLUDE)/libchip/ata.h: ide/ata.h $(PROJECT_INCLUDE)/libchip/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/libchip/ata.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/libchip/ata.h diff --git a/c/src/wrapup/Makefile.am b/c/src/wrapup/Makefile.am index ccc8118819..474b417f38 100644 --- a/c/src/wrapup/Makefile.am +++ b/c/src/wrapup/Makefile.am @@ -20,6 +20,7 @@ endif # SRCS += $(wildcard $(PROJECT_LIB)/libcpu.a) if LIBCHIP +SRCS += ../libchip/libflash.a SRCS += ../libchip/librtcio.a SRCS += ../libchip/libserialio.a SRCS += ../libchip/libide.a |