1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Loongson-3 Virtual IPI interrupt support.
5 * Copyright (C) 2019 Loongson Technologies, Inc. All rights reserved.
7 * Authors: Chen Zhu <zhuchen@loongson.cn>
8 * Authors: Huacai Chen <chenhc@lemote.com>
11 #include <linux/kvm_host.h>
13 #include "interrupt.h"
15 #define IPI_BASE 0x3ff01000ULL
17 #define CORE0_STATUS_OFF 0x000
18 #define CORE0_EN_OFF 0x004
19 #define CORE0_SET_OFF 0x008
20 #define CORE0_CLEAR_OFF 0x00c
21 #define CORE0_BUF_20 0x020
22 #define CORE0_BUF_28 0x028
23 #define CORE0_BUF_30 0x030
24 #define CORE0_BUF_38 0x038
26 #define CORE1_STATUS_OFF 0x100
27 #define CORE1_EN_OFF 0x104
28 #define CORE1_SET_OFF 0x108
29 #define CORE1_CLEAR_OFF 0x10c
30 #define CORE1_BUF_20 0x120
31 #define CORE1_BUF_28 0x128
32 #define CORE1_BUF_30 0x130
33 #define CORE1_BUF_38 0x138
35 #define CORE2_STATUS_OFF 0x200
36 #define CORE2_EN_OFF 0x204
37 #define CORE2_SET_OFF 0x208
38 #define CORE2_CLEAR_OFF 0x20c
39 #define CORE2_BUF_20 0x220
40 #define CORE2_BUF_28 0x228
41 #define CORE2_BUF_30 0x230
42 #define CORE2_BUF_38 0x238
44 #define CORE3_STATUS_OFF 0x300
45 #define CORE3_EN_OFF 0x304
46 #define CORE3_SET_OFF 0x308
47 #define CORE3_CLEAR_OFF 0x30c
48 #define CORE3_BUF_20 0x320
49 #define CORE3_BUF_28 0x328
50 #define CORE3_BUF_30 0x330
51 #define CORE3_BUF_38 0x338
53 static int loongson_vipi_read(struct loongson_kvm_ipi
*ipi
,
54 gpa_t addr
, int len
, void *val
)
56 uint32_t core
= (addr
>> 8) & 3;
57 uint32_t node
= (addr
>> 44) & 3;
58 uint32_t id
= core
+ node
* 4;
59 uint64_t offset
= addr
& 0xff;
61 struct ipi_state
*s
= &(ipi
->ipistate
[id
]);
63 BUG_ON(offset
& (len
- 1));
66 case CORE0_STATUS_OFF
:
67 *(uint64_t *)val
= s
->status
;
71 *(uint64_t *)val
= s
->en
;
82 case CORE0_BUF_20
... CORE0_BUF_38
:
83 pbuf
= (void *)s
->buf
+ (offset
- 0x20);
85 *(uint64_t *)val
= *(uint64_t *)pbuf
;
86 else /* Assume len == 4 */
87 *(uint32_t *)val
= *(uint32_t *)pbuf
;
91 pr_notice("%s with unknown addr %llx\n", __func__
, addr
);
98 static int loongson_vipi_write(struct loongson_kvm_ipi
*ipi
,
99 gpa_t addr
, int len
, const void *val
)
101 uint32_t core
= (addr
>> 8) & 3;
102 uint32_t node
= (addr
>> 44) & 3;
103 uint32_t id
= core
+ node
* 4;
104 uint64_t data
, offset
= addr
& 0xff;
106 struct kvm
*kvm
= ipi
->kvm
;
107 struct kvm_mips_interrupt irq
;
108 struct ipi_state
*s
= &(ipi
->ipistate
[id
]);
110 data
= *(uint64_t *)val
;
111 BUG_ON(offset
& (len
- 1));
114 case CORE0_STATUS_OFF
:
125 kvm_vcpu_ioctl_interrupt(kvm_get_vcpu(kvm
, id
), &irq
);
128 case CORE0_CLEAR_OFF
:
133 kvm_vcpu_ioctl_interrupt(kvm_get_vcpu(kvm
, id
), &irq
);
137 case CORE0_BUF_20
... CORE0_BUF_38
:
138 pbuf
= (void *)s
->buf
+ (offset
- 0x20);
140 *(uint64_t *)pbuf
= (uint64_t)data
;
141 else /* Assume len == 4 */
142 *(uint32_t *)pbuf
= (uint32_t)data
;
146 pr_notice("%s with unknown addr %llx\n", __func__
, addr
);
153 static int kvm_ipi_read(struct kvm_vcpu
*vcpu
, struct kvm_io_device
*dev
,
154 gpa_t addr
, int len
, void *val
)
157 struct loongson_kvm_ipi
*ipi
;
158 struct ipi_io_device
*ipi_device
;
160 ipi_device
= container_of(dev
, struct ipi_io_device
, device
);
161 ipi
= ipi_device
->ipi
;
163 spin_lock_irqsave(&ipi
->lock
, flags
);
164 loongson_vipi_read(ipi
, addr
, len
, val
);
165 spin_unlock_irqrestore(&ipi
->lock
, flags
);
170 static int kvm_ipi_write(struct kvm_vcpu
*vcpu
, struct kvm_io_device
*dev
,
171 gpa_t addr
, int len
, const void *val
)
174 struct loongson_kvm_ipi
*ipi
;
175 struct ipi_io_device
*ipi_device
;
177 ipi_device
= container_of(dev
, struct ipi_io_device
, device
);
178 ipi
= ipi_device
->ipi
;
180 spin_lock_irqsave(&ipi
->lock
, flags
);
181 loongson_vipi_write(ipi
, addr
, len
, val
);
182 spin_unlock_irqrestore(&ipi
->lock
, flags
);
187 static const struct kvm_io_device_ops kvm_ipi_ops
= {
188 .read
= kvm_ipi_read
,
189 .write
= kvm_ipi_write
,
192 void kvm_init_loongson_ipi(struct kvm
*kvm
)
196 struct loongson_kvm_ipi
*s
;
197 struct kvm_io_device
*device
;
201 spin_lock_init(&s
->lock
);
204 * Initialize IPI device
206 for (i
= 0; i
< 4; i
++) {
207 device
= &s
->dev_ipi
[i
].device
;
208 kvm_iodevice_init(device
, &kvm_ipi_ops
);
209 addr
= (((unsigned long)i
) << 44) + IPI_BASE
;
210 mutex_lock(&kvm
->slots_lock
);
211 kvm_io_bus_register_dev(kvm
, KVM_MMIO_BUS
, addr
, 0x400, device
);
212 mutex_unlock(&kvm
->slots_lock
);
213 s
->dev_ipi
[i
].ipi
= s
;
214 s
->dev_ipi
[i
].node_id
= i
;