binutils-gdb/sim/ppc/hw_eeprom.c

290 lines
7.6 KiB
C

/* 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_ */