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