xref: /netbsd-src/external/gpl3/gdb/dist/sim/mips/dv-tx3904irc.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*  This file is part of the program GDB, the GNU debugger.
2 
3     Copyright (C) 1998-2014 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 */
102 enum
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 
115 enum
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 
133 static 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 
176 struct 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 
191 static hw_io_read_buffer_method tx3904irc_io_read_buffer;
192 static hw_io_write_buffer_method tx3904irc_io_write_buffer;
193 static hw_port_event_method tx3904irc_port_event;
194 
195 static void
196 attach_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 
227 static void
228 tx3904irc_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 
255 static void
256 tx3904irc_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 
325 static unsigned
326 tx3904irc_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 
365 static unsigned
366 tx3904irc_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 
409 const struct hw_descriptor dv_tx3904irc_descriptor[] = {
410   { "tx3904irc", tx3904irc_finish, },
411   { NULL },
412 };
413