xref: /openbmc/qemu/hw/m68k/mcf_intc.c (revision 14b61600)
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     case 0x1c:
106         if (val & 0x40) {
107             s->imr = ~0ull;
108         } else {
109             s->imr |= (0x1ull << (val & 0x3f));
110         }
111         break;
112     case 0x1d:
113         if (val & 0x40) {
114             s->imr = 0ull;
115         } else {
116             s->imr &= ~(0x1ull << (val & 0x3f));
117         }
118         break;
119     default:
120         hw_error("mcf_intc_write: Bad write offset %d\n", offset);
121         break;
122     }
123     mcf_intc_update(s);
124 }
125 
126 static void mcf_intc_set_irq(void *opaque, int irq, int level)
127 {
128     mcf_intc_state *s = (mcf_intc_state *)opaque;
129     if (irq >= 64)
130         return;
131     if (level)
132         s->ipr |= 1ull << irq;
133     else
134         s->ipr &= ~(1ull << irq);
135     mcf_intc_update(s);
136 }
137 
138 static void mcf_intc_reset(mcf_intc_state *s)
139 {
140     s->imr = ~0ull;
141     s->ipr = 0;
142     s->ifr = 0;
143     s->enabled = 0;
144     memset(s->icr, 0, 64);
145     s->active_vector = 24;
146 }
147 
148 static const MemoryRegionOps mcf_intc_ops = {
149     .read = mcf_intc_read,
150     .write = mcf_intc_write,
151     .endianness = DEVICE_NATIVE_ENDIAN,
152 };
153 
154 qemu_irq *mcf_intc_init(MemoryRegion *sysmem,
155                         hwaddr base,
156                         M68kCPU *cpu)
157 {
158     mcf_intc_state *s;
159 
160     s = g_malloc0(sizeof(mcf_intc_state));
161     s->cpu = cpu;
162     mcf_intc_reset(s);
163 
164     memory_region_init_io(&s->iomem, NULL, &mcf_intc_ops, s, "mcf", 0x100);
165     memory_region_add_subregion(sysmem, base, &s->iomem);
166 
167     return qemu_allocate_irqs(mcf_intc_set_irq, s, 64);
168 }
169