11 changed files with 4496 additions and 0 deletions
File diff suppressed because it is too large
@ -0,0 +1,23 @@ |
|||
static unsigned |
|||
hw_com_io_write_buffer_callback(device *me, |
|||
const void *source, |
|||
int space, |
|||
unsigned_word addr, |
|||
unsigned nr_bytes, |
|||
cpu *processor, |
|||
unsigned_word cia) |
|||
{ |
|||
hw_com_device *hw_com = (hw_com_device*)device_data(me); |
|||
unsigned_1 val = *(unsigned_1*)source; |
|||
|
|||
switch ((int)addr & hw_com_offset_mask) { |
|||
|
|||
default: |
|||
error("hw_com_write_callback() internal error\n"); |
|||
|
|||
} |
|||
|
|||
return nr_bytes; |
|||
} |
|||
|
|||
#endif /* _HW_COM_ */ |
|||
@ -0,0 +1,145 @@ |
|||
/* 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_CPU_C_ |
|||
#define _HW_CPU_C_ |
|||
|
|||
#ifndef STATIC_INLINE_HW_CPU |
|||
#define STATIC_INLINE_HW_CPU STATIC_INLINE |
|||
#endif |
|||
|
|||
#include "device_table.h" |
|||
#include "hw_cpu.h" |
|||
|
|||
#include "interrupts.h" |
|||
#include "cpu.h" |
|||
|
|||
|
|||
/* CPU (HW) - Interface to a Processor
|
|||
|
|||
Description: |
|||
|
|||
The CPU device provides the connection between the interrupt net |
|||
(linking the devices and the interrupt controller) and the |
|||
simulated model of each processor. This device contains interrupt |
|||
ports that correspond directly to the external interrupt stimulus |
|||
that can be sent to a given processor. Sending an interrupt to one |
|||
of the ports results in an interrupt being delivered to the |
|||
corresponding processor. |
|||
|
|||
Typically, an interrupt controller would have its inputs connected |
|||
to device interrupt sources and its outputs (sreset, int, et.al.) |
|||
connected to this device. |
|||
|
|||
Properties: |
|||
|
|||
cpu-nr = <integer>. Specify the processor (1..N) that this cpu |
|||
device node should control. */ |
|||
|
|||
typedef struct _hw_cpu_device { |
|||
int cpu_nr; |
|||
cpu *processor; |
|||
} hw_cpu_device; |
|||
|
|||
static const device_interrupt_port_descriptor hw_cpu_interrupt_ports[] = { |
|||
{ "hreset", hw_cpu_hard_reset }, |
|||
{ "sreset", hw_cpu_soft_reset }, |
|||
{ "int", hw_cpu_external_interrupt }, |
|||
{ "mci", hw_cpu_machine_check_interrupt }, |
|||
{ "smi", hw_cpu_system_management_interrupt }, |
|||
{ NULL } |
|||
}; |
|||
|
|||
|
|||
static void * |
|||
hw_cpu_create(const char *name, |
|||
const device_unit *unit_address, |
|||
const char *args, |
|||
device *parent) |
|||
{ |
|||
hw_cpu_device *hw_cpu = ZALLOC(hw_cpu_device); |
|||
return hw_cpu; |
|||
} |
|||
|
|||
|
|||
/* during address initialization ensure that any missing cpu
|
|||
properties are added to this devices node */ |
|||
|
|||
static void |
|||
hw_cpu_init_address(device *me) |
|||
{ |
|||
hw_cpu_device *hw_cpu = (hw_cpu_device*)device_data(me); |
|||
/* populate the node with properties */ |
|||
/* clear our data */ |
|||
memset(hw_cpu, 0x0, sizeof(hw_cpu_device)); |
|||
hw_cpu->cpu_nr = device_find_integer_property(me, "cpu-nr"); |
|||
hw_cpu->processor = psim_cpu(device_system(me), hw_cpu->cpu_nr); |
|||
} |
|||
|
|||
|
|||
/* Take the interrupt and synchronize its delivery with the clock. If
|
|||
we've not yet scheduled an interrupt for the next clock tick, take |
|||
the oportunity to do it now */ |
|||
|
|||
static void |
|||
hw_cpu_interrupt_event(device *me, |
|||
int my_port, |
|||
device *source, |
|||
int source_port, |
|||
int level, |
|||
cpu *processor, |
|||
unsigned_word cia) |
|||
{ |
|||
hw_cpu_device *hw_cpu = (hw_cpu_device*)device_data(me); |
|||
if (my_port < 0 || my_port >= hw_cpu_nr_interrupt_ports) |
|||
error("hw_cpu_interrupt_event_callback: interrupt port out of range %d\n", |
|||
my_port); |
|||
switch (my_port) { |
|||
/*case hw_cpu_hard_reset:*/ |
|||
/*case hw_cpu_soft_reset:*/ |
|||
case hw_cpu_external_interrupt: |
|||
external_interrupt(hw_cpu->processor, level); |
|||
break; |
|||
/*case hw_cpu_machine_check_interrupt:*/ |
|||
default: |
|||
error("hw_cpu_deliver_interrupt: unimplemented interrupt port %d\n", |
|||
my_port); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
|
|||
static device_callbacks const hw_cpu_callbacks = { |
|||
{ hw_cpu_init_address, }, /* init */ |
|||
{ NULL, }, /* address */ |
|||
{ NULL, }, /* io */ |
|||
{ NULL, }, /* DMA */ |
|||
{ hw_cpu_interrupt_event, NULL, hw_cpu_interrupt_ports }, /* interrupts */ |
|||
{ NULL, NULL, }, |
|||
}; |
|||
|
|||
const device_descriptor hw_cpu_device_descriptor[] = { |
|||
{ "hw-cpu", hw_cpu_create, &hw_cpu_callbacks }, |
|||
{ "cpu", hw_cpu_create, &hw_cpu_callbacks }, |
|||
{ NULL, }, |
|||
}; |
|||
|
|||
#endif /* _HW_CPU_C_ */ |
|||
@ -0,0 +1,34 @@ |
|||
/* 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_CPU_H_ |
|||
#define _HW_CPU_H_ |
|||
|
|||
enum { |
|||
hw_cpu_hard_reset, |
|||
hw_cpu_soft_reset, |
|||
hw_cpu_external_interrupt, |
|||
hw_cpu_machine_check_interrupt, |
|||
hw_cpu_system_management_interrupt, |
|||
hw_cpu_nr_interrupt_ports |
|||
}; |
|||
|
|||
#endif /* _HW_CPU_H_ */ |
|||
@ -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_ */ |
|||
@ -0,0 +1,98 @@ |
|||
/* 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_IOBUS_C_ |
|||
#define _HW_IOBUS_C_ |
|||
|
|||
#ifndef STATIC_INLINE_HW_IOBUS |
|||
#define STATIC_INLINE_HW_IOBUS STATIC_INLINE |
|||
#endif |
|||
|
|||
#include "device_table.h" |
|||
|
|||
|
|||
/* Device:
|
|||
|
|||
iobus@<address> - bus for attaching devices |
|||
|
|||
|
|||
Description: |
|||
|
|||
IOBUS provides a simple `local' bus for attaching (hanging) |
|||
programmed IO devices from. All child devices are mapped into this |
|||
devices parent address space (after adjusting for the iobus's base |
|||
address). |
|||
|
|||
|
|||
Properties: |
|||
|
|||
None. |
|||
|
|||
*/ |
|||
|
|||
static void |
|||
hw_iobus_attach_address_callback(device *me, |
|||
const char *name, |
|||
attach_type type, |
|||
int space, |
|||
unsigned_word addr, |
|||
unsigned nr_bytes, |
|||
access_type access, |
|||
device *who) /*callback/default*/ |
|||
{ |
|||
unsigned_word hw_iobus_addr; |
|||
/* sanity check */ |
|||
if (space != 0) |
|||
error("hw_iobus_attach_address_callback() no space for %s/%s\n", |
|||
device_name(me), name); |
|||
/* get the bus address */ |
|||
if (device_unit_address(me)->nr_cells != 1) |
|||
error("hw_iobus_attach_address_callback() invalid address for %s\n", |
|||
device_name(me)); |
|||
hw_iobus_addr = device_unit_address(me)->cells[0]; |
|||
device_attach_address(device_parent(me), |
|||
device_name(me), |
|||
type, |
|||
0 /*space*/, |
|||
hw_iobus_addr + addr, |
|||
nr_bytes, |
|||
access, |
|||
who); |
|||
} |
|||
|
|||
|
|||
static device_callbacks const hw_iobus_callbacks = { |
|||
{ NULL, }, |
|||
{ hw_iobus_attach_address_callback, }, |
|||
{ NULL, }, /* IO */ |
|||
{ NULL, }, /* DMA */ |
|||
{ NULL, }, /* interrupt */ |
|||
{ generic_device_unit_decode, |
|||
generic_device_unit_encode, } |
|||
}; |
|||
|
|||
|
|||
const device_descriptor hw_iobus_device_descriptor[] = { |
|||
{ "iobus", NULL, &hw_iobus_callbacks }, |
|||
{ NULL, }, |
|||
}; |
|||
|
|||
#endif /* _HW_IOBUS_ */ |
|||
@ -0,0 +1,234 @@ |
|||
/* 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_NVRAM_C_ |
|||
#define _HW_NVRAM_C_ |
|||
|
|||
#ifndef STATIC_INLINE_HW_NVRAM |
|||
#define STATIC_INLINE_HW_NVRAM STATIC_INLINE |
|||
#endif |
|||
|
|||
#include "device_table.h" |
|||
|
|||
#ifdef HAVE_TIME_H |
|||
#include <time.h> |
|||
#endif |
|||
|
|||
#ifdef HAVE_STRING_H |
|||
#include <string.h> |
|||
#else |
|||
#ifdef HAVE_STRINGS_H |
|||
#include <strings.h> |
|||
#endif |
|||
#endif |
|||
|
|||
/* NVRAM - non-volatile memory with optional clock.
|
|||
|
|||
Description: |
|||
|
|||
This device implements a small byte addressable non-volatile memory |
|||
component. The component may include an optional real-time clock |
|||
at its upper addresses. |
|||
|
|||
Properties: |
|||
|
|||
reg = <address> <size>. Determine where the device lives in the |
|||
parents address space. |
|||
|
|||
timezone = <integer>. Adjustment to current host's GMT (in secons) |
|||
that should be applied when updating the NVRAM's clock. */ |
|||
|
|||
typedef struct _hw_nvram_device { |
|||
unsigned8 *memory; |
|||
unsigned sizeof_memory; |
|||
#ifdef HAVE_TIME_H |
|||
time_t host_time; |
|||
#else |
|||
long host_time; |
|||
#endif |
|||
unsigned timezone; |
|||
/* useful */ |
|||
unsigned addr_year; |
|||
unsigned addr_month; |
|||
unsigned addr_date; |
|||
unsigned addr_day; |
|||
unsigned addr_hour; |
|||
unsigned addr_minutes; |
|||
unsigned addr_seconds; |
|||
unsigned addr_control; |
|||
} hw_nvram_device; |
|||
|
|||
static void * |
|||
hw_nvram_create(const char *name, |
|||
const device_unit *unit_address, |
|||
const char *args, |
|||
device *parent) |
|||
{ |
|||
hw_nvram_device *hw_nvram = ZALLOC(hw_nvram_device); |
|||
return hw_nvram; |
|||
} |
|||
|
|||
typedef struct _hw_nvram_reg_spec { |
|||
unsigned32 base; |
|||
unsigned32 size; |
|||
} hw_nvram_reg_spec; |
|||
|
|||
static void |
|||
hw_nvram_init_address(device *me) |
|||
{ |
|||
hw_nvram_device *hw_nvram = (hw_nvram_device*)device_data(me); |
|||
const device_property *reg = device_find_array_property(me, "reg"); |
|||
const hw_nvram_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 hw_nvram */ |
|||
if (hw_nvram->memory == NULL) { |
|||
hw_nvram->sizeof_memory = BE2H_4(spec->size); |
|||
hw_nvram->memory = zalloc(hw_nvram->sizeof_memory); |
|||
} |
|||
else |
|||
memset(hw_nvram->memory, hw_nvram->sizeof_memory, 0); |
|||
|
|||
hw_nvram->timezone = device_find_integer_property(me, "timezone"); |
|||
|
|||
hw_nvram->addr_year = hw_nvram->sizeof_memory - 1; |
|||
hw_nvram->addr_month = hw_nvram->sizeof_memory - 2; |
|||
hw_nvram->addr_date = hw_nvram->sizeof_memory - 3; |
|||
hw_nvram->addr_day = hw_nvram->sizeof_memory - 4; |
|||
hw_nvram->addr_hour = hw_nvram->sizeof_memory - 5; |
|||
hw_nvram->addr_minutes = hw_nvram->sizeof_memory - 6; |
|||
hw_nvram->addr_seconds = hw_nvram->sizeof_memory - 7; |
|||
hw_nvram->addr_control = hw_nvram->sizeof_memory - 8; |
|||
|
|||
device_attach_address(device_parent(me), |
|||
device_name(me), |
|||
attach_callback, |
|||
0 /*address space*/, |
|||
BE2H_4(spec->base), |
|||
hw_nvram->sizeof_memory, |
|||
access_read_write_exec, |
|||
me); |
|||
} |
|||
|
|||
static int |
|||
hw_nvram_bcd(int val) |
|||
{ |
|||
return ((val / 10) << 4) + (val % 10); |
|||
} |
|||
|
|||
/* If reached an update interval and allowed, update the clock within
|
|||
the hw_nvram. While this function could be implemented using events |
|||
it isn't on the assumption that the HW_NVRAM will hardly ever be |
|||
referenced and hence there is little need in keeping the clock |
|||
continually up-to-date */ |
|||
static void |
|||
hw_nvram_update_clock(hw_nvram_device *hw_nvram, cpu *processor) |
|||
{ |
|||
#ifdef HAVE_TIME_H |
|||
if (!(hw_nvram->memory[hw_nvram->addr_control] & 0xc0)) { |
|||
time_t host_time = time(NULL); |
|||
if (hw_nvram->host_time != host_time) { |
|||
time_t nvtime = hw_nvram->host_time + hw_nvram->timezone; |
|||
struct tm *clock = gmtime(&nvtime); |
|||
hw_nvram->host_time = host_time; |
|||
hw_nvram->memory[hw_nvram->addr_year] = hw_nvram_bcd(clock->tm_year); |
|||
hw_nvram->memory[hw_nvram->addr_month] = hw_nvram_bcd(clock->tm_mon + 1); |
|||
hw_nvram->memory[hw_nvram->addr_date] = hw_nvram_bcd(clock->tm_mday); |
|||
hw_nvram->memory[hw_nvram->addr_day] = hw_nvram_bcd(clock->tm_wday + 1); |
|||
hw_nvram->memory[hw_nvram->addr_hour] = hw_nvram_bcd(clock->tm_hour); |
|||
hw_nvram->memory[hw_nvram->addr_minutes] = hw_nvram_bcd(clock->tm_min); |
|||
hw_nvram->memory[hw_nvram->addr_seconds] = hw_nvram_bcd(clock->tm_sec); |
|||
} |
|||
} |
|||
#else |
|||
error("fixme - where do I find out GMT\n"); |
|||
#endif |
|||
} |
|||
|
|||
static void |
|||
hw_nvram_set_clock(hw_nvram_device *hw_nvram, cpu *processor) |
|||
{ |
|||
error ("fixme - how do I set the localtime\n"); |
|||
} |
|||
|
|||
static unsigned |
|||
hw_nvram_io_read_buffer(device *me, |
|||
void *dest, |
|||
int space, |
|||
unsigned_word addr, |
|||
unsigned nr_bytes, |
|||
cpu *processor, |
|||
unsigned_word cia) |
|||
{ |
|||
int i; |
|||
hw_nvram_device *hw_nvram = (hw_nvram_device*)device_data(me); |
|||
for (i = 0; i < nr_bytes; i++) { |
|||
unsigned address = (addr + i) % hw_nvram->sizeof_memory; |
|||
unsigned8 data = hw_nvram->memory[address]; |
|||
hw_nvram_update_clock(hw_nvram, processor); |
|||
((unsigned8*)dest)[i] = data; |
|||
} |
|||
return nr_bytes; |
|||
} |
|||
|
|||
static unsigned |
|||
hw_nvram_io_write_buffer(device *me, |
|||
const void *source, |
|||
int space, |
|||
unsigned_word addr, |
|||
unsigned nr_bytes, |
|||
cpu *processor, |
|||
unsigned_word cia) |
|||
{ |
|||
int i; |
|||
hw_nvram_device *hw_nvram = (hw_nvram_device*)device_data(me); |
|||
for (i = 0; i < nr_bytes; i++) { |
|||
unsigned address = (addr + i) % hw_nvram->sizeof_memory; |
|||
unsigned8 data = ((unsigned8*)source)[i]; |
|||
if (address == hw_nvram->addr_control |
|||
&& (data & 0x80) == 0 |
|||
&& (hw_nvram->memory[address] & 0x80) == 0x80) |
|||
hw_nvram_set_clock(hw_nvram, processor); |
|||
else |
|||
hw_nvram_update_clock(hw_nvram, processor); |
|||
hw_nvram->memory[address] = data; |
|||
} |
|||
return nr_bytes; |
|||
} |
|||
|
|||
static device_callbacks const hw_nvram_callbacks = { |
|||
{ hw_nvram_init_address, }, |
|||
{ NULL, }, /* address */ |
|||
{ hw_nvram_io_read_buffer, hw_nvram_io_write_buffer }, /* IO */ |
|||
}; |
|||
|
|||
const device_descriptor hw_nvram_device_descriptor[] = { |
|||
{ "nvram", hw_nvram_create, &hw_nvram_callbacks }, |
|||
{ NULL }, |
|||
}; |
|||
|
|||
#endif /* _HW_NVRAM_C_ */ |
|||
@ -0,0 +1 @@ |
|||
something goes here |
|||
@ -0,0 +1,98 @@ |
|||
/* ICU device: icu@<address>
|
|||
|
|||
<address> : read - processor nr |
|||
<address> : write - interrupt processor nr |
|||
<address> + 4 : read - nr processors |
|||
|
|||
Single byte registers that control a simple ICU. |
|||
|
|||
Illustrates passing of events to parent device. Passing of |
|||
interrupts to an interrupt destination. */ |
|||
|
|||
|
|||
static unsigned |
|||
icu_io_read_buffer_callback(device *me, |
|||
void *dest, |
|||
int space, |
|||
unsigned_word addr, |
|||
unsigned nr_bytes, |
|||
cpu *processor, |
|||
unsigned_word cia) |
|||
{ |
|||
memset(dest, 0, nr_bytes); |
|||
switch (addr & 4) { |
|||
case 0: |
|||
*(unsigned_1*)dest = cpu_nr(processor); |
|||
break; |
|||
case 4: |
|||
*(unsigned_1*)dest = |
|||
device_find_integer_property(me, "/openprom/options/smp"); |
|||
break; |
|||
} |
|||
return nr_bytes; |
|||
} |
|||
|
|||
|
|||
static unsigned |
|||
icu_io_write_buffer_callback(device *me, |
|||
const void *source, |
|||
int space, |
|||
unsigned_word addr, |
|||
unsigned nr_bytes, |
|||
cpu *processor, |
|||
unsigned_word cia) |
|||
{ |
|||
unsigned_1 val = H2T_1(*(unsigned_1*)source); |
|||
/* tell the parent device that the interrupt lines have changed.
|
|||
For this fake ICU. The interrupt lines just indicate the cpu to |
|||
interrupt next */ |
|||
device_interrupt_event(me, |
|||
val, /*my_port*/ |
|||
val, /*val*/ |
|||
processor, cia); |
|||
return nr_bytes; |
|||
} |
|||
|
|||
static void |
|||
icu_do_interrupt(event_queue *queue, |
|||
void *data) |
|||
{ |
|||
cpu *target = (cpu*)data; |
|||
/* try to interrupt the processor. If the attempt fails, try again
|
|||
on the next tick */ |
|||
if (!external_interrupt(target)) |
|||
event_queue_schedule(queue, 1, icu_do_interrupt, target); |
|||
} |
|||
|
|||
|
|||
static void |
|||
icu_interrupt_event_callback(device *me, |
|||
int my_port, |
|||
device *source, |
|||
int source_port, |
|||
int level, |
|||
cpu *processor, |
|||
unsigned_word cia) |
|||
{ |
|||
/* the interrupt controller can't interrupt a cpu at any time.
|
|||
Rather it must synchronize with the system clock before |
|||
performing an interrupt on the given processor */ |
|||
psim *system = cpu_system(processor); |
|||
cpu *target = psim_cpu(system, my_port); |
|||
if (target != NULL) { |
|||
event_queue *events = cpu_event_queue(target); |
|||
event_queue_schedule(events, 1, icu_do_interrupt, target); |
|||
} |
|||
} |
|||
|
|||
static device_callbacks const icu_callbacks = { |
|||
{ generic_device_init_address, }, |
|||
{ NULL, }, /* address */ |
|||
{ icu_io_read_buffer_callback, |
|||
icu_io_write_buffer_callback, }, |
|||
{ NULL, }, /* DMA */ |
|||
{ icu_interrupt_event_callback, }, |
|||
{ NULL, }, /* unit */ |
|||
}; |
|||
|
|||
|
|||
File diff suppressed because it is too large
Loading…
Reference in new issue