xref: /openbmc/qemu/hw/intc/loongarch_ipi.c (revision d01d42ccc9510c039b2e4ec49af164e374eab154)
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 
get_iocsr_as(CPUState * cpu)17 static AddressSpace *get_iocsr_as(CPUState *cpu)
18 {
19     return LOONGARCH_CPU(cpu)->env.address_space_iocsr;
20 }
21 
loongarch_ipi_cmp(const void * a,const void * b)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 
loongarch_cpu_by_arch_id(LoongsonIPICommonState * lics,int64_t arch_id,int * index,CPUState ** pcs)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 
loongarch_ipi_get_cpu(LoongsonIPICommonState * lics,DeviceState * dev)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 
loongarch_ipi_realize(DeviceState * dev,Error ** errp)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 
loongarch_ipi_reset_hold(Object * obj,ResetType type)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     if (kvm_irqchip_in_kernel()) {
127         kvm_ipi_put(obj, 0);
128     }
129 }
130 
loongarch_ipi_cpu_plug(HotplugHandler * hotplug_dev,DeviceState * dev,Error ** errp)131 static void loongarch_ipi_cpu_plug(HotplugHandler *hotplug_dev,
132                                    DeviceState *dev, Error **errp)
133 {
134     LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(hotplug_dev);
135     Object *obj = OBJECT(dev);
136     IPICore *core;
137     int index;
138 
139     if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) {
140         warn_report("LoongArch extioi: Invalid %s device type",
141                                        object_get_typename(obj));
142         return;
143     }
144 
145     core = loongarch_ipi_get_cpu(lics, dev);
146     if (!core) {
147         return;
148     }
149 
150     core->cpu = CPU(dev);
151     index = core - lics->cpu;
152 
153     /* connect ipi irq to cpu irq */
154     qdev_connect_gpio_out(DEVICE(lics), index, qdev_get_gpio_in(dev, IRQ_IPI));
155 }
156 
loongarch_ipi_cpu_unplug(HotplugHandler * hotplug_dev,DeviceState * dev,Error ** errp)157 static void loongarch_ipi_cpu_unplug(HotplugHandler *hotplug_dev,
158                                      DeviceState *dev, Error **errp)
159 {
160     LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(hotplug_dev);
161     Object *obj = OBJECT(dev);
162     IPICore *core;
163 
164     if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) {
165         warn_report("LoongArch extioi: Invalid %s device type",
166                                        object_get_typename(obj));
167         return;
168     }
169 
170     core = loongarch_ipi_get_cpu(lics, dev);
171     if (!core) {
172         return;
173     }
174 
175     core->cpu = NULL;
176 }
177 
loongarch_ipi_pre_save(void * opaque)178 static int loongarch_ipi_pre_save(void *opaque)
179 {
180     if (kvm_irqchip_in_kernel()) {
181         return kvm_ipi_get(opaque);
182     }
183 
184     return 0;
185 }
186 
loongarch_ipi_post_load(void * opaque,int version_id)187 static int loongarch_ipi_post_load(void *opaque, int version_id)
188 {
189     if (kvm_irqchip_in_kernel()) {
190         return kvm_ipi_put(opaque, version_id);
191     }
192 
193     return 0;
194 }
195 
loongarch_ipi_class_init(ObjectClass * klass,const void * data)196 static void loongarch_ipi_class_init(ObjectClass *klass, const void *data)
197 {
198     LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass);
199     HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
200     LoongarchIPIClass *lic = LOONGARCH_IPI_CLASS(klass);
201     ResettableClass *rc = RESETTABLE_CLASS(klass);
202     DeviceClass *dc = DEVICE_CLASS(klass);
203 
204     device_class_set_parent_realize(dc, loongarch_ipi_realize,
205                                     &lic->parent_realize);
206     resettable_class_set_parent_phases(rc, NULL, loongarch_ipi_reset_hold,
207                                        NULL, &lic->parent_phases);
208     licc->get_iocsr_as = get_iocsr_as;
209     licc->cpu_by_arch_id = loongarch_cpu_by_arch_id;
210     hc->plug = loongarch_ipi_cpu_plug;
211     hc->unplug = loongarch_ipi_cpu_unplug;
212     licc->pre_save = loongarch_ipi_pre_save;
213     licc->post_load = loongarch_ipi_post_load;
214 }
215 
216 static const TypeInfo loongarch_ipi_types[] = {
217     {
218         .name               = TYPE_LOONGARCH_IPI,
219         .parent             = TYPE_LOONGSON_IPI_COMMON,
220         .instance_size      = sizeof(LoongarchIPIState),
221         .class_size         = sizeof(LoongarchIPIClass),
222         .class_init         = loongarch_ipi_class_init,
223         .interfaces         = (const InterfaceInfo[]) {
224             { TYPE_HOTPLUG_HANDLER },
225             { }
226         },
227     }
228 };
229 
230 DEFINE_TYPES(loongarch_ipi_types)
231