8 changed files with 3636 additions and 378 deletions
@ -0,0 +1,371 @@ |
|||
/* 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_GLUE_C_ |
|||
#define _HW_GLUE_C_ |
|||
|
|||
#include "device_table.h" |
|||
|
|||
|
|||
/* DEVICE
|
|||
|
|||
|
|||
glue - glue to interconnect and test interrupts |
|||
|
|||
|
|||
DESCRIPTION |
|||
|
|||
|
|||
The glue device provides two functions. Firstly, it provides a |
|||
mechanism for inspecting and driving the interrupt net. Secondly, |
|||
it provides a set of boolean primitives that can be used add |
|||
combinatorial operations to the interrupt network. |
|||
|
|||
Glue devices have a variable number of big endian <<output>> |
|||
registers. Each host-word size. The registers can be both read |
|||
and written. |
|||
|
|||
Writing a value to an output register causes an interrupt (of the |
|||
specified level) to be driven on the devices corresponding output |
|||
interrupt port. |
|||
|
|||
Reading an <<output>> register returns either the last value |
|||
written or the most recently computed value (for that register) as |
|||
a result of an interrupt ariving (which ever was computed last). |
|||
|
|||
At present the following sub device types are available: |
|||
|
|||
<<glue>>: In addition to driving its output interrupt port with any |
|||
value written to an interrupt input port is stored in the |
|||
corresponding <<output>> register. Such input interrupts, however, |
|||
are not propogated to an output interrupt port. |
|||
|
|||
<<glue-and>>: The bit-wise AND of the interrupt inputs is computed |
|||
and then both stored in <<output>> register zero and propogated to |
|||
output interrupt output port zero. |
|||
|
|||
|
|||
PROPERTIES |
|||
|
|||
|
|||
reg = <address> <size> (required) |
|||
|
|||
Specify the address (within the parent bus) that this device is to |
|||
live. The address must be 2048 * sizeof(word) (8k in a 32bit |
|||
simulation) aligned. |
|||
|
|||
|
|||
interrupt-ranges = <int-number> <range> (optional) |
|||
|
|||
If present, this specifies the number of valid interrupt inputs (up |
|||
to the maximum of 2048). By default, <<int-number>> is zero and |
|||
range is determined by the <<reg>> size. |
|||
|
|||
|
|||
EXAMPLES |
|||
|
|||
|
|||
Enable tracing of the device: |
|||
|
|||
| -t glue-device \ |
|||
|
|||
|
|||
Create source, bitwize-and, and sink glue devices. Since the |
|||
device at address <<0x10000>> is of size <<8>> it will have two |
|||
output interrupt ports. |
|||
|
|||
| -o '/iobus@0xf0000000/glue@0x10000/reg 0x10000 8' \ |
|||
| -o '/iobus@0xf0000000/glue-and@0x20000/reg 0x20000 4' \ |
|||
| -o '/iobus@0xf0000000/glue-and/interrupt-ranges 0 2' \ |
|||
| -o '/iobus@0xf0000000/glue@0x30000/reg 0x30000 4' \ |
|||
|
|||
|
|||
Wire the two source interrupts to the AND device: |
|||
|
|||
| -o '/iobus@0xf0000000/glue@0x10000 > 0 0 /iobus/glue-and' \ |
|||
| -o '/iobus@0xf0000000/glue@0x10000 > 1 1 /iobus/glue-and' \ |
|||
|
|||
|
|||
Wire the AND device up to the sink so that the and's output is not |
|||
left open. |
|||
|
|||
| -o '/iobus@0xf0000000/glue-and > 0 0 /iobus/glue@0x30000' \ |
|||
|
|||
|
|||
With the above configuration. The client program is able to |
|||
compute a two bit AND. For instance the <<C>> stub below prints 1 |
|||
AND 0. |
|||
|
|||
| unsigned *input = (void*)0xf0010000; |
|||
| unsigned *output = (void*)0xf0030000; |
|||
| unsigned ans; |
|||
| input[0] = htonl(1); |
|||
| input[1] = htonl(0); |
|||
| ans = ntohl(*output); |
|||
| write_string("AND is "); |
|||
| write_int(ans); |
|||
| write_line(); |
|||
|
|||
|
|||
BUGS |
|||
|
|||
|
|||
A future implementation of this device may support multiple |
|||
interrupt ranges. |
|||
|
|||
Some of the devices listed may not yet be fully implemented. |
|||
|
|||
Additional devices such as a dff, an inverter or a latch may be |
|||
useful. |
|||
|
|||
*/ |
|||
|
|||
|
|||
enum { |
|||
max_nr_interrupts = 2048, |
|||
}; |
|||
|
|||
typedef enum _hw_glue_type { |
|||
glue_undefined = 0, |
|||
glue_io, |
|||
glue_and, |
|||
glue_nand, |
|||
glue_or, |
|||
glue_xor, |
|||
glue_nor, |
|||
glue_not, |
|||
} hw_glue_type; |
|||
|
|||
typedef struct _hw_glue_device { |
|||
hw_glue_type type; |
|||
int int_number; |
|||
int *input; |
|||
int nr_inputs; |
|||
unsigned sizeof_input; |
|||
/* our output registers */ |
|||
int space; |
|||
unsigned_word address; |
|||
unsigned sizeof_output; |
|||
int *output; |
|||
int nr_outputs; |
|||
} hw_glue_device; |
|||
|
|||
|
|||
static void |
|||
hw_glue_init_address(device *me) |
|||
{ |
|||
hw_glue_device *glue = (hw_glue_device*)device_data(me); |
|||
|
|||
/* attach to my parent */ |
|||
generic_device_init_address(me); |
|||
|
|||
/* establish the output registers */ |
|||
if (glue->output != NULL) { |
|||
memset(glue->output, 0, glue->sizeof_output); |
|||
} |
|||
else { |
|||
reg_property_spec unit; |
|||
int reg_nr; |
|||
/* find a relevant reg entry */ |
|||
reg_nr = 0; |
|||
while (device_find_reg_array_property(me, "reg", reg_nr, &unit) |
|||
&& !device_size_to_attach_size(device_parent(me), &unit.size, |
|||
&glue->sizeof_output, me)) |
|||
reg_nr++; |
|||
/* check out the size */ |
|||
if (glue->sizeof_output == 0) |
|||
device_error(me, "at least one reg property size must be nonzero"); |
|||
if (glue->sizeof_output % sizeof(unsigned_word) != 0) |
|||
device_error(me, "reg property size must be %d aligned", sizeof(unsigned_word)); |
|||
/* and the address */ |
|||
device_address_to_attach_address(device_parent(me), |
|||
&unit.address, &glue->space, &glue->address, |
|||
me); |
|||
if (glue->address % (sizeof(unsigned_word) * max_nr_interrupts) != 0) |
|||
device_error(me, "reg property address must be %d aligned", |
|||
sizeof(unsigned_word) * max_nr_interrupts); |
|||
glue->nr_outputs = glue->sizeof_output / sizeof(unsigned_word); |
|||
glue->output = zalloc(glue->sizeof_output); |
|||
} |
|||
|
|||
/* establish the input interrupt ports */ |
|||
if (glue->input != NULL) { |
|||
memset(glue->input, 0, glue->sizeof_input); |
|||
} |
|||
else { |
|||
const device_property *ranges = device_find_property(me, "interrupt-ranges"); |
|||
if (ranges == NULL) { |
|||
glue->int_number = 0; |
|||
glue->nr_inputs = glue->nr_outputs; |
|||
} |
|||
else if (ranges->sizeof_array != sizeof(unsigned_cell) * 2) { |
|||
device_error(me, "invalid interrupt-ranges property (incorrect size)"); |
|||
} |
|||
else { |
|||
const unsigned_cell *int_range = ranges->array; |
|||
glue->int_number = BE2H_cell(int_range[0]); |
|||
glue->nr_inputs = BE2H_cell(int_range[1]); |
|||
} |
|||
glue->sizeof_input = glue->nr_inputs * sizeof(unsigned); |
|||
glue->input = zalloc(glue->sizeof_input); |
|||
} |
|||
|
|||
/* determine our type */ |
|||
if (glue->type == glue_undefined) { |
|||
const char *name = device_name(me); |
|||
if (strcmp(name, "glue") == 0) |
|||
glue->type = glue_io; |
|||
else if (strcmp(name, "glue-and") == 0) |
|||
glue->type = glue_and; |
|||
else |
|||
device_error(me, "unimplemented glue type"); |
|||
} |
|||
|
|||
DTRACE(glue, ("int-number %d, nr_inputs %d, nr_outputs %d\n", |
|||
glue->int_number, glue->nr_inputs, glue->nr_outputs)); |
|||
} |
|||
|
|||
static unsigned |
|||
hw_glue_io_read_buffer_callback(device *me, |
|||
void *dest, |
|||
int space, |
|||
unsigned_word addr, |
|||
unsigned nr_bytes, |
|||
cpu *processor, |
|||
unsigned_word cia) |
|||
{ |
|||
hw_glue_device *glue = (hw_glue_device*)device_data(me); |
|||
int reg = ((addr - glue->address) / sizeof(unsigned_word)) % glue->nr_outputs; |
|||
if (nr_bytes != sizeof(unsigned_word) |
|||
|| (addr % sizeof(unsigned_word)) != 0) |
|||
device_error(me, "missaligned read access (%d:0x%lx:%d) not supported", |
|||
space, (unsigned long)addr, nr_bytes); |
|||
*(unsigned_word*)dest = H2BE_4(glue->output[reg]); |
|||
DTRACE(glue, ("read - interrupt %d (0x%lx), level %d\n", |
|||
reg, (unsigned long) addr, glue->output[reg])); |
|||
return nr_bytes; |
|||
} |
|||
|
|||
|
|||
static unsigned |
|||
hw_glue_io_write_buffer_callback(device *me, |
|||
const void *source, |
|||
int space, |
|||
unsigned_word addr, |
|||
unsigned nr_bytes, |
|||
cpu *processor, |
|||
unsigned_word cia) |
|||
{ |
|||
hw_glue_device *glue = (hw_glue_device*)device_data(me); |
|||
int reg = ((addr - glue->address) / sizeof(unsigned_word)) % max_nr_interrupts; |
|||
if (nr_bytes != sizeof(unsigned_word) |
|||
|| (addr % sizeof(unsigned_word)) != 0) |
|||
device_error(me, "missaligned write access (%d:0x%lx:%d) not supported", |
|||
space, (unsigned long)addr, nr_bytes); |
|||
glue->output[reg] = H2BE_4(*(unsigned_word*)source); |
|||
DTRACE(glue, ("write - interrupt %d (0x%lx), level %d\n", |
|||
reg, (unsigned long) addr, glue->output[reg])); |
|||
device_interrupt_event(me, reg, glue->output[reg], processor, cia); |
|||
return nr_bytes; |
|||
} |
|||
|
|||
static void |
|||
hw_glue_interrupt_event(device *me, |
|||
int my_port, |
|||
device *source, |
|||
int source_port, |
|||
int level, |
|||
cpu *processor, |
|||
unsigned_word cia) |
|||
{ |
|||
hw_glue_device *glue = (hw_glue_device*)device_data(me); |
|||
int i; |
|||
if (my_port < glue->int_number |
|||
|| my_port >= glue->int_number + glue->nr_inputs) |
|||
device_error(me, "interrupt %d outside of valid range", my_port); |
|||
glue->input[my_port - glue->int_number] = level; |
|||
switch (glue->type) { |
|||
case glue_io: |
|||
{ |
|||
int port = my_port % glue->nr_outputs; |
|||
glue->output[port] = level; |
|||
DTRACE(glue, ("input - interrupt %d (0x%lx), level %d\n", |
|||
my_port, |
|||
(unsigned long)glue->address + port * sizeof(unsigned_word), |
|||
level)); |
|||
break; |
|||
} |
|||
case glue_and: |
|||
glue->output[0] = glue->input[0]; |
|||
for (i = 1; i < glue->nr_inputs; i++) |
|||
glue->output[0] &= glue->input[i]; |
|||
DTRACE(glue, ("and - interrupt %d, level %d arrived - output %d\n", |
|||
my_port, level, glue->output[0])); |
|||
device_interrupt_event(me, 0, glue->output[0], processor, cia); |
|||
break; |
|||
default: |
|||
device_error(me, "operator not implemented"); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
|
|||
static const device_interrupt_port_descriptor hw_glue_interrupt_ports[] = { |
|||
{ "int", 0, max_nr_interrupts }, |
|||
{ NULL } |
|||
}; |
|||
|
|||
|
|||
static device_callbacks const hw_glue_callbacks = { |
|||
{ hw_glue_init_address, NULL }, |
|||
{ NULL, }, /* address */ |
|||
{ hw_glue_io_read_buffer_callback, |
|||
hw_glue_io_write_buffer_callback, }, |
|||
{ NULL, }, /* DMA */ |
|||
{ hw_glue_interrupt_event, NULL, hw_glue_interrupt_ports }, /* interrupt */ |
|||
{ NULL, }, /* unit */ |
|||
NULL, /* instance */ |
|||
}; |
|||
|
|||
|
|||
static void * |
|||
hw_glue_create(const char *name, |
|||
const device_unit *unit_address, |
|||
const char *args) |
|||
{ |
|||
/* create the descriptor */ |
|||
hw_glue_device *glue = ZALLOC(hw_glue_device); |
|||
return glue; |
|||
} |
|||
|
|||
|
|||
const device_descriptor hw_glue_device_descriptor[] = { |
|||
{ "glue", hw_glue_create, &hw_glue_callbacks }, |
|||
{ "glue-and", hw_glue_create, &hw_glue_callbacks }, |
|||
{ "glue-nand", hw_glue_create, &hw_glue_callbacks }, |
|||
{ "glue-or", hw_glue_create, &hw_glue_callbacks }, |
|||
{ "glue-xor", hw_glue_create, &hw_glue_callbacks }, |
|||
{ "glue-nor", hw_glue_create, &hw_glue_callbacks }, |
|||
{ "glue-not", hw_glue_create, &hw_glue_callbacks }, |
|||
{ NULL }, |
|||
}; |
|||
|
|||
#endif /* _HW_GLUE_C_ */ |
|||
@ -0,0 +1,869 @@ |
|||
/* This file is part of the program psim.
|
|||
|
|||
Copyright (C) 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_IDE_C_ |
|||
#define _HW_IDE_C_ |
|||
|
|||
#include "device_table.h" |
|||
|
|||
|
|||
|
|||
/* DEVICE
|
|||
|
|||
|
|||
ide - Integrated Disk Electronics |
|||
|
|||
|
|||
DESCRIPTION |
|||
|
|||
|
|||
This device models the primary/secondary <<ide>> controller |
|||
described in the [CHRPIO] document. |
|||
|
|||
The controller has separate independant interrupt outputs for each |
|||
<<ide>> bus. |
|||
|
|||
|
|||
PROPERTIES |
|||
|
|||
|
|||
reg = ... (required) |
|||
|
|||
The <<reg>> property is described in the document [CHRPIO]. |
|||
|
|||
|
|||
ready-delay = <integer> (optional) |
|||
|
|||
If present, this specifies the time that the <<ide>> device takes |
|||
to complete an I/O operation. |
|||
|
|||
|
|||
disk@?/ide-byte-count = <integer> (optional) |
|||
|
|||
disk@?/ide-sector-count = <integer> (optional) |
|||
|
|||
disk@?/ide-head-count = <integer> (optional) |
|||
|
|||
The <<ide>> device checks each child (disk device) node to see if |
|||
it has the above properties. If present, these values will be used |
|||
to compute the <<LBA>> address in <<CHS>> addressing mode. |
|||
|
|||
|
|||
EXAMPLES |
|||
|
|||
|
|||
Enable tracing: |
|||
|
|||
| -t ide-device \ |
|||
|
|||
|
|||
Attach the <<ide>> device to the <<pci>> bus at slot one. Specify |
|||
legacy I/O addresses: |
|||
|
|||
| -o '/phb/ide@1/assigned-addresses \ |
|||
| ni0,0,10,1f0 8 \ |
|||
| ni0,0,14,3f8 8 \ |
|||
| ni0,0,18,170 8 \ |
|||
| ni0,0,1c,378 8 \ |
|||
| ni0,0,20,200 8' \ |
|||
| -o '/phb@0x80000000/ide@1/reg \ |
|||
| 1 0 \ |
|||
| i0,0,10,0 8 \ |
|||
| i0,0,18,0 8 \ |
|||
| i0,0,14,6 1 \ |
|||
| i0,0,1c,6 1 \ |
|||
| i0,0,20,0 8' \ |
|||
|
|||
Note: the fouth and fifth reg entries specify that the register is |
|||
at an offset into the address specified by the base register |
|||
(<<assigned-addresses>>); Apart from restrictions placed by the |
|||
<<pci>> specification, no restrictions are placed on the number of |
|||
base registers specified by the <<assigned-addresses>> property. |
|||
|
|||
Attach a <<disk>> to the primary and a <<cdrom>> to the secondary |
|||
<<ide>> controller. |
|||
|
|||
| -o '/phb@0x80000000/ide@1/disk@0/file "zero' \
|
|||
| -o '/phb@0x80000000/ide@1/cdrom@2/file "/dev/cdrom"' \ |
|||
|
|||
Connect the two interrupt outputs (a and b) to a <<glue>> device to |
|||
allow testing of the interrupt port. In a real simulation they |
|||
would be wired to the interrupt controller. |
|||
|
|||
| -o '/phb@0x80000000/glue@2/reg 2 0 ni0,0,0,0 8' \ |
|||
| -o '/phb@0x80000000/ide@1 > a 0 /phb@0x80000000/glue@2' \ |
|||
| -o '/phb@0x80000000/ide@1 > b 1 /phb@0x80000000/glue@2' |
|||
|
|||
|
|||
BUGS |
|||
|
|||
|
|||
While the DMA registers are present, DMA support has not yet been |
|||
implemented. |
|||
|
|||
The number of supported commands is very limited. |
|||
|
|||
The standards documents appear to be vague on how to specify the |
|||
<<unit-address>> of disk devices devices being attached to the |
|||
<<ide>> controller. I've chosen to use integers with devices zero |
|||
and one going to the primary controller while two and three are |
|||
connected to the secondary controller. |
|||
|
|||
|
|||
REFERENCES |
|||
|
|||
|
|||
[CHRPIO] PowerPC(tm) Microprocessor Common Hardware Reference |
|||
Platform: I/O Device Reference. http://chrp.apple.com/???.
|
|||
|
|||
[SCHMIDT] The SCSI Bus and IDE Interface - Protocols, Applications |
|||
and Programming. Friedhelm Schmidt (translated by Michael |
|||
Schultz). ISBN 0-201-42284-0. Addison-Wesley Publishing Company. |
|||
|
|||
|
|||
*/ |
|||
|
|||
|
|||
|
|||
typedef enum _io_direction { |
|||
is_read, |
|||
is_write, |
|||
} io_direction; |
|||
|
|||
|
|||
enum { |
|||
nr_ide_controllers = 2, |
|||
nr_ide_drives_per_controller = 2, |
|||
nr_fifo_entries = 8192, |
|||
}; |
|||
|
|||
enum { |
|||
/* command register block - read */ |
|||
ide_data_reg, |
|||
ide_error_reg, /*ide_feature_reg*/ |
|||
ide_sector_count_reg, |
|||
ide_sector_number_reg, |
|||
ide_cylinder_reg0, |
|||
ide_cylinder_reg1, |
|||
ide_drive_head_reg, |
|||
ide_status_reg, /*ide_command_reg*/ |
|||
/* command register block - write */ |
|||
ide_feature_reg, /*ide_error_reg*/ |
|||
ide_command_reg, /*ide_status_reg*/ |
|||
/* control register block - read */ |
|||
ide_alternate_status_reg, /*ide_control_reg*/ |
|||
ide_control_reg, /*ide_alternate_status_reg*/ |
|||
/* dma register block */ |
|||
ide_dma_command_reg, |
|||
ide_dma_unused_1_reg, |
|||
ide_dma_status_reg, |
|||
ide_dma_unused_3_reg, |
|||
ide_dma_prd_table_address_reg0, |
|||
ide_dma_prd_table_address_reg1, |
|||
ide_dma_prd_table_address_reg2, |
|||
ide_dma_prd_table_address_reg3, |
|||
nr_ide_registers, |
|||
}; |
|||
|
|||
|
|||
typedef enum _ide_states { |
|||
idle_state, |
|||
busy_loaded_state, |
|||
busy_drained_state, |
|||
busy_dma_state, |
|||
busy_command_state, |
|||
loading_state, |
|||
draining_state, |
|||
} ide_states; |
|||
|
|||
static const char * |
|||
ide_state_name(ide_states state) |
|||
{ |
|||
switch (state) { |
|||
case idle_state: return "idle"; |
|||
case busy_loaded_state: return "busy_loaded_state"; |
|||
case busy_drained_state: return "busy_drained_state"; |
|||
case busy_dma_state: return "busy_dma_state"; |
|||
case busy_command_state: return "busy_command_state"; |
|||
case loading_state: return "loading_state"; |
|||
case draining_state: return "draining_state"; |
|||
default: return "illegal-state"; |
|||
} |
|||
} |
|||
|
|||
typedef struct _ide_geometry { |
|||
int head; |
|||
int sector; |
|||
int byte; |
|||
} ide_geometry; |
|||
|
|||
typedef struct _ide_drive { |
|||
int nr; |
|||
device *device; |
|||
ide_geometry geometry; |
|||
ide_geometry default_geometry; |
|||
} ide_drive; |
|||
|
|||
typedef struct _ide_controller { |
|||
int nr; |
|||
ide_states state; |
|||
unsigned8 reg[nr_ide_registers]; |
|||
unsigned8 fifo[nr_fifo_entries]; |
|||
int fifo_pos; |
|||
int fifo_size; |
|||
ide_drive *current_drive; |
|||
int current_byte; |
|||
int current_transfer; |
|||
ide_drive drive[nr_ide_drives_per_controller]; |
|||
device *me; |
|||
event_entry_tag event_tag; |
|||
int is_interrupting; |
|||
signed64 ready_delay; |
|||
} ide_controller; |
|||
|
|||
|
|||
|
|||
static void |
|||
set_interrupt(device *me, |
|||
ide_controller *controller) |
|||
{ |
|||
if ((controller->reg[ide_control_reg] & 0x2) == 0) { |
|||
DTRACE(ide, ("controller %d - interrupt set\n", controller->nr)); |
|||
device_interrupt_event(me, controller->nr, 1, NULL, 0); |
|||
controller->is_interrupting = 1; |
|||
} |
|||
} |
|||
|
|||
|
|||
static void |
|||
clear_interrupt(device *me, |
|||
ide_controller *controller) |
|||
{ |
|||
if (controller->is_interrupting) { |
|||
DTRACE(ide, ("controller %d - interrupt clear\n", controller->nr)); |
|||
device_interrupt_event(me, controller->nr, 0, NULL, 0); |
|||
controller->is_interrupting = 0; |
|||
} |
|||
} |
|||
|
|||
|
|||
static void |
|||
do_event(void *data) |
|||
{ |
|||
ide_controller *controller = data; |
|||
device *me = controller->me; |
|||
controller->event_tag = 0; |
|||
switch (controller->state) { |
|||
case busy_loaded_state: |
|||
case busy_drained_state: |
|||
if (controller->current_transfer > 0) { |
|||
controller->state = (controller->state == busy_loaded_state |
|||
? loading_state : draining_state); |
|||
} |
|||
else { |
|||
controller->state = idle_state; |
|||
} |
|||
set_interrupt(me, controller); |
|||
break; |
|||
default: |
|||
device_error(me, "controller %d - unexpected event", controller->nr); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
|
|||
static void |
|||
schedule_ready_event(device *me, |
|||
ide_controller *controller) |
|||
{ |
|||
if (controller->event_tag != 0) |
|||
device_error(me, "controller %d - attempting to schedule multiple events", |
|||
controller->nr); |
|||
controller->event_tag = |
|||
device_event_queue_schedule(me, controller->ready_delay, |
|||
do_event, controller); |
|||
} |
|||
|
|||
|
|||
static void |
|||
do_fifo_read(device *me, |
|||
ide_controller *controller, |
|||
void *dest, |
|||
int nr_bytes) |
|||
{ |
|||
if (controller->state != draining_state) |
|||
device_error(me, "controller %d - reading fifo when not ready (%s)", |
|||
controller->nr, |
|||
ide_state_name(controller->state)); |
|||
if (controller->fifo_pos + nr_bytes > controller->fifo_size) |
|||
device_error(me, "controller %d - fifo underflow", controller->nr); |
|||
if (nr_bytes > 0) { |
|||
memcpy(dest, &controller->fifo[controller->fifo_pos], nr_bytes); |
|||
controller->fifo_pos += nr_bytes; |
|||
} |
|||
if (controller->fifo_pos == controller->fifo_size) { |
|||
controller->current_transfer -= 1; |
|||
if (controller->current_transfer > 0 |
|||
&& controller->current_drive != NULL) { |
|||
DTRACE(ide, ("controller %d:%d - reading %d byte block at 0x%x\n", |
|||
controller->nr, |
|||
controller->current_drive->nr, |
|||
controller->fifo_size, |
|||
controller->current_byte)); |
|||
if (device_io_read_buffer(controller->current_drive->device, |
|||
controller->fifo, |
|||
0, controller->current_byte, |
|||
controller->fifo_size, |
|||
NULL, 0) |
|||
!= controller->fifo_size) |
|||
device_error(me, "controller %d - disk %s io read error", |
|||
controller->nr, |
|||
device_path(controller->current_drive->device)); |
|||
} |
|||
controller->state = busy_drained_state; |
|||
controller->fifo_pos = 0; |
|||
controller->current_byte += controller->fifo_size; |
|||
schedule_ready_event(me, controller); |
|||
} |
|||
} |
|||
|
|||
|
|||
static void |
|||
do_fifo_write(device *me, |
|||
ide_controller *controller, |
|||
const void *source, |
|||
int nr_bytes) |
|||
{ |
|||
if (controller->state != loading_state) |
|||
device_error(me, "controller %d - writing fifo when not ready (%s)", |
|||
controller->nr, |
|||
ide_state_name(controller->state)); |
|||
if (controller->fifo_pos + nr_bytes > controller->fifo_size) |
|||
device_error(me, "controller %d - fifo overflow", controller->nr); |
|||
if (nr_bytes > 0) { |
|||
memcpy(&controller->fifo[controller->fifo_pos], source, nr_bytes); |
|||
controller->fifo_pos += nr_bytes; |
|||
} |
|||
if (controller->fifo_pos == controller->fifo_size) { |
|||
if (controller->current_transfer > 0 |
|||
&& controller->current_drive != NULL) { |
|||
DTRACE(ide, ("controller %d:%d - writing %d byte block at 0x%x\n", |
|||
controller->nr, |
|||
controller->current_drive->nr, |
|||
controller->fifo_size, |
|||
controller->current_byte)); |
|||
if (device_io_write_buffer(controller->current_drive->device, |
|||
controller->fifo, |
|||
0, controller->current_byte, |
|||
controller->fifo_size, |
|||
NULL, 0) |
|||
!= controller->fifo_size) |
|||
device_error(me, "controller %d - disk %s io write error", |
|||
controller->nr, |
|||
device_path(controller->current_drive->device)); |
|||
} |
|||
controller->current_transfer -= 1; |
|||
controller->fifo_pos = 0; |
|||
controller->current_byte += controller->fifo_size; |
|||
controller->state = busy_loaded_state; |
|||
schedule_ready_event(me, controller); |
|||
} |
|||
} |
|||
|
|||
|
|||
static void |
|||
setup_fifo(device *me, |
|||
ide_controller *controller, |
|||
int is_simple, |
|||
int is_with_disk, |
|||
io_direction direction) |
|||
{ |
|||
/* find the disk */ |
|||
if (is_with_disk) { |
|||
int drive_nr = (controller->reg[ide_drive_head_reg] & 0x10) != 0; |
|||
controller->current_drive = &controller->drive[drive_nr]; |
|||
} |
|||
else { |
|||
controller->current_drive = NULL; |
|||
} |
|||
|
|||
/* number of transfers */ |
|||
if (is_simple) |
|||
controller->current_transfer = 1; |
|||
else { |
|||
int sector_count = controller->reg[ide_sector_count_reg]; |
|||
if (sector_count == 0) |
|||
controller->current_transfer = 256; |
|||
else |
|||
controller->current_transfer = sector_count; |
|||
} |
|||
|
|||
/* the transfer size */ |
|||
if (controller->current_drive == NULL) |
|||
controller->fifo_size = 512; |
|||
else |
|||
controller->fifo_size = controller->current_drive->geometry.byte; |
|||
|
|||
/* empty the fifo */ |
|||
controller->fifo_pos = 0; |
|||
|
|||
/* the starting address */ |
|||
if (controller->current_drive == NULL) |
|||
controller->current_byte = 0; |
|||
else if (controller->reg[ide_drive_head_reg] & 0x40) { |
|||
/* LBA addressing mode */ |
|||
controller->current_byte = controller->fifo_size |
|||
* (((controller->reg[ide_drive_head_reg] & 0xf) << 24) |
|||
| (controller->reg[ide_cylinder_reg1] << 16) |
|||
| (controller->reg[ide_cylinder_reg0] << 8) |
|||
| (controller->reg[ide_sector_number_reg])); |
|||
} |
|||
else if (controller->current_drive->geometry.head != 0 |
|||
&& controller->current_drive->geometry.sector != 0) { |
|||
/* CHS addressing mode */ |
|||
int head_nr = controller->reg[ide_drive_head_reg] & 0xf; |
|||
int cylinder_nr = ((controller->reg[ide_cylinder_reg1] << 8) |
|||
| controller->reg[ide_cylinder_reg0]); |
|||
int sector_nr = controller->reg[ide_sector_number_reg]; |
|||
controller->current_byte = controller->fifo_size |
|||
* ((cylinder_nr * controller->current_drive->geometry.head + head_nr) |
|||
* controller->current_drive->geometry.sector + sector_nr - 1); |
|||
} |
|||
else |
|||
device_error(me, "controller %d:%d - CHS addressing disabled", |
|||
controller->nr, controller->current_drive->nr); |
|||
DTRACE(ide, ("controller %ld:%ld - transfer (%s) %ld blocks of %ld bytes from 0x%lx\n", |
|||
(long)controller->nr, |
|||
controller->current_drive == NULL ? -1L : (long)controller->current_drive->nr, |
|||
direction == is_read ? "read" : "write", |
|||
(long)controller->current_transfer, |
|||
(long)controller->fifo_size, |
|||
(unsigned long)controller->current_byte)); |
|||
switch (direction) { |
|||
case is_read: |
|||
/* force a primeing read */ |
|||
controller->current_transfer += 1; |
|||
controller->state = draining_state; |
|||
controller->fifo_pos = controller->fifo_size; |
|||
do_fifo_read(me, controller, NULL, 0); |
|||
break; |
|||
case is_write: |
|||
controller->state = loading_state; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
|
|||
static void |
|||
do_command(device *me, |
|||
ide_controller *controller, |
|||
int command) |
|||
{ |
|||
if (controller->state != idle_state) |
|||
device_error(me, "controller %d - command when not idle", controller->nr); |
|||
switch (command) { |
|||
case 0x20: case 0x21: /* read-sectors */ |
|||
setup_fifo(me, controller, 0/*is_simple*/, 1/*is_with_disk*/, is_read); |
|||
break; |
|||
case 0x30: case 0x31: /* write */ |
|||
setup_fifo(me, controller, 0/*is_simple*/, 1/*is_with_disk*/, is_write); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
static unsigned8 |
|||
get_status(device *me, |
|||
ide_controller *controller) |
|||
{ |
|||
switch (controller->state) { |
|||
case loading_state: |
|||
case draining_state: |
|||
return 0x08; /* data req */ |
|||
case busy_loaded_state: |
|||
case busy_drained_state: |
|||
return 0x80; /* busy */ |
|||
case idle_state: |
|||
return 0x40; /* drive ready */ |
|||
default: |
|||
device_error(me, "internal error"); |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
|
|||
/* The address presented to the IDE controler is decoded and then
|
|||
mapped onto a controller:reg pair */ |
|||
|
|||
enum { |
|||
nr_address_blocks = 6, |
|||
}; |
|||
|
|||
typedef struct _address_block { |
|||
int space; |
|||
unsigned_word base_addr; |
|||
unsigned_word bound_addr; |
|||
int controller; |
|||
int base_reg; |
|||
} address_block; |
|||
|
|||
typedef struct _address_decoder { |
|||
address_block block[nr_address_blocks]; |
|||
} address_decoder; |
|||
|
|||
static void |
|||
decode_address(device *me, |
|||
address_decoder *decoder, |
|||
int space, |
|||
unsigned_word address, |
|||
int *controller, |
|||
int *reg, |
|||
io_direction direction) |
|||
{ |
|||
int i; |
|||
for (i = 0; i < nr_address_blocks; i++) { |
|||
if (space == decoder->block[i].space |
|||
&& address >= decoder->block[i].base_addr |
|||
&& address <= decoder->block[i].bound_addr) { |
|||
*controller = decoder->block[i].controller; |
|||
*reg = (address |
|||
- decoder->block[i].base_addr |
|||
+ decoder->block[i].base_reg); |
|||
if (direction == is_write) { |
|||
switch (*reg) { |
|||
case ide_error_reg: *reg = ide_feature_reg; break; |
|||
case ide_status_reg: *reg = ide_command_reg; break; |
|||
case ide_alternate_status_reg: *reg = ide_control_reg; break; |
|||
default: break; |
|||
} |
|||
} |
|||
return; |
|||
} |
|||
} |
|||
device_error(me, "address %d:0x%lx invalid", |
|||
space, (unsigned long)address); |
|||
} |
|||
|
|||
|
|||
static void |
|||
build_address_decoder(device *me, |
|||
address_decoder *decoder) |
|||
{ |
|||
int reg; |
|||
for (reg = 1; reg < 6; reg++) { |
|||
reg_property_spec unit; |
|||
int space; |
|||
unsigned_word address; |
|||
unsigned size; |
|||
/* find and decode the reg property */ |
|||
if (!device_find_reg_array_property(me, "reg", reg, &unit)) |
|||
device_error(me, "missing or invalid reg entry %d", reg); |
|||
device_address_to_attach_address(device_parent(me), &unit.address, |
|||
&space, &address, me); |
|||
device_size_to_attach_size(device_parent(me), &unit.size, &size, me); |
|||
/* insert it into the address decoder */ |
|||
switch (reg) { |
|||
case 1: |
|||
case 2: |
|||
/* command register block */ |
|||
if (size != 8) |
|||
device_error(me, "reg entry %d must have a size of 8", reg); |
|||
decoder->block[reg-1].space = space; |
|||
decoder->block[reg-1].base_addr = address; |
|||
decoder->block[reg-1].bound_addr = address + size - 1; |
|||
decoder->block[reg-1].controller = (reg + 1) % nr_ide_controllers; |
|||
decoder->block[reg-1].base_reg = ide_data_reg; |
|||
DTRACE(ide, ("controller %d command register block at %d:0x%lx..0x%lx\n", |
|||
decoder->block[reg-1].controller, |
|||
decoder->block[reg-1].space, |
|||
(unsigned long)decoder->block[reg-1].base_addr, |
|||
(unsigned long)decoder->block[reg-1].bound_addr)); |
|||
break; |
|||
case 3: |
|||
case 4: |
|||
/* control register block */ |
|||
if (size != 1) |
|||
device_error(me, "reg entry %d must have a size of 1", reg); |
|||
decoder->block[reg-1].space = space; |
|||
decoder->block[reg-1].base_addr = address; |
|||
decoder->block[reg-1].bound_addr = address + size - 1; |
|||
decoder->block[reg-1].controller = (reg + 1) % nr_ide_controllers; |
|||
decoder->block[reg-1].base_reg = ide_alternate_status_reg; |
|||
DTRACE(ide, ("controller %d control register block at %d:0x%lx..0x%lx\n", |
|||
decoder->block[reg-1].controller, |
|||
decoder->block[reg-1].space, |
|||
(unsigned long)decoder->block[reg-1].base_addr, |
|||
(unsigned long)decoder->block[reg-1].bound_addr)); |
|||
break; |
|||
case 5: |
|||
/* dma register block */ |
|||
if (size != 8) |
|||
device_error(me, "reg entry %d must have a size of 8", reg); |
|||
decoder->block[reg-1].space = space; |
|||
decoder->block[reg-1].base_addr = address; |
|||
decoder->block[reg-1].bound_addr = address + 4 - 1; |
|||
decoder->block[reg-1].base_reg = ide_dma_command_reg; |
|||
decoder->block[reg-1].controller = 0; |
|||
DTRACE(ide, ("controller %d dma register block at %d:0x%lx..0x%lx\n", |
|||
decoder->block[reg-1].controller, |
|||
decoder->block[reg-1].space, |
|||
(unsigned long)decoder->block[reg-1].base_addr, |
|||
(unsigned long)decoder->block[reg-1].bound_addr)); |
|||
decoder->block[reg].space = space; |
|||
decoder->block[reg].base_addr = address + 4; |
|||
decoder->block[reg].bound_addr = address + 8 - 1; |
|||
decoder->block[reg].controller = 1; |
|||
decoder->block[reg].base_reg = ide_dma_command_reg; |
|||
DTRACE(ide, ("controller %d dma register block at %d:0x%lx..0x%lx\n", |
|||
decoder->block[reg].controller, |
|||
decoder->block[reg-1].space, |
|||
(unsigned long)decoder->block[reg].base_addr, |
|||
(unsigned long)decoder->block[reg].bound_addr)); |
|||
break; |
|||
default: |
|||
device_error(me, "internal error - bad switch"); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
typedef struct _hw_ide_device { |
|||
ide_controller controller[nr_ide_controllers]; |
|||
address_decoder decoder; |
|||
} hw_ide_device; |
|||
|
|||
|
|||
static void |
|||
hw_ide_init_address(device *me) |
|||
{ |
|||
hw_ide_device *ide = device_data(me); |
|||
int controller; |
|||
int drive; |
|||
|
|||
/* zero some things */ |
|||
for (controller = 0; controller < nr_ide_controllers; controller++) { |
|||
memset(&ide->controller[controller], 0, sizeof(ide_controller)); |
|||
for (drive = 0; drive < nr_ide_drives_per_controller; drive++) { |
|||
ide->controller[controller].drive[drive].nr = drive; |
|||
} |
|||
ide->controller[controller].me = me; |
|||
if (device_find_property(me, "ready-delay") != NULL) |
|||
ide->controller[controller].ready_delay = |
|||
device_find_integer_property(me, "ready-delay"); |
|||
} |
|||
|
|||
/* attach this device to its parent */ |
|||
generic_device_init_address(me); |
|||
|
|||
/* determine our own address map */ |
|||
build_address_decoder(me, &ide->decoder); |
|||
|
|||
} |
|||
|
|||
|
|||
static void |
|||
hw_ide_attach_address(device *me, |
|||
attach_type type, |
|||
int space, |
|||
unsigned_word addr, |
|||
unsigned nr_bytes, |
|||
access_type access, |
|||
device *client) /*callback/default*/ |
|||
{ |
|||
hw_ide_device *ide = (hw_ide_device*)device_data(me); |
|||
int controller_nr = addr / nr_ide_drives_per_controller; |
|||
int drive_nr = addr % nr_ide_drives_per_controller; |
|||
ide_controller *controller; |
|||
ide_drive *drive; |
|||
if (controller_nr >= nr_ide_controllers) |
|||
device_error(me, "no controller for disk %s", |
|||
device_path(client)); |
|||
|
|||
controller = &ide->controller[controller_nr]; |
|||
drive = &controller->drive[drive_nr]; |
|||
drive->device = client; |
|||
if (device_find_property(client, "ide-byte-count") != NULL) |
|||
drive->geometry.byte = device_find_integer_property(client, "ide-byte-count"); |
|||
else |
|||
drive->geometry.byte = 512; |
|||
if (device_find_property(client, "ide-sector-count") != NULL) |
|||
drive->geometry.sector = device_find_integer_property(client, "ide-sector-count"); |
|||
if (device_find_property(client, "ide-head-count") != NULL) |
|||
drive->geometry.head = device_find_integer_property(client, "ide-head-count"); |
|||
drive->default_geometry = drive->geometry; |
|||
DTRACE(ide, ("controller %d:%d %s byte-count %d, sector-count %d, head-count %d\n", |
|||
controller_nr, |
|||
drive->nr, |
|||
device_path(client), |
|||
drive->geometry.byte, |
|||
drive->geometry.sector, |
|||
drive->geometry.head)); |
|||
} |
|||
|
|||
|
|||
static unsigned |
|||
hw_ide_io_read_buffer(device *me, |
|||
void *dest, |
|||
int space, |
|||
unsigned_word addr, |
|||
unsigned nr_bytes, |
|||
cpu *processor, |
|||
unsigned_word cia) |
|||
{ |
|||
hw_ide_device *ide = (hw_ide_device *)device_data(me); |
|||
int control_nr; |
|||
int reg; |
|||
ide_controller *controller; |
|||
|
|||
/* find the interface */ |
|||
decode_address(me, &ide->decoder, space, addr, &control_nr, ®, is_read); |
|||
controller = & ide->controller[control_nr]; |
|||
|
|||
/* process the transfer */ |
|||
memset(dest, 0, nr_bytes); |
|||
switch (reg) { |
|||
case ide_data_reg: |
|||
do_fifo_read(me, controller, dest, nr_bytes); |
|||
break; |
|||
case ide_status_reg: |
|||
*(unsigned8*)dest = get_status(me, controller); |
|||
clear_interrupt(me, controller); |
|||
break; |
|||
case ide_alternate_status_reg: |
|||
*(unsigned8*)dest = get_status(me, controller); |
|||
break; |
|||
case ide_error_reg: |
|||
case ide_sector_count_reg: |
|||
case ide_sector_number_reg: |
|||
case ide_cylinder_reg0: |
|||
case ide_cylinder_reg1: |
|||
case ide_drive_head_reg: |
|||
case ide_control_reg: |
|||
case ide_dma_command_reg: |
|||
case ide_dma_status_reg: |
|||
case ide_dma_prd_table_address_reg0: |
|||
case ide_dma_prd_table_address_reg1: |
|||
case ide_dma_prd_table_address_reg2: |
|||
case ide_dma_prd_table_address_reg3: |
|||
*(unsigned8*)dest = controller->reg[reg]; |
|||
break; |
|||
default: |
|||
device_error(me, "bus-error at address 0x%lx", addr); |
|||
break; |
|||
} |
|||
return nr_bytes; |
|||
} |
|||
|
|||
|
|||
static unsigned |
|||
hw_ide_io_write_buffer(device *me, |
|||
const void *source, |
|||
int space, |
|||
unsigned_word addr, |
|||
unsigned nr_bytes, |
|||
cpu *processor, |
|||
unsigned_word cia) |
|||
{ |
|||
hw_ide_device *ide = (hw_ide_device *)device_data(me); |
|||
int control_nr; |
|||
int reg; |
|||
ide_controller *controller; |
|||
|
|||
/* find the interface */ |
|||
decode_address(me, &ide->decoder, space, addr, &control_nr, ®, is_write); |
|||
controller = &ide->controller[control_nr]; |
|||
|
|||
/* process the access */ |
|||
switch (reg) { |
|||
case ide_data_reg: |
|||
do_fifo_write(me, controller, source, nr_bytes); |
|||
break; |
|||
case ide_command_reg: |
|||
do_command(me, controller, *(unsigned8*)source); |
|||
break; |
|||
case ide_control_reg: |
|||
controller->reg[reg] = *(unsigned8*)source; |
|||
/* possibly cancel interrupts */ |
|||
if ((controller->reg[reg] & 0x02) == 0x02) |
|||
clear_interrupt(me, controller); |
|||
break; |
|||
case ide_feature_reg: |
|||
case ide_sector_count_reg: |
|||
case ide_sector_number_reg: |
|||
case ide_cylinder_reg0: |
|||
case ide_cylinder_reg1: |
|||
case ide_drive_head_reg: |
|||
case ide_dma_command_reg: |
|||
case ide_dma_status_reg: |
|||
case ide_dma_prd_table_address_reg0: |
|||
case ide_dma_prd_table_address_reg1: |
|||
case ide_dma_prd_table_address_reg2: |
|||
case ide_dma_prd_table_address_reg3: |
|||
controller->reg[reg] = *(unsigned8*)source; |
|||
break; |
|||
default: |
|||
device_error(me, "bus-error at 0x%lx", addr); |
|||
break; |
|||
} |
|||
return nr_bytes; |
|||
} |
|||
|
|||
|
|||
static const device_interrupt_port_descriptor hw_ide_interrupt_ports[] = { |
|||
{ "a", 0, 0 }, |
|||
{ "b", 1, 0 }, |
|||
{ "c", 2, 0 }, |
|||
{ "d", 3, 0 }, |
|||
{ NULL } |
|||
}; |
|||
|
|||
|
|||
|
|||
static device_callbacks const hw_ide_callbacks = { |
|||
{ hw_ide_init_address, }, |
|||
{ hw_ide_attach_address, }, /* attach */ |
|||
{ hw_ide_io_read_buffer, hw_ide_io_write_buffer, }, |
|||
{ NULL, }, /* DMA */ |
|||
{ NULL, NULL, hw_ide_interrupt_ports }, /* interrupt */ |
|||
{ generic_device_unit_decode, |
|||
generic_device_unit_encode, |
|||
generic_device_address_to_attach_address, |
|||
generic_device_size_to_attach_size }, |
|||
}; |
|||
|
|||
|
|||
static void * |
|||
hw_ide_create(const char *name, |
|||
const device_unit *unit_address, |
|||
const char *args) |
|||
{ |
|||
hw_ide_device *ide = ZALLOC(hw_ide_device); |
|||
return ide; |
|||
} |
|||
|
|||
|
|||
const device_descriptor hw_ide_device_descriptor[] = { |
|||
{ "ide", hw_ide_create, &hw_ide_callbacks }, |
|||
{ NULL, }, |
|||
}; |
|||
|
|||
#endif /* _HW_IDE_ */ |
|||
File diff suppressed because it is too large
@ -1,98 +0,0 @@ |
|||
/* 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 */ |
|||
}; |
|||
|
|||
|
|||
@ -0,0 +1,108 @@ |
|||
/* 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_TRACE_C_ |
|||
#define _HW_TRACE_C_ |
|||
|
|||
#include "device_table.h" |
|||
#include <stdarg.h> |
|||
|
|||
/* DEVICE
|
|||
|
|||
trace - the properties of this dummy device specify trace options |
|||
|
|||
DESCRIPTION |
|||
|
|||
The properties of this device are used, during initialization, to |
|||
specify the value various simulation trace options. The |
|||
initialization can occure implicitly (during device tree init) or |
|||
explicitly using this devices ioctl method. |
|||
|
|||
The actual options and their default values (for a given target) |
|||
are defined in the file debug. |
|||
|
|||
This device is normally a child of the /openprom node. |
|||
|
|||
EXAMPLE |
|||
|
|||
The trace option dump-device-tree can be enabled by specifying the |
|||
option: |
|||
|
|||
| -o '/openprom/trace/dump-device-tree 0x1' |
|||
|
|||
Alternativly the shorthand version: |
|||
|
|||
| -t dump-device-tree |
|||
|
|||
can be used. */ |
|||
|
|||
static void |
|||
hw_trace_init_data(device *me) |
|||
{ |
|||
const device_property *prop = device_find_property(me, NULL); |
|||
while (prop != NULL) { |
|||
const char *name = prop->name; |
|||
unsigned32 value = device_find_integer_property(me, name); |
|||
trace_option(name, value); |
|||
prop = device_next_property(prop); |
|||
} |
|||
} |
|||
|
|||
|
|||
/* Hook to allow the (re) initialization of the trace options at any
|
|||
time */ |
|||
|
|||
static int |
|||
hw_trace_ioctl(device *me, |
|||
cpu *processor, |
|||
unsigned_word cia, |
|||
device_ioctl_request request, |
|||
va_list ap) |
|||
{ |
|||
switch (request) { |
|||
case device_ioctl_set_trace: |
|||
hw_trace_init_data(me); |
|||
break; |
|||
default: |
|||
device_error(me, "insupported ioctl request"); |
|||
break; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
static device_callbacks const hw_trace_callbacks = { |
|||
{ NULL, hw_trace_init_data, }, /* init */ |
|||
{ NULL, }, /* address */ |
|||
{ NULL, }, /* IO */ |
|||
{ NULL, }, /* DMA */ |
|||
{ NULL, }, /* interrupt */ |
|||
{ NULL, }, /* unit */ |
|||
NULL, /* instance-create */ |
|||
hw_trace_ioctl, |
|||
}; |
|||
|
|||
const device_descriptor hw_trace_device_descriptor[] = { |
|||
{ "trace", NULL, &hw_trace_callbacks }, |
|||
{ NULL }, |
|||
}; |
|||
|
|||
#endif _HW_TRACE_C_ |
|||
@ -0,0 +1,139 @@ |
|||
/* 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 _TREE_H_ |
|||
#define _TREE_H_ |
|||
|
|||
#ifndef INLINE_TREE |
|||
#define INLINE_TREE |
|||
#endif |
|||
|
|||
/* Constructing the device tree:
|
|||
|
|||
The initial device tree populated with devices and basic properties |
|||
is created using the function <<device_tree_add_parsed()>>. This |
|||
function parses a PSIM device specification and uses it to populate |
|||
the tree accordingly. |
|||
|
|||
This function accepts a printf style formatted string as the |
|||
argument that describes the entry. Any properties or interrupt |
|||
connections added to a device tree using this function are marked |
|||
as having a permenant disposition. When the tree is (re) |
|||
initialized they will be restored to their initial value. |
|||
|
|||
*/ |
|||
|
|||
EXTERN_TREE\ |
|||
(device *) tree_parse |
|||
(device *root, |
|||
const char *fmt, |
|||
...) __attribute__ ((format (printf, 2, 3))); |
|||
|
|||
|
|||
INLINE_TREE\ |
|||
(void) tree_usage |
|||
(int verbose); |
|||
|
|||
INLINE_TREE\ |
|||
(void) tree_print |
|||
(device *root); |
|||
|
|||
INLINE_TREE\ |
|||
(device_instance*) tree_instance |
|||
(device *root, |
|||
const char *device_specifier); |
|||
|
|||
|
|||
/* Tree traversal::
|
|||
|
|||
The entire device tree can be traversed using the |
|||
<<device_tree_traverse()>> function. The traversal can be in |
|||
either pre- or postfix order. |
|||
|
|||
*/ |
|||
|
|||
typedef void (tree_traverse_function) |
|||
(device *device, |
|||
void *data); |
|||
|
|||
INLINE_DEVICE\ |
|||
(void) tree_traverse |
|||
(device *root, |
|||
tree_traverse_function *prefix, |
|||
tree_traverse_function *postfix, |
|||
void *data); |
|||
|
|||
|
|||
/* Tree lookup::
|
|||
|
|||
The function <<tree_find_device()>> will attempt to locate |
|||
the specified device within the tree. If the device is not found a |
|||
NULL device is returned. |
|||
|
|||
*/ |
|||
|
|||
INLINE_TREE\ |
|||
(device *) tree_find_device |
|||
(device *root, |
|||
const char *path); |
|||
|
|||
|
|||
INLINE_TREE\ |
|||
(const device_property *) tree_find_property |
|||
(device *root, |
|||
const char *path_to_property); |
|||
|
|||
INLINE_TREE\ |
|||
(int) tree_find_boolean_property |
|||
(device *root, |
|||
const char *path_to_property); |
|||
|
|||
INLINE_TREE\ |
|||
(signed_cell) tree_find_integer_property |
|||
(device *root, |
|||
const char *path_to_property); |
|||
|
|||
INLINE_TREE\ |
|||
(device_instance *) tree_find_ihandle_property |
|||
(device *root, |
|||
const char *path_to_property); |
|||
|
|||
INLINE_TREE\ |
|||
(const char *) tree_find_string_property |
|||
(device *root, |
|||
const char *path_to_property); |
|||
|
|||
|
|||
/* Initializing the created tree:
|
|||
|
|||
Once a device tree has been created the <<device_tree_init()>> |
|||
function is used to initialize it. The exact sequence of events |
|||
that occure during initialization are described separatly. |
|||
|
|||
*/ |
|||
|
|||
INLINE_TREE\ |
|||
(void) tree_init |
|||
(device *root, |
|||
psim *system); |
|||
|
|||
|
|||
#endif /* _TREE_H_ */ |
|||
Loading…
Reference in new issue