aboutsummaryrefslogtreecommitdiff
path: root/sim/ppc/hw_eeprom.c
diff options
context:
space:
mode:
Diffstat (limited to 'sim/ppc/hw_eeprom.c')
-rw-r--r--sim/ppc/hw_eeprom.c289
1 files changed, 289 insertions, 0 deletions
diff --git a/sim/ppc/hw_eeprom.c b/sim/ppc/hw_eeprom.c
new file mode 100644
index 0000000..8a52e9d
--- /dev/null
+++ b/sim/ppc/hw_eeprom.c
@@ -0,0 +1,289 @@
+/* This file is part of the program psim.
+
+ Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+
+#ifndef _HW_EEPROM_C_
+#define _HW_EEPROM_C_
+
+#ifndef STATIC_INLINE_HW_EEPROM
+#define STATIC_INLINE_HW_EEPROM STATIC_INLINE
+#endif
+
+#include "device_table.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+
+/* EEPROM - electricaly erasable programable memory
+
+ Description:
+
+ This device implements a small byte addressable EEPROM.
+ Programming is performed using the same write sequences as used by
+ modern EEPROM components. Writes occure in real time, the device
+ returning a progress value until the programing has been completed.
+
+ Properties:
+
+ reg = <address> <size>. Determine where the device lives in the
+ parents address space.
+
+ nr-sectors = <integer>. When erasing an entire sector is cleared
+ at a time. This specifies the number of sectors in the EEPROM
+ component.
+
+ byte-write-delay = <integer>. Number of clock ticks before the
+ programming of a single byte completes.
+
+ sector-start-delay = <integer>. When erasing sectors, the number
+ of clock ticks after the sector has been specified and the actual
+ erase process commences.
+
+ erase-delay = <intger>. Number of clock ticks before an erase
+ program completes. */
+
+typedef enum {
+ read_reset,
+ write_nr_2,
+ write_nr_3,
+ write_nr_4,
+ write_nr_5,
+ write_nr_6,
+ byte_program,
+ byte_programming,
+ chip_erase, chip_erasing,
+ sector_erase, sector_erasing,
+ sector_erase_suspend,
+ sector_erase_resume,
+} eeprom_states;
+
+typedef struct _eeprom_device {
+ unsigned8 *memory;
+ unsigned sizeof_memory;
+ unsigned sector_size;
+ unsigned nr_sectors;
+ unsigned byte_write_delay;
+ unsigned sector_start_delay;
+ unsigned erase_delay;
+ signed64 programme_start_time;
+ unsigned program_byte_address;
+ eeprom_states state;
+} eeprom_device;
+
+static void *
+eeprom_create(const char *name,
+ const device_unit *unit_address,
+ const char *args,
+ device *parent)
+{
+ eeprom_device *eeprom = ZALLOC(eeprom_device);
+ return eeprom;
+}
+
+typedef struct _eeprom_reg_spec {
+ unsigned32 base;
+ unsigned32 size;
+} eeprom_reg_spec;
+
+static void
+eeprom_init_address(device *me,
+ psim *system)
+{
+ eeprom_device *eeprom = (eeprom_device*)device_data(me);
+ const device_property *reg = device_find_array_property(me, "reg");
+ const eeprom_reg_spec *spec = reg->array;
+ int nr_entries = reg->sizeof_array / sizeof(*spec);
+
+ if ((reg->sizeof_array % sizeof(*spec)) != 0)
+ error("devices/%s reg property of incorrect size\n", device_name(me));
+ if (nr_entries > 1)
+ error("devices/%s reg property contains multiple specs\n",
+ device_name(me));
+
+ /* initialize the eeprom */
+ if (eeprom->memory == NULL) {
+ eeprom->sizeof_memory = BE2H_4(spec->size);
+ eeprom->memory = zalloc(eeprom->sizeof_memory);
+ }
+ else
+ memset(eeprom->memory, eeprom->sizeof_memory, 0);
+
+ /* figure out the sectors in the eeprom */
+ eeprom->nr_sectors = device_find_integer_property(me, "nr-sectors");
+ eeprom->sector_size = eeprom->sizeof_memory / eeprom->nr_sectors;
+ if (eeprom->sector_size * eeprom->nr_sectors != eeprom->sizeof_memory)
+ error("device/%s nr-sectors does not evenly divide eeprom\n",
+ device_name(me));
+
+ /* timing */
+ eeprom->byte_write_delay = device_find_integer_property(me, "byte-write-delay");
+ eeprom->sector_start_delay = device_find_integer_property(me, "sector-start-delay");
+ eeprom->erase_delay = device_find_integer_property(me, "erase-delay");
+
+ device_attach_address(device_parent(me),
+ device_name(me),
+ attach_callback,
+ 0 /*address space*/,
+ BE2H_4(spec->base),
+ eeprom->sizeof_memory,
+ access_read_write_exec,
+ me);
+}
+
+
+static unsigned
+eeprom_io_read_buffer(device *me,
+ void *dest,
+ int space,
+ unsigned_word addr,
+ unsigned nr_bytes,
+ cpu *processor,
+ unsigned_word cia)
+{
+ eeprom_device *eeprom = (eeprom_device*)device_data(me);
+ int i;
+ for (i = 0; i < nr_bytes; i++) {
+ unsigned_word address = (addr + nr_bytes) % eeprom->sizeof_memory;
+ eeprom->memory[address] = eeprom_io_read_byte(address);
+ }
+ return nr_bytes;
+}
+
+static void
+eeprom_io_write_byte()
+{
+ switch (state) {
+ case read_reset:
+ if (address == 0x5555 && data = 0xaa)
+ state = first_write;
+ else
+ state = read_reset;
+ break;
+ case first_write:
+ if (address == 0x2aaa && data == 0x55)
+ state = second_write;
+ else
+ state = read_reset; /* issue warning */
+ break;
+ case second_write:
+ if (address == 0x5555 && data == 0xf0)
+ state = read_reset;
+ else if (address == 0x5555 && data == 0x90)
+ state = auto_select;
+ else if (address == 0x5555 && data == 0xa0)
+ state = byte_program;
+ else if (address == 0x5555 && data == 0x80)
+ state = third_write;
+ else
+ state = read_reset;
+ break;
+ case fourth_write:
+ if (address == 0x5555 && data == 0xaa)
+ state = fith_write;
+ else
+ state = read_reset;
+ break;
+ case fith_write:
+ if (address == 0x2aaa && data == 0x55)
+ state = sixth_write;
+ else
+ state = read_reset;
+ break;
+ case sixth_write:
+ if (address == 0x5555 && data == 0x10)
+ state = chip_erase;
+ else
+ sector_erase();
+ break;
+ case auto_select:
+ if (data == 0xf0)
+ state = read_reset;
+ else if (address == 0x5555 && data == 0xaa)
+ state = second_write;
+ else
+ state = read_reset; /* issue warning */
+ break;
+ case sector_erase:
+ if (data == 0xb0)
+ state = sector_erase_suspend;
+ else
+ state = sector_erase; /* ignore */
+ break;
+ case sector_erase_suspend:
+ if (data == 0x30)
+ state = sector_erase;
+ else
+ state = sector_erase_suspend; /* ignore */
+ break;
+ case byte_program:
+ /* perform the byte program */
+ program_address = address;
+ program_start = some_time();
+ toggle = 0;
+ /* but only make things `0' and never 1 */
+ byte[address] = data;
+ state = byte_programming;
+ break;
+ case byte_programming:
+ if (finished)
+ state = read_reset;
+ else
+ state = byte_programming;
+ break;
+ }
+}
+
+static unsigned
+eeprom_io_write_buffer(device *me,
+ const void *source,
+ int space,
+ unsigned_word addr,
+ unsigned nr_bytes,
+ cpu *processor,
+ unsigned_word cia)
+{
+ eeprom_device *eeprom = (eeprom_device*)device_data(me);
+ int i;
+ for (i = 0; i < nr_bytes; i++) {
+ unsigned_word address = (addr + nr_bytes) % eeprom->sizeof_memory;
+ eeprom_io_read_byte(address, eeprom->memory[address]);
+ }
+ return nr_bytes;
+}
+
+
+
+static device_callbacks const eeprom_callbacks = {
+ { eeprom_init_address, },
+ { NULL, }, /* address */
+ { eeprom_io_read_buffer, eeprom_io_write_buffer }, /* IO */
+};
+
+const device_descriptor eeprom_device_descriptor[] = {
+ { "eeprom", eeprom_create, &eeprom_callbacks },
+ { NULL },
+};
+
+#endif /* _HW_EEPROM_C_ */