xref: /qemu/hw/intc/grlib_irqmp.c (revision e4751d340a49b117b90a411b179b8c892cf43d85)
1  /*
2   * QEMU GRLIB IRQMP Emulator
3   *
4   * (Extended interrupt not supported)
5   *
6   * SPDX-License-Identifier: MIT
7   *
8   * Copyright (c) 2010-2024 AdaCore
9   *
10   * Permission is hereby granted, free of charge, to any person obtaining a copy
11   * of this software and associated documentation files (the "Software"), to deal
12   * in the Software without restriction, including without limitation the rights
13   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14   * copies of the Software, and to permit persons to whom the Software is
15   * furnished to do so, subject to the following conditions:
16   *
17   * The above copyright notice and this permission notice shall be included in
18   * all copies or substantial portions of the Software.
19   *
20   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23   * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26   * THE SOFTWARE.
27   */
28  
29  #include "qemu/osdep.h"
30  #include "hw/irq.h"
31  #include "hw/sysbus.h"
32  
33  #include "hw/qdev-properties.h"
34  #include "hw/intc/grlib_irqmp.h"
35  
36  #include "trace.h"
37  #include "qapi/error.h"
38  #include "qemu/module.h"
39  #include "qom/object.h"
40  
41  #define IRQMP_MAX_CPU 16
42  #define IRQMP_REG_SIZE 256      /* Size of memory mapped registers */
43  
44  /* Memory mapped register offsets */
45  #define LEVEL_OFFSET     0x00
46  #define PENDING_OFFSET   0x04
47  #define FORCE0_OFFSET    0x08
48  #define CLEAR_OFFSET     0x0C
49  #define MP_STATUS_OFFSET 0x10
50  #define BROADCAST_OFFSET 0x14
51  #define MASK_OFFSET      0x40
52  #define FORCE_OFFSET     0x80
53  #define EXTENDED_OFFSET  0xC0
54  
55  /* Multiprocessor Status Register  */
56  #define MP_STATUS_CPU_STATUS_MASK ((1 << IRQMP_MAX_CPU)-2)
57  #define MP_STATUS_NCPU_SHIFT      28
58  
59  #define MAX_PILS 16
60  
61  OBJECT_DECLARE_SIMPLE_TYPE(IRQMP, GRLIB_IRQMP)
62  
63  typedef struct IRQMPState IRQMPState;
64  
65  struct IRQMP {
66      SysBusDevice parent_obj;
67  
68      MemoryRegion iomem;
69  
70      unsigned int ncpus;
71      IRQMPState *state;
72      qemu_irq start_signal[IRQMP_MAX_CPU];
73      qemu_irq irq[IRQMP_MAX_CPU];
74  };
75  
76  struct IRQMPState {
77      uint32_t level;
78      uint32_t pending;
79      uint32_t clear;
80      uint32_t mpstatus;
81      uint32_t broadcast;
82  
83      uint32_t mask[IRQMP_MAX_CPU];
84      uint32_t force[IRQMP_MAX_CPU];
85      uint32_t extended[IRQMP_MAX_CPU];
86  
87      IRQMP    *parent;
88  };
89  
90  static void grlib_irqmp_check_irqs(IRQMPState *state)
91  {
92      int i;
93  
94      assert(state != NULL);
95      assert(state->parent != NULL);
96  
97      for (i = 0; i < state->parent->ncpus; i++) {
98          uint32_t pend = (state->pending | state->force[i]) & state->mask[i];
99          uint32_t level0 = pend & ~state->level;
100          uint32_t level1 = pend &  state->level;
101  
102          trace_grlib_irqmp_check_irqs(state->pending, state->force[i],
103                                       state->mask[i], level1, level0);
104  
105          /* Trigger level1 interrupt first and level0 if there is no level1 */
106          qemu_set_irq(state->parent->irq[i], level1 ?: level0);
107      }
108  }
109  
110  static void grlib_irqmp_ack_mask(IRQMPState *state, unsigned int cpu,
111                                   uint32_t mask)
112  {
113      /* Clear registers */
114      state->pending  &= ~mask;
115      state->force[cpu] &= ~mask;
116  
117      grlib_irqmp_check_irqs(state);
118  }
119  
120  void grlib_irqmp_ack(DeviceState *dev, unsigned int cpu, int intno)
121  {
122      IRQMP        *irqmp = GRLIB_IRQMP(dev);
123      IRQMPState   *state;
124      uint32_t      mask;
125  
126      state = irqmp->state;
127      assert(state != NULL);
128  
129      intno &= 15;
130      mask = 1 << intno;
131  
132      trace_grlib_irqmp_ack(intno);
133  
134      grlib_irqmp_ack_mask(state, cpu, mask);
135  }
136  
137  static void grlib_irqmp_set_irq(void *opaque, int irq, int level)
138  {
139      IRQMP      *irqmp = GRLIB_IRQMP(opaque);
140      IRQMPState *s;
141      int         i = 0;
142  
143      s = irqmp->state;
144      assert(s         != NULL);
145      assert(s->parent != NULL);
146  
147  
148      if (level) {
149          trace_grlib_irqmp_set_irq(irq);
150  
151          if (s->broadcast & 1 << irq) {
152              /* Broadcasted IRQ */
153              for (i = 0; i < IRQMP_MAX_CPU; i++) {
154                  s->force[i] |= 1 << irq;
155              }
156          } else {
157              s->pending |= 1 << irq;
158          }
159          grlib_irqmp_check_irqs(s);
160      }
161  }
162  
163  static uint64_t grlib_irqmp_read(void *opaque, hwaddr addr,
164                                   unsigned size)
165  {
166      IRQMP      *irqmp = opaque;
167      IRQMPState *state;
168  
169      assert(irqmp != NULL);
170      state = irqmp->state;
171      assert(state != NULL);
172  
173      addr &= 0xff;
174  
175      /* global registers */
176      switch (addr) {
177      case LEVEL_OFFSET:
178          return state->level;
179  
180      case PENDING_OFFSET:
181          return state->pending;
182  
183      case FORCE0_OFFSET:
184          /* This register is an "alias" for the force register of CPU 0 */
185          return state->force[0];
186  
187      case CLEAR_OFFSET:
188          /* Always read as 0 */
189          return 0;
190  
191      case MP_STATUS_OFFSET:
192          return state->mpstatus;
193  
194      case BROADCAST_OFFSET:
195          return state->broadcast;
196  
197      default:
198          break;
199      }
200  
201      /* mask registers */
202      if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) {
203          int cpu = (addr - MASK_OFFSET) / 4;
204          assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
205  
206          return state->mask[cpu];
207      }
208  
209      /* force registers */
210      if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) {
211          int cpu = (addr - FORCE_OFFSET) / 4;
212          assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
213  
214          return state->force[cpu];
215      }
216  
217      /* extended (not supported) */
218      if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) {
219          int cpu = (addr - EXTENDED_OFFSET) / 4;
220          assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
221  
222          return state->extended[cpu];
223      }
224  
225      trace_grlib_irqmp_readl_unknown(addr);
226      return 0;
227  }
228  
229  static void grlib_irqmp_write(void *opaque, hwaddr addr,
230                                uint64_t value, unsigned size)
231  {
232      IRQMP *irqmp = opaque;
233      IRQMPState *state;
234      int i;
235  
236      assert(irqmp != NULL);
237      state = irqmp->state;
238      assert(state != NULL);
239  
240      addr &= 0xff;
241  
242      /* global registers */
243      switch (addr) {
244      case LEVEL_OFFSET:
245          value &= 0xFFFF << 1; /* clean up the value */
246          state->level = value;
247          return;
248  
249      case PENDING_OFFSET:
250          /* Read Only */
251          return;
252  
253      case FORCE0_OFFSET:
254          /* This register is an "alias" for the force register of CPU 0 */
255  
256          value &= 0xFFFE; /* clean up the value */
257          state->force[0] = value;
258          grlib_irqmp_check_irqs(irqmp->state);
259          return;
260  
261      case CLEAR_OFFSET:
262          value &= ~1; /* clean up the value */
263          for (i = 0; i < irqmp->ncpus; i++) {
264              grlib_irqmp_ack_mask(state, i, value);
265          }
266          return;
267  
268      case MP_STATUS_OFFSET:
269          /*
270           * Writing and reading operations are reversed for the CPU status.
271           * Writing "1" will start the CPU, but reading "1" means that the CPU
272           * is power-down.
273           */
274          value &= MP_STATUS_CPU_STATUS_MASK;
275          for (i = 0; i < irqmp->ncpus; i++) {
276              if ((value >> i) & 1) {
277                  qemu_set_irq(irqmp->start_signal[i], 1);
278                  state->mpstatus &= ~(1 << i);
279              }
280          }
281          return;
282  
283      case BROADCAST_OFFSET:
284          value &= 0xFFFE; /* clean up the value */
285          state->broadcast = value;
286          return;
287  
288      default:
289          break;
290      }
291  
292      /* mask registers */
293      if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) {
294          int cpu = (addr - MASK_OFFSET) / 4;
295          assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
296  
297          value &= ~1; /* clean up the value */
298          state->mask[cpu] = value;
299          grlib_irqmp_check_irqs(irqmp->state);
300          return;
301      }
302  
303      /* force registers */
304      if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) {
305          int cpu = (addr - FORCE_OFFSET) / 4;
306          assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
307  
308          uint32_t force = value & 0xFFFE;
309          uint32_t clear = (value >> 16) & 0xFFFE;
310          uint32_t old   = state->force[cpu];
311  
312          state->force[cpu] = (old | force) & ~clear;
313          grlib_irqmp_check_irqs(irqmp->state);
314          return;
315      }
316  
317      /* extended (not supported) */
318      if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) {
319          int cpu = (addr - EXTENDED_OFFSET) / 4;
320          assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
321  
322          value &= 0xF; /* clean up the value */
323          state->extended[cpu] = value;
324          return;
325      }
326  
327      trace_grlib_irqmp_writel_unknown(addr, value);
328  }
329  
330  static const MemoryRegionOps grlib_irqmp_ops = {
331      .read = grlib_irqmp_read,
332      .write = grlib_irqmp_write,
333      .endianness = DEVICE_NATIVE_ENDIAN,
334      .valid = {
335          .min_access_size = 4,
336          .max_access_size = 4,
337      },
338  };
339  
340  static void grlib_irqmp_reset(DeviceState *d)
341  {
342      IRQMP *irqmp = GRLIB_IRQMP(d);
343      assert(irqmp->state != NULL);
344  
345      memset(irqmp->state, 0, sizeof *irqmp->state);
346      irqmp->state->parent = irqmp;
347      irqmp->state->mpstatus = ((irqmp->ncpus - 1) << MP_STATUS_NCPU_SHIFT) |
348          ((1 << irqmp->ncpus) - 2);
349  }
350  
351  static void grlib_irqmp_realize(DeviceState *dev, Error **errp)
352  {
353      IRQMP *irqmp = GRLIB_IRQMP(dev);
354  
355      if ((!irqmp->ncpus) || (irqmp->ncpus > IRQMP_MAX_CPU)) {
356          error_setg(errp, "Invalid ncpus properties: "
357                     "%u, must be 0 < ncpus =< %u.", irqmp->ncpus,
358                     IRQMP_MAX_CPU);
359          return;
360      }
361  
362      qdev_init_gpio_in(dev, grlib_irqmp_set_irq, MAX_PILS);
363  
364      /*
365       * Transitionning from 0 to 1 starts the CPUs. The opposite can't
366       * happen.
367       */
368      qdev_init_gpio_out_named(dev, irqmp->start_signal, "grlib-start-cpu",
369                               IRQMP_MAX_CPU);
370      qdev_init_gpio_out_named(dev, irqmp->irq, "grlib-irq", irqmp->ncpus);
371      memory_region_init_io(&irqmp->iomem, OBJECT(dev), &grlib_irqmp_ops, irqmp,
372                            "irqmp", IRQMP_REG_SIZE);
373  
374      irqmp->state = g_malloc0(sizeof *irqmp->state);
375  
376      sysbus_init_mmio(SYS_BUS_DEVICE(dev), &irqmp->iomem);
377  }
378  
379  static Property grlib_irqmp_properties[] = {
380      DEFINE_PROP_UINT32("ncpus", IRQMP, ncpus, 1),
381      DEFINE_PROP_END_OF_LIST(),
382  };
383  
384  static void grlib_irqmp_class_init(ObjectClass *klass, void *data)
385  {
386      DeviceClass *dc = DEVICE_CLASS(klass);
387  
388      dc->realize = grlib_irqmp_realize;
389      dc->reset = grlib_irqmp_reset;
390      device_class_set_props(dc, grlib_irqmp_properties);
391  }
392  
393  static const TypeInfo grlib_irqmp_info = {
394      .name          = TYPE_GRLIB_IRQMP,
395      .parent        = TYPE_SYS_BUS_DEVICE,
396      .instance_size = sizeof(IRQMP),
397      .class_init    = grlib_irqmp_class_init,
398  };
399  
400  static void grlib_irqmp_register_types(void)
401  {
402      type_register_static(&grlib_irqmp_info);
403  }
404  
405  type_init(grlib_irqmp_register_types)
406