]> git.proxmox.com Git - mirror_qemu.git/blob - hw/intc/loongarch_ipi.c
Merge tag 'pull-maintainer-may24-160524-2' of https://gitlab.com/stsquad/qemu into...
[mirror_qemu.git] / hw / intc / loongarch_ipi.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * LoongArch ipi interrupt support
4 *
5 * Copyright (C) 2021 Loongson Technology Corporation Limited
6 */
7
8 #include "qemu/osdep.h"
9 #include "hw/boards.h"
10 #include "hw/sysbus.h"
11 #include "hw/intc/loongarch_ipi.h"
12 #include "hw/irq.h"
13 #include "hw/qdev-properties.h"
14 #include "qapi/error.h"
15 #include "qemu/log.h"
16 #include "exec/address-spaces.h"
17 #include "migration/vmstate.h"
18 #include "target/loongarch/cpu.h"
19 #include "trace.h"
20
21 static MemTxResult loongarch_ipi_readl(void *opaque, hwaddr addr,
22 uint64_t *data,
23 unsigned size, MemTxAttrs attrs)
24 {
25 IPICore *s;
26 LoongArchIPI *ipi = opaque;
27 uint64_t ret = 0;
28 int index = 0;
29
30 s = &ipi->cpu[attrs.requester_id];
31 addr &= 0xff;
32 switch (addr) {
33 case CORE_STATUS_OFF:
34 ret = s->status;
35 break;
36 case CORE_EN_OFF:
37 ret = s->en;
38 break;
39 case CORE_SET_OFF:
40 ret = 0;
41 break;
42 case CORE_CLEAR_OFF:
43 ret = 0;
44 break;
45 case CORE_BUF_20 ... CORE_BUF_38 + 4:
46 index = (addr - CORE_BUF_20) >> 2;
47 ret = s->buf[index];
48 break;
49 default:
50 qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr);
51 break;
52 }
53
54 trace_loongarch_ipi_read(size, (uint64_t)addr, ret);
55 *data = ret;
56 return MEMTX_OK;
57 }
58
59 static void send_ipi_data(CPULoongArchState *env, uint64_t val, hwaddr addr,
60 MemTxAttrs attrs)
61 {
62 int i, mask = 0, data = 0;
63
64 /*
65 * bit 27-30 is mask for byte writing,
66 * if the mask is 0, we need not to do anything.
67 */
68 if ((val >> 27) & 0xf) {
69 data = address_space_ldl(env->address_space_iocsr, addr,
70 attrs, NULL);
71 for (i = 0; i < 4; i++) {
72 /* get mask for byte writing */
73 if (val & (0x1 << (27 + i))) {
74 mask |= 0xff << (i * 8);
75 }
76 }
77 }
78
79 data &= mask;
80 data |= (val >> 32) & ~mask;
81 address_space_stl(env->address_space_iocsr, addr,
82 data, attrs, NULL);
83 }
84
85 static int archid_cmp(const void *a, const void *b)
86 {
87 CPUArchId *archid_a = (CPUArchId *)a;
88 CPUArchId *archid_b = (CPUArchId *)b;
89
90 return archid_a->arch_id - archid_b->arch_id;
91 }
92
93 static CPUArchId *find_cpu_by_archid(MachineState *ms, uint32_t id)
94 {
95 CPUArchId apic_id, *found_cpu;
96
97 apic_id.arch_id = id;
98 found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus,
99 ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus),
100 archid_cmp);
101
102 return found_cpu;
103 }
104
105 static CPUState *ipi_getcpu(int arch_id)
106 {
107 MachineState *machine = MACHINE(qdev_get_machine());
108 CPUArchId *archid;
109
110 archid = find_cpu_by_archid(machine, arch_id);
111 if (archid) {
112 return CPU(archid->cpu);
113 }
114
115 return NULL;
116 }
117
118 static MemTxResult mail_send(uint64_t val, MemTxAttrs attrs)
119 {
120 uint32_t cpuid;
121 hwaddr addr;
122 CPUState *cs;
123
124 cpuid = extract32(val, 16, 10);
125 cs = ipi_getcpu(cpuid);
126 if (cs == NULL) {
127 return MEMTX_DECODE_ERROR;
128 }
129
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);
134 return MEMTX_OK;
135 }
136
137 static MemTxResult any_send(uint64_t val, MemTxAttrs attrs)
138 {
139 uint32_t cpuid;
140 hwaddr addr;
141 CPUState *cs;
142
143 cpuid = extract32(val, 16, 10);
144 cs = ipi_getcpu(cpuid);
145 if (cs == NULL) {
146 return MEMTX_DECODE_ERROR;
147 }
148
149 /* override requester_id */
150 addr = val & 0xffff;
151 attrs.requester_id = cs->cpu_index;
152 send_ipi_data(&LOONGARCH_CPU(cs)->env, val, addr, attrs);
153 return MEMTX_OK;
154 }
155
156 static MemTxResult loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val,
157 unsigned size, MemTxAttrs attrs)
158 {
159 LoongArchIPI *ipi = opaque;
160 IPICore *s;
161 int index = 0;
162 uint32_t cpuid;
163 uint8_t vector;
164 CPUState *cs;
165
166 s = &ipi->cpu[attrs.requester_id];
167 addr &= 0xff;
168 trace_loongarch_ipi_write(size, (uint64_t)addr, val);
169 switch (addr) {
170 case CORE_STATUS_OFF:
171 qemu_log_mask(LOG_GUEST_ERROR, "can not be written");
172 break;
173 case CORE_EN_OFF:
174 s->en = val;
175 break;
176 case CORE_SET_OFF:
177 s->status |= val;
178 if (s->status != 0 && (s->status & s->en) != 0) {
179 qemu_irq_raise(s->irq);
180 }
181 break;
182 case CORE_CLEAR_OFF:
183 s->status &= ~val;
184 if (s->status == 0 && s->en != 0) {
185 qemu_irq_lower(s->irq);
186 }
187 break;
188 case CORE_BUF_20 ... CORE_BUF_38 + 4:
189 index = (addr - CORE_BUF_20) >> 2;
190 s->buf[index] = val;
191 break;
192 case IOCSR_IPI_SEND:
193 cpuid = extract32(val, 16, 10);
194 /* IPI status vector */
195 vector = extract8(val, 0, 5);
196 cs = ipi_getcpu(cpuid);
197 if (cs == NULL) {
198 return MEMTX_DECODE_ERROR;
199 }
200
201 /* override requester_id */
202 attrs.requester_id = cs->cpu_index;
203 loongarch_ipi_writel(ipi, CORE_SET_OFF, BIT(vector), 4, attrs);
204 break;
205 default:
206 qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr);
207 break;
208 }
209
210 return MEMTX_OK;
211 }
212
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,
221 };
222
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)
226 {
227 MemTxResult ret = MEMTX_OK;
228
229 addr &= 0xfff;
230 switch (addr) {
231 case MAIL_SEND_OFFSET:
232 ret = mail_send(val, attrs);
233 break;
234 case ANY_SEND_OFFSET:
235 ret = any_send(val, attrs);
236 break;
237 default:
238 break;
239 }
240
241 return ret;
242 }
243
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,
251 };
252
253 static void loongarch_ipi_realize(DeviceState *dev, Error **errp)
254 {
255 LoongArchIPI *s = LOONGARCH_IPI(dev);
256 SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
257 int i;
258
259 if (s->num_cpu == 0) {
260 error_setg(errp, "num-cpu must be at least 1");
261 return;
262 }
263
264 memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev), &loongarch_ipi_ops,
265 s, "loongarch_ipi_iocsr", 0x48);
266
267 /* loongarch_ipi_iocsr performs re-entrant IO through ipi_send */
268 s->ipi_iocsr_mem.disable_reentrancy_guard = true;
269
270 sysbus_init_mmio(sbd, &s->ipi_iocsr_mem);
271
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);
276
277 s->cpu = g_new0(IPICore, s->num_cpu);
278 if (s->cpu == NULL) {
279 error_setg(errp, "Memory allocation for ExtIOICore faile");
280 return;
281 }
282
283 for (i = 0; i < s->num_cpu; i++) {
284 qdev_init_gpio_out(dev, &s->cpu[i].irq, 1);
285 }
286 }
287
288 static const VMStateDescription vmstate_ipi_core = {
289 .name = "ipi-single",
290 .version_id = 2,
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()
299 }
300 };
301
302 static const VMStateDescription vmstate_loongarch_ipi = {
303 .name = TYPE_LOONGARCH_IPI,
304 .version_id = 2,
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()
310 }
311 };
312
313 static Property ipi_properties[] = {
314 DEFINE_PROP_UINT32("num-cpu", LoongArchIPI, num_cpu, 1),
315 DEFINE_PROP_END_OF_LIST(),
316 };
317
318 static void loongarch_ipi_class_init(ObjectClass *klass, void *data)
319 {
320 DeviceClass *dc = DEVICE_CLASS(klass);
321
322 dc->realize = loongarch_ipi_realize;
323 device_class_set_props(dc, ipi_properties);
324 dc->vmsd = &vmstate_loongarch_ipi;
325 }
326
327 static void loongarch_ipi_finalize(Object *obj)
328 {
329 LoongArchIPI *s = LOONGARCH_IPI(obj);
330
331 g_free(s->cpu);
332 }
333
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,
340 };
341
342 static void loongarch_ipi_register_types(void)
343 {
344 type_register_static(&loongarch_ipi_info);
345 }
346
347 type_init(loongarch_ipi_register_types)