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