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