diff options
Diffstat (limited to 'sim/ppc/hw_eeprom.c')
-rw-r--r-- | sim/ppc/hw_eeprom.c | 289 |
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_ */ |