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