2 * Copyright (c) 2013 - 2019, Max Filippov, Open Source and Linux Lab.
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.
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.
28 #include "qemu/osdep.h"
30 #include "hw/xtensa/mx_pic.h"
37 #define MIPICAUSE 0x100
40 #define MIENGSET 0x184
42 #define MIASGSET 0x18c
43 #define MIPIPART 0x190
44 #define SYSCFGID 0x1a0
52 uint32_t ext_irq_state;
55 uint32_t mirout[MX_MAX_IRQ];
60 struct XtensaMxPicCpu {
65 uint32_t mirout_cache;
66 uint32_t irq_state_cache;
72 static uint64_t xtensa_mx_pic_ext_reg_read(void *opaque, hwaddr offset,
75 struct XtensaMxPicCpu *mx_cpu = opaque;
76 struct XtensaMxPic *mx = mx_cpu->mx;
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;
94 return ((mx->n_cpu - 1) << 18) | (mx_cpu - mx->cpu);
103 qemu_log_mask(LOG_GUEST_ERROR,
104 "unknown RER in MX PIC range: 0x%08x\n",
111 static uint32_t xtensa_mx_pic_get_ipi_for_cpu(const XtensaMxPic *mx,
114 uint32_t mipicause = mx->cpu[cpu].mipicause;
115 uint32_t mipipart = mx->mipipart;
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;
123 static uint32_t xtensa_mx_pic_get_ext_irq_for_cpu(const XtensaMxPic *mx,
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);
131 static void xtensa_mx_pic_update_cpu(XtensaMxPic *mx, unsigned cpu)
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;
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;
143 if (changed_irq & mask) {
145 qemu_set_irq(mx->cpu[cpu].irq[i], irq & mask);
150 static void xtensa_mx_pic_update_all(XtensaMxPic *mx)
154 for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
155 xtensa_mx_pic_update_cpu(mx, cpu);
159 static void xtensa_mx_pic_ext_reg_write(void *opaque, hwaddr offset,
160 uint64_t v, unsigned size)
162 struct XtensaMxPicCpu *mx_cpu = opaque;
163 struct XtensaMxPic *mx = mx_cpu->mx;
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);
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);
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);
190 const char *name = "???";
194 change = mx->mieng & v;
202 change = ~mx->mieng & v;
210 change = mx->miasg & v;
218 change = ~mx->miasg & v;
226 change = mx->mipipart ^ v;
234 change = mx->runstall ^ v;
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));
247 mx_cpu->ccon = v & 0x1;
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);
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),
261 xtensa_mx_pic_update_all(mx);
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,
275 MemoryRegion *xtensa_mx_pic_register_cpu(XtensaMxPic *mx,
279 struct XtensaMxPicCpu *mx_cpu = mx->cpu + mx->n_cpu;
283 mx_cpu->runstall = runstall;
285 memory_region_init_io(&mx_cpu->reg, NULL, &xtensa_mx_pic_ops, mx_cpu,
292 static void xtensa_mx_pic_set_irq(void *opaque, int irq, int active)
294 XtensaMxPic *mx = opaque;
296 if (irq < mx->n_irq) {
297 uint32_t old_irq_state = mx->ext_irq_state;
300 mx->ext_irq_state |= 1u << irq;
302 mx->ext_irq_state &= ~(1u << irq);
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);
312 qemu_log_mask(LOG_GUEST_ERROR, "%s: IRQ %d out of range\n",
317 XtensaMxPic *xtensa_mx_pic_init(unsigned n_irq)
319 XtensaMxPic *mx = calloc(1, sizeof(XtensaMxPic));
321 mx->n_irq = n_irq + 1;
322 mx->irq_inputs = qemu_allocate_irqs(xtensa_mx_pic_set_irq, mx,
327 void xtensa_mx_pic_reset(void *opaque)
329 XtensaMxPic *mx = opaque;
332 mx->ext_irq_state = 0;
333 mx->mieng = mx->n_irq < 32 ? (1u << mx->n_irq) - 1 : ~0u;
336 for (i = 0; i < mx->n_irq; ++i) {
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;
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);
351 qemu_irq *xtensa_mx_pic_get_extints(XtensaMxPic *mx)
353 return mx->irq_inputs + 1;