xref: /qemu/hw/intc/loongson_ipi_common.c (revision d01d42ccc9510c039b2e4ec49af164e374eab154)
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