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 "trace.h"
15
loongson_ipi_core_readl(void * opaque,hwaddr addr,uint64_t * data,unsigned size,MemTxAttrs attrs)16 MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr, uint64_t *data,
17 unsigned size, MemTxAttrs attrs)
18 {
19 IPICore *s = opaque;
20 uint64_t ret = 0;
21 int index = 0;
22
23 addr &= 0xff;
24 switch (addr) {
25 case CORE_STATUS_OFF:
26 ret = s->status;
27 break;
28 case CORE_EN_OFF:
29 ret = s->en;
30 break;
31 case CORE_SET_OFF:
32 ret = 0;
33 break;
34 case CORE_CLEAR_OFF:
35 ret = 0;
36 break;
37 case CORE_BUF_20 ... CORE_BUF_38 + 4:
38 index = (addr - CORE_BUF_20) >> 2;
39 ret = s->buf[index];
40 break;
41 default:
42 qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr);
43 break;
44 }
45
46 trace_loongson_ipi_read(size, (uint64_t)addr, ret);
47 *data = ret;
48
49 return MEMTX_OK;
50 }
51
loongson_ipi_iocsr_readl(void * opaque,hwaddr addr,uint64_t * data,unsigned size,MemTxAttrs attrs)52 static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr,
53 uint64_t *data, unsigned size,
54 MemTxAttrs attrs)
55 {
56 LoongsonIPICommonState *ipi = opaque;
57 IPICore *s;
58
59 if (attrs.requester_id >= ipi->num_cpu) {
60 return MEMTX_DECODE_ERROR;
61 }
62
63 s = &ipi->cpu[attrs.requester_id];
64 return loongson_ipi_core_readl(s, addr, data, size, attrs);
65 }
66
send_ipi_data(LoongsonIPICommonState * ipi,CPUState * cpu,uint64_t val,hwaddr addr,MemTxAttrs attrs)67 static MemTxResult send_ipi_data(LoongsonIPICommonState *ipi, CPUState *cpu,
68 uint64_t val, hwaddr addr, MemTxAttrs attrs)
69 {
70 LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
71 int i, mask = 0, data = 0;
72 AddressSpace *iocsr_as = licc->get_iocsr_as(cpu);
73
74 if (!iocsr_as) {
75 return MEMTX_DECODE_ERROR;
76 }
77
78 /*
79 * bit 27-30 is mask for byte writing,
80 * if the mask is 0, we need not to do anything.
81 */
82 if ((val >> 27) & 0xf) {
83 data = address_space_ldl_le(iocsr_as, addr, attrs, NULL);
84 for (i = 0; i < 4; i++) {
85 /* get mask for byte writing */
86 if (val & (0x1 << (27 + i))) {
87 mask |= 0xff << (i * 8);
88 }
89 }
90 }
91
92 data &= mask;
93 data |= (val >> 32) & ~mask;
94 address_space_stl_le(iocsr_as, addr, data, attrs, NULL);
95
96 return MEMTX_OK;
97 }
98
mail_send(LoongsonIPICommonState * ipi,uint64_t val,MemTxAttrs attrs)99 static MemTxResult mail_send(LoongsonIPICommonState *ipi,
100 uint64_t val, MemTxAttrs attrs)
101 {
102 LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
103 uint32_t cpuid;
104 hwaddr addr;
105 CPUState *cs;
106 int cpu, ret;
107
108 cpuid = extract32(val, 16, 10);
109 ret = licc->cpu_by_arch_id(ipi, cpuid, &cpu, &cs);
110 if (ret != MEMTX_OK) {
111 return MEMTX_DECODE_ERROR;
112 }
113
114 /* override requester_id */
115 addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c);
116 attrs.requester_id = cpu;
117 return send_ipi_data(ipi, cs, val, addr, attrs);
118 }
119
any_send(LoongsonIPICommonState * ipi,uint64_t val,MemTxAttrs attrs)120 static MemTxResult any_send(LoongsonIPICommonState *ipi,
121 uint64_t val, MemTxAttrs attrs)
122 {
123 LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
124 uint32_t cpuid;
125 hwaddr addr;
126 CPUState *cs;
127 int cpu, ret;
128
129 cpuid = extract32(val, 16, 10);
130 ret = licc->cpu_by_arch_id(ipi, cpuid, &cpu, &cs);
131 if (ret != MEMTX_OK) {
132 return MEMTX_DECODE_ERROR;
133 }
134
135 /* override requester_id */
136 addr = val & 0xffff;
137 attrs.requester_id = cpu;
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 int cpu, ret;
152
153 addr &= 0xff;
154 trace_loongson_ipi_write(size, (uint64_t)addr, val);
155 switch (addr) {
156 case CORE_STATUS_OFF:
157 qemu_log_mask(LOG_GUEST_ERROR, "can not be written");
158 break;
159 case CORE_EN_OFF:
160 s->en = val;
161 break;
162 case CORE_SET_OFF:
163 s->status |= val;
164 if (s->status != 0 && (s->status & s->en) != 0) {
165 qemu_irq_raise(s->irq);
166 }
167 break;
168 case CORE_CLEAR_OFF:
169 s->status &= ~val;
170 if (s->status == 0 && s->en != 0) {
171 qemu_irq_lower(s->irq);
172 }
173 break;
174 case CORE_BUF_20 ... CORE_BUF_38 + 4:
175 index = (addr - CORE_BUF_20) >> 2;
176 s->buf[index] = val;
177 break;
178 case IOCSR_IPI_SEND:
179 cpuid = extract32(val, 16, 10);
180 /* IPI status vector */
181 vector = extract8(val, 0, 5);
182 ret = licc->cpu_by_arch_id(ipi, cpuid, &cpu, &cs);
183 if (ret != MEMTX_OK || cpu >= ipi->num_cpu) {
184 return MEMTX_DECODE_ERROR;
185 }
186 loongson_ipi_core_writel(&ipi->cpu[cpu], CORE_SET_OFF,
187 BIT(vector), 4, attrs);
188 break;
189 default:
190 qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr);
191 break;
192 }
193
194 return MEMTX_OK;
195 }
196
loongson_ipi_iocsr_writel(void * opaque,hwaddr addr,uint64_t val,unsigned size,MemTxAttrs attrs)197 static MemTxResult loongson_ipi_iocsr_writel(void *opaque, hwaddr addr,
198 uint64_t val, unsigned size,
199 MemTxAttrs attrs)
200 {
201 LoongsonIPICommonState *ipi = opaque;
202 IPICore *s;
203
204 if (attrs.requester_id >= ipi->num_cpu) {
205 return MEMTX_DECODE_ERROR;
206 }
207
208 s = &ipi->cpu[attrs.requester_id];
209 return loongson_ipi_core_writel(s, addr, val, size, attrs);
210 }
211
212 static const MemoryRegionOps loongson_ipi_iocsr_ops = {
213 .read_with_attrs = loongson_ipi_iocsr_readl,
214 .write_with_attrs = loongson_ipi_iocsr_writel,
215 .impl.min_access_size = 4,
216 .impl.max_access_size = 4,
217 .valid.min_access_size = 4,
218 .valid.max_access_size = 8,
219 .endianness = DEVICE_LITTLE_ENDIAN,
220 };
221
222 /* mail send and any send only support writeq */
loongson_ipi_writeq(void * opaque,hwaddr addr,uint64_t val,unsigned size,MemTxAttrs attrs)223 static MemTxResult loongson_ipi_writeq(void *opaque, hwaddr addr, uint64_t val,
224 unsigned size, MemTxAttrs attrs)
225 {
226 LoongsonIPICommonState *ipi = opaque;
227 MemTxResult ret = MEMTX_OK;
228
229 addr &= 0xfff;
230 switch (addr) {
231 case MAIL_SEND_OFFSET:
232 ret = mail_send(ipi, val, attrs);
233 break;
234 case ANY_SEND_OFFSET:
235 ret = any_send(ipi, val, attrs);
236 break;
237 default:
238 break;
239 }
240
241 return ret;
242 }
243
244 static const MemoryRegionOps loongson_ipi64_ops = {
245 .write_with_attrs = loongson_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
loongson_ipi_common_realize(DeviceState * dev,Error ** errp)253 static void loongson_ipi_common_realize(DeviceState *dev, Error **errp)
254 {
255 LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev);
256 SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
257
258 memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev),
259 &loongson_ipi_iocsr_ops,
260 s, "loongson_ipi_iocsr", 0x48);
261
262 /* loongson_ipi_iocsr performs re-entrant IO through ipi_send */
263 s->ipi_iocsr_mem.disable_reentrancy_guard = true;
264
265 sysbus_init_mmio(sbd, &s->ipi_iocsr_mem);
266
267 memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev),
268 &loongson_ipi64_ops,
269 s, "loongson_ipi64_iocsr", 0x118);
270 sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem);
271 }
272
loongson_ipi_common_unrealize(DeviceState * dev)273 static void loongson_ipi_common_unrealize(DeviceState *dev)
274 {
275 LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev);
276
277 g_free(s->cpu);
278 }
279
280 static const VMStateDescription vmstate_ipi_core = {
281 .name = "ipi-single",
282 .version_id = 2,
283 .minimum_version_id = 2,
284 .fields = (const VMStateField[]) {
285 VMSTATE_UINT32(status, IPICore),
286 VMSTATE_UINT32(en, IPICore),
287 VMSTATE_UINT32(set, IPICore),
288 VMSTATE_UINT32(clear, IPICore),
289 VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2),
290 VMSTATE_END_OF_LIST()
291 }
292 };
293
294 static const VMStateDescription vmstate_loongson_ipi_common = {
295 .name = "loongson_ipi",
296 .version_id = 2,
297 .minimum_version_id = 2,
298 .fields = (const VMStateField[]) {
299 VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongsonIPICommonState,
300 num_cpu, vmstate_ipi_core,
301 IPICore),
302 VMSTATE_END_OF_LIST()
303 }
304 };
305
loongson_ipi_common_class_init(ObjectClass * klass,const void * data)306 static void loongson_ipi_common_class_init(ObjectClass *klass, const void *data)
307 {
308 DeviceClass *dc = DEVICE_CLASS(klass);
309 LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass);
310
311 device_class_set_parent_realize(dc, loongson_ipi_common_realize,
312 &licc->parent_realize);
313 device_class_set_parent_unrealize(dc, loongson_ipi_common_unrealize,
314 &licc->parent_unrealize);
315 dc->vmsd = &vmstate_loongson_ipi_common;
316 }
317
318 static const TypeInfo loongarch_ipi_common_types[] = {
319 {
320 .name = TYPE_LOONGSON_IPI_COMMON,
321 .parent = TYPE_SYS_BUS_DEVICE,
322 .instance_size = sizeof(LoongsonIPICommonState),
323 .class_size = sizeof(LoongsonIPICommonClass),
324 .class_init = loongson_ipi_common_class_init,
325 .abstract = true,
326 }
327 };
328
329 DEFINE_TYPES(loongarch_ipi_common_types)
330