summaryrefslogtreecommitdiffstats
path: root/c/src/libchip
diff options
context:
space:
mode:
authorChris Johns <chrisj@rtems.org>2007-12-14 04:54:57 +0000
committerChris Johns <chrisj@rtems.org>2007-12-14 04:54:57 +0000
commitf3bf21f2cf21ec70437cfbff12840148ce6a4034 (patch)
tree2e80ceb5ef6fae5193a1879621a414b428747410 /c/src/libchip
parent2007-12-14 Chris Johns <chrisj@rtems.org> (diff)
downloadrtems-f3bf21f2cf21ec70437cfbff12840148ce6a4034.tar.bz2
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.
Diffstat (limited to 'c/src/libchip')
-rw-r--r--c/src/libchip/Makefile.am9
-rw-r--r--c/src/libchip/flash/am29lv160.c472
-rw-r--r--c/src/libchip/flash/am29lv160.h57
-rw-r--r--c/src/libchip/preinstall.am5
4 files changed, 543 insertions, 0 deletions
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