xref: /qemu/hw/intc/loongarch_extioi_common.c (revision f07a5674cf97b8473e5d06d7b1df9b51e97d553f)
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3  * Loongson extioi interrupt controller emulation
4  * Copyright (C) 2024 Loongson Technology Corporation Limited
5  */
6 #include "qemu/osdep.h"
7 #include "qemu/error-report.h"
8 #include "qemu/module.h"
9 #include "qapi/error.h"
10 #include "hw/qdev-properties.h"
11 #include "hw/intc/loongarch_extioi_common.h"
12 #include "migration/vmstate.h"
13 #include "target/loongarch/cpu.h"
14 
15 static ExtIOICore *loongarch_extioi_get_cpu(LoongArchExtIOICommonState *s,
16                                             DeviceState *dev)
17 {
18     CPUClass *k = CPU_GET_CLASS(dev);
19     uint64_t arch_id = k->get_arch_id(CPU(dev));
20     int i;
21 
22     for (i = 0; i < s->num_cpu; i++) {
23         if (s->cpu[i].arch_id == arch_id) {
24             return &s->cpu[i];
25         }
26     }
27 
28     return NULL;
29 }
30 
31 static void loongarch_extioi_cpu_plug(HotplugHandler *hotplug_dev,
32                                       DeviceState *dev, Error **errp)
33 {
34     LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(hotplug_dev);
35     Object *obj = OBJECT(dev);
36     ExtIOICore *core;
37     int pin, index;
38 
39     if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) {
40         warn_report("LoongArch extioi: Invalid %s device type",
41                                        object_get_typename(obj));
42         return;
43     }
44 
45     core = loongarch_extioi_get_cpu(s, dev);
46     if (!core) {
47         return;
48     }
49 
50     core->cpu = CPU(dev);
51     index = core - s->cpu;
52 
53     /*
54      * connect extioi irq to the cpu irq
55      * cpu_pin[LS3A_INTC_IP + 2 : 2] <= intc_pin[LS3A_INTC_IP : 0]
56      */
57     for (pin = 0; pin < LS3A_INTC_IP; pin++) {
58         qdev_connect_gpio_out(DEVICE(s), index * LS3A_INTC_IP + pin,
59                               qdev_get_gpio_in(dev, pin + 2));
60     }
61 }
62 
63 static void loongarch_extioi_cpu_unplug(HotplugHandler *hotplug_dev,
64                                         DeviceState *dev, Error **errp)
65 {
66     LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(hotplug_dev);
67     Object *obj = OBJECT(dev);
68     ExtIOICore *core;
69 
70     if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) {
71         warn_report("LoongArch extioi: Invalid %s device type",
72                                        object_get_typename(obj));
73         return;
74     }
75 
76     core = loongarch_extioi_get_cpu(s, dev);
77     if (!core) {
78         return;
79     }
80 
81     core->cpu = NULL;
82 }
83 
84 static void loongarch_extioi_common_realize(DeviceState *dev, Error **errp)
85 {
86     LoongArchExtIOICommonState *s = (LoongArchExtIOICommonState *)dev;
87     MachineState *machine = MACHINE(qdev_get_machine());
88     MachineClass *mc = MACHINE_GET_CLASS(machine);
89     const CPUArchIdList *id_list;
90     int i, pin;
91 
92     assert(mc->possible_cpu_arch_ids);
93     id_list = mc->possible_cpu_arch_ids(machine);
94     s->num_cpu = id_list->len;
95     s->cpu = g_new0(ExtIOICore, s->num_cpu);
96     if (s->cpu == NULL) {
97         error_setg(errp, "Memory allocation for ExtIOICore faile");
98         return;
99     }
100 
101     for (i = 0; i < s->num_cpu; i++) {
102         s->cpu[i].arch_id = id_list->cpus[i].arch_id;
103         s->cpu[i].cpu = CPU(id_list->cpus[i].cpu);
104 
105         for (pin = 0; pin < LS3A_INTC_IP; pin++) {
106             qdev_init_gpio_out(dev, &s->cpu[i].parent_irq[pin], 1);
107         }
108     }
109 }
110 
111 static int loongarch_extioi_common_pre_save(void *opaque)
112 {
113     LoongArchExtIOICommonState *s = (LoongArchExtIOICommonState *)opaque;
114     LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_GET_CLASS(s);
115 
116     if (lecc->pre_save) {
117         return lecc->pre_save(s);
118     }
119 
120     return 0;
121 }
122 
123 static int loongarch_extioi_common_post_load(void *opaque, int version_id)
124 {
125     LoongArchExtIOICommonState *s = (LoongArchExtIOICommonState *)opaque;
126     LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_GET_CLASS(s);
127 
128     if (lecc->post_load) {
129         return lecc->post_load(s, version_id);
130     }
131 
132     return 0;
133 }
134 
135 static const VMStateDescription vmstate_extioi_core = {
136     .name = "extioi-core",
137     .version_id = 1,
138     .minimum_version_id = 1,
139     .fields = (const VMStateField[]) {
140         VMSTATE_UINT32_ARRAY(coreisr, ExtIOICore, EXTIOI_IRQS_GROUP_COUNT),
141         VMSTATE_END_OF_LIST()
142     }
143 };
144 
145 static const VMStateDescription vmstate_loongarch_extioi = {
146     .name = "loongarch.extioi",
147     .version_id = 3,
148     .minimum_version_id = 3,
149     .pre_save  = loongarch_extioi_common_pre_save,
150     .post_load = loongarch_extioi_common_post_load,
151     .fields = (const VMStateField[]) {
152         VMSTATE_UINT32_ARRAY(bounce, LoongArchExtIOICommonState,
153                              EXTIOI_IRQS_GROUP_COUNT),
154         VMSTATE_UINT32_ARRAY(nodetype, LoongArchExtIOICommonState,
155                              EXTIOI_IRQS_NODETYPE_COUNT / 2),
156         VMSTATE_UINT32_ARRAY(enable, LoongArchExtIOICommonState,
157                              EXTIOI_IRQS / 32),
158         VMSTATE_UINT32_ARRAY(isr, LoongArchExtIOICommonState,
159                              EXTIOI_IRQS / 32),
160         VMSTATE_UINT32_ARRAY(ipmap, LoongArchExtIOICommonState,
161                              EXTIOI_IRQS_IPMAP_SIZE / 4),
162         VMSTATE_UINT32_ARRAY(coremap, LoongArchExtIOICommonState,
163                              EXTIOI_IRQS / 4),
164         VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchExtIOICommonState,
165                              num_cpu, vmstate_extioi_core, ExtIOICore),
166         VMSTATE_UINT32(features, LoongArchExtIOICommonState),
167         VMSTATE_UINT32(status, LoongArchExtIOICommonState),
168         VMSTATE_END_OF_LIST()
169     }
170 };
171 
172 static const Property extioi_properties[] = {
173     DEFINE_PROP_BIT("has-virtualization-extension", LoongArchExtIOICommonState,
174                     features, EXTIOI_HAS_VIRT_EXTENSION, 0),
175 };
176 
177 static void loongarch_extioi_common_class_init(ObjectClass *klass, void *data)
178 {
179     DeviceClass *dc = DEVICE_CLASS(klass);
180     LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_CLASS(klass);
181     HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
182 
183     device_class_set_parent_realize(dc, loongarch_extioi_common_realize,
184                                     &lecc->parent_realize);
185     device_class_set_props(dc, extioi_properties);
186     dc->vmsd = &vmstate_loongarch_extioi;
187     hc->plug = loongarch_extioi_cpu_plug;
188     hc->unplug = loongarch_extioi_cpu_unplug;
189 }
190 
191 static const TypeInfo loongarch_extioi_common_types[] = {
192     {
193         .name               = TYPE_LOONGARCH_EXTIOI_COMMON,
194         .parent             = TYPE_SYS_BUS_DEVICE,
195         .instance_size      = sizeof(LoongArchExtIOICommonState),
196         .class_size         = sizeof(LoongArchExtIOICommonClass),
197         .class_init         = loongarch_extioi_common_class_init,
198         .interfaces         = (InterfaceInfo[]) {
199             { TYPE_HOTPLUG_HANDLER },
200             { }
201         },
202         .abstract           = true,
203     }
204 };
205 
206 DEFINE_TYPES(loongarch_extioi_common_types)
207