xref: /openbmc/qemu/hw/intc/loongarch_ipi.c (revision 412f655566bfadfe85d6f52a7e4420b418f261c3)
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3  * LoongArch IPI interrupt support
4  *
5  * Copyright (C) 2024 Loongson Technology Corporation Limited
6  */
7 
8 #include "qemu/osdep.h"
9 #include "qemu/error-report.h"
10 #include "hw/boards.h"
11 #include "qapi/error.h"
12 #include "hw/intc/loongarch_ipi.h"
13 #include "hw/qdev-properties.h"
14 #include "system/kvm.h"
15 #include "target/loongarch/cpu.h"
16 
17 static AddressSpace *get_iocsr_as(CPUState *cpu)
18 {
19     return LOONGARCH_CPU(cpu)->env.address_space_iocsr;
20 }
21 
22 static int loongarch_ipi_cmp(const void *a, const void *b)
23 {
24    IPICore *ipi_a = (IPICore *)a;
25    IPICore *ipi_b = (IPICore *)b;
26 
27    return ipi_a->arch_id - ipi_b->arch_id;
28 }
29 
30 static int loongarch_cpu_by_arch_id(LoongsonIPICommonState *lics,
31                                     int64_t arch_id, int *index, CPUState **pcs)
32 {
33     IPICore ipi, *found;
34 
35     ipi.arch_id = arch_id;
36     found = bsearch(&ipi, lics->cpu, lics->num_cpu, sizeof(IPICore),
37                     loongarch_ipi_cmp);
38     if (found && found->cpu) {
39         if (index) {
40             *index = found - lics->cpu;
41         }
42 
43         if (pcs) {
44             *pcs = found->cpu;
45         }
46 
47         return MEMTX_OK;
48     }
49 
50     return MEMTX_ERROR;
51 }
52 
53 static IPICore *loongarch_ipi_get_cpu(LoongsonIPICommonState *lics,
54                                       DeviceState *dev)
55 {
56     CPUClass *k = CPU_GET_CLASS(dev);
57     uint64_t arch_id = k->get_arch_id(CPU(dev));
58     int i;
59 
60     for (i = 0; i < lics->num_cpu; i++) {
61         if (lics->cpu[i].arch_id == arch_id) {
62             return &lics->cpu[i];
63         }
64     }
65 
66     return NULL;
67 }
68 
69 static void loongarch_ipi_realize(DeviceState *dev, Error **errp)
70 {
71     LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(dev);
72     LoongarchIPIClass *lic = LOONGARCH_IPI_GET_CLASS(dev);
73     MachineState *machine = MACHINE(qdev_get_machine());
74     MachineClass *mc = MACHINE_GET_CLASS(machine);
75     const CPUArchIdList *id_list;
76     Error *local_err = NULL;
77     int i;
78 
79     lic->parent_realize(dev, &local_err);
80     if (local_err) {
81         error_propagate(errp, local_err);
82         return;
83     }
84 
85     assert(mc->possible_cpu_arch_ids);
86     id_list = mc->possible_cpu_arch_ids(machine);
87     lics->num_cpu = id_list->len;
88     lics->cpu = g_new0(IPICore, lics->num_cpu);
89     for (i = 0; i < lics->num_cpu; i++) {
90         lics->cpu[i].arch_id = id_list->cpus[i].arch_id;
91         lics->cpu[i].cpu = CPU(id_list->cpus[i].cpu);
92         lics->cpu[i].ipi = lics;
93         qdev_init_gpio_out(dev, &lics->cpu[i].irq, 1);
94     }
95 
96     if (kvm_irqchip_in_kernel()) {
97         kvm_ipi_realize(dev, errp);
98     }
99 }
100 
101 static void loongarch_ipi_reset_hold(Object *obj, ResetType type)
102 {
103     int i;
104     LoongarchIPIClass *lic = LOONGARCH_IPI_GET_CLASS(obj);
105     LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(obj);
106     IPICore *core;
107 
108     if (lic->parent_phases.hold) {
109         lic->parent_phases.hold(obj, type);
110     }
111 
112     for (i = 0; i < lics->num_cpu; i++) {
113         core = lics->cpu + i;
114         /* IPI with targeted CPU available however not present */
115         if (!core->cpu) {
116             continue;
117         }
118 
119         core->status = 0;
120         core->en = 0;
121         core->set = 0;
122         core->clear = 0;
123         memset(core->buf, 0, sizeof(core->buf));
124     }
125 }
126 
127 static void loongarch_ipi_cpu_plug(HotplugHandler *hotplug_dev,
128                                    DeviceState *dev, Error **errp)
129 {
130     LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(hotplug_dev);
131     Object *obj = OBJECT(dev);
132     IPICore *core;
133     int index;
134 
135     if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) {
136         warn_report("LoongArch extioi: Invalid %s device type",
137                                        object_get_typename(obj));
138         return;
139     }
140 
141     core = loongarch_ipi_get_cpu(lics, dev);
142     if (!core) {
143         return;
144     }
145 
146     core->cpu = CPU(dev);
147     index = core - lics->cpu;
148 
149     /* connect ipi irq to cpu irq */
150     qdev_connect_gpio_out(DEVICE(lics), index, qdev_get_gpio_in(dev, IRQ_IPI));
151 }
152 
153 static void loongarch_ipi_cpu_unplug(HotplugHandler *hotplug_dev,
154                                      DeviceState *dev, Error **errp)
155 {
156     LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(hotplug_dev);
157     Object *obj = OBJECT(dev);
158     IPICore *core;
159 
160     if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) {
161         warn_report("LoongArch extioi: Invalid %s device type",
162                                        object_get_typename(obj));
163         return;
164     }
165 
166     core = loongarch_ipi_get_cpu(lics, dev);
167     if (!core) {
168         return;
169     }
170 
171     core->cpu = NULL;
172 }
173 
174 static void loongarch_ipi_class_init(ObjectClass *klass, const void *data)
175 {
176     LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass);
177     HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
178     LoongarchIPIClass *lic = LOONGARCH_IPI_CLASS(klass);
179     ResettableClass *rc = RESETTABLE_CLASS(klass);
180     DeviceClass *dc = DEVICE_CLASS(klass);
181 
182     device_class_set_parent_realize(dc, loongarch_ipi_realize,
183                                     &lic->parent_realize);
184     resettable_class_set_parent_phases(rc, NULL, loongarch_ipi_reset_hold,
185                                        NULL, &lic->parent_phases);
186     licc->get_iocsr_as = get_iocsr_as;
187     licc->cpu_by_arch_id = loongarch_cpu_by_arch_id;
188     hc->plug = loongarch_ipi_cpu_plug;
189     hc->unplug = loongarch_ipi_cpu_unplug;
190 }
191 
192 static const TypeInfo loongarch_ipi_types[] = {
193     {
194         .name               = TYPE_LOONGARCH_IPI,
195         .parent             = TYPE_LOONGSON_IPI_COMMON,
196         .instance_size      = sizeof(LoongarchIPIState),
197         .class_size         = sizeof(LoongarchIPIClass),
198         .class_init         = loongarch_ipi_class_init,
199         .interfaces         = (const InterfaceInfo[]) {
200             { TYPE_HOTPLUG_HANDLER },
201             { }
202         },
203     }
204 };
205 
206 DEFINE_TYPES(loongarch_ipi_types)
207