xref: /openbmc/qemu/hw/intc/loongarch_pch_pic.c (revision 270950b49d36659372acbc12e65ff34969bed678)
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 "hw/sysbus.h"
11 #include "hw/loongarch/virt.h"
12 #include "hw/irq.h"
13 #include "hw/intc/loongarch_pch_pic.h"
14 #include "hw/qdev-properties.h"
15 #include "migration/vmstate.h"
16 #include "trace.h"
17 #include "qapi/error.h"
18 
19 static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level)
20 {
21     uint64_t val;
22     int irq;
23 
24     if (level) {
25         val = mask & s->intirr & ~s->int_mask;
26         if (val) {
27             irq = ctz64(val);
28             s->intisr |= MAKE_64BIT_MASK(irq, 1);
29             qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1);
30         }
31     } else {
32         val = mask & s->intisr;
33         if (val) {
34             irq = ctz64(val);
35             s->intisr &= ~MAKE_64BIT_MASK(irq, 1);
36             qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0);
37         }
38     }
39 }
40 
41 static void pch_pic_irq_handler(void *opaque, int irq, int level)
42 {
43     LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
44     uint64_t mask = 1ULL << irq;
45 
46     assert(irq < s->irq_num);
47     trace_loongarch_pch_pic_irq_handler(irq, level);
48 
49     if (s->intedge & mask) {
50         /* Edge triggered */
51         if (level) {
52             if ((s->last_intirr & mask) == 0) {
53                 s->intirr |= mask;
54             }
55             s->last_intirr |= mask;
56         } else {
57             s->last_intirr &= ~mask;
58         }
59     } else {
60         /* Level triggered */
61         if (level) {
62             s->intirr |= mask;
63             s->last_intirr |= mask;
64         } else {
65             s->intirr &= ~mask;
66             s->last_intirr &= ~mask;
67         }
68     }
69     pch_pic_update_irq(s, mask, level);
70 }
71 
72 static uint64_t loongarch_pch_pic_low_readw(void *opaque, hwaddr addr,
73                                             unsigned size)
74 {
75     LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
76     uint64_t val = 0;
77     uint32_t offset = addr & 0xfff;
78 
79     switch (offset) {
80     case PCH_PIC_INT_ID_LO:
81         val = PCH_PIC_INT_ID_VAL;
82         break;
83     case PCH_PIC_INT_ID_HI:
84         /*
85          * With 7A1000 manual
86          *   bit  0-15 pch irqchip version
87          *   bit 16-31 irq number supported with pch irqchip
88          */
89         val = deposit32(PCH_PIC_INT_ID_VER, 16, 16, s->irq_num - 1);
90         break;
91     case PCH_PIC_INT_MASK_LO:
92         val = (uint32_t)s->int_mask;
93         break;
94     case PCH_PIC_INT_MASK_HI:
95         val = s->int_mask >> 32;
96         break;
97     case PCH_PIC_INT_EDGE_LO:
98         val = (uint32_t)s->intedge;
99         break;
100     case PCH_PIC_INT_EDGE_HI:
101         val = s->intedge >> 32;
102         break;
103     case PCH_PIC_HTMSI_EN_LO:
104         val = (uint32_t)s->htmsi_en;
105         break;
106     case PCH_PIC_HTMSI_EN_HI:
107         val = s->htmsi_en >> 32;
108         break;
109     case PCH_PIC_AUTO_CTRL0_LO:
110     case PCH_PIC_AUTO_CTRL0_HI:
111     case PCH_PIC_AUTO_CTRL1_LO:
112     case PCH_PIC_AUTO_CTRL1_HI:
113         break;
114     default:
115         break;
116     }
117 
118     trace_loongarch_pch_pic_low_readw(size, addr, val);
119     return val;
120 }
121 
122 static uint64_t get_writew_val(uint64_t value, uint32_t target, bool hi)
123 {
124     uint64_t mask = 0xffffffff00000000;
125     uint64_t data = target;
126 
127     return hi ? (value & ~mask) | (data << 32) : (value & mask) | data;
128 }
129 
130 static void loongarch_pch_pic_low_writew(void *opaque, hwaddr addr,
131                                          uint64_t value, unsigned size)
132 {
133     LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
134     uint32_t offset, old_valid, data = (uint32_t)value;
135     uint64_t old, int_mask;
136     offset = addr & 0xfff;
137 
138     trace_loongarch_pch_pic_low_writew(size, addr, data);
139 
140     switch (offset) {
141     case PCH_PIC_INT_MASK_LO:
142         old = s->int_mask;
143         s->int_mask = get_writew_val(old, data, 0);
144         old_valid = (uint32_t)old;
145         if (old_valid & ~data) {
146             pch_pic_update_irq(s, (old_valid & ~data), 1);
147         }
148         if (~old_valid & data) {
149             pch_pic_update_irq(s, (~old_valid & data), 0);
150         }
151         break;
152     case PCH_PIC_INT_MASK_HI:
153         old = s->int_mask;
154         s->int_mask = get_writew_val(old, data, 1);
155         old_valid = (uint32_t)(old >> 32);
156         int_mask = old_valid & ~data;
157         if (int_mask) {
158             pch_pic_update_irq(s, int_mask << 32, 1);
159         }
160         int_mask = ~old_valid & data;
161         if (int_mask) {
162             pch_pic_update_irq(s, int_mask << 32, 0);
163         }
164         break;
165     case PCH_PIC_INT_EDGE_LO:
166         s->intedge = get_writew_val(s->intedge, data, 0);
167         break;
168     case PCH_PIC_INT_EDGE_HI:
169         s->intedge = get_writew_val(s->intedge, data, 1);
170         break;
171     case PCH_PIC_INT_CLEAR_LO:
172         if (s->intedge & data) {
173             s->intirr &= (~data);
174             pch_pic_update_irq(s, data, 0);
175             s->intisr &= (~data);
176         }
177         break;
178     case PCH_PIC_INT_CLEAR_HI:
179         value <<= 32;
180         if (s->intedge & value) {
181             s->intirr &= (~value);
182             pch_pic_update_irq(s, value, 0);
183             s->intisr &= (~value);
184         }
185         break;
186     case PCH_PIC_HTMSI_EN_LO:
187         s->htmsi_en = get_writew_val(s->htmsi_en, data, 0);
188         break;
189     case PCH_PIC_HTMSI_EN_HI:
190         s->htmsi_en = get_writew_val(s->htmsi_en, data, 1);
191         break;
192     case PCH_PIC_AUTO_CTRL0_LO:
193     case PCH_PIC_AUTO_CTRL0_HI:
194     case PCH_PIC_AUTO_CTRL1_LO:
195     case PCH_PIC_AUTO_CTRL1_HI:
196         break;
197     default:
198         break;
199     }
200 }
201 
202 static uint64_t loongarch_pch_pic_high_readw(void *opaque, hwaddr addr,
203                                         unsigned size)
204 {
205     LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
206     uint64_t val = 0;
207     uint32_t offset = addr & 0xfff;
208 
209     switch (offset) {
210     case STATUS_LO_START:
211         val = (uint32_t)(s->intisr & (~s->int_mask));
212         break;
213     case STATUS_HI_START:
214         val = (s->intisr & (~s->int_mask)) >> 32;
215         break;
216     case POL_LO_START:
217         val = (uint32_t)s->int_polarity;
218         break;
219     case POL_HI_START:
220         val = s->int_polarity >> 32;
221         break;
222     default:
223         break;
224     }
225 
226     trace_loongarch_pch_pic_high_readw(size, addr, val);
227     return val;
228 }
229 
230 static void loongarch_pch_pic_high_writew(void *opaque, hwaddr addr,
231                                      uint64_t value, unsigned size)
232 {
233     LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
234     uint32_t offset, data = (uint32_t)value;
235     offset = addr & 0xfff;
236 
237     trace_loongarch_pch_pic_high_writew(size, addr, data);
238 
239     switch (offset) {
240     case STATUS_LO_START:
241         s->intisr = get_writew_val(s->intisr, data, 0);
242         break;
243     case STATUS_HI_START:
244         s->intisr = get_writew_val(s->intisr, data, 1);
245         break;
246     case POL_LO_START:
247         s->int_polarity = get_writew_val(s->int_polarity, data, 0);
248         break;
249     case POL_HI_START:
250         s->int_polarity = get_writew_val(s->int_polarity, data, 1);
251         break;
252     default:
253         break;
254     }
255 }
256 
257 static uint64_t loongarch_pch_pic_readb(void *opaque, hwaddr addr,
258                                         unsigned size)
259 {
260     LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
261     uint64_t val = 0;
262     uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
263     int64_t offset_tmp;
264 
265     switch (offset) {
266     case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
267         offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
268         if (offset_tmp >= 0 && offset_tmp < 64) {
269             val = s->htmsi_vector[offset_tmp];
270         }
271         break;
272     case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
273         offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
274         if (offset_tmp >= 0 && offset_tmp < 64) {
275             val = s->route_entry[offset_tmp];
276         }
277         break;
278     default:
279         break;
280     }
281 
282     trace_loongarch_pch_pic_readb(size, addr, val);
283     return val;
284 }
285 
286 static void loongarch_pch_pic_writeb(void *opaque, hwaddr addr,
287                                      uint64_t data, unsigned size)
288 {
289     LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
290     int32_t offset_tmp;
291     uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
292 
293     trace_loongarch_pch_pic_writeb(size, addr, data);
294 
295     switch (offset) {
296     case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
297         offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
298         if (offset_tmp >= 0 && offset_tmp < 64) {
299             s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff);
300         }
301         break;
302     case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
303         offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
304         if (offset_tmp >= 0 && offset_tmp < 64) {
305             s->route_entry[offset_tmp] = (uint8_t)(data & 0xff);
306         }
307         break;
308     default:
309         break;
310     }
311 }
312 
313 static const MemoryRegionOps loongarch_pch_pic_reg32_low_ops = {
314     .read = loongarch_pch_pic_low_readw,
315     .write = loongarch_pch_pic_low_writew,
316     .valid = {
317         .min_access_size = 4,
318         .max_access_size = 8,
319     },
320     .impl = {
321         .min_access_size = 4,
322         .max_access_size = 4,
323     },
324     .endianness = DEVICE_LITTLE_ENDIAN,
325 };
326 
327 static const MemoryRegionOps loongarch_pch_pic_reg32_high_ops = {
328     .read = loongarch_pch_pic_high_readw,
329     .write = loongarch_pch_pic_high_writew,
330     .valid = {
331         .min_access_size = 4,
332         .max_access_size = 8,
333     },
334     .impl = {
335         .min_access_size = 4,
336         .max_access_size = 4,
337     },
338     .endianness = DEVICE_LITTLE_ENDIAN,
339 };
340 
341 static const MemoryRegionOps loongarch_pch_pic_reg8_ops = {
342     .read = loongarch_pch_pic_readb,
343     .write = loongarch_pch_pic_writeb,
344     .valid = {
345         .min_access_size = 1,
346         .max_access_size = 1,
347     },
348     .impl = {
349         .min_access_size = 1,
350         .max_access_size = 1,
351     },
352     .endianness = DEVICE_LITTLE_ENDIAN,
353 };
354 
355 static void loongarch_pch_pic_reset(DeviceState *d)
356 {
357     LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(d);
358     int i;
359 
360     s->int_mask = -1;
361     s->htmsi_en = 0x0;
362     s->intedge  = 0x0;
363     s->intclr   = 0x0;
364     s->auto_crtl0 = 0x0;
365     s->auto_crtl1 = 0x0;
366     for (i = 0; i < 64; i++) {
367         s->route_entry[i] = 0x1;
368         s->htmsi_vector[i] = 0x0;
369     }
370     s->intirr = 0x0;
371     s->intisr = 0x0;
372     s->last_intirr = 0x0;
373     s->int_polarity = 0x0;
374 }
375 
376 static void loongarch_pch_pic_realize(DeviceState *dev, Error **errp)
377 {
378     LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(dev);
379 
380     if (!s->irq_num || s->irq_num  > PCH_PIC_IRQ_NUM) {
381         error_setg(errp, "Invalid 'pic_irq_num'");
382         return;
383     }
384 
385     qdev_init_gpio_out(dev, s->parent_irq, s->irq_num);
386     qdev_init_gpio_in(dev, pch_pic_irq_handler, s->irq_num);
387 }
388 
389 static void loongarch_pch_pic_init(Object *obj)
390 {
391     LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(obj);
392     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
393 
394     memory_region_init_io(&s->iomem32_low, obj,
395                           &loongarch_pch_pic_reg32_low_ops,
396                           s, PCH_PIC_NAME(.reg32_part1), 0x100);
397     memory_region_init_io(&s->iomem8, obj, &loongarch_pch_pic_reg8_ops,
398                           s, PCH_PIC_NAME(.reg8), 0x2a0);
399     memory_region_init_io(&s->iomem32_high, obj,
400                           &loongarch_pch_pic_reg32_high_ops,
401                           s, PCH_PIC_NAME(.reg32_part2), 0xc60);
402     sysbus_init_mmio(sbd, &s->iomem32_low);
403     sysbus_init_mmio(sbd, &s->iomem8);
404     sysbus_init_mmio(sbd, &s->iomem32_high);
405 
406 }
407 
408 static Property loongarch_pch_pic_properties[] = {
409     DEFINE_PROP_UINT32("pch_pic_irq_num",  LoongArchPCHPIC, irq_num, 0),
410     DEFINE_PROP_END_OF_LIST(),
411 };
412 
413 static const VMStateDescription vmstate_loongarch_pch_pic = {
414     .name = TYPE_LOONGARCH_PCH_PIC,
415     .version_id = 1,
416     .minimum_version_id = 1,
417     .fields = (VMStateField[]) {
418         VMSTATE_UINT64(int_mask, LoongArchPCHPIC),
419         VMSTATE_UINT64(htmsi_en, LoongArchPCHPIC),
420         VMSTATE_UINT64(intedge, LoongArchPCHPIC),
421         VMSTATE_UINT64(intclr, LoongArchPCHPIC),
422         VMSTATE_UINT64(auto_crtl0, LoongArchPCHPIC),
423         VMSTATE_UINT64(auto_crtl1, LoongArchPCHPIC),
424         VMSTATE_UINT8_ARRAY(route_entry, LoongArchPCHPIC, 64),
425         VMSTATE_UINT8_ARRAY(htmsi_vector, LoongArchPCHPIC, 64),
426         VMSTATE_UINT64(last_intirr, LoongArchPCHPIC),
427         VMSTATE_UINT64(intirr, LoongArchPCHPIC),
428         VMSTATE_UINT64(intisr, LoongArchPCHPIC),
429         VMSTATE_UINT64(int_polarity, LoongArchPCHPIC),
430         VMSTATE_END_OF_LIST()
431     }
432 };
433 
434 static void loongarch_pch_pic_class_init(ObjectClass *klass, void *data)
435 {
436     DeviceClass *dc = DEVICE_CLASS(klass);
437 
438     dc->realize = loongarch_pch_pic_realize;
439     dc->reset = loongarch_pch_pic_reset;
440     dc->vmsd = &vmstate_loongarch_pch_pic;
441     device_class_set_props(dc, loongarch_pch_pic_properties);
442 }
443 
444 static const TypeInfo loongarch_pch_pic_info = {
445     .name          = TYPE_LOONGARCH_PCH_PIC,
446     .parent        = TYPE_SYS_BUS_DEVICE,
447     .instance_size = sizeof(LoongArchPCHPIC),
448     .instance_init = loongarch_pch_pic_init,
449     .class_init    = loongarch_pch_pic_class_init,
450 };
451 
452 static void loongarch_pch_pic_register_types(void)
453 {
454     type_register_static(&loongarch_pch_pic_info);
455 }
456 
457 type_init(loongarch_pch_pic_register_types)
458