877 lines
24 KiB
Text
877 lines
24 KiB
Text
|
/* 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 _DEVICE_H_
|
|||
|
#define _DEVICE_H_
|
|||
|
|
|||
|
#ifndef INLINE_DEVICE
|
|||
|
#define INLINE_DEVICE
|
|||
|
#endif
|
|||
|
|
|||
|
/* declared in basics.h, this object is used everywhere */
|
|||
|
/* typedef struct _device device; */
|
|||
|
|
|||
|
|
|||
|
/* Device templates:
|
|||
|
|
|||
|
*** THIS SECTION DESCRIBES HOW A DEVICE HAS A STATIC AND DYNAMIC
|
|||
|
COMPONENT ** on the device in the tree is dynamic. *****
|
|||
|
|
|||
|
A device node is created from its template. The only valid
|
|||
|
operation on a template is to create a device node from it: */
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(device *) device_template_create_device
|
|||
|
(device *parent,
|
|||
|
const char *name,
|
|||
|
const char *unit_address,
|
|||
|
const char *args);
|
|||
|
|
|||
|
/* The create is paramaterized by both the devices unit address (a
|
|||
|
string that is converted into numeric form by the devices parent)
|
|||
|
and optionally extra argument information.
|
|||
|
|
|||
|
The actual device node is constructed by a number of pieces provided
|
|||
|
by the template function: */
|
|||
|
|
|||
|
typedef struct _device_callbacks device_callbacks;
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(device *) device_create_from
|
|||
|
(const char *name,
|
|||
|
const device_unit *unit_address,
|
|||
|
void *data,
|
|||
|
const device_callbacks *callbacks,
|
|||
|
device *parent);
|
|||
|
|
|||
|
/* OpenBoot discusses the creation of packages (devices). */
|
|||
|
|
|||
|
|
|||
|
/* Devices:
|
|||
|
|
|||
|
As with OpenBoot, all nodes in the device tree are considered to be
|
|||
|
devices. Each node then has associated with it a number of methods
|
|||
|
and properties (duscussed later).
|
|||
|
|
|||
|
OpenBoot documentation refers to devices, device nodes, packages,
|
|||
|
package instances, methods, static methods and properties. This
|
|||
|
device implementation uses its own termonology. Where ever it
|
|||
|
exists, the notes will indicate a correspondance between PSIM terms
|
|||
|
and those found in OpenBoot.
|
|||
|
|
|||
|
device:
|
|||
|
|
|||
|
A device is the basic building block in this model. A device can
|
|||
|
be further categorized into one of three classes - template, node
|
|||
|
and instance.
|
|||
|
|
|||
|
device-node (aka device):
|
|||
|
|
|||
|
The device tree is constructed from device-nodes. Each node has
|
|||
|
both local state (data), a relationship with the device nodes
|
|||
|
around it and an address (unit-address) on the parents bus `bus' */
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(device *) device_parent
|
|||
|
(device *me);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(device *) device_sibling
|
|||
|
(device *me);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(device *) device_child
|
|||
|
(device *me);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(const char *) device_name
|
|||
|
(device *me);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(const char *) device_path
|
|||
|
(device *me);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(void *) device_data
|
|||
|
(device *me);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(psim *) device_system
|
|||
|
(device *me);
|
|||
|
|
|||
|
typedef struct _device_unit {
|
|||
|
int nr_cells;
|
|||
|
unsigned32 cells[4]; /* unused cells are zero */
|
|||
|
} device_unit;
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(const device_unit *) device_unit_address
|
|||
|
(device *me);
|
|||
|
|
|||
|
/* Each device-node normally corresponds to a hardware component of
|
|||
|
the system being modeled. Leaf nodes matching external devices and
|
|||
|
intermediate nodes matching bridges and controllers.
|
|||
|
|
|||
|
Device nodes also support methods that are an abstraction of the
|
|||
|
transactions that occure in real hardware. These operations
|
|||
|
(io/dma read/writes and interrupts) are discussed separatly.
|
|||
|
|
|||
|
OpenBoot refers to device nodes by many names. The most common are
|
|||
|
device, device node and package. */
|
|||
|
|
|||
|
|
|||
|
/* Properties:
|
|||
|
|
|||
|
In IEEE1275 many of the the characteristics of a device are stored
|
|||
|
in the device tree as properties. Each property consists of a name
|
|||
|
and an associated (implicitly typed) value. A device will have a
|
|||
|
list of properties attached to it. The user is able to manipulate
|
|||
|
the list, adding and removing properties and set/modify the value
|
|||
|
of each property.
|
|||
|
|
|||
|
PSIM's device tree follows this model but with the addition of
|
|||
|
strongly typing each property's value. The simulator will detect
|
|||
|
at run time, the incorrect use of a property.
|
|||
|
|
|||
|
In addition to the standard use of properties, Both PSIM and
|
|||
|
individual devices will use properties to record simulation
|
|||
|
configuration information. For instance, a disk device might store
|
|||
|
in a string property called <<file>> the name of the file that
|
|||
|
contains the disk image to use. */
|
|||
|
|
|||
|
/* The following are valid property types. The property `array' is a
|
|||
|
for generic untyped data. */
|
|||
|
|
|||
|
typedef enum {
|
|||
|
array_property,
|
|||
|
boolean_property,
|
|||
|
ihandle_property,
|
|||
|
integer_property,
|
|||
|
string_property,
|
|||
|
} device_property_type;
|
|||
|
|
|||
|
typedef struct _device_property device_property;
|
|||
|
struct _device_property {
|
|||
|
device *owner;
|
|||
|
const char *name;
|
|||
|
device_property_type type;
|
|||
|
unsigned sizeof_array;
|
|||
|
const void *array;
|
|||
|
const device_property *original;
|
|||
|
object_disposition disposition;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/* iterate through the properties attached to a device */
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(const device_property *) device_next_property
|
|||
|
(const device_property *previous);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(const device_property *) device_find_property
|
|||
|
(device *me,
|
|||
|
const char *property); /* NULL for first property */
|
|||
|
|
|||
|
|
|||
|
/* Manipulate the properties belonging to a given device.
|
|||
|
|
|||
|
SET on the other hand will force the properties value. The
|
|||
|
simulation is aborted if the property was present but of a
|
|||
|
conflicting type.
|
|||
|
|
|||
|
FIND returns the specified properties value, aborting the
|
|||
|
simulation if the property is missing. Code locating a property
|
|||
|
should first check its type (using device_find_property above) and
|
|||
|
then obtain its value using the below. */
|
|||
|
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(void) device_set_array_property
|
|||
|
(device *me,
|
|||
|
const char *property,
|
|||
|
const void *array,
|
|||
|
int sizeof_array);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(const device_property *) device_find_array_property
|
|||
|
(device *me,
|
|||
|
const char *property);
|
|||
|
|
|||
|
|
|||
|
#if 0
|
|||
|
INLINE_DEVICE\
|
|||
|
(void) device_set_boolean_property
|
|||
|
(device *me,
|
|||
|
const char *property,
|
|||
|
int bool);
|
|||
|
#endif
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(int) device_find_boolean_property
|
|||
|
(device *me,
|
|||
|
const char *property);
|
|||
|
|
|||
|
|
|||
|
#if 0
|
|||
|
INLINE_DEVICE\
|
|||
|
(void) device_set_ihandle_property
|
|||
|
(device *me,
|
|||
|
const char *property,
|
|||
|
device_instance *ihandle);
|
|||
|
#endif
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(device_instance *) device_find_ihandle_property
|
|||
|
(device *me,
|
|||
|
const char *property);
|
|||
|
|
|||
|
|
|||
|
#if 0
|
|||
|
INLINE_DEVICE\
|
|||
|
(void) device_set_integer_property
|
|||
|
(device *me,
|
|||
|
const char *property,
|
|||
|
signed_word integer);
|
|||
|
#endif
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(signed_word) device_find_integer_property
|
|||
|
(device *me,
|
|||
|
const char *property);
|
|||
|
|
|||
|
|
|||
|
#if 0
|
|||
|
INLINE_DEVICE\
|
|||
|
(void) device_set_string_property
|
|||
|
(device *me,
|
|||
|
const char *property,
|
|||
|
const char *string);
|
|||
|
#endif
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(const char *) device_find_string_property
|
|||
|
(device *me,
|
|||
|
const char *property);
|
|||
|
|
|||
|
|
|||
|
/* Instances:
|
|||
|
|
|||
|
As with IEEE1275, a device can be opened, creating an instance.
|
|||
|
Instances provide more abstract interfaces to the underlying
|
|||
|
hardware. For example, the instance methods for a disk may include
|
|||
|
code that is able to interpret file systems found on disks. Such
|
|||
|
methods would there for allow the manipulation of files on the
|
|||
|
disks file system. The operations would be implemented using the
|
|||
|
basic block I/O model provided by the disk.
|
|||
|
|
|||
|
This model includes methods that faciliate the creation of device
|
|||
|
instance and (should a given device support it) standard operations
|
|||
|
on those instances. */
|
|||
|
|
|||
|
*** device-instance ***
|
|||
|
|
|||
|
Devices support an abstract I/O model. A unique I/O instance can be
|
|||
|
created from a device node and then this instance used to perform
|
|||
|
I/O that is independant of other instances. */
|
|||
|
|
|||
|
typedef struct _device_instance_callbacks device_instance_callbacks;
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(device_instance *) device_create_instance_from
|
|||
|
(device *me, /*OR*/ device_instance *parent,
|
|||
|
void *data,
|
|||
|
const char *path,
|
|||
|
const char *args,
|
|||
|
const device_instance_callbacks *callbacks);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(device_instance *) device_create_instance
|
|||
|
(device *me,
|
|||
|
const char *device_specifier);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(void) device_instance_delete
|
|||
|
(device_instance *instance);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(int) device_instance_read
|
|||
|
(device_instance *instance,
|
|||
|
void *addr,
|
|||
|
unsigned_word len);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(int) device_instance_write
|
|||
|
(device_instance *instance,
|
|||
|
const void *addr,
|
|||
|
unsigned_word len);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(int) device_instance_seek
|
|||
|
(device_instance *instance,
|
|||
|
unsigned_word pos_hi,
|
|||
|
unsigned_word pos_lo);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(unsigned_word) device_instance_claim
|
|||
|
(device_instance *instance,
|
|||
|
unsigned_word address,
|
|||
|
unsigned_word length,
|
|||
|
unsigned_word alignment);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(void) device_instance_release
|
|||
|
(device_instance *instance,
|
|||
|
unsigned_word address,
|
|||
|
unsigned_word length);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(device *) device_instance_device
|
|||
|
(device_instance *instance);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(const char *) device_instance_path
|
|||
|
(device_instance *instance);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(void *) device_instance_data
|
|||
|
(device_instance *instance);
|
|||
|
|
|||
|
/* A device instance can be marked (when created) as being permenant.
|
|||
|
Such instances are assigned a reserved address and are *not*
|
|||
|
deleted between simulation runs.
|
|||
|
|
|||
|
OpenBoot refers to a device instace as a package instance */
|
|||
|
|
|||
|
|
|||
|
/* PIO:
|
|||
|
|
|||
|
*** DESCRIBE HERE WHAT A PIO OPERATION IS and how, broadly it is
|
|||
|
modeled ****
|
|||
|
|
|||
|
|
|||
|
During initialization, each device attaches its self to is parent
|
|||
|
registering the address spaces that it is interested in:
|
|||
|
|
|||
|
a. The <<com>> device attaches its self to its parent <<phb>>
|
|||
|
device at address <<0x3f8>> through to address <<0x3f8 + 16>>.
|
|||
|
|
|||
|
b. The <<phb>> has in turn attached its self to addresses
|
|||
|
<<0xf0000000 .. 0xf0100000>>.
|
|||
|
|
|||
|
During the execution of the simulation propper, the following then
|
|||
|
occure:
|
|||
|
|
|||
|
1. After any virtual to physical translation, the processor
|
|||
|
passes the address to be read (or written to the core device).
|
|||
|
(eg address 0xf00003f8).
|
|||
|
|
|||
|
2. The core device then looks up the specified addresses in its
|
|||
|
address to device map, determines that in this case the address
|
|||
|
belongs to the phb and passes it down.
|
|||
|
|
|||
|
3. The <<phb>> in turn determines that the address belongs to the
|
|||
|
serial port and passes to that device the request for an access
|
|||
|
to location <<0x3f8>>.
|
|||
|
|
|||
|
@figure mio
|
|||
|
|
|||
|
*/
|
|||
|
|
|||
|
/* Device Hardware
|
|||
|
|
|||
|
This model assumes that the data paths of the system being modeled
|
|||
|
have a tree topology. That is, one or more processors sit at the
|
|||
|
top of a tree. That tree containing leaf nodes (real devices) and
|
|||
|
branch nodes (bridges).
|
|||
|
|
|||
|
For instance, consider the tree:
|
|||
|
|
|||
|
/pci # PCI-HOST bridge
|
|||
|
/pci/pci1000,1@1 # A pci controller
|
|||
|
/pci/isa8086 # PCI-ISA bridge
|
|||
|
/pci/isa8086/fdc@300 # floppy disk controller on ISA bus
|
|||
|
|
|||
|
A processor needing to access the device fdc@300 on the ISA bus
|
|||
|
would do so using a data path that goes through the pci-host bridge
|
|||
|
(pci)and the isa-pci bridge (isa8086) to finally reach the device
|
|||
|
fdc@300. As the data transfer passes through each intermediate
|
|||
|
bridging node that bridge device is able to (just like with real
|
|||
|
hardware) manipulate either the address or data involved in the
|
|||
|
transfer. */
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(unsigned) device_io_read_buffer
|
|||
|
(device *me,
|
|||
|
void *dest,
|
|||
|
int space,
|
|||
|
unsigned_word addr,
|
|||
|
unsigned nr_bytes,
|
|||
|
cpu *processor,
|
|||
|
unsigned_word cia);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(unsigned) device_io_write_buffer
|
|||
|
(device *me,
|
|||
|
const void *source,
|
|||
|
int space,
|
|||
|
unsigned_word addr,
|
|||
|
unsigned nr_bytes,
|
|||
|
cpu *processor,
|
|||
|
unsigned_word cia);
|
|||
|
|
|||
|
/* To avoid the need for an intermediate (bridging) node to ask each
|
|||
|
of its child devices in turn if an IO access is intended for them,
|
|||
|
parent nodes maintain a table mapping addresses directly to
|
|||
|
specific devices. When a device is `connected' to its bus it
|
|||
|
attaches its self to its parent. */
|
|||
|
|
|||
|
/* Address access attributes */
|
|||
|
typedef enum _access_type {
|
|||
|
access_invalid = 0,
|
|||
|
access_read = 1,
|
|||
|
access_write = 2,
|
|||
|
access_read_write = 3,
|
|||
|
access_exec = 4,
|
|||
|
access_read_exec = 5,
|
|||
|
access_write_exec = 6,
|
|||
|
access_read_write_exec = 7,
|
|||
|
} access_type;
|
|||
|
|
|||
|
/* Address attachement types */
|
|||
|
typedef enum _attach_type {
|
|||
|
attach_invalid,
|
|||
|
attach_raw_memory,
|
|||
|
attach_callback,
|
|||
|
/* ... */
|
|||
|
} attach_type;
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(void) device_attach_address
|
|||
|
(device *me,
|
|||
|
const char *name,
|
|||
|
attach_type attach,
|
|||
|
int space,
|
|||
|
unsigned_word addr,
|
|||
|
unsigned nr_bytes,
|
|||
|
access_type access,
|
|||
|
device *who); /*callback/default*/
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(void) device_detach_address
|
|||
|
(device *me,
|
|||
|
const char *name,
|
|||
|
attach_type attach,
|
|||
|
int space,
|
|||
|
unsigned_word addr,
|
|||
|
unsigned nr_bytes,
|
|||
|
access_type access,
|
|||
|
device *who); /*callback/default*/
|
|||
|
|
|||
|
/* where the attached address space can be any of
|
|||
|
|
|||
|
callback - all accesses to that range of addresses are past on to
|
|||
|
the attached child device. The callback addresses are ordered
|
|||
|
according to the callback level (attach_callback, .. + 1, .. + 2,
|
|||
|
...). Lower levels are searched first. This facilitates the
|
|||
|
implementation of more unusual addressing schema such as
|
|||
|
subtractive decoding (as seen on the PCI bus). Within a given
|
|||
|
callback level addresses must not overlap.
|
|||
|
|
|||
|
memory - the specified address space contains RAM, the node that is
|
|||
|
having the ram attached is responsible for allocating space for and
|
|||
|
maintaining that space. The device initiating the attach will not
|
|||
|
be notified of accesses to such an attachement.
|
|||
|
|
|||
|
The memory attachment is very important. By giving the parent node
|
|||
|
the responsability (and freedom) of managing the RAM, that node is
|
|||
|
able to implement memory spaces more efficiently. For instance it
|
|||
|
could `cache' accesses or merge adjacent memory areas.
|
|||
|
|
|||
|
|
|||
|
In addition to I/O and DMA, devices interact with the rest of the
|
|||
|
system via interrupts. Interrupts are discussed separatly. */
|
|||
|
|
|||
|
|
|||
|
/* DMA:
|
|||
|
|
|||
|
*** DESCRIBE HERE WHAT A DMA OPERATION IS AND HOW IT IS MODELED,
|
|||
|
include an interation of an access being reflected back down ***
|
|||
|
|
|||
|
*/
|
|||
|
|
|||
|
/* Conversly, the device pci1000,1@1 my need to perform a dma transfer
|
|||
|
into the cpu/memory core. Just as I/O moves towards the leaves,
|
|||
|
dma transfers move towards the core via the initiating devices
|
|||
|
parent nodes. The root device (special) converts the DMA transfer
|
|||
|
into reads/writes to memory */
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(unsigned) device_dma_read_buffer
|
|||
|
(device *me,
|
|||
|
void *dest,
|
|||
|
int space,
|
|||
|
unsigned_word addr,
|
|||
|
unsigned nr_bytes);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(unsigned) device_dma_write_buffer
|
|||
|
(device *me,
|
|||
|
const void *source,
|
|||
|
int space,
|
|||
|
unsigned_word addr,
|
|||
|
unsigned nr_bytes,
|
|||
|
int violate_read_only_section);
|
|||
|
|
|||
|
|
|||
|
/* Interrupts:
|
|||
|
|
|||
|
*** DESCRIBE HERE THE INTERRUPT NETWORK ***
|
|||
|
|
|||
|
PSIM models interrupts and their wiring as a directed graph of
|
|||
|
connections between interrupt sources and destinations. The source
|
|||
|
and destination are both a tupple consisting of a port number and
|
|||
|
device. Both multiple destinations attached to a single source and
|
|||
|
multiple sources attached to a single destination are allowed.
|
|||
|
|
|||
|
When a device drives an interrupt port with multiple destinations a
|
|||
|
broadcast of that interrupt event (message to all destinations)
|
|||
|
occures. Each of those destination (device/port) are able to
|
|||
|
further propogate the interrupt until it reaches its ultimate
|
|||
|
destination.
|
|||
|
|
|||
|
Normally an interrupt source would be a model of a real device
|
|||
|
(such as a keyboard) while an interrupt destination would be an
|
|||
|
interrupt controller. The facility that allows an interrupt to be
|
|||
|
delivered to multiple devices and to be propogated from device to
|
|||
|
device was designed to support the requirements specified by
|
|||
|
OpenPIC (ISA interrupts go to both OpenPIC and 8259), CHRP (8259
|
|||
|
connected to OpenPIC) and hardware designs such as PCI-PCI
|
|||
|
bridges. */
|
|||
|
|
|||
|
|
|||
|
/* Interrupting a processor
|
|||
|
|
|||
|
The cpu object provides methods for delivering external interrupts
|
|||
|
to a given processor.
|
|||
|
|
|||
|
The problem of synchronizing external interrupt delivery with the
|
|||
|
execution of the cpu is handled internally by the processor object. */
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* Interrupt Source
|
|||
|
|
|||
|
A device drives its interrupt line using the call: */
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(void) device_interrupt_event
|
|||
|
(device *me,
|
|||
|
int my_port,
|
|||
|
int value,
|
|||
|
cpu *processor,
|
|||
|
unsigned_word cia);
|
|||
|
|
|||
|
/* This interrupt event will then be propogated to any attached
|
|||
|
interrupt destinations.
|
|||
|
|
|||
|
Any interpretation of PORT and VALUE is model dependant. However
|
|||
|
as guidelines the following are recommended: PCI interrupts a-d
|
|||
|
correspond to lines 0-3; level sensative interrupts be requested
|
|||
|
with a value of one and withdrawn with a value of 0; edge sensative
|
|||
|
interrupts always have a value of 1, the event its self is treated
|
|||
|
as the interrupt.
|
|||
|
|
|||
|
|
|||
|
Interrupt Destinations
|
|||
|
|
|||
|
Attached to each interrupt line of a device can be zero or more
|
|||
|
desitinations. These destinations consist of a device/port pair.
|
|||
|
A destination is attached/detached to a device line using the
|
|||
|
attach and detach calls. */
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(void) device_interrupt_attach
|
|||
|
(device *me,
|
|||
|
int my_port,
|
|||
|
device *dest,
|
|||
|
int dest_port,
|
|||
|
object_disposition disposition);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(void) device_interrupt_detach
|
|||
|
(device *me,
|
|||
|
int my_port,
|
|||
|
device *dest,
|
|||
|
int dest_port);
|
|||
|
|
|||
|
/* DESTINATION is attached (detached) to LINE of the device ME
|
|||
|
|
|||
|
|
|||
|
Interrupt conversion
|
|||
|
|
|||
|
Users refer to interrupt port numbers symbolically. For instance a
|
|||
|
device may refer to its `INT' signal which is internally
|
|||
|
represented by port 3.
|
|||
|
|
|||
|
To convert to/from the symbolic and internal representation of a
|
|||
|
port name/number. The following functions are available. */
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(int) device_interrupt_decode
|
|||
|
(device *me,
|
|||
|
const char *symbolic_name);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(int) device_interrupt_encode
|
|||
|
(device *me,
|
|||
|
int port_number,
|
|||
|
char *buf,
|
|||
|
int sizeof_buf);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* Initialization:
|
|||
|
|
|||
|
In PSIM, the device tree is created and then initialized in stages.
|
|||
|
When using devices it is important to be clear what initialization
|
|||
|
the simulator assumes is being performed during each of these
|
|||
|
stages.
|
|||
|
|
|||
|
Firstly, each device is created in isolation (using the create from
|
|||
|
template method). Only after it has been created will a device be
|
|||
|
inserted into the tree ready for initialization.
|
|||
|
|
|||
|
Once the tree is created, it is initialized as follows:
|
|||
|
|
|||
|
1. All properties (apart from those containing instances)
|
|||
|
are (re)initialized
|
|||
|
|
|||
|
2. Any interrupts addeded as part of the simulation run
|
|||
|
are removed.
|
|||
|
|
|||
|
4. The initialize address method of each device (in top
|
|||
|
down order) is called. At this stage the device
|
|||
|
is expected to:
|
|||
|
|
|||
|
o Clear address maps and delete allocated memory
|
|||
|
associated with the devices children.
|
|||
|
|
|||
|
o (Re)attach its own addresses to its parent device.
|
|||
|
|
|||
|
o Ensure that it is otherwize sufficiently
|
|||
|
initialized such that it is ready for a
|
|||
|
device instance create call.
|
|||
|
|
|||
|
5. All properties containing an instance of
|
|||
|
a device are (re)initialized
|
|||
|
|
|||
|
6. The initialize data method for each device is called (in
|
|||
|
top down) order. At this stage the device is expected to:
|
|||
|
|
|||
|
o Perform any needed data transfers. Such
|
|||
|
transfers would include the initialization
|
|||
|
of memory created during the address initialization
|
|||
|
stage using DMA.
|
|||
|
|
|||
|
*/
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(void) device_tree_init
|
|||
|
(device *root,
|
|||
|
psim *system);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* IOCTL:
|
|||
|
|
|||
|
Very simply, a catch all for any thing that turns up that until now
|
|||
|
either hasn't been thought of or doesn't justify an extra function. */
|
|||
|
|
|||
|
EXTERN_DEVICE\
|
|||
|
(int) device_ioctl
|
|||
|
(device *me,
|
|||
|
cpu *processor,
|
|||
|
unsigned_word cia,
|
|||
|
...);
|
|||
|
|
|||
|
|
|||
|
/* External communcation:
|
|||
|
|
|||
|
Devices interface to the external environment */
|
|||
|
|
|||
|
/* device_error() reports the problem to the console and aborts the
|
|||
|
simulation. The error message is prefixed with the name of the
|
|||
|
reporting device. */
|
|||
|
|
|||
|
EXTERN_DEVICE\
|
|||
|
(void volatile) device_error
|
|||
|
(device *me,
|
|||
|
const char *fmt,
|
|||
|
...) __attribute__ ((format (printf, 2, 3)));
|
|||
|
|
|||
|
|
|||
|
/* Tree utilities:
|
|||
|
|
|||
|
In addition to the standard method of creating a device from a
|
|||
|
device template, the following sortcuts can be used.
|
|||
|
|
|||
|
Create a device or property from a textual representation */
|
|||
|
|
|||
|
EXTERN_DEVICE\
|
|||
|
(device *) device_tree_add_parsed
|
|||
|
(device *current,
|
|||
|
const char *fmt,
|
|||
|
...) __attribute__ ((format (printf, 2, 3)));
|
|||
|
|
|||
|
/* where FMT,... once formatted (using vsprintf) is used to locate and
|
|||
|
create either a device or property. Its syntax is almost identical
|
|||
|
to that used in OpenBoot documentation - the only extension is in
|
|||
|
allowing properties and their values to be specified vis:
|
|||
|
|
|||
|
"/pci/pci1000,1@1/disk@0,0"
|
|||
|
|
|||
|
Path:
|
|||
|
|
|||
|
The path to a device or property can either be absolute (leading
|
|||
|
`/') or relative (leading `.' or `..'). Relative paths start from
|
|||
|
the CURRENT node. The new current node is returned as the result.
|
|||
|
In addition, a path may start with a leading alias (resolved by
|
|||
|
looking in /aliases).
|
|||
|
|
|||
|
Device name:
|
|||
|
|
|||
|
<name> "@" <unit> [ ":" <args> ]
|
|||
|
|
|||
|
Where <name> is the name of the template device, <unit> is a
|
|||
|
textual specification of the devices unit address (that is
|
|||
|
converted into a numeric form by the devices parent) and <args> are
|
|||
|
optional additional information to be passed to the device-template
|
|||
|
when it creates the device.
|
|||
|
|
|||
|
Properties:
|
|||
|
|
|||
|
Properties are specified in a similar way to devices except that
|
|||
|
the last element on the path (which would have been the device) is
|
|||
|
the property name. This path is then followed by the property
|
|||
|
value. Unlike OpenBoot, the property values in the device tree are
|
|||
|
strongly typed.
|
|||
|
|
|||
|
String property:
|
|||
|
|
|||
|
<property-name> " " <text>
|
|||
|
<property-name> " " "\"" <text>
|
|||
|
|
|||
|
Boolean property:
|
|||
|
|
|||
|
<property-name> " " [ "true" | "false" ]
|
|||
|
Integer property or integer array property:
|
|||
|
|
|||
|
<property-name> " " <number> { <number> }
|
|||
|
|
|||
|
Phandle property:
|
|||
|
|
|||
|
<property-name> " " "&" <path-to-device>
|
|||
|
|
|||
|
Ihandle property:
|
|||
|
|
|||
|
<property-name> " " "*" <path-to-device-to-open>
|
|||
|
|
|||
|
Duplicate existing property:
|
|||
|
|
|||
|
<property-name> " " "!" <path-to-original-property>
|
|||
|
|
|||
|
|
|||
|
In addition to properties, the wiring of interrupts can be
|
|||
|
specified:
|
|||
|
|
|||
|
Attach interrupt <line> of <device> to <controller>:
|
|||
|
|
|||
|
<device> " " ">" <my-port> <dest-port> <dest-device>
|
|||
|
|
|||
|
|
|||
|
Once created, a device tree can be traversed in various orders: */
|
|||
|
|
|||
|
typedef void (device_tree_traverse_function)
|
|||
|
(device *device,
|
|||
|
void *data);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(void) device_tree_traverse
|
|||
|
(device *root,
|
|||
|
device_tree_traverse_function *prefix,
|
|||
|
device_tree_traverse_function *postfix,
|
|||
|
void *data);
|
|||
|
|
|||
|
/* Or dumped out in a format that can be read back in using
|
|||
|
device_add_parsed() */
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(void) device_tree_print_device
|
|||
|
(device *device,
|
|||
|
void *ignore_data_argument);
|
|||
|
|
|||
|
/* Individual nodes can be located using */
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(device *) device_tree_find_device
|
|||
|
(device *root,
|
|||
|
const char *path);
|
|||
|
|
|||
|
/* And the current list of devices can be listed */
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(void) device_usage
|
|||
|
(int verbose);
|
|||
|
|
|||
|
|
|||
|
/* ihandles and phandles:
|
|||
|
|
|||
|
Both device nodes and device instances, in OpenBoot firmware have
|
|||
|
an external representation (phandles and ihandles) and these values
|
|||
|
are both stored in the device tree in property nodes and passed
|
|||
|
between the client program and the simulator during emulation
|
|||
|
calls.
|
|||
|
|
|||
|
To limit the potential risk associated with trusing `data' from the
|
|||
|
client program, the following mapping operators `safely' convert
|
|||
|
between the two representations: */
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(device *) external_to_device
|
|||
|
(device *tree_member,
|
|||
|
unsigned32 phandle);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(unsigned32) device_to_external
|
|||
|
(device *me);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(device_instance *) external_to_device_instance
|
|||
|
(device *tree_member,
|
|||
|
unsigned32 ihandle);
|
|||
|
|
|||
|
INLINE_DEVICE\
|
|||
|
(unsigned32) device_instance_to_external
|
|||
|
(device_instance *me);
|
|||
|
|
|||
|
#endif /* _DEVICE_H_ */
|