1
2
3
4
5
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
20static 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
53static void send_ipi_data(CPULoongArchState *env, target_ulong val, target_ulong addr)
54{
55 int i, mask = 0, data = 0;
56
57
58
59
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
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
78static 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
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
97static 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
113static 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
127static 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
167static 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
178static 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
194static 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
203static 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
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
228static 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
242static 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
254static 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
261static 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
269static void loongarch_ipi_register_types(void)
270{
271 type_register_static(&loongarch_ipi_info);
272}
273
274type_init(loongarch_ipi_register_types)
275