1c403d5ffSBibo Mao /* SPDX-License-Identifier: GPL-2.0-or-later */
2c403d5ffSBibo Mao /*
3c403d5ffSBibo Mao * LoongArch IPI interrupt support
4c403d5ffSBibo Mao *
5c403d5ffSBibo Mao * Copyright (C) 2024 Loongson Technology Corporation Limited
6c403d5ffSBibo Mao */
7c403d5ffSBibo Mao
8c403d5ffSBibo Mao #include "qemu/osdep.h"
99d71149aSBibo Mao #include "qemu/error-report.h"
10c403d5ffSBibo Mao #include "hw/boards.h"
1159c54c1cSBibo Mao #include "qapi/error.h"
12c403d5ffSBibo Mao #include "hw/intc/loongarch_ipi.h"
13ce78dacfSBibo Mao #include "hw/qdev-properties.h"
14c403d5ffSBibo Mao #include "target/loongarch/cpu.h"
15c403d5ffSBibo Mao
get_iocsr_as(CPUState * cpu)16c403d5ffSBibo Mao static AddressSpace *get_iocsr_as(CPUState *cpu)
17c403d5ffSBibo Mao {
18c403d5ffSBibo Mao return LOONGARCH_CPU(cpu)->env.address_space_iocsr;
19c403d5ffSBibo Mao }
20c403d5ffSBibo Mao
loongarch_ipi_cmp(const void * a,const void * b)21bb81f237SBibo Mao static int loongarch_ipi_cmp(const void *a, const void *b)
22c403d5ffSBibo Mao {
23bb81f237SBibo Mao IPICore *ipi_a = (IPICore *)a;
24bb81f237SBibo Mao IPICore *ipi_b = (IPICore *)b;
25c403d5ffSBibo Mao
26bb81f237SBibo Mao return ipi_a->arch_id - ipi_b->arch_id;
27c403d5ffSBibo Mao }
28c403d5ffSBibo Mao
loongarch_cpu_by_arch_id(LoongsonIPICommonState * lics,int64_t arch_id,int * index,CPUState ** pcs)29999b112dSBibo Mao static int loongarch_cpu_by_arch_id(LoongsonIPICommonState *lics,
30999b112dSBibo Mao int64_t arch_id, int *index, CPUState **pcs)
31c403d5ffSBibo Mao {
32bb81f237SBibo Mao IPICore ipi, *found;
33c403d5ffSBibo Mao
34bb81f237SBibo Mao ipi.arch_id = arch_id;
35bb81f237SBibo Mao found = bsearch(&ipi, lics->cpu, lics->num_cpu, sizeof(IPICore),
36bb81f237SBibo Mao loongarch_ipi_cmp);
37bb81f237SBibo Mao if (found && found->cpu) {
38999b112dSBibo Mao if (index) {
39bb81f237SBibo Mao *index = found - lics->cpu;
40c403d5ffSBibo Mao }
41c403d5ffSBibo Mao
42999b112dSBibo Mao if (pcs) {
43bb81f237SBibo Mao *pcs = found->cpu;
44999b112dSBibo Mao }
45999b112dSBibo Mao
46999b112dSBibo Mao return MEMTX_OK;
47999b112dSBibo Mao }
48999b112dSBibo Mao
49999b112dSBibo Mao return MEMTX_ERROR;
50c403d5ffSBibo Mao }
51c403d5ffSBibo Mao
loongarch_ipi_get_cpu(LoongsonIPICommonState * lics,DeviceState * dev)5254492213SBibo Mao static IPICore *loongarch_ipi_get_cpu(LoongsonIPICommonState *lics,
5354492213SBibo Mao DeviceState *dev)
5454492213SBibo Mao {
5554492213SBibo Mao CPUClass *k = CPU_GET_CLASS(dev);
5654492213SBibo Mao uint64_t arch_id = k->get_arch_id(CPU(dev));
5754492213SBibo Mao int i;
5854492213SBibo Mao
5954492213SBibo Mao for (i = 0; i < lics->num_cpu; i++) {
6054492213SBibo Mao if (lics->cpu[i].arch_id == arch_id) {
6154492213SBibo Mao return &lics->cpu[i];
6254492213SBibo Mao }
6354492213SBibo Mao }
6454492213SBibo Mao
6554492213SBibo Mao return NULL;
6654492213SBibo Mao }
6754492213SBibo Mao
loongarch_ipi_realize(DeviceState * dev,Error ** errp)6859c54c1cSBibo Mao static void loongarch_ipi_realize(DeviceState *dev, Error **errp)
6959c54c1cSBibo Mao {
705b82177aSBibo Mao LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(dev);
7159c54c1cSBibo Mao LoongarchIPIClass *lic = LOONGARCH_IPI_GET_CLASS(dev);
7214dc02b5SBibo Mao MachineState *machine = MACHINE(qdev_get_machine());
7314dc02b5SBibo Mao MachineClass *mc = MACHINE_GET_CLASS(machine);
7414dc02b5SBibo Mao const CPUArchIdList *id_list;
7559c54c1cSBibo Mao Error *local_err = NULL;
765b82177aSBibo Mao int i;
7759c54c1cSBibo Mao
7859c54c1cSBibo Mao lic->parent_realize(dev, &local_err);
7959c54c1cSBibo Mao if (local_err) {
8059c54c1cSBibo Mao error_propagate(errp, local_err);
8159c54c1cSBibo Mao return;
8259c54c1cSBibo Mao }
835b82177aSBibo Mao
8414dc02b5SBibo Mao assert(mc->possible_cpu_arch_ids);
8514dc02b5SBibo Mao id_list = mc->possible_cpu_arch_ids(machine);
8614dc02b5SBibo Mao lics->num_cpu = id_list->len;
875b82177aSBibo Mao lics->cpu = g_new0(IPICore, lics->num_cpu);
885b82177aSBibo Mao for (i = 0; i < lics->num_cpu; i++) {
8914dc02b5SBibo Mao lics->cpu[i].arch_id = id_list->cpus[i].arch_id;
9014dc02b5SBibo Mao lics->cpu[i].cpu = CPU(id_list->cpus[i].cpu);
915b82177aSBibo Mao lics->cpu[i].ipi = lics;
925b82177aSBibo Mao qdev_init_gpio_out(dev, &lics->cpu[i].irq, 1);
935b82177aSBibo Mao }
9459c54c1cSBibo Mao }
9559c54c1cSBibo Mao
loongarch_ipi_reset_hold(Object * obj,ResetType type)96*36ad84ecSBibo Mao static void loongarch_ipi_reset_hold(Object *obj, ResetType type)
97*36ad84ecSBibo Mao {
98*36ad84ecSBibo Mao int i;
99*36ad84ecSBibo Mao LoongarchIPIClass *lic = LOONGARCH_IPI_GET_CLASS(obj);
100*36ad84ecSBibo Mao LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(obj);
101*36ad84ecSBibo Mao IPICore *core;
102*36ad84ecSBibo Mao
103*36ad84ecSBibo Mao if (lic->parent_phases.hold) {
104*36ad84ecSBibo Mao lic->parent_phases.hold(obj, type);
105*36ad84ecSBibo Mao }
106*36ad84ecSBibo Mao
107*36ad84ecSBibo Mao for (i = 0; i < lics->num_cpu; i++) {
108*36ad84ecSBibo Mao core = lics->cpu + i;
109*36ad84ecSBibo Mao /* IPI with targeted CPU available however not present */
110*36ad84ecSBibo Mao if (!core->cpu) {
111*36ad84ecSBibo Mao continue;
112*36ad84ecSBibo Mao }
113*36ad84ecSBibo Mao
114*36ad84ecSBibo Mao core->status = 0;
115*36ad84ecSBibo Mao core->en = 0;
116*36ad84ecSBibo Mao core->set = 0;
117*36ad84ecSBibo Mao core->clear = 0;
118*36ad84ecSBibo Mao memset(core->buf, 0, sizeof(core->buf));
119*36ad84ecSBibo Mao }
120*36ad84ecSBibo Mao }
121*36ad84ecSBibo Mao
loongarch_ipi_cpu_plug(HotplugHandler * hotplug_dev,DeviceState * dev,Error ** errp)1229d71149aSBibo Mao static void loongarch_ipi_cpu_plug(HotplugHandler *hotplug_dev,
1239d71149aSBibo Mao DeviceState *dev, Error **errp)
1249d71149aSBibo Mao {
12554492213SBibo Mao LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(hotplug_dev);
1269d71149aSBibo Mao Object *obj = OBJECT(dev);
12754492213SBibo Mao IPICore *core;
12854492213SBibo Mao int index;
1299d71149aSBibo Mao
1309d71149aSBibo Mao if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) {
1319d71149aSBibo Mao warn_report("LoongArch extioi: Invalid %s device type",
1329d71149aSBibo Mao object_get_typename(obj));
1339d71149aSBibo Mao return;
1349d71149aSBibo Mao }
13554492213SBibo Mao
13654492213SBibo Mao core = loongarch_ipi_get_cpu(lics, dev);
13754492213SBibo Mao if (!core) {
13854492213SBibo Mao return;
13954492213SBibo Mao }
14054492213SBibo Mao
14154492213SBibo Mao core->cpu = CPU(dev);
14254492213SBibo Mao index = core - lics->cpu;
14354492213SBibo Mao
14454492213SBibo Mao /* connect ipi irq to cpu irq */
14554492213SBibo Mao qdev_connect_gpio_out(DEVICE(lics), index, qdev_get_gpio_in(dev, IRQ_IPI));
1469d71149aSBibo Mao }
1479d71149aSBibo Mao
loongarch_ipi_cpu_unplug(HotplugHandler * hotplug_dev,DeviceState * dev,Error ** errp)1489d71149aSBibo Mao static void loongarch_ipi_cpu_unplug(HotplugHandler *hotplug_dev,
1499d71149aSBibo Mao DeviceState *dev, Error **errp)
1509d71149aSBibo Mao {
15154492213SBibo Mao LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(hotplug_dev);
1529d71149aSBibo Mao Object *obj = OBJECT(dev);
15354492213SBibo Mao IPICore *core;
1549d71149aSBibo Mao
1559d71149aSBibo Mao if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) {
1569d71149aSBibo Mao warn_report("LoongArch extioi: Invalid %s device type",
1579d71149aSBibo Mao object_get_typename(obj));
1589d71149aSBibo Mao return;
1599d71149aSBibo Mao }
16054492213SBibo Mao
16154492213SBibo Mao core = loongarch_ipi_get_cpu(lics, dev);
16254492213SBibo Mao if (!core) {
16354492213SBibo Mao return;
16454492213SBibo Mao }
16554492213SBibo Mao
16654492213SBibo Mao core->cpu = NULL;
1679d71149aSBibo Mao }
1689d71149aSBibo Mao
loongarch_ipi_class_init(ObjectClass * klass,const void * data)16912d1a768SPhilippe Mathieu-Daudé static void loongarch_ipi_class_init(ObjectClass *klass, const void *data)
170c403d5ffSBibo Mao {
171c403d5ffSBibo Mao LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass);
1729d71149aSBibo Mao HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
17359c54c1cSBibo Mao LoongarchIPIClass *lic = LOONGARCH_IPI_CLASS(klass);
174*36ad84ecSBibo Mao ResettableClass *rc = RESETTABLE_CLASS(klass);
17559c54c1cSBibo Mao DeviceClass *dc = DEVICE_CLASS(klass);
176c403d5ffSBibo Mao
17759c54c1cSBibo Mao device_class_set_parent_realize(dc, loongarch_ipi_realize,
17859c54c1cSBibo Mao &lic->parent_realize);
179*36ad84ecSBibo Mao resettable_class_set_parent_phases(rc, NULL, loongarch_ipi_reset_hold,
180*36ad84ecSBibo Mao NULL, &lic->parent_phases);
181c403d5ffSBibo Mao licc->get_iocsr_as = get_iocsr_as;
182c403d5ffSBibo Mao licc->cpu_by_arch_id = loongarch_cpu_by_arch_id;
1839d71149aSBibo Mao hc->plug = loongarch_ipi_cpu_plug;
1849d71149aSBibo Mao hc->unplug = loongarch_ipi_cpu_unplug;
185c403d5ffSBibo Mao }
186c403d5ffSBibo Mao
187c403d5ffSBibo Mao static const TypeInfo loongarch_ipi_types[] = {
188c403d5ffSBibo Mao {
189c403d5ffSBibo Mao .name = TYPE_LOONGARCH_IPI,
190c403d5ffSBibo Mao .parent = TYPE_LOONGSON_IPI_COMMON,
19159c54c1cSBibo Mao .instance_size = sizeof(LoongarchIPIState),
19259c54c1cSBibo Mao .class_size = sizeof(LoongarchIPIClass),
193c403d5ffSBibo Mao .class_init = loongarch_ipi_class_init,
1942cd09e47SPhilippe Mathieu-Daudé .interfaces = (const InterfaceInfo[]) {
1959d71149aSBibo Mao { TYPE_HOTPLUG_HANDLER },
1969d71149aSBibo Mao { }
1979d71149aSBibo Mao },
198c403d5ffSBibo Mao }
199c403d5ffSBibo Mao };
200c403d5ffSBibo Mao
201c403d5ffSBibo Mao DEFINE_TYPES(loongarch_ipi_types)
202