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