xref: /openbmc/qemu/hw/xtensa/mx_pic.c (revision 709395f8)
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/hw.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