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 "target/loongarch/cpu.h" 15 16 static AddressSpace *get_iocsr_as(CPUState *cpu) 17 { 18 return LOONGARCH_CPU(cpu)->env.address_space_iocsr; 19 } 20 21 static int loongarch_ipi_cmp(const void *a, const void *b) 22 { 23 IPICore *ipi_a = (IPICore *)a; 24 IPICore *ipi_b = (IPICore *)b; 25 26 return ipi_a->arch_id - ipi_b->arch_id; 27 } 28 29 static int loongarch_cpu_by_arch_id(LoongsonIPICommonState *lics, 30 int64_t arch_id, int *index, CPUState **pcs) 31 { 32 IPICore ipi, *found; 33 34 ipi.arch_id = arch_id; 35 found = bsearch(&ipi, lics->cpu, lics->num_cpu, sizeof(IPICore), 36 loongarch_ipi_cmp); 37 if (found && found->cpu) { 38 if (index) { 39 *index = found - lics->cpu; 40 } 41 42 if (pcs) { 43 *pcs = found->cpu; 44 } 45 46 return MEMTX_OK; 47 } 48 49 return MEMTX_ERROR; 50 } 51 52 static IPICore *loongarch_ipi_get_cpu(LoongsonIPICommonState *lics, 53 DeviceState *dev) 54 { 55 CPUClass *k = CPU_GET_CLASS(dev); 56 uint64_t arch_id = k->get_arch_id(CPU(dev)); 57 int i; 58 59 for (i = 0; i < lics->num_cpu; i++) { 60 if (lics->cpu[i].arch_id == arch_id) { 61 return &lics->cpu[i]; 62 } 63 } 64 65 return NULL; 66 } 67 68 static void loongarch_ipi_realize(DeviceState *dev, Error **errp) 69 { 70 LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(dev); 71 LoongarchIPIClass *lic = LOONGARCH_IPI_GET_CLASS(dev); 72 MachineState *machine = MACHINE(qdev_get_machine()); 73 MachineClass *mc = MACHINE_GET_CLASS(machine); 74 const CPUArchIdList *id_list; 75 Error *local_err = NULL; 76 int i; 77 78 lic->parent_realize(dev, &local_err); 79 if (local_err) { 80 error_propagate(errp, local_err); 81 return; 82 } 83 84 assert(mc->possible_cpu_arch_ids); 85 id_list = mc->possible_cpu_arch_ids(machine); 86 lics->num_cpu = id_list->len; 87 lics->cpu = g_new0(IPICore, lics->num_cpu); 88 for (i = 0; i < lics->num_cpu; i++) { 89 lics->cpu[i].arch_id = id_list->cpus[i].arch_id; 90 lics->cpu[i].cpu = CPU(id_list->cpus[i].cpu); 91 lics->cpu[i].ipi = lics; 92 qdev_init_gpio_out(dev, &lics->cpu[i].irq, 1); 93 } 94 } 95 96 static void loongarch_ipi_reset_hold(Object *obj, ResetType type) 97 { 98 int i; 99 LoongarchIPIClass *lic = LOONGARCH_IPI_GET_CLASS(obj); 100 LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(obj); 101 IPICore *core; 102 103 if (lic->parent_phases.hold) { 104 lic->parent_phases.hold(obj, type); 105 } 106 107 for (i = 0; i < lics->num_cpu; i++) { 108 core = lics->cpu + i; 109 /* IPI with targeted CPU available however not present */ 110 if (!core->cpu) { 111 continue; 112 } 113 114 core->status = 0; 115 core->en = 0; 116 core->set = 0; 117 core->clear = 0; 118 memset(core->buf, 0, sizeof(core->buf)); 119 } 120 } 121 122 static void loongarch_ipi_cpu_plug(HotplugHandler *hotplug_dev, 123 DeviceState *dev, Error **errp) 124 { 125 LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(hotplug_dev); 126 Object *obj = OBJECT(dev); 127 IPICore *core; 128 int index; 129 130 if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { 131 warn_report("LoongArch extioi: Invalid %s device type", 132 object_get_typename(obj)); 133 return; 134 } 135 136 core = loongarch_ipi_get_cpu(lics, dev); 137 if (!core) { 138 return; 139 } 140 141 core->cpu = CPU(dev); 142 index = core - lics->cpu; 143 144 /* connect ipi irq to cpu irq */ 145 qdev_connect_gpio_out(DEVICE(lics), index, qdev_get_gpio_in(dev, IRQ_IPI)); 146 } 147 148 static void loongarch_ipi_cpu_unplug(HotplugHandler *hotplug_dev, 149 DeviceState *dev, Error **errp) 150 { 151 LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(hotplug_dev); 152 Object *obj = OBJECT(dev); 153 IPICore *core; 154 155 if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { 156 warn_report("LoongArch extioi: Invalid %s device type", 157 object_get_typename(obj)); 158 return; 159 } 160 161 core = loongarch_ipi_get_cpu(lics, dev); 162 if (!core) { 163 return; 164 } 165 166 core->cpu = NULL; 167 } 168 169 static void loongarch_ipi_class_init(ObjectClass *klass, const void *data) 170 { 171 LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass); 172 HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); 173 LoongarchIPIClass *lic = LOONGARCH_IPI_CLASS(klass); 174 ResettableClass *rc = RESETTABLE_CLASS(klass); 175 DeviceClass *dc = DEVICE_CLASS(klass); 176 177 device_class_set_parent_realize(dc, loongarch_ipi_realize, 178 &lic->parent_realize); 179 resettable_class_set_parent_phases(rc, NULL, loongarch_ipi_reset_hold, 180 NULL, &lic->parent_phases); 181 licc->get_iocsr_as = get_iocsr_as; 182 licc->cpu_by_arch_id = loongarch_cpu_by_arch_id; 183 hc->plug = loongarch_ipi_cpu_plug; 184 hc->unplug = loongarch_ipi_cpu_unplug; 185 } 186 187 static const TypeInfo loongarch_ipi_types[] = { 188 { 189 .name = TYPE_LOONGARCH_IPI, 190 .parent = TYPE_LOONGSON_IPI_COMMON, 191 .instance_size = sizeof(LoongarchIPIState), 192 .class_size = sizeof(LoongarchIPIClass), 193 .class_init = loongarch_ipi_class_init, 194 .interfaces = (const InterfaceInfo[]) { 195 { TYPE_HOTPLUG_HANDLER }, 196 { } 197 }, 198 } 199 }; 200 201 DEFINE_TYPES(loongarch_ipi_types) 202