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