xref: /qemu/hw/intc/loongson_ipi.c (revision 13e8ec6c)
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3  * Loongson 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/loongson_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 "exec/memory.h"
18 #include "migration/vmstate.h"
19 #ifdef TARGET_LOONGARCH64
20 #include "target/loongarch/cpu.h"
21 #endif
22 #ifdef TARGET_MIPS
23 #include "target/mips/cpu.h"
24 #endif
25 #include "trace.h"
26 
loongson_ipi_core_readl(void * opaque,hwaddr addr,uint64_t * data,unsigned size,MemTxAttrs attrs)27 static MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr,
28                                            uint64_t *data,
29                                            unsigned size, MemTxAttrs attrs)
30 {
31     IPICore *s = opaque;
32     uint64_t ret = 0;
33     int index = 0;
34 
35     addr &= 0xff;
36     switch (addr) {
37     case CORE_STATUS_OFF:
38         ret = s->status;
39         break;
40     case CORE_EN_OFF:
41         ret = s->en;
42         break;
43     case CORE_SET_OFF:
44         ret = 0;
45         break;
46     case CORE_CLEAR_OFF:
47         ret = 0;
48         break;
49     case CORE_BUF_20 ... CORE_BUF_38 + 4:
50         index = (addr - CORE_BUF_20) >> 2;
51         ret = s->buf[index];
52         break;
53     default:
54         qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr);
55         break;
56     }
57 
58     trace_loongson_ipi_read(size, (uint64_t)addr, ret);
59     *data = ret;
60     return MEMTX_OK;
61 }
62 
loongson_ipi_iocsr_readl(void * opaque,hwaddr addr,uint64_t * data,unsigned size,MemTxAttrs attrs)63 static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr,
64                                             uint64_t *data,
65                                             unsigned size, MemTxAttrs attrs)
66 {
67     LoongsonIPI *ipi = opaque;
68     IPICore *s;
69 
70     if (attrs.requester_id >= ipi->num_cpu) {
71         return MEMTX_DECODE_ERROR;
72     }
73 
74     s = &ipi->cpu[attrs.requester_id];
75     return loongson_ipi_core_readl(s, addr, data, size, attrs);
76 }
77 
get_cpu_iocsr_as(CPUState * cpu)78 static AddressSpace *get_cpu_iocsr_as(CPUState *cpu)
79 {
80 #ifdef TARGET_LOONGARCH64
81     return LOONGARCH_CPU(cpu)->env.address_space_iocsr;
82 #endif
83 #ifdef TARGET_MIPS
84     if (ase_lcsr_available(&MIPS_CPU(cpu)->env)) {
85         return &MIPS_CPU(cpu)->env.iocsr.as;
86     }
87 #endif
88     return NULL;
89 }
90 
send_ipi_data(CPUState * cpu,uint64_t val,hwaddr addr,MemTxAttrs attrs)91 static MemTxResult send_ipi_data(CPUState *cpu, uint64_t val, hwaddr addr,
92                           MemTxAttrs attrs)
93 {
94     int i, mask = 0, data = 0;
95     AddressSpace *iocsr_as = get_cpu_iocsr_as(cpu);
96 
97     if (!iocsr_as) {
98         return MEMTX_DECODE_ERROR;
99     }
100 
101     /*
102      * bit 27-30 is mask for byte writing,
103      * if the mask is 0, we need not to do anything.
104      */
105     if ((val >> 27) & 0xf) {
106         data = address_space_ldl_le(iocsr_as, addr, attrs, NULL);
107         for (i = 0; i < 4; i++) {
108             /* get mask for byte writing */
109             if (val & (0x1 << (27 + i))) {
110                 mask |= 0xff << (i * 8);
111             }
112         }
113     }
114 
115     data &= mask;
116     data |= (val >> 32) & ~mask;
117     address_space_stl_le(iocsr_as, addr, data, attrs, NULL);
118 
119     return MEMTX_OK;
120 }
121 
mail_send(uint64_t val,MemTxAttrs attrs)122 static MemTxResult mail_send(uint64_t val, MemTxAttrs attrs)
123 {
124     uint32_t cpuid;
125     hwaddr addr;
126     CPUState *cs;
127 
128     cpuid = extract32(val, 16, 10);
129     cs = cpu_by_arch_id(cpuid);
130     if (cs == NULL) {
131         return MEMTX_DECODE_ERROR;
132     }
133 
134     /* override requester_id */
135     addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c);
136     attrs.requester_id = cs->cpu_index;
137     return send_ipi_data(cs, val, addr, attrs);
138 }
139 
any_send(uint64_t val,MemTxAttrs attrs)140 static MemTxResult any_send(uint64_t val, MemTxAttrs attrs)
141 {
142     uint32_t cpuid;
143     hwaddr addr;
144     CPUState *cs;
145 
146     cpuid = extract32(val, 16, 10);
147     cs = cpu_by_arch_id(cpuid);
148     if (cs == NULL) {
149         return MEMTX_DECODE_ERROR;
150     }
151 
152     /* override requester_id */
153     addr = val & 0xffff;
154     attrs.requester_id = cs->cpu_index;
155     return send_ipi_data(cs, val, addr, attrs);
156 }
157 
loongson_ipi_core_writel(void * opaque,hwaddr addr,uint64_t val,unsigned size,MemTxAttrs attrs)158 static MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr,
159                                             uint64_t val, unsigned size,
160                                             MemTxAttrs attrs)
161 {
162     IPICore *s = opaque;
163     LoongsonIPI *ipi = s->ipi;
164     int index = 0;
165     uint32_t cpuid;
166     uint8_t vector;
167     CPUState *cs;
168 
169     addr &= 0xff;
170     trace_loongson_ipi_write(size, (uint64_t)addr, val);
171     switch (addr) {
172     case CORE_STATUS_OFF:
173         qemu_log_mask(LOG_GUEST_ERROR, "can not be written");
174         break;
175     case CORE_EN_OFF:
176         s->en = val;
177         break;
178     case CORE_SET_OFF:
179         s->status |= val;
180         if (s->status != 0 && (s->status & s->en) != 0) {
181             qemu_irq_raise(s->irq);
182         }
183         break;
184     case CORE_CLEAR_OFF:
185         s->status &= ~val;
186         if (s->status == 0 && s->en != 0) {
187             qemu_irq_lower(s->irq);
188         }
189         break;
190     case CORE_BUF_20 ... CORE_BUF_38 + 4:
191         index = (addr - CORE_BUF_20) >> 2;
192         s->buf[index] = val;
193         break;
194     case IOCSR_IPI_SEND:
195         cpuid = extract32(val, 16, 10);
196         /* IPI status vector */
197         vector = extract8(val, 0, 5);
198         cs = cpu_by_arch_id(cpuid);
199         if (cs == NULL || cs->cpu_index >= ipi->num_cpu) {
200             return MEMTX_DECODE_ERROR;
201         }
202         loongson_ipi_core_writel(&ipi->cpu[cs->cpu_index], CORE_SET_OFF,
203                                  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 
loongson_ipi_iocsr_writel(void * opaque,hwaddr addr,uint64_t val,unsigned size,MemTxAttrs attrs)213 static MemTxResult loongson_ipi_iocsr_writel(void *opaque, hwaddr addr,
214                                             uint64_t val, unsigned size,
215                                             MemTxAttrs attrs)
216 {
217     LoongsonIPI *ipi = opaque;
218     IPICore *s;
219 
220     if (attrs.requester_id >= ipi->num_cpu) {
221         return MEMTX_DECODE_ERROR;
222     }
223 
224     s = &ipi->cpu[attrs.requester_id];
225     return loongson_ipi_core_writel(s, addr, val, size, attrs);
226 }
227 
228 static const MemoryRegionOps loongson_ipi_core_ops = {
229     .read_with_attrs = loongson_ipi_core_readl,
230     .write_with_attrs = loongson_ipi_core_writel,
231     .impl.min_access_size = 4,
232     .impl.max_access_size = 4,
233     .valid.min_access_size = 4,
234     .valid.max_access_size = 8,
235     .endianness = DEVICE_LITTLE_ENDIAN,
236 };
237 
238 static const MemoryRegionOps loongson_ipi_iocsr_ops = {
239     .read_with_attrs = loongson_ipi_iocsr_readl,
240     .write_with_attrs = loongson_ipi_iocsr_writel,
241     .impl.min_access_size = 4,
242     .impl.max_access_size = 4,
243     .valid.min_access_size = 4,
244     .valid.max_access_size = 8,
245     .endianness = DEVICE_LITTLE_ENDIAN,
246 };
247 
248 /* mail send and any send only support writeq */
loongson_ipi_writeq(void * opaque,hwaddr addr,uint64_t val,unsigned size,MemTxAttrs attrs)249 static MemTxResult loongson_ipi_writeq(void *opaque, hwaddr addr, uint64_t val,
250                                         unsigned size, MemTxAttrs attrs)
251 {
252     MemTxResult ret = MEMTX_OK;
253 
254     addr &= 0xfff;
255     switch (addr) {
256     case MAIL_SEND_OFFSET:
257         ret = mail_send(val, attrs);
258         break;
259     case ANY_SEND_OFFSET:
260         ret = any_send(val, attrs);
261         break;
262     default:
263        break;
264     }
265 
266     return ret;
267 }
268 
269 static const MemoryRegionOps loongson_ipi64_ops = {
270     .write_with_attrs = loongson_ipi_writeq,
271     .impl.min_access_size = 8,
272     .impl.max_access_size = 8,
273     .valid.min_access_size = 8,
274     .valid.max_access_size = 8,
275     .endianness = DEVICE_LITTLE_ENDIAN,
276 };
277 
loongson_ipi_realize(DeviceState * dev,Error ** errp)278 static void loongson_ipi_realize(DeviceState *dev, Error **errp)
279 {
280     LoongsonIPI *s = LOONGSON_IPI(dev);
281     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
282     int i;
283 
284     if (s->num_cpu == 0) {
285         error_setg(errp, "num-cpu must be at least 1");
286         return;
287     }
288 
289     memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev),
290                           &loongson_ipi_iocsr_ops,
291                           s, "loongson_ipi_iocsr", 0x48);
292 
293     /* loongson_ipi_iocsr performs re-entrant IO through ipi_send */
294     s->ipi_iocsr_mem.disable_reentrancy_guard = true;
295 
296     sysbus_init_mmio(sbd, &s->ipi_iocsr_mem);
297 
298     memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev),
299                           &loongson_ipi64_ops,
300                           s, "loongson_ipi64_iocsr", 0x118);
301     sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem);
302 
303     s->cpu = g_new0(IPICore, s->num_cpu);
304     if (s->cpu == NULL) {
305         error_setg(errp, "Memory allocation for IPICore faile");
306         return;
307     }
308 
309     for (i = 0; i < s->num_cpu; i++) {
310         s->cpu[i].ipi = s;
311         s->cpu[i].ipi_mmio_mem = g_new0(MemoryRegion, 1);
312         g_autofree char *name = g_strdup_printf("loongson_ipi_cpu%d_mmio", i);
313         memory_region_init_io(s->cpu[i].ipi_mmio_mem, OBJECT(dev),
314                               &loongson_ipi_core_ops, &s->cpu[i], name, 0x48);
315         sysbus_init_mmio(sbd, s->cpu[i].ipi_mmio_mem);
316 
317         qdev_init_gpio_out(dev, &s->cpu[i].irq, 1);
318     }
319 }
320 
loongson_ipi_unrealize(DeviceState * dev)321 static void loongson_ipi_unrealize(DeviceState *dev)
322 {
323     LoongsonIPI *s = LOONGSON_IPI(dev);
324 
325     g_free(s->cpu);
326 }
327 
328 static const VMStateDescription vmstate_ipi_core = {
329     .name = "ipi-single",
330     .version_id = 2,
331     .minimum_version_id = 2,
332     .fields = (const VMStateField[]) {
333         VMSTATE_UINT32(status, IPICore),
334         VMSTATE_UINT32(en, IPICore),
335         VMSTATE_UINT32(set, IPICore),
336         VMSTATE_UINT32(clear, IPICore),
337         VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2),
338         VMSTATE_END_OF_LIST()
339     }
340 };
341 
342 static const VMStateDescription vmstate_loongson_ipi = {
343     .name = TYPE_LOONGSON_IPI,
344     .version_id = 2,
345     .minimum_version_id = 2,
346     .fields = (const VMStateField[]) {
347         VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongsonIPI, num_cpu,
348                          vmstate_ipi_core, IPICore),
349         VMSTATE_END_OF_LIST()
350     }
351 };
352 
353 static Property ipi_properties[] = {
354     DEFINE_PROP_UINT32("num-cpu", LoongsonIPI, num_cpu, 1),
355     DEFINE_PROP_END_OF_LIST(),
356 };
357 
loongson_ipi_class_init(ObjectClass * klass,void * data)358 static void loongson_ipi_class_init(ObjectClass *klass, void *data)
359 {
360     DeviceClass *dc = DEVICE_CLASS(klass);
361 
362     dc->realize = loongson_ipi_realize;
363     dc->unrealize = loongson_ipi_unrealize;
364     device_class_set_props(dc, ipi_properties);
365     dc->vmsd = &vmstate_loongson_ipi;
366 }
367 
368 static const TypeInfo loongson_ipi_types[] = {
369     {
370         .name               = TYPE_LOONGSON_IPI,
371         .parent             = TYPE_SYS_BUS_DEVICE,
372         .instance_size      = sizeof(LoongsonIPI),
373         .class_init         = loongson_ipi_class_init,
374     }
375 };
376 
377 DEFINE_TYPES(loongson_ipi_types)
378