xref: /openbmc/qemu/hw/intc/ppc-uic.c (revision f101c9fe)
1 /*
2  * "Universal" Interrupt Controller for PowerPPC 4xx embedded processors
3  *
4  * Copyright (c) 2007 Jocelyn Mayer
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 
25 #include "qemu/osdep.h"
26 #include "hw/intc/ppc-uic.h"
27 #include "hw/irq.h"
28 #include "cpu.h"
29 #include "hw/ppc/ppc.h"
30 #include "hw/qdev-properties.h"
31 #include "migration/vmstate.h"
32 #include "qapi/error.h"
33 
34 enum {
35     DCR_UICSR  = 0x000,
36     DCR_UICSRS = 0x001,
37     DCR_UICER  = 0x002,
38     DCR_UICCR  = 0x003,
39     DCR_UICPR  = 0x004,
40     DCR_UICTR  = 0x005,
41     DCR_UICMSR = 0x006,
42     DCR_UICVR  = 0x007,
43     DCR_UICVCR = 0x008,
44     DCR_UICMAX = 0x009,
45 };
46 
47 /*#define DEBUG_UIC*/
48 
49 #ifdef DEBUG_UIC
50 #  define LOG_UIC(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__)
51 #else
52 #  define LOG_UIC(...) do { } while (0)
53 #endif
54 
55 static void ppcuic_trigger_irq(PPCUIC *uic)
56 {
57     uint32_t ir, cr;
58     int start, end, inc, i;
59 
60     /* Trigger interrupt if any is pending */
61     ir = uic->uicsr & uic->uicer & (~uic->uiccr);
62     cr = uic->uicsr & uic->uicer & uic->uiccr;
63     LOG_UIC("%s: uicsr %08" PRIx32 " uicer %08" PRIx32
64                 " uiccr %08" PRIx32 "\n"
65                 "   %08" PRIx32 " ir %08" PRIx32 " cr %08" PRIx32 "\n",
66                 __func__, uic->uicsr, uic->uicer, uic->uiccr,
67                 uic->uicsr & uic->uicer, ir, cr);
68     if (ir != 0x0000000) {
69         LOG_UIC("Raise UIC interrupt\n");
70         qemu_irq_raise(uic->output_int);
71     } else {
72         LOG_UIC("Lower UIC interrupt\n");
73         qemu_irq_lower(uic->output_int);
74     }
75     /* Trigger critical interrupt if any is pending and update vector */
76     if (cr != 0x0000000) {
77         qemu_irq_raise(uic->output_cint);
78         if (uic->use_vectors) {
79             /* Compute critical IRQ vector */
80             if (uic->uicvcr & 1) {
81                 start = 31;
82                 end = 0;
83                 inc = -1;
84             } else {
85                 start = 0;
86                 end = 31;
87                 inc = 1;
88             }
89             uic->uicvr = uic->uicvcr & 0xFFFFFFFC;
90             for (i = start; i <= end; i += inc) {
91                 if (cr & (1 << i)) {
92                     uic->uicvr += (i - start) * 512 * inc;
93                     break;
94                 }
95             }
96         }
97         LOG_UIC("Raise UIC critical interrupt - "
98                     "vector %08" PRIx32 "\n", uic->uicvr);
99     } else {
100         LOG_UIC("Lower UIC critical interrupt\n");
101         qemu_irq_lower(uic->output_cint);
102         uic->uicvr = 0x00000000;
103     }
104 }
105 
106 static void ppcuic_set_irq(void *opaque, int irq_num, int level)
107 {
108     PPCUIC *uic;
109     uint32_t mask, sr;
110 
111     uic = opaque;
112     mask = 1U << (31 - irq_num);
113     LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32
114                 " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n",
115                 __func__, irq_num, level,
116                 uic->uicsr, mask, uic->uicsr & mask, level << irq_num);
117     if (irq_num < 0 || irq_num > 31) {
118         return;
119     }
120     sr = uic->uicsr;
121 
122     /* Update status register */
123     if (uic->uictr & mask) {
124         /* Edge sensitive interrupt */
125         if (level == 1) {
126             uic->uicsr |= mask;
127         }
128     } else {
129         /* Level sensitive interrupt */
130         if (level == 1) {
131             uic->uicsr |= mask;
132             uic->level |= mask;
133         } else {
134             uic->uicsr &= ~mask;
135             uic->level &= ~mask;
136         }
137     }
138     LOG_UIC("%s: irq %d level %d sr %" PRIx32 " => "
139                 "%08" PRIx32 "\n", __func__, irq_num, level, uic->uicsr, sr);
140     if (sr != uic->uicsr) {
141         ppcuic_trigger_irq(uic);
142     }
143 }
144 
145 static uint32_t dcr_read_uic(void *opaque, int dcrn)
146 {
147     PPCUIC *uic;
148     uint32_t ret;
149 
150     uic = opaque;
151     dcrn -= uic->dcr_base;
152     switch (dcrn) {
153     case DCR_UICSR:
154     case DCR_UICSRS:
155         ret = uic->uicsr;
156         break;
157     case DCR_UICER:
158         ret = uic->uicer;
159         break;
160     case DCR_UICCR:
161         ret = uic->uiccr;
162         break;
163     case DCR_UICPR:
164         ret = uic->uicpr;
165         break;
166     case DCR_UICTR:
167         ret = uic->uictr;
168         break;
169     case DCR_UICMSR:
170         ret = uic->uicsr & uic->uicer;
171         break;
172     case DCR_UICVR:
173         if (!uic->use_vectors) {
174             goto no_read;
175         }
176         ret = uic->uicvr;
177         break;
178     case DCR_UICVCR:
179         if (!uic->use_vectors) {
180             goto no_read;
181         }
182         ret = uic->uicvcr;
183         break;
184     default:
185     no_read:
186         ret = 0x00000000;
187         break;
188     }
189 
190     return ret;
191 }
192 
193 static void dcr_write_uic(void *opaque, int dcrn, uint32_t val)
194 {
195     PPCUIC *uic;
196 
197     uic = opaque;
198     dcrn -= uic->dcr_base;
199     LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val);
200     switch (dcrn) {
201     case DCR_UICSR:
202         uic->uicsr &= ~val;
203         uic->uicsr |= uic->level;
204         ppcuic_trigger_irq(uic);
205         break;
206     case DCR_UICSRS:
207         uic->uicsr |= val;
208         ppcuic_trigger_irq(uic);
209         break;
210     case DCR_UICER:
211         uic->uicer = val;
212         ppcuic_trigger_irq(uic);
213         break;
214     case DCR_UICCR:
215         uic->uiccr = val;
216         ppcuic_trigger_irq(uic);
217         break;
218     case DCR_UICPR:
219         uic->uicpr = val;
220         break;
221     case DCR_UICTR:
222         uic->uictr = val;
223         ppcuic_trigger_irq(uic);
224         break;
225     case DCR_UICMSR:
226         break;
227     case DCR_UICVR:
228         break;
229     case DCR_UICVCR:
230         uic->uicvcr = val & 0xFFFFFFFD;
231         ppcuic_trigger_irq(uic);
232         break;
233     }
234 }
235 
236 static void ppc_uic_reset(DeviceState *dev)
237 {
238     PPCUIC *uic = PPC_UIC(dev);
239 
240     uic->uiccr = 0x00000000;
241     uic->uicer = 0x00000000;
242     uic->uicpr = 0x00000000;
243     uic->uicsr = 0x00000000;
244     uic->uictr = 0x00000000;
245     if (uic->use_vectors) {
246         uic->uicvcr = 0x00000000;
247         uic->uicvr = 0x0000000;
248     }
249 }
250 
251 static void ppc_uic_realize(DeviceState *dev, Error **errp)
252 {
253     PPCUIC *uic = PPC_UIC(dev);
254     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
255     PowerPCCPU *cpu;
256     int i;
257 
258     if (!uic->cpu) {
259         /* This is a programming error in the code using this device */
260         error_setg(errp, "ppc-uic 'cpu' link property was not set");
261         return;
262     }
263 
264     cpu = POWERPC_CPU(uic->cpu);
265     for (i = 0; i < DCR_UICMAX; i++) {
266         ppc_dcr_register(&cpu->env, uic->dcr_base + i, uic,
267                          &dcr_read_uic, &dcr_write_uic);
268     }
269 
270     sysbus_init_irq(sbd, &uic->output_int);
271     sysbus_init_irq(sbd, &uic->output_cint);
272     qdev_init_gpio_in(dev, ppcuic_set_irq, UIC_MAX_IRQ);
273 }
274 
275 static Property ppc_uic_properties[] = {
276     DEFINE_PROP_LINK("cpu", PPCUIC, cpu, TYPE_CPU, CPUState *),
277     DEFINE_PROP_UINT32("dcr-base", PPCUIC, dcr_base, 0xc0),
278     DEFINE_PROP_BOOL("use-vectors", PPCUIC, use_vectors, true),
279     DEFINE_PROP_END_OF_LIST()
280 };
281 
282 static const VMStateDescription ppc_uic_vmstate = {
283     .name = "ppc-uic",
284     .version_id = 1,
285     .minimum_version_id = 1,
286     .fields = (VMStateField[]) {
287         VMSTATE_UINT32(level, PPCUIC),
288         VMSTATE_UINT32(uicsr, PPCUIC),
289         VMSTATE_UINT32(uicer, PPCUIC),
290         VMSTATE_UINT32(uiccr, PPCUIC),
291         VMSTATE_UINT32(uicpr, PPCUIC),
292         VMSTATE_UINT32(uictr, PPCUIC),
293         VMSTATE_UINT32(uicvcr, PPCUIC),
294         VMSTATE_UINT32(uicvr, PPCUIC),
295         VMSTATE_END_OF_LIST()
296     },
297 };
298 
299 static void ppc_uic_class_init(ObjectClass *klass, void *data)
300 {
301     DeviceClass *dc = DEVICE_CLASS(klass);
302 
303     dc->reset = ppc_uic_reset;
304     dc->realize = ppc_uic_realize;
305     dc->vmsd = &ppc_uic_vmstate;
306     device_class_set_props(dc, ppc_uic_properties);
307 }
308 
309 static const TypeInfo ppc_uic_info = {
310     .name = TYPE_PPC_UIC,
311     .parent = TYPE_SYS_BUS_DEVICE,
312     .instance_size = sizeof(PPCUIC),
313     .class_init = ppc_uic_class_init,
314 };
315 
316 static void ppc_uic_register_types(void)
317 {
318     type_register_static(&ppc_uic_info);
319 }
320 
321 type_init(ppc_uic_register_types);
322