xref: /openbmc/qemu/hw/intc/ppc-uic.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
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"
2658519090SPhilippe Mathieu-Daudé #include "hw/intc/ppc-uic.h"
2734d0831fSPeter Maydell #include "hw/irq.h"
2834d0831fSPeter Maydell #include "hw/qdev-properties.h"
2934d0831fSPeter Maydell #include "migration/vmstate.h"
3034d0831fSPeter Maydell 
3134d0831fSPeter Maydell enum {
3234d0831fSPeter Maydell     DCR_UICSR  = 0x000,
3334d0831fSPeter Maydell     DCR_UICSRS = 0x001,
3434d0831fSPeter Maydell     DCR_UICER  = 0x002,
3534d0831fSPeter Maydell     DCR_UICCR  = 0x003,
3634d0831fSPeter Maydell     DCR_UICPR  = 0x004,
3734d0831fSPeter Maydell     DCR_UICTR  = 0x005,
3834d0831fSPeter Maydell     DCR_UICMSR = 0x006,
3934d0831fSPeter Maydell     DCR_UICVR  = 0x007,
4034d0831fSPeter Maydell     DCR_UICVCR = 0x008,
4134d0831fSPeter Maydell     DCR_UICMAX = 0x009,
4234d0831fSPeter Maydell };
4334d0831fSPeter Maydell 
4434d0831fSPeter Maydell /*#define DEBUG_UIC*/
4534d0831fSPeter Maydell 
4634d0831fSPeter Maydell #ifdef DEBUG_UIC
4734d0831fSPeter Maydell #  define LOG_UIC(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__)
4834d0831fSPeter Maydell #else
4934d0831fSPeter Maydell #  define LOG_UIC(...) do { } while (0)
5034d0831fSPeter Maydell #endif
5134d0831fSPeter Maydell 
ppcuic_trigger_irq(PPCUIC * uic)5234d0831fSPeter Maydell static void ppcuic_trigger_irq(PPCUIC *uic)
5334d0831fSPeter Maydell {
5434d0831fSPeter Maydell     uint32_t ir, cr;
5534d0831fSPeter Maydell     int start, end, inc, i;
5634d0831fSPeter Maydell 
5734d0831fSPeter Maydell     /* Trigger interrupt if any is pending */
5834d0831fSPeter Maydell     ir = uic->uicsr & uic->uicer & (~uic->uiccr);
5934d0831fSPeter Maydell     cr = uic->uicsr & uic->uicer & uic->uiccr;
6034d0831fSPeter Maydell     LOG_UIC("%s: uicsr %08" PRIx32 " uicer %08" PRIx32
6134d0831fSPeter Maydell                 " uiccr %08" PRIx32 "\n"
6234d0831fSPeter Maydell                 "   %08" PRIx32 " ir %08" PRIx32 " cr %08" PRIx32 "\n",
6334d0831fSPeter Maydell                 __func__, uic->uicsr, uic->uicer, uic->uiccr,
6434d0831fSPeter Maydell                 uic->uicsr & uic->uicer, ir, cr);
6534d0831fSPeter Maydell     if (ir != 0x0000000) {
6634d0831fSPeter Maydell         LOG_UIC("Raise UIC interrupt\n");
6734d0831fSPeter Maydell         qemu_irq_raise(uic->output_int);
6834d0831fSPeter Maydell     } else {
6934d0831fSPeter Maydell         LOG_UIC("Lower UIC interrupt\n");
7034d0831fSPeter Maydell         qemu_irq_lower(uic->output_int);
7134d0831fSPeter Maydell     }
7234d0831fSPeter Maydell     /* Trigger critical interrupt if any is pending and update vector */
7334d0831fSPeter Maydell     if (cr != 0x0000000) {
7434d0831fSPeter Maydell         qemu_irq_raise(uic->output_cint);
7534d0831fSPeter Maydell         if (uic->use_vectors) {
7634d0831fSPeter Maydell             /* Compute critical IRQ vector */
7734d0831fSPeter Maydell             if (uic->uicvcr & 1) {
7834d0831fSPeter Maydell                 start = 31;
7934d0831fSPeter Maydell                 end = 0;
8034d0831fSPeter Maydell                 inc = -1;
8134d0831fSPeter Maydell             } else {
8234d0831fSPeter Maydell                 start = 0;
8334d0831fSPeter Maydell                 end = 31;
8434d0831fSPeter Maydell                 inc = 1;
8534d0831fSPeter Maydell             }
8634d0831fSPeter Maydell             uic->uicvr = uic->uicvcr & 0xFFFFFFFC;
8734d0831fSPeter Maydell             for (i = start; i <= end; i += inc) {
8834d0831fSPeter Maydell                 if (cr & (1 << i)) {
8934d0831fSPeter Maydell                     uic->uicvr += (i - start) * 512 * inc;
9034d0831fSPeter Maydell                     break;
9134d0831fSPeter Maydell                 }
9234d0831fSPeter Maydell             }
9334d0831fSPeter Maydell         }
9434d0831fSPeter Maydell         LOG_UIC("Raise UIC critical interrupt - "
9534d0831fSPeter Maydell                     "vector %08" PRIx32 "\n", uic->uicvr);
9634d0831fSPeter Maydell     } else {
9734d0831fSPeter Maydell         LOG_UIC("Lower UIC critical interrupt\n");
9834d0831fSPeter Maydell         qemu_irq_lower(uic->output_cint);
9934d0831fSPeter Maydell         uic->uicvr = 0x00000000;
10034d0831fSPeter Maydell     }
10134d0831fSPeter Maydell }
10234d0831fSPeter Maydell 
ppcuic_set_irq(void * opaque,int irq_num,int level)10334d0831fSPeter Maydell static void ppcuic_set_irq(void *opaque, int irq_num, int level)
10434d0831fSPeter Maydell {
105a55b2136SBALATON Zoltan     PPCUIC *uic = opaque;
10634d0831fSPeter Maydell     uint32_t mask, sr;
10734d0831fSPeter Maydell 
10834d0831fSPeter Maydell     mask = 1U << (31 - irq_num);
10934d0831fSPeter Maydell     LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32
11034d0831fSPeter Maydell                 " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n",
11134d0831fSPeter Maydell                 __func__, irq_num, level,
11234d0831fSPeter Maydell                 uic->uicsr, mask, uic->uicsr & mask, level << irq_num);
11334d0831fSPeter Maydell     if (irq_num < 0 || irq_num > 31) {
11434d0831fSPeter Maydell         return;
11534d0831fSPeter Maydell     }
11634d0831fSPeter Maydell     sr = uic->uicsr;
11734d0831fSPeter Maydell 
11834d0831fSPeter Maydell     /* Update status register */
11934d0831fSPeter Maydell     if (uic->uictr & mask) {
12034d0831fSPeter Maydell         /* Edge sensitive interrupt */
12134d0831fSPeter Maydell         if (level == 1) {
12234d0831fSPeter Maydell             uic->uicsr |= mask;
12334d0831fSPeter Maydell         }
12434d0831fSPeter Maydell     } else {
12534d0831fSPeter Maydell         /* Level sensitive interrupt */
12634d0831fSPeter Maydell         if (level == 1) {
12734d0831fSPeter Maydell             uic->uicsr |= mask;
12834d0831fSPeter Maydell             uic->level |= mask;
12934d0831fSPeter Maydell         } else {
13034d0831fSPeter Maydell             uic->uicsr &= ~mask;
13134d0831fSPeter Maydell             uic->level &= ~mask;
13234d0831fSPeter Maydell         }
13334d0831fSPeter Maydell     }
13434d0831fSPeter Maydell     LOG_UIC("%s: irq %d level %d sr %" PRIx32 " => "
13534d0831fSPeter Maydell                 "%08" PRIx32 "\n", __func__, irq_num, level, uic->uicsr, sr);
13634d0831fSPeter Maydell     if (sr != uic->uicsr) {
13734d0831fSPeter Maydell         ppcuic_trigger_irq(uic);
13834d0831fSPeter Maydell     }
13934d0831fSPeter Maydell }
14034d0831fSPeter Maydell 
dcr_read_uic(void * opaque,int dcrn)14134d0831fSPeter Maydell static uint32_t dcr_read_uic(void *opaque, int dcrn)
14234d0831fSPeter Maydell {
143a55b2136SBALATON Zoltan     PPCUIC *uic = opaque;
14434d0831fSPeter Maydell     uint32_t ret;
14534d0831fSPeter Maydell 
14634d0831fSPeter Maydell     dcrn -= uic->dcr_base;
14734d0831fSPeter Maydell     switch (dcrn) {
14834d0831fSPeter Maydell     case DCR_UICSR:
14934d0831fSPeter Maydell     case DCR_UICSRS:
15034d0831fSPeter Maydell         ret = uic->uicsr;
15134d0831fSPeter Maydell         break;
15234d0831fSPeter Maydell     case DCR_UICER:
15334d0831fSPeter Maydell         ret = uic->uicer;
15434d0831fSPeter Maydell         break;
15534d0831fSPeter Maydell     case DCR_UICCR:
15634d0831fSPeter Maydell         ret = uic->uiccr;
15734d0831fSPeter Maydell         break;
15834d0831fSPeter Maydell     case DCR_UICPR:
15934d0831fSPeter Maydell         ret = uic->uicpr;
16034d0831fSPeter Maydell         break;
16134d0831fSPeter Maydell     case DCR_UICTR:
16234d0831fSPeter Maydell         ret = uic->uictr;
16334d0831fSPeter Maydell         break;
16434d0831fSPeter Maydell     case DCR_UICMSR:
16534d0831fSPeter Maydell         ret = uic->uicsr & uic->uicer;
16634d0831fSPeter Maydell         break;
16734d0831fSPeter Maydell     case DCR_UICVR:
16834d0831fSPeter Maydell         if (!uic->use_vectors) {
16934d0831fSPeter Maydell             goto no_read;
17034d0831fSPeter Maydell         }
17134d0831fSPeter Maydell         ret = uic->uicvr;
17234d0831fSPeter Maydell         break;
17334d0831fSPeter Maydell     case DCR_UICVCR:
17434d0831fSPeter Maydell         if (!uic->use_vectors) {
17534d0831fSPeter Maydell             goto no_read;
17634d0831fSPeter Maydell         }
17734d0831fSPeter Maydell         ret = uic->uicvcr;
17834d0831fSPeter Maydell         break;
17934d0831fSPeter Maydell     default:
18034d0831fSPeter Maydell     no_read:
18134d0831fSPeter Maydell         ret = 0x00000000;
18234d0831fSPeter Maydell         break;
18334d0831fSPeter Maydell     }
18434d0831fSPeter Maydell 
18534d0831fSPeter Maydell     return ret;
18634d0831fSPeter Maydell }
18734d0831fSPeter Maydell 
dcr_write_uic(void * opaque,int dcrn,uint32_t val)18834d0831fSPeter Maydell static void dcr_write_uic(void *opaque, int dcrn, uint32_t val)
18934d0831fSPeter Maydell {
190a55b2136SBALATON Zoltan     PPCUIC *uic = opaque;
19134d0831fSPeter Maydell 
19234d0831fSPeter Maydell     dcrn -= uic->dcr_base;
19334d0831fSPeter Maydell     LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val);
19434d0831fSPeter Maydell     switch (dcrn) {
19534d0831fSPeter Maydell     case DCR_UICSR:
19634d0831fSPeter Maydell         uic->uicsr &= ~val;
19734d0831fSPeter Maydell         uic->uicsr |= uic->level;
19834d0831fSPeter Maydell         ppcuic_trigger_irq(uic);
19934d0831fSPeter Maydell         break;
20034d0831fSPeter Maydell     case DCR_UICSRS:
20134d0831fSPeter Maydell         uic->uicsr |= val;
20234d0831fSPeter Maydell         ppcuic_trigger_irq(uic);
20334d0831fSPeter Maydell         break;
20434d0831fSPeter Maydell     case DCR_UICER:
20534d0831fSPeter Maydell         uic->uicer = val;
20634d0831fSPeter Maydell         ppcuic_trigger_irq(uic);
20734d0831fSPeter Maydell         break;
20834d0831fSPeter Maydell     case DCR_UICCR:
20934d0831fSPeter Maydell         uic->uiccr = val;
21034d0831fSPeter Maydell         ppcuic_trigger_irq(uic);
21134d0831fSPeter Maydell         break;
21234d0831fSPeter Maydell     case DCR_UICPR:
21334d0831fSPeter Maydell         uic->uicpr = val;
21434d0831fSPeter Maydell         break;
21534d0831fSPeter Maydell     case DCR_UICTR:
21634d0831fSPeter Maydell         uic->uictr = val;
21734d0831fSPeter Maydell         ppcuic_trigger_irq(uic);
21834d0831fSPeter Maydell         break;
21934d0831fSPeter Maydell     case DCR_UICMSR:
22034d0831fSPeter Maydell         break;
22134d0831fSPeter Maydell     case DCR_UICVR:
22234d0831fSPeter Maydell         break;
22334d0831fSPeter Maydell     case DCR_UICVCR:
22434d0831fSPeter Maydell         uic->uicvcr = val & 0xFFFFFFFD;
22534d0831fSPeter Maydell         ppcuic_trigger_irq(uic);
22634d0831fSPeter Maydell         break;
22734d0831fSPeter Maydell     }
22834d0831fSPeter Maydell }
22934d0831fSPeter Maydell 
ppc_uic_reset(DeviceState * dev)23034d0831fSPeter Maydell static void ppc_uic_reset(DeviceState *dev)
23134d0831fSPeter Maydell {
23234d0831fSPeter Maydell     PPCUIC *uic = PPC_UIC(dev);
23334d0831fSPeter Maydell 
23434d0831fSPeter Maydell     uic->uiccr = 0x00000000;
23534d0831fSPeter Maydell     uic->uicer = 0x00000000;
23634d0831fSPeter Maydell     uic->uicpr = 0x00000000;
23734d0831fSPeter Maydell     uic->uicsr = 0x00000000;
23834d0831fSPeter Maydell     uic->uictr = 0x00000000;
23934d0831fSPeter Maydell     if (uic->use_vectors) {
24034d0831fSPeter Maydell         uic->uicvcr = 0x00000000;
24134d0831fSPeter Maydell         uic->uicvr = 0x0000000;
24234d0831fSPeter Maydell     }
24334d0831fSPeter Maydell }
24434d0831fSPeter Maydell 
ppc_uic_realize(DeviceState * dev,Error ** errp)24534d0831fSPeter Maydell static void ppc_uic_realize(DeviceState *dev, Error **errp)
24634d0831fSPeter Maydell {
24734d0831fSPeter Maydell     PPCUIC *uic = PPC_UIC(dev);
248a55b2136SBALATON Zoltan     Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev);
24934d0831fSPeter Maydell     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
25034d0831fSPeter Maydell     int i;
25134d0831fSPeter Maydell 
25234d0831fSPeter Maydell     for (i = 0; i < DCR_UICMAX; i++) {
253a55b2136SBALATON Zoltan         ppc4xx_dcr_register(dcr, uic->dcr_base + i, uic,
25434d0831fSPeter Maydell                          &dcr_read_uic, &dcr_write_uic);
25534d0831fSPeter Maydell     }
25634d0831fSPeter Maydell 
25734d0831fSPeter Maydell     sysbus_init_irq(sbd, &uic->output_int);
25834d0831fSPeter Maydell     sysbus_init_irq(sbd, &uic->output_cint);
25934d0831fSPeter Maydell     qdev_init_gpio_in(dev, ppcuic_set_irq, UIC_MAX_IRQ);
26034d0831fSPeter Maydell }
26134d0831fSPeter Maydell 
26234d0831fSPeter Maydell static Property ppc_uic_properties[] = {
26337dc4b5fSPeter Maydell     DEFINE_PROP_UINT32("dcr-base", PPCUIC, dcr_base, 0xc0),
26434d0831fSPeter Maydell     DEFINE_PROP_BOOL("use-vectors", PPCUIC, use_vectors, true),
26534d0831fSPeter Maydell     DEFINE_PROP_END_OF_LIST()
26634d0831fSPeter Maydell };
26734d0831fSPeter Maydell 
26834d0831fSPeter Maydell static const VMStateDescription ppc_uic_vmstate = {
26934d0831fSPeter Maydell     .name = "ppc-uic",
27034d0831fSPeter Maydell     .version_id = 1,
27134d0831fSPeter Maydell     .minimum_version_id = 1,
27245b1f81dSRichard Henderson     .fields = (const VMStateField[]) {
27334d0831fSPeter Maydell         VMSTATE_UINT32(level, PPCUIC),
27434d0831fSPeter Maydell         VMSTATE_UINT32(uicsr, PPCUIC),
27534d0831fSPeter Maydell         VMSTATE_UINT32(uicer, PPCUIC),
27634d0831fSPeter Maydell         VMSTATE_UINT32(uiccr, PPCUIC),
27734d0831fSPeter Maydell         VMSTATE_UINT32(uicpr, PPCUIC),
27834d0831fSPeter Maydell         VMSTATE_UINT32(uictr, PPCUIC),
27934d0831fSPeter Maydell         VMSTATE_UINT32(uicvcr, PPCUIC),
28034d0831fSPeter Maydell         VMSTATE_UINT32(uicvr, PPCUIC),
28134d0831fSPeter Maydell         VMSTATE_END_OF_LIST()
28234d0831fSPeter Maydell     },
28334d0831fSPeter Maydell };
28434d0831fSPeter Maydell 
ppc_uic_class_init(ObjectClass * klass,void * data)28534d0831fSPeter Maydell static void ppc_uic_class_init(ObjectClass *klass, void *data)
28634d0831fSPeter Maydell {
28734d0831fSPeter Maydell     DeviceClass *dc = DEVICE_CLASS(klass);
28834d0831fSPeter Maydell 
289*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, ppc_uic_reset);
29034d0831fSPeter Maydell     dc->realize = ppc_uic_realize;
29134d0831fSPeter Maydell     dc->vmsd = &ppc_uic_vmstate;
29234d0831fSPeter Maydell     device_class_set_props(dc, ppc_uic_properties);
29334d0831fSPeter Maydell }
29434d0831fSPeter Maydell 
29534d0831fSPeter Maydell static const TypeInfo ppc_uic_info = {
29634d0831fSPeter Maydell     .name = TYPE_PPC_UIC,
297a55b2136SBALATON Zoltan     .parent = TYPE_PPC4xx_DCR_DEVICE,
29834d0831fSPeter Maydell     .instance_size = sizeof(PPCUIC),
29934d0831fSPeter Maydell     .class_init = ppc_uic_class_init,
30034d0831fSPeter Maydell };
30134d0831fSPeter Maydell 
ppc_uic_register_types(void)30234d0831fSPeter Maydell static void ppc_uic_register_types(void)
30334d0831fSPeter Maydell {
30434d0831fSPeter Maydell     type_register_static(&ppc_uic_info);
30534d0831fSPeter Maydell }
30634d0831fSPeter Maydell 
30734d0831fSPeter Maydell type_init(ppc_uic_register_types);
308