xref: /qemu/hw/intc/xilinx_intc.c (revision f07a5674cf97b8473e5d06d7b1df9b51e97d553f)
1 /*
2  * QEMU Xilinx OPB Interrupt Controller.
3  *
4  * Copyright (c) 2009 Edgar E. Iglesias.
5  *
6  * https://docs.amd.com/v/u/en-US/xps_intc
7  * DS572: LogiCORE IP XPS Interrupt Controller (v2.01a)
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a copy
10  * of this software and associated documentation files (the "Software"), to deal
11  * in the Software without restriction, including without limitation the rights
12  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13  * copies of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included in
17  * all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25  * THE SOFTWARE.
26  */
27 
28 #include "qemu/osdep.h"
29 #include "qapi/error.h"
30 #include "hw/sysbus.h"
31 #include "qemu/module.h"
32 #include "hw/irq.h"
33 #include "hw/qdev-properties.h"
34 #include "hw/qdev-properties-system.h"
35 #include "qom/object.h"
36 
37 #define D(x)
38 
39 #define R_ISR       0
40 #define R_IPR       1
41 #define R_IER       2
42 #define R_IAR       3
43 #define R_SIE       4
44 #define R_CIE       5
45 #define R_IVR       6
46 #define R_MER       7
47 #define R_MAX       8
48 
49 #define TYPE_XILINX_INTC "xlnx.xps-intc"
50 typedef struct XpsIntc XpsIntc;
51 DECLARE_INSTANCE_CHECKER(XpsIntc, XILINX_INTC, TYPE_XILINX_INTC)
52 
53 struct XpsIntc
54 {
55     SysBusDevice parent_obj;
56 
57     EndianMode model_endianness;
58     MemoryRegion mmio;
59     qemu_irq parent_irq;
60 
61     /* Configuration reg chosen at synthesis-time. QEMU populates
62        the bits at board-setup.  */
63     uint32_t c_kind_of_intr;
64 
65     /* Runtime control registers.  */
66     uint32_t regs[R_MAX];
67     /* state of the interrupt input pins */
68     uint32_t irq_pin_state;
69 };
70 
71 static void update_irq(XpsIntc *p)
72 {
73     uint32_t i;
74 
75     /* level triggered interrupt */
76     if (p->regs[R_MER] & 2) {
77         p->regs[R_ISR] |= p->irq_pin_state & ~p->c_kind_of_intr;
78     }
79 
80     /* Update the pending register.  */
81     p->regs[R_IPR] = p->regs[R_ISR] & p->regs[R_IER];
82 
83     /* Update the vector register.  */
84     for (i = 0; i < 32; i++) {
85         if (p->regs[R_IPR] & (1U << i)) {
86             break;
87         }
88     }
89     if (i == 32)
90         i = ~0;
91 
92     p->regs[R_IVR] = i;
93     qemu_set_irq(p->parent_irq, (p->regs[R_MER] & 1) && p->regs[R_IPR]);
94 }
95 
96 static uint64_t pic_read(void *opaque, hwaddr addr, unsigned int size)
97 {
98     XpsIntc *p = opaque;
99     uint32_t r = 0;
100 
101     addr >>= 2;
102     switch (addr)
103     {
104         default:
105             if (addr < ARRAY_SIZE(p->regs))
106                 r = p->regs[addr];
107             break;
108 
109     }
110     D(printf("%s %x=%x\n", __func__, addr * 4, r));
111     return r;
112 }
113 
114 static void pic_write(void *opaque, hwaddr addr,
115                       uint64_t val64, unsigned int size)
116 {
117     XpsIntc *p = opaque;
118     uint32_t value = val64;
119 
120     addr >>= 2;
121     D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value));
122     switch (addr)
123     {
124         case R_IAR:
125             p->regs[R_ISR] &= ~value; /* ACK.  */
126             break;
127         case R_SIE:
128             p->regs[R_IER] |= value;  /* Atomic set ie.  */
129             break;
130         case R_CIE:
131             p->regs[R_IER] &= ~value; /* Atomic clear ie.  */
132             break;
133         case R_MER:
134             p->regs[R_MER] = value & 0x3;
135             break;
136         case R_ISR:
137             if ((p->regs[R_MER] & 2)) {
138                 break;
139             }
140             /* fallthrough */
141         default:
142             if (addr < ARRAY_SIZE(p->regs))
143                 p->regs[addr] = value;
144             break;
145     }
146     update_irq(p);
147 }
148 
149 static const MemoryRegionOps pic_ops[2] = {
150     [0 ... 1] = {
151         .read = pic_read,
152         .write = pic_write,
153         .impl = {
154             .min_access_size = 4,
155             .max_access_size = 4,
156         },
157         .valid = {
158             /*
159              * All XPS INTC registers are accessed through the PLB interface.
160              * The base address for these registers is provided by the
161              * configuration parameter, C_BASEADDR. Each register is 32 bits
162              * although some bits may be unused and is accessed on a 4-byte
163              * boundary offset from the base address.
164              */
165             .min_access_size = 4,
166             .max_access_size = 4,
167         },
168     },
169     [0].endianness = DEVICE_LITTLE_ENDIAN,
170     [1].endianness = DEVICE_BIG_ENDIAN,
171 };
172 
173 static void irq_handler(void *opaque, int irq, int level)
174 {
175     XpsIntc *p = opaque;
176 
177     /* edge triggered interrupt */
178     if (p->c_kind_of_intr & (1 << irq) && p->regs[R_MER] & 2) {
179         p->regs[R_ISR] |= (level << irq);
180     }
181 
182     p->irq_pin_state &= ~(1 << irq);
183     p->irq_pin_state |= level << irq;
184     update_irq(p);
185 }
186 
187 static void xilinx_intc_init(Object *obj)
188 {
189     XpsIntc *p = XILINX_INTC(obj);
190 
191     qdev_init_gpio_in(DEVICE(obj), irq_handler, 32);
192     sysbus_init_irq(SYS_BUS_DEVICE(obj), &p->parent_irq);
193     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &p->mmio);
194 }
195 
196 static void xilinx_intc_realize(DeviceState *dev, Error **errp)
197 {
198     XpsIntc *p = XILINX_INTC(dev);
199 
200     if (p->model_endianness == ENDIAN_MODE_UNSPECIFIED) {
201         error_setg(errp, TYPE_XILINX_INTC " property 'endianness'"
202                          " must be set to 'big' or 'little'");
203         return;
204     }
205 
206     memory_region_init_io(&p->mmio, OBJECT(dev),
207                           &pic_ops[p->model_endianness == ENDIAN_MODE_BIG],
208                           p, "xlnx.xps-intc",
209                           R_MAX * 4);
210 }
211 
212 static const Property xilinx_intc_properties[] = {
213     DEFINE_PROP_ENDIAN_NODEFAULT("endianness", XpsIntc, model_endianness),
214     DEFINE_PROP_UINT32("kind-of-intr", XpsIntc, c_kind_of_intr, 0),
215 };
216 
217 static void xilinx_intc_class_init(ObjectClass *klass, void *data)
218 {
219     DeviceClass *dc = DEVICE_CLASS(klass);
220 
221     dc->realize = xilinx_intc_realize;
222     device_class_set_props(dc, xilinx_intc_properties);
223 }
224 
225 static const TypeInfo xilinx_intc_info = {
226     .name          = TYPE_XILINX_INTC,
227     .parent        = TYPE_SYS_BUS_DEVICE,
228     .instance_size = sizeof(XpsIntc),
229     .instance_init = xilinx_intc_init,
230     .class_init    = xilinx_intc_class_init,
231 };
232 
233 static void xilinx_intc_register_types(void)
234 {
235     type_register_static(&xilinx_intc_info);
236 }
237 
238 type_init(xilinx_intc_register_types)
239