xref: /openbmc/qemu/hw/xtensa/mx_pic.c (revision 6a0acfff)
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/irq.h"
31 #include "hw/xtensa/mx_pic.h"
32 #include "qemu/log.h"
33 
34 #define MX_MAX_CPU 32
35 #define MX_MAX_IRQ 32
36 
37 #define MIROUT 0x0
38 #define MIPICAUSE 0x100
39 #define MIPISET 0x140
40 #define MIENG 0x180
41 #define MIENGSET 0x184
42 #define MIASG 0x188
43 #define MIASGSET 0x18c
44 #define MIPIPART 0x190
45 #define SYSCFGID 0x1a0
46 #define MPSCORE 0x200
47 #define CCON 0x220
48 
49 struct XtensaMxPic {
50     unsigned n_cpu;
51     unsigned n_irq;
52 
53     uint32_t ext_irq_state;
54     uint32_t mieng;
55     uint32_t miasg;
56     uint32_t mirout[MX_MAX_IRQ];
57     uint32_t mipipart;
58     uint32_t runstall;
59 
60     qemu_irq *irq_inputs;
61     struct XtensaMxPicCpu {
62         XtensaMxPic *mx;
63         qemu_irq *irq;
64         qemu_irq runstall;
65         uint32_t mipicause;
66         uint32_t mirout_cache;
67         uint32_t irq_state_cache;
68         uint32_t ccon;
69         MemoryRegion reg;
70     } cpu[MX_MAX_CPU];
71 };
72 
73 static uint64_t xtensa_mx_pic_ext_reg_read(void *opaque, hwaddr offset,
74                                            unsigned size)
75 {
76     struct XtensaMxPicCpu *mx_cpu = opaque;
77     struct XtensaMxPic *mx = mx_cpu->mx;
78 
79     if (offset < MIROUT + MX_MAX_IRQ) {
80         return mx->mirout[offset - MIROUT];
81     } else if (offset >= MIPICAUSE && offset < MIPICAUSE + MX_MAX_CPU) {
82         return mx->cpu[offset - MIPICAUSE].mipicause;
83     } else {
84         switch (offset) {
85         case MIENG:
86             return mx->mieng;
87 
88         case MIASG:
89             return mx->miasg;
90 
91         case MIPIPART:
92             return mx->mipipart;
93 
94         case SYSCFGID:
95             return ((mx->n_cpu - 1) << 18) | (mx_cpu - mx->cpu);
96 
97         case MPSCORE:
98             return mx->runstall;
99 
100         case CCON:
101             return mx_cpu->ccon;
102 
103         default:
104             qemu_log_mask(LOG_GUEST_ERROR,
105                           "unknown RER in MX PIC range: 0x%08x\n",
106                           (uint32_t)offset);
107             return 0;
108         }
109     }
110 }
111 
112 static uint32_t xtensa_mx_pic_get_ipi_for_cpu(const XtensaMxPic *mx,
113                                               unsigned cpu)
114 {
115     uint32_t mipicause = mx->cpu[cpu].mipicause;
116     uint32_t mipipart = mx->mipipart;
117 
118     return (((mipicause & 1) << (mipipart & 3)) |
119             ((mipicause & 0x000e) != 0) << ((mipipart >> 2) & 3) |
120             ((mipicause & 0x00f0) != 0) << ((mipipart >> 4) & 3) |
121             ((mipicause & 0xff00) != 0) << ((mipipart >> 6) & 3)) & 0x7;
122 }
123 
124 static uint32_t xtensa_mx_pic_get_ext_irq_for_cpu(const XtensaMxPic *mx,
125                                                   unsigned cpu)
126 {
127     return ((((mx->ext_irq_state & mx->mieng) | mx->miasg) &
128              mx->cpu[cpu].mirout_cache) << 2) |
129         xtensa_mx_pic_get_ipi_for_cpu(mx, cpu);
130 }
131 
132 static void xtensa_mx_pic_update_cpu(XtensaMxPic *mx, unsigned cpu)
133 {
134     uint32_t irq = xtensa_mx_pic_get_ext_irq_for_cpu(mx, cpu);
135     uint32_t changed_irq = mx->cpu[cpu].irq_state_cache ^ irq;
136     unsigned i;
137 
138     qemu_log_mask(CPU_LOG_INT, "%s: CPU %d, irq: %08x, changed_irq: %08x\n",
139                   __func__, cpu, irq, changed_irq);
140     mx->cpu[cpu].irq_state_cache = irq;
141     for (i = 0; changed_irq; ++i) {
142         uint32_t mask = 1u << i;
143 
144         if (changed_irq & mask) {
145             changed_irq ^= mask;
146             qemu_set_irq(mx->cpu[cpu].irq[i], irq & mask);
147         }
148     }
149 }
150 
151 static void xtensa_mx_pic_update_all(XtensaMxPic *mx)
152 {
153     unsigned cpu;
154 
155     for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
156         xtensa_mx_pic_update_cpu(mx, cpu);
157     }
158 }
159 
160 static void xtensa_mx_pic_ext_reg_write(void *opaque, hwaddr offset,
161                                         uint64_t v, unsigned size)
162 {
163     struct XtensaMxPicCpu *mx_cpu = opaque;
164     struct XtensaMxPic *mx = mx_cpu->mx;
165     unsigned cpu;
166 
167     if (offset < MIROUT + mx->n_irq) {
168         mx->mirout[offset - MIROUT] = v;
169         for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
170             uint32_t mask = 1u << (offset - MIROUT);
171 
172             if (!(mx->cpu[cpu].mirout_cache & mask) != !(v & (1u << cpu))) {
173                 mx->cpu[cpu].mirout_cache ^= mask;
174                 xtensa_mx_pic_update_cpu(mx, cpu);
175             }
176         }
177     } else if (offset >= MIPICAUSE && offset < MIPICAUSE + mx->n_cpu) {
178         cpu = offset - MIPICAUSE;
179         mx->cpu[cpu].mipicause &= ~v;
180         xtensa_mx_pic_update_cpu(mx, cpu);
181     } else if (offset >= MIPISET && offset < MIPISET + 16) {
182         for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
183             if (v & (1u << cpu)) {
184                 mx->cpu[cpu].mipicause |= 1u << (offset - MIPISET);
185                 xtensa_mx_pic_update_cpu(mx, cpu);
186             }
187         }
188     } else {
189         uint32_t change = 0;
190         uint32_t oldv, newv;
191         const char *name = "???";
192 
193         switch (offset) {
194         case MIENG:
195             change = mx->mieng & v;
196             oldv = mx->mieng;
197             mx->mieng &= ~v;
198             newv = mx->mieng;
199             name = "MIENG";
200             break;
201 
202         case MIENGSET:
203             change = ~mx->mieng & v;
204             oldv = mx->mieng;
205             mx->mieng |= v;
206             newv = mx->mieng;
207             name = "MIENG";
208             break;
209 
210         case MIASG:
211             change = mx->miasg & v;
212             oldv = mx->miasg;
213             mx->miasg &= ~v;
214             newv = mx->miasg;
215             name = "MIASG";
216             break;
217 
218         case MIASGSET:
219             change = ~mx->miasg & v;
220             oldv = mx->miasg;
221             mx->miasg |= v;
222             newv = mx->miasg;
223             name = "MIASG";
224             break;
225 
226         case MIPIPART:
227             change = mx->mipipart ^ v;
228             oldv = mx->mipipart;
229             mx->mipipart = v;
230             newv = mx->mipipart;
231             name = "MIPIPART";
232             break;
233 
234         case MPSCORE:
235             change = mx->runstall ^ v;
236             oldv = mx->runstall;
237             mx->runstall = v;
238             newv = mx->runstall;
239             name = "RUNSTALL";
240             for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
241                 if (change & (1u << cpu)) {
242                     qemu_set_irq(mx->cpu[cpu].runstall, v & (1u << cpu));
243                 }
244             }
245             break;
246 
247         case CCON:
248             mx_cpu->ccon = v & 0x1;
249             break;
250 
251         default:
252             qemu_log_mask(LOG_GUEST_ERROR,
253                           "unknown WER in MX PIC range: 0x%08x = 0x%08x\n",
254                           (uint32_t)offset, (uint32_t)v);
255             break;
256         }
257         if (change) {
258             qemu_log_mask(CPU_LOG_INT,
259                           "%s: %s changed by CPU %d: %08x -> %08x\n",
260                           __func__, name, (int)(mx_cpu - mx->cpu),
261                           oldv, newv);
262             xtensa_mx_pic_update_all(mx);
263         }
264     }
265 }
266 
267 static const MemoryRegionOps xtensa_mx_pic_ops = {
268     .read = xtensa_mx_pic_ext_reg_read,
269     .write = xtensa_mx_pic_ext_reg_write,
270     .endianness = DEVICE_NATIVE_ENDIAN,
271     .valid = {
272         .unaligned = true,
273     },
274 };
275 
276 MemoryRegion *xtensa_mx_pic_register_cpu(XtensaMxPic *mx,
277                                          qemu_irq *irq,
278                                          qemu_irq runstall)
279 {
280     struct XtensaMxPicCpu *mx_cpu = mx->cpu + mx->n_cpu;
281 
282     mx_cpu->mx = mx;
283     mx_cpu->irq = irq;
284     mx_cpu->runstall = runstall;
285 
286     memory_region_init_io(&mx_cpu->reg, NULL, &xtensa_mx_pic_ops, mx_cpu,
287                           "mx_pic", 0x280);
288 
289     ++mx->n_cpu;
290     return &mx_cpu->reg;
291 }
292 
293 static void xtensa_mx_pic_set_irq(void *opaque, int irq, int active)
294 {
295     XtensaMxPic *mx = opaque;
296 
297     if (irq < mx->n_irq) {
298         uint32_t old_irq_state = mx->ext_irq_state;
299 
300         if (active) {
301             mx->ext_irq_state |= 1u << irq;
302         } else {
303             mx->ext_irq_state &= ~(1u << irq);
304         }
305         if (old_irq_state != mx->ext_irq_state) {
306             qemu_log_mask(CPU_LOG_INT,
307                           "%s: IRQ %d, active: %d, ext_irq_state: %08x -> %08x\n",
308                           __func__, irq, active,
309                           old_irq_state, mx->ext_irq_state);
310             xtensa_mx_pic_update_all(mx);
311         }
312     } else {
313         qemu_log_mask(LOG_GUEST_ERROR, "%s: IRQ %d out of range\n",
314                       __func__, irq);
315     }
316 }
317 
318 XtensaMxPic *xtensa_mx_pic_init(unsigned n_irq)
319 {
320     XtensaMxPic *mx = calloc(1, sizeof(XtensaMxPic));
321 
322     mx->n_irq = n_irq + 1;
323     mx->irq_inputs = qemu_allocate_irqs(xtensa_mx_pic_set_irq, mx,
324                                         mx->n_irq);
325     return mx;
326 }
327 
328 void xtensa_mx_pic_reset(void *opaque)
329 {
330     XtensaMxPic *mx = opaque;
331     unsigned i;
332 
333     mx->ext_irq_state = 0;
334     mx->mieng = mx->n_irq < 32 ? (1u << mx->n_irq) - 1 : ~0u;
335     mx->miasg = 0;
336     mx->mipipart = 0;
337     for (i = 0; i < mx->n_irq; ++i) {
338         mx->mirout[i] = 1;
339     }
340     for (i = 0; i < mx->n_cpu; ++i) {
341         mx->cpu[i].mipicause = 0;
342         mx->cpu[i].mirout_cache = i ? 0 : mx->mieng;
343         mx->cpu[i].irq_state_cache = 0;
344         mx->cpu[i].ccon = 0;
345     }
346     mx->runstall = (1u << mx->n_cpu) - 2;
347     for (i = 0; i < mx->n_cpu; ++i) {
348         qemu_set_irq(mx->cpu[i].runstall, i > 0);
349     }
350 }
351 
352 qemu_irq *xtensa_mx_pic_get_extints(XtensaMxPic *mx)
353 {
354     return mx->irq_inputs + 1;
355 }
356