1 /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 /* 3 * LoongArch ipi interrupt support 4 * 5 * Copyright (C) 2021 Loongson Technology Corporation Limited 6 */ 7 8 #include "qemu/osdep.h" 9 #include "hw/sysbus.h" 10 #include "hw/intc/loongarch_ipi.h" 11 #include "hw/irq.h" 12 #include "hw/qdev-properties.h" 13 #include "qapi/error.h" 14 #include "qemu/log.h" 15 #include "exec/address-spaces.h" 16 #include "hw/loongarch/virt.h" 17 #include "migration/vmstate.h" 18 #include "target/loongarch/internals.h" 19 #include "trace.h" 20 21 static MemTxResult loongarch_ipi_readl(void *opaque, hwaddr addr, 22 uint64_t *data, 23 unsigned size, MemTxAttrs attrs) 24 { 25 IPICore *s; 26 LoongArchIPI *ipi = opaque; 27 uint64_t ret = 0; 28 int index = 0; 29 30 s = &ipi->cpu[attrs.requester_id]; 31 addr &= 0xff; 32 switch (addr) { 33 case CORE_STATUS_OFF: 34 ret = s->status; 35 break; 36 case CORE_EN_OFF: 37 ret = s->en; 38 break; 39 case CORE_SET_OFF: 40 ret = 0; 41 break; 42 case CORE_CLEAR_OFF: 43 ret = 0; 44 break; 45 case CORE_BUF_20 ... CORE_BUF_38 + 4: 46 index = (addr - CORE_BUF_20) >> 2; 47 ret = s->buf[index]; 48 break; 49 default: 50 qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr); 51 break; 52 } 53 54 trace_loongarch_ipi_read(size, (uint64_t)addr, ret); 55 *data = ret; 56 return MEMTX_OK; 57 } 58 59 static void send_ipi_data(CPULoongArchState *env, uint64_t val, hwaddr addr, 60 MemTxAttrs attrs) 61 { 62 int i, mask = 0, data = 0; 63 64 /* 65 * bit 27-30 is mask for byte writing, 66 * if the mask is 0, we need not to do anything. 67 */ 68 if ((val >> 27) & 0xf) { 69 data = address_space_ldl(env->address_space_iocsr, addr, 70 attrs, NULL); 71 for (i = 0; i < 4; i++) { 72 /* get mask for byte writing */ 73 if (val & (0x1 << (27 + i))) { 74 mask |= 0xff << (i * 8); 75 } 76 } 77 } 78 79 data &= mask; 80 data |= (val >> 32) & ~mask; 81 address_space_stl(env->address_space_iocsr, addr, 82 data, attrs, NULL); 83 } 84 85 static int archid_cmp(const void *a, const void *b) 86 { 87 CPUArchId *archid_a = (CPUArchId *)a; 88 CPUArchId *archid_b = (CPUArchId *)b; 89 90 return archid_a->arch_id - archid_b->arch_id; 91 } 92 93 static CPUArchId *find_cpu_by_archid(MachineState *ms, uint32_t id) 94 { 95 CPUArchId apic_id, *found_cpu; 96 97 apic_id.arch_id = id; 98 found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus, 99 ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus), 100 archid_cmp); 101 102 return found_cpu; 103 } 104 105 static CPUState *ipi_getcpu(int arch_id) 106 { 107 MachineState *machine = MACHINE(qdev_get_machine()); 108 CPUArchId *archid; 109 110 archid = find_cpu_by_archid(machine, arch_id); 111 if (archid) { 112 return CPU(archid->cpu); 113 } 114 115 return NULL; 116 } 117 118 static MemTxResult mail_send(uint64_t val, MemTxAttrs attrs) 119 { 120 uint32_t cpuid; 121 hwaddr addr; 122 CPUState *cs; 123 124 cpuid = extract32(val, 16, 10); 125 if (cpuid >= LOONGARCH_MAX_CPUS) { 126 trace_loongarch_ipi_unsupported_cpuid("IOCSR_MAIL_SEND", cpuid); 127 return MEMTX_DECODE_ERROR; 128 } 129 130 cs = ipi_getcpu(cpuid); 131 if (cs == NULL) { 132 return MEMTX_DECODE_ERROR; 133 } 134 135 /* override requester_id */ 136 addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c); 137 attrs.requester_id = cs->cpu_index; 138 send_ipi_data(&LOONGARCH_CPU(cs)->env, val, addr, attrs); 139 return MEMTX_OK; 140 } 141 142 static MemTxResult any_send(uint64_t val, MemTxAttrs attrs) 143 { 144 uint32_t cpuid; 145 hwaddr addr; 146 CPUState *cs; 147 148 cpuid = extract32(val, 16, 10); 149 if (cpuid >= LOONGARCH_MAX_CPUS) { 150 trace_loongarch_ipi_unsupported_cpuid("IOCSR_ANY_SEND", cpuid); 151 return MEMTX_DECODE_ERROR; 152 } 153 154 cs = ipi_getcpu(cpuid); 155 if (cs == NULL) { 156 return MEMTX_DECODE_ERROR; 157 } 158 159 /* override requester_id */ 160 addr = val & 0xffff; 161 attrs.requester_id = cs->cpu_index; 162 send_ipi_data(&LOONGARCH_CPU(cs)->env, val, addr, attrs); 163 return MEMTX_OK; 164 } 165 166 static MemTxResult loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val, 167 unsigned size, MemTxAttrs attrs) 168 { 169 LoongArchIPI *ipi = opaque; 170 IPICore *s; 171 int index = 0; 172 uint32_t cpuid; 173 uint8_t vector; 174 CPUState *cs; 175 176 s = &ipi->cpu[attrs.requester_id]; 177 addr &= 0xff; 178 trace_loongarch_ipi_write(size, (uint64_t)addr, val); 179 switch (addr) { 180 case CORE_STATUS_OFF: 181 qemu_log_mask(LOG_GUEST_ERROR, "can not be written"); 182 break; 183 case CORE_EN_OFF: 184 s->en = val; 185 break; 186 case CORE_SET_OFF: 187 s->status |= val; 188 if (s->status != 0 && (s->status & s->en) != 0) { 189 qemu_irq_raise(s->irq); 190 } 191 break; 192 case CORE_CLEAR_OFF: 193 s->status &= ~val; 194 if (s->status == 0 && s->en != 0) { 195 qemu_irq_lower(s->irq); 196 } 197 break; 198 case CORE_BUF_20 ... CORE_BUF_38 + 4: 199 index = (addr - CORE_BUF_20) >> 2; 200 s->buf[index] = val; 201 break; 202 case IOCSR_IPI_SEND: 203 cpuid = extract32(val, 16, 10); 204 if (cpuid >= LOONGARCH_MAX_CPUS) { 205 trace_loongarch_ipi_unsupported_cpuid("IOCSR_IPI_SEND", cpuid); 206 return MEMTX_DECODE_ERROR; 207 } 208 209 /* IPI status vector */ 210 vector = extract8(val, 0, 5); 211 cs = ipi_getcpu(cpuid); 212 if (cs == NULL) { 213 return MEMTX_DECODE_ERROR; 214 } 215 216 /* override requester_id */ 217 attrs.requester_id = cs->cpu_index; 218 loongarch_ipi_writel(ipi, CORE_SET_OFF, BIT(vector), 4, attrs); 219 break; 220 default: 221 qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr); 222 break; 223 } 224 225 return MEMTX_OK; 226 } 227 228 static const MemoryRegionOps loongarch_ipi_ops = { 229 .read_with_attrs = loongarch_ipi_readl, 230 .write_with_attrs = loongarch_ipi_writel, 231 .impl.min_access_size = 4, 232 .impl.max_access_size = 4, 233 .valid.min_access_size = 4, 234 .valid.max_access_size = 8, 235 .endianness = DEVICE_LITTLE_ENDIAN, 236 }; 237 238 /* mail send and any send only support writeq */ 239 static MemTxResult loongarch_ipi_writeq(void *opaque, hwaddr addr, uint64_t val, 240 unsigned size, MemTxAttrs attrs) 241 { 242 MemTxResult ret = MEMTX_OK; 243 244 addr &= 0xfff; 245 switch (addr) { 246 case MAIL_SEND_OFFSET: 247 ret = mail_send(val, attrs); 248 break; 249 case ANY_SEND_OFFSET: 250 ret = any_send(val, attrs); 251 break; 252 default: 253 break; 254 } 255 256 return ret; 257 } 258 259 static const MemoryRegionOps loongarch_ipi64_ops = { 260 .write_with_attrs = loongarch_ipi_writeq, 261 .impl.min_access_size = 8, 262 .impl.max_access_size = 8, 263 .valid.min_access_size = 8, 264 .valid.max_access_size = 8, 265 .endianness = DEVICE_LITTLE_ENDIAN, 266 }; 267 268 static void loongarch_ipi_realize(DeviceState *dev, Error **errp) 269 { 270 LoongArchIPI *s = LOONGARCH_IPI(dev); 271 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 272 int i; 273 274 if (s->num_cpu == 0) { 275 error_setg(errp, "num-cpu must be at least 1"); 276 return; 277 } 278 279 memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev), &loongarch_ipi_ops, 280 s, "loongarch_ipi_iocsr", 0x48); 281 282 /* loongarch_ipi_iocsr performs re-entrant IO through ipi_send */ 283 s->ipi_iocsr_mem.disable_reentrancy_guard = true; 284 285 sysbus_init_mmio(sbd, &s->ipi_iocsr_mem); 286 287 memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev), 288 &loongarch_ipi64_ops, 289 s, "loongarch_ipi64_iocsr", 0x118); 290 sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem); 291 292 s->cpu = g_new0(IPICore, s->num_cpu); 293 if (s->cpu == NULL) { 294 error_setg(errp, "Memory allocation for ExtIOICore faile"); 295 return; 296 } 297 298 for (i = 0; i < s->num_cpu; i++) { 299 qdev_init_gpio_out(dev, &s->cpu[i].irq, 1); 300 } 301 } 302 303 static const VMStateDescription vmstate_ipi_core = { 304 .name = "ipi-single", 305 .version_id = 2, 306 .minimum_version_id = 2, 307 .fields = (const VMStateField[]) { 308 VMSTATE_UINT32(status, IPICore), 309 VMSTATE_UINT32(en, IPICore), 310 VMSTATE_UINT32(set, IPICore), 311 VMSTATE_UINT32(clear, IPICore), 312 VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2), 313 VMSTATE_END_OF_LIST() 314 } 315 }; 316 317 static const VMStateDescription vmstate_loongarch_ipi = { 318 .name = TYPE_LOONGARCH_IPI, 319 .version_id = 2, 320 .minimum_version_id = 2, 321 .fields = (const VMStateField[]) { 322 VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchIPI, num_cpu, 323 vmstate_ipi_core, IPICore), 324 VMSTATE_END_OF_LIST() 325 } 326 }; 327 328 static Property ipi_properties[] = { 329 DEFINE_PROP_UINT32("num-cpu", LoongArchIPI, num_cpu, 1), 330 DEFINE_PROP_END_OF_LIST(), 331 }; 332 333 static void loongarch_ipi_class_init(ObjectClass *klass, void *data) 334 { 335 DeviceClass *dc = DEVICE_CLASS(klass); 336 337 dc->realize = loongarch_ipi_realize; 338 device_class_set_props(dc, ipi_properties); 339 dc->vmsd = &vmstate_loongarch_ipi; 340 } 341 342 static void loongarch_ipi_finalize(Object *obj) 343 { 344 LoongArchIPI *s = LOONGARCH_IPI(obj); 345 346 g_free(s->cpu); 347 } 348 349 static const TypeInfo loongarch_ipi_info = { 350 .name = TYPE_LOONGARCH_IPI, 351 .parent = TYPE_SYS_BUS_DEVICE, 352 .instance_size = sizeof(LoongArchIPI), 353 .class_init = loongarch_ipi_class_init, 354 .instance_finalize = loongarch_ipi_finalize, 355 }; 356 357 static void loongarch_ipi_register_types(void) 358 { 359 type_register_static(&loongarch_ipi_info); 360 } 361 362 type_init(loongarch_ipi_register_types) 363