* MONSTER sky -> devo merge
[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 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 2 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, write to the Free Software
18     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
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
44    PROPERTIES
45
46
47    reg <base> <length>
48
49    Base of IRC control register bank.  <length> must equal 0x20.
50    Registers offsets:       0: ISR: interrupt status register
51                             4: IMR: interrupt mask register
52                            16: ILR0: interrupt level register 3..0
53                            20: ILR1: interrupt level register 7..4
54                            24: ILR2: interrupt level register 11..8
55                            28: ILR3: interrupt level register 15..12
56
57
58
59    PORTS
60
61
62    ip (output)
63
64    Interrupt priority port.  An event is generated when an interrupt
65    of a sufficient priority is passed through the IRC.  The value
66    associated with the event is the interrupt level (16-31), as given
67    for bits IP[5:0] in the book TMPR3904F Rev. 2.0, pg. 11-3.  Note
68    that even though INT[0] is tied externally to IP[5], we simulate
69    it as passing through the controller.
70
71
72    int0-7 (input)
73
74    External interrupts.
75
76    
77    dmac0-3 (input)
78
79    DMA internal interrupts, correspond to DMA channels 0-3.
80
81
82    sio0-1 (input)
83
84    SIO internal interrupts.
85
86
87    tmr0-2 (input)
88
89    Timer internal interrupts.
90
91    */
92
93
94
95
96
97 /* register numbers; each is one word long */
98 enum
99 {
100   ISR_REG = 0,
101   IMR_REG = 1,
102   ILR0_REG = 4,
103   ILR1_REG = 5,
104   ILR2_REG = 6,
105   ILR3_REG = 7,
106 };
107
108
109 /* port ID's */
110
111 enum
112 {
113   /* inputs, ordered to correspond to interrupt sources 0..15 */
114   INT1_PORT = 0, INT2_PORT, INT3_PORT, INT4_PORT, INT5_PORT, INT6_PORT, INT7_PORT,
115   DMAC3_PORT, DMAC2_PORT, DMAC1_PORT, DMAC0_PORT, SIO0_PORT, SIO1_PORT,
116   TMR0_PORT, TMR1_PORT, TMR2_PORT,
117
118   /* special INT[0] port */
119   INT0_PORT,
120
121   /* reset */
122   RESET_PORT,
123
124   /* output */
125   IP_PORT
126 };
127
128
129 static const struct hw_port_descriptor tx3904irc_ports[] = {
130
131   /* interrupt output */
132
133   { "ip", IP_PORT, 0, output_port, },
134
135   /* interrupt inputs (as names) */
136   /* in increasing order of level number */
137
138   { "int1", INT1_PORT, 0, input_port, },
139   { "int2", INT2_PORT, 0, input_port, },
140   { "int3", INT3_PORT, 0, input_port, },
141   { "int4", INT4_PORT, 0, input_port, },
142   { "int5", INT5_PORT, 0, input_port, },
143   { "int6", INT6_PORT, 0, input_port, },
144   { "int7", INT7_PORT, 0, input_port, },
145
146   { "dmac3", DMAC3_PORT, 0, input_port, },
147   { "dmac2", DMAC2_PORT, 0, input_port, },
148   { "dmac1", DMAC1_PORT, 0, input_port, },
149   { "dmac0", DMAC0_PORT, 0, input_port, },
150
151   { "sio0", SIO0_PORT, 0, input_port, },
152   { "sio1", SIO1_PORT, 0, input_port, },
153
154   { "tmr0", TMR0_PORT, 0, input_port, },
155   { "tmr1", TMR1_PORT, 0, input_port, },
156   { "tmr2", TMR2_PORT, 0, input_port, },
157
158   { "reset", RESET_PORT, 0, input_port, },
159   { "int0", INT0_PORT, 0, input_port, },
160
161   { NULL, },
162 };
163
164
165 #define NR_SOURCES (TMR3_PORT - INT1_PORT + 1) /* 16: number of interrupt sources */
166
167
168 /* The interrupt controller register internal state.  Note that we
169    store state using the control register images, in host endian
170    order. */
171
172 struct tx3904irc {
173   address_word base_address; /* control register base */
174   unsigned_4 isr;
175 #define ISR_SET(c,s) ((c)->isr &= ~ (1 << (s)))
176   unsigned_4 imr;
177 #define IMR_GET(c) ((c)->imr)
178   unsigned_4 ilr[4];
179 #define ILR_GET(c,s) LSEXTRACTED32((c)->ilr[(s)/4], (s) % 4 * 8 + 2, (s) % 4 * 8)
180 };
181
182
183
184 /* Finish off the partially created hw device.  Attach our local
185    callbacks.  Wire up our port names etc */
186
187 static hw_io_read_buffer_method tx3904irc_io_read_buffer;
188 static hw_io_write_buffer_method tx3904irc_io_write_buffer;
189 static hw_port_event_method tx3904irc_port_event;
190
191 static void
192 attach_tx3904irc_regs (struct hw *me,
193                       struct tx3904irc *controller)
194 {
195   unsigned_word attach_address;
196   int attach_space;
197   unsigned attach_size;
198   reg_property_spec reg;
199
200   if (hw_find_property (me, "reg") == NULL)
201     hw_abort (me, "Missing \"reg\" property");
202
203   if (!hw_find_reg_array_property (me, "reg", 0, &reg))
204     hw_abort (me, "\"reg\" property must contain one addr/size entry");
205
206   hw_unit_address_to_attach_address (hw_parent (me),
207                                      &reg.address,
208                                      &attach_space,
209                                      &attach_address,
210                                      me);
211   hw_unit_size_to_attach_size (hw_parent (me),
212                                &reg.size,
213                                &attach_size, me);
214
215   hw_attach_address (hw_parent (me), 0,
216                      attach_space, attach_address, attach_size,
217                      me);
218
219   controller->base_address = attach_address;
220 }
221
222
223 static void
224 tx3904irc_finish (struct hw *me)
225 {
226   struct tx3904irc *controller;
227
228   controller = HW_ZALLOC (me, struct tx3904irc);
229   set_hw_data (me, controller);
230   set_hw_io_read_buffer (me, tx3904irc_io_read_buffer);
231   set_hw_io_write_buffer (me, tx3904irc_io_write_buffer);
232   set_hw_ports (me, tx3904irc_ports);
233   set_hw_port_event (me, tx3904irc_port_event);
234
235   /* Attach ourself to our parent bus */
236   attach_tx3904irc_regs (me, controller);
237
238   /* Initialize to reset state */
239   controller->isr = 0x0000ffff;
240   controller->imr = 0;
241   controller->ilr[0] =
242     controller->ilr[1] =
243     controller->ilr[2] =
244     controller->ilr[3] = 0;
245 }
246
247
248
249 /* An event arrives on an interrupt port */
250
251 static void
252 tx3904irc_port_event (struct hw *me,
253                      int my_port,
254                      struct hw *source,
255                      int source_port,
256                      int level)
257 {
258   struct tx3904irc *controller = hw_data (me);
259
260   /* Ignore level - use only edge-triggered interrupts */
261
262   switch (my_port)
263     {
264     case INT0_PORT: 
265       {
266         int ip_number = 32; /* compute IP[5:0] */
267         HW_TRACE ((me, "port-event INT[0]"));
268         hw_port_event(me, IP_PORT, ip_number);
269         break;
270       }
271
272     case INT1_PORT: case INT2_PORT: case INT3_PORT: case INT4_PORT:
273     case INT5_PORT: case INT6_PORT: case INT7_PORT: case DMAC3_PORT:
274     case DMAC2_PORT: case DMAC1_PORT: case DMAC0_PORT: case SIO0_PORT:
275     case SIO1_PORT: case TMR0_PORT: case TMR1_PORT: case TMR2_PORT:
276       {
277         int source = my_port - INT1_PORT;
278
279         HW_TRACE ((me, "port-event interrupt source %d", source));
280         ISR_SET(controller, source);
281         if(ILR_GET(controller, source) > IMR_GET(controller))
282           {
283             int ip_number = 16 + source; /* compute IP[4:0] */
284             HW_TRACE ((me, "interrupt level %ld", ILR_GET(controller,source)));
285             hw_port_event(me, IP_PORT, ip_number);
286           }
287         break;
288       }
289
290     case RESET_PORT:
291       {
292         HW_TRACE ((me, "reset"));
293         controller->isr = 0x0000ffff;
294         controller->imr = 0;
295         controller->ilr[0] =
296           controller->ilr[1] =
297           controller->ilr[2] =
298           controller->ilr[3] = 0;
299         break;
300       }
301
302     case IP_PORT:
303       hw_abort (me, "Event on output port %d", my_port);
304       break;
305
306     default:
307       hw_abort (me, "Event on unknown port %d", my_port);
308       break;
309     }
310 }
311
312
313 /* generic read/write */
314
315 static unsigned
316 tx3904irc_io_read_buffer (struct hw *me,
317                          void *dest,
318                          int space,
319                          unsigned_word base,
320                          unsigned nr_bytes)
321 {
322   struct tx3904irc *controller = hw_data (me);
323   unsigned byte;
324
325   HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
326   for (byte = 0; byte < nr_bytes; byte++)
327     {
328       address_word address = base + byte;
329       int reg_number = (address - controller->base_address) / 4;
330       int reg_offset = (address - controller->base_address) % 4;
331       unsigned_4 register_value; /* in target byte order */
332
333       /* fill in entire register_value word */
334       switch (reg_number)
335         {
336         case ISR_REG: register_value = controller->isr; break;
337         case IMR_REG: register_value = controller->imr; break;
338         case ILR0_REG: register_value = controller->ilr[0]; break;
339         case ILR1_REG: register_value = controller->ilr[1]; break;
340         case ILR2_REG: register_value = controller->ilr[2]; break;
341         case ILR3_REG: register_value = controller->ilr[3]; break;
342         default: register_value = 0;
343         }
344
345       /* write requested byte out */
346       memcpy ((char*) dest + byte, ((char*)& register_value)+reg_offset, 1);
347     }
348
349   return nr_bytes;
350 }     
351
352
353
354 static unsigned
355 tx3904irc_io_write_buffer (struct hw *me,
356                           const void *source,
357                           int space,
358                           unsigned_word base,
359                           unsigned nr_bytes)
360 {
361   struct tx3904irc *controller = hw_data (me);
362   unsigned byte;
363
364   HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
365   for (byte = 0; byte < nr_bytes; byte++)
366     {
367       address_word address = base + byte;
368       int reg_number = (address - controller->base_address) / 4;
369       int reg_offset = (address - controller->base_address) % 4;
370       unsigned_4* register_ptr;
371       unsigned_4 register_value;
372
373       /* fill in entire register_value word */
374       switch (reg_number)
375         {
376         case ISR_REG: register_ptr = & controller->isr; break;
377         case IMR_REG: register_ptr = & controller->imr; break;
378         case ILR0_REG: register_ptr = & controller->ilr[0]; break;
379         case ILR1_REG: register_ptr = & controller->ilr[1]; break;
380         case ILR2_REG: register_ptr = & controller->ilr[2]; break;
381         case ILR3_REG: register_ptr = & controller->ilr[3]; break;
382         default: register_ptr = & register_value; /* used as a dummy */
383         }
384
385       HW_TRACE ((me, "reg %d pre: %08lx", reg_number, (long) *register_ptr));
386
387       /* overwrite requested byte */
388       memcpy (((char*)register_ptr)+reg_offset, (char*)source + byte, 1);
389
390       HW_TRACE ((me, "post: %08lx", (long) *register_ptr));
391     }
392   return nr_bytes;
393 }     
394
395
396 const struct hw_descriptor dv_tx3904irc_descriptor[] = {
397   { "tx3904irc", tx3904irc_finish, },
398   { NULL },
399 };