123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832 |
- /* This file is part of the program GDB, the GNU debugger.
-
- Copyright (C) 1998-2022 Free Software Foundation, Inc.
- Contributed by Cygnus Solutions.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- */
- /* This must come before any other includes. */
- #include "defs.h"
- #include "sim-main.h"
- #include "hw-main.h"
- #include "sim-hw.h"
- /* DEVICE
-
- mn103int - mn103002 interrupt controller
-
- DESCRIPTION
-
- Implements the mn103002 interrupt controller described in the
- mn103002 user guide.
- PROPERTIES
-
- reg = <icr-adr> <icr-siz> <iagr-adr> <iadr-siz> <extmd-adr> <extmd-siz>
- Specify the address of the ICR (total of 30 registers), IAGR and
- EXTMD registers (within the parent bus).
- The reg property value `0x34000100 0x7C 0x34000200 0x8 0x3400280
- 0x8' locates the interrupt controller at the addresses specified in
- the mn103002 interrupt controller user guide.
- PORTS
- nmi (output)
- Non-maskable interrupt output port. An event on this output ports
- indicates a NMI request from the interrupt controller. The value
- attached to the event should be ignored.
- level (output)
- Maskable interrupt level output port. An event on this output port
- indicates a maskable interrupt request at the specified level. The
- event value defines the level being requested.
- The interrupt controller will generate an event on this port
- whenever there is a change to the internal state of the interrupt
- controller.
- ack (input)
- Signal from processor indicating that a maskable interrupt has been
- accepted and the interrupt controller should latch the IAGR with
- value of the current highest priority interrupting group.
- The event value is the interrupt level being accepted by the
- processor. It should be consistent with the most recent LEVEL sent
- to the processor from the interrupt controller.
- int[0..100] (input)
- Level or edge triggered interrupt input port. Each of the 30
- groups (0..30) can have up to 4 (0..3) interrupt inputs. The
- interpretation of a port event/value is determined by the
- configuration of the corresponding interrupt group.
- For convenience, numerous aliases to these interrupt inputs are
- provided.
- BUGS
- For edge triggered interrupts, the interrupt controller does not
- differentiate between POSITIVE (rising) and NEGATIVE (falling)
- edges. Instead any input port event is considered to be an
- interrupt trigger.
- For level sensitive interrupts, the interrupt controller ignores
- active HIGH/LOW settings and instead always interprets a nonzero
- port value as an interrupt assertion and a zero port value as a
- negation.
- */
- /* The interrupt groups - numbered according to mn103002 convention */
- enum mn103int_trigger {
- ACTIVE_LOW,
- ACTIVE_HIGH,
- POSITIVE_EDGE,
- NEGATIVE_EDGE,
- };
- enum mn103int_type {
- NMI_GROUP,
- LEVEL_GROUP,
- };
- struct mn103int_group {
- int gid;
- int level;
- unsigned enable;
- unsigned request;
- unsigned input;
- enum mn103int_trigger trigger;
- enum mn103int_type type;
- };
- enum {
- FIRST_NMI_GROUP = 0,
- LAST_NMI_GROUP = 1,
- FIRST_LEVEL_GROUP = 2,
- LAST_LEVEL_GROUP = 30,
- NR_GROUPS,
- };
- enum {
- LOWEST_LEVEL = 7,
- };
- /* The interrupt controller register address blocks */
- struct mn103int_block {
- unsigned_word base;
- unsigned_word bound;
- };
- enum { ICR_BLOCK, IAGR_BLOCK, EXTMD_BLOCK, NR_BLOCKS };
- struct mn103int {
- struct mn103int_block block[NR_BLOCKS];
- struct mn103int_group group[NR_GROUPS];
- unsigned interrupt_accepted_group;
- };
- /* output port ID's */
- enum {
- NMI_PORT,
- LEVEL_PORT,
- };
- /* input port ID's */
- enum {
- G0_PORT = 0,
- G1_PORT = 4,
- G2_PORT = 8,
- G3_PORT = 12,
- G4_PORT = 16,
- G5_PORT = 20,
- G6_PORT = 24,
- G7_PORT = 28,
- G8_PORT = 32,
- G9_PORT = 36,
- G10_PORT = 40,
- G11_PORT = 44,
- G12_PORT = 48,
- G13_PORT = 52,
- G14_PORT = 56,
- G15_PORT = 60,
- G16_PORT = 64,
- G17_PORT = 68,
- G18_PORT = 72,
- G19_PORT = 76,
- G20_PORT = 80,
- G21_PORT = 84,
- G22_PORT = 88,
- G23_PORT = 92,
- IRQ0_PORT = G23_PORT,
- G24_PORT = 96,
- G25_PORT = 100,
- G26_PORT = 104,
- G27_PORT = 108,
- IRQ4_PORT = G27_PORT,
- G28_PORT = 112,
- G29_PORT = 116,
- G30_PORT = 120,
- NR_G_PORTS = 124,
- ACK_PORT,
- };
- static const struct hw_port_descriptor mn103int_ports[] = {
- /* interrupt outputs */
- { "nmi", NMI_PORT, 0, output_port, },
- { "level", LEVEL_PORT, 0, output_port, },
- /* interrupt ack (latch) input from cpu */
- { "ack", ACK_PORT, 0, input_port, },
- /* interrupt inputs (as names) */
- { "nmirq", G0_PORT + 0, 0, input_port, },
- { "watchdog", G0_PORT + 1, 0, input_port, },
- { "syserr", G0_PORT + 2, 0, input_port, },
- { "timer-0-underflow", G2_PORT, 0, input_port, },
- { "timer-1-underflow", G3_PORT, 0, input_port, },
- { "timer-2-underflow", G4_PORT, 0, input_port, },
- { "timer-3-underflow", G5_PORT, 0, input_port, },
- { "timer-4-underflow", G6_PORT, 0, input_port, },
- { "timer-5-underflow", G7_PORT, 0, input_port, },
- { "timer-6-underflow", G8_PORT, 0, input_port, },
- { "timer-6-compare-a", G9_PORT, 0, input_port, },
- { "timer-6-compare-b", G10_PORT, 0, input_port, },
- { "dma-0-end", G12_PORT, 0, input_port, },
- { "dma-1-end", G13_PORT, 0, input_port, },
- { "dma-2-end", G14_PORT, 0, input_port, },
- { "dma-3-end", G15_PORT, 0, input_port, },
- { "serial-0-receive", G16_PORT, 0, input_port, },
- { "serial-0-transmit", G17_PORT, 0, input_port, },
- { "serial-1-receive", G18_PORT, 0, input_port, },
- { "serial-1-transmit", G19_PORT, 0, input_port, },
- { "serial-2-receive", G20_PORT, 0, input_port, },
- { "serial-2-transmit", G21_PORT, 0, input_port, },
- { "irq-0", G23_PORT, 0, input_port, },
- { "irq-1", G24_PORT, 0, input_port, },
- { "irq-2", G25_PORT, 0, input_port, },
- { "irq-3", G26_PORT, 0, input_port, },
- { "irq-4", G27_PORT, 0, input_port, },
- { "irq-5", G28_PORT, 0, input_port, },
- { "irq-6", G29_PORT, 0, input_port, },
- { "irq-7", G30_PORT, 0, input_port, },
- /* interrupt inputs (as generic numbers) */
- { "int", 0, NR_G_PORTS, input_port, },
- { NULL, },
- };
- /* Macros for extracting/restoring the various register bits */
- #define EXTRACT_ID(X) (LSEXTRACTED8 ((X), 3, 0))
- #define INSERT_ID(X) (LSINSERTED8 ((X), 3, 0))
- #define EXTRACT_IR(X) (LSEXTRACTED8 ((X), 7, 4))
- #define INSERT_IR(X) (LSINSERTED8 ((X), 7, 4))
- #define EXTRACT_IE(X) (LSEXTRACTED8 ((X), 3, 0))
- #define INSERT_IE(X) (LSINSERTED8 ((X), 3, 0))
- #define EXTRACT_LV(X) (LSEXTRACTED8 ((X), 6, 4))
- #define INSERT_LV(X) (LSINSERTED8 ((X), 6, 4))
- /* Finish off the partially created hw device. Attach our local
- callbacks. Wire up our port names etc */
- static hw_io_read_buffer_method mn103int_io_read_buffer;
- static hw_io_write_buffer_method mn103int_io_write_buffer;
- static hw_port_event_method mn103int_port_event;
- static hw_ioctl_method mn103int_ioctl;
- static void
- attach_mn103int_regs (struct hw *me,
- struct mn103int *controller)
- {
- int i;
- if (hw_find_property (me, "reg") == NULL)
- hw_abort (me, "Missing \"reg\" property");
- for (i = 0; i < NR_BLOCKS; i++)
- {
- unsigned_word attach_address;
- int attach_space;
- unsigned attach_size;
- reg_property_spec reg;
- if (!hw_find_reg_array_property (me, "reg", i, ®))
- hw_abort (me, "\"reg\" property must contain three addr/size entries");
- hw_unit_address_to_attach_address (hw_parent (me),
- ®.address,
- &attach_space,
- &attach_address,
- me);
- controller->block[i].base = attach_address;
- hw_unit_size_to_attach_size (hw_parent (me),
- ®.size,
- &attach_size, me);
- controller->block[i].bound = attach_address + (attach_size - 1);
- hw_attach_address (hw_parent (me),
- 0,
- attach_space, attach_address, attach_size,
- me);
- }
- }
- static void
- mn103int_finish (struct hw *me)
- {
- int gid;
- struct mn103int *controller;
- controller = HW_ZALLOC (me, struct mn103int);
- set_hw_data (me, controller);
- set_hw_io_read_buffer (me, mn103int_io_read_buffer);
- set_hw_io_write_buffer (me, mn103int_io_write_buffer);
- set_hw_ports (me, mn103int_ports);
- set_hw_port_event (me, mn103int_port_event);
- me->to_ioctl = mn103int_ioctl;
- /* Attach ourself to our parent bus */
- attach_mn103int_regs (me, controller);
- /* Initialize all the groups according to their default configuration */
- for (gid = 0; gid < NR_GROUPS; gid++)
- {
- struct mn103int_group *group = &controller->group[gid];
- group->trigger = NEGATIVE_EDGE;
- group->gid = gid;
- if (FIRST_NMI_GROUP <= gid && gid <= LAST_NMI_GROUP)
- {
- group->enable = 0xf;
- group->type = NMI_GROUP;
- }
- else if (FIRST_LEVEL_GROUP <= gid && gid <= LAST_LEVEL_GROUP)
- {
- group->enable = 0x0;
- group->type = LEVEL_GROUP;
- }
- else
- hw_abort (me, "internal error - unknown group id");
- }
- }
- /* Perform the nasty work of figuring out which of the interrupt
- groups should have its interrupt delivered. */
- static int
- find_highest_interrupt_group (struct hw *me,
- struct mn103int *controller)
- {
- int gid;
- int selected;
- /* FIRST_NMI_GROUP (group zero) is used as a special default value
- when searching for an interrupt group.*/
- selected = FIRST_NMI_GROUP;
- controller->group[FIRST_NMI_GROUP].level = 7;
-
- for (gid = FIRST_LEVEL_GROUP; gid <= LAST_LEVEL_GROUP; gid++)
- {
- struct mn103int_group *group = &controller->group[gid];
- if ((group->request & group->enable) != 0)
- {
- /* Remember, lower level, higher priority. */
- if (group->level < controller->group[selected].level)
- {
- selected = gid;
- }
- }
- }
- return selected;
- }
- /* Notify the processor of an interrupt level update */
- static void
- push_interrupt_level (struct hw *me,
- struct mn103int *controller)
- {
- int selected = find_highest_interrupt_group (me, controller);
- int level = controller->group[selected].level;
- HW_TRACE ((me, "port-out - selected=%d level=%d", selected, level));
- hw_port_event (me, LEVEL_PORT, level);
- }
- /* An event arrives on an interrupt port */
- static void
- mn103int_port_event (struct hw *me,
- int my_port,
- struct hw *source,
- int source_port,
- int level)
- {
- struct mn103int *controller = hw_data (me);
- switch (my_port)
- {
- case ACK_PORT:
- {
- int selected = find_highest_interrupt_group (me, controller);
- if (controller->group[selected].level != level)
- hw_abort (me, "botched level synchronisation");
- controller->interrupt_accepted_group = selected;
- HW_TRACE ((me, "port-event port=ack level=%d - selected=%d",
- level, selected));
- break;
- }
- default:
- {
- int gid;
- int iid;
- struct mn103int_group *group;
- unsigned interrupt;
- if (my_port > NR_G_PORTS)
- hw_abort (me, "Event on unknown port %d", my_port);
- /* map the port onto an interrupt group */
- gid = (my_port % NR_G_PORTS) / 4;
- group = &controller->group[gid];
- iid = (my_port % 4);
- interrupt = 1 << iid;
- /* update our cached input */
- if (level)
- group->input |= interrupt;
- else
- group->input &= ~interrupt;
- /* update the request bits */
- switch (group->trigger)
- {
- case ACTIVE_LOW:
- case ACTIVE_HIGH:
- if (level)
- group->request |= interrupt;
- break;
- case NEGATIVE_EDGE:
- case POSITIVE_EDGE:
- group->request |= interrupt;
- }
- /* force a corresponding output */
- switch (group->type)
- {
- case NMI_GROUP:
- {
- /* for NMI's the event is the trigger */
- HW_TRACE ((me, "port-in port=%d group=%d interrupt=%d - NMI",
- my_port, gid, iid));
- if ((group->request & group->enable) != 0)
- {
- HW_TRACE ((me, "port-out NMI"));
- hw_port_event (me, NMI_PORT, 1);
- }
- break;
- }
-
- case LEVEL_GROUP:
- {
- /* if an interrupt is now pending */
- HW_TRACE ((me, "port-in port=%d group=%d interrupt=%d - INT",
- my_port, gid, iid));
- push_interrupt_level (me, controller);
- break;
- }
- }
- break;
- }
- }
- }
- /* Read/write to to an ICR (group control register) */
- static struct mn103int_group *
- decode_group (struct hw *me,
- struct mn103int *controller,
- unsigned_word base,
- unsigned_word *offset)
- {
- int gid = (base / 4) % NR_GROUPS;
- *offset = (base % 4);
- return &controller->group[gid];
- }
- static uint8_t
- read_icr (struct hw *me,
- struct mn103int *controller,
- unsigned_word base)
- {
- unsigned_word offset;
- struct mn103int_group *group = decode_group (me, controller, base, &offset);
- uint8_t val = 0;
- switch (group->type)
- {
- case NMI_GROUP:
- switch (offset)
- {
- case 0:
- val = INSERT_ID (group->request);
- HW_TRACE ((me, "read-icr group=%d:0 nmi 0x%02x",
- group->gid, val));
- break;
- default:
- break;
- }
- break;
- case LEVEL_GROUP:
- switch (offset)
- {
- case 0:
- val = (INSERT_IR (group->request)
- | INSERT_ID (group->request & group->enable));
- HW_TRACE ((me, "read-icr group=%d:0 level 0x%02x",
- group->gid, val));
- break;
- case 1:
- val = (INSERT_LV (group->level)
- | INSERT_IE (group->enable));
- HW_TRACE ((me, "read-icr level-%d:1 level 0x%02x",
- group->gid, val));
- break;
- }
- break;
- default:
- break;
- }
- return val;
- }
- static void
- write_icr (struct hw *me,
- struct mn103int *controller,
- unsigned_word base,
- uint8_t val)
- {
- unsigned_word offset;
- struct mn103int_group *group = decode_group (me, controller, base, &offset);
- switch (group->type)
- {
- case NMI_GROUP:
- switch (offset)
- {
- case 0:
- HW_TRACE ((me, "write-icr group=%d:0 nmi 0x%02x",
- group->gid, val));
- group->request &= ~EXTRACT_ID (val);
- break;
- /* Special backdoor access to SYSEF flag from CPU. See
- interp.c:program_interrupt(). */
- case 3:
- HW_TRACE ((me, "write-icr-special group=%d:0 nmi 0x%02x",
- group->gid, val));
- group->request |= EXTRACT_ID (val);
- default:
- break;
- }
- break;
- case LEVEL_GROUP:
- switch (offset)
- {
- case 0: /* request/detect */
- /* Clear any ID bits and then set them according to IR */
- HW_TRACE ((me, "write-icr group=%d:0 level 0x%02x %x:%x:%x",
- group->gid, val,
- group->request, EXTRACT_IR (val), EXTRACT_ID (val)));
- group->request =
- ((EXTRACT_IR (val) & EXTRACT_ID (val))
- | (EXTRACT_IR (val) & group->request)
- | (~EXTRACT_IR (val) & ~EXTRACT_ID (val) & group->request));
- break;
- case 1: /* level/enable */
- HW_TRACE ((me, "write-icr group=%d:1 level 0x%02x",
- group->gid, val));
- group->level = EXTRACT_LV (val);
- group->enable = EXTRACT_IE (val);
- break;
- default:
- /* ignore */
- break;
- }
- push_interrupt_level (me, controller);
- break;
- default:
- break;
- }
- }
- /* Read the IAGR (Interrupt accepted group register) */
- static uint8_t
- read_iagr (struct hw *me,
- struct mn103int *controller,
- unsigned_word offset)
- {
- uint8_t val;
- switch (offset)
- {
- case 0:
- {
- if (!(controller->group[controller->interrupt_accepted_group].request
- & controller->group[controller->interrupt_accepted_group].enable))
- {
- /* oops, lost the request */
- val = 0;
- HW_TRACE ((me, "read-iagr:0 lost-0"));
- }
- else
- {
- val = (controller->interrupt_accepted_group << 2);
- HW_TRACE ((me, "read-iagr:0 %d", (int) val));
- }
- break;
- }
- case 1:
- val = 0;
- HW_TRACE ((me, "read-iagr:1 %d", (int) val));
- break;
- default:
- val = 0;
- HW_TRACE ((me, "read-iagr 0x%08lx bad offset", (long) offset));
- break;
- }
- return val;
- }
- /* Reads/writes to the EXTMD (external interrupt trigger configuration
- register) */
- static struct mn103int_group *
- external_group (struct mn103int *controller,
- unsigned_word offset)
- {
- switch (offset)
- {
- case 0:
- return &controller->group[IRQ0_PORT/4];
- case 1:
- return &controller->group[IRQ4_PORT/4];
- default:
- return NULL;
- }
- }
- static uint8_t
- read_extmd (struct hw *me,
- struct mn103int *controller,
- unsigned_word offset)
- {
- int gid;
- uint8_t val = 0;
- struct mn103int_group *group = external_group (controller, offset);
- if (group != NULL)
- {
- for (gid = 0; gid < 4; gid++)
- {
- val |= (group[gid].trigger << (gid * 2));
- }
- }
- HW_TRACE ((me, "read-extmd 0x%02lx", (long) val));
- return val;
- }
- static void
- write_extmd (struct hw *me,
- struct mn103int *controller,
- unsigned_word offset,
- uint8_t val)
- {
- int gid;
- struct mn103int_group *group = external_group (controller, offset);
- if (group != NULL)
- {
- for (gid = 0; gid < 4; gid++)
- {
- group[gid].trigger = (val >> (gid * 2)) & 0x3;
- /* MAYBE: interrupts already pending? */
- }
- }
- HW_TRACE ((me, "write-extmd 0x%02lx", (long) val));
- }
- /* generic read/write */
- static int
- decode_addr (struct hw *me,
- struct mn103int *controller,
- unsigned_word address,
- unsigned_word *offset)
- {
- int i;
- for (i = 0; i < NR_BLOCKS; i++)
- {
- if (address >= controller->block[i].base
- && address <= controller->block[i].bound)
- {
- *offset = address - controller->block[i].base;
- return i;
- }
- }
- hw_abort (me, "bad address");
- return -1;
- }
- static unsigned
- mn103int_io_read_buffer (struct hw *me,
- void *dest,
- int space,
- unsigned_word base,
- unsigned nr_bytes)
- {
- struct mn103int *controller = hw_data (me);
- uint8_t *buf = dest;
- unsigned byte;
- /* HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes)); */
- for (byte = 0; byte < nr_bytes; byte++)
- {
- unsigned_word address = base + byte;
- unsigned_word offset;
- switch (decode_addr (me, controller, address, &offset))
- {
- case ICR_BLOCK:
- buf[byte] = read_icr (me, controller, offset);
- break;
- case IAGR_BLOCK:
- buf[byte] = read_iagr (me, controller, offset);
- break;
- case EXTMD_BLOCK:
- buf[byte] = read_extmd (me, controller, offset);
- break;
- default:
- hw_abort (me, "bad switch");
- }
- }
- return nr_bytes;
- }
- static unsigned
- mn103int_io_write_buffer (struct hw *me,
- const void *source,
- int space,
- unsigned_word base,
- unsigned nr_bytes)
- {
- struct mn103int *controller = hw_data (me);
- const uint8_t *buf = source;
- unsigned byte;
- /* HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes)); */
- for (byte = 0; byte < nr_bytes; byte++)
- {
- unsigned_word address = base + byte;
- unsigned_word offset;
- switch (decode_addr (me, controller, address, &offset))
- {
- case ICR_BLOCK:
- write_icr (me, controller, offset, buf[byte]);
- break;
- case IAGR_BLOCK:
- /* not allowed */
- break;
- case EXTMD_BLOCK:
- write_extmd (me, controller, offset, buf[byte]);
- break;
- default:
- hw_abort (me, "bad switch");
- }
- }
- return nr_bytes;
- }
- static int
- mn103int_ioctl(struct hw *me,
- hw_ioctl_request request,
- va_list ap)
- {
- struct mn103int *controller = (struct mn103int *)hw_data(me);
- controller->group[0].request = EXTRACT_ID(4);
- mn103int_port_event(me, 2 /* nmi_port(syserr) */, NULL, 0, 0);
- return 0;
- }
- const struct hw_descriptor dv_mn103int_descriptor[] = {
- { "mn103int", mn103int_finish, },
- { NULL },
- };
|