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