16b69f778SBibo Mao /* SPDX-License-Identifier: GPL-2.0-or-later */ 26b69f778SBibo Mao /* 36b69f778SBibo Mao * Loongson extioi interrupt controller emulation 46b69f778SBibo Mao * Copyright (C) 2024 Loongson Technology Corporation Limited 56b69f778SBibo Mao */ 6272c467aSBibo Mao #include "qemu/osdep.h" 7e45c96b7SBibo Mao #include "qemu/error-report.h" 8272c467aSBibo Mao #include "qemu/module.h" 9272c467aSBibo Mao #include "qapi/error.h" 10272c467aSBibo Mao #include "hw/qdev-properties.h" 11272c467aSBibo Mao #include "hw/intc/loongarch_extioi_common.h" 12272c467aSBibo Mao #include "migration/vmstate.h" 13e45c96b7SBibo Mao #include "target/loongarch/cpu.h" 14e45c96b7SBibo Mao 158e63a7a7SBibo Mao static ExtIOICore *loongarch_extioi_get_cpu(LoongArchExtIOICommonState *s, 168e63a7a7SBibo Mao DeviceState *dev) 178e63a7a7SBibo Mao { 188e63a7a7SBibo Mao CPUClass *k = CPU_GET_CLASS(dev); 198e63a7a7SBibo Mao uint64_t arch_id = k->get_arch_id(CPU(dev)); 208e63a7a7SBibo Mao int i; 218e63a7a7SBibo Mao 228e63a7a7SBibo Mao for (i = 0; i < s->num_cpu; i++) { 238e63a7a7SBibo Mao if (s->cpu[i].arch_id == arch_id) { 248e63a7a7SBibo Mao return &s->cpu[i]; 258e63a7a7SBibo Mao } 268e63a7a7SBibo Mao } 278e63a7a7SBibo Mao 288e63a7a7SBibo Mao return NULL; 298e63a7a7SBibo Mao } 308e63a7a7SBibo Mao 31e45c96b7SBibo Mao static void loongarch_extioi_cpu_plug(HotplugHandler *hotplug_dev, 32e45c96b7SBibo Mao DeviceState *dev, Error **errp) 33e45c96b7SBibo Mao { 348e63a7a7SBibo Mao LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(hotplug_dev); 35e45c96b7SBibo Mao Object *obj = OBJECT(dev); 368e63a7a7SBibo Mao ExtIOICore *core; 378e63a7a7SBibo Mao int pin, index; 38e45c96b7SBibo Mao 39e45c96b7SBibo Mao if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { 40e45c96b7SBibo Mao warn_report("LoongArch extioi: Invalid %s device type", 41e45c96b7SBibo Mao object_get_typename(obj)); 42e45c96b7SBibo Mao return; 43e45c96b7SBibo Mao } 448e63a7a7SBibo Mao 458e63a7a7SBibo Mao core = loongarch_extioi_get_cpu(s, dev); 468e63a7a7SBibo Mao if (!core) { 478e63a7a7SBibo Mao return; 488e63a7a7SBibo Mao } 498e63a7a7SBibo Mao 508e63a7a7SBibo Mao core->cpu = CPU(dev); 518e63a7a7SBibo Mao index = core - s->cpu; 528e63a7a7SBibo Mao 538e63a7a7SBibo Mao /* 548e63a7a7SBibo Mao * connect extioi irq to the cpu irq 558e63a7a7SBibo Mao * cpu_pin[LS3A_INTC_IP + 2 : 2] <= intc_pin[LS3A_INTC_IP : 0] 568e63a7a7SBibo Mao */ 578e63a7a7SBibo Mao for (pin = 0; pin < LS3A_INTC_IP; pin++) { 588e63a7a7SBibo Mao qdev_connect_gpio_out(DEVICE(s), index * LS3A_INTC_IP + pin, 598e63a7a7SBibo Mao qdev_get_gpio_in(dev, pin + 2)); 608e63a7a7SBibo Mao } 61e45c96b7SBibo Mao } 62e45c96b7SBibo Mao 63e45c96b7SBibo Mao static void loongarch_extioi_cpu_unplug(HotplugHandler *hotplug_dev, 64e45c96b7SBibo Mao DeviceState *dev, Error **errp) 65e45c96b7SBibo Mao { 668e63a7a7SBibo Mao LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(hotplug_dev); 67e45c96b7SBibo Mao Object *obj = OBJECT(dev); 688e63a7a7SBibo Mao ExtIOICore *core; 69e45c96b7SBibo Mao 70e45c96b7SBibo Mao if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { 71e45c96b7SBibo Mao warn_report("LoongArch extioi: Invalid %s device type", 72e45c96b7SBibo Mao object_get_typename(obj)); 73e45c96b7SBibo Mao return; 74e45c96b7SBibo Mao } 758e63a7a7SBibo Mao 768e63a7a7SBibo Mao core = loongarch_extioi_get_cpu(s, dev); 778e63a7a7SBibo Mao if (!core) { 788e63a7a7SBibo Mao return; 798e63a7a7SBibo Mao } 808e63a7a7SBibo Mao 818e63a7a7SBibo Mao core->cpu = NULL; 82e45c96b7SBibo Mao } 836b69f778SBibo Mao 846b69f778SBibo Mao static void loongarch_extioi_common_realize(DeviceState *dev, Error **errp) 856b69f778SBibo Mao { 866b69f778SBibo Mao LoongArchExtIOICommonState *s = (LoongArchExtIOICommonState *)dev; 875a3e068dSBibo Mao MachineState *machine = MACHINE(qdev_get_machine()); 885a3e068dSBibo Mao MachineClass *mc = MACHINE_GET_CLASS(machine); 895a3e068dSBibo Mao const CPUArchIdList *id_list; 908b4b668fSBibo Mao int i, pin; 916b69f778SBibo Mao 925a3e068dSBibo Mao assert(mc->possible_cpu_arch_ids); 935a3e068dSBibo Mao id_list = mc->possible_cpu_arch_ids(machine); 945a3e068dSBibo Mao s->num_cpu = id_list->len; 955a3e068dSBibo Mao s->cpu = g_new0(ExtIOICore, s->num_cpu); 965a3e068dSBibo Mao if (s->cpu == NULL) { 975a3e068dSBibo Mao error_setg(errp, "Memory allocation for ExtIOICore faile"); 986b69f778SBibo Mao return; 996b69f778SBibo Mao } 1005a3e068dSBibo Mao 1015a3e068dSBibo Mao for (i = 0; i < s->num_cpu; i++) { 1025a3e068dSBibo Mao s->cpu[i].arch_id = id_list->cpus[i].arch_id; 1035a3e068dSBibo Mao s->cpu[i].cpu = CPU(id_list->cpus[i].cpu); 1048b4b668fSBibo Mao 1058b4b668fSBibo Mao for (pin = 0; pin < LS3A_INTC_IP; pin++) { 1068b4b668fSBibo Mao qdev_init_gpio_out(dev, &s->cpu[i].parent_irq[pin], 1); 1078b4b668fSBibo Mao } 1085a3e068dSBibo Mao } 1096b69f778SBibo Mao } 1106b69f778SBibo Mao 111*86e4a647SBibo Mao static void loongarch_extioi_common_reset_hold(Object *obj, ResetType type) 112*86e4a647SBibo Mao { 113*86e4a647SBibo Mao LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_GET_CLASS(obj); 114*86e4a647SBibo Mao LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(obj); 115*86e4a647SBibo Mao ExtIOICore *core; 116*86e4a647SBibo Mao int i; 117*86e4a647SBibo Mao 118*86e4a647SBibo Mao if (lecc->parent_phases.hold) { 119*86e4a647SBibo Mao lecc->parent_phases.hold(obj, type); 120*86e4a647SBibo Mao } 121*86e4a647SBibo Mao 122*86e4a647SBibo Mao /* Clear HW registers for the board */ 123*86e4a647SBibo Mao memset(s->nodetype, 0, sizeof(s->nodetype)); 124*86e4a647SBibo Mao memset(s->bounce, 0, sizeof(s->bounce)); 125*86e4a647SBibo Mao memset(s->isr, 0, sizeof(s->isr)); 126*86e4a647SBibo Mao memset(s->enable, 0, sizeof(s->enable)); 127*86e4a647SBibo Mao memset(s->ipmap, 0, sizeof(s->ipmap)); 128*86e4a647SBibo Mao memset(s->coremap, 0, sizeof(s->coremap)); 129*86e4a647SBibo Mao memset(s->sw_pending, 0, sizeof(s->sw_pending)); 130*86e4a647SBibo Mao memset(s->sw_ipmap, 0, sizeof(s->sw_ipmap)); 131*86e4a647SBibo Mao memset(s->sw_coremap, 0, sizeof(s->sw_coremap)); 132*86e4a647SBibo Mao 133*86e4a647SBibo Mao for (i = 0; i < s->num_cpu; i++) { 134*86e4a647SBibo Mao core = s->cpu + i; 135*86e4a647SBibo Mao /* EXTIOI with targeted CPU available however not present */ 136*86e4a647SBibo Mao if (!core->cpu) { 137*86e4a647SBibo Mao continue; 138*86e4a647SBibo Mao } 139*86e4a647SBibo Mao 140*86e4a647SBibo Mao /* Clear HW registers for CPUs */ 141*86e4a647SBibo Mao memset(core->coreisr, 0, sizeof(core->coreisr)); 142*86e4a647SBibo Mao memset(core->sw_isr, 0, sizeof(core->sw_isr)); 143*86e4a647SBibo Mao } 144*86e4a647SBibo Mao 145*86e4a647SBibo Mao s->status = 0; 146*86e4a647SBibo Mao } 147*86e4a647SBibo Mao 148ff09444aSBibo Mao static int loongarch_extioi_common_pre_save(void *opaque) 149ff09444aSBibo Mao { 150ff09444aSBibo Mao LoongArchExtIOICommonState *s = (LoongArchExtIOICommonState *)opaque; 151ff09444aSBibo Mao LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_GET_CLASS(s); 152ff09444aSBibo Mao 153ff09444aSBibo Mao if (lecc->pre_save) { 154ff09444aSBibo Mao return lecc->pre_save(s); 155ff09444aSBibo Mao } 156ff09444aSBibo Mao 157ff09444aSBibo Mao return 0; 158ff09444aSBibo Mao } 159ff09444aSBibo Mao 1606b69f778SBibo Mao static int loongarch_extioi_common_post_load(void *opaque, int version_id) 1616b69f778SBibo Mao { 162272c467aSBibo Mao LoongArchExtIOICommonState *s = (LoongArchExtIOICommonState *)opaque; 163272c467aSBibo Mao LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_GET_CLASS(s); 164272c467aSBibo Mao 165272c467aSBibo Mao if (lecc->post_load) { 166272c467aSBibo Mao return lecc->post_load(s, version_id); 167272c467aSBibo Mao } 168272c467aSBibo Mao 169272c467aSBibo Mao return 0; 1706b69f778SBibo Mao } 1716b69f778SBibo Mao 1726b69f778SBibo Mao static const VMStateDescription vmstate_extioi_core = { 1736b69f778SBibo Mao .name = "extioi-core", 1746b69f778SBibo Mao .version_id = 1, 1756b69f778SBibo Mao .minimum_version_id = 1, 1766b69f778SBibo Mao .fields = (const VMStateField[]) { 1776b69f778SBibo Mao VMSTATE_UINT32_ARRAY(coreisr, ExtIOICore, EXTIOI_IRQS_GROUP_COUNT), 1786b69f778SBibo Mao VMSTATE_END_OF_LIST() 1796b69f778SBibo Mao } 1806b69f778SBibo Mao }; 1816b69f778SBibo Mao 1826b69f778SBibo Mao static const VMStateDescription vmstate_loongarch_extioi = { 1836b69f778SBibo Mao .name = "loongarch.extioi", 1846b69f778SBibo Mao .version_id = 3, 1856b69f778SBibo Mao .minimum_version_id = 3, 186ff09444aSBibo Mao .pre_save = loongarch_extioi_common_pre_save, 1876b69f778SBibo Mao .post_load = loongarch_extioi_common_post_load, 1886b69f778SBibo Mao .fields = (const VMStateField[]) { 1896b69f778SBibo Mao VMSTATE_UINT32_ARRAY(bounce, LoongArchExtIOICommonState, 1906b69f778SBibo Mao EXTIOI_IRQS_GROUP_COUNT), 1916b69f778SBibo Mao VMSTATE_UINT32_ARRAY(nodetype, LoongArchExtIOICommonState, 1926b69f778SBibo Mao EXTIOI_IRQS_NODETYPE_COUNT / 2), 1936b69f778SBibo Mao VMSTATE_UINT32_ARRAY(enable, LoongArchExtIOICommonState, 1946b69f778SBibo Mao EXTIOI_IRQS / 32), 1956b69f778SBibo Mao VMSTATE_UINT32_ARRAY(isr, LoongArchExtIOICommonState, 1966b69f778SBibo Mao EXTIOI_IRQS / 32), 1976b69f778SBibo Mao VMSTATE_UINT32_ARRAY(ipmap, LoongArchExtIOICommonState, 1986b69f778SBibo Mao EXTIOI_IRQS_IPMAP_SIZE / 4), 1996b69f778SBibo Mao VMSTATE_UINT32_ARRAY(coremap, LoongArchExtIOICommonState, 2006b69f778SBibo Mao EXTIOI_IRQS / 4), 2016b69f778SBibo Mao VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchExtIOICommonState, 2026b69f778SBibo Mao num_cpu, vmstate_extioi_core, ExtIOICore), 2036b69f778SBibo Mao VMSTATE_UINT32(features, LoongArchExtIOICommonState), 2046b69f778SBibo Mao VMSTATE_UINT32(status, LoongArchExtIOICommonState), 2056b69f778SBibo Mao VMSTATE_END_OF_LIST() 2066b69f778SBibo Mao } 2076b69f778SBibo Mao }; 2086b69f778SBibo Mao 2096b69f778SBibo Mao static const Property extioi_properties[] = { 2106b69f778SBibo Mao DEFINE_PROP_BIT("has-virtualization-extension", LoongArchExtIOICommonState, 2116b69f778SBibo Mao features, EXTIOI_HAS_VIRT_EXTENSION, 0), 2126b69f778SBibo Mao }; 213272c467aSBibo Mao 21412d1a768SPhilippe Mathieu-Daudé static void loongarch_extioi_common_class_init(ObjectClass *klass, 21512d1a768SPhilippe Mathieu-Daudé const void *data) 216272c467aSBibo Mao { 217272c467aSBibo Mao DeviceClass *dc = DEVICE_CLASS(klass); 218272c467aSBibo Mao LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_CLASS(klass); 219e45c96b7SBibo Mao HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); 220*86e4a647SBibo Mao ResettableClass *rc = RESETTABLE_CLASS(klass); 221272c467aSBibo Mao 222272c467aSBibo Mao device_class_set_parent_realize(dc, loongarch_extioi_common_realize, 223272c467aSBibo Mao &lecc->parent_realize); 224*86e4a647SBibo Mao resettable_class_set_parent_phases(rc, NULL, 225*86e4a647SBibo Mao loongarch_extioi_common_reset_hold, 226*86e4a647SBibo Mao NULL, &lecc->parent_phases); 227272c467aSBibo Mao device_class_set_props(dc, extioi_properties); 228272c467aSBibo Mao dc->vmsd = &vmstate_loongarch_extioi; 229e45c96b7SBibo Mao hc->plug = loongarch_extioi_cpu_plug; 230e45c96b7SBibo Mao hc->unplug = loongarch_extioi_cpu_unplug; 231272c467aSBibo Mao } 232272c467aSBibo Mao 233272c467aSBibo Mao static const TypeInfo loongarch_extioi_common_types[] = { 234272c467aSBibo Mao { 235272c467aSBibo Mao .name = TYPE_LOONGARCH_EXTIOI_COMMON, 236272c467aSBibo Mao .parent = TYPE_SYS_BUS_DEVICE, 237272c467aSBibo Mao .instance_size = sizeof(LoongArchExtIOICommonState), 238272c467aSBibo Mao .class_size = sizeof(LoongArchExtIOICommonClass), 239272c467aSBibo Mao .class_init = loongarch_extioi_common_class_init, 2402cd09e47SPhilippe Mathieu-Daudé .interfaces = (const InterfaceInfo[]) { 241e45c96b7SBibo Mao { TYPE_HOTPLUG_HANDLER }, 242e45c96b7SBibo Mao { } 243e45c96b7SBibo Mao }, 244272c467aSBibo Mao .abstract = true, 245272c467aSBibo Mao } 246272c467aSBibo Mao }; 247272c467aSBibo Mao 248272c467aSBibo Mao DEFINE_TYPES(loongarch_extioi_common_types) 249