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