xref: /openbmc/qemu/hw/intc/loongarch_pch_pic.c (revision 6dab13c4152b5b3c35b627483bfdce2d331d95f2)
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 
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 
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 
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 = 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     default:
107         qemu_log_mask(LOG_GUEST_ERROR,
108                       "pch_pic_read: Bad address 0x%"PRIx64"\n", addr);
109         break;
110     }
111 
112     return (val >> (offset * 8)) & field_mask;
113 }
114 
115 static uint64_t loongarch_pch_pic_read(void *opaque, hwaddr addr,
116                                        unsigned size)
117 {
118     uint64_t val = 0;
119 
120     switch (size) {
121     case 1:
122         val = pch_pic_read(opaque, addr, UCHAR_MAX);
123         break;
124     case 2:
125         val = pch_pic_read(opaque, addr, USHRT_MAX);
126         break;
127     case 4:
128         val = pch_pic_read(opaque, addr, UINT_MAX);
129         break;
130     case 8:
131         val = pch_pic_read(opaque, addr, UINT64_MAX);
132         break;
133     default:
134         qemu_log_mask(LOG_GUEST_ERROR,
135                       "loongarch_pch_pic_read: Bad size %d\n", size);
136         break;
137     }
138 
139     return val;
140 }
141 
142 static uint64_t loongarch_pch_pic_low_readw(void *opaque, hwaddr addr,
143                                             unsigned size)
144 {
145     uint64_t val;
146 
147     val = loongarch_pch_pic_read(opaque, addr, size);
148     trace_loongarch_pch_pic_low_readw(size, addr, val);
149     return val;
150 }
151 
152 static uint64_t get_writew_val(uint64_t value, uint32_t target, bool hi)
153 {
154     uint64_t mask = 0xffffffff00000000;
155     uint64_t data = target;
156 
157     return hi ? (value & ~mask) | (data << 32) : (value & mask) | data;
158 }
159 
160 static void loongarch_pch_pic_low_writew(void *opaque, hwaddr addr,
161                                          uint64_t value, unsigned size)
162 {
163     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
164     uint32_t old_valid, data = (uint32_t)value;
165     uint64_t old, int_mask;
166 
167     trace_loongarch_pch_pic_low_writew(size, addr, data);
168 
169     switch (addr) {
170     case PCH_PIC_INT_MASK:
171         old = s->int_mask;
172         s->int_mask = get_writew_val(old, data, 0);
173         old_valid = (uint32_t)old;
174         if (old_valid & ~data) {
175             pch_pic_update_irq(s, (old_valid & ~data), 1);
176         }
177         if (~old_valid & data) {
178             pch_pic_update_irq(s, (~old_valid & data), 0);
179         }
180         break;
181     case PCH_PIC_INT_MASK + 4:
182         old = s->int_mask;
183         s->int_mask = get_writew_val(old, data, 1);
184         old_valid = (uint32_t)(old >> 32);
185         int_mask = old_valid & ~data;
186         if (int_mask) {
187             pch_pic_update_irq(s, int_mask << 32, 1);
188         }
189         int_mask = ~old_valid & data;
190         if (int_mask) {
191             pch_pic_update_irq(s, int_mask << 32, 0);
192         }
193         break;
194     case PCH_PIC_INT_EDGE:
195         s->intedge = get_writew_val(s->intedge, data, 0);
196         break;
197     case PCH_PIC_INT_EDGE + 4:
198         s->intedge = get_writew_val(s->intedge, data, 1);
199         break;
200     case PCH_PIC_INT_CLEAR:
201         if (s->intedge & data) {
202             s->intirr &= (~data);
203             pch_pic_update_irq(s, data, 0);
204             s->intisr &= (~data);
205         }
206         break;
207     case PCH_PIC_INT_CLEAR + 4:
208         value <<= 32;
209         if (s->intedge & value) {
210             s->intirr &= (~value);
211             pch_pic_update_irq(s, value, 0);
212             s->intisr &= (~value);
213         }
214         break;
215     case PCH_PIC_HTMSI_EN:
216         s->htmsi_en = get_writew_val(s->htmsi_en, data, 0);
217         break;
218     case PCH_PIC_HTMSI_EN + 4:
219         s->htmsi_en = get_writew_val(s->htmsi_en, data, 1);
220         break;
221     case PCH_PIC_AUTO_CTRL0:
222     case PCH_PIC_AUTO_CTRL0 + 4:
223     case PCH_PIC_AUTO_CTRL1:
224     case PCH_PIC_AUTO_CTRL1 + 4:
225         /* discard auto_ctrl access */
226         break;
227     default:
228         break;
229     }
230 }
231 
232 static uint64_t loongarch_pch_pic_high_readw(void *opaque, hwaddr addr,
233                                         unsigned size)
234 {
235     uint64_t val;
236 
237     addr += PCH_PIC_INT_STATUS;
238     val = loongarch_pch_pic_read(opaque, addr, size);
239     trace_loongarch_pch_pic_high_readw(size, addr, val);
240     return val;
241 }
242 
243 static void loongarch_pch_pic_high_writew(void *opaque, hwaddr addr,
244                                      uint64_t value, unsigned size)
245 {
246     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
247     uint32_t data = (uint32_t)value;
248 
249     addr += PCH_PIC_INT_STATUS;
250     trace_loongarch_pch_pic_high_writew(size, addr, data);
251 
252     switch (addr) {
253     case PCH_PIC_INT_POL:
254         s->int_polarity = get_writew_val(s->int_polarity, data, 0);
255         break;
256     case PCH_PIC_INT_POL + 4:
257         s->int_polarity = get_writew_val(s->int_polarity, data, 1);
258         break;
259     default:
260         break;
261     }
262 }
263 
264 static uint64_t loongarch_pch_pic_readb(void *opaque, hwaddr addr,
265                                         unsigned size)
266 {
267     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
268     uint64_t val = 0;
269     int64_t offset_tmp;
270 
271     addr += PCH_PIC_ROUTE_ENTRY;
272     switch (addr) {
273     case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END:
274         offset_tmp = addr - PCH_PIC_HTMSI_VEC;
275         if (offset_tmp >= 0 && offset_tmp < 64) {
276             val = s->htmsi_vector[offset_tmp];
277         }
278         break;
279     case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END:
280         offset_tmp = addr - PCH_PIC_ROUTE_ENTRY;
281         if (offset_tmp >= 0 && offset_tmp < 64) {
282             val = s->route_entry[offset_tmp];
283         }
284         break;
285     default:
286         break;
287     }
288 
289     trace_loongarch_pch_pic_readb(size, addr, val);
290     return val;
291 }
292 
293 static void loongarch_pch_pic_writeb(void *opaque, hwaddr addr,
294                                      uint64_t data, unsigned size)
295 {
296     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
297     int32_t offset_tmp;
298 
299     addr += PCH_PIC_ROUTE_ENTRY;
300     trace_loongarch_pch_pic_writeb(size, addr, data);
301 
302     switch (addr) {
303     case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END:
304         offset_tmp = addr - PCH_PIC_HTMSI_VEC;
305         if (offset_tmp >= 0 && offset_tmp < 64) {
306             s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff);
307         }
308         break;
309     case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END:
310         offset_tmp = addr - PCH_PIC_ROUTE_ENTRY;
311         if (offset_tmp >= 0 && offset_tmp < 64) {
312             s->route_entry[offset_tmp] = (uint8_t)(data & 0xff);
313         }
314         break;
315     default:
316         break;
317     }
318 }
319 
320 static const MemoryRegionOps loongarch_pch_pic_reg32_low_ops = {
321     .read = loongarch_pch_pic_low_readw,
322     .write = loongarch_pch_pic_low_writew,
323     .valid = {
324         .min_access_size = 4,
325         .max_access_size = 8,
326     },
327     .impl = {
328         .min_access_size = 4,
329         .max_access_size = 4,
330     },
331     .endianness = DEVICE_LITTLE_ENDIAN,
332 };
333 
334 static const MemoryRegionOps loongarch_pch_pic_reg32_high_ops = {
335     .read = loongarch_pch_pic_high_readw,
336     .write = loongarch_pch_pic_high_writew,
337     .valid = {
338         .min_access_size = 4,
339         .max_access_size = 8,
340     },
341     .impl = {
342         .min_access_size = 4,
343         .max_access_size = 4,
344     },
345     .endianness = DEVICE_LITTLE_ENDIAN,
346 };
347 
348 static const MemoryRegionOps loongarch_pch_pic_reg8_ops = {
349     .read = loongarch_pch_pic_readb,
350     .write = loongarch_pch_pic_writeb,
351     .valid = {
352         .min_access_size = 1,
353         .max_access_size = 1,
354     },
355     .impl = {
356         .min_access_size = 1,
357         .max_access_size = 1,
358     },
359     .endianness = DEVICE_LITTLE_ENDIAN,
360 };
361 
362 static void loongarch_pic_reset_hold(Object *obj, ResetType type)
363 {
364     LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(obj);
365 
366     if (lpc->parent_phases.hold) {
367         lpc->parent_phases.hold(obj, type);
368     }
369 }
370 
371 static void loongarch_pic_realize(DeviceState *dev, Error **errp)
372 {
373     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(dev);
374     LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(dev);
375     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
376     Error *local_err = NULL;
377 
378     lpc->parent_realize(dev, &local_err);
379     if (local_err) {
380         error_propagate(errp, local_err);
381         return;
382     }
383 
384     qdev_init_gpio_out(dev, s->parent_irq, s->irq_num);
385     qdev_init_gpio_in(dev, pch_pic_irq_handler, s->irq_num);
386     memory_region_init_io(&s->iomem32_low, OBJECT(dev),
387                           &loongarch_pch_pic_reg32_low_ops,
388                           s, PCH_PIC_NAME(.reg32_part1), 0x100);
389     memory_region_init_io(&s->iomem8, OBJECT(dev), &loongarch_pch_pic_reg8_ops,
390                           s, PCH_PIC_NAME(.reg8), 0x2a0);
391     memory_region_init_io(&s->iomem32_high, OBJECT(dev),
392                           &loongarch_pch_pic_reg32_high_ops,
393                           s, PCH_PIC_NAME(.reg32_part2), 0xc60);
394     sysbus_init_mmio(sbd, &s->iomem32_low);
395     sysbus_init_mmio(sbd, &s->iomem8);
396     sysbus_init_mmio(sbd, &s->iomem32_high);
397 
398 }
399 
400 static void loongarch_pic_class_init(ObjectClass *klass, const void *data)
401 {
402     DeviceClass *dc = DEVICE_CLASS(klass);
403     LoongarchPICClass *lpc = LOONGARCH_PIC_CLASS(klass);
404     ResettableClass *rc = RESETTABLE_CLASS(klass);
405 
406     resettable_class_set_parent_phases(rc, NULL, loongarch_pic_reset_hold,
407                                        NULL, &lpc->parent_phases);
408     device_class_set_parent_realize(dc, loongarch_pic_realize,
409                                     &lpc->parent_realize);
410 }
411 
412 static const TypeInfo loongarch_pic_types[] = {
413    {
414         .name               = TYPE_LOONGARCH_PIC,
415         .parent             = TYPE_LOONGARCH_PIC_COMMON,
416         .instance_size      = sizeof(LoongarchPICState),
417         .class_size         = sizeof(LoongarchPICClass),
418         .class_init         = loongarch_pic_class_init,
419     }
420 };
421 
422 DEFINE_TYPES(loongarch_pic_types)
423