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 "qapi/error.h" 13 #include "qemu/log.h" 14 #include "exec/address-spaces.h" 15 #include "hw/loongarch/virt.h" 16 #include "migration/vmstate.h" 17 #include "target/loongarch/internals.h" 18 #include "trace.h" 19 20 static MemTxResult loongarch_ipi_readl(void *opaque, hwaddr addr, 21 uint64_t *data, 22 unsigned size, MemTxAttrs attrs) 23 { 24 IPICore *s; 25 LoongArchIPI *ipi = opaque; 26 uint64_t ret = 0; 27 int index = 0; 28 29 s = &ipi->ipi_core; 30 addr &= 0xff; 31 switch (addr) { 32 case CORE_STATUS_OFF: 33 ret = s->status; 34 break; 35 case CORE_EN_OFF: 36 ret = s->en; 37 break; 38 case CORE_SET_OFF: 39 ret = 0; 40 break; 41 case CORE_CLEAR_OFF: 42 ret = 0; 43 break; 44 case CORE_BUF_20 ... CORE_BUF_38 + 4: 45 index = (addr - CORE_BUF_20) >> 2; 46 ret = s->buf[index]; 47 break; 48 default: 49 qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr); 50 break; 51 } 52 53 trace_loongarch_ipi_read(size, (uint64_t)addr, ret); 54 *data = ret; 55 return MEMTX_OK; 56 } 57 58 static void send_ipi_data(CPULoongArchState *env, uint64_t val, hwaddr addr, 59 MemTxAttrs attrs) 60 { 61 int i, mask = 0, data = 0; 62 63 /* 64 * bit 27-30 is mask for byte writing, 65 * if the mask is 0, we need not to do anything. 66 */ 67 if ((val >> 27) & 0xf) { 68 data = address_space_ldl(&env->address_space_iocsr, addr, 69 attrs, NULL); 70 for (i = 0; i < 4; i++) { 71 /* get mask for byte writing */ 72 if (val & (0x1 << (27 + i))) { 73 mask |= 0xff << (i * 8); 74 } 75 } 76 } 77 78 data &= mask; 79 data |= (val >> 32) & ~mask; 80 address_space_stl(&env->address_space_iocsr, addr, 81 data, attrs, NULL); 82 } 83 84 static int archid_cmp(const void *a, const void *b) 85 { 86 CPUArchId *archid_a = (CPUArchId *)a; 87 CPUArchId *archid_b = (CPUArchId *)b; 88 89 return archid_a->arch_id - archid_b->arch_id; 90 } 91 92 static CPUArchId *find_cpu_by_archid(MachineState *ms, uint32_t id) 93 { 94 CPUArchId apic_id, *found_cpu; 95 96 apic_id.arch_id = id; 97 found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus, 98 ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus), 99 archid_cmp); 100 101 return found_cpu; 102 } 103 104 static CPUState *ipi_getcpu(int arch_id) 105 { 106 MachineState *machine = MACHINE(qdev_get_machine()); 107 CPUArchId *archid; 108 109 archid = find_cpu_by_archid(machine, arch_id); 110 if (archid) { 111 return CPU(archid->cpu); 112 } 113 114 return NULL; 115 } 116 117 static MemTxResult mail_send(uint64_t val, MemTxAttrs attrs) 118 { 119 uint32_t cpuid; 120 hwaddr addr; 121 CPUState *cs; 122 123 cpuid = extract32(val, 16, 10); 124 if (cpuid >= LOONGARCH_MAX_CPUS) { 125 trace_loongarch_ipi_unsupported_cpuid("IOCSR_MAIL_SEND", cpuid); 126 return MEMTX_DECODE_ERROR; 127 } 128 129 cs = ipi_getcpu(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 send_ipi_data(&LOONGARCH_CPU(cs)->env, val, addr, attrs); 138 return MEMTX_OK; 139 } 140 141 static MemTxResult any_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 if (cpuid >= LOONGARCH_MAX_CPUS) { 149 trace_loongarch_ipi_unsupported_cpuid("IOCSR_ANY_SEND", cpuid); 150 return MEMTX_DECODE_ERROR; 151 } 152 153 cs = ipi_getcpu(cpuid); 154 if (cs == NULL) { 155 return MEMTX_DECODE_ERROR; 156 } 157 158 /* override requester_id */ 159 addr = val & 0xffff; 160 attrs.requester_id = cs->cpu_index; 161 send_ipi_data(&LOONGARCH_CPU(cs)->env, val, addr, attrs); 162 return MEMTX_OK; 163 } 164 165 static MemTxResult loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val, 166 unsigned size, MemTxAttrs attrs) 167 { 168 LoongArchIPI *ipi = opaque; 169 IPICore *s; 170 int index = 0; 171 uint32_t cpuid; 172 uint8_t vector; 173 CPUState *cs; 174 175 s = &ipi->ipi_core; 176 addr &= 0xff; 177 trace_loongarch_ipi_write(size, (uint64_t)addr, val); 178 switch (addr) { 179 case CORE_STATUS_OFF: 180 qemu_log_mask(LOG_GUEST_ERROR, "can not be written"); 181 break; 182 case CORE_EN_OFF: 183 s->en = val; 184 break; 185 case CORE_SET_OFF: 186 s->status |= val; 187 if (s->status != 0 && (s->status & s->en) != 0) { 188 qemu_irq_raise(s->irq); 189 } 190 break; 191 case CORE_CLEAR_OFF: 192 s->status &= ~val; 193 if (s->status == 0 && s->en != 0) { 194 qemu_irq_lower(s->irq); 195 } 196 break; 197 case CORE_BUF_20 ... CORE_BUF_38 + 4: 198 index = (addr - CORE_BUF_20) >> 2; 199 s->buf[index] = val; 200 break; 201 case IOCSR_IPI_SEND: 202 cpuid = extract32(val, 16, 10); 203 if (cpuid >= LOONGARCH_MAX_CPUS) { 204 trace_loongarch_ipi_unsupported_cpuid("IOCSR_IPI_SEND", cpuid); 205 return MEMTX_DECODE_ERROR; 206 } 207 208 /* IPI status vector */ 209 vector = extract8(val, 0, 5); 210 cs = ipi_getcpu(cpuid); 211 if (cs == NULL) { 212 return MEMTX_DECODE_ERROR; 213 } 214 215 /* override requester_id */ 216 attrs.requester_id = cs->cpu_index; 217 ipi = LOONGARCH_IPI(LOONGARCH_CPU(cs)->env.ipistate); 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_init(Object *obj) 269 { 270 LoongArchIPI *s = LOONGARCH_IPI(obj); 271 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 272 273 memory_region_init_io(&s->ipi_iocsr_mem, obj, &loongarch_ipi_ops, 274 s, "loongarch_ipi_iocsr", 0x48); 275 276 /* loongarch_ipi_iocsr performs re-entrant IO through ipi_send */ 277 s->ipi_iocsr_mem.disable_reentrancy_guard = true; 278 279 sysbus_init_mmio(sbd, &s->ipi_iocsr_mem); 280 281 memory_region_init_io(&s->ipi64_iocsr_mem, obj, &loongarch_ipi64_ops, 282 s, "loongarch_ipi64_iocsr", 0x118); 283 sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem); 284 qdev_init_gpio_out(DEVICE(obj), &s->ipi_core.irq, 1); 285 } 286 287 static const VMStateDescription vmstate_ipi_core = { 288 .name = "ipi-single", 289 .version_id = 2, 290 .minimum_version_id = 2, 291 .fields = (const VMStateField[]) { 292 VMSTATE_UINT32(status, IPICore), 293 VMSTATE_UINT32(en, IPICore), 294 VMSTATE_UINT32(set, IPICore), 295 VMSTATE_UINT32(clear, IPICore), 296 VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2), 297 VMSTATE_END_OF_LIST() 298 } 299 }; 300 301 static const VMStateDescription vmstate_loongarch_ipi = { 302 .name = TYPE_LOONGARCH_IPI, 303 .version_id = 1, 304 .minimum_version_id = 1, 305 .fields = (const VMStateField[]) { 306 VMSTATE_STRUCT(ipi_core, LoongArchIPI, 0, vmstate_ipi_core, IPICore), 307 VMSTATE_END_OF_LIST() 308 } 309 }; 310 311 static void loongarch_ipi_class_init(ObjectClass *klass, void *data) 312 { 313 DeviceClass *dc = DEVICE_CLASS(klass); 314 315 dc->vmsd = &vmstate_loongarch_ipi; 316 } 317 318 static const TypeInfo loongarch_ipi_info = { 319 .name = TYPE_LOONGARCH_IPI, 320 .parent = TYPE_SYS_BUS_DEVICE, 321 .instance_size = sizeof(LoongArchIPI), 322 .instance_init = loongarch_ipi_init, 323 .class_init = loongarch_ipi_class_init, 324 }; 325 326 static void loongarch_ipi_register_types(void) 327 { 328 type_register_static(&loongarch_ipi_info); 329 } 330 331 type_init(loongarch_ipi_register_types) 332