xref: /netbsd/external/gpl3/gdb/dist/sim/ppc/hw_glue.c (revision 48596154)
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