1 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 * LoongArch ipi interrupt support
5 * Copyright (C) 2021 Loongson Technology Corporation Limited
8 #include "qemu/osdep.h"
10 #include "hw/intc/loongarch_ipi.h"
12 #include "qapi/error.h"
14 #include "exec/address-spaces.h"
15 #include "hw/loongarch/virt.h"
16 #include "migration/vmstate.h"
17 #include "target/loongarch/internals.h"
20 static void loongarch_ipi_writel(void *, hwaddr
, uint64_t, unsigned);
22 static uint64_t loongarch_ipi_readl(void *opaque
, hwaddr addr
, unsigned size
)
42 case CORE_BUF_20
... CORE_BUF_38
+ 4:
43 index
= (addr
- CORE_BUF_20
) >> 2;
47 qemu_log_mask(LOG_UNIMP
, "invalid read: %x", (uint32_t)addr
);
51 trace_loongarch_ipi_read(size
, (uint64_t)addr
, ret
);
55 static void send_ipi_data(CPULoongArchState
*env
, uint64_t val
, hwaddr addr
)
57 int i
, mask
= 0, data
= 0;
60 * bit 27-30 is mask for byte writing,
61 * if the mask is 0, we need not to do anything.
63 if ((val
>> 27) & 0xf) {
64 data
= address_space_ldl(&env
->address_space_iocsr
, addr
,
65 MEMTXATTRS_UNSPECIFIED
, NULL
);
66 for (i
= 0; i
< 4; i
++) {
67 /* get mask for byte writing */
68 if (val
& (0x1 << (27 + i
))) {
69 mask
|= 0xff << (i
* 8);
75 data
|= (val
>> 32) & ~mask
;
76 address_space_stl(&env
->address_space_iocsr
, addr
,
77 data
, MEMTXATTRS_UNSPECIFIED
, NULL
);
80 static int archid_cmp(const void *a
, const void *b
)
82 CPUArchId
*archid_a
= (CPUArchId
*)a
;
83 CPUArchId
*archid_b
= (CPUArchId
*)b
;
85 return archid_a
->arch_id
- archid_b
->arch_id
;
88 static CPUArchId
*find_cpu_by_archid(MachineState
*ms
, uint32_t id
)
90 CPUArchId apic_id
, *found_cpu
;
93 found_cpu
= bsearch(&apic_id
, ms
->possible_cpus
->cpus
,
94 ms
->possible_cpus
->len
, sizeof(*ms
->possible_cpus
->cpus
),
100 static CPUState
*ipi_getcpu(int arch_id
)
102 MachineState
*machine
= MACHINE(qdev_get_machine());
105 archid
= find_cpu_by_archid(machine
, arch_id
);
106 return CPU(archid
->cpu
);
109 static void ipi_send(uint64_t val
)
117 cpuid
= extract32(val
, 16, 10);
118 if (cpuid
>= LOONGARCH_MAX_CPUS
) {
119 trace_loongarch_ipi_unsupported_cpuid("IOCSR_IPI_SEND", cpuid
);
123 /* IPI status vector */
124 vector
= extract8(val
, 0, 5);
126 cs
= ipi_getcpu(cpuid
);
127 cpu
= LOONGARCH_CPU(cs
);
128 s
= LOONGARCH_IPI(cpu
->env
.ipistate
);
129 loongarch_ipi_writel(&s
->ipi_core
, CORE_SET_OFF
, BIT(vector
), 4);
132 static void mail_send(uint64_t val
)
136 CPULoongArchState
*env
;
140 cpuid
= extract32(val
, 16, 10);
141 if (cpuid
>= LOONGARCH_MAX_CPUS
) {
142 trace_loongarch_ipi_unsupported_cpuid("IOCSR_MAIL_SEND", cpuid
);
146 addr
= 0x1020 + (val
& 0x1c);
147 cs
= ipi_getcpu(cpuid
);
148 cpu
= LOONGARCH_CPU(cs
);
150 send_ipi_data(env
, val
, addr
);
153 static void any_send(uint64_t val
)
157 CPULoongArchState
*env
;
161 cpuid
= extract32(val
, 16, 10);
162 if (cpuid
>= LOONGARCH_MAX_CPUS
) {
163 trace_loongarch_ipi_unsupported_cpuid("IOCSR_ANY_SEND", cpuid
);
168 cs
= ipi_getcpu(cpuid
);
169 cpu
= LOONGARCH_CPU(cs
);
171 send_ipi_data(env
, val
, addr
);
174 static void loongarch_ipi_writel(void *opaque
, hwaddr addr
, uint64_t val
,
181 trace_loongarch_ipi_write(size
, (uint64_t)addr
, val
);
183 case CORE_STATUS_OFF
:
184 qemu_log_mask(LOG_GUEST_ERROR
, "can not be written");
191 if (s
->status
!= 0 && (s
->status
& s
->en
) != 0) {
192 qemu_irq_raise(s
->irq
);
197 if (s
->status
== 0 && s
->en
!= 0) {
198 qemu_irq_lower(s
->irq
);
201 case CORE_BUF_20
... CORE_BUF_38
+ 4:
202 index
= (addr
- CORE_BUF_20
) >> 2;
209 qemu_log_mask(LOG_UNIMP
, "invalid write: %x", (uint32_t)addr
);
214 static const MemoryRegionOps loongarch_ipi_ops
= {
215 .read
= loongarch_ipi_readl
,
216 .write
= loongarch_ipi_writel
,
217 .impl
.min_access_size
= 4,
218 .impl
.max_access_size
= 4,
219 .valid
.min_access_size
= 4,
220 .valid
.max_access_size
= 8,
221 .endianness
= DEVICE_LITTLE_ENDIAN
,
224 /* mail send and any send only support writeq */
225 static void loongarch_ipi_writeq(void *opaque
, hwaddr addr
, uint64_t val
,
230 case MAIL_SEND_OFFSET
:
233 case ANY_SEND_OFFSET
:
241 static const MemoryRegionOps loongarch_ipi64_ops
= {
242 .write
= loongarch_ipi_writeq
,
243 .impl
.min_access_size
= 8,
244 .impl
.max_access_size
= 8,
245 .valid
.min_access_size
= 8,
246 .valid
.max_access_size
= 8,
247 .endianness
= DEVICE_LITTLE_ENDIAN
,
250 static void loongarch_ipi_init(Object
*obj
)
252 LoongArchIPI
*s
= LOONGARCH_IPI(obj
);
253 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
255 memory_region_init_io(&s
->ipi_iocsr_mem
, obj
, &loongarch_ipi_ops
,
256 &s
->ipi_core
, "loongarch_ipi_iocsr", 0x48);
258 /* loongarch_ipi_iocsr performs re-entrant IO through ipi_send */
259 s
->ipi_iocsr_mem
.disable_reentrancy_guard
= true;
261 sysbus_init_mmio(sbd
, &s
->ipi_iocsr_mem
);
263 memory_region_init_io(&s
->ipi64_iocsr_mem
, obj
, &loongarch_ipi64_ops
,
264 &s
->ipi_core
, "loongarch_ipi64_iocsr", 0x118);
265 sysbus_init_mmio(sbd
, &s
->ipi64_iocsr_mem
);
266 qdev_init_gpio_out(DEVICE(obj
), &s
->ipi_core
.irq
, 1);
269 static const VMStateDescription vmstate_ipi_core
= {
270 .name
= "ipi-single",
272 .minimum_version_id
= 2,
273 .fields
= (VMStateField
[]) {
274 VMSTATE_UINT32(status
, IPICore
),
275 VMSTATE_UINT32(en
, IPICore
),
276 VMSTATE_UINT32(set
, IPICore
),
277 VMSTATE_UINT32(clear
, IPICore
),
278 VMSTATE_UINT32_ARRAY(buf
, IPICore
, IPI_MBX_NUM
* 2),
279 VMSTATE_END_OF_LIST()
283 static const VMStateDescription vmstate_loongarch_ipi
= {
284 .name
= TYPE_LOONGARCH_IPI
,
286 .minimum_version_id
= 1,
287 .fields
= (VMStateField
[]) {
288 VMSTATE_STRUCT(ipi_core
, LoongArchIPI
, 0, vmstate_ipi_core
, IPICore
),
289 VMSTATE_END_OF_LIST()
293 static void loongarch_ipi_class_init(ObjectClass
*klass
, void *data
)
295 DeviceClass
*dc
= DEVICE_CLASS(klass
);
297 dc
->vmsd
= &vmstate_loongarch_ipi
;
300 static const TypeInfo loongarch_ipi_info
= {
301 .name
= TYPE_LOONGARCH_IPI
,
302 .parent
= TYPE_SYS_BUS_DEVICE
,
303 .instance_size
= sizeof(LoongArchIPI
),
304 .instance_init
= loongarch_ipi_init
,
305 .class_init
= loongarch_ipi_class_init
,
308 static void loongarch_ipi_register_types(void)
310 type_register_static(&loongarch_ipi_info
);
313 type_init(loongarch_ipi_register_types
)