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