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