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/sysbus.h"
11 #include "hw/intc/loongarch_ipi.h"
13 #include "hw/qdev-properties.h"
14 #include "qapi/error.h"
16 #include "exec/address-spaces.h"
17 #include "migration/vmstate.h"
18 #include "target/loongarch/cpu.h"
21 static MemTxResult
loongarch_ipi_readl(void *opaque
, hwaddr addr
,
23 unsigned size
, MemTxAttrs attrs
)
26 LoongArchIPI
*ipi
= opaque
;
30 s
= &ipi
->cpu
[attrs
.requester_id
];
45 case CORE_BUF_20
... CORE_BUF_38
+ 4:
46 index
= (addr
- CORE_BUF_20
) >> 2;
50 qemu_log_mask(LOG_UNIMP
, "invalid read: %x", (uint32_t)addr
);
54 trace_loongarch_ipi_read(size
, (uint64_t)addr
, ret
);
59 static void send_ipi_data(CPULoongArchState
*env
, uint64_t val
, hwaddr addr
,
62 int i
, mask
= 0, data
= 0;
65 * bit 27-30 is mask for byte writing,
66 * if the mask is 0, we need not to do anything.
68 if ((val
>> 27) & 0xf) {
69 data
= address_space_ldl(env
->address_space_iocsr
, addr
,
71 for (i
= 0; i
< 4; i
++) {
72 /* get mask for byte writing */
73 if (val
& (0x1 << (27 + i
))) {
74 mask
|= 0xff << (i
* 8);
80 data
|= (val
>> 32) & ~mask
;
81 address_space_stl(env
->address_space_iocsr
, addr
,
85 static int archid_cmp(const void *a
, const void *b
)
87 CPUArchId
*archid_a
= (CPUArchId
*)a
;
88 CPUArchId
*archid_b
= (CPUArchId
*)b
;
90 return archid_a
->arch_id
- archid_b
->arch_id
;
93 static CPUArchId
*find_cpu_by_archid(MachineState
*ms
, uint32_t id
)
95 CPUArchId apic_id
, *found_cpu
;
98 found_cpu
= bsearch(&apic_id
, ms
->possible_cpus
->cpus
,
99 ms
->possible_cpus
->len
, sizeof(*ms
->possible_cpus
->cpus
),
105 static CPUState
*ipi_getcpu(int arch_id
)
107 MachineState
*machine
= MACHINE(qdev_get_machine());
110 archid
= find_cpu_by_archid(machine
, arch_id
);
112 return CPU(archid
->cpu
);
118 static MemTxResult
mail_send(uint64_t val
, MemTxAttrs attrs
)
124 cpuid
= extract32(val
, 16, 10);
125 cs
= ipi_getcpu(cpuid
);
127 return MEMTX_DECODE_ERROR
;
130 /* override requester_id */
131 addr
= SMP_IPI_MAILBOX
+ CORE_BUF_20
+ (val
& 0x1c);
132 attrs
.requester_id
= cs
->cpu_index
;
133 send_ipi_data(&LOONGARCH_CPU(cs
)->env
, val
, addr
, attrs
);
137 static MemTxResult
any_send(uint64_t val
, MemTxAttrs attrs
)
143 cpuid
= extract32(val
, 16, 10);
144 cs
= ipi_getcpu(cpuid
);
146 return MEMTX_DECODE_ERROR
;
149 /* override requester_id */
151 attrs
.requester_id
= cs
->cpu_index
;
152 send_ipi_data(&LOONGARCH_CPU(cs
)->env
, val
, addr
, attrs
);
156 static MemTxResult
loongarch_ipi_writel(void *opaque
, hwaddr addr
, uint64_t val
,
157 unsigned size
, MemTxAttrs attrs
)
159 LoongArchIPI
*ipi
= opaque
;
166 s
= &ipi
->cpu
[attrs
.requester_id
];
168 trace_loongarch_ipi_write(size
, (uint64_t)addr
, val
);
170 case CORE_STATUS_OFF
:
171 qemu_log_mask(LOG_GUEST_ERROR
, "can not be written");
178 if (s
->status
!= 0 && (s
->status
& s
->en
) != 0) {
179 qemu_irq_raise(s
->irq
);
184 if (s
->status
== 0 && s
->en
!= 0) {
185 qemu_irq_lower(s
->irq
);
188 case CORE_BUF_20
... CORE_BUF_38
+ 4:
189 index
= (addr
- CORE_BUF_20
) >> 2;
193 cpuid
= extract32(val
, 16, 10);
194 /* IPI status vector */
195 vector
= extract8(val
, 0, 5);
196 cs
= ipi_getcpu(cpuid
);
198 return MEMTX_DECODE_ERROR
;
201 /* override requester_id */
202 attrs
.requester_id
= cs
->cpu_index
;
203 loongarch_ipi_writel(ipi
, CORE_SET_OFF
, BIT(vector
), 4, attrs
);
206 qemu_log_mask(LOG_UNIMP
, "invalid write: %x", (uint32_t)addr
);
213 static const MemoryRegionOps loongarch_ipi_ops
= {
214 .read_with_attrs
= loongarch_ipi_readl
,
215 .write_with_attrs
= loongarch_ipi_writel
,
216 .impl
.min_access_size
= 4,
217 .impl
.max_access_size
= 4,
218 .valid
.min_access_size
= 4,
219 .valid
.max_access_size
= 8,
220 .endianness
= DEVICE_LITTLE_ENDIAN
,
223 /* mail send and any send only support writeq */
224 static MemTxResult
loongarch_ipi_writeq(void *opaque
, hwaddr addr
, uint64_t val
,
225 unsigned size
, MemTxAttrs attrs
)
227 MemTxResult ret
= MEMTX_OK
;
231 case MAIL_SEND_OFFSET
:
232 ret
= mail_send(val
, attrs
);
234 case ANY_SEND_OFFSET
:
235 ret
= any_send(val
, attrs
);
244 static const MemoryRegionOps loongarch_ipi64_ops
= {
245 .write_with_attrs
= loongarch_ipi_writeq
,
246 .impl
.min_access_size
= 8,
247 .impl
.max_access_size
= 8,
248 .valid
.min_access_size
= 8,
249 .valid
.max_access_size
= 8,
250 .endianness
= DEVICE_LITTLE_ENDIAN
,
253 static void loongarch_ipi_realize(DeviceState
*dev
, Error
**errp
)
255 LoongArchIPI
*s
= LOONGARCH_IPI(dev
);
256 SysBusDevice
*sbd
= SYS_BUS_DEVICE(dev
);
259 if (s
->num_cpu
== 0) {
260 error_setg(errp
, "num-cpu must be at least 1");
264 memory_region_init_io(&s
->ipi_iocsr_mem
, OBJECT(dev
), &loongarch_ipi_ops
,
265 s
, "loongarch_ipi_iocsr", 0x48);
267 /* loongarch_ipi_iocsr performs re-entrant IO through ipi_send */
268 s
->ipi_iocsr_mem
.disable_reentrancy_guard
= true;
270 sysbus_init_mmio(sbd
, &s
->ipi_iocsr_mem
);
272 memory_region_init_io(&s
->ipi64_iocsr_mem
, OBJECT(dev
),
273 &loongarch_ipi64_ops
,
274 s
, "loongarch_ipi64_iocsr", 0x118);
275 sysbus_init_mmio(sbd
, &s
->ipi64_iocsr_mem
);
277 s
->cpu
= g_new0(IPICore
, s
->num_cpu
);
278 if (s
->cpu
== NULL
) {
279 error_setg(errp
, "Memory allocation for ExtIOICore faile");
283 for (i
= 0; i
< s
->num_cpu
; i
++) {
284 qdev_init_gpio_out(dev
, &s
->cpu
[i
].irq
, 1);
288 static const VMStateDescription vmstate_ipi_core
= {
289 .name
= "ipi-single",
291 .minimum_version_id
= 2,
292 .fields
= (const VMStateField
[]) {
293 VMSTATE_UINT32(status
, IPICore
),
294 VMSTATE_UINT32(en
, IPICore
),
295 VMSTATE_UINT32(set
, IPICore
),
296 VMSTATE_UINT32(clear
, IPICore
),
297 VMSTATE_UINT32_ARRAY(buf
, IPICore
, IPI_MBX_NUM
* 2),
298 VMSTATE_END_OF_LIST()
302 static const VMStateDescription vmstate_loongarch_ipi
= {
303 .name
= TYPE_LOONGARCH_IPI
,
305 .minimum_version_id
= 2,
306 .fields
= (const VMStateField
[]) {
307 VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu
, LoongArchIPI
, num_cpu
,
308 vmstate_ipi_core
, IPICore
),
309 VMSTATE_END_OF_LIST()
313 static Property ipi_properties
[] = {
314 DEFINE_PROP_UINT32("num-cpu", LoongArchIPI
, num_cpu
, 1),
315 DEFINE_PROP_END_OF_LIST(),
318 static void loongarch_ipi_class_init(ObjectClass
*klass
, void *data
)
320 DeviceClass
*dc
= DEVICE_CLASS(klass
);
322 dc
->realize
= loongarch_ipi_realize
;
323 device_class_set_props(dc
, ipi_properties
);
324 dc
->vmsd
= &vmstate_loongarch_ipi
;
327 static void loongarch_ipi_finalize(Object
*obj
)
329 LoongArchIPI
*s
= LOONGARCH_IPI(obj
);
334 static const TypeInfo loongarch_ipi_info
= {
335 .name
= TYPE_LOONGARCH_IPI
,
336 .parent
= TYPE_SYS_BUS_DEVICE
,
337 .instance_size
= sizeof(LoongArchIPI
),
338 .class_init
= loongarch_ipi_class_init
,
339 .instance_finalize
= loongarch_ipi_finalize
,
342 static void loongarch_ipi_register_types(void)
344 type_register_static(&loongarch_ipi_info
);
347 type_init(loongarch_ipi_register_types
)