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"
11ec859557SBibo Mao #include "hw/irq.h"
12ec859557SBibo Mao #include "qemu/log.h"
136c8698a5SBibo Mao #include "migration/vmstate.h"
14ec859557SBibo Mao #include "trace.h"
15ec859557SBibo Mao
loongson_ipi_core_readl(void * opaque,hwaddr addr,uint64_t * data,unsigned size,MemTxAttrs attrs)16ec859557SBibo Mao MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr, uint64_t *data,
17ec859557SBibo Mao unsigned size, MemTxAttrs attrs)
18ec859557SBibo Mao {
19ec859557SBibo Mao IPICore *s = opaque;
20ec859557SBibo Mao uint64_t ret = 0;
21ec859557SBibo Mao int index = 0;
22ec859557SBibo Mao
23ec859557SBibo Mao addr &= 0xff;
24ec859557SBibo Mao switch (addr) {
25ec859557SBibo Mao case CORE_STATUS_OFF:
26ec859557SBibo Mao ret = s->status;
27ec859557SBibo Mao break;
28ec859557SBibo Mao case CORE_EN_OFF:
29ec859557SBibo Mao ret = s->en;
30ec859557SBibo Mao break;
31ec859557SBibo Mao case CORE_SET_OFF:
32ec859557SBibo Mao ret = 0;
33ec859557SBibo Mao break;
34ec859557SBibo Mao case CORE_CLEAR_OFF:
35ec859557SBibo Mao ret = 0;
36ec859557SBibo Mao break;
37ec859557SBibo Mao case CORE_BUF_20 ... CORE_BUF_38 + 4:
38ec859557SBibo Mao index = (addr - CORE_BUF_20) >> 2;
39ec859557SBibo Mao ret = s->buf[index];
40ec859557SBibo Mao break;
41ec859557SBibo Mao default:
42ec859557SBibo Mao qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr);
43ec859557SBibo Mao break;
44ec859557SBibo Mao }
45ec859557SBibo Mao
46ec859557SBibo Mao trace_loongson_ipi_read(size, (uint64_t)addr, ret);
47ec859557SBibo Mao *data = ret;
48ec859557SBibo Mao
49ec859557SBibo Mao return MEMTX_OK;
50ec859557SBibo Mao }
51ec859557SBibo Mao
loongson_ipi_iocsr_readl(void * opaque,hwaddr addr,uint64_t * data,unsigned size,MemTxAttrs attrs)52ec859557SBibo Mao static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr,
53ec859557SBibo Mao uint64_t *data, unsigned size,
54ec859557SBibo Mao MemTxAttrs attrs)
55ec859557SBibo Mao {
56ec859557SBibo Mao LoongsonIPICommonState *ipi = opaque;
57ec859557SBibo Mao IPICore *s;
58ec859557SBibo Mao
59ec859557SBibo Mao if (attrs.requester_id >= ipi->num_cpu) {
60ec859557SBibo Mao return MEMTX_DECODE_ERROR;
61ec859557SBibo Mao }
62ec859557SBibo Mao
63ec859557SBibo Mao s = &ipi->cpu[attrs.requester_id];
64ec859557SBibo Mao return loongson_ipi_core_readl(s, addr, data, size, attrs);
65ec859557SBibo Mao }
66ec859557SBibo Mao
send_ipi_data(LoongsonIPICommonState * ipi,CPUState * cpu,uint64_t val,hwaddr addr,MemTxAttrs attrs)67ec859557SBibo Mao static MemTxResult send_ipi_data(LoongsonIPICommonState *ipi, CPUState *cpu,
68ec859557SBibo Mao uint64_t val, hwaddr addr, MemTxAttrs attrs)
69ec859557SBibo Mao {
70ec859557SBibo Mao LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
71ec859557SBibo Mao int i, mask = 0, data = 0;
72ec859557SBibo Mao AddressSpace *iocsr_as = licc->get_iocsr_as(cpu);
73ec859557SBibo Mao
74ec859557SBibo Mao if (!iocsr_as) {
75ec859557SBibo Mao return MEMTX_DECODE_ERROR;
76ec859557SBibo Mao }
77ec859557SBibo Mao
78ec859557SBibo Mao /*
79ec859557SBibo Mao * bit 27-30 is mask for byte writing,
80ec859557SBibo Mao * if the mask is 0, we need not to do anything.
81ec859557SBibo Mao */
82ec859557SBibo Mao if ((val >> 27) & 0xf) {
83ec859557SBibo Mao data = address_space_ldl_le(iocsr_as, addr, attrs, NULL);
84ec859557SBibo Mao for (i = 0; i < 4; i++) {
85ec859557SBibo Mao /* get mask for byte writing */
86ec859557SBibo Mao if (val & (0x1 << (27 + i))) {
87ec859557SBibo Mao mask |= 0xff << (i * 8);
88ec859557SBibo Mao }
89ec859557SBibo Mao }
90ec859557SBibo Mao }
91ec859557SBibo Mao
92ec859557SBibo Mao data &= mask;
93ec859557SBibo Mao data |= (val >> 32) & ~mask;
94ec859557SBibo Mao address_space_stl_le(iocsr_as, addr, data, attrs, NULL);
95ec859557SBibo Mao
96ec859557SBibo Mao return MEMTX_OK;
97ec859557SBibo Mao }
98ec859557SBibo Mao
mail_send(LoongsonIPICommonState * ipi,uint64_t val,MemTxAttrs attrs)99ec859557SBibo Mao static MemTxResult mail_send(LoongsonIPICommonState *ipi,
100ec859557SBibo Mao uint64_t val, MemTxAttrs attrs)
101ec859557SBibo Mao {
102ec859557SBibo Mao LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
103ec859557SBibo Mao uint32_t cpuid;
104ec859557SBibo Mao hwaddr addr;
105ec859557SBibo Mao CPUState *cs;
106999b112dSBibo Mao int cpu, ret;
107ec859557SBibo Mao
108ec859557SBibo Mao cpuid = extract32(val, 16, 10);
109999b112dSBibo Mao ret = licc->cpu_by_arch_id(ipi, cpuid, &cpu, &cs);
110999b112dSBibo Mao if (ret != MEMTX_OK) {
111ec859557SBibo Mao return MEMTX_DECODE_ERROR;
112ec859557SBibo Mao }
113ec859557SBibo Mao
114ec859557SBibo Mao /* override requester_id */
115ec859557SBibo Mao addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c);
116999b112dSBibo Mao attrs.requester_id = cpu;
117ec859557SBibo Mao return send_ipi_data(ipi, cs, val, addr, attrs);
118ec859557SBibo Mao }
119ec859557SBibo Mao
any_send(LoongsonIPICommonState * ipi,uint64_t val,MemTxAttrs attrs)120ec859557SBibo Mao static MemTxResult any_send(LoongsonIPICommonState *ipi,
121ec859557SBibo Mao uint64_t val, MemTxAttrs attrs)
122ec859557SBibo Mao {
123ec859557SBibo Mao LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
124ec859557SBibo Mao uint32_t cpuid;
125ec859557SBibo Mao hwaddr addr;
126ec859557SBibo Mao CPUState *cs;
127999b112dSBibo Mao int cpu, ret;
128ec859557SBibo Mao
129ec859557SBibo Mao cpuid = extract32(val, 16, 10);
130999b112dSBibo Mao ret = licc->cpu_by_arch_id(ipi, cpuid, &cpu, &cs);
131999b112dSBibo Mao if (ret != MEMTX_OK) {
132ec859557SBibo Mao return MEMTX_DECODE_ERROR;
133ec859557SBibo Mao }
134ec859557SBibo Mao
135ec859557SBibo Mao /* override requester_id */
136ec859557SBibo Mao addr = val & 0xffff;
137999b112dSBibo Mao attrs.requester_id = cpu;
138ec859557SBibo Mao return send_ipi_data(ipi, cs, val, addr, attrs);
139ec859557SBibo Mao }
140ec859557SBibo Mao
loongson_ipi_core_writel(void * opaque,hwaddr addr,uint64_t val,unsigned size,MemTxAttrs attrs)141ec859557SBibo Mao MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, uint64_t val,
142ec859557SBibo Mao unsigned size, MemTxAttrs attrs)
143ec859557SBibo Mao {
144ec859557SBibo Mao IPICore *s = opaque;
145ec859557SBibo Mao LoongsonIPICommonState *ipi = s->ipi;
146ec859557SBibo Mao LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
147ec859557SBibo Mao int index = 0;
148ec859557SBibo Mao uint32_t cpuid;
149ec859557SBibo Mao uint8_t vector;
150ec859557SBibo Mao CPUState *cs;
151999b112dSBibo Mao int cpu, ret;
152ec859557SBibo Mao
153ec859557SBibo Mao addr &= 0xff;
154ec859557SBibo Mao trace_loongson_ipi_write(size, (uint64_t)addr, val);
155ec859557SBibo Mao switch (addr) {
156ec859557SBibo Mao case CORE_STATUS_OFF:
157ec859557SBibo Mao qemu_log_mask(LOG_GUEST_ERROR, "can not be written");
158ec859557SBibo Mao break;
159ec859557SBibo Mao case CORE_EN_OFF:
160ec859557SBibo Mao s->en = val;
161ec859557SBibo Mao break;
162ec859557SBibo Mao case CORE_SET_OFF:
163ec859557SBibo Mao s->status |= val;
164ec859557SBibo Mao if (s->status != 0 && (s->status & s->en) != 0) {
165ec859557SBibo Mao qemu_irq_raise(s->irq);
166ec859557SBibo Mao }
167ec859557SBibo Mao break;
168ec859557SBibo Mao case CORE_CLEAR_OFF:
169ec859557SBibo Mao s->status &= ~val;
170ec859557SBibo Mao if (s->status == 0 && s->en != 0) {
171ec859557SBibo Mao qemu_irq_lower(s->irq);
172ec859557SBibo Mao }
173ec859557SBibo Mao break;
174ec859557SBibo Mao case CORE_BUF_20 ... CORE_BUF_38 + 4:
175ec859557SBibo Mao index = (addr - CORE_BUF_20) >> 2;
176ec859557SBibo Mao s->buf[index] = val;
177ec859557SBibo Mao break;
178ec859557SBibo Mao case IOCSR_IPI_SEND:
179ec859557SBibo Mao cpuid = extract32(val, 16, 10);
180ec859557SBibo Mao /* IPI status vector */
181ec859557SBibo Mao vector = extract8(val, 0, 5);
182999b112dSBibo Mao ret = licc->cpu_by_arch_id(ipi, cpuid, &cpu, &cs);
183999b112dSBibo Mao if (ret != MEMTX_OK || cpu >= ipi->num_cpu) {
184ec859557SBibo Mao return MEMTX_DECODE_ERROR;
185ec859557SBibo Mao }
186999b112dSBibo Mao loongson_ipi_core_writel(&ipi->cpu[cpu], CORE_SET_OFF,
187ec859557SBibo Mao BIT(vector), 4, attrs);
188ec859557SBibo Mao break;
189ec859557SBibo Mao default:
190ec859557SBibo Mao qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr);
191ec859557SBibo Mao break;
192ec859557SBibo Mao }
193ec859557SBibo Mao
194ec859557SBibo Mao return MEMTX_OK;
195ec859557SBibo Mao }
196ec859557SBibo Mao
loongson_ipi_iocsr_writel(void * opaque,hwaddr addr,uint64_t val,unsigned size,MemTxAttrs attrs)197ec859557SBibo Mao static MemTxResult loongson_ipi_iocsr_writel(void *opaque, hwaddr addr,
198ec859557SBibo Mao uint64_t val, unsigned size,
199ec859557SBibo Mao MemTxAttrs attrs)
200ec859557SBibo Mao {
201ec859557SBibo Mao LoongsonIPICommonState *ipi = opaque;
202ec859557SBibo Mao IPICore *s;
203ec859557SBibo Mao
204ec859557SBibo Mao if (attrs.requester_id >= ipi->num_cpu) {
205ec859557SBibo Mao return MEMTX_DECODE_ERROR;
206ec859557SBibo Mao }
207ec859557SBibo Mao
208ec859557SBibo Mao s = &ipi->cpu[attrs.requester_id];
209ec859557SBibo Mao return loongson_ipi_core_writel(s, addr, val, size, attrs);
210ec859557SBibo Mao }
211ec859557SBibo Mao
212ec859557SBibo Mao static const MemoryRegionOps loongson_ipi_iocsr_ops = {
213ec859557SBibo Mao .read_with_attrs = loongson_ipi_iocsr_readl,
214ec859557SBibo Mao .write_with_attrs = loongson_ipi_iocsr_writel,
215ec859557SBibo Mao .impl.min_access_size = 4,
216ec859557SBibo Mao .impl.max_access_size = 4,
217ec859557SBibo Mao .valid.min_access_size = 4,
218ec859557SBibo Mao .valid.max_access_size = 8,
219ec859557SBibo Mao .endianness = DEVICE_LITTLE_ENDIAN,
220ec859557SBibo Mao };
221ec859557SBibo Mao
222ec859557SBibo Mao /* mail send and any send only support writeq */
loongson_ipi_writeq(void * opaque,hwaddr addr,uint64_t val,unsigned size,MemTxAttrs attrs)223ec859557SBibo Mao static MemTxResult loongson_ipi_writeq(void *opaque, hwaddr addr, uint64_t val,
224ec859557SBibo Mao unsigned size, MemTxAttrs attrs)
225ec859557SBibo Mao {
226ec859557SBibo Mao LoongsonIPICommonState *ipi = opaque;
227ec859557SBibo Mao MemTxResult ret = MEMTX_OK;
228ec859557SBibo Mao
229ec859557SBibo Mao addr &= 0xfff;
230ec859557SBibo Mao switch (addr) {
231ec859557SBibo Mao case MAIL_SEND_OFFSET:
232ec859557SBibo Mao ret = mail_send(ipi, val, attrs);
233ec859557SBibo Mao break;
234ec859557SBibo Mao case ANY_SEND_OFFSET:
235ec859557SBibo Mao ret = any_send(ipi, val, attrs);
236ec859557SBibo Mao break;
237ec859557SBibo Mao default:
238ec859557SBibo Mao break;
239ec859557SBibo Mao }
240ec859557SBibo Mao
241ec859557SBibo Mao return ret;
242ec859557SBibo Mao }
243ec859557SBibo Mao
244ec859557SBibo Mao static const MemoryRegionOps loongson_ipi64_ops = {
245ec859557SBibo Mao .write_with_attrs = loongson_ipi_writeq,
246ec859557SBibo Mao .impl.min_access_size = 8,
247ec859557SBibo Mao .impl.max_access_size = 8,
248ec859557SBibo Mao .valid.min_access_size = 8,
249ec859557SBibo Mao .valid.max_access_size = 8,
250ec859557SBibo Mao .endianness = DEVICE_LITTLE_ENDIAN,
251ec859557SBibo Mao };
252ec859557SBibo Mao
loongson_ipi_common_realize(DeviceState * dev,Error ** errp)253ec859557SBibo Mao static void loongson_ipi_common_realize(DeviceState *dev, Error **errp)
254ec859557SBibo Mao {
255ec859557SBibo Mao LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev);
256ec859557SBibo Mao SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
257ec859557SBibo Mao
258ec859557SBibo Mao memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev),
259ec859557SBibo Mao &loongson_ipi_iocsr_ops,
260ec859557SBibo Mao s, "loongson_ipi_iocsr", 0x48);
261ec859557SBibo Mao
262ec859557SBibo Mao /* loongson_ipi_iocsr performs re-entrant IO through ipi_send */
263ec859557SBibo Mao s->ipi_iocsr_mem.disable_reentrancy_guard = true;
264ec859557SBibo Mao
265ec859557SBibo Mao sysbus_init_mmio(sbd, &s->ipi_iocsr_mem);
266ec859557SBibo Mao
267ec859557SBibo Mao memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev),
268ec859557SBibo Mao &loongson_ipi64_ops,
269ec859557SBibo Mao s, "loongson_ipi64_iocsr", 0x118);
270ec859557SBibo Mao sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem);
271ec859557SBibo Mao }
272ec859557SBibo Mao
loongson_ipi_common_unrealize(DeviceState * dev)273ec859557SBibo Mao static void loongson_ipi_common_unrealize(DeviceState *dev)
274ec859557SBibo Mao {
275ec859557SBibo Mao LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev);
276ec859557SBibo Mao
277ec859557SBibo Mao g_free(s->cpu);
278ec859557SBibo Mao }
2796c8698a5SBibo Mao
2806c8698a5SBibo Mao static const VMStateDescription vmstate_ipi_core = {
2816c8698a5SBibo Mao .name = "ipi-single",
2826c8698a5SBibo Mao .version_id = 2,
2836c8698a5SBibo Mao .minimum_version_id = 2,
2846c8698a5SBibo Mao .fields = (const VMStateField[]) {
2856c8698a5SBibo Mao VMSTATE_UINT32(status, IPICore),
2866c8698a5SBibo Mao VMSTATE_UINT32(en, IPICore),
2876c8698a5SBibo Mao VMSTATE_UINT32(set, IPICore),
2886c8698a5SBibo Mao VMSTATE_UINT32(clear, IPICore),
2896c8698a5SBibo Mao VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2),
2906c8698a5SBibo Mao VMSTATE_END_OF_LIST()
2916c8698a5SBibo Mao }
2926c8698a5SBibo Mao };
2936c8698a5SBibo Mao
2946c8698a5SBibo Mao static const VMStateDescription vmstate_loongson_ipi_common = {
2956c8698a5SBibo Mao .name = "loongson_ipi",
2966c8698a5SBibo Mao .version_id = 2,
2976c8698a5SBibo Mao .minimum_version_id = 2,
2986c8698a5SBibo Mao .fields = (const VMStateField[]) {
2996c8698a5SBibo Mao VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongsonIPICommonState,
3006c8698a5SBibo Mao num_cpu, vmstate_ipi_core,
3016c8698a5SBibo Mao IPICore),
3026c8698a5SBibo Mao VMSTATE_END_OF_LIST()
3036c8698a5SBibo Mao }
3046c8698a5SBibo Mao };
3056c8698a5SBibo Mao
loongson_ipi_common_class_init(ObjectClass * klass,const void * data)306*12d1a768SPhilippe Mathieu-Daudé static void loongson_ipi_common_class_init(ObjectClass *klass, const void *data)
3076c8698a5SBibo Mao {
3086c8698a5SBibo Mao DeviceClass *dc = DEVICE_CLASS(klass);
309ec859557SBibo Mao LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass);
3106c8698a5SBibo Mao
311ec859557SBibo Mao device_class_set_parent_realize(dc, loongson_ipi_common_realize,
312ec859557SBibo Mao &licc->parent_realize);
313ec859557SBibo Mao device_class_set_parent_unrealize(dc, loongson_ipi_common_unrealize,
314ec859557SBibo Mao &licc->parent_unrealize);
3156c8698a5SBibo Mao dc->vmsd = &vmstate_loongson_ipi_common;
3166c8698a5SBibo Mao }
3177e555781SBibo Mao
3187e555781SBibo Mao static const TypeInfo loongarch_ipi_common_types[] = {
3197e555781SBibo Mao {
3207e555781SBibo Mao .name = TYPE_LOONGSON_IPI_COMMON,
3217e555781SBibo Mao .parent = TYPE_SYS_BUS_DEVICE,
3227e555781SBibo Mao .instance_size = sizeof(LoongsonIPICommonState),
3237e555781SBibo Mao .class_size = sizeof(LoongsonIPICommonClass),
3246c8698a5SBibo Mao .class_init = loongson_ipi_common_class_init,
3257e555781SBibo Mao .abstract = true,
3267e555781SBibo Mao }
3277e555781SBibo Mao };
3287e555781SBibo Mao
3297e555781SBibo Mao DEFINE_TYPES(loongarch_ipi_common_types)
330