xref: /qemu/hw/intc/loongarch_ipi.c (revision fdd6ee0b7653e5b195e0b351fb53bb1e6d4c84bd)
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3  * LoongArch ipi interrupt 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/loongarch_ipi.h"
11 #include "hw/irq.h"
12 #include "qapi/error.h"
13 #include "qemu/log.h"
14 #include "exec/address-spaces.h"
15 #include "hw/loongarch/virt.h"
16 #include "migration/vmstate.h"
17 #include "target/loongarch/internals.h"
18 #include "trace.h"
19 
20 static MemTxResult loongarch_ipi_readl(void *opaque, hwaddr addr,
21                                        uint64_t *data,
22                                        unsigned size, MemTxAttrs attrs)
23 {
24     IPICore *s;
25     LoongArchIPI *ipi = opaque;
26     uint64_t ret = 0;
27     int index = 0;
28 
29     s = &ipi->ipi_core;
30     addr &= 0xff;
31     switch (addr) {
32     case CORE_STATUS_OFF:
33         ret = s->status;
34         break;
35     case CORE_EN_OFF:
36         ret = s->en;
37         break;
38     case CORE_SET_OFF:
39         ret = 0;
40         break;
41     case CORE_CLEAR_OFF:
42         ret = 0;
43         break;
44     case CORE_BUF_20 ... CORE_BUF_38 + 4:
45         index = (addr - CORE_BUF_20) >> 2;
46         ret = s->buf[index];
47         break;
48     default:
49         qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr);
50         break;
51     }
52 
53     trace_loongarch_ipi_read(size, (uint64_t)addr, ret);
54     *data = ret;
55     return MEMTX_OK;
56 }
57 
58 static void send_ipi_data(CPULoongArchState *env, uint64_t val, hwaddr addr,
59                           MemTxAttrs attrs)
60 {
61     int i, mask = 0, data = 0;
62 
63     /*
64      * bit 27-30 is mask for byte writing,
65      * if the mask is 0, we need not to do anything.
66      */
67     if ((val >> 27) & 0xf) {
68         data = address_space_ldl(&env->address_space_iocsr, addr,
69                                  attrs, NULL);
70         for (i = 0; i < 4; i++) {
71             /* get mask for byte writing */
72             if (val & (0x1 << (27 + i))) {
73                 mask |= 0xff << (i * 8);
74             }
75         }
76     }
77 
78     data &= mask;
79     data |= (val >> 32) & ~mask;
80     address_space_stl(&env->address_space_iocsr, addr,
81                       data, attrs, NULL);
82 }
83 
84 static int archid_cmp(const void *a, const void *b)
85 {
86    CPUArchId *archid_a = (CPUArchId *)a;
87    CPUArchId *archid_b = (CPUArchId *)b;
88 
89    return archid_a->arch_id - archid_b->arch_id;
90 }
91 
92 static CPUArchId *find_cpu_by_archid(MachineState *ms, uint32_t id)
93 {
94     CPUArchId apic_id, *found_cpu;
95 
96     apic_id.arch_id = id;
97     found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus,
98         ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus),
99         archid_cmp);
100 
101     return found_cpu;
102 }
103 
104 static CPUState *ipi_getcpu(int arch_id)
105 {
106     MachineState *machine = MACHINE(qdev_get_machine());
107     CPUArchId *archid;
108 
109     archid = find_cpu_by_archid(machine, arch_id);
110     if (archid) {
111         return CPU(archid->cpu);
112     }
113 
114     return NULL;
115 }
116 
117 static MemTxResult mail_send(uint64_t val, MemTxAttrs attrs)
118 {
119     uint32_t cpuid;
120     hwaddr addr;
121     CPUState *cs;
122 
123     cpuid = extract32(val, 16, 10);
124     if (cpuid >= LOONGARCH_MAX_CPUS) {
125         trace_loongarch_ipi_unsupported_cpuid("IOCSR_MAIL_SEND", cpuid);
126         return MEMTX_DECODE_ERROR;
127     }
128 
129     cs = ipi_getcpu(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     send_ipi_data(&LOONGARCH_CPU(cs)->env, val, addr, attrs);
138     return MEMTX_OK;
139 }
140 
141 static MemTxResult any_send(uint64_t val, MemTxAttrs attrs)
142 {
143     uint32_t cpuid;
144     hwaddr addr;
145     CPUState *cs;
146 
147     cpuid = extract32(val, 16, 10);
148     if (cpuid >= LOONGARCH_MAX_CPUS) {
149         trace_loongarch_ipi_unsupported_cpuid("IOCSR_ANY_SEND", cpuid);
150         return MEMTX_DECODE_ERROR;
151     }
152 
153     cs = ipi_getcpu(cpuid);
154     if (cs == NULL) {
155         return MEMTX_DECODE_ERROR;
156     }
157 
158     /* override requester_id */
159     addr = val & 0xffff;
160     attrs.requester_id = cs->cpu_index;
161     send_ipi_data(&LOONGARCH_CPU(cs)->env, val, addr, attrs);
162     return MEMTX_OK;
163 }
164 
165 static MemTxResult loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val,
166                                         unsigned size, MemTxAttrs attrs)
167 {
168     LoongArchIPI *ipi = opaque;
169     IPICore *s;
170     int index = 0;
171     uint32_t cpuid;
172     uint8_t vector;
173     CPUState *cs;
174 
175     s = &ipi->ipi_core;
176     addr &= 0xff;
177     trace_loongarch_ipi_write(size, (uint64_t)addr, val);
178     switch (addr) {
179     case CORE_STATUS_OFF:
180         qemu_log_mask(LOG_GUEST_ERROR, "can not be written");
181         break;
182     case CORE_EN_OFF:
183         s->en = val;
184         break;
185     case CORE_SET_OFF:
186         s->status |= val;
187         if (s->status != 0 && (s->status & s->en) != 0) {
188             qemu_irq_raise(s->irq);
189         }
190         break;
191     case CORE_CLEAR_OFF:
192         s->status &= ~val;
193         if (s->status == 0 && s->en != 0) {
194             qemu_irq_lower(s->irq);
195         }
196         break;
197     case CORE_BUF_20 ... CORE_BUF_38 + 4:
198         index = (addr - CORE_BUF_20) >> 2;
199         s->buf[index] = val;
200         break;
201     case IOCSR_IPI_SEND:
202         cpuid = extract32(val, 16, 10);
203         if (cpuid >= LOONGARCH_MAX_CPUS) {
204             trace_loongarch_ipi_unsupported_cpuid("IOCSR_IPI_SEND", cpuid);
205             return MEMTX_DECODE_ERROR;
206         }
207 
208         /* IPI status vector */
209         vector = extract8(val, 0, 5);
210         cs = ipi_getcpu(cpuid);
211         if (cs == NULL) {
212             return MEMTX_DECODE_ERROR;
213         }
214 
215         /* override requester_id */
216         attrs.requester_id = cs->cpu_index;
217         ipi = LOONGARCH_IPI(LOONGARCH_CPU(cs)->env.ipistate);
218         loongarch_ipi_writel(ipi, CORE_SET_OFF, BIT(vector), 4, attrs);
219         break;
220     default:
221         qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr);
222         break;
223     }
224 
225     return MEMTX_OK;
226 }
227 
228 static const MemoryRegionOps loongarch_ipi_ops = {
229     .read_with_attrs = loongarch_ipi_readl,
230     .write_with_attrs = loongarch_ipi_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 /* mail send and any send only support writeq */
239 static MemTxResult loongarch_ipi_writeq(void *opaque, hwaddr addr, uint64_t val,
240                                         unsigned size, MemTxAttrs attrs)
241 {
242     MemTxResult ret = MEMTX_OK;
243 
244     addr &= 0xfff;
245     switch (addr) {
246     case MAIL_SEND_OFFSET:
247         ret = mail_send(val, attrs);
248         break;
249     case ANY_SEND_OFFSET:
250         ret = any_send(val, attrs);
251         break;
252     default:
253        break;
254     }
255 
256     return ret;
257 }
258 
259 static const MemoryRegionOps loongarch_ipi64_ops = {
260     .write_with_attrs = loongarch_ipi_writeq,
261     .impl.min_access_size = 8,
262     .impl.max_access_size = 8,
263     .valid.min_access_size = 8,
264     .valid.max_access_size = 8,
265     .endianness = DEVICE_LITTLE_ENDIAN,
266 };
267 
268 static void loongarch_ipi_init(Object *obj)
269 {
270     LoongArchIPI *s = LOONGARCH_IPI(obj);
271     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
272 
273     memory_region_init_io(&s->ipi_iocsr_mem, obj, &loongarch_ipi_ops,
274                           s, "loongarch_ipi_iocsr", 0x48);
275 
276     /* loongarch_ipi_iocsr performs re-entrant IO through ipi_send */
277     s->ipi_iocsr_mem.disable_reentrancy_guard = true;
278 
279     sysbus_init_mmio(sbd, &s->ipi_iocsr_mem);
280 
281     memory_region_init_io(&s->ipi64_iocsr_mem, obj, &loongarch_ipi64_ops,
282                           s, "loongarch_ipi64_iocsr", 0x118);
283     sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem);
284     qdev_init_gpio_out(DEVICE(obj), &s->ipi_core.irq, 1);
285 }
286 
287 static const VMStateDescription vmstate_ipi_core = {
288     .name = "ipi-single",
289     .version_id = 2,
290     .minimum_version_id = 2,
291     .fields = (const VMStateField[]) {
292         VMSTATE_UINT32(status, IPICore),
293         VMSTATE_UINT32(en, IPICore),
294         VMSTATE_UINT32(set, IPICore),
295         VMSTATE_UINT32(clear, IPICore),
296         VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2),
297         VMSTATE_END_OF_LIST()
298     }
299 };
300 
301 static const VMStateDescription vmstate_loongarch_ipi = {
302     .name = TYPE_LOONGARCH_IPI,
303     .version_id = 1,
304     .minimum_version_id = 1,
305     .fields = (const VMStateField[]) {
306         VMSTATE_STRUCT(ipi_core, LoongArchIPI, 0, vmstate_ipi_core, IPICore),
307         VMSTATE_END_OF_LIST()
308     }
309 };
310 
311 static void loongarch_ipi_class_init(ObjectClass *klass, void *data)
312 {
313     DeviceClass *dc = DEVICE_CLASS(klass);
314 
315     dc->vmsd = &vmstate_loongarch_ipi;
316 }
317 
318 static const TypeInfo loongarch_ipi_info = {
319     .name          = TYPE_LOONGARCH_IPI,
320     .parent        = TYPE_SYS_BUS_DEVICE,
321     .instance_size = sizeof(LoongArchIPI),
322     .instance_init = loongarch_ipi_init,
323     .class_init    = loongarch_ipi_class_init,
324 };
325 
326 static void loongarch_ipi_register_types(void)
327 {
328     type_register_static(&loongarch_ipi_info);
329 }
330 
331 type_init(loongarch_ipi_register_types)
332