1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * QEMU Loongson 7A1000 I/O interrupt controller.
4 *
5 * Copyright (C) 2021 Loongson Technology Corporation Limited
6 */
7
8 #include "qemu/osdep.h"
9 #include "qemu/bitops.h"
10 #include "qemu/log.h"
11 #include "hw/irq.h"
12 #include "hw/intc/loongarch_pch_pic.h"
13 #include "trace.h"
14 #include "qapi/error.h"
15
pch_pic_update_irq(LoongArchPICCommonState * s,uint64_t mask,int level)16 static void pch_pic_update_irq(LoongArchPICCommonState *s, uint64_t mask,
17 int level)
18 {
19 uint64_t val;
20 int irq;
21
22 if (level) {
23 val = mask & s->intirr & ~s->int_mask;
24 if (val) {
25 irq = ctz64(val);
26 s->intisr |= MAKE_64BIT_MASK(irq, 1);
27 qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1);
28 }
29 } else {
30 /*
31 * intirr means requested pending irq
32 * do not clear pending irq for edge-triggered on lowering edge
33 */
34 val = mask & s->intisr & ~s->intirr;
35 if (val) {
36 irq = ctz64(val);
37 s->intisr &= ~MAKE_64BIT_MASK(irq, 1);
38 qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0);
39 }
40 }
41 }
42
pch_pic_irq_handler(void * opaque,int irq,int level)43 static void pch_pic_irq_handler(void *opaque, int irq, int level)
44 {
45 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
46 uint64_t mask = 1ULL << irq;
47
48 assert(irq < s->irq_num);
49 trace_loongarch_pch_pic_irq_handler(irq, level);
50
51 if (s->intedge & mask) {
52 /* Edge triggered */
53 if (level) {
54 if ((s->last_intirr & mask) == 0) {
55 /* marked pending on a rising edge */
56 s->intirr |= mask;
57 }
58 s->last_intirr |= mask;
59 } else {
60 s->last_intirr &= ~mask;
61 }
62 } else {
63 /* Level triggered */
64 if (level) {
65 s->intirr |= mask;
66 s->last_intirr |= mask;
67 } else {
68 s->intirr &= ~mask;
69 s->last_intirr &= ~mask;
70 }
71 }
72 pch_pic_update_irq(s, mask, level);
73 }
74
pch_pic_read(void * opaque,hwaddr addr,uint64_t field_mask)75 static uint64_t pch_pic_read(void *opaque, hwaddr addr, uint64_t field_mask)
76 {
77 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
78 uint64_t val = 0;
79 uint32_t offset;
80
81 offset = addr & 7;
82 addr -= offset;
83 switch (addr) {
84 case PCH_PIC_INT_ID:
85 val = cpu_to_le64(s->id.data);
86 break;
87 case PCH_PIC_INT_MASK:
88 val = s->int_mask;
89 break;
90 case PCH_PIC_INT_EDGE:
91 val = s->intedge;
92 break;
93 case PCH_PIC_HTMSI_EN:
94 val = s->htmsi_en;
95 break;
96 case PCH_PIC_AUTO_CTRL0:
97 case PCH_PIC_AUTO_CTRL1:
98 /* PCH PIC connect to EXTIOI always, discard auto_ctrl access */
99 break;
100 case PCH_PIC_INT_STATUS:
101 val = s->intisr & (~s->int_mask);
102 break;
103 case PCH_PIC_INT_POL:
104 val = s->int_polarity;
105 break;
106 case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END:
107 val = *(uint64_t *)(s->htmsi_vector + addr - PCH_PIC_HTMSI_VEC);
108 break;
109 case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END:
110 val = *(uint64_t *)(s->route_entry + addr - PCH_PIC_ROUTE_ENTRY);
111 break;
112 default:
113 qemu_log_mask(LOG_GUEST_ERROR,
114 "pch_pic_read: Bad address 0x%"PRIx64"\n", addr);
115 break;
116 }
117
118 return (val >> (offset * 8)) & field_mask;
119 }
120
pch_pic_write(void * opaque,hwaddr addr,uint64_t value,uint64_t field_mask)121 static void pch_pic_write(void *opaque, hwaddr addr, uint64_t value,
122 uint64_t field_mask)
123 {
124 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
125 uint32_t offset;
126 uint64_t old, mask, data, *ptemp;
127
128 offset = addr & 7;
129 addr -= offset;
130 mask = field_mask << (offset * 8);
131 data = (value & field_mask) << (offset * 8);
132 switch (addr) {
133 case PCH_PIC_INT_MASK:
134 old = s->int_mask;
135 s->int_mask = (old & ~mask) | data;
136 if (old & ~data) {
137 pch_pic_update_irq(s, old & ~data, 1);
138 }
139
140 if (~old & data) {
141 pch_pic_update_irq(s, ~old & data, 0);
142 }
143 break;
144 case PCH_PIC_INT_EDGE:
145 s->intedge = (s->intedge & ~mask) | data;
146 break;
147 case PCH_PIC_INT_CLEAR:
148 if (s->intedge & data) {
149 s->intirr &= ~data;
150 pch_pic_update_irq(s, data, 0);
151 s->intisr &= ~data;
152 }
153 break;
154 case PCH_PIC_HTMSI_EN:
155 s->htmsi_en = (s->htmsi_en & ~mask) | data;
156 break;
157 case PCH_PIC_AUTO_CTRL0:
158 case PCH_PIC_AUTO_CTRL1:
159 /* Discard auto_ctrl access */
160 break;
161 case PCH_PIC_INT_POL:
162 s->int_polarity = (s->int_polarity & ~mask) | data;
163 break;
164 case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END:
165 ptemp = (uint64_t *)(s->htmsi_vector + addr - PCH_PIC_HTMSI_VEC);
166 *ptemp = (*ptemp & ~mask) | data;
167 break;
168 case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END:
169 ptemp = (uint64_t *)(s->route_entry + addr - PCH_PIC_ROUTE_ENTRY);
170 *ptemp = (*ptemp & ~mask) | data;
171 break;
172 default:
173 qemu_log_mask(LOG_GUEST_ERROR,
174 "pch_pic_write: Bad address 0x%"PRIx64"\n", addr);
175 break;
176 }
177 }
178
loongarch_pch_pic_read(void * opaque,hwaddr addr,unsigned size)179 static uint64_t loongarch_pch_pic_read(void *opaque, hwaddr addr,
180 unsigned size)
181 {
182 uint64_t val = 0;
183
184 switch (size) {
185 case 1:
186 val = pch_pic_read(opaque, addr, UCHAR_MAX);
187 break;
188 case 2:
189 val = pch_pic_read(opaque, addr, USHRT_MAX);
190 break;
191 case 4:
192 val = pch_pic_read(opaque, addr, UINT_MAX);
193 break;
194 case 8:
195 val = pch_pic_read(opaque, addr, UINT64_MAX);
196 break;
197 default:
198 qemu_log_mask(LOG_GUEST_ERROR,
199 "loongarch_pch_pic_read: Bad size %d\n", size);
200 break;
201 }
202
203 trace_loongarch_pch_pic_read(size, addr, val);
204 return val;
205 }
206
loongarch_pch_pic_write(void * opaque,hwaddr addr,uint64_t value,unsigned size)207 static void loongarch_pch_pic_write(void *opaque, hwaddr addr,
208 uint64_t value, unsigned size)
209 {
210 trace_loongarch_pch_pic_write(size, addr, value);
211
212 switch (size) {
213 case 1:
214 pch_pic_write(opaque, addr, value, UCHAR_MAX);
215 break;
216 case 2:
217 pch_pic_write(opaque, addr, value, USHRT_MAX);
218 break;
219 break;
220 case 4:
221 pch_pic_write(opaque, addr, value, UINT_MAX);
222 break;
223 case 8:
224 pch_pic_write(opaque, addr, value, UINT64_MAX);
225 break;
226 default:
227 qemu_log_mask(LOG_GUEST_ERROR,
228 "loongarch_pch_pic_write: Bad size %d\n", size);
229 break;
230 }
231 }
232
233 static const MemoryRegionOps loongarch_pch_pic_ops = {
234 .read = loongarch_pch_pic_read,
235 .write = loongarch_pch_pic_write,
236 .valid = {
237 .min_access_size = 1,
238 .max_access_size = 8,
239 /*
240 * PCH PIC device would not work correctly if the guest was doing
241 * unaligned access. This might not be a limitation on the real
242 * device but in practice there is no reason for a guest to access
243 * this device unaligned.
244 */
245 .unaligned = false,
246 },
247 .impl = {
248 .min_access_size = 1,
249 .max_access_size = 8,
250 },
251 .endianness = DEVICE_LITTLE_ENDIAN,
252 };
253
loongarch_pic_reset_hold(Object * obj,ResetType type)254 static void loongarch_pic_reset_hold(Object *obj, ResetType type)
255 {
256 LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(obj);
257
258 if (lpc->parent_phases.hold) {
259 lpc->parent_phases.hold(obj, type);
260 }
261 }
262
loongarch_pic_realize(DeviceState * dev,Error ** errp)263 static void loongarch_pic_realize(DeviceState *dev, Error **errp)
264 {
265 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(dev);
266 LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(dev);
267 SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
268 Error *local_err = NULL;
269
270 lpc->parent_realize(dev, &local_err);
271 if (local_err) {
272 error_propagate(errp, local_err);
273 return;
274 }
275
276 qdev_init_gpio_out(dev, s->parent_irq, s->irq_num);
277 qdev_init_gpio_in(dev, pch_pic_irq_handler, s->irq_num);
278 memory_region_init_io(&s->iomem, OBJECT(dev),
279 &loongarch_pch_pic_ops,
280 s, TYPE_LOONGARCH_PIC, VIRT_PCH_REG_SIZE);
281 sysbus_init_mmio(sbd, &s->iomem);
282 }
283
loongarch_pic_class_init(ObjectClass * klass,const void * data)284 static void loongarch_pic_class_init(ObjectClass *klass, const void *data)
285 {
286 DeviceClass *dc = DEVICE_CLASS(klass);
287 LoongarchPICClass *lpc = LOONGARCH_PIC_CLASS(klass);
288 ResettableClass *rc = RESETTABLE_CLASS(klass);
289
290 resettable_class_set_parent_phases(rc, NULL, loongarch_pic_reset_hold,
291 NULL, &lpc->parent_phases);
292 device_class_set_parent_realize(dc, loongarch_pic_realize,
293 &lpc->parent_realize);
294 }
295
296 static const TypeInfo loongarch_pic_types[] = {
297 {
298 .name = TYPE_LOONGARCH_PIC,
299 .parent = TYPE_LOONGARCH_PIC_COMMON,
300 .instance_size = sizeof(LoongarchPICState),
301 .class_size = sizeof(LoongarchPICClass),
302 .class_init = loongarch_pic_class_init,
303 }
304 };
305
306 DEFINE_TYPES(loongarch_pic_types)
307