xref: /openbmc/qemu/hw/intc/loongson_ipi.c (revision ed722e0ec4eb275a7710e7af518a0063e75b054f)
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 
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 
63 static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr,
64                                             uint64_t *data,
65                                             unsigned size, MemTxAttrs attrs)
66 {
67     LoongsonIPICommonState *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 
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 
91 static MemTxResult send_ipi_data(LoongsonIPICommonState *ipi, CPUState *cpu,
92                                  uint64_t val, hwaddr addr, 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 
122 static MemTxResult mail_send(LoongsonIPICommonState *ipi,
123                              uint64_t val, MemTxAttrs attrs)
124 {
125     uint32_t cpuid;
126     hwaddr addr;
127     CPUState *cs;
128 
129     cpuid = extract32(val, 16, 10);
130     cs = cpu_by_arch_id(cpuid);
131     if (cs == NULL) {
132         return MEMTX_DECODE_ERROR;
133     }
134 
135     /* override requester_id */
136     addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c);
137     attrs.requester_id = cs->cpu_index;
138     return send_ipi_data(ipi, cs, val, addr, attrs);
139 }
140 
141 static MemTxResult any_send(LoongsonIPICommonState *ipi,
142                             uint64_t val, MemTxAttrs attrs)
143 {
144     uint32_t cpuid;
145     hwaddr addr;
146     CPUState *cs;
147 
148     cpuid = extract32(val, 16, 10);
149     cs = cpu_by_arch_id(cpuid);
150     if (cs == NULL) {
151         return MEMTX_DECODE_ERROR;
152     }
153 
154     /* override requester_id */
155     addr = val & 0xffff;
156     attrs.requester_id = cs->cpu_index;
157     return send_ipi_data(ipi, cs, val, addr, attrs);
158 }
159 
160 static MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr,
161                                             uint64_t val, unsigned size,
162                                             MemTxAttrs attrs)
163 {
164     IPICore *s = opaque;
165     LoongsonIPICommonState *ipi = s->ipi;
166     int index = 0;
167     uint32_t cpuid;
168     uint8_t vector;
169     CPUState *cs;
170 
171     addr &= 0xff;
172     trace_loongson_ipi_write(size, (uint64_t)addr, val);
173     switch (addr) {
174     case CORE_STATUS_OFF:
175         qemu_log_mask(LOG_GUEST_ERROR, "can not be written");
176         break;
177     case CORE_EN_OFF:
178         s->en = val;
179         break;
180     case CORE_SET_OFF:
181         s->status |= val;
182         if (s->status != 0 && (s->status & s->en) != 0) {
183             qemu_irq_raise(s->irq);
184         }
185         break;
186     case CORE_CLEAR_OFF:
187         s->status &= ~val;
188         if (s->status == 0 && s->en != 0) {
189             qemu_irq_lower(s->irq);
190         }
191         break;
192     case CORE_BUF_20 ... CORE_BUF_38 + 4:
193         index = (addr - CORE_BUF_20) >> 2;
194         s->buf[index] = val;
195         break;
196     case IOCSR_IPI_SEND:
197         cpuid = extract32(val, 16, 10);
198         /* IPI status vector */
199         vector = extract8(val, 0, 5);
200         cs = cpu_by_arch_id(cpuid);
201         if (cs == NULL || cs->cpu_index >= ipi->num_cpu) {
202             return MEMTX_DECODE_ERROR;
203         }
204         loongson_ipi_core_writel(&ipi->cpu[cs->cpu_index], CORE_SET_OFF,
205                                  BIT(vector), 4, attrs);
206         break;
207     default:
208         qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr);
209         break;
210     }
211 
212     return MEMTX_OK;
213 }
214 
215 static MemTxResult loongson_ipi_iocsr_writel(void *opaque, hwaddr addr,
216                                             uint64_t val, unsigned size,
217                                             MemTxAttrs attrs)
218 {
219     LoongsonIPICommonState *ipi = opaque;
220     IPICore *s;
221 
222     if (attrs.requester_id >= ipi->num_cpu) {
223         return MEMTX_DECODE_ERROR;
224     }
225 
226     s = &ipi->cpu[attrs.requester_id];
227     return loongson_ipi_core_writel(s, addr, val, size, attrs);
228 }
229 
230 static const MemoryRegionOps loongson_ipi_core_ops = {
231     .read_with_attrs = loongson_ipi_core_readl,
232     .write_with_attrs = loongson_ipi_core_writel,
233     .impl.min_access_size = 4,
234     .impl.max_access_size = 4,
235     .valid.min_access_size = 4,
236     .valid.max_access_size = 8,
237     .endianness = DEVICE_LITTLE_ENDIAN,
238 };
239 
240 static const MemoryRegionOps loongson_ipi_iocsr_ops = {
241     .read_with_attrs = loongson_ipi_iocsr_readl,
242     .write_with_attrs = loongson_ipi_iocsr_writel,
243     .impl.min_access_size = 4,
244     .impl.max_access_size = 4,
245     .valid.min_access_size = 4,
246     .valid.max_access_size = 8,
247     .endianness = DEVICE_LITTLE_ENDIAN,
248 };
249 
250 /* mail send and any send only support writeq */
251 static MemTxResult loongson_ipi_writeq(void *opaque, hwaddr addr, uint64_t val,
252                                         unsigned size, MemTxAttrs attrs)
253 {
254     LoongsonIPICommonState *ipi = opaque;
255     MemTxResult ret = MEMTX_OK;
256 
257     addr &= 0xfff;
258     switch (addr) {
259     case MAIL_SEND_OFFSET:
260         ret = mail_send(ipi, val, attrs);
261         break;
262     case ANY_SEND_OFFSET:
263         ret = any_send(ipi, val, attrs);
264         break;
265     default:
266        break;
267     }
268 
269     return ret;
270 }
271 
272 static const MemoryRegionOps loongson_ipi64_ops = {
273     .write_with_attrs = loongson_ipi_writeq,
274     .impl.min_access_size = 8,
275     .impl.max_access_size = 8,
276     .valid.min_access_size = 8,
277     .valid.max_access_size = 8,
278     .endianness = DEVICE_LITTLE_ENDIAN,
279 };
280 
281 static void loongson_ipi_common_realize(DeviceState *dev, Error **errp)
282 {
283     LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev);
284     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
285     int i;
286 
287     if (s->num_cpu == 0) {
288         error_setg(errp, "num-cpu must be at least 1");
289         return;
290     }
291 
292     memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev),
293                           &loongson_ipi_iocsr_ops,
294                           s, "loongson_ipi_iocsr", 0x48);
295 
296     /* loongson_ipi_iocsr performs re-entrant IO through ipi_send */
297     s->ipi_iocsr_mem.disable_reentrancy_guard = true;
298 
299     sysbus_init_mmio(sbd, &s->ipi_iocsr_mem);
300 
301     memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev),
302                           &loongson_ipi64_ops,
303                           s, "loongson_ipi64_iocsr", 0x118);
304     sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem);
305 
306     s->cpu = g_new0(IPICore, s->num_cpu);
307     for (i = 0; i < s->num_cpu; i++) {
308         s->cpu[i].ipi = s;
309 
310         qdev_init_gpio_out(dev, &s->cpu[i].irq, 1);
311     }
312 }
313 
314 static void loongson_ipi_realize(DeviceState *dev, Error **errp)
315 {
316     LoongsonIPICommonState *sc = LOONGSON_IPI_COMMON(dev);
317     LoongsonIPIState *s = LOONGSON_IPI(dev);
318     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
319     Error *local_err = NULL;
320 
321     loongson_ipi_common_realize(dev, &local_err);
322     if (local_err) {
323         error_propagate(errp, local_err);
324         return;
325     }
326 
327     s->ipi_mmio_mem = g_new0(MemoryRegion, sc->num_cpu);
328     for (unsigned i = 0; i < sc->num_cpu; i++) {
329         g_autofree char *name = g_strdup_printf("loongson_ipi_cpu%d_mmio", i);
330 
331         memory_region_init_io(&s->ipi_mmio_mem[i], OBJECT(dev),
332                               &loongson_ipi_core_ops, &sc->cpu[i], name, 0x48);
333         sysbus_init_mmio(sbd, &s->ipi_mmio_mem[i]);
334     }
335 }
336 
337 static void loongson_ipi_common_unrealize(DeviceState *dev)
338 {
339     LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev);
340 
341     g_free(s->cpu);
342 }
343 
344 static void loongson_ipi_unrealize(DeviceState *dev)
345 {
346     LoongsonIPIState *s = LOONGSON_IPI(dev);
347 
348     g_free(s->ipi_mmio_mem);
349 
350     loongson_ipi_common_unrealize(dev);
351 }
352 
353 static void loongson_ipi_class_init(ObjectClass *klass, void *data)
354 {
355     DeviceClass *dc = DEVICE_CLASS(klass);
356     LoongsonIPIClass *lic = LOONGSON_IPI_CLASS(klass);
357 
358     device_class_set_parent_realize(dc, loongson_ipi_realize,
359                                     &lic->parent_realize);
360     device_class_set_parent_unrealize(dc, loongson_ipi_unrealize,
361                                       &lic->parent_unrealize);
362 }
363 
364 static const TypeInfo loongson_ipi_types[] = {
365     {
366         .name               = TYPE_LOONGSON_IPI,
367         .parent             = TYPE_LOONGSON_IPI_COMMON,
368         .instance_size      = sizeof(LoongsonIPIState),
369         .class_size         = sizeof(LoongsonIPIClass),
370         .class_init         = loongson_ipi_class_init,
371     }
372 };
373 
374 DEFINE_TYPES(loongson_ipi_types)
375