* Makefile.in (tmp-mach-multi): Exit early when igen fails.
[external/binutils.git] / sim / mips / dv-tx3904irc.c
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 };