17e555781SBibo Mao /* SPDX-License-Identifier: GPL-2.0-or-later */
27e555781SBibo Mao /*
37e555781SBibo Mao * Loongson IPI interrupt common support
47e555781SBibo Mao *
57e555781SBibo Mao * Copyright (C) 2021 Loongson Technology Corporation Limited
67e555781SBibo Mao */
77e555781SBibo Mao
87e555781SBibo Mao #include "qemu/osdep.h"
97e555781SBibo Mao #include "hw/sysbus.h"
107e555781SBibo Mao #include "hw/intc/loongson_ipi_common.h"
11*ec859557SBibo Mao #include "hw/irq.h"
126c8698a5SBibo Mao #include "hw/qdev-properties.h"
13*ec859557SBibo Mao #include "qapi/error.h"
14*ec859557SBibo Mao #include "qemu/log.h"
156c8698a5SBibo Mao #include "migration/vmstate.h"
16*ec859557SBibo Mao #include "trace.h"
17*ec859557SBibo Mao
loongson_ipi_core_readl(void * opaque,hwaddr addr,uint64_t * data,unsigned size,MemTxAttrs attrs)18*ec859557SBibo Mao MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr, uint64_t *data,
19*ec859557SBibo Mao unsigned size, MemTxAttrs attrs)
20*ec859557SBibo Mao {
21*ec859557SBibo Mao IPICore *s = opaque;
22*ec859557SBibo Mao uint64_t ret = 0;
23*ec859557SBibo Mao int index = 0;
24*ec859557SBibo Mao
25*ec859557SBibo Mao addr &= 0xff;
26*ec859557SBibo Mao switch (addr) {
27*ec859557SBibo Mao case CORE_STATUS_OFF:
28*ec859557SBibo Mao ret = s->status;
29*ec859557SBibo Mao break;
30*ec859557SBibo Mao case CORE_EN_OFF:
31*ec859557SBibo Mao ret = s->en;
32*ec859557SBibo Mao break;
33*ec859557SBibo Mao case CORE_SET_OFF:
34*ec859557SBibo Mao ret = 0;
35*ec859557SBibo Mao break;
36*ec859557SBibo Mao case CORE_CLEAR_OFF:
37*ec859557SBibo Mao ret = 0;
38*ec859557SBibo Mao break;
39*ec859557SBibo Mao case CORE_BUF_20 ... CORE_BUF_38 + 4:
40*ec859557SBibo Mao index = (addr - CORE_BUF_20) >> 2;
41*ec859557SBibo Mao ret = s->buf[index];
42*ec859557SBibo Mao break;
43*ec859557SBibo Mao default:
44*ec859557SBibo Mao qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr);
45*ec859557SBibo Mao break;
46*ec859557SBibo Mao }
47*ec859557SBibo Mao
48*ec859557SBibo Mao trace_loongson_ipi_read(size, (uint64_t)addr, ret);
49*ec859557SBibo Mao *data = ret;
50*ec859557SBibo Mao
51*ec859557SBibo Mao return MEMTX_OK;
52*ec859557SBibo Mao }
53*ec859557SBibo Mao
loongson_ipi_iocsr_readl(void * opaque,hwaddr addr,uint64_t * data,unsigned size,MemTxAttrs attrs)54*ec859557SBibo Mao static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr,
55*ec859557SBibo Mao uint64_t *data, unsigned size,
56*ec859557SBibo Mao MemTxAttrs attrs)
57*ec859557SBibo Mao {
58*ec859557SBibo Mao LoongsonIPICommonState *ipi = opaque;
59*ec859557SBibo Mao IPICore *s;
60*ec859557SBibo Mao
61*ec859557SBibo Mao if (attrs.requester_id >= ipi->num_cpu) {
62*ec859557SBibo Mao return MEMTX_DECODE_ERROR;
63*ec859557SBibo Mao }
64*ec859557SBibo Mao
65*ec859557SBibo Mao s = &ipi->cpu[attrs.requester_id];
66*ec859557SBibo Mao return loongson_ipi_core_readl(s, addr, data, size, attrs);
67*ec859557SBibo Mao }
68*ec859557SBibo Mao
send_ipi_data(LoongsonIPICommonState * ipi,CPUState * cpu,uint64_t val,hwaddr addr,MemTxAttrs attrs)69*ec859557SBibo Mao static MemTxResult send_ipi_data(LoongsonIPICommonState *ipi, CPUState *cpu,
70*ec859557SBibo Mao uint64_t val, hwaddr addr, MemTxAttrs attrs)
71*ec859557SBibo Mao {
72*ec859557SBibo Mao LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
73*ec859557SBibo Mao int i, mask = 0, data = 0;
74*ec859557SBibo Mao AddressSpace *iocsr_as = licc->get_iocsr_as(cpu);
75*ec859557SBibo Mao
76*ec859557SBibo Mao if (!iocsr_as) {
77*ec859557SBibo Mao return MEMTX_DECODE_ERROR;
78*ec859557SBibo Mao }
79*ec859557SBibo Mao
80*ec859557SBibo Mao /*
81*ec859557SBibo Mao * bit 27-30 is mask for byte writing,
82*ec859557SBibo Mao * if the mask is 0, we need not to do anything.
83*ec859557SBibo Mao */
84*ec859557SBibo Mao if ((val >> 27) & 0xf) {
85*ec859557SBibo Mao data = address_space_ldl_le(iocsr_as, addr, attrs, NULL);
86*ec859557SBibo Mao for (i = 0; i < 4; i++) {
87*ec859557SBibo Mao /* get mask for byte writing */
88*ec859557SBibo Mao if (val & (0x1 << (27 + i))) {
89*ec859557SBibo Mao mask |= 0xff << (i * 8);
90*ec859557SBibo Mao }
91*ec859557SBibo Mao }
92*ec859557SBibo Mao }
93*ec859557SBibo Mao
94*ec859557SBibo Mao data &= mask;
95*ec859557SBibo Mao data |= (val >> 32) & ~mask;
96*ec859557SBibo Mao address_space_stl_le(iocsr_as, addr, data, attrs, NULL);
97*ec859557SBibo Mao
98*ec859557SBibo Mao return MEMTX_OK;
99*ec859557SBibo Mao }
100*ec859557SBibo Mao
mail_send(LoongsonIPICommonState * ipi,uint64_t val,MemTxAttrs attrs)101*ec859557SBibo Mao static MemTxResult mail_send(LoongsonIPICommonState *ipi,
102*ec859557SBibo Mao uint64_t val, MemTxAttrs attrs)
103*ec859557SBibo Mao {
104*ec859557SBibo Mao LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
105*ec859557SBibo Mao uint32_t cpuid;
106*ec859557SBibo Mao hwaddr addr;
107*ec859557SBibo Mao CPUState *cs;
108*ec859557SBibo Mao
109*ec859557SBibo Mao cpuid = extract32(val, 16, 10);
110*ec859557SBibo Mao cs = licc->cpu_by_arch_id(cpuid);
111*ec859557SBibo Mao if (cs == NULL) {
112*ec859557SBibo Mao return MEMTX_DECODE_ERROR;
113*ec859557SBibo Mao }
114*ec859557SBibo Mao
115*ec859557SBibo Mao /* override requester_id */
116*ec859557SBibo Mao addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c);
117*ec859557SBibo Mao attrs.requester_id = cs->cpu_index;
118*ec859557SBibo Mao return send_ipi_data(ipi, cs, val, addr, attrs);
119*ec859557SBibo Mao }
120*ec859557SBibo Mao
any_send(LoongsonIPICommonState * ipi,uint64_t val,MemTxAttrs attrs)121*ec859557SBibo Mao static MemTxResult any_send(LoongsonIPICommonState *ipi,
122*ec859557SBibo Mao uint64_t val, MemTxAttrs attrs)
123*ec859557SBibo Mao {
124*ec859557SBibo Mao LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
125*ec859557SBibo Mao uint32_t cpuid;
126*ec859557SBibo Mao hwaddr addr;
127*ec859557SBibo Mao CPUState *cs;
128*ec859557SBibo Mao
129*ec859557SBibo Mao cpuid = extract32(val, 16, 10);
130*ec859557SBibo Mao cs = licc->cpu_by_arch_id(cpuid);
131*ec859557SBibo Mao if (cs == NULL) {
132*ec859557SBibo Mao return MEMTX_DECODE_ERROR;
133*ec859557SBibo Mao }
134*ec859557SBibo Mao
135*ec859557SBibo Mao /* override requester_id */
136*ec859557SBibo Mao addr = val & 0xffff;
137*ec859557SBibo Mao attrs.requester_id = cs->cpu_index;
138*ec859557SBibo Mao return send_ipi_data(ipi, cs, val, addr, attrs);
139*ec859557SBibo Mao }
140*ec859557SBibo Mao
loongson_ipi_core_writel(void * opaque,hwaddr addr,uint64_t val,unsigned size,MemTxAttrs attrs)141*ec859557SBibo Mao MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, uint64_t val,
142*ec859557SBibo Mao unsigned size, MemTxAttrs attrs)
143*ec859557SBibo Mao {
144*ec859557SBibo Mao IPICore *s = opaque;
145*ec859557SBibo Mao LoongsonIPICommonState *ipi = s->ipi;
146*ec859557SBibo Mao LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
147*ec859557SBibo Mao int index = 0;
148*ec859557SBibo Mao uint32_t cpuid;
149*ec859557SBibo Mao uint8_t vector;
150*ec859557SBibo Mao CPUState *cs;
151*ec859557SBibo Mao
152*ec859557SBibo Mao addr &= 0xff;
153*ec859557SBibo Mao trace_loongson_ipi_write(size, (uint64_t)addr, val);
154*ec859557SBibo Mao switch (addr) {
155*ec859557SBibo Mao case CORE_STATUS_OFF:
156*ec859557SBibo Mao qemu_log_mask(LOG_GUEST_ERROR, "can not be written");
157*ec859557SBibo Mao break;
158*ec859557SBibo Mao case CORE_EN_OFF:
159*ec859557SBibo Mao s->en = val;
160*ec859557SBibo Mao break;
161*ec859557SBibo Mao case CORE_SET_OFF:
162*ec859557SBibo Mao s->status |= val;
163*ec859557SBibo Mao if (s->status != 0 && (s->status & s->en) != 0) {
164*ec859557SBibo Mao qemu_irq_raise(s->irq);
165*ec859557SBibo Mao }
166*ec859557SBibo Mao break;
167*ec859557SBibo Mao case CORE_CLEAR_OFF:
168*ec859557SBibo Mao s->status &= ~val;
169*ec859557SBibo Mao if (s->status == 0 && s->en != 0) {
170*ec859557SBibo Mao qemu_irq_lower(s->irq);
171*ec859557SBibo Mao }
172*ec859557SBibo Mao break;
173*ec859557SBibo Mao case CORE_BUF_20 ... CORE_BUF_38 + 4:
174*ec859557SBibo Mao index = (addr - CORE_BUF_20) >> 2;
175*ec859557SBibo Mao s->buf[index] = val;
176*ec859557SBibo Mao break;
177*ec859557SBibo Mao case IOCSR_IPI_SEND:
178*ec859557SBibo Mao cpuid = extract32(val, 16, 10);
179*ec859557SBibo Mao /* IPI status vector */
180*ec859557SBibo Mao vector = extract8(val, 0, 5);
181*ec859557SBibo Mao cs = licc->cpu_by_arch_id(cpuid);
182*ec859557SBibo Mao if (cs == NULL || cs->cpu_index >= ipi->num_cpu) {
183*ec859557SBibo Mao return MEMTX_DECODE_ERROR;
184*ec859557SBibo Mao }
185*ec859557SBibo Mao loongson_ipi_core_writel(&ipi->cpu[cs->cpu_index], CORE_SET_OFF,
186*ec859557SBibo Mao BIT(vector), 4, attrs);
187*ec859557SBibo Mao break;
188*ec859557SBibo Mao default:
189*ec859557SBibo Mao qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr);
190*ec859557SBibo Mao break;
191*ec859557SBibo Mao }
192*ec859557SBibo Mao
193*ec859557SBibo Mao return MEMTX_OK;
194*ec859557SBibo Mao }
195*ec859557SBibo Mao
loongson_ipi_iocsr_writel(void * opaque,hwaddr addr,uint64_t val,unsigned size,MemTxAttrs attrs)196*ec859557SBibo Mao static MemTxResult loongson_ipi_iocsr_writel(void *opaque, hwaddr addr,
197*ec859557SBibo Mao uint64_t val, unsigned size,
198*ec859557SBibo Mao MemTxAttrs attrs)
199*ec859557SBibo Mao {
200*ec859557SBibo Mao LoongsonIPICommonState *ipi = opaque;
201*ec859557SBibo Mao IPICore *s;
202*ec859557SBibo Mao
203*ec859557SBibo Mao if (attrs.requester_id >= ipi->num_cpu) {
204*ec859557SBibo Mao return MEMTX_DECODE_ERROR;
205*ec859557SBibo Mao }
206*ec859557SBibo Mao
207*ec859557SBibo Mao s = &ipi->cpu[attrs.requester_id];
208*ec859557SBibo Mao return loongson_ipi_core_writel(s, addr, val, size, attrs);
209*ec859557SBibo Mao }
210*ec859557SBibo Mao
211*ec859557SBibo Mao static const MemoryRegionOps loongson_ipi_iocsr_ops = {
212*ec859557SBibo Mao .read_with_attrs = loongson_ipi_iocsr_readl,
213*ec859557SBibo Mao .write_with_attrs = loongson_ipi_iocsr_writel,
214*ec859557SBibo Mao .impl.min_access_size = 4,
215*ec859557SBibo Mao .impl.max_access_size = 4,
216*ec859557SBibo Mao .valid.min_access_size = 4,
217*ec859557SBibo Mao .valid.max_access_size = 8,
218*ec859557SBibo Mao .endianness = DEVICE_LITTLE_ENDIAN,
219*ec859557SBibo Mao };
220*ec859557SBibo Mao
221*ec859557SBibo Mao /* mail send and any send only support writeq */
loongson_ipi_writeq(void * opaque,hwaddr addr,uint64_t val,unsigned size,MemTxAttrs attrs)222*ec859557SBibo Mao static MemTxResult loongson_ipi_writeq(void *opaque, hwaddr addr, uint64_t val,
223*ec859557SBibo Mao unsigned size, MemTxAttrs attrs)
224*ec859557SBibo Mao {
225*ec859557SBibo Mao LoongsonIPICommonState *ipi = opaque;
226*ec859557SBibo Mao MemTxResult ret = MEMTX_OK;
227*ec859557SBibo Mao
228*ec859557SBibo Mao addr &= 0xfff;
229*ec859557SBibo Mao switch (addr) {
230*ec859557SBibo Mao case MAIL_SEND_OFFSET:
231*ec859557SBibo Mao ret = mail_send(ipi, val, attrs);
232*ec859557SBibo Mao break;
233*ec859557SBibo Mao case ANY_SEND_OFFSET:
234*ec859557SBibo Mao ret = any_send(ipi, val, attrs);
235*ec859557SBibo Mao break;
236*ec859557SBibo Mao default:
237*ec859557SBibo Mao break;
238*ec859557SBibo Mao }
239*ec859557SBibo Mao
240*ec859557SBibo Mao return ret;
241*ec859557SBibo Mao }
242*ec859557SBibo Mao
243*ec859557SBibo Mao static const MemoryRegionOps loongson_ipi64_ops = {
244*ec859557SBibo Mao .write_with_attrs = loongson_ipi_writeq,
245*ec859557SBibo Mao .impl.min_access_size = 8,
246*ec859557SBibo Mao .impl.max_access_size = 8,
247*ec859557SBibo Mao .valid.min_access_size = 8,
248*ec859557SBibo Mao .valid.max_access_size = 8,
249*ec859557SBibo Mao .endianness = DEVICE_LITTLE_ENDIAN,
250*ec859557SBibo Mao };
251*ec859557SBibo Mao
loongson_ipi_common_realize(DeviceState * dev,Error ** errp)252*ec859557SBibo Mao static void loongson_ipi_common_realize(DeviceState *dev, Error **errp)
253*ec859557SBibo Mao {
254*ec859557SBibo Mao LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev);
255*ec859557SBibo Mao SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
256*ec859557SBibo Mao int i;
257*ec859557SBibo Mao
258*ec859557SBibo Mao if (s->num_cpu == 0) {
259*ec859557SBibo Mao error_setg(errp, "num-cpu must be at least 1");
260*ec859557SBibo Mao return;
261*ec859557SBibo Mao }
262*ec859557SBibo Mao
263*ec859557SBibo Mao memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev),
264*ec859557SBibo Mao &loongson_ipi_iocsr_ops,
265*ec859557SBibo Mao s, "loongson_ipi_iocsr", 0x48);
266*ec859557SBibo Mao
267*ec859557SBibo Mao /* loongson_ipi_iocsr performs re-entrant IO through ipi_send */
268*ec859557SBibo Mao s->ipi_iocsr_mem.disable_reentrancy_guard = true;
269*ec859557SBibo Mao
270*ec859557SBibo Mao sysbus_init_mmio(sbd, &s->ipi_iocsr_mem);
271*ec859557SBibo Mao
272*ec859557SBibo Mao memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev),
273*ec859557SBibo Mao &loongson_ipi64_ops,
274*ec859557SBibo Mao s, "loongson_ipi64_iocsr", 0x118);
275*ec859557SBibo Mao sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem);
276*ec859557SBibo Mao
277*ec859557SBibo Mao s->cpu = g_new0(IPICore, s->num_cpu);
278*ec859557SBibo Mao for (i = 0; i < s->num_cpu; i++) {
279*ec859557SBibo Mao s->cpu[i].ipi = s;
280*ec859557SBibo Mao
281*ec859557SBibo Mao qdev_init_gpio_out(dev, &s->cpu[i].irq, 1);
282*ec859557SBibo Mao }
283*ec859557SBibo Mao }
284*ec859557SBibo Mao
loongson_ipi_common_unrealize(DeviceState * dev)285*ec859557SBibo Mao static void loongson_ipi_common_unrealize(DeviceState *dev)
286*ec859557SBibo Mao {
287*ec859557SBibo Mao LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev);
288*ec859557SBibo Mao
289*ec859557SBibo Mao g_free(s->cpu);
290*ec859557SBibo Mao }
2916c8698a5SBibo Mao
2926c8698a5SBibo Mao static const VMStateDescription vmstate_ipi_core = {
2936c8698a5SBibo Mao .name = "ipi-single",
2946c8698a5SBibo Mao .version_id = 2,
2956c8698a5SBibo Mao .minimum_version_id = 2,
2966c8698a5SBibo Mao .fields = (const VMStateField[]) {
2976c8698a5SBibo Mao VMSTATE_UINT32(status, IPICore),
2986c8698a5SBibo Mao VMSTATE_UINT32(en, IPICore),
2996c8698a5SBibo Mao VMSTATE_UINT32(set, IPICore),
3006c8698a5SBibo Mao VMSTATE_UINT32(clear, IPICore),
3016c8698a5SBibo Mao VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2),
3026c8698a5SBibo Mao VMSTATE_END_OF_LIST()
3036c8698a5SBibo Mao }
3046c8698a5SBibo Mao };
3056c8698a5SBibo Mao
3066c8698a5SBibo Mao static const VMStateDescription vmstate_loongson_ipi_common = {
3076c8698a5SBibo Mao .name = "loongson_ipi",
3086c8698a5SBibo Mao .version_id = 2,
3096c8698a5SBibo Mao .minimum_version_id = 2,
3106c8698a5SBibo Mao .fields = (const VMStateField[]) {
3116c8698a5SBibo Mao VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongsonIPICommonState,
3126c8698a5SBibo Mao num_cpu, vmstate_ipi_core,
3136c8698a5SBibo Mao IPICore),
3146c8698a5SBibo Mao VMSTATE_END_OF_LIST()
3156c8698a5SBibo Mao }
3166c8698a5SBibo Mao };
3176c8698a5SBibo Mao
3186c8698a5SBibo Mao static Property ipi_common_properties[] = {
3196c8698a5SBibo Mao DEFINE_PROP_UINT32("num-cpu", LoongsonIPICommonState, num_cpu, 1),
3206c8698a5SBibo Mao DEFINE_PROP_END_OF_LIST(),
3216c8698a5SBibo Mao };
3226c8698a5SBibo Mao
loongson_ipi_common_class_init(ObjectClass * klass,void * data)3236c8698a5SBibo Mao static void loongson_ipi_common_class_init(ObjectClass *klass, void *data)
3246c8698a5SBibo Mao {
3256c8698a5SBibo Mao DeviceClass *dc = DEVICE_CLASS(klass);
326*ec859557SBibo Mao LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass);
3276c8698a5SBibo Mao
328*ec859557SBibo Mao device_class_set_parent_realize(dc, loongson_ipi_common_realize,
329*ec859557SBibo Mao &licc->parent_realize);
330*ec859557SBibo Mao device_class_set_parent_unrealize(dc, loongson_ipi_common_unrealize,
331*ec859557SBibo Mao &licc->parent_unrealize);
3326c8698a5SBibo Mao device_class_set_props(dc, ipi_common_properties);
3336c8698a5SBibo Mao dc->vmsd = &vmstate_loongson_ipi_common;
3346c8698a5SBibo Mao }
3357e555781SBibo Mao
3367e555781SBibo Mao static const TypeInfo loongarch_ipi_common_types[] = {
3377e555781SBibo Mao {
3387e555781SBibo Mao .name = TYPE_LOONGSON_IPI_COMMON,
3397e555781SBibo Mao .parent = TYPE_SYS_BUS_DEVICE,
3407e555781SBibo Mao .instance_size = sizeof(LoongsonIPICommonState),
3417e555781SBibo Mao .class_size = sizeof(LoongsonIPICommonClass),
3426c8698a5SBibo Mao .class_init = loongson_ipi_common_class_init,
3437e555781SBibo Mao .abstract = true,
3447e555781SBibo Mao }
3457e555781SBibo Mao };
3467e555781SBibo Mao
3477e555781SBibo Mao DEFINE_TYPES(loongarch_ipi_common_types)
348