master
c 381 lines 11.6 KB
Raw
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/core/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 /*
73 * Note that decode for these registers is rather strange by the usual
74 * MMIO standards -- the MIROUT and MIPICAUSE areas can be read and
75 * written at 32-bit length, returning different values for each byte
76 * offset, because the low bits of the address are treated as selecting
77 * an IRQ or a processor:
78 *
79 * 00nn 0...0p..p Interrupt Routing, route IRQ n to processor p
80 * 01pp 0...0d..d 16 bits (d) 'ored' as single IPI to processor p
81 *
82 * This is because (like x86 IO port in/out accesses) the offset is
83 * not a memory-mapped address but is really a register number,
84 * accessed via the Xtensa RER/WER "external register" instructions.
85 *
86 * We set .valid and .impl to both allow unaligned = true to permit
87 * these byte-offsets. Because this device is not a true memory mapped
88 * device but is accessible only via the Xtensa RER/WER "external
89 * register" interface, all accesses are guaranteed 32 bits.
90 */
91
92 static uint64_t xtensa_mx_pic_ext_reg_read(void *opaque, hwaddr offset,
93 unsigned size)
94 {
95 struct XtensaMxPicCpu *mx_cpu = opaque;
96 struct XtensaMxPic *mx = mx_cpu->mx;
97
98 if (offset < MIROUT + MX_MAX_IRQ) {
99 return mx->mirout[offset - MIROUT];
100 } else if (offset >= MIPICAUSE && offset < MIPICAUSE + MX_MAX_CPU) {
101 return mx->cpu[offset - MIPICAUSE].mipicause;
102 } else {
103 switch (offset) {
104 case MIENG:
105 return mx->mieng;
106
107 case MIASG:
108 return mx->miasg;
109
110 case MIPIPART:
111 return mx->mipipart;
112
113 case SYSCFGID:
114 return ((mx->n_cpu - 1) << 18) | (mx_cpu - mx->cpu);
115
116 case MPSCORE:
117 return mx->runstall;
118
119 case CCON:
120 return mx_cpu->ccon;
121
122 default:
123 qemu_log_mask(LOG_GUEST_ERROR,
124 "unknown RER in MX PIC range: 0x%08x\n",
125 (uint32_t)offset);
126 return 0;
127 }
128 }
129 }
130
131 static uint32_t xtensa_mx_pic_get_ipi_for_cpu(const XtensaMxPic *mx,
132 unsigned cpu)
133 {
134 uint32_t mipicause = mx->cpu[cpu].mipicause;
135 uint32_t mipipart = mx->mipipart;
136
137 return (((mipicause & 1) << (mipipart & 3)) |
138 ((mipicause & 0x000e) != 0) << ((mipipart >> 2) & 3) |
139 ((mipicause & 0x00f0) != 0) << ((mipipart >> 4) & 3) |
140 ((mipicause & 0xff00) != 0) << ((mipipart >> 6) & 3)) & 0x7;
141 }
142
143 static uint32_t xtensa_mx_pic_get_ext_irq_for_cpu(const XtensaMxPic *mx,
144 unsigned cpu)
145 {
146 return ((((mx->ext_irq_state & mx->mieng) | mx->miasg) &
147 mx->cpu[cpu].mirout_cache) << 2) |
148 xtensa_mx_pic_get_ipi_for_cpu(mx, cpu);
149 }
150
151 static void xtensa_mx_pic_update_cpu(XtensaMxPic *mx, unsigned cpu)
152 {
153 uint32_t irq = xtensa_mx_pic_get_ext_irq_for_cpu(mx, cpu);
154 uint32_t changed_irq = mx->cpu[cpu].irq_state_cache ^ irq;
155 unsigned i;
156
157 qemu_log_mask(CPU_LOG_INT, "%s: CPU %d, irq: %08x, changed_irq: %08x\n",
158 __func__, cpu, irq, changed_irq);
159 mx->cpu[cpu].irq_state_cache = irq;
160 for (i = 0; changed_irq; ++i) {
161 uint32_t mask = 1u << i;
162
163 if (changed_irq & mask) {
164 changed_irq ^= mask;
165 qemu_set_irq(mx->cpu[cpu].irq[i], irq & mask);
166 }
167 }
168 }
169
170 static void xtensa_mx_pic_update_all(XtensaMxPic *mx)
171 {
172 unsigned cpu;
173
174 for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
175 xtensa_mx_pic_update_cpu(mx, cpu);
176 }
177 }
178
179 static void xtensa_mx_pic_ext_reg_write(void *opaque, hwaddr offset,
180 uint64_t v, unsigned size)
181 {
182 struct XtensaMxPicCpu *mx_cpu = opaque;
183 struct XtensaMxPic *mx = mx_cpu->mx;
184 unsigned cpu;
185
186 if (offset < MIROUT + mx->n_irq) {
187 mx->mirout[offset - MIROUT] = v;
188 for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
189 uint32_t mask = 1u << (offset - MIROUT);
190
191 if (!(mx->cpu[cpu].mirout_cache & mask) != !(v & (1u << cpu))) {
192 mx->cpu[cpu].mirout_cache ^= mask;
193 xtensa_mx_pic_update_cpu(mx, cpu);
194 }
195 }
196 } else if (offset >= MIPICAUSE && offset < MIPICAUSE + mx->n_cpu) {
197 cpu = offset - MIPICAUSE;
198 mx->cpu[cpu].mipicause &= ~v;
199 xtensa_mx_pic_update_cpu(mx, cpu);
200 } else if (offset >= MIPISET && offset < MIPISET + 16) {
201 for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
202 if (v & (1u << cpu)) {
203 mx->cpu[cpu].mipicause |= 1u << (offset - MIPISET);
204 xtensa_mx_pic_update_cpu(mx, cpu);
205 }
206 }
207 } else {
208 uint32_t change = 0;
209 uint32_t oldv, newv;
210 const char *name = "???";
211
212 switch (offset) {
213 case MIENG:
214 change = mx->mieng & v;
215 oldv = mx->mieng;
216 mx->mieng &= ~v;
217 newv = mx->mieng;
218 name = "MIENG";
219 break;
220
221 case MIENGSET:
222 change = ~mx->mieng & v;
223 oldv = mx->mieng;
224 mx->mieng |= v;
225 newv = mx->mieng;
226 name = "MIENG";
227 break;
228
229 case MIASG:
230 change = mx->miasg & v;
231 oldv = mx->miasg;
232 mx->miasg &= ~v;
233 newv = mx->miasg;
234 name = "MIASG";
235 break;
236
237 case MIASGSET:
238 change = ~mx->miasg & v;
239 oldv = mx->miasg;
240 mx->miasg |= v;
241 newv = mx->miasg;
242 name = "MIASG";
243 break;
244
245 case MIPIPART:
246 change = mx->mipipart ^ v;
247 oldv = mx->mipipart;
248 mx->mipipart = v;
249 newv = mx->mipipart;
250 name = "MIPIPART";
251 break;
252
253 case MPSCORE:
254 change = mx->runstall ^ v;
255 oldv = mx->runstall;
256 mx->runstall = v;
257 newv = mx->runstall;
258 name = "RUNSTALL";
259 for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
260 if (change & (1u << cpu)) {
261 qemu_set_irq(mx->cpu[cpu].runstall, v & (1u << cpu));
262 }
263 }
264 break;
265
266 case CCON:
267 mx_cpu->ccon = v & 0x1;
268 break;
269
270 default:
271 qemu_log_mask(LOG_GUEST_ERROR,
272 "unknown WER in MX PIC range: 0x%08x = 0x%08x\n",
273 (uint32_t)offset, (uint32_t)v);
274 break;
275 }
276 if (change) {
277 qemu_log_mask(CPU_LOG_INT,
278 "%s: %s changed by CPU %d: %08x -> %08x\n",
279 __func__, name, (int)(mx_cpu - mx->cpu),
280 oldv, newv);
281 xtensa_mx_pic_update_all(mx);
282 }
283 }
284 }
285
286 static const MemoryRegionOps xtensa_mx_pic_ops = {
287 .read = xtensa_mx_pic_ext_reg_read,
288 .write = xtensa_mx_pic_ext_reg_write,
289 .endianness = DEVICE_NATIVE_ENDIAN,
290 .impl = {
291 .min_access_size = 4,
292 .max_access_size = 4,
293 .unaligned = true,
294 },
295 .valid = {
296 .min_access_size = 4,
297 .max_access_size = 4,
298 .unaligned = true,
299 },
300 };
301
302 MemoryRegion *xtensa_mx_pic_register_cpu(XtensaMxPic *mx,
303 qemu_irq *irq,
304 qemu_irq runstall)
305 {
306 struct XtensaMxPicCpu *mx_cpu = mx->cpu + mx->n_cpu;
307
308 mx_cpu->mx = mx;
309 mx_cpu->irq = irq;
310 mx_cpu->runstall = runstall;
311
312 memory_region_init_io(&mx_cpu->reg, NULL, &xtensa_mx_pic_ops, mx_cpu,
313 "mx_pic", 0x280);
314
315 ++mx->n_cpu;
316 return &mx_cpu->reg;
317 }
318
319 static void xtensa_mx_pic_set_irq(void *opaque, int irq, int active)
320 {
321 XtensaMxPic *mx = opaque;
322
323 if (irq < mx->n_irq) {
324 uint32_t old_irq_state = mx->ext_irq_state;
325
326 if (active) {
327 mx->ext_irq_state |= 1u << irq;
328 } else {
329 mx->ext_irq_state &= ~(1u << irq);
330 }
331 if (old_irq_state != mx->ext_irq_state) {
332 qemu_log_mask(CPU_LOG_INT,
333 "%s: IRQ %d, active: %d, ext_irq_state: %08x -> %08x\n",
334 __func__, irq, active,
335 old_irq_state, mx->ext_irq_state);
336 xtensa_mx_pic_update_all(mx);
337 }
338 } else {
339 qemu_log_mask(LOG_GUEST_ERROR, "%s: IRQ %d out of range\n",
340 __func__, irq);
341 }
342 }
343
344 XtensaMxPic *xtensa_mx_pic_init(unsigned n_irq)
345 {
346 XtensaMxPic *mx = g_new0(XtensaMxPic, 1);
347
348 mx->n_irq = n_irq + 1;
349 mx->irq_inputs = qemu_allocate_irqs(xtensa_mx_pic_set_irq, mx,
350 mx->n_irq);
351 return mx;
352 }
353
354 void xtensa_mx_pic_reset(void *opaque)
355 {
356 XtensaMxPic *mx = opaque;
357 unsigned i;
358
359 mx->ext_irq_state = 0;
360 mx->mieng = mx->n_irq < 32 ? (1u << mx->n_irq) - 1 : ~0u;
361 mx->miasg = 0;
362 mx->mipipart = 0;
363 for (i = 0; i < mx->n_irq; ++i) {
364 mx->mirout[i] = 0;
365 }
366 for (i = 0; i < mx->n_cpu; ++i) {
367 mx->cpu[i].mipicause = 0;
368 mx->cpu[i].mirout_cache = i ? 0 : mx->mieng;
369 mx->cpu[i].irq_state_cache = 0;
370 mx->cpu[i].ccon = 0;
371 }
372 mx->runstall = (1u << mx->n_cpu) - 2;
373 for (i = 0; i < mx->n_cpu; ++i) {
374 qemu_set_irq(mx->cpu[i].runstall, i > 0);
375 }
376 }
377
378 qemu_irq *xtensa_mx_pic_get_extints(XtensaMxPic *mx)
379 {
380 return mx->irq_inputs + 1;
381 }