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