xref: /openbmc/qemu/hw/intc/loongarch_ipi.c (revision db96605a)
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