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/boards.h" 10 #include "hw/sysbus.h" 11 #include "hw/intc/loongarch_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 #include "target/loongarch/cpu.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 cs = ipi_getcpu(cpuid); 126 if (cs == NULL) { 127 return MEMTX_DECODE_ERROR; 128 } 129 130 /* override requester_id */ 131 addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c); 132 attrs.requester_id = cs->cpu_index; 133 send_ipi_data(&LOONGARCH_CPU(cs)->env, val, addr, attrs); 134 return MEMTX_OK; 135 } 136 137 static MemTxResult any_send(uint64_t val, MemTxAttrs attrs) 138 { 139 uint32_t cpuid; 140 hwaddr addr; 141 CPUState *cs; 142 143 cpuid = extract32(val, 16, 10); 144 cs = ipi_getcpu(cpuid); 145 if (cs == NULL) { 146 return MEMTX_DECODE_ERROR; 147 } 148 149 /* override requester_id */ 150 addr = val & 0xffff; 151 attrs.requester_id = cs->cpu_index; 152 send_ipi_data(&LOONGARCH_CPU(cs)->env, val, addr, attrs); 153 return MEMTX_OK; 154 } 155 156 static MemTxResult loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val, 157 unsigned size, MemTxAttrs attrs) 158 { 159 LoongArchIPI *ipi = opaque; 160 IPICore *s; 161 int index = 0; 162 uint32_t cpuid; 163 uint8_t vector; 164 CPUState *cs; 165 166 s = &ipi->cpu[attrs.requester_id]; 167 addr &= 0xff; 168 trace_loongarch_ipi_write(size, (uint64_t)addr, val); 169 switch (addr) { 170 case CORE_STATUS_OFF: 171 qemu_log_mask(LOG_GUEST_ERROR, "can not be written"); 172 break; 173 case CORE_EN_OFF: 174 s->en = val; 175 break; 176 case CORE_SET_OFF: 177 s->status |= val; 178 if (s->status != 0 && (s->status & s->en) != 0) { 179 qemu_irq_raise(s->irq); 180 } 181 break; 182 case CORE_CLEAR_OFF: 183 s->status &= ~val; 184 if (s->status == 0 && s->en != 0) { 185 qemu_irq_lower(s->irq); 186 } 187 break; 188 case CORE_BUF_20 ... CORE_BUF_38 + 4: 189 index = (addr - CORE_BUF_20) >> 2; 190 s->buf[index] = val; 191 break; 192 case IOCSR_IPI_SEND: 193 cpuid = extract32(val, 16, 10); 194 /* IPI status vector */ 195 vector = extract8(val, 0, 5); 196 cs = ipi_getcpu(cpuid); 197 if (cs == NULL) { 198 return MEMTX_DECODE_ERROR; 199 } 200 201 /* override requester_id */ 202 attrs.requester_id = cs->cpu_index; 203 loongarch_ipi_writel(ipi, CORE_SET_OFF, 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 const MemoryRegionOps loongarch_ipi_ops = { 214 .read_with_attrs = loongarch_ipi_readl, 215 .write_with_attrs = loongarch_ipi_writel, 216 .impl.min_access_size = 4, 217 .impl.max_access_size = 4, 218 .valid.min_access_size = 4, 219 .valid.max_access_size = 8, 220 .endianness = DEVICE_LITTLE_ENDIAN, 221 }; 222 223 /* mail send and any send only support writeq */ 224 static MemTxResult loongarch_ipi_writeq(void *opaque, hwaddr addr, uint64_t val, 225 unsigned size, MemTxAttrs attrs) 226 { 227 MemTxResult ret = MEMTX_OK; 228 229 addr &= 0xfff; 230 switch (addr) { 231 case MAIL_SEND_OFFSET: 232 ret = mail_send(val, attrs); 233 break; 234 case ANY_SEND_OFFSET: 235 ret = any_send(val, attrs); 236 break; 237 default: 238 break; 239 } 240 241 return ret; 242 } 243 244 static const MemoryRegionOps loongarch_ipi64_ops = { 245 .write_with_attrs = loongarch_ipi_writeq, 246 .impl.min_access_size = 8, 247 .impl.max_access_size = 8, 248 .valid.min_access_size = 8, 249 .valid.max_access_size = 8, 250 .endianness = DEVICE_LITTLE_ENDIAN, 251 }; 252 253 static void loongarch_ipi_realize(DeviceState *dev, Error **errp) 254 { 255 LoongArchIPI *s = LOONGARCH_IPI(dev); 256 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 257 int i; 258 259 if (s->num_cpu == 0) { 260 error_setg(errp, "num-cpu must be at least 1"); 261 return; 262 } 263 264 memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev), &loongarch_ipi_ops, 265 s, "loongarch_ipi_iocsr", 0x48); 266 267 /* loongarch_ipi_iocsr performs re-entrant IO through ipi_send */ 268 s->ipi_iocsr_mem.disable_reentrancy_guard = true; 269 270 sysbus_init_mmio(sbd, &s->ipi_iocsr_mem); 271 272 memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev), 273 &loongarch_ipi64_ops, 274 s, "loongarch_ipi64_iocsr", 0x118); 275 sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem); 276 277 s->cpu = g_new0(IPICore, s->num_cpu); 278 if (s->cpu == NULL) { 279 error_setg(errp, "Memory allocation for ExtIOICore faile"); 280 return; 281 } 282 283 for (i = 0; i < s->num_cpu; i++) { 284 qdev_init_gpio_out(dev, &s->cpu[i].irq, 1); 285 } 286 } 287 288 static const VMStateDescription vmstate_ipi_core = { 289 .name = "ipi-single", 290 .version_id = 2, 291 .minimum_version_id = 2, 292 .fields = (const VMStateField[]) { 293 VMSTATE_UINT32(status, IPICore), 294 VMSTATE_UINT32(en, IPICore), 295 VMSTATE_UINT32(set, IPICore), 296 VMSTATE_UINT32(clear, IPICore), 297 VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2), 298 VMSTATE_END_OF_LIST() 299 } 300 }; 301 302 static const VMStateDescription vmstate_loongarch_ipi = { 303 .name = TYPE_LOONGARCH_IPI, 304 .version_id = 2, 305 .minimum_version_id = 2, 306 .fields = (const VMStateField[]) { 307 VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchIPI, num_cpu, 308 vmstate_ipi_core, IPICore), 309 VMSTATE_END_OF_LIST() 310 } 311 }; 312 313 static Property ipi_properties[] = { 314 DEFINE_PROP_UINT32("num-cpu", LoongArchIPI, num_cpu, 1), 315 DEFINE_PROP_END_OF_LIST(), 316 }; 317 318 static void loongarch_ipi_class_init(ObjectClass *klass, void *data) 319 { 320 DeviceClass *dc = DEVICE_CLASS(klass); 321 322 dc->realize = loongarch_ipi_realize; 323 device_class_set_props(dc, ipi_properties); 324 dc->vmsd = &vmstate_loongarch_ipi; 325 } 326 327 static void loongarch_ipi_finalize(Object *obj) 328 { 329 LoongArchIPI *s = LOONGARCH_IPI(obj); 330 331 g_free(s->cpu); 332 } 333 334 static const TypeInfo loongarch_ipi_info = { 335 .name = TYPE_LOONGARCH_IPI, 336 .parent = TYPE_SYS_BUS_DEVICE, 337 .instance_size = sizeof(LoongArchIPI), 338 .class_init = loongarch_ipi_class_init, 339 .instance_finalize = loongarch_ipi_finalize, 340 }; 341 342 static void loongarch_ipi_register_types(void) 343 { 344 type_register_static(&loongarch_ipi_info); 345 } 346 347 type_init(loongarch_ipi_register_types) 348