xref: /qemu/hw/intc/ppc-uic.c (revision 37dc4b5f7c5ef7300b3a1e2bc298db9edb0efdc5)
134d0831fSPeter Maydell /*
234d0831fSPeter Maydell  * "Universal" Interrupt Controller for PowerPPC 4xx embedded processors
334d0831fSPeter Maydell  *
434d0831fSPeter Maydell  * Copyright (c) 2007 Jocelyn Mayer
534d0831fSPeter Maydell  *
634d0831fSPeter Maydell  * Permission is hereby granted, free of charge, to any person obtaining a copy
734d0831fSPeter Maydell  * of this software and associated documentation files (the "Software"), to deal
834d0831fSPeter Maydell  * in the Software without restriction, including without limitation the rights
934d0831fSPeter Maydell  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1034d0831fSPeter Maydell  * copies of the Software, and to permit persons to whom the Software is
1134d0831fSPeter Maydell  * furnished to do so, subject to the following conditions:
1234d0831fSPeter Maydell  *
1334d0831fSPeter Maydell  * The above copyright notice and this permission notice shall be included in
1434d0831fSPeter Maydell  * all copies or substantial portions of the Software.
1534d0831fSPeter Maydell  *
1634d0831fSPeter Maydell  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1734d0831fSPeter Maydell  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1834d0831fSPeter Maydell  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1934d0831fSPeter Maydell  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2034d0831fSPeter Maydell  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2134d0831fSPeter Maydell  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2234d0831fSPeter Maydell  * THE SOFTWARE.
2334d0831fSPeter Maydell  */
2434d0831fSPeter Maydell 
2534d0831fSPeter Maydell #include "qemu/osdep.h"
2634d0831fSPeter Maydell #include "include/hw/intc/ppc-uic.h"
2734d0831fSPeter Maydell #include "hw/irq.h"
2834d0831fSPeter Maydell #include "cpu.h"
2934d0831fSPeter Maydell #include "hw/ppc/ppc.h"
3034d0831fSPeter Maydell #include "hw/qdev-properties.h"
3134d0831fSPeter Maydell #include "migration/vmstate.h"
3234d0831fSPeter Maydell #include "qapi/error.h"
3334d0831fSPeter Maydell 
3434d0831fSPeter Maydell enum {
3534d0831fSPeter Maydell     DCR_UICSR  = 0x000,
3634d0831fSPeter Maydell     DCR_UICSRS = 0x001,
3734d0831fSPeter Maydell     DCR_UICER  = 0x002,
3834d0831fSPeter Maydell     DCR_UICCR  = 0x003,
3934d0831fSPeter Maydell     DCR_UICPR  = 0x004,
4034d0831fSPeter Maydell     DCR_UICTR  = 0x005,
4134d0831fSPeter Maydell     DCR_UICMSR = 0x006,
4234d0831fSPeter Maydell     DCR_UICVR  = 0x007,
4334d0831fSPeter Maydell     DCR_UICVCR = 0x008,
4434d0831fSPeter Maydell     DCR_UICMAX = 0x009,
4534d0831fSPeter Maydell };
4634d0831fSPeter Maydell 
4734d0831fSPeter Maydell /*#define DEBUG_UIC*/
4834d0831fSPeter Maydell 
4934d0831fSPeter Maydell #ifdef DEBUG_UIC
5034d0831fSPeter Maydell #  define LOG_UIC(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__)
5134d0831fSPeter Maydell #else
5234d0831fSPeter Maydell #  define LOG_UIC(...) do { } while (0)
5334d0831fSPeter Maydell #endif
5434d0831fSPeter Maydell 
5534d0831fSPeter Maydell static void ppcuic_trigger_irq(PPCUIC *uic)
5634d0831fSPeter Maydell {
5734d0831fSPeter Maydell     uint32_t ir, cr;
5834d0831fSPeter Maydell     int start, end, inc, i;
5934d0831fSPeter Maydell 
6034d0831fSPeter Maydell     /* Trigger interrupt if any is pending */
6134d0831fSPeter Maydell     ir = uic->uicsr & uic->uicer & (~uic->uiccr);
6234d0831fSPeter Maydell     cr = uic->uicsr & uic->uicer & uic->uiccr;
6334d0831fSPeter Maydell     LOG_UIC("%s: uicsr %08" PRIx32 " uicer %08" PRIx32
6434d0831fSPeter Maydell                 " uiccr %08" PRIx32 "\n"
6534d0831fSPeter Maydell                 "   %08" PRIx32 " ir %08" PRIx32 " cr %08" PRIx32 "\n",
6634d0831fSPeter Maydell                 __func__, uic->uicsr, uic->uicer, uic->uiccr,
6734d0831fSPeter Maydell                 uic->uicsr & uic->uicer, ir, cr);
6834d0831fSPeter Maydell     if (ir != 0x0000000) {
6934d0831fSPeter Maydell         LOG_UIC("Raise UIC interrupt\n");
7034d0831fSPeter Maydell         qemu_irq_raise(uic->output_int);
7134d0831fSPeter Maydell     } else {
7234d0831fSPeter Maydell         LOG_UIC("Lower UIC interrupt\n");
7334d0831fSPeter Maydell         qemu_irq_lower(uic->output_int);
7434d0831fSPeter Maydell     }
7534d0831fSPeter Maydell     /* Trigger critical interrupt if any is pending and update vector */
7634d0831fSPeter Maydell     if (cr != 0x0000000) {
7734d0831fSPeter Maydell         qemu_irq_raise(uic->output_cint);
7834d0831fSPeter Maydell         if (uic->use_vectors) {
7934d0831fSPeter Maydell             /* Compute critical IRQ vector */
8034d0831fSPeter Maydell             if (uic->uicvcr & 1) {
8134d0831fSPeter Maydell                 start = 31;
8234d0831fSPeter Maydell                 end = 0;
8334d0831fSPeter Maydell                 inc = -1;
8434d0831fSPeter Maydell             } else {
8534d0831fSPeter Maydell                 start = 0;
8634d0831fSPeter Maydell                 end = 31;
8734d0831fSPeter Maydell                 inc = 1;
8834d0831fSPeter Maydell             }
8934d0831fSPeter Maydell             uic->uicvr = uic->uicvcr & 0xFFFFFFFC;
9034d0831fSPeter Maydell             for (i = start; i <= end; i += inc) {
9134d0831fSPeter Maydell                 if (cr & (1 << i)) {
9234d0831fSPeter Maydell                     uic->uicvr += (i - start) * 512 * inc;
9334d0831fSPeter Maydell                     break;
9434d0831fSPeter Maydell                 }
9534d0831fSPeter Maydell             }
9634d0831fSPeter Maydell         }
9734d0831fSPeter Maydell         LOG_UIC("Raise UIC critical interrupt - "
9834d0831fSPeter Maydell                     "vector %08" PRIx32 "\n", uic->uicvr);
9934d0831fSPeter Maydell     } else {
10034d0831fSPeter Maydell         LOG_UIC("Lower UIC critical interrupt\n");
10134d0831fSPeter Maydell         qemu_irq_lower(uic->output_cint);
10234d0831fSPeter Maydell         uic->uicvr = 0x00000000;
10334d0831fSPeter Maydell     }
10434d0831fSPeter Maydell }
10534d0831fSPeter Maydell 
10634d0831fSPeter Maydell static void ppcuic_set_irq(void *opaque, int irq_num, int level)
10734d0831fSPeter Maydell {
10834d0831fSPeter Maydell     PPCUIC *uic;
10934d0831fSPeter Maydell     uint32_t mask, sr;
11034d0831fSPeter Maydell 
11134d0831fSPeter Maydell     uic = opaque;
11234d0831fSPeter Maydell     mask = 1U << (31 - irq_num);
11334d0831fSPeter Maydell     LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32
11434d0831fSPeter Maydell                 " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n",
11534d0831fSPeter Maydell                 __func__, irq_num, level,
11634d0831fSPeter Maydell                 uic->uicsr, mask, uic->uicsr & mask, level << irq_num);
11734d0831fSPeter Maydell     if (irq_num < 0 || irq_num > 31) {
11834d0831fSPeter Maydell         return;
11934d0831fSPeter Maydell     }
12034d0831fSPeter Maydell     sr = uic->uicsr;
12134d0831fSPeter Maydell 
12234d0831fSPeter Maydell     /* Update status register */
12334d0831fSPeter Maydell     if (uic->uictr & mask) {
12434d0831fSPeter Maydell         /* Edge sensitive interrupt */
12534d0831fSPeter Maydell         if (level == 1) {
12634d0831fSPeter Maydell             uic->uicsr |= mask;
12734d0831fSPeter Maydell         }
12834d0831fSPeter Maydell     } else {
12934d0831fSPeter Maydell         /* Level sensitive interrupt */
13034d0831fSPeter Maydell         if (level == 1) {
13134d0831fSPeter Maydell             uic->uicsr |= mask;
13234d0831fSPeter Maydell             uic->level |= mask;
13334d0831fSPeter Maydell         } else {
13434d0831fSPeter Maydell             uic->uicsr &= ~mask;
13534d0831fSPeter Maydell             uic->level &= ~mask;
13634d0831fSPeter Maydell         }
13734d0831fSPeter Maydell     }
13834d0831fSPeter Maydell     LOG_UIC("%s: irq %d level %d sr %" PRIx32 " => "
13934d0831fSPeter Maydell                 "%08" PRIx32 "\n", __func__, irq_num, level, uic->uicsr, sr);
14034d0831fSPeter Maydell     if (sr != uic->uicsr) {
14134d0831fSPeter Maydell         ppcuic_trigger_irq(uic);
14234d0831fSPeter Maydell     }
14334d0831fSPeter Maydell }
14434d0831fSPeter Maydell 
14534d0831fSPeter Maydell static uint32_t dcr_read_uic(void *opaque, int dcrn)
14634d0831fSPeter Maydell {
14734d0831fSPeter Maydell     PPCUIC *uic;
14834d0831fSPeter Maydell     uint32_t ret;
14934d0831fSPeter Maydell 
15034d0831fSPeter Maydell     uic = opaque;
15134d0831fSPeter Maydell     dcrn -= uic->dcr_base;
15234d0831fSPeter Maydell     switch (dcrn) {
15334d0831fSPeter Maydell     case DCR_UICSR:
15434d0831fSPeter Maydell     case DCR_UICSRS:
15534d0831fSPeter Maydell         ret = uic->uicsr;
15634d0831fSPeter Maydell         break;
15734d0831fSPeter Maydell     case DCR_UICER:
15834d0831fSPeter Maydell         ret = uic->uicer;
15934d0831fSPeter Maydell         break;
16034d0831fSPeter Maydell     case DCR_UICCR:
16134d0831fSPeter Maydell         ret = uic->uiccr;
16234d0831fSPeter Maydell         break;
16334d0831fSPeter Maydell     case DCR_UICPR:
16434d0831fSPeter Maydell         ret = uic->uicpr;
16534d0831fSPeter Maydell         break;
16634d0831fSPeter Maydell     case DCR_UICTR:
16734d0831fSPeter Maydell         ret = uic->uictr;
16834d0831fSPeter Maydell         break;
16934d0831fSPeter Maydell     case DCR_UICMSR:
17034d0831fSPeter Maydell         ret = uic->uicsr & uic->uicer;
17134d0831fSPeter Maydell         break;
17234d0831fSPeter Maydell     case DCR_UICVR:
17334d0831fSPeter Maydell         if (!uic->use_vectors) {
17434d0831fSPeter Maydell             goto no_read;
17534d0831fSPeter Maydell         }
17634d0831fSPeter Maydell         ret = uic->uicvr;
17734d0831fSPeter Maydell         break;
17834d0831fSPeter Maydell     case DCR_UICVCR:
17934d0831fSPeter Maydell         if (!uic->use_vectors) {
18034d0831fSPeter Maydell             goto no_read;
18134d0831fSPeter Maydell         }
18234d0831fSPeter Maydell         ret = uic->uicvcr;
18334d0831fSPeter Maydell         break;
18434d0831fSPeter Maydell     default:
18534d0831fSPeter Maydell     no_read:
18634d0831fSPeter Maydell         ret = 0x00000000;
18734d0831fSPeter Maydell         break;
18834d0831fSPeter Maydell     }
18934d0831fSPeter Maydell 
19034d0831fSPeter Maydell     return ret;
19134d0831fSPeter Maydell }
19234d0831fSPeter Maydell 
19334d0831fSPeter Maydell static void dcr_write_uic(void *opaque, int dcrn, uint32_t val)
19434d0831fSPeter Maydell {
19534d0831fSPeter Maydell     PPCUIC *uic;
19634d0831fSPeter Maydell 
19734d0831fSPeter Maydell     uic = opaque;
19834d0831fSPeter Maydell     dcrn -= uic->dcr_base;
19934d0831fSPeter Maydell     LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val);
20034d0831fSPeter Maydell     switch (dcrn) {
20134d0831fSPeter Maydell     case DCR_UICSR:
20234d0831fSPeter Maydell         uic->uicsr &= ~val;
20334d0831fSPeter Maydell         uic->uicsr |= uic->level;
20434d0831fSPeter Maydell         ppcuic_trigger_irq(uic);
20534d0831fSPeter Maydell         break;
20634d0831fSPeter Maydell     case DCR_UICSRS:
20734d0831fSPeter Maydell         uic->uicsr |= val;
20834d0831fSPeter Maydell         ppcuic_trigger_irq(uic);
20934d0831fSPeter Maydell         break;
21034d0831fSPeter Maydell     case DCR_UICER:
21134d0831fSPeter Maydell         uic->uicer = val;
21234d0831fSPeter Maydell         ppcuic_trigger_irq(uic);
21334d0831fSPeter Maydell         break;
21434d0831fSPeter Maydell     case DCR_UICCR:
21534d0831fSPeter Maydell         uic->uiccr = val;
21634d0831fSPeter Maydell         ppcuic_trigger_irq(uic);
21734d0831fSPeter Maydell         break;
21834d0831fSPeter Maydell     case DCR_UICPR:
21934d0831fSPeter Maydell         uic->uicpr = val;
22034d0831fSPeter Maydell         break;
22134d0831fSPeter Maydell     case DCR_UICTR:
22234d0831fSPeter Maydell         uic->uictr = val;
22334d0831fSPeter Maydell         ppcuic_trigger_irq(uic);
22434d0831fSPeter Maydell         break;
22534d0831fSPeter Maydell     case DCR_UICMSR:
22634d0831fSPeter Maydell         break;
22734d0831fSPeter Maydell     case DCR_UICVR:
22834d0831fSPeter Maydell         break;
22934d0831fSPeter Maydell     case DCR_UICVCR:
23034d0831fSPeter Maydell         uic->uicvcr = val & 0xFFFFFFFD;
23134d0831fSPeter Maydell         ppcuic_trigger_irq(uic);
23234d0831fSPeter Maydell         break;
23334d0831fSPeter Maydell     }
23434d0831fSPeter Maydell }
23534d0831fSPeter Maydell 
23634d0831fSPeter Maydell static void ppc_uic_reset(DeviceState *dev)
23734d0831fSPeter Maydell {
23834d0831fSPeter Maydell     PPCUIC *uic = PPC_UIC(dev);
23934d0831fSPeter Maydell 
24034d0831fSPeter Maydell     uic->uiccr = 0x00000000;
24134d0831fSPeter Maydell     uic->uicer = 0x00000000;
24234d0831fSPeter Maydell     uic->uicpr = 0x00000000;
24334d0831fSPeter Maydell     uic->uicsr = 0x00000000;
24434d0831fSPeter Maydell     uic->uictr = 0x00000000;
24534d0831fSPeter Maydell     if (uic->use_vectors) {
24634d0831fSPeter Maydell         uic->uicvcr = 0x00000000;
24734d0831fSPeter Maydell         uic->uicvr = 0x0000000;
24834d0831fSPeter Maydell     }
24934d0831fSPeter Maydell }
25034d0831fSPeter Maydell 
25134d0831fSPeter Maydell static void ppc_uic_realize(DeviceState *dev, Error **errp)
25234d0831fSPeter Maydell {
25334d0831fSPeter Maydell     PPCUIC *uic = PPC_UIC(dev);
25434d0831fSPeter Maydell     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
25534d0831fSPeter Maydell     PowerPCCPU *cpu;
25634d0831fSPeter Maydell     int i;
25734d0831fSPeter Maydell 
25834d0831fSPeter Maydell     if (!uic->cpu) {
25934d0831fSPeter Maydell         /* This is a programming error in the code using this device */
26034d0831fSPeter Maydell         error_setg(errp, "ppc-uic 'cpu' link property was not set");
26134d0831fSPeter Maydell         return;
26234d0831fSPeter Maydell     }
26334d0831fSPeter Maydell 
26434d0831fSPeter Maydell     cpu = POWERPC_CPU(uic->cpu);
26534d0831fSPeter Maydell     for (i = 0; i < DCR_UICMAX; i++) {
26634d0831fSPeter Maydell         ppc_dcr_register(&cpu->env, uic->dcr_base + i, uic,
26734d0831fSPeter Maydell                          &dcr_read_uic, &dcr_write_uic);
26834d0831fSPeter Maydell     }
26934d0831fSPeter Maydell 
27034d0831fSPeter Maydell     sysbus_init_irq(sbd, &uic->output_int);
27134d0831fSPeter Maydell     sysbus_init_irq(sbd, &uic->output_cint);
27234d0831fSPeter Maydell     qdev_init_gpio_in(dev, ppcuic_set_irq, UIC_MAX_IRQ);
27334d0831fSPeter Maydell }
27434d0831fSPeter Maydell 
27534d0831fSPeter Maydell static Property ppc_uic_properties[] = {
27634d0831fSPeter Maydell     DEFINE_PROP_LINK("cpu", PPCUIC, cpu, TYPE_CPU, CPUState *),
277*37dc4b5fSPeter Maydell     DEFINE_PROP_UINT32("dcr-base", PPCUIC, dcr_base, 0xc0),
27834d0831fSPeter Maydell     DEFINE_PROP_BOOL("use-vectors", PPCUIC, use_vectors, true),
27934d0831fSPeter Maydell     DEFINE_PROP_END_OF_LIST()
28034d0831fSPeter Maydell };
28134d0831fSPeter Maydell 
28234d0831fSPeter Maydell static const VMStateDescription ppc_uic_vmstate = {
28334d0831fSPeter Maydell     .name = "ppc-uic",
28434d0831fSPeter Maydell     .version_id = 1,
28534d0831fSPeter Maydell     .minimum_version_id = 1,
28634d0831fSPeter Maydell     .fields = (VMStateField[]) {
28734d0831fSPeter Maydell         VMSTATE_UINT32(level, PPCUIC),
28834d0831fSPeter Maydell         VMSTATE_UINT32(uicsr, PPCUIC),
28934d0831fSPeter Maydell         VMSTATE_UINT32(uicer, PPCUIC),
29034d0831fSPeter Maydell         VMSTATE_UINT32(uiccr, PPCUIC),
29134d0831fSPeter Maydell         VMSTATE_UINT32(uicpr, PPCUIC),
29234d0831fSPeter Maydell         VMSTATE_UINT32(uictr, PPCUIC),
29334d0831fSPeter Maydell         VMSTATE_UINT32(uicvcr, PPCUIC),
29434d0831fSPeter Maydell         VMSTATE_UINT32(uicvr, PPCUIC),
29534d0831fSPeter Maydell         VMSTATE_END_OF_LIST()
29634d0831fSPeter Maydell     },
29734d0831fSPeter Maydell };
29834d0831fSPeter Maydell 
29934d0831fSPeter Maydell static void ppc_uic_class_init(ObjectClass *klass, void *data)
30034d0831fSPeter Maydell {
30134d0831fSPeter Maydell     DeviceClass *dc = DEVICE_CLASS(klass);
30234d0831fSPeter Maydell 
30334d0831fSPeter Maydell     dc->reset = ppc_uic_reset;
30434d0831fSPeter Maydell     dc->realize = ppc_uic_realize;
30534d0831fSPeter Maydell     dc->vmsd = &ppc_uic_vmstate;
30634d0831fSPeter Maydell     device_class_set_props(dc, ppc_uic_properties);
30734d0831fSPeter Maydell }
30834d0831fSPeter Maydell 
30934d0831fSPeter Maydell static const TypeInfo ppc_uic_info = {
31034d0831fSPeter Maydell     .name = TYPE_PPC_UIC,
31134d0831fSPeter Maydell     .parent = TYPE_SYS_BUS_DEVICE,
31234d0831fSPeter Maydell     .instance_size = sizeof(PPCUIC),
31334d0831fSPeter Maydell     .class_init = ppc_uic_class_init,
31434d0831fSPeter Maydell };
31534d0831fSPeter Maydell 
31634d0831fSPeter Maydell static void ppc_uic_register_types(void)
31734d0831fSPeter Maydell {
31834d0831fSPeter Maydell     type_register_static(&ppc_uic_info);
31934d0831fSPeter Maydell }
32034d0831fSPeter Maydell 
32134d0831fSPeter Maydell type_init(ppc_uic_register_types);
322