xref: /openbmc/qemu/hw/m68k/mcf_intc.c (revision a719a27c)
1 /*
2  * ColdFire Interrupt Controller emulation.
3  *
4  * Copyright (c) 2007 CodeSourcery.
5  *
6  * This code is licensed under the GPL
7  */
8 #include "hw/hw.h"
9 #include "hw/m68k/mcf.h"
10 #include "exec/address-spaces.h"
11 
12 typedef struct {
13     MemoryRegion iomem;
14     uint64_t ipr;
15     uint64_t imr;
16     uint64_t ifr;
17     uint64_t enabled;
18     uint8_t icr[64];
19     M68kCPU *cpu;
20     int active_vector;
21 } mcf_intc_state;
22 
23 static void mcf_intc_update(mcf_intc_state *s)
24 {
25     uint64_t active;
26     int i;
27     int best;
28     int best_level;
29 
30     active = (s->ipr | s->ifr) & s->enabled & ~s->imr;
31     best_level = 0;
32     best = 64;
33     if (active) {
34         for (i = 0; i < 64; i++) {
35             if ((active & 1) != 0 && s->icr[i] >= best_level) {
36                 best_level = s->icr[i];
37                 best = i;
38             }
39             active >>= 1;
40         }
41     }
42     s->active_vector = ((best == 64) ? 24 : (best + 64));
43     m68k_set_irq_level(s->cpu, best_level, s->active_vector);
44 }
45 
46 static uint64_t mcf_intc_read(void *opaque, hwaddr addr,
47                               unsigned size)
48 {
49     int offset;
50     mcf_intc_state *s = (mcf_intc_state *)opaque;
51     offset = addr & 0xff;
52     if (offset >= 0x40 && offset < 0x80) {
53         return s->icr[offset - 0x40];
54     }
55     switch (offset) {
56     case 0x00:
57         return (uint32_t)(s->ipr >> 32);
58     case 0x04:
59         return (uint32_t)s->ipr;
60     case 0x08:
61         return (uint32_t)(s->imr >> 32);
62     case 0x0c:
63         return (uint32_t)s->imr;
64     case 0x10:
65         return (uint32_t)(s->ifr >> 32);
66     case 0x14:
67         return (uint32_t)s->ifr;
68     case 0xe0: /* SWIACK.  */
69         return s->active_vector;
70     case 0xe1: case 0xe2: case 0xe3: case 0xe4:
71     case 0xe5: case 0xe6: case 0xe7:
72         /* LnIACK */
73         hw_error("mcf_intc_read: LnIACK not implemented\n");
74     default:
75         return 0;
76     }
77 }
78 
79 static void mcf_intc_write(void *opaque, hwaddr addr,
80                            uint64_t val, unsigned size)
81 {
82     int offset;
83     mcf_intc_state *s = (mcf_intc_state *)opaque;
84     offset = addr & 0xff;
85     if (offset >= 0x40 && offset < 0x80) {
86         int n = offset - 0x40;
87         s->icr[n] = val;
88         if (val == 0)
89             s->enabled &= ~(1ull << n);
90         else
91             s->enabled |= (1ull << n);
92         mcf_intc_update(s);
93         return;
94     }
95     switch (offset) {
96     case 0x00: case 0x04:
97         /* Ignore IPR writes.  */
98         return;
99     case 0x08:
100         s->imr = (s->imr & 0xffffffff) | ((uint64_t)val << 32);
101         break;
102     case 0x0c:
103         s->imr = (s->imr & 0xffffffff00000000ull) | (uint32_t)val;
104         break;
105     default:
106         hw_error("mcf_intc_write: Bad write offset %d\n", offset);
107         break;
108     }
109     mcf_intc_update(s);
110 }
111 
112 static void mcf_intc_set_irq(void *opaque, int irq, int level)
113 {
114     mcf_intc_state *s = (mcf_intc_state *)opaque;
115     if (irq >= 64)
116         return;
117     if (level)
118         s->ipr |= 1ull << irq;
119     else
120         s->ipr &= ~(1ull << irq);
121     mcf_intc_update(s);
122 }
123 
124 static void mcf_intc_reset(mcf_intc_state *s)
125 {
126     s->imr = ~0ull;
127     s->ipr = 0;
128     s->ifr = 0;
129     s->enabled = 0;
130     memset(s->icr, 0, 64);
131     s->active_vector = 24;
132 }
133 
134 static const MemoryRegionOps mcf_intc_ops = {
135     .read = mcf_intc_read,
136     .write = mcf_intc_write,
137     .endianness = DEVICE_NATIVE_ENDIAN,
138 };
139 
140 qemu_irq *mcf_intc_init(MemoryRegion *sysmem,
141                         hwaddr base,
142                         M68kCPU *cpu)
143 {
144     mcf_intc_state *s;
145 
146     s = g_malloc0(sizeof(mcf_intc_state));
147     s->cpu = cpu;
148     mcf_intc_reset(s);
149 
150     memory_region_init_io(&s->iomem, NULL, &mcf_intc_ops, s, "mcf", 0x100);
151     memory_region_add_subregion(sysmem, base, &s->iomem);
152 
153     return qemu_allocate_irqs(mcf_intc_set_irq, s, 64);
154 }
155