1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * Loongson IPI interrupt common support
4 *
5 * Copyright (C) 2021 Loongson Technology Corporation Limited
6 */
7
8 #include "qemu/osdep.h"
9 #include "hw/sysbus.h"
10 #include "hw/intc/loongson_ipi_common.h"
11 #include "hw/irq.h"
12 #include "qemu/log.h"
13 #include "migration/vmstate.h"
14 #include "system/kvm.h"
15 #include "trace.h"
16
loongson_ipi_core_readl(void * opaque,hwaddr addr,uint64_t * data,unsigned size,MemTxAttrs attrs)17 MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr, uint64_t *data,
18 unsigned size, MemTxAttrs attrs)
19 {
20 IPICore *s = opaque;
21 uint64_t ret = 0;
22 int index = 0;
23
24 addr &= 0xff;
25 switch (addr) {
26 case CORE_STATUS_OFF:
27 ret = s->status;
28 break;
29 case CORE_EN_OFF:
30 ret = s->en;
31 break;
32 case CORE_SET_OFF:
33 ret = 0;
34 break;
35 case CORE_CLEAR_OFF:
36 ret = 0;
37 break;
38 case CORE_BUF_20 ... CORE_BUF_38 + 4:
39 index = (addr - CORE_BUF_20) >> 2;
40 ret = s->buf[index];
41 break;
42 default:
43 qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr);
44 break;
45 }
46
47 trace_loongson_ipi_read(size, (uint64_t)addr, ret);
48 *data = ret;
49
50 return MEMTX_OK;
51 }
52
loongson_ipi_iocsr_readl(void * opaque,hwaddr addr,uint64_t * data,unsigned size,MemTxAttrs attrs)53 static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr,
54 uint64_t *data, unsigned size,
55 MemTxAttrs attrs)
56 {
57 LoongsonIPICommonState *ipi = opaque;
58 IPICore *s;
59
60 if (attrs.requester_id >= ipi->num_cpu) {
61 return MEMTX_DECODE_ERROR;
62 }
63
64 s = &ipi->cpu[attrs.requester_id];
65 return loongson_ipi_core_readl(s, addr, data, size, attrs);
66 }
67
send_ipi_data(LoongsonIPICommonState * ipi,CPUState * cpu,uint64_t val,hwaddr addr,MemTxAttrs attrs)68 static MemTxResult send_ipi_data(LoongsonIPICommonState *ipi, CPUState *cpu,
69 uint64_t val, hwaddr addr, MemTxAttrs attrs)
70 {
71 LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
72 int i, mask = 0, data = 0;
73 AddressSpace *iocsr_as = licc->get_iocsr_as(cpu);
74
75 if (!iocsr_as) {
76 return MEMTX_DECODE_ERROR;
77 }
78
79 /*
80 * bit 27-30 is mask for byte writing,
81 * if the mask is 0, we need not to do anything.
82 */
83 if ((val >> 27) & 0xf) {
84 data = address_space_ldl_le(iocsr_as, addr, attrs, NULL);
85 for (i = 0; i < 4; i++) {
86 /* get mask for byte writing */
87 if (val & (0x1 << (27 + i))) {
88 mask |= 0xff << (i * 8);
89 }
90 }
91 }
92
93 data &= mask;
94 data |= (val >> 32) & ~mask;
95 address_space_stl_le(iocsr_as, addr, data, attrs, NULL);
96
97 return MEMTX_OK;
98 }
99
mail_send(LoongsonIPICommonState * ipi,uint64_t val,MemTxAttrs attrs)100 static MemTxResult mail_send(LoongsonIPICommonState *ipi,
101 uint64_t val, MemTxAttrs attrs)
102 {
103 LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
104 uint32_t cpuid;
105 hwaddr addr;
106 CPUState *cs;
107 int cpu, ret;
108
109 cpuid = extract32(val, 16, 10);
110 ret = licc->cpu_by_arch_id(ipi, cpuid, &cpu, &cs);
111 if (ret != MEMTX_OK) {
112 return MEMTX_DECODE_ERROR;
113 }
114
115 /* override requester_id */
116 addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c);
117 attrs.requester_id = cpu;
118 return send_ipi_data(ipi, cs, val, addr, attrs);
119 }
120
any_send(LoongsonIPICommonState * ipi,uint64_t val,MemTxAttrs attrs)121 static MemTxResult any_send(LoongsonIPICommonState *ipi,
122 uint64_t val, MemTxAttrs attrs)
123 {
124 LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
125 uint32_t cpuid;
126 hwaddr addr;
127 CPUState *cs;
128 int cpu, ret;
129
130 cpuid = extract32(val, 16, 10);
131 ret = licc->cpu_by_arch_id(ipi, cpuid, &cpu, &cs);
132 if (ret != MEMTX_OK) {
133 return MEMTX_DECODE_ERROR;
134 }
135
136 /* override requester_id */
137 addr = val & 0xffff;
138 attrs.requester_id = cpu;
139 return send_ipi_data(ipi, cs, val, addr, attrs);
140 }
141
loongson_ipi_core_writel(void * opaque,hwaddr addr,uint64_t val,unsigned size,MemTxAttrs attrs)142 MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, uint64_t val,
143 unsigned size, MemTxAttrs attrs)
144 {
145 IPICore *s = opaque;
146 LoongsonIPICommonState *ipi = s->ipi;
147 LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
148 int index = 0;
149 uint32_t cpuid;
150 uint8_t vector;
151 CPUState *cs;
152 int cpu, ret;
153
154 addr &= 0xff;
155 trace_loongson_ipi_write(size, (uint64_t)addr, val);
156 switch (addr) {
157 case CORE_STATUS_OFF:
158 qemu_log_mask(LOG_GUEST_ERROR, "can not be written");
159 break;
160 case CORE_EN_OFF:
161 s->en = val;
162 break;
163 case CORE_SET_OFF:
164 s->status |= val;
165 if (s->status != 0 && (s->status & s->en) != 0) {
166 qemu_irq_raise(s->irq);
167 }
168 break;
169 case CORE_CLEAR_OFF:
170 s->status &= ~val;
171 if (s->status == 0 && s->en != 0) {
172 qemu_irq_lower(s->irq);
173 }
174 break;
175 case CORE_BUF_20 ... CORE_BUF_38 + 4:
176 index = (addr - CORE_BUF_20) >> 2;
177 s->buf[index] = val;
178 break;
179 case IOCSR_IPI_SEND:
180 cpuid = extract32(val, 16, 10);
181 /* IPI status vector */
182 vector = extract8(val, 0, 5);
183 ret = licc->cpu_by_arch_id(ipi, cpuid, &cpu, &cs);
184 if (ret != MEMTX_OK || cpu >= ipi->num_cpu) {
185 return MEMTX_DECODE_ERROR;
186 }
187 loongson_ipi_core_writel(&ipi->cpu[cpu], CORE_SET_OFF,
188 BIT(vector), 4, attrs);
189 break;
190 default:
191 qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr);
192 break;
193 }
194
195 return MEMTX_OK;
196 }
197
loongson_ipi_iocsr_writel(void * opaque,hwaddr addr,uint64_t val,unsigned size,MemTxAttrs attrs)198 static MemTxResult loongson_ipi_iocsr_writel(void *opaque, hwaddr addr,
199 uint64_t val, unsigned size,
200 MemTxAttrs attrs)
201 {
202 LoongsonIPICommonState *ipi = opaque;
203 IPICore *s;
204
205 if (attrs.requester_id >= ipi->num_cpu) {
206 return MEMTX_DECODE_ERROR;
207 }
208
209 s = &ipi->cpu[attrs.requester_id];
210 return loongson_ipi_core_writel(s, addr, val, size, attrs);
211 }
212
213 static const MemoryRegionOps loongson_ipi_iocsr_ops = {
214 .read_with_attrs = loongson_ipi_iocsr_readl,
215 .write_with_attrs = loongson_ipi_iocsr_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 */
loongson_ipi_writeq(void * opaque,hwaddr addr,uint64_t val,unsigned size,MemTxAttrs attrs)224 static MemTxResult loongson_ipi_writeq(void *opaque, hwaddr addr, uint64_t val,
225 unsigned size, MemTxAttrs attrs)
226 {
227 LoongsonIPICommonState *ipi = opaque;
228 MemTxResult ret = MEMTX_OK;
229
230 addr &= 0xfff;
231 switch (addr) {
232 case MAIL_SEND_OFFSET:
233 ret = mail_send(ipi, val, attrs);
234 break;
235 case ANY_SEND_OFFSET:
236 ret = any_send(ipi, val, attrs);
237 break;
238 default:
239 break;
240 }
241
242 return ret;
243 }
244
245 static const MemoryRegionOps loongson_ipi64_ops = {
246 .write_with_attrs = loongson_ipi_writeq,
247 .impl.min_access_size = 8,
248 .impl.max_access_size = 8,
249 .valid.min_access_size = 8,
250 .valid.max_access_size = 8,
251 .endianness = DEVICE_LITTLE_ENDIAN,
252 };
253
loongson_ipi_common_realize(DeviceState * dev,Error ** errp)254 static void loongson_ipi_common_realize(DeviceState *dev, Error **errp)
255 {
256 LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev);
257 SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
258
259 if (kvm_irqchip_in_kernel()) {
260 return;
261 }
262
263 memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev),
264 &loongson_ipi_iocsr_ops,
265 s, "loongson_ipi_iocsr", 0x48);
266
267 /* loongson_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 &loongson_ipi64_ops,
274 s, "loongson_ipi64_iocsr", 0x118);
275 sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem);
276 }
277
loongson_ipi_common_unrealize(DeviceState * dev)278 static void loongson_ipi_common_unrealize(DeviceState *dev)
279 {
280 LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev);
281
282 g_free(s->cpu);
283 }
284
loongson_ipi_common_pre_save(void * opaque)285 static int loongson_ipi_common_pre_save(void *opaque)
286 {
287 IPICore *ipicore = (IPICore *)opaque;
288 LoongsonIPICommonState *s = ipicore->ipi;
289 LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(s);
290
291 if (licc->pre_save) {
292 return licc->pre_save(s);
293 }
294
295 return 0;
296 }
297
loongson_ipi_common_post_load(void * opaque,int version_id)298 static int loongson_ipi_common_post_load(void *opaque, int version_id)
299 {
300 IPICore *ipicore = (IPICore *)opaque;
301 LoongsonIPICommonState *s = ipicore->ipi;
302 LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(s);
303
304 if (licc->post_load) {
305 return licc->post_load(s, version_id);
306 }
307
308 return 0;
309 }
310
311 static const VMStateDescription vmstate_ipi_core = {
312 .name = "ipi-single",
313 .version_id = 2,
314 .minimum_version_id = 2,
315 .pre_save = loongson_ipi_common_pre_save,
316 .post_load = loongson_ipi_common_post_load,
317 .fields = (const VMStateField[]) {
318 VMSTATE_UINT32(status, IPICore),
319 VMSTATE_UINT32(en, IPICore),
320 VMSTATE_UINT32(set, IPICore),
321 VMSTATE_UINT32(clear, IPICore),
322 VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2),
323 VMSTATE_END_OF_LIST()
324 }
325 };
326
327 static const VMStateDescription vmstate_loongson_ipi_common = {
328 .name = "loongson_ipi",
329 .version_id = 2,
330 .minimum_version_id = 2,
331 .fields = (const VMStateField[]) {
332 VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongsonIPICommonState,
333 num_cpu, vmstate_ipi_core,
334 IPICore),
335 VMSTATE_END_OF_LIST()
336 }
337 };
338
loongson_ipi_common_class_init(ObjectClass * klass,const void * data)339 static void loongson_ipi_common_class_init(ObjectClass *klass, const void *data)
340 {
341 DeviceClass *dc = DEVICE_CLASS(klass);
342 LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass);
343
344 device_class_set_parent_realize(dc, loongson_ipi_common_realize,
345 &licc->parent_realize);
346 device_class_set_parent_unrealize(dc, loongson_ipi_common_unrealize,
347 &licc->parent_unrealize);
348 dc->vmsd = &vmstate_loongson_ipi_common;
349 }
350
351 static const TypeInfo loongarch_ipi_common_types[] = {
352 {
353 .name = TYPE_LOONGSON_IPI_COMMON,
354 .parent = TYPE_SYS_BUS_DEVICE,
355 .instance_size = sizeof(LoongsonIPICommonState),
356 .class_size = sizeof(LoongsonIPICommonClass),
357 .class_init = loongson_ipi_common_class_init,
358 .abstract = true,
359 }
360 };
361
362 DEFINE_TYPES(loongarch_ipi_common_types)
363