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