166e63ce3Schristos /* This file is part of the program psim.
266e63ce3Schristos
366e63ce3Schristos Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
466e63ce3Schristos
566e63ce3Schristos This program is free software; you can redistribute it and/or modify
666e63ce3Schristos it under the terms of the GNU General Public License as published by
7*48596154Schristos the Free Software Foundation; either version 3 of the License, or
866e63ce3Schristos (at your option) any later version.
966e63ce3Schristos
1066e63ce3Schristos This program is distributed in the hope that it will be useful,
1166e63ce3Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
1266e63ce3Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1366e63ce3Schristos GNU General Public License for more details.
1466e63ce3Schristos
1566e63ce3Schristos You should have received a copy of the GNU General Public License
16*48596154Schristos along with this program; if not, see <http://www.gnu.org/licenses/>.
1766e63ce3Schristos
1866e63ce3Schristos */
1966e63ce3Schristos
2066e63ce3Schristos
2166e63ce3Schristos #ifndef _HW_GLUE_C_
2266e63ce3Schristos #define _HW_GLUE_C_
2366e63ce3Schristos
2466e63ce3Schristos #include "device_table.h"
2566e63ce3Schristos
2666e63ce3Schristos
2766e63ce3Schristos /* DEVICE
2866e63ce3Schristos
2966e63ce3Schristos
3066e63ce3Schristos glue - glue to interconnect and test interrupts
3166e63ce3Schristos
3266e63ce3Schristos
3366e63ce3Schristos DESCRIPTION
3466e63ce3Schristos
3566e63ce3Schristos
3666e63ce3Schristos The glue device provides two functions. Firstly, it provides a
3766e63ce3Schristos mechanism for inspecting and driving the interrupt net. Secondly,
3866e63ce3Schristos it provides a set of boolean primitives that can be used add
3966e63ce3Schristos combinatorial operations to the interrupt network.
4066e63ce3Schristos
4166e63ce3Schristos Glue devices have a variable number of big endian <<output>>
4266e63ce3Schristos registers. Each host-word size. The registers can be both read
4366e63ce3Schristos and written.
4466e63ce3Schristos
4566e63ce3Schristos Writing a value to an output register causes an interrupt (of the
4666e63ce3Schristos specified level) to be driven on the devices corresponding output
4766e63ce3Schristos interrupt port.
4866e63ce3Schristos
4966e63ce3Schristos Reading an <<output>> register returns either the last value
5066e63ce3Schristos written or the most recently computed value (for that register) as
5166e63ce3Schristos a result of an interrupt ariving (which ever was computed last).
5266e63ce3Schristos
5366e63ce3Schristos At present the following sub device types are available:
5466e63ce3Schristos
5566e63ce3Schristos <<glue>>: In addition to driving its output interrupt port with any
5666e63ce3Schristos value written to an interrupt input port is stored in the
5766e63ce3Schristos corresponding <<output>> register. Such input interrupts, however,
5866e63ce3Schristos are not propogated to an output interrupt port.
5966e63ce3Schristos
6066e63ce3Schristos <<glue-and>>: The bit-wise AND of the interrupt inputs is computed
6166e63ce3Schristos and then both stored in <<output>> register zero and propogated to
6266e63ce3Schristos output interrupt output port zero.
6366e63ce3Schristos
6466e63ce3Schristos
6566e63ce3Schristos PROPERTIES
6666e63ce3Schristos
6766e63ce3Schristos
6866e63ce3Schristos reg = <address> <size> (required)
6966e63ce3Schristos
7066e63ce3Schristos Specify the address (within the parent bus) that this device is to
7166e63ce3Schristos live. The address must be 2048 * sizeof(word) (8k in a 32bit
7266e63ce3Schristos simulation) aligned.
7366e63ce3Schristos
7466e63ce3Schristos
7566e63ce3Schristos interrupt-ranges = <int-number> <range> (optional)
7666e63ce3Schristos
7766e63ce3Schristos If present, this specifies the number of valid interrupt inputs (up
7866e63ce3Schristos to the maximum of 2048). By default, <<int-number>> is zero and
7966e63ce3Schristos range is determined by the <<reg>> size.
8066e63ce3Schristos
8166e63ce3Schristos
8266e63ce3Schristos EXAMPLES
8366e63ce3Schristos
8466e63ce3Schristos
8566e63ce3Schristos Enable tracing of the device:
8666e63ce3Schristos
8766e63ce3Schristos | -t glue-device \
8866e63ce3Schristos
8966e63ce3Schristos
9066e63ce3Schristos Create source, bitwize-and, and sink glue devices. Since the
9166e63ce3Schristos device at address <<0x10000>> is of size <<8>> it will have two
9266e63ce3Schristos output interrupt ports.
9366e63ce3Schristos
9466e63ce3Schristos | -o '/iobus@0xf0000000/glue@0x10000/reg 0x10000 8' \
9566e63ce3Schristos | -o '/iobus@0xf0000000/glue-and@0x20000/reg 0x20000 4' \
9666e63ce3Schristos | -o '/iobus@0xf0000000/glue-and/interrupt-ranges 0 2' \
9766e63ce3Schristos | -o '/iobus@0xf0000000/glue@0x30000/reg 0x30000 4' \
9866e63ce3Schristos
9966e63ce3Schristos
10066e63ce3Schristos Wire the two source interrupts to the AND device:
10166e63ce3Schristos
10266e63ce3Schristos | -o '/iobus@0xf0000000/glue@0x10000 > 0 0 /iobus/glue-and' \
10366e63ce3Schristos | -o '/iobus@0xf0000000/glue@0x10000 > 1 1 /iobus/glue-and' \
10466e63ce3Schristos
10566e63ce3Schristos
10666e63ce3Schristos Wire the AND device up to the sink so that the and's output is not
10766e63ce3Schristos left open.
10866e63ce3Schristos
10966e63ce3Schristos | -o '/iobus@0xf0000000/glue-and > 0 0 /iobus/glue@0x30000' \
11066e63ce3Schristos
11166e63ce3Schristos
11266e63ce3Schristos With the above configuration. The client program is able to
11366e63ce3Schristos compute a two bit AND. For instance the <<C>> stub below prints 1
11466e63ce3Schristos AND 0.
11566e63ce3Schristos
11666e63ce3Schristos | unsigned *input = (void*)0xf0010000;
11766e63ce3Schristos | unsigned *output = (void*)0xf0030000;
11866e63ce3Schristos | unsigned ans;
11966e63ce3Schristos | input[0] = htonl(1);
12066e63ce3Schristos | input[1] = htonl(0);
12166e63ce3Schristos | ans = ntohl(*output);
12266e63ce3Schristos | write_string("AND is ");
12366e63ce3Schristos | write_int(ans);
12466e63ce3Schristos | write_line();
12566e63ce3Schristos
12666e63ce3Schristos
12766e63ce3Schristos BUGS
12866e63ce3Schristos
12966e63ce3Schristos
13066e63ce3Schristos A future implementation of this device may support multiple
13166e63ce3Schristos interrupt ranges.
13266e63ce3Schristos
13366e63ce3Schristos Some of the devices listed may not yet be fully implemented.
13466e63ce3Schristos
13566e63ce3Schristos Additional devices such as a dff, an inverter or a latch may be
13666e63ce3Schristos useful.
13766e63ce3Schristos
13866e63ce3Schristos */
13966e63ce3Schristos
14066e63ce3Schristos
14166e63ce3Schristos enum {
14266e63ce3Schristos max_nr_interrupts = 2048,
14366e63ce3Schristos };
14466e63ce3Schristos
14566e63ce3Schristos typedef enum _hw_glue_type {
14666e63ce3Schristos glue_undefined = 0,
14766e63ce3Schristos glue_io,
14866e63ce3Schristos glue_and,
14966e63ce3Schristos glue_nand,
15066e63ce3Schristos glue_or,
15166e63ce3Schristos glue_xor,
15266e63ce3Schristos glue_nor,
15366e63ce3Schristos glue_not,
15466e63ce3Schristos } hw_glue_type;
15566e63ce3Schristos
15666e63ce3Schristos typedef struct _hw_glue_device {
15766e63ce3Schristos hw_glue_type type;
15866e63ce3Schristos int int_number;
15966e63ce3Schristos int *input;
16066e63ce3Schristos int nr_inputs;
16166e63ce3Schristos unsigned sizeof_input;
16266e63ce3Schristos /* our output registers */
16366e63ce3Schristos int space;
16466e63ce3Schristos unsigned_word address;
16566e63ce3Schristos unsigned sizeof_output;
16666e63ce3Schristos int *output;
16766e63ce3Schristos int nr_outputs;
16866e63ce3Schristos } hw_glue_device;
16966e63ce3Schristos
17066e63ce3Schristos
17166e63ce3Schristos static void
hw_glue_init_address(device * me)17266e63ce3Schristos hw_glue_init_address(device *me)
17366e63ce3Schristos {
17466e63ce3Schristos hw_glue_device *glue = (hw_glue_device*)device_data(me);
17566e63ce3Schristos
17666e63ce3Schristos /* attach to my parent */
17766e63ce3Schristos generic_device_init_address(me);
17866e63ce3Schristos
17966e63ce3Schristos /* establish the output registers */
18066e63ce3Schristos if (glue->output != NULL) {
18166e63ce3Schristos memset(glue->output, 0, glue->sizeof_output);
18266e63ce3Schristos }
18366e63ce3Schristos else {
18466e63ce3Schristos reg_property_spec unit;
18566e63ce3Schristos int reg_nr;
18666e63ce3Schristos /* find a relevant reg entry */
18766e63ce3Schristos reg_nr = 0;
18866e63ce3Schristos while (device_find_reg_array_property(me, "reg", reg_nr, &unit)
18966e63ce3Schristos && !device_size_to_attach_size(device_parent(me), &unit.size,
19066e63ce3Schristos &glue->sizeof_output, me))
19166e63ce3Schristos reg_nr++;
19266e63ce3Schristos /* check out the size */
19366e63ce3Schristos if (glue->sizeof_output == 0)
19466e63ce3Schristos device_error(me, "at least one reg property size must be nonzero");
19566e63ce3Schristos if (glue->sizeof_output % sizeof(unsigned_word) != 0)
19666e63ce3Schristos device_error(me, "reg property size must be %d aligned", sizeof(unsigned_word));
19766e63ce3Schristos /* and the address */
19866e63ce3Schristos device_address_to_attach_address(device_parent(me),
19966e63ce3Schristos &unit.address, &glue->space, &glue->address,
20066e63ce3Schristos me);
20166e63ce3Schristos if (glue->address % (sizeof(unsigned_word) * max_nr_interrupts) != 0)
20266e63ce3Schristos device_error(me, "reg property address must be %d aligned",
20366e63ce3Schristos sizeof(unsigned_word) * max_nr_interrupts);
20466e63ce3Schristos glue->nr_outputs = glue->sizeof_output / sizeof(unsigned_word);
20566e63ce3Schristos glue->output = zalloc(glue->sizeof_output);
20666e63ce3Schristos }
20766e63ce3Schristos
20866e63ce3Schristos /* establish the input interrupt ports */
20966e63ce3Schristos if (glue->input != NULL) {
21066e63ce3Schristos memset(glue->input, 0, glue->sizeof_input);
21166e63ce3Schristos }
21266e63ce3Schristos else {
21366e63ce3Schristos const device_property *ranges = device_find_property(me, "interrupt-ranges");
21466e63ce3Schristos if (ranges == NULL) {
21566e63ce3Schristos glue->int_number = 0;
21666e63ce3Schristos glue->nr_inputs = glue->nr_outputs;
21766e63ce3Schristos }
21866e63ce3Schristos else if (ranges->sizeof_array != sizeof(unsigned_cell) * 2) {
21966e63ce3Schristos device_error(me, "invalid interrupt-ranges property (incorrect size)");
22066e63ce3Schristos }
22166e63ce3Schristos else {
22266e63ce3Schristos const unsigned_cell *int_range = ranges->array;
22366e63ce3Schristos glue->int_number = BE2H_cell(int_range[0]);
22466e63ce3Schristos glue->nr_inputs = BE2H_cell(int_range[1]);
22566e63ce3Schristos }
22666e63ce3Schristos glue->sizeof_input = glue->nr_inputs * sizeof(unsigned);
22766e63ce3Schristos glue->input = zalloc(glue->sizeof_input);
22866e63ce3Schristos }
22966e63ce3Schristos
23066e63ce3Schristos /* determine our type */
23166e63ce3Schristos if (glue->type == glue_undefined) {
23266e63ce3Schristos const char *name = device_name(me);
23366e63ce3Schristos if (strcmp(name, "glue") == 0)
23466e63ce3Schristos glue->type = glue_io;
23566e63ce3Schristos else if (strcmp(name, "glue-and") == 0)
23666e63ce3Schristos glue->type = glue_and;
23766e63ce3Schristos else
23866e63ce3Schristos device_error(me, "unimplemented glue type");
23966e63ce3Schristos }
24066e63ce3Schristos
24166e63ce3Schristos DTRACE(glue, ("int-number %d, nr_inputs %d, nr_outputs %d\n",
24266e63ce3Schristos glue->int_number, glue->nr_inputs, glue->nr_outputs));
24366e63ce3Schristos }
24466e63ce3Schristos
24566e63ce3Schristos 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)24666e63ce3Schristos hw_glue_io_read_buffer_callback(device *me,
24766e63ce3Schristos void *dest,
24866e63ce3Schristos int space,
24966e63ce3Schristos unsigned_word addr,
25066e63ce3Schristos unsigned nr_bytes,
25166e63ce3Schristos cpu *processor,
25266e63ce3Schristos unsigned_word cia)
25366e63ce3Schristos {
25466e63ce3Schristos hw_glue_device *glue = (hw_glue_device*)device_data(me);
25566e63ce3Schristos int reg = ((addr - glue->address) / sizeof(unsigned_word)) % glue->nr_outputs;
25666e63ce3Schristos if (nr_bytes != sizeof(unsigned_word)
25766e63ce3Schristos || (addr % sizeof(unsigned_word)) != 0)
25866e63ce3Schristos device_error(me, "missaligned read access (%d:0x%lx:%d) not supported",
25966e63ce3Schristos space, (unsigned long)addr, nr_bytes);
26066e63ce3Schristos *(unsigned_word*)dest = H2BE_4(glue->output[reg]);
26166e63ce3Schristos DTRACE(glue, ("read - interrupt %d (0x%lx), level %d\n",
26266e63ce3Schristos reg, (unsigned long) addr, glue->output[reg]));
26366e63ce3Schristos return nr_bytes;
26466e63ce3Schristos }
26566e63ce3Schristos
26666e63ce3Schristos
26766e63ce3Schristos 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)26866e63ce3Schristos hw_glue_io_write_buffer_callback(device *me,
26966e63ce3Schristos const void *source,
27066e63ce3Schristos int space,
27166e63ce3Schristos unsigned_word addr,
27266e63ce3Schristos unsigned nr_bytes,
27366e63ce3Schristos cpu *processor,
27466e63ce3Schristos unsigned_word cia)
27566e63ce3Schristos {
27666e63ce3Schristos hw_glue_device *glue = (hw_glue_device*)device_data(me);
27766e63ce3Schristos int reg = ((addr - glue->address) / sizeof(unsigned_word)) % max_nr_interrupts;
27866e63ce3Schristos if (nr_bytes != sizeof(unsigned_word)
27966e63ce3Schristos || (addr % sizeof(unsigned_word)) != 0)
28066e63ce3Schristos device_error(me, "missaligned write access (%d:0x%lx:%d) not supported",
28166e63ce3Schristos space, (unsigned long)addr, nr_bytes);
28266e63ce3Schristos glue->output[reg] = H2BE_4(*(unsigned_word*)source);
28366e63ce3Schristos DTRACE(glue, ("write - interrupt %d (0x%lx), level %d\n",
28466e63ce3Schristos reg, (unsigned long) addr, glue->output[reg]));
28566e63ce3Schristos device_interrupt_event(me, reg, glue->output[reg], processor, cia);
28666e63ce3Schristos return nr_bytes;
28766e63ce3Schristos }
28866e63ce3Schristos
28966e63ce3Schristos static void
hw_glue_interrupt_event(device * me,int my_port,device * source,int source_port,int level,cpu * processor,unsigned_word cia)29066e63ce3Schristos hw_glue_interrupt_event(device *me,
29166e63ce3Schristos int my_port,
29266e63ce3Schristos device *source,
29366e63ce3Schristos int source_port,
29466e63ce3Schristos int level,
29566e63ce3Schristos cpu *processor,
29666e63ce3Schristos unsigned_word cia)
29766e63ce3Schristos {
29866e63ce3Schristos hw_glue_device *glue = (hw_glue_device*)device_data(me);
29966e63ce3Schristos int i;
30066e63ce3Schristos if (my_port < glue->int_number
30166e63ce3Schristos || my_port >= glue->int_number + glue->nr_inputs)
30266e63ce3Schristos device_error(me, "interrupt %d outside of valid range", my_port);
30366e63ce3Schristos glue->input[my_port - glue->int_number] = level;
30466e63ce3Schristos switch (glue->type) {
30566e63ce3Schristos case glue_io:
30666e63ce3Schristos {
30766e63ce3Schristos int port = my_port % glue->nr_outputs;
30866e63ce3Schristos glue->output[port] = level;
30966e63ce3Schristos DTRACE(glue, ("input - interrupt %d (0x%lx), level %d\n",
31066e63ce3Schristos my_port,
31166e63ce3Schristos (unsigned long)glue->address + port * sizeof(unsigned_word),
31266e63ce3Schristos level));
31366e63ce3Schristos break;
31466e63ce3Schristos }
31566e63ce3Schristos case glue_and:
31666e63ce3Schristos glue->output[0] = glue->input[0];
31766e63ce3Schristos for (i = 1; i < glue->nr_inputs; i++)
31866e63ce3Schristos glue->output[0] &= glue->input[i];
31966e63ce3Schristos DTRACE(glue, ("and - interrupt %d, level %d arrived - output %d\n",
32066e63ce3Schristos my_port, level, glue->output[0]));
32166e63ce3Schristos device_interrupt_event(me, 0, glue->output[0], processor, cia);
32266e63ce3Schristos break;
32366e63ce3Schristos default:
32466e63ce3Schristos device_error(me, "operator not implemented");
32566e63ce3Schristos break;
32666e63ce3Schristos }
32766e63ce3Schristos }
32866e63ce3Schristos
32966e63ce3Schristos
33066e63ce3Schristos static const device_interrupt_port_descriptor hw_glue_interrupt_ports[] = {
33166e63ce3Schristos { "int", 0, max_nr_interrupts },
33266e63ce3Schristos { NULL }
33366e63ce3Schristos };
33466e63ce3Schristos
33566e63ce3Schristos
33666e63ce3Schristos static device_callbacks const hw_glue_callbacks = {
33766e63ce3Schristos { hw_glue_init_address, NULL },
33866e63ce3Schristos { NULL, }, /* address */
33966e63ce3Schristos { hw_glue_io_read_buffer_callback,
34066e63ce3Schristos hw_glue_io_write_buffer_callback, },
34166e63ce3Schristos { NULL, }, /* DMA */
34266e63ce3Schristos { hw_glue_interrupt_event, NULL, hw_glue_interrupt_ports }, /* interrupt */
34366e63ce3Schristos { NULL, }, /* unit */
34466e63ce3Schristos NULL, /* instance */
34566e63ce3Schristos };
34666e63ce3Schristos
34766e63ce3Schristos
34866e63ce3Schristos static void *
hw_glue_create(const char * name,const device_unit * unit_address,const char * args)34966e63ce3Schristos hw_glue_create(const char *name,
35066e63ce3Schristos const device_unit *unit_address,
35166e63ce3Schristos const char *args)
35266e63ce3Schristos {
35366e63ce3Schristos /* create the descriptor */
35466e63ce3Schristos hw_glue_device *glue = ZALLOC(hw_glue_device);
35566e63ce3Schristos return glue;
35666e63ce3Schristos }
35766e63ce3Schristos
35866e63ce3Schristos
35966e63ce3Schristos const device_descriptor hw_glue_device_descriptor[] = {
36066e63ce3Schristos { "glue", hw_glue_create, &hw_glue_callbacks },
36166e63ce3Schristos { "glue-and", hw_glue_create, &hw_glue_callbacks },
36266e63ce3Schristos { "glue-nand", hw_glue_create, &hw_glue_callbacks },
36366e63ce3Schristos { "glue-or", hw_glue_create, &hw_glue_callbacks },
36466e63ce3Schristos { "glue-xor", hw_glue_create, &hw_glue_callbacks },
36566e63ce3Schristos { "glue-nor", hw_glue_create, &hw_glue_callbacks },
36666e63ce3Schristos { "glue-not", hw_glue_create, &hw_glue_callbacks },
36766e63ce3Schristos { NULL },
36866e63ce3Schristos };
36966e63ce3Schristos
37066e63ce3Schristos #endif /* _HW_GLUE_C_ */
371