1/*  This file is part of the program GDB, the GNU debugger.
2
3    Copyright (C) 1998, 2007 Free Software Foundation, Inc.
4    Contributed by Cygnus Solutions.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19    */
20
21
22#include "sim-main.h"
23#include "hw-main.h"
24
25
26/* DEVICE
27
28
29   tx3904irc - tx3904 interrupt controller
30
31
32   DESCRIPTION
33
34
35   Implements the tx3904 interrupt controller described in the tx3904
36   user guide.  It does not include the interrupt detection circuit
37   that preprocesses the eight external interrupts, so assumes that
38   each event on an input interrupt port signals a new interrupt.
39   That is, it implements edge- rather than level-triggered
40   interrupts.
41
42   This implementation does not support multiple concurrent
43   interrupts.
44
45
46   PROPERTIES
47
48
49   reg <base> <length>
50
51   Base of IRC control register bank.  <length> must equal 0x20.
52   Registers offsets:       0: ISR: interrupt status register
53                            4: IMR: interrupt mask register
54                           16: ILR0: interrupt level register 3..0
55                           20: ILR1: interrupt level register 7..4
56                           24: ILR2: interrupt level register 11..8
57                           28: ILR3: interrupt level register 15..12
58
59
60
61   PORTS
62
63
64   ip (output)
65
66   Interrupt priority port.  An event is generated when an interrupt
67   of a sufficient priority is passed through the IRC.  The value
68   associated with the event is the interrupt level (16-31), as given
69   for bits IP[5:0] in the book TMPR3904F Rev. 2.0, pg. 11-3.  Note
70   that even though INT[0] is tied externally to IP[5], we simulate
71   it as passing through the controller.
72
73   An output level of zero signals the clearing of a level interrupt.
74
75
76   int0-7 (input)
77
78   External interrupts.  Level = 0 -> level interrupt cleared.
79
80
81   dmac0-3 (input)
82
83   DMA internal interrupts, correspond to DMA channels 0-3.  Level = 0 -> level interrupt cleared.
84
85
86   sio0-1 (input)
87
88   SIO internal interrupts.  Level = 0 -> level interrupt cleared.
89
90
91   tmr0-2 (input)
92
93   Timer internal interrupts.  Level = 0 -> level interrupt cleared.
94
95   */
96
97
98
99
100
101/* register numbers; each is one word long */
102enum
103{
104  ISR_REG = 0,
105  IMR_REG = 1,
106  ILR0_REG = 4,
107  ILR1_REG = 5,
108  ILR2_REG = 6,
109  ILR3_REG = 7,
110};
111
112
113/* port ID's */
114
115enum
116{
117  /* inputs, ordered to correspond to interrupt sources 0..15 */
118  INT1_PORT = 0, INT2_PORT, INT3_PORT, INT4_PORT, INT5_PORT, INT6_PORT, INT7_PORT,
119  DMAC3_PORT, DMAC2_PORT, DMAC1_PORT, DMAC0_PORT, SIO0_PORT, SIO1_PORT,
120  TMR0_PORT, TMR1_PORT, TMR2_PORT,
121
122  /* special INT[0] port */
123  INT0_PORT,
124
125  /* reset */
126  RESET_PORT,
127
128  /* output */
129  IP_PORT
130};
131
132
133static const struct hw_port_descriptor tx3904irc_ports[] = {
134
135  /* interrupt output */
136
137  { "ip", IP_PORT, 0, output_port, },
138
139  /* interrupt inputs (as names) */
140  /* in increasing order of level number */
141
142  { "int1", INT1_PORT, 0, input_port, },
143  { "int2", INT2_PORT, 0, input_port, },
144  { "int3", INT3_PORT, 0, input_port, },
145  { "int4", INT4_PORT, 0, input_port, },
146  { "int5", INT5_PORT, 0, input_port, },
147  { "int6", INT6_PORT, 0, input_port, },
148  { "int7", INT7_PORT, 0, input_port, },
149
150  { "dmac3", DMAC3_PORT, 0, input_port, },
151  { "dmac2", DMAC2_PORT, 0, input_port, },
152  { "dmac1", DMAC1_PORT, 0, input_port, },
153  { "dmac0", DMAC0_PORT, 0, input_port, },
154
155  { "sio0", SIO0_PORT, 0, input_port, },
156  { "sio1", SIO1_PORT, 0, input_port, },
157
158  { "tmr0", TMR0_PORT, 0, input_port, },
159  { "tmr1", TMR1_PORT, 0, input_port, },
160  { "tmr2", TMR2_PORT, 0, input_port, },
161
162  { "reset", RESET_PORT, 0, input_port, },
163  { "int0", INT0_PORT, 0, input_port, },
164
165  { NULL, },
166};
167
168
169#define NR_SOURCES (TMR3_PORT - INT1_PORT + 1) /* 16: number of interrupt sources */
170
171
172/* The interrupt controller register internal state.  Note that we
173   store state using the control register images, in host endian
174   order. */
175
176struct tx3904irc {
177  address_word base_address; /* control register base */
178  unsigned_4 isr;
179#define ISR_SET(c,s) ((c)->isr &= ~ (1 << (s)))
180  unsigned_4 imr;
181#define IMR_GET(c) ((c)->imr)
182  unsigned_4 ilr[4];
183#define ILR_GET(c,s) LSEXTRACTED32((c)->ilr[(s)/4], (s) % 4 * 8 + 2, (s) % 4 * 8)
184};
185
186
187
188/* Finish off the partially created hw device.  Attach our local
189   callbacks.  Wire up our port names etc */
190
191static hw_io_read_buffer_method tx3904irc_io_read_buffer;
192static hw_io_write_buffer_method tx3904irc_io_write_buffer;
193static hw_port_event_method tx3904irc_port_event;
194
195static void
196attach_tx3904irc_regs (struct hw *me,
197		      struct tx3904irc *controller)
198{
199  unsigned_word attach_address;
200  int attach_space;
201  unsigned attach_size;
202  reg_property_spec reg;
203
204  if (hw_find_property (me, "reg") == NULL)
205    hw_abort (me, "Missing \"reg\" property");
206
207  if (!hw_find_reg_array_property (me, "reg", 0, &reg))
208    hw_abort (me, "\"reg\" property must contain one addr/size entry");
209
210  hw_unit_address_to_attach_address (hw_parent (me),
211				     &reg.address,
212				     &attach_space,
213				     &attach_address,
214				     me);
215  hw_unit_size_to_attach_size (hw_parent (me),
216			       &reg.size,
217			       &attach_size, me);
218
219  hw_attach_address (hw_parent (me), 0,
220		     attach_space, attach_address, attach_size,
221		     me);
222
223  controller->base_address = attach_address;
224}
225
226
227static void
228tx3904irc_finish (struct hw *me)
229{
230  struct tx3904irc *controller;
231
232  controller = HW_ZALLOC (me, struct tx3904irc);
233  set_hw_data (me, controller);
234  set_hw_io_read_buffer (me, tx3904irc_io_read_buffer);
235  set_hw_io_write_buffer (me, tx3904irc_io_write_buffer);
236  set_hw_ports (me, tx3904irc_ports);
237  set_hw_port_event (me, tx3904irc_port_event);
238
239  /* Attach ourself to our parent bus */
240  attach_tx3904irc_regs (me, controller);
241
242  /* Initialize to reset state */
243  controller->isr = 0x0000ffff;
244  controller->imr = 0;
245  controller->ilr[0] =
246    controller->ilr[1] =
247    controller->ilr[2] =
248    controller->ilr[3] = 0;
249}
250
251
252
253/* An event arrives on an interrupt port */
254
255static void
256tx3904irc_port_event (struct hw *me,
257		     int my_port,
258		     struct hw *source_dev,
259		     int source_port,
260		     int level)
261{
262  struct tx3904irc *controller = hw_data (me);
263
264  /* handle deactivated interrupt */
265  if(level == 0)
266    {
267      HW_TRACE ((me, "interrupt cleared on port %d", my_port));
268      hw_port_event(me, IP_PORT, 0);
269      return;
270    }
271
272  switch (my_port)
273    {
274    case INT0_PORT:
275      {
276	int ip_number = 32; /* compute IP[5:0] */
277	HW_TRACE ((me, "port-event INT[0]"));
278	hw_port_event(me, IP_PORT, ip_number);
279	break;
280      }
281
282    case INT1_PORT: case INT2_PORT: case INT3_PORT: case INT4_PORT:
283    case INT5_PORT: case INT6_PORT: case INT7_PORT: case DMAC3_PORT:
284    case DMAC2_PORT: case DMAC1_PORT: case DMAC0_PORT: case SIO0_PORT:
285    case SIO1_PORT: case TMR0_PORT: case TMR1_PORT: case TMR2_PORT:
286      {
287	int source = my_port - INT1_PORT;
288
289	HW_TRACE ((me, "interrupt asserted on port %d", source));
290	ISR_SET(controller, source);
291	if(ILR_GET(controller, source) > IMR_GET(controller))
292	  {
293	    int ip_number = 16 + source; /* compute IP[4:0] */
294	    HW_TRACE ((me, "interrupt level %d", ILR_GET(controller,source)));
295	    hw_port_event(me, IP_PORT, ip_number);
296	  }
297	break;
298      }
299
300    case RESET_PORT:
301      {
302	HW_TRACE ((me, "reset"));
303	controller->isr = 0x0000ffff;
304	controller->imr = 0;
305	controller->ilr[0] =
306	  controller->ilr[1] =
307	  controller->ilr[2] =
308	  controller->ilr[3] = 0;
309	break;
310      }
311
312    case IP_PORT:
313      hw_abort (me, "Event on output port %d", my_port);
314      break;
315
316    default:
317      hw_abort (me, "Event on unknown port %d", my_port);
318      break;
319    }
320}
321
322
323/* generic read/write */
324
325static unsigned
326tx3904irc_io_read_buffer (struct hw *me,
327			 void *dest,
328			 int space,
329			 unsigned_word base,
330			 unsigned nr_bytes)
331{
332  struct tx3904irc *controller = hw_data (me);
333  unsigned byte;
334
335  HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
336  for (byte = 0; byte < nr_bytes; byte++)
337    {
338      address_word address = base + byte;
339      int reg_number = (address - controller->base_address) / 4;
340      int reg_offset = (address - controller->base_address) % 4;
341      unsigned_4 register_value; /* in target byte order */
342
343      /* fill in entire register_value word */
344      switch (reg_number)
345	{
346	case ISR_REG: register_value = controller->isr; break;
347	case IMR_REG: register_value = controller->imr; break;
348	case ILR0_REG: register_value = controller->ilr[0]; break;
349	case ILR1_REG: register_value = controller->ilr[1]; break;
350	case ILR2_REG: register_value = controller->ilr[2]; break;
351	case ILR3_REG: register_value = controller->ilr[3]; break;
352	default: register_value = 0;
353	}
354
355      /* write requested byte out */
356      register_value = H2T_4(register_value);
357      memcpy ((char*) dest + byte, ((char*)& register_value)+reg_offset, 1);
358    }
359
360  return nr_bytes;
361}
362
363
364
365static unsigned
366tx3904irc_io_write_buffer (struct hw *me,
367			  const void *source,
368			  int space,
369			  unsigned_word base,
370			  unsigned nr_bytes)
371{
372  struct tx3904irc *controller = hw_data (me);
373  unsigned byte;
374
375  HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
376  for (byte = 0; byte < nr_bytes; byte++)
377    {
378      address_word address = base + byte;
379      int reg_number = (address - controller->base_address) / 4;
380      int reg_offset = (address - controller->base_address) % 4;
381      unsigned_4* register_ptr;
382      unsigned_4 register_value;
383
384      /* fill in entire register_value word */
385      switch (reg_number)
386	{
387	case ISR_REG: register_ptr = & controller->isr; break;
388	case IMR_REG: register_ptr = & controller->imr; break;
389	case ILR0_REG: register_ptr = & controller->ilr[0]; break;
390	case ILR1_REG: register_ptr = & controller->ilr[1]; break;
391	case ILR2_REG: register_ptr = & controller->ilr[2]; break;
392	case ILR3_REG: register_ptr = & controller->ilr[3]; break;
393	default: register_ptr = & register_value; /* used as a dummy */
394	}
395
396      /* HW_TRACE ((me, "reg %d pre: %08lx", reg_number, (long) *register_ptr)); */
397
398      /* overwrite requested byte */
399      register_value = H2T_4(* register_ptr);
400      memcpy (((char*)&register_value)+reg_offset, (const char*)source + byte, 1);
401      * register_ptr = T2H_4(register_value);
402
403      /* HW_TRACE ((me, "post: %08lx", (long) *register_ptr)); */
404    }
405  return nr_bytes;
406}
407
408
409const struct hw_descriptor dv_tx3904irc_descriptor[] = {
410  { "tx3904irc", tx3904irc_finish, },
411  { NULL },
412};
413