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;