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 void loongarch_extioi_common_unrealize(DeviceState *dev) 112 { 113 LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(dev); 114 115 g_free(s->cpu); 116 } 117 118 static void loongarch_extioi_common_reset_hold(Object *obj, ResetType type) 119 { 120 LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_GET_CLASS(obj); 121 LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(obj); 122 ExtIOICore *core; 123 int i; 124 125 if (lecc->parent_phases.hold) { 126 lecc->parent_phases.hold(obj, type); 127 } 128 129 /* Clear HW registers for the board */ 130 memset(s->nodetype, 0, sizeof(s->nodetype)); 131 memset(s->bounce, 0, sizeof(s->bounce)); 132 memset(s->isr, 0, sizeof(s->isr)); 133 memset(s->enable, 0, sizeof(s->enable)); 134 memset(s->ipmap, 0, sizeof(s->ipmap)); 135 memset(s->coremap, 0, sizeof(s->coremap)); 136 memset(s->sw_pending, 0, sizeof(s->sw_pending)); 137 memset(s->sw_ipmap, 0, sizeof(s->sw_ipmap)); 138 memset(s->sw_coremap, 0, sizeof(s->sw_coremap)); 139 140 for (i = 0; i < s->num_cpu; i++) { 141 core = s->cpu + i; 142 /* EXTIOI with targeted CPU available however not present */ 143 if (!core->cpu) { 144 continue; 145 } 146 147 /* Clear HW registers for CPUs */ 148 memset(core->coreisr, 0, sizeof(core->coreisr)); 149 memset(core->sw_isr, 0, sizeof(core->sw_isr)); 150 } 151 152 s->status = 0; 153 } 154 155 static int loongarch_extioi_common_pre_save(void *opaque) 156 { 157 LoongArchExtIOICommonState *s = (LoongArchExtIOICommonState *)opaque; 158 LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_GET_CLASS(s); 159 160 if (lecc->pre_save) { 161 return lecc->pre_save(s); 162 } 163 164 return 0; 165 } 166 167 static int loongarch_extioi_common_post_load(void *opaque, int version_id) 168 { 169 LoongArchExtIOICommonState *s = (LoongArchExtIOICommonState *)opaque; 170 LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_GET_CLASS(s); 171 172 if (lecc->post_load) { 173 return lecc->post_load(s, version_id); 174 } 175 176 return 0; 177 } 178 179 static const VMStateDescription vmstate_extioi_core = { 180 .name = "extioi-core", 181 .version_id = 1, 182 .minimum_version_id = 1, 183 .fields = (const VMStateField[]) { 184 VMSTATE_UINT32_ARRAY(coreisr, ExtIOICore, EXTIOI_IRQS_GROUP_COUNT), 185 VMSTATE_END_OF_LIST() 186 } 187 }; 188 189 static const VMStateDescription vmstate_loongarch_extioi = { 190 .name = "loongarch.extioi", 191 .version_id = 3, 192 .minimum_version_id = 3, 193 .pre_save = loongarch_extioi_common_pre_save, 194 .post_load = loongarch_extioi_common_post_load, 195 .fields = (const VMStateField[]) { 196 VMSTATE_UINT32_ARRAY(bounce, LoongArchExtIOICommonState, 197 EXTIOI_IRQS_GROUP_COUNT), 198 VMSTATE_UINT32_ARRAY(nodetype, LoongArchExtIOICommonState, 199 EXTIOI_IRQS_NODETYPE_COUNT / 2), 200 VMSTATE_UINT32_ARRAY(enable, LoongArchExtIOICommonState, 201 EXTIOI_IRQS / 32), 202 VMSTATE_UINT32_ARRAY(isr, LoongArchExtIOICommonState, 203 EXTIOI_IRQS / 32), 204 VMSTATE_UINT32_ARRAY(ipmap, LoongArchExtIOICommonState, 205 EXTIOI_IRQS_IPMAP_SIZE / 4), 206 VMSTATE_UINT32_ARRAY(coremap, LoongArchExtIOICommonState, 207 EXTIOI_IRQS / 4), 208 VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchExtIOICommonState, 209 num_cpu, vmstate_extioi_core, ExtIOICore), 210 VMSTATE_UINT32(features, LoongArchExtIOICommonState), 211 VMSTATE_UINT32(status, LoongArchExtIOICommonState), 212 VMSTATE_END_OF_LIST() 213 } 214 }; 215 216 static const Property extioi_properties[] = { 217 DEFINE_PROP_BIT("has-virtualization-extension", LoongArchExtIOICommonState, 218 features, EXTIOI_HAS_VIRT_EXTENSION, 0), 219 }; 220 221 static void loongarch_extioi_common_class_init(ObjectClass *klass, 222 const void *data) 223 { 224 DeviceClass *dc = DEVICE_CLASS(klass); 225 LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_CLASS(klass); 226 HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); 227 ResettableClass *rc = RESETTABLE_CLASS(klass); 228 229 device_class_set_parent_realize(dc, loongarch_extioi_common_realize, 230 &lecc->parent_realize); 231 device_class_set_parent_unrealize(dc, loongarch_extioi_common_unrealize, 232 &lecc->parent_unrealize); 233 resettable_class_set_parent_phases(rc, NULL, 234 loongarch_extioi_common_reset_hold, 235 NULL, &lecc->parent_phases); 236 device_class_set_props(dc, extioi_properties); 237 dc->vmsd = &vmstate_loongarch_extioi; 238 hc->plug = loongarch_extioi_cpu_plug; 239 hc->unplug = loongarch_extioi_cpu_unplug; 240 } 241 242 static const TypeInfo loongarch_extioi_common_types[] = { 243 { 244 .name = TYPE_LOONGARCH_EXTIOI_COMMON, 245 .parent = TYPE_SYS_BUS_DEVICE, 246 .instance_size = sizeof(LoongArchExtIOICommonState), 247 .class_size = sizeof(LoongArchExtIOICommonClass), 248 .class_init = loongarch_extioi_common_class_init, 249 .interfaces = (const InterfaceInfo[]) { 250 { TYPE_HOTPLUG_HANDLER }, 251 { } 252 }, 253 .abstract = true, 254 } 255 }; 256 257 DEFINE_TYPES(loongarch_extioi_common_types) 258