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
xtensa_mx_pic_ext_reg_read(void * opaque,hwaddr offset,unsigned size)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
xtensa_mx_pic_get_ipi_for_cpu(const XtensaMxPic * mx,unsigned cpu)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
xtensa_mx_pic_get_ext_irq_for_cpu(const XtensaMxPic * mx,unsigned cpu)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
xtensa_mx_pic_update_cpu(XtensaMxPic * mx,unsigned cpu)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
xtensa_mx_pic_update_all(XtensaMxPic * mx)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
xtensa_mx_pic_ext_reg_write(void * opaque,hwaddr offset,uint64_t v,unsigned size)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
xtensa_mx_pic_register_cpu(XtensaMxPic * mx,qemu_irq * irq,qemu_irq runstall)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
xtensa_mx_pic_set_irq(void * opaque,int irq,int active)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
xtensa_mx_pic_init(unsigned n_irq)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
xtensa_mx_pic_reset(void * opaque)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] = 0;
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
xtensa_mx_pic_get_extints(XtensaMxPic * mx)351 qemu_irq *xtensa_mx_pic_get_extints(XtensaMxPic *mx)
352 {
353 return mx->irq_inputs + 1;
354 }
355