117628bc6SEdgar E. Iglesias /*
217628bc6SEdgar E. Iglesias * QEMU Xilinx OPB Interrupt Controller.
317628bc6SEdgar E. Iglesias *
417628bc6SEdgar E. Iglesias * Copyright (c) 2009 Edgar E. Iglesias.
517628bc6SEdgar E. Iglesias *
62cdf693bSPhilippe Mathieu-Daudé * https://docs.amd.com/v/u/en-US/xps_intc
72cdf693bSPhilippe Mathieu-Daudé * DS572: LogiCORE IP XPS Interrupt Controller (v2.01a)
82cdf693bSPhilippe Mathieu-Daudé *
917628bc6SEdgar E. Iglesias * Permission is hereby granted, free of charge, to any person obtaining a copy
1017628bc6SEdgar E. Iglesias * of this software and associated documentation files (the "Software"), to deal
1117628bc6SEdgar E. Iglesias * in the Software without restriction, including without limitation the rights
1217628bc6SEdgar E. Iglesias * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1317628bc6SEdgar E. Iglesias * copies of the Software, and to permit persons to whom the Software is
1417628bc6SEdgar E. Iglesias * furnished to do so, subject to the following conditions:
1517628bc6SEdgar E. Iglesias *
1617628bc6SEdgar E. Iglesias * The above copyright notice and this permission notice shall be included in
1717628bc6SEdgar E. Iglesias * all copies or substantial portions of the Software.
1817628bc6SEdgar E. Iglesias *
1917628bc6SEdgar E. Iglesias * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2017628bc6SEdgar E. Iglesias * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2117628bc6SEdgar E. Iglesias * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
2217628bc6SEdgar E. Iglesias * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2317628bc6SEdgar E. Iglesias * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2417628bc6SEdgar E. Iglesias * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2517628bc6SEdgar E. Iglesias * THE SOFTWARE.
2617628bc6SEdgar E. Iglesias */
2717628bc6SEdgar E. Iglesias
2890191d07SPeter Maydell #include "qemu/osdep.h"
292cdf693bSPhilippe Mathieu-Daudé #include "qapi/error.h"
3083c9f4caSPaolo Bonzini #include "hw/sysbus.h"
310b8fa32fSMarkus Armbruster #include "qemu/module.h"
3264552b6bSMarkus Armbruster #include "hw/irq.h"
33a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
342cdf693bSPhilippe Mathieu-Daudé #include "hw/qdev-properties-system.h"
35db1015e9SEduardo Habkost #include "qom/object.h"
3617628bc6SEdgar E. Iglesias
3717628bc6SEdgar E. Iglesias #define D(x)
3817628bc6SEdgar E. Iglesias
3917628bc6SEdgar E. Iglesias #define R_ISR 0
4017628bc6SEdgar E. Iglesias #define R_IPR 1
4117628bc6SEdgar E. Iglesias #define R_IER 2
4217628bc6SEdgar E. Iglesias #define R_IAR 3
4317628bc6SEdgar E. Iglesias #define R_SIE 4
4417628bc6SEdgar E. Iglesias #define R_CIE 5
4517628bc6SEdgar E. Iglesias #define R_IVR 6
4617628bc6SEdgar E. Iglesias #define R_MER 7
4717628bc6SEdgar E. Iglesias #define R_MAX 8
4817628bc6SEdgar E. Iglesias
49cc3e064eSAndreas Färber #define TYPE_XILINX_INTC "xlnx.xps-intc"
50d2960be0SPhilippe Mathieu-Daudé typedef struct XpsIntc XpsIntc;
51d2960be0SPhilippe Mathieu-Daudé DECLARE_INSTANCE_CHECKER(XpsIntc, XILINX_INTC, TYPE_XILINX_INTC)
52cc3e064eSAndreas Färber
53d2960be0SPhilippe Mathieu-Daudé struct XpsIntc
5417628bc6SEdgar E. Iglesias {
55cc3e064eSAndreas Färber SysBusDevice parent_obj;
56cc3e064eSAndreas Färber
572cdf693bSPhilippe Mathieu-Daudé EndianMode model_endianness;
58010f3f5fSEdgar E. Iglesias MemoryRegion mmio;
5917628bc6SEdgar E. Iglesias qemu_irq parent_irq;
6017628bc6SEdgar E. Iglesias
6117628bc6SEdgar E. Iglesias /* Configuration reg chosen at synthesis-time. QEMU populates
6217628bc6SEdgar E. Iglesias the bits at board-setup. */
6317628bc6SEdgar E. Iglesias uint32_t c_kind_of_intr;
6417628bc6SEdgar E. Iglesias
6517628bc6SEdgar E. Iglesias /* Runtime control registers. */
6617628bc6SEdgar E. Iglesias uint32_t regs[R_MAX];
6745fdd3bfSPeter Crosthwaite /* state of the interrupt input pins */
6845fdd3bfSPeter Crosthwaite uint32_t irq_pin_state;
6917628bc6SEdgar E. Iglesias };
7017628bc6SEdgar E. Iglesias
update_irq(XpsIntc * p)71d2960be0SPhilippe Mathieu-Daudé static void update_irq(XpsIntc *p)
7217628bc6SEdgar E. Iglesias {
7317628bc6SEdgar E. Iglesias uint32_t i;
7445fdd3bfSPeter Crosthwaite
7545fdd3bfSPeter Crosthwaite /* level triggered interrupt */
7645fdd3bfSPeter Crosthwaite if (p->regs[R_MER] & 2) {
7745fdd3bfSPeter Crosthwaite p->regs[R_ISR] |= p->irq_pin_state & ~p->c_kind_of_intr;
7845fdd3bfSPeter Crosthwaite }
7945fdd3bfSPeter Crosthwaite
8017628bc6SEdgar E. Iglesias /* Update the pending register. */
8117628bc6SEdgar E. Iglesias p->regs[R_IPR] = p->regs[R_ISR] & p->regs[R_IER];
8217628bc6SEdgar E. Iglesias
8317628bc6SEdgar E. Iglesias /* Update the vector register. */
8417628bc6SEdgar E. Iglesias for (i = 0; i < 32; i++) {
850bc60bd7SPeter Maydell if (p->regs[R_IPR] & (1U << i)) {
8617628bc6SEdgar E. Iglesias break;
8717628bc6SEdgar E. Iglesias }
880bc60bd7SPeter Maydell }
8917628bc6SEdgar E. Iglesias if (i == 32)
9017628bc6SEdgar E. Iglesias i = ~0;
9117628bc6SEdgar E. Iglesias
9217628bc6SEdgar E. Iglesias p->regs[R_IVR] = i;
935c9f4336SPeter Crosthwaite qemu_set_irq(p->parent_irq, (p->regs[R_MER] & 1) && p->regs[R_IPR]);
9417628bc6SEdgar E. Iglesias }
9517628bc6SEdgar E. Iglesias
pic_read(void * opaque,hwaddr addr,unsigned int size)96d2960be0SPhilippe Mathieu-Daudé static uint64_t pic_read(void *opaque, hwaddr addr, unsigned int size)
9717628bc6SEdgar E. Iglesias {
98d2960be0SPhilippe Mathieu-Daudé XpsIntc *p = opaque;
9917628bc6SEdgar E. Iglesias uint32_t r = 0;
10017628bc6SEdgar E. Iglesias
10117628bc6SEdgar E. Iglesias addr >>= 2;
10217628bc6SEdgar E. Iglesias switch (addr)
10317628bc6SEdgar E. Iglesias {
10417628bc6SEdgar E. Iglesias default:
10517628bc6SEdgar E. Iglesias if (addr < ARRAY_SIZE(p->regs))
10617628bc6SEdgar E. Iglesias r = p->regs[addr];
10717628bc6SEdgar E. Iglesias break;
10817628bc6SEdgar E. Iglesias
10917628bc6SEdgar E. Iglesias }
11017628bc6SEdgar E. Iglesias D(printf("%s %x=%x\n", __func__, addr * 4, r));
11117628bc6SEdgar E. Iglesias return r;
11217628bc6SEdgar E. Iglesias }
11317628bc6SEdgar E. Iglesias
pic_write(void * opaque,hwaddr addr,uint64_t val64,unsigned int size)114d2960be0SPhilippe Mathieu-Daudé static void pic_write(void *opaque, hwaddr addr,
115010f3f5fSEdgar E. Iglesias uint64_t val64, unsigned int size)
11617628bc6SEdgar E. Iglesias {
117d2960be0SPhilippe Mathieu-Daudé XpsIntc *p = opaque;
118010f3f5fSEdgar E. Iglesias uint32_t value = val64;
11917628bc6SEdgar E. Iglesias
12017628bc6SEdgar E. Iglesias addr >>= 2;
12117628bc6SEdgar E. Iglesias D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value));
12217628bc6SEdgar E. Iglesias switch (addr)
12317628bc6SEdgar E. Iglesias {
12417628bc6SEdgar E. Iglesias case R_IAR:
12517628bc6SEdgar E. Iglesias p->regs[R_ISR] &= ~value; /* ACK. */
12617628bc6SEdgar E. Iglesias break;
12717628bc6SEdgar E. Iglesias case R_SIE:
12817628bc6SEdgar E. Iglesias p->regs[R_IER] |= value; /* Atomic set ie. */
12917628bc6SEdgar E. Iglesias break;
13017628bc6SEdgar E. Iglesias case R_CIE:
13117628bc6SEdgar E. Iglesias p->regs[R_IER] &= ~value; /* Atomic clear ie. */
13217628bc6SEdgar E. Iglesias break;
13312f7fb60SGuenter Roeck case R_MER:
13412f7fb60SGuenter Roeck p->regs[R_MER] = value & 0x3;
13512f7fb60SGuenter Roeck break;
136fa96d614SPeter Crosthwaite case R_ISR:
137fa96d614SPeter Crosthwaite if ((p->regs[R_MER] & 2)) {
138fa96d614SPeter Crosthwaite break;
139fa96d614SPeter Crosthwaite }
140fa96d614SPeter Crosthwaite /* fallthrough */
14117628bc6SEdgar E. Iglesias default:
14217628bc6SEdgar E. Iglesias if (addr < ARRAY_SIZE(p->regs))
14317628bc6SEdgar E. Iglesias p->regs[addr] = value;
14417628bc6SEdgar E. Iglesias break;
14517628bc6SEdgar E. Iglesias }
14617628bc6SEdgar E. Iglesias update_irq(p);
14717628bc6SEdgar E. Iglesias }
14817628bc6SEdgar E. Iglesias
1492cdf693bSPhilippe Mathieu-Daudé static const MemoryRegionOps pic_ops[2] = {
1502cdf693bSPhilippe Mathieu-Daudé [0 ... 1] = {
151010f3f5fSEdgar E. Iglesias .read = pic_read,
152010f3f5fSEdgar E. Iglesias .write = pic_write,
1536909b616SPhilippe Mathieu-Daudé .impl = {
1546909b616SPhilippe Mathieu-Daudé .min_access_size = 4,
1556909b616SPhilippe Mathieu-Daudé .max_access_size = 4,
1566909b616SPhilippe Mathieu-Daudé },
157010f3f5fSEdgar E. Iglesias .valid = {
1582cdf693bSPhilippe Mathieu-Daudé /*
1592cdf693bSPhilippe Mathieu-Daudé * All XPS INTC registers are accessed through the PLB interface.
1602cdf693bSPhilippe Mathieu-Daudé * The base address for these registers is provided by the
1612cdf693bSPhilippe Mathieu-Daudé * configuration parameter, C_BASEADDR. Each register is 32 bits
1622cdf693bSPhilippe Mathieu-Daudé * although some bits may be unused and is accessed on a 4-byte
1632cdf693bSPhilippe Mathieu-Daudé * boundary offset from the base address.
1642cdf693bSPhilippe Mathieu-Daudé */
165010f3f5fSEdgar E. Iglesias .min_access_size = 4,
1662cdf693bSPhilippe Mathieu-Daudé .max_access_size = 4,
1672cdf693bSPhilippe Mathieu-Daudé },
1682cdf693bSPhilippe Mathieu-Daudé },
1692cdf693bSPhilippe Mathieu-Daudé [0].endianness = DEVICE_LITTLE_ENDIAN,
1702cdf693bSPhilippe Mathieu-Daudé [1].endianness = DEVICE_BIG_ENDIAN,
17117628bc6SEdgar E. Iglesias };
17217628bc6SEdgar E. Iglesias
irq_handler(void * opaque,int irq,int level)17317628bc6SEdgar E. Iglesias static void irq_handler(void *opaque, int irq, int level)
17417628bc6SEdgar E. Iglesias {
175d2960be0SPhilippe Mathieu-Daudé XpsIntc *p = opaque;
17617628bc6SEdgar E. Iglesias
17745fdd3bfSPeter Crosthwaite /* edge triggered interrupt */
17845fdd3bfSPeter Crosthwaite if (p->c_kind_of_intr & (1 << irq) && p->regs[R_MER] & 2) {
17917628bc6SEdgar E. Iglesias p->regs[R_ISR] |= (level << irq);
18045fdd3bfSPeter Crosthwaite }
18145fdd3bfSPeter Crosthwaite
18245fdd3bfSPeter Crosthwaite p->irq_pin_state &= ~(1 << irq);
18345fdd3bfSPeter Crosthwaite p->irq_pin_state |= level << irq;
18417628bc6SEdgar E. Iglesias update_irq(p);
18517628bc6SEdgar E. Iglesias }
18617628bc6SEdgar E. Iglesias
xilinx_intc_init(Object * obj)187a373cdb5SPeter Crosthwaite static void xilinx_intc_init(Object *obj)
18817628bc6SEdgar E. Iglesias {
189d2960be0SPhilippe Mathieu-Daudé XpsIntc *p = XILINX_INTC(obj);
19017628bc6SEdgar E. Iglesias
191a373cdb5SPeter Crosthwaite qdev_init_gpio_in(DEVICE(obj), irq_handler, 32);
192a373cdb5SPeter Crosthwaite sysbus_init_irq(SYS_BUS_DEVICE(obj), &p->parent_irq);
193a373cdb5SPeter Crosthwaite sysbus_init_mmio(SYS_BUS_DEVICE(obj), &p->mmio);
19417628bc6SEdgar E. Iglesias }
19517628bc6SEdgar E. Iglesias
xilinx_intc_realize(DeviceState * dev,Error ** errp)1962cdf693bSPhilippe Mathieu-Daudé static void xilinx_intc_realize(DeviceState *dev, Error **errp)
1972cdf693bSPhilippe Mathieu-Daudé {
1982cdf693bSPhilippe Mathieu-Daudé XpsIntc *p = XILINX_INTC(dev);
1992cdf693bSPhilippe Mathieu-Daudé
2002cdf693bSPhilippe Mathieu-Daudé if (p->model_endianness == ENDIAN_MODE_UNSPECIFIED) {
2012cdf693bSPhilippe Mathieu-Daudé error_setg(errp, TYPE_XILINX_INTC " property 'endianness'"
2022cdf693bSPhilippe Mathieu-Daudé " must be set to 'big' or 'little'");
2032cdf693bSPhilippe Mathieu-Daudé return;
2042cdf693bSPhilippe Mathieu-Daudé }
2052cdf693bSPhilippe Mathieu-Daudé
2062cdf693bSPhilippe Mathieu-Daudé memory_region_init_io(&p->mmio, OBJECT(dev),
2072cdf693bSPhilippe Mathieu-Daudé &pic_ops[p->model_endianness == ENDIAN_MODE_BIG],
2082cdf693bSPhilippe Mathieu-Daudé p, "xlnx.xps-intc",
2092cdf693bSPhilippe Mathieu-Daudé R_MAX * 4);
2102cdf693bSPhilippe Mathieu-Daudé }
2112cdf693bSPhilippe Mathieu-Daudé
212783e3b21SRichard Henderson static const Property xilinx_intc_properties[] = {
2132cdf693bSPhilippe Mathieu-Daudé DEFINE_PROP_ENDIAN_NODEFAULT("endianness", XpsIntc, model_endianness),
214d2960be0SPhilippe Mathieu-Daudé DEFINE_PROP_UINT32("kind-of-intr", XpsIntc, c_kind_of_intr, 0),
215999e12bbSAnthony Liguori };
216999e12bbSAnthony Liguori
xilinx_intc_class_init(ObjectClass * klass,const void * data)217*12d1a768SPhilippe Mathieu-Daudé static void xilinx_intc_class_init(ObjectClass *klass, const void *data)
218999e12bbSAnthony Liguori {
21939bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass);
220999e12bbSAnthony Liguori
2212cdf693bSPhilippe Mathieu-Daudé dc->realize = xilinx_intc_realize;
2224f67d30bSMarc-André Lureau device_class_set_props(dc, xilinx_intc_properties);
223ee6847d1SGerd Hoffmann }
224999e12bbSAnthony Liguori
2258c43a6f0SAndreas Färber static const TypeInfo xilinx_intc_info = {
226cc3e064eSAndreas Färber .name = TYPE_XILINX_INTC,
22739bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE,
228d2960be0SPhilippe Mathieu-Daudé .instance_size = sizeof(XpsIntc),
229a373cdb5SPeter Crosthwaite .instance_init = xilinx_intc_init,
230999e12bbSAnthony Liguori .class_init = xilinx_intc_class_init,
231ee6847d1SGerd Hoffmann };
232ee6847d1SGerd Hoffmann
xilinx_intc_register_types(void)23383f7d43aSAndreas Färber static void xilinx_intc_register_types(void)
23417628bc6SEdgar E. Iglesias {
23539bffca2SAnthony Liguori type_register_static(&xilinx_intc_info);
23617628bc6SEdgar E. Iglesias }
23717628bc6SEdgar E. Iglesias
23883f7d43aSAndreas Färber type_init(xilinx_intc_register_types)
239