diff options
author | Andrew Cagney <cagney@redhat.com> | 1998-03-25 04:15:38 +0000 |
---|---|---|
committer | Andrew Cagney <cagney@redhat.com> | 1998-03-25 04:15:38 +0000 |
commit | 6100784a60a46ce81f1264a630cf003f74df78ce (patch) | |
tree | 872d16ff0f41d3fa580c29f797b16d1aded249a7 /sim/mn10300/dv-mn103int.c | |
parent | 8388c9a5646ad6b1eff9db4851ccd8761decad9e (diff) | |
download | gdb-6100784a60a46ce81f1264a630cf003f74df78ce.zip gdb-6100784a60a46ce81f1264a630cf003f74df78ce.tar.gz gdb-6100784a60a46ce81f1264a630cf003f74df78ce.tar.bz2 |
* interp.c (sim_open): Create second 1mb memory region at 0x40000000.
(sim_open): Create a device tree.
(sim-hw.h): Include.
(do_interrupt): Delete, needs to use dv-mn103cpu.c
* dv-mn103int.c, dv-mn103cpu.c: New files.
Diffstat (limited to 'sim/mn10300/dv-mn103int.c')
-rw-r--r-- | sim/mn10300/dv-mn103int.c | 785 |
1 files changed, 785 insertions, 0 deletions
diff --git a/sim/mn10300/dv-mn103int.c b/sim/mn10300/dv-mn103int.c new file mode 100644 index 0000000..061b9b8 --- /dev/null +++ b/sim/mn10300/dv-mn103int.c @@ -0,0 +1,785 @@ +/* This file is part of the program GDB, the GU debugger. + + Copyright (C) 1998 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#include "sim-main.h" +#include "hw-base.h" + +/* DEVICE + + + mn103int - mn10300 interrupt controller + + + DESCRIPTION + + + Implements the mn10300 interrupt controller described in the + mn10300 user guide. + + + PROPERTIES + + + reg = <icr-adr> <icr-siz> <iagr-adr> <iadr-siz> <extmd-adr> <extmd-siz> + + Specify the address of the ICR (total of 25 registers), IAGR and + EXTMD registers (within the parent bus). + + The reg property value `0x34000100 0x68 0x34000200 0x8 0x3400280 + 0x8' locates the interrupt controller at the addresses specified in + the mn10300 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 25 + groups (0..24) 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 sensative interrupts, the interrupt controller ignores + active HIGH/LOW settings and instead always interprets a nonzero + port value as an interupt assertion and a zero port value as a + negation. + + */ + + +/* The interrupt groups - numbered according to mn10300 convention */ + +enum mn103int_trigger { + ACTIVE_LOW, + ACTIVE_HIGH, + POSITIVE_EDGE, + NEGATIVE_EDGE, +}; + +enum mn103int_type { + NMI_GROUP, + INT_GROUP, +}; + +struct mn103int_group { + 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_INT_GROUP = 2, + LAST_INT_GROUP = 24, + 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, + G24_PORT = 96, + NR_G_PORTS = 100, + 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, 0, input_port, }, + { "timer-1-underflow", G2_PORT + 1, 0, input_port, }, + { "timer-2-underflow", G2_PORT + 2, 0, input_port, }, + { "timer-3-underflow", G2_PORT + 3, 0, input_port, }, + { "timer-4-underflow", G3_PORT + 0, 0, input_port, }, + { "timer-5-underflow", G3_PORT + 1, 0, input_port, }, + { "timer-6-underflow", G3_PORT + 2, 0, input_port, }, + { "timer-7-underflow", G3_PORT + 3, 0, input_port, }, + + { "timer-8-underflow", G4_PORT + 0, 0, input_port, }, + { "timer-8-compare-a", G4_PORT + 1, 0, input_port, }, + { "timer-8-compare-b", G4_PORT + 2, 0, input_port, }, + + { "timer-9-underflow", G5_PORT + 0, 0, input_port, }, + { "timer-9-compare-a", G5_PORT + 1, 0, input_port, }, + { "timer-9-compare-b", G5_PORT + 2, 0, input_port, }, + + { "timer-10-underflow", G6_PORT + 0, 0, input_port, }, + { "timer-10-compare-a", G6_PORT + 1, 0, input_port, }, + { "timer-10-compare-b", G6_PORT + 2, 0, input_port, }, + { "timer-10-compare-c", G6_PORT + 3, 0, input_port, }, + + { "timer-11-underflow", G7_PORT + 0, 0, input_port, }, + { "timer-11-compare-a", G7_PORT + 1, 0, input_port, }, + { "timer-11-compare-b", G7_PORT + 2, 0, input_port, }, + { "timer-11-compare-c", G7_PORT + 3, 0, input_port, }, + + { "timer-12-underflow", G8_PORT + 0, 0, input_port, }, + { "timer-12-compare-a", G8_PORT + 1, 0, input_port, }, + { "timer-12-compare-b", G8_PORT + 2, 0, input_port, }, + { "timer-12-compare-c", G8_PORT + 3, 0, input_port, }, + + { "timer-11-compare-d", G9_PORT + 0, 0, input_port, }, + { "timer-12-compare-d", G9_PORT + 1, 0, input_port, }, + + { "dma-0-end", G10_PORT, 0, input_port, }, + { "dma-1-end", G11_PORT, 0, input_port, }, + { "dma-2-end", G12_PORT, 0, input_port, }, + { "dma-3-end", G13_PORT, 0, input_port, }, + + { "serial-0-recieve", G14_PORT + 0, 0, input_port, }, + { "serial-0-transmit", G14_PORT + 1, 0, input_port, }, + + { "serial-1-recieve", G15_PORT + 0, 0, input_port, }, + { "serial-1-transmit", G15_PORT + 1, 0, input_port, }, + + { "irq-0", G16_PORT, 0, input_port, }, + { "irq-1", G17_PORT, 0, input_port, }, + { "irq-2", G18_PORT, 0, input_port, }, + { "irq-3", G19_PORT, 0, input_port, }, + { "irq-4", G20_PORT, 0, input_port, }, + { "irq-5", G21_PORT, 0, input_port, }, + { "irq-6", G22_PORT, 0, input_port, }, + { "irq-7", G23_PORT, 0, input_port, }, + + { "ad-end", G24_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_callback mn103int_io_read_buffer; +static hw_io_write_buffer_callback mn103int_io_write_buffer; +static hw_port_event_callback mn103int_port_event; + +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); + + /* 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->enable = 0xf; + group->trigger = NEGATIVE_EDGE; + if (FIRST_NMI_GROUP <= gid && gid <= LAST_NMI_GROUP) + { + group->type = NMI_GROUP; + } + else if (FIRST_INT_GROUP <= gid && gid <= LAST_INT_GROUP) + { + group->type = INT_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_INT_GROUP; gid <= LAST_INT_GROUP; gid++) + { + struct mn103int_group *group = &controller->group[gid]; + if ((group->request & group->enable) != 0) + { + 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, NULL, NULL_CIA); +} + + +/* 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, + sim_cpu *processor, + sim_cia cia) +{ + 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, 0, NULL, NULL_CIA); + } + break; + } + + case INT_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 unsigned8 +read_icr (struct hw *me, + struct mn103int *controller, + struct mn103int_group *group, + unsigned_word offset) +{ + unsigned8 val = 0; + switch (group->type) + { + + case NMI_GROUP: + switch (offset) + { + case 0: + val = INSERT_ID (group->request); + HW_TRACE ((me, "read-icr 0x%02x", val)); + break; + } + break; + + case INT_GROUP: + switch (offset) + { + case 0: + val = (INSERT_IR (group->request) + | INSERT_ID (group->request & group->enable)); + HW_TRACE ((me, "read-icr 0:0x%02x", val)); + break; + case 1: + val = (INSERT_LV (group->level) + | INSERT_IE (group->enable)); + HW_TRACE ((me, "read-icr 1:0x%02x", val)); + break; + } + } + + return val; +} + +static void +write_icr (struct hw *me, + struct mn103int *controller, + struct mn103int_group *group, + unsigned_word offset, + unsigned8 val) +{ + switch (group->type) + { + + case NMI_GROUP: + switch (offset) + { + case 0: + HW_TRACE ((me, "write-icr 0x%02x", val)); + group->request &= ~EXTRACT_ID (val); + break; + default: + break; + } + break; + + case INT_GROUP: + switch (offset) + { + case 0: /* request/detect */ + /* Clear any ID bits and then set them according to IR */ + HW_TRACE ((me, "write-icr 0:0x%02x", val)); + group->request &= EXTRACT_ID (val); + group->request |= EXTRACT_IR (val) & EXTRACT_ID (val); + break; + case 1: /* level/enable */ + HW_TRACE ((me, "write-icr 1:0x%02x", val)); + group->level = EXTRACT_LV (val); + group->enable = EXTRACT_IE (val); + break; + default: + /* ignore */ + break; + } + push_interrupt_level (me, controller); + break; + + } +} + + +/* Read the IAGR (Interrupt accepted group register) */ + +static unsigned8 +read_iagr (struct hw *me, + struct mn103int *controller, + unsigned_word offset) +{ + unsigned8 val; + switch (offset) + { + case 0: + { + val = (controller->interrupt_accepted_group << 2); + if (!(controller->group[val].request + & controller->group[val].enable)) + /* oops, lost the request */ + val = 0; + break; + } + default: + val = 0; + } + 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[16]; + case 1: + return &controller->group[20]; + default: + return NULL; + } +} + +static unsigned8 +read_extmd (struct hw *me, + struct mn103int *controller, + unsigned_word offset) +{ + int gid; + unsigned8 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)); + } + } + return val; +} + +static void +write_extmd (struct hw *me, + struct mn103int *controller, + unsigned_word offset, + unsigned8 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? */ + } + } +} + + +/* generic read/write */ + +static int +decode_addr (struct hw *me, + struct mn103int *controller, + unsigned_word addr) +{ + int i; + for (i = 0; i < NR_BLOCKS; i++) + { + if (addr >= controller->block[i].base + && addr <= controller->block[i].bound) + return i; + } + hw_abort (me, "bad address"); + return -1; +} + +static struct mn103int_group * +decode_group (struct hw *me, + struct mn103int *controller, + unsigned_word addr) +{ + unsigned_word offset = (addr - controller->block[ICR_BLOCK].base); + int gid = (offset / 8) % NR_GROUPS; + return &controller->group[gid]; +} + +static unsigned +mn103int_io_read_buffer (struct hw *me, + void *dest, + int space, + unsigned_word base, + unsigned nr_bytes, + sim_cpu *processor, + sim_cia cia) +{ + struct mn103int *controller = hw_data (me); + unsigned8 *buf = dest; + unsigned byte; + for (byte = 0; byte < nr_bytes; byte++) + { + unsigned_word address = base + byte; + switch (decode_addr (me, controller, address)) + { + case ICR_BLOCK: + buf[byte] = read_icr (me, controller, + decode_group (me, controller, address), + address); + break; + case IAGR_BLOCK: + buf[byte] = read_iagr (me, controller, address); + break; + case EXTMD_BLOCK: + buf[byte] = read_extmd (me, controller, address); + 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, + sim_cpu *cpu, + sim_cia cia) +{ + struct mn103int *controller = hw_data (me); + const unsigned8 *buf = source; + unsigned byte; + for (byte = 0; byte < nr_bytes; byte++) + { + unsigned_word address = base + byte; + switch (decode_addr (me, controller, address)) + { + case ICR_BLOCK: + write_icr (me, controller, + decode_group (me, controller, address), + address, buf[byte]); + break; + case IAGR_BLOCK: + /* not allowed */ + break; + case EXTMD_BLOCK: + write_extmd (me, controller, address, buf[byte]); + break; + default: + hw_abort (me, "bad switch"); + } + } + return nr_bytes; +} + + +const struct hw_device_descriptor dv_mn103int_descriptor[] = { + { "mn103int", mn103int_finish, }, + { NULL }, +}; |