xref: /qemu/hw/xtensa/mx_pic.c (revision 05a248715cef192336a594afed812871a52efc1f)
1  /*
2   * Copyright (c) 2013 - 2019, Max Filippov, Open Source and Linux Lab.
3   * All rights reserved.
4   *
5   * Redistribution and use in source and binary forms, with or without
6   * modification, are permitted provided that the following conditions are met:
7   *     * Redistributions of source code must retain the above copyright
8   *       notice, this list of conditions and the following disclaimer.
9   *     * Redistributions in binary form must reproduce the above copyright
10   *       notice, this list of conditions and the following disclaimer in the
11   *       documentation and/or other materials provided with the distribution.
12   *     * Neither the name of the Open Source and Linux Lab nor the
13   *       names of its contributors may be used to endorse or promote products
14   *       derived from this software without specific prior written permission.
15   *
16   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19   * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20   * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26   */
27  
28  #include "qemu/osdep.h"
29  #include "hw/irq.h"
30  #include "hw/xtensa/mx_pic.h"
31  #include "qemu/log.h"
32  
33  #define MX_MAX_CPU 32
34  #define MX_MAX_IRQ 32
35  
36  #define MIROUT 0x0
37  #define MIPICAUSE 0x100
38  #define MIPISET 0x140
39  #define MIENG 0x180
40  #define MIENGSET 0x184
41  #define MIASG 0x188
42  #define MIASGSET 0x18c
43  #define MIPIPART 0x190
44  #define SYSCFGID 0x1a0
45  #define MPSCORE 0x200
46  #define CCON 0x220
47  
48  struct XtensaMxPic {
49      unsigned n_cpu;
50      unsigned n_irq;
51  
52      uint32_t ext_irq_state;
53      uint32_t mieng;
54      uint32_t miasg;
55      uint32_t mirout[MX_MAX_IRQ];
56      uint32_t mipipart;
57      uint32_t runstall;
58  
59      qemu_irq *irq_inputs;
60      struct XtensaMxPicCpu {
61          XtensaMxPic *mx;
62          qemu_irq *irq;
63          qemu_irq runstall;
64          uint32_t mipicause;
65          uint32_t mirout_cache;
66          uint32_t irq_state_cache;
67          uint32_t ccon;
68          MemoryRegion reg;
69      } cpu[MX_MAX_CPU];
70  };
71  
72  static uint64_t xtensa_mx_pic_ext_reg_read(void *opaque, hwaddr offset,
73                                             unsigned size)
74  {
75      struct XtensaMxPicCpu *mx_cpu = opaque;
76      struct XtensaMxPic *mx = mx_cpu->mx;
77  
78      if (offset < MIROUT + MX_MAX_IRQ) {
79          return mx->mirout[offset - MIROUT];
80      } else if (offset >= MIPICAUSE && offset < MIPICAUSE + MX_MAX_CPU) {
81          return mx->cpu[offset - MIPICAUSE].mipicause;
82      } else {
83          switch (offset) {
84          case MIENG:
85              return mx->mieng;
86  
87          case MIASG:
88              return mx->miasg;
89  
90          case MIPIPART:
91              return mx->mipipart;
92  
93          case SYSCFGID:
94              return ((mx->n_cpu - 1) << 18) | (mx_cpu - mx->cpu);
95  
96          case MPSCORE:
97              return mx->runstall;
98  
99          case CCON:
100              return mx_cpu->ccon;
101  
102          default:
103              qemu_log_mask(LOG_GUEST_ERROR,
104                            "unknown RER in MX PIC range: 0x%08x\n",
105                            (uint32_t)offset);
106              return 0;
107          }
108      }
109  }
110  
111  static uint32_t xtensa_mx_pic_get_ipi_for_cpu(const XtensaMxPic *mx,
112                                                unsigned cpu)
113  {
114      uint32_t mipicause = mx->cpu[cpu].mipicause;
115      uint32_t mipipart = mx->mipipart;
116  
117      return (((mipicause & 1) << (mipipart & 3)) |
118              ((mipicause & 0x000e) != 0) << ((mipipart >> 2) & 3) |
119              ((mipicause & 0x00f0) != 0) << ((mipipart >> 4) & 3) |
120              ((mipicause & 0xff00) != 0) << ((mipipart >> 6) & 3)) & 0x7;
121  }
122  
123  static uint32_t xtensa_mx_pic_get_ext_irq_for_cpu(const XtensaMxPic *mx,
124                                                    unsigned cpu)
125  {
126      return ((((mx->ext_irq_state & mx->mieng) | mx->miasg) &
127               mx->cpu[cpu].mirout_cache) << 2) |
128          xtensa_mx_pic_get_ipi_for_cpu(mx, cpu);
129  }
130  
131  static void xtensa_mx_pic_update_cpu(XtensaMxPic *mx, unsigned cpu)
132  {
133      uint32_t irq = xtensa_mx_pic_get_ext_irq_for_cpu(mx, cpu);
134      uint32_t changed_irq = mx->cpu[cpu].irq_state_cache ^ irq;
135      unsigned i;
136  
137      qemu_log_mask(CPU_LOG_INT, "%s: CPU %d, irq: %08x, changed_irq: %08x\n",
138                    __func__, cpu, irq, changed_irq);
139      mx->cpu[cpu].irq_state_cache = irq;
140      for (i = 0; changed_irq; ++i) {
141          uint32_t mask = 1u << i;
142  
143          if (changed_irq & mask) {
144              changed_irq ^= mask;
145              qemu_set_irq(mx->cpu[cpu].irq[i], irq & mask);
146          }
147      }
148  }
149  
150  static void xtensa_mx_pic_update_all(XtensaMxPic *mx)
151  {
152      unsigned cpu;
153  
154      for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
155          xtensa_mx_pic_update_cpu(mx, cpu);
156      }
157  }
158  
159  static void xtensa_mx_pic_ext_reg_write(void *opaque, hwaddr offset,
160                                          uint64_t v, unsigned size)
161  {
162      struct XtensaMxPicCpu *mx_cpu = opaque;
163      struct XtensaMxPic *mx = mx_cpu->mx;
164      unsigned cpu;
165  
166      if (offset < MIROUT + mx->n_irq) {
167          mx->mirout[offset - MIROUT] = v;
168          for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
169              uint32_t mask = 1u << (offset - MIROUT);
170  
171              if (!(mx->cpu[cpu].mirout_cache & mask) != !(v & (1u << cpu))) {
172                  mx->cpu[cpu].mirout_cache ^= mask;
173                  xtensa_mx_pic_update_cpu(mx, cpu);
174              }
175          }
176      } else if (offset >= MIPICAUSE && offset < MIPICAUSE + mx->n_cpu) {
177          cpu = offset - MIPICAUSE;
178          mx->cpu[cpu].mipicause &= ~v;
179          xtensa_mx_pic_update_cpu(mx, cpu);
180      } else if (offset >= MIPISET && offset < MIPISET + 16) {
181          for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
182              if (v & (1u << cpu)) {
183                  mx->cpu[cpu].mipicause |= 1u << (offset - MIPISET);
184                  xtensa_mx_pic_update_cpu(mx, cpu);
185              }
186          }
187      } else {
188          uint32_t change = 0;
189          uint32_t oldv, newv;
190          const char *name = "???";
191  
192          switch (offset) {
193          case MIENG:
194              change = mx->mieng & v;
195              oldv = mx->mieng;
196              mx->mieng &= ~v;
197              newv = mx->mieng;
198              name = "MIENG";
199              break;
200  
201          case MIENGSET:
202              change = ~mx->mieng & v;
203              oldv = mx->mieng;
204              mx->mieng |= v;
205              newv = mx->mieng;
206              name = "MIENG";
207              break;
208  
209          case MIASG:
210              change = mx->miasg & v;
211              oldv = mx->miasg;
212              mx->miasg &= ~v;
213              newv = mx->miasg;
214              name = "MIASG";
215              break;
216  
217          case MIASGSET:
218              change = ~mx->miasg & v;
219              oldv = mx->miasg;
220              mx->miasg |= v;
221              newv = mx->miasg;
222              name = "MIASG";
223              break;
224  
225          case MIPIPART:
226              change = mx->mipipart ^ v;
227              oldv = mx->mipipart;
228              mx->mipipart = v;
229              newv = mx->mipipart;
230              name = "MIPIPART";
231              break;
232  
233          case MPSCORE:
234              change = mx->runstall ^ v;
235              oldv = mx->runstall;
236              mx->runstall = v;
237              newv = mx->runstall;
238              name = "RUNSTALL";
239              for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
240                  if (change & (1u << cpu)) {
241                      qemu_set_irq(mx->cpu[cpu].runstall, v & (1u << cpu));
242                  }
243              }
244              break;
245  
246          case CCON:
247              mx_cpu->ccon = v & 0x1;
248              break;
249  
250          default:
251              qemu_log_mask(LOG_GUEST_ERROR,
252                            "unknown WER in MX PIC range: 0x%08x = 0x%08x\n",
253                            (uint32_t)offset, (uint32_t)v);
254              break;
255          }
256          if (change) {
257              qemu_log_mask(CPU_LOG_INT,
258                            "%s: %s changed by CPU %d: %08x -> %08x\n",
259                            __func__, name, (int)(mx_cpu - mx->cpu),
260                            oldv, newv);
261              xtensa_mx_pic_update_all(mx);
262          }
263      }
264  }
265  
266  static const MemoryRegionOps xtensa_mx_pic_ops = {
267      .read = xtensa_mx_pic_ext_reg_read,
268      .write = xtensa_mx_pic_ext_reg_write,
269      .endianness = DEVICE_NATIVE_ENDIAN,
270      .valid = {
271          .unaligned = true,
272      },
273  };
274  
275  MemoryRegion *xtensa_mx_pic_register_cpu(XtensaMxPic *mx,
276                                           qemu_irq *irq,
277                                           qemu_irq runstall)
278  {
279      struct XtensaMxPicCpu *mx_cpu = mx->cpu + mx->n_cpu;
280  
281      mx_cpu->mx = mx;
282      mx_cpu->irq = irq;
283      mx_cpu->runstall = runstall;
284  
285      memory_region_init_io(&mx_cpu->reg, NULL, &xtensa_mx_pic_ops, mx_cpu,
286                            "mx_pic", 0x280);
287  
288      ++mx->n_cpu;
289      return &mx_cpu->reg;
290  }
291  
292  static void xtensa_mx_pic_set_irq(void *opaque, int irq, int active)
293  {
294      XtensaMxPic *mx = opaque;
295  
296      if (irq < mx->n_irq) {
297          uint32_t old_irq_state = mx->ext_irq_state;
298  
299          if (active) {
300              mx->ext_irq_state |= 1u << irq;
301          } else {
302              mx->ext_irq_state &= ~(1u << irq);
303          }
304          if (old_irq_state != mx->ext_irq_state) {
305              qemu_log_mask(CPU_LOG_INT,
306                            "%s: IRQ %d, active: %d, ext_irq_state: %08x -> %08x\n",
307                            __func__, irq, active,
308                            old_irq_state, mx->ext_irq_state);
309              xtensa_mx_pic_update_all(mx);
310          }
311      } else {
312          qemu_log_mask(LOG_GUEST_ERROR, "%s: IRQ %d out of range\n",
313                        __func__, irq);
314      }
315  }
316  
317  XtensaMxPic *xtensa_mx_pic_init(unsigned n_irq)
318  {
319      XtensaMxPic *mx = calloc(1, sizeof(XtensaMxPic));
320  
321      mx->n_irq = n_irq + 1;
322      mx->irq_inputs = qemu_allocate_irqs(xtensa_mx_pic_set_irq, mx,
323                                          mx->n_irq);
324      return mx;
325  }
326  
327  void xtensa_mx_pic_reset(void *opaque)
328  {
329      XtensaMxPic *mx = opaque;
330      unsigned i;
331  
332      mx->ext_irq_state = 0;
333      mx->mieng = mx->n_irq < 32 ? (1u << mx->n_irq) - 1 : ~0u;
334      mx->miasg = 0;
335      mx->mipipart = 0;
336      for (i = 0; i < mx->n_irq; ++i) {
337          mx->mirout[i] = 1;
338      }
339      for (i = 0; i < mx->n_cpu; ++i) {
340          mx->cpu[i].mipicause = 0;
341          mx->cpu[i].mirout_cache = i ? 0 : mx->mieng;
342          mx->cpu[i].irq_state_cache = 0;
343          mx->cpu[i].ccon = 0;
344      }
345      mx->runstall = (1u << mx->n_cpu) - 2;
346      for (i = 0; i < mx->n_cpu; ++i) {
347          qemu_set_irq(mx->cpu[i].runstall, i > 0);
348      }
349  }
350  
351  qemu_irq *xtensa_mx_pic_get_extints(XtensaMxPic *mx)
352  {
353      return mx->irq_inputs + 1;
354  }
355