1 /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 /* 3 * Loongson IPI interrupt common 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/loongson_ipi_common.h" 11 #include "hw/irq.h" 12 #include "qemu/log.h" 13 #include "migration/vmstate.h" 14 #include "system/kvm.h" 15 #include "trace.h" 16 17 MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr, uint64_t *data, 18 unsigned size, MemTxAttrs attrs) 19 { 20 IPICore *s = opaque; 21 uint64_t ret = 0; 22 int index = 0; 23 24 addr &= 0xff; 25 switch (addr) { 26 case CORE_STATUS_OFF: 27 ret = s->status; 28 break; 29 case CORE_EN_OFF: 30 ret = s->en; 31 break; 32 case CORE_SET_OFF: 33 ret = 0; 34 break; 35 case CORE_CLEAR_OFF: 36 ret = 0; 37 break; 38 case CORE_BUF_20 ... CORE_BUF_38 + 4: 39 index = (addr - CORE_BUF_20) >> 2; 40 ret = s->buf[index]; 41 break; 42 default: 43 qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr); 44 break; 45 } 46 47 trace_loongson_ipi_read(size, (uint64_t)addr, ret); 48 *data = ret; 49 50 return MEMTX_OK; 51 } 52 53 static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr, 54 uint64_t *data, unsigned size, 55 MemTxAttrs attrs) 56 { 57 LoongsonIPICommonState *ipi = opaque; 58 IPICore *s; 59 60 if (attrs.requester_id >= ipi->num_cpu) { 61 return MEMTX_DECODE_ERROR; 62 } 63 64 s = &ipi->cpu[attrs.requester_id]; 65 return loongson_ipi_core_readl(s, addr, data, size, attrs); 66 } 67 68 static MemTxResult send_ipi_data(LoongsonIPICommonState *ipi, CPUState *cpu, 69 uint64_t val, hwaddr addr, MemTxAttrs attrs) 70 { 71 LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); 72 int i, mask = 0, data = 0; 73 AddressSpace *iocsr_as = licc->get_iocsr_as(cpu); 74 75 if (!iocsr_as) { 76 return MEMTX_DECODE_ERROR; 77 } 78 79 /* 80 * bit 27-30 is mask for byte writing, 81 * if the mask is 0, we need not to do anything. 82 */ 83 if ((val >> 27) & 0xf) { 84 data = address_space_ldl_le(iocsr_as, addr, attrs, NULL); 85 for (i = 0; i < 4; i++) { 86 /* get mask for byte writing */ 87 if (val & (0x1 << (27 + i))) { 88 mask |= 0xff << (i * 8); 89 } 90 } 91 } 92 93 data &= mask; 94 data |= (val >> 32) & ~mask; 95 address_space_stl_le(iocsr_as, addr, data, attrs, NULL); 96 97 return MEMTX_OK; 98 } 99 100 static MemTxResult mail_send(LoongsonIPICommonState *ipi, 101 uint64_t val, MemTxAttrs attrs) 102 { 103 LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); 104 uint32_t cpuid; 105 hwaddr addr; 106 CPUState *cs; 107 int cpu, ret; 108 109 cpuid = extract32(val, 16, 10); 110 ret = licc->cpu_by_arch_id(ipi, cpuid, &cpu, &cs); 111 if (ret != MEMTX_OK) { 112 return MEMTX_DECODE_ERROR; 113 } 114 115 /* override requester_id */ 116 addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c); 117 attrs.requester_id = cpu; 118 return send_ipi_data(ipi, cs, val, addr, attrs); 119 } 120 121 static MemTxResult any_send(LoongsonIPICommonState *ipi, 122 uint64_t val, MemTxAttrs attrs) 123 { 124 LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); 125 uint32_t cpuid; 126 hwaddr addr; 127 CPUState *cs; 128 int cpu, ret; 129 130 cpuid = extract32(val, 16, 10); 131 ret = licc->cpu_by_arch_id(ipi, cpuid, &cpu, &cs); 132 if (ret != MEMTX_OK) { 133 return MEMTX_DECODE_ERROR; 134 } 135 136 /* override requester_id */ 137 addr = val & 0xffff; 138 attrs.requester_id = cpu; 139 return send_ipi_data(ipi, cs, val, addr, attrs); 140 } 141 142 MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, uint64_t val, 143 unsigned size, MemTxAttrs attrs) 144 { 145 IPICore *s = opaque; 146 LoongsonIPICommonState *ipi = s->ipi; 147 LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); 148 int index = 0; 149 uint32_t cpuid; 150 uint8_t vector; 151 CPUState *cs; 152 int cpu, ret; 153 154 addr &= 0xff; 155 trace_loongson_ipi_write(size, (uint64_t)addr, val); 156 switch (addr) { 157 case CORE_STATUS_OFF: 158 qemu_log_mask(LOG_GUEST_ERROR, "can not be written"); 159 break; 160 case CORE_EN_OFF: 161 s->en = val; 162 break; 163 case CORE_SET_OFF: 164 s->status |= val; 165 if (s->status != 0 && (s->status & s->en) != 0) { 166 qemu_irq_raise(s->irq); 167 } 168 break; 169 case CORE_CLEAR_OFF: 170 s->status &= ~val; 171 if (s->status == 0 && s->en != 0) { 172 qemu_irq_lower(s->irq); 173 } 174 break; 175 case CORE_BUF_20 ... CORE_BUF_38 + 4: 176 index = (addr - CORE_BUF_20) >> 2; 177 s->buf[index] = val; 178 break; 179 case IOCSR_IPI_SEND: 180 cpuid = extract32(val, 16, 10); 181 /* IPI status vector */ 182 vector = extract8(val, 0, 5); 183 ret = licc->cpu_by_arch_id(ipi, cpuid, &cpu, &cs); 184 if (ret != MEMTX_OK || cpu >= ipi->num_cpu) { 185 return MEMTX_DECODE_ERROR; 186 } 187 loongson_ipi_core_writel(&ipi->cpu[cpu], CORE_SET_OFF, 188 BIT(vector), 4, attrs); 189 break; 190 default: 191 qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr); 192 break; 193 } 194 195 return MEMTX_OK; 196 } 197 198 static MemTxResult loongson_ipi_iocsr_writel(void *opaque, hwaddr addr, 199 uint64_t val, unsigned size, 200 MemTxAttrs attrs) 201 { 202 LoongsonIPICommonState *ipi = opaque; 203 IPICore *s; 204 205 if (attrs.requester_id >= ipi->num_cpu) { 206 return MEMTX_DECODE_ERROR; 207 } 208 209 s = &ipi->cpu[attrs.requester_id]; 210 return loongson_ipi_core_writel(s, addr, val, size, attrs); 211 } 212 213 static const MemoryRegionOps loongson_ipi_iocsr_ops = { 214 .read_with_attrs = loongson_ipi_iocsr_readl, 215 .write_with_attrs = loongson_ipi_iocsr_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 loongson_ipi_writeq(void *opaque, hwaddr addr, uint64_t val, 225 unsigned size, MemTxAttrs attrs) 226 { 227 LoongsonIPICommonState *ipi = opaque; 228 MemTxResult ret = MEMTX_OK; 229 230 addr &= 0xfff; 231 switch (addr) { 232 case MAIL_SEND_OFFSET: 233 ret = mail_send(ipi, val, attrs); 234 break; 235 case ANY_SEND_OFFSET: 236 ret = any_send(ipi, val, attrs); 237 break; 238 default: 239 break; 240 } 241 242 return ret; 243 } 244 245 static const MemoryRegionOps loongson_ipi64_ops = { 246 .write_with_attrs = loongson_ipi_writeq, 247 .impl.min_access_size = 8, 248 .impl.max_access_size = 8, 249 .valid.min_access_size = 8, 250 .valid.max_access_size = 8, 251 .endianness = DEVICE_LITTLE_ENDIAN, 252 }; 253 254 static void loongson_ipi_common_realize(DeviceState *dev, Error **errp) 255 { 256 LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev); 257 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 258 259 if (kvm_irqchip_in_kernel()) { 260 return; 261 } 262 263 memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev), 264 &loongson_ipi_iocsr_ops, 265 s, "loongson_ipi_iocsr", 0x48); 266 267 /* loongson_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 &loongson_ipi64_ops, 274 s, "loongson_ipi64_iocsr", 0x118); 275 sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem); 276 } 277 278 static void loongson_ipi_common_unrealize(DeviceState *dev) 279 { 280 LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev); 281 282 g_free(s->cpu); 283 } 284 285 static int loongson_ipi_common_pre_save(void *opaque) 286 { 287 IPICore *ipicore = (IPICore *)opaque; 288 LoongsonIPICommonState *s = ipicore->ipi; 289 LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(s); 290 291 if (licc->pre_save) { 292 return licc->pre_save(s); 293 } 294 295 return 0; 296 } 297 298 static int loongson_ipi_common_post_load(void *opaque, int version_id) 299 { 300 IPICore *ipicore = (IPICore *)opaque; 301 LoongsonIPICommonState *s = ipicore->ipi; 302 LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(s); 303 304 if (licc->post_load) { 305 return licc->post_load(s, version_id); 306 } 307 308 return 0; 309 } 310 311 static const VMStateDescription vmstate_ipi_core = { 312 .name = "ipi-single", 313 .version_id = 2, 314 .minimum_version_id = 2, 315 .pre_save = loongson_ipi_common_pre_save, 316 .post_load = loongson_ipi_common_post_load, 317 .fields = (const VMStateField[]) { 318 VMSTATE_UINT32(status, IPICore), 319 VMSTATE_UINT32(en, IPICore), 320 VMSTATE_UINT32(set, IPICore), 321 VMSTATE_UINT32(clear, IPICore), 322 VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2), 323 VMSTATE_END_OF_LIST() 324 } 325 }; 326 327 static const VMStateDescription vmstate_loongson_ipi_common = { 328 .name = "loongson_ipi", 329 .version_id = 2, 330 .minimum_version_id = 2, 331 .fields = (const VMStateField[]) { 332 VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongsonIPICommonState, 333 num_cpu, vmstate_ipi_core, 334 IPICore), 335 VMSTATE_END_OF_LIST() 336 } 337 }; 338 339 static void loongson_ipi_common_class_init(ObjectClass *klass, const void *data) 340 { 341 DeviceClass *dc = DEVICE_CLASS(klass); 342 LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass); 343 344 device_class_set_parent_realize(dc, loongson_ipi_common_realize, 345 &licc->parent_realize); 346 device_class_set_parent_unrealize(dc, loongson_ipi_common_unrealize, 347 &licc->parent_unrealize); 348 dc->vmsd = &vmstate_loongson_ipi_common; 349 } 350 351 static const TypeInfo loongarch_ipi_common_types[] = { 352 { 353 .name = TYPE_LOONGSON_IPI_COMMON, 354 .parent = TYPE_SYS_BUS_DEVICE, 355 .instance_size = sizeof(LoongsonIPICommonState), 356 .class_size = sizeof(LoongsonIPICommonClass), 357 .class_init = loongson_ipi_common_class_init, 358 .abstract = true, 359 } 360 }; 361 362 DEFINE_TYPES(loongarch_ipi_common_types) 363