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, target_ulong val, target_ulong 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 int cpuid, data; 81 CPULoongArchState *env; 82 CPUState *cs; 83 LoongArchCPU *cpu; 84 85 cpuid = (val >> 16) & 0x3ff; 86 /* IPI status vector */ 87 data = 1 << (val & 0x1f); 88 cs = qemu_get_cpu(cpuid); 89 cpu = LOONGARCH_CPU(cs); 90 env = &cpu->env; 91 loongarch_cpu_set_irq(cpu, IRQ_IPI, 1); 92 address_space_stl(&env->address_space_iocsr, 0x1008, 93 data, MEMTXATTRS_UNSPECIFIED, NULL); 94 95 } 96 97 static void mail_send(uint64_t val) 98 { 99 int cpuid; 100 hwaddr addr; 101 CPULoongArchState *env; 102 CPUState *cs; 103 LoongArchCPU *cpu; 104 105 cpuid = (val >> 16) & 0x3ff; 106 addr = 0x1020 + (val & 0x1c); 107 cs = qemu_get_cpu(cpuid); 108 cpu = LOONGARCH_CPU(cs); 109 env = &cpu->env; 110 send_ipi_data(env, val, addr); 111 } 112 113 static void any_send(uint64_t val) 114 { 115 int cpuid; 116 hwaddr addr; 117 CPULoongArchState *env; 118 119 cpuid = (val >> 16) & 0x3ff; 120 addr = val & 0xffff; 121 CPUState *cs = qemu_get_cpu(cpuid); 122 LoongArchCPU *cpu = LOONGARCH_CPU(cs); 123 env = &cpu->env; 124 send_ipi_data(env, val, addr); 125 } 126 127 static void loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val, 128 unsigned size) 129 { 130 IPICore *s = opaque; 131 int index = 0; 132 133 addr &= 0xff; 134 trace_loongarch_ipi_write(size, (uint64_t)addr, val); 135 switch (addr) { 136 case CORE_STATUS_OFF: 137 qemu_log_mask(LOG_GUEST_ERROR, "can not be written"); 138 break; 139 case CORE_EN_OFF: 140 s->en = val; 141 break; 142 case CORE_SET_OFF: 143 s->status |= val; 144 if (s->status != 0 && (s->status & s->en) != 0) { 145 qemu_irq_raise(s->irq); 146 } 147 break; 148 case CORE_CLEAR_OFF: 149 s->status &= ~val; 150 if (s->status == 0 && s->en != 0) { 151 qemu_irq_lower(s->irq); 152 } 153 break; 154 case CORE_BUF_20 ... CORE_BUF_38 + 4: 155 index = (addr - CORE_BUF_20) >> 2; 156 s->buf[index] = val; 157 break; 158 case IOCSR_IPI_SEND: 159 ipi_send(val); 160 break; 161 default: 162 qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr); 163 break; 164 } 165 } 166 167 static const MemoryRegionOps loongarch_ipi_ops = { 168 .read = loongarch_ipi_readl, 169 .write = loongarch_ipi_writel, 170 .impl.min_access_size = 4, 171 .impl.max_access_size = 4, 172 .valid.min_access_size = 4, 173 .valid.max_access_size = 8, 174 .endianness = DEVICE_LITTLE_ENDIAN, 175 }; 176 177 /* mail send and any send only support writeq */ 178 static void loongarch_ipi_writeq(void *opaque, hwaddr addr, uint64_t val, 179 unsigned size) 180 { 181 addr &= 0xfff; 182 switch (addr) { 183 case MAIL_SEND_OFFSET: 184 mail_send(val); 185 break; 186 case ANY_SEND_OFFSET: 187 any_send(val); 188 break; 189 default: 190 break; 191 } 192 } 193 194 static const MemoryRegionOps loongarch_ipi64_ops = { 195 .write = loongarch_ipi_writeq, 196 .impl.min_access_size = 8, 197 .impl.max_access_size = 8, 198 .valid.min_access_size = 8, 199 .valid.max_access_size = 8, 200 .endianness = DEVICE_LITTLE_ENDIAN, 201 }; 202 203 static void loongarch_ipi_init(Object *obj) 204 { 205 int cpu; 206 LoongArchMachineState *lams; 207 LoongArchIPI *s = LOONGARCH_IPI(obj); 208 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 209 Object *machine = qdev_get_machine(); 210 ObjectClass *mc = object_get_class(machine); 211 /* 'lams' should be initialized */ 212 if (!strcmp(MACHINE_CLASS(mc)->name, "none")) { 213 return; 214 } 215 lams = LOONGARCH_MACHINE(machine); 216 for (cpu = 0; cpu < MAX_IPI_CORE_NUM; cpu++) { 217 memory_region_init_io(&s->ipi_iocsr_mem[cpu], obj, &loongarch_ipi_ops, 218 &lams->ipi_core[cpu], "loongarch_ipi_iocsr", 0x48); 219 sysbus_init_mmio(sbd, &s->ipi_iocsr_mem[cpu]); 220 221 memory_region_init_io(&s->ipi64_iocsr_mem[cpu], obj, &loongarch_ipi64_ops, 222 &lams->ipi_core[cpu], "loongarch_ipi64_iocsr", 0x118); 223 sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem[cpu]); 224 qdev_init_gpio_out(DEVICE(obj), &lams->ipi_core[cpu].irq, 1); 225 } 226 } 227 228 static const VMStateDescription vmstate_ipi_core = { 229 .name = "ipi-single", 230 .version_id = 0, 231 .minimum_version_id = 0, 232 .fields = (VMStateField[]) { 233 VMSTATE_UINT32(status, IPICore), 234 VMSTATE_UINT32(en, IPICore), 235 VMSTATE_UINT32(set, IPICore), 236 VMSTATE_UINT32(clear, IPICore), 237 VMSTATE_UINT32_ARRAY(buf, IPICore, MAX_IPI_MBX_NUM * 2), 238 VMSTATE_END_OF_LIST() 239 } 240 }; 241 242 static const VMStateDescription vmstate_loongarch_ipi = { 243 .name = TYPE_LOONGARCH_IPI, 244 .version_id = 0, 245 .minimum_version_id = 0, 246 .fields = (VMStateField[]) { 247 VMSTATE_STRUCT_ARRAY(ipi_core, LoongArchMachineState, 248 MAX_IPI_CORE_NUM, 0, 249 vmstate_ipi_core, IPICore), 250 VMSTATE_END_OF_LIST() 251 } 252 }; 253 254 static void loongarch_ipi_class_init(ObjectClass *klass, void *data) 255 { 256 DeviceClass *dc = DEVICE_CLASS(klass); 257 258 dc->vmsd = &vmstate_loongarch_ipi; 259 } 260 261 static const TypeInfo loongarch_ipi_info = { 262 .name = TYPE_LOONGARCH_IPI, 263 .parent = TYPE_SYS_BUS_DEVICE, 264 .instance_size = sizeof(LoongArchIPI), 265 .instance_init = loongarch_ipi_init, 266 .class_init = loongarch_ipi_class_init, 267 }; 268 269 static void loongarch_ipi_register_types(void) 270 { 271 type_register_static(&loongarch_ipi_info); 272 } 273 274 type_init(loongarch_ipi_register_types) 275