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 void loongarch_ipi_writel(void *, hwaddr, uint64_t, unsigned); 21 22 static uint64_t loongarch_ipi_readl(void *opaque, hwaddr addr, unsigned size) 23 { 24 IPICore *s = opaque; 25 uint64_t ret = 0; 26 int index = 0; 27 28 addr &= 0xff; 29 switch (addr) { 30 case CORE_STATUS_OFF: 31 ret = s->status; 32 break; 33 case CORE_EN_OFF: 34 ret = s->en; 35 break; 36 case CORE_SET_OFF: 37 ret = 0; 38 break; 39 case CORE_CLEAR_OFF: 40 ret = 0; 41 break; 42 case CORE_BUF_20 ... CORE_BUF_38 + 4: 43 index = (addr - CORE_BUF_20) >> 2; 44 ret = s->buf[index]; 45 break; 46 default: 47 qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr); 48 break; 49 } 50 51 trace_loongarch_ipi_read(size, (uint64_t)addr, ret); 52 return ret; 53 } 54 55 static void send_ipi_data(CPULoongArchState *env, uint64_t val, hwaddr addr) 56 { 57 int i, mask = 0, data = 0; 58 59 /* 60 * bit 27-30 is mask for byte writing, 61 * if the mask is 0, we need not to do anything. 62 */ 63 if ((val >> 27) & 0xf) { 64 data = address_space_ldl(&env->address_space_iocsr, addr, 65 MEMTXATTRS_UNSPECIFIED, NULL); 66 for (i = 0; i < 4; i++) { 67 /* get mask for byte writing */ 68 if (val & (0x1 << (27 + i))) { 69 mask |= 0xff << (i * 8); 70 } 71 } 72 } 73 74 data &= mask; 75 data |= (val >> 32) & ~mask; 76 address_space_stl(&env->address_space_iocsr, addr, 77 data, MEMTXATTRS_UNSPECIFIED, NULL); 78 } 79 80 static int archid_cmp(const void *a, const void *b) 81 { 82 CPUArchId *archid_a = (CPUArchId *)a; 83 CPUArchId *archid_b = (CPUArchId *)b; 84 85 return archid_a->arch_id - archid_b->arch_id; 86 } 87 88 static CPUArchId *find_cpu_by_archid(MachineState *ms, uint32_t id) 89 { 90 CPUArchId apic_id, *found_cpu; 91 92 apic_id.arch_id = id; 93 found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus, 94 ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus), 95 archid_cmp); 96 97 return found_cpu; 98 } 99 100 static CPUState *ipi_getcpu(int arch_id) 101 { 102 MachineState *machine = MACHINE(qdev_get_machine()); 103 CPUArchId *archid; 104 105 archid = find_cpu_by_archid(machine, arch_id); 106 return CPU(archid->cpu); 107 } 108 109 static void ipi_send(uint64_t val) 110 { 111 uint32_t cpuid; 112 uint8_t vector; 113 CPUState *cs; 114 LoongArchCPU *cpu; 115 LoongArchIPI *s; 116 117 cpuid = extract32(val, 16, 10); 118 if (cpuid >= LOONGARCH_MAX_CPUS) { 119 trace_loongarch_ipi_unsupported_cpuid("IOCSR_IPI_SEND", cpuid); 120 return; 121 } 122 123 /* IPI status vector */ 124 vector = extract8(val, 0, 5); 125 126 cs = ipi_getcpu(cpuid); 127 cpu = LOONGARCH_CPU(cs); 128 s = LOONGARCH_IPI(cpu->env.ipistate); 129 loongarch_ipi_writel(&s->ipi_core, CORE_SET_OFF, BIT(vector), 4); 130 } 131 132 static void mail_send(uint64_t val) 133 { 134 uint32_t cpuid; 135 hwaddr addr; 136 CPULoongArchState *env; 137 CPUState *cs; 138 LoongArchCPU *cpu; 139 140 cpuid = extract32(val, 16, 10); 141 if (cpuid >= LOONGARCH_MAX_CPUS) { 142 trace_loongarch_ipi_unsupported_cpuid("IOCSR_MAIL_SEND", cpuid); 143 return; 144 } 145 146 addr = 0x1020 + (val & 0x1c); 147 cs = ipi_getcpu(cpuid); 148 cpu = LOONGARCH_CPU(cs); 149 env = &cpu->env; 150 send_ipi_data(env, val, addr); 151 } 152 153 static void any_send(uint64_t val) 154 { 155 uint32_t cpuid; 156 hwaddr addr; 157 CPULoongArchState *env; 158 CPUState *cs; 159 LoongArchCPU *cpu; 160 161 cpuid = extract32(val, 16, 10); 162 if (cpuid >= LOONGARCH_MAX_CPUS) { 163 trace_loongarch_ipi_unsupported_cpuid("IOCSR_ANY_SEND", cpuid); 164 return; 165 } 166 167 addr = val & 0xffff; 168 cs = ipi_getcpu(cpuid); 169 cpu = LOONGARCH_CPU(cs); 170 env = &cpu->env; 171 send_ipi_data(env, val, addr); 172 } 173 174 static void loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val, 175 unsigned size) 176 { 177 IPICore *s = opaque; 178 int index = 0; 179 180 addr &= 0xff; 181 trace_loongarch_ipi_write(size, (uint64_t)addr, val); 182 switch (addr) { 183 case CORE_STATUS_OFF: 184 qemu_log_mask(LOG_GUEST_ERROR, "can not be written"); 185 break; 186 case CORE_EN_OFF: 187 s->en = val; 188 break; 189 case CORE_SET_OFF: 190 s->status |= val; 191 if (s->status != 0 && (s->status & s->en) != 0) { 192 qemu_irq_raise(s->irq); 193 } 194 break; 195 case CORE_CLEAR_OFF: 196 s->status &= ~val; 197 if (s->status == 0 && s->en != 0) { 198 qemu_irq_lower(s->irq); 199 } 200 break; 201 case CORE_BUF_20 ... CORE_BUF_38 + 4: 202 index = (addr - CORE_BUF_20) >> 2; 203 s->buf[index] = val; 204 break; 205 case IOCSR_IPI_SEND: 206 ipi_send(val); 207 break; 208 default: 209 qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr); 210 break; 211 } 212 } 213 214 static const MemoryRegionOps loongarch_ipi_ops = { 215 .read = loongarch_ipi_readl, 216 .write = loongarch_ipi_writel, 217 .impl.min_access_size = 4, 218 .impl.max_access_size = 4, 219 .valid.min_access_size = 4, 220 .valid.max_access_size = 8, 221 .endianness = DEVICE_LITTLE_ENDIAN, 222 }; 223 224 /* mail send and any send only support writeq */ 225 static void loongarch_ipi_writeq(void *opaque, hwaddr addr, uint64_t val, 226 unsigned size) 227 { 228 addr &= 0xfff; 229 switch (addr) { 230 case MAIL_SEND_OFFSET: 231 mail_send(val); 232 break; 233 case ANY_SEND_OFFSET: 234 any_send(val); 235 break; 236 default: 237 break; 238 } 239 } 240 241 static const MemoryRegionOps loongarch_ipi64_ops = { 242 .write = loongarch_ipi_writeq, 243 .impl.min_access_size = 8, 244 .impl.max_access_size = 8, 245 .valid.min_access_size = 8, 246 .valid.max_access_size = 8, 247 .endianness = DEVICE_LITTLE_ENDIAN, 248 }; 249 250 static void loongarch_ipi_init(Object *obj) 251 { 252 LoongArchIPI *s = LOONGARCH_IPI(obj); 253 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 254 255 memory_region_init_io(&s->ipi_iocsr_mem, obj, &loongarch_ipi_ops, 256 &s->ipi_core, "loongarch_ipi_iocsr", 0x48); 257 258 /* loongarch_ipi_iocsr performs re-entrant IO through ipi_send */ 259 s->ipi_iocsr_mem.disable_reentrancy_guard = true; 260 261 sysbus_init_mmio(sbd, &s->ipi_iocsr_mem); 262 263 memory_region_init_io(&s->ipi64_iocsr_mem, obj, &loongarch_ipi64_ops, 264 &s->ipi_core, "loongarch_ipi64_iocsr", 0x118); 265 sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem); 266 qdev_init_gpio_out(DEVICE(obj), &s->ipi_core.irq, 1); 267 } 268 269 static const VMStateDescription vmstate_ipi_core = { 270 .name = "ipi-single", 271 .version_id = 2, 272 .minimum_version_id = 2, 273 .fields = (VMStateField[]) { 274 VMSTATE_UINT32(status, IPICore), 275 VMSTATE_UINT32(en, IPICore), 276 VMSTATE_UINT32(set, IPICore), 277 VMSTATE_UINT32(clear, IPICore), 278 VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2), 279 VMSTATE_END_OF_LIST() 280 } 281 }; 282 283 static const VMStateDescription vmstate_loongarch_ipi = { 284 .name = TYPE_LOONGARCH_IPI, 285 .version_id = 1, 286 .minimum_version_id = 1, 287 .fields = (VMStateField[]) { 288 VMSTATE_STRUCT(ipi_core, LoongArchIPI, 0, vmstate_ipi_core, IPICore), 289 VMSTATE_END_OF_LIST() 290 } 291 }; 292 293 static void loongarch_ipi_class_init(ObjectClass *klass, void *data) 294 { 295 DeviceClass *dc = DEVICE_CLASS(klass); 296 297 dc->vmsd = &vmstate_loongarch_ipi; 298 } 299 300 static const TypeInfo loongarch_ipi_info = { 301 .name = TYPE_LOONGARCH_IPI, 302 .parent = TYPE_SYS_BUS_DEVICE, 303 .instance_size = sizeof(LoongArchIPI), 304 .instance_init = loongarch_ipi_init, 305 .class_init = loongarch_ipi_class_init, 306 }; 307 308 static void loongarch_ipi_register_types(void) 309 { 310 type_register_static(&loongarch_ipi_info); 311 } 312 313 type_init(loongarch_ipi_register_types) 314