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
loongarch_extioi_get_cpu(LoongArchExtIOICommonState * s,DeviceState * dev)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
loongarch_extioi_cpu_plug(HotplugHandler * hotplug_dev,DeviceState * dev,Error ** errp)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
loongarch_extioi_cpu_unplug(HotplugHandler * hotplug_dev,DeviceState * dev,Error ** errp)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
loongarch_extioi_common_realize(DeviceState * dev,Error ** errp)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
loongarch_extioi_common_pre_save(void * opaque)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
loongarch_extioi_common_post_load(void * opaque,int version_id)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
loongarch_extioi_common_class_init(ObjectClass * klass,void * data)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