#include "sim-main.h"
#include "hw-main.h"
#include "sim-hw.h"
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,
};
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;
};
enum {
NMI_PORT,
LEVEL_PORT,
};
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[] = {
{ "nmi", NMI_PORT, 0, output_port, },
{ "level", LEVEL_PORT, 0, output_port, },
{ "ack", ACK_PORT, 0, input_port, },
{ "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, },
{ "int", 0, NR_G_PORTS, input_port, },
{ NULL, },
};
#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))
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_mn103int_regs (me, controller);
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");
}
}
static int
find_highest_interrupt_group (struct hw *me,
struct mn103int *controller)
{
int gid;
int selected;
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)
{
if (group->level < controller->group[selected].level)
{
selected = gid;
}
}
}
return selected;
}
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);
}
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);
gid = (my_port % NR_G_PORTS) / 4;
group = &controller->group[gid];
iid = (my_port % 4);
interrupt = 1 << iid;
if (level)
group->input |= interrupt;
else
group->input &= ~interrupt;
switch (group->trigger)
{
case ACTIVE_LOW:
case ACTIVE_HIGH:
if (level)
group->request |= interrupt;
break;
case NEGATIVE_EDGE:
case POSITIVE_EDGE:
group->request |= interrupt;
}
switch (group->type)
{
case NMI_GROUP:
{
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:
{
HW_TRACE ((me, "port-in port=%d group=%d interrupt=%d - INT",
my_port, gid, iid));
push_interrupt_level (me, controller);
break;
}
}
break;
}
}
}
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 unsigned8
read_icr (struct hw *me,
struct mn103int *controller,
unsigned_word base)
{
unsigned_word offset;
struct mn103int_group *group = decode_group (me, controller, base, &offset);
unsigned8 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,
unsigned8 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;
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:
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:
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:
break;
}
push_interrupt_level (me, controller);
break;
default:
break;
}
}
static unsigned8
read_iagr (struct hw *me,
struct mn103int *controller,
unsigned_word offset)
{
unsigned8 val;
switch (offset)
{
case 0:
{
if (!(controller->group[controller->interrupt_accepted_group].request
& controller->group[controller->interrupt_accepted_group].enable))
{
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;
}
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 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));
}
}
HW_TRACE ((me, "read-extmd 0x%02lx", (long) val));
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;
}
}
HW_TRACE ((me, "write-extmd 0x%02lx", (long) val));
}
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);
unsigned8 *buf = dest;
unsigned byte;
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 unsigned8 *buf = source;
unsigned byte;
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:
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 , NULL, 0, 0);
return 0;
}
const struct hw_descriptor dv_mn103int_descriptor[] = {
{ "mn103int", mn103int_finish, },
{ NULL },
};