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