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 uint64_t loongarch_ipi_readl(void *opaque
, hwaddr addr
, unsigned size
)
40 case CORE_BUF_20
... CORE_BUF_38
+ 4:
41 index
= (addr
- CORE_BUF_20
) >> 2;
45 qemu_log_mask(LOG_UNIMP
, "invalid read: %x", (uint32_t)addr
);
49 trace_loongarch_ipi_read(size
, (uint64_t)addr
, ret
);
53 static void send_ipi_data(CPULoongArchState
*env
, uint64_t val
, hwaddr addr
)
55 int i
, mask
= 0, data
= 0;
58 * bit 27-30 is mask for byte writing,
59 * if the mask is 0, we need not to do anything.
61 if ((val
>> 27) & 0xf) {
62 data
= address_space_ldl(&env
->address_space_iocsr
, addr
,
63 MEMTXATTRS_UNSPECIFIED
, NULL
);
64 for (i
= 0; i
< 4; i
++) {
65 /* get mask for byte writing */
66 if (val
& (0x1 << (27 + i
))) {
67 mask
|= 0xff << (i
* 8);
73 data
|= (val
>> 32) & ~mask
;
74 address_space_stl(&env
->address_space_iocsr
, addr
,
75 data
, MEMTXATTRS_UNSPECIFIED
, NULL
);
78 static void ipi_send(uint64_t val
)
81 CPULoongArchState
*env
;
85 cpuid
= (val
>> 16) & 0x3ff;
86 /* IPI status vector */
87 data
= 1 << (val
& 0x1f);
88 cs
= qemu_get_cpu(cpuid
);
89 cpu
= LOONGARCH_CPU(cs
);
91 address_space_stl(&env
->address_space_iocsr
, 0x1008,
92 data
, MEMTXATTRS_UNSPECIFIED
, NULL
);
96 static void mail_send(uint64_t val
)
100 CPULoongArchState
*env
;
104 cpuid
= (val
>> 16) & 0x3ff;
105 addr
= 0x1020 + (val
& 0x1c);
106 cs
= qemu_get_cpu(cpuid
);
107 cpu
= LOONGARCH_CPU(cs
);
109 send_ipi_data(env
, val
, addr
);
112 static void any_send(uint64_t val
)
116 CPULoongArchState
*env
;
118 cpuid
= (val
>> 16) & 0x3ff;
120 CPUState
*cs
= qemu_get_cpu(cpuid
);
121 LoongArchCPU
*cpu
= LOONGARCH_CPU(cs
);
123 send_ipi_data(env
, val
, addr
);
126 static void loongarch_ipi_writel(void *opaque
, hwaddr addr
, uint64_t val
,
133 trace_loongarch_ipi_write(size
, (uint64_t)addr
, val
);
135 case CORE_STATUS_OFF
:
136 qemu_log_mask(LOG_GUEST_ERROR
, "can not be written");
143 if (s
->status
!= 0 && (s
->status
& s
->en
) != 0) {
144 qemu_irq_raise(s
->irq
);
149 if (s
->status
== 0 && s
->en
!= 0) {
150 qemu_irq_lower(s
->irq
);
153 case CORE_BUF_20
... CORE_BUF_38
+ 4:
154 index
= (addr
- CORE_BUF_20
) >> 2;
161 qemu_log_mask(LOG_UNIMP
, "invalid write: %x", (uint32_t)addr
);
166 static const MemoryRegionOps loongarch_ipi_ops
= {
167 .read
= loongarch_ipi_readl
,
168 .write
= loongarch_ipi_writel
,
169 .impl
.min_access_size
= 4,
170 .impl
.max_access_size
= 4,
171 .valid
.min_access_size
= 4,
172 .valid
.max_access_size
= 8,
173 .endianness
= DEVICE_LITTLE_ENDIAN
,
176 /* mail send and any send only support writeq */
177 static void loongarch_ipi_writeq(void *opaque
, hwaddr addr
, uint64_t val
,
182 case MAIL_SEND_OFFSET
:
185 case ANY_SEND_OFFSET
:
193 static const MemoryRegionOps loongarch_ipi64_ops
= {
194 .write
= loongarch_ipi_writeq
,
195 .impl
.min_access_size
= 8,
196 .impl
.max_access_size
= 8,
197 .valid
.min_access_size
= 8,
198 .valid
.max_access_size
= 8,
199 .endianness
= DEVICE_LITTLE_ENDIAN
,
202 static void loongarch_ipi_init(Object
*obj
)
204 LoongArchIPI
*s
= LOONGARCH_IPI(obj
);
205 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
207 memory_region_init_io(&s
->ipi_iocsr_mem
, obj
, &loongarch_ipi_ops
,
208 &s
->ipi_core
, "loongarch_ipi_iocsr", 0x48);
210 /* loongarch_ipi_iocsr performs re-entrant IO through ipi_send */
211 s
->ipi_iocsr_mem
.disable_reentrancy_guard
= true;
213 sysbus_init_mmio(sbd
, &s
->ipi_iocsr_mem
);
215 memory_region_init_io(&s
->ipi64_iocsr_mem
, obj
, &loongarch_ipi64_ops
,
216 &s
->ipi_core
, "loongarch_ipi64_iocsr", 0x118);
217 sysbus_init_mmio(sbd
, &s
->ipi64_iocsr_mem
);
218 qdev_init_gpio_out(DEVICE(obj
), &s
->ipi_core
.irq
, 1);
221 static const VMStateDescription vmstate_ipi_core
= {
222 .name
= "ipi-single",
224 .minimum_version_id
= 1,
225 .fields
= (VMStateField
[]) {
226 VMSTATE_UINT32(status
, IPICore
),
227 VMSTATE_UINT32(en
, IPICore
),
228 VMSTATE_UINT32(set
, IPICore
),
229 VMSTATE_UINT32(clear
, IPICore
),
230 VMSTATE_UINT32_ARRAY(buf
, IPICore
, 2),
231 VMSTATE_END_OF_LIST()
235 static const VMStateDescription vmstate_loongarch_ipi
= {
236 .name
= TYPE_LOONGARCH_IPI
,
238 .minimum_version_id
= 1,
239 .fields
= (VMStateField
[]) {
240 VMSTATE_STRUCT(ipi_core
, LoongArchIPI
, 0, vmstate_ipi_core
, IPICore
),
241 VMSTATE_END_OF_LIST()
245 static void loongarch_ipi_class_init(ObjectClass
*klass
, void *data
)
247 DeviceClass
*dc
= DEVICE_CLASS(klass
);
249 dc
->vmsd
= &vmstate_loongarch_ipi
;
252 static const TypeInfo loongarch_ipi_info
= {
253 .name
= TYPE_LOONGARCH_IPI
,
254 .parent
= TYPE_SYS_BUS_DEVICE
,
255 .instance_size
= sizeof(LoongArchIPI
),
256 .instance_init
= loongarch_ipi_init
,
257 .class_init
= loongarch_ipi_class_init
,
260 static void loongarch_ipi_register_types(void)
262 type_register_static(&loongarch_ipi_info
);
265 type_init(loongarch_ipi_register_types
)