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