]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - sim/mips/dv-tx3904irc.c
sim: overhaul & unify endian settings management
[thirdparty/binutils-gdb.git] / sim / mips / dv-tx3904irc.c
CommitLineData
c906108c
SS
1/* This file is part of the program GDB, the GNU debugger.
2
3666a048 3 Copyright (C) 1998-2021 Free Software Foundation, Inc.
c906108c
SS
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
4744ac1b 8 the Free Software Foundation; either version 3 of the License, or
c906108c 9 (at your option) any later version.
4744ac1b 10
c906108c
SS
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.
4744ac1b 15
c906108c 16 You should have received a copy of the GNU General Public License
4744ac1b 17 along with this program. If not, see <http://www.gnu.org/licenses/>.
c906108c
SS
18
19 */
20
6df01ab8
MF
21/* This must come before any other includes. */
22#include "defs.h"
c906108c
SS
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 */
104enum
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
117enum
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
135static 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
178struct 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
193static hw_io_read_buffer_method tx3904irc_io_read_buffer;
194static hw_io_write_buffer_method tx3904irc_io_write_buffer;
195static hw_port_event_method tx3904irc_port_event;
196
197static void
198attach_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
229static void
230tx3904irc_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
257static void
258tx3904irc_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
327static unsigned
328tx3904irc_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
367static unsigned
368tx3904irc_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;
7b2298cb 384 unsigned_4 register_value = 0;
c906108c
SS
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
411const struct hw_descriptor dv_tx3904irc_descriptor[] = {
412 { "tx3904irc", tx3904irc_finish, },
413 { NULL },
414};