IOCSR MISC register 0x420 controlls some features of eiointc, such
as BIT48 enables eiointc and BIT49 set interrupt encoding mode. Here
add IOCSR MISC register emulation in eiointc driver.
Signed-off-by: Bibo Mao <maobibo@loongson.cn>
---
arch/loongarch/include/asm/kvm_eiointc.h | 4 +
arch/loongarch/include/asm/loongarch.h | 1 +
arch/loongarch/kvm/intc/eiointc.c | 136 ++++++++++++++++++++++-
3 files changed, 140 insertions(+), 1 deletion(-)
diff --git a/arch/loongarch/include/asm/kvm_eiointc.h b/arch/loongarch/include/asm/kvm_eiointc.h
index a3a40aba8acf..77cd2b3a6f76 100644
--- a/arch/loongarch/include/asm/kvm_eiointc.h
+++ b/arch/loongarch/include/asm/kvm_eiointc.h
@@ -17,6 +17,9 @@
/* map to ipnum per 32 irqs */
#define EIOINTC_IRQS_NODETYPE_COUNT 16
+#define MISC_BASE 0x420
+#define MISC_SIZE 0x8
+
#define EIOINTC_BASE 0x1400
#define EIOINTC_SIZE 0x900
@@ -59,6 +62,7 @@ struct loongarch_eiointc {
struct kvm *kvm;
struct kvm_io_device device;
struct kvm_io_device device_vext;
+ struct kvm_io_device misc;
uint32_t num_cpu;
uint32_t features;
uint32_t status;
diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h
index d84dac88a584..e30d330d497e 100644
--- a/arch/loongarch/include/asm/loongarch.h
+++ b/arch/loongarch/include/asm/loongarch.h
@@ -1141,6 +1141,7 @@
#define IOCSR_MISC_FUNC_SOFT_INT BIT_ULL(10)
#define IOCSR_MISC_FUNC_TIMER_RESET BIT_ULL(21)
#define IOCSR_MISC_FUNC_EXT_IOI_EN BIT_ULL(48)
+#define IOCSR_MISC_FUNC_INT_ENCODE BIT_ULL(49)
#define IOCSR_MISC_FUNC_AVEC_EN BIT_ULL(51)
#define LOONGARCH_IOCSR_CPUTEMP 0x428
diff --git a/arch/loongarch/kvm/intc/eiointc.c b/arch/loongarch/kvm/intc/eiointc.c
index d9c4fe93405d..26b8e52d74f1 100644
--- a/arch/loongarch/kvm/intc/eiointc.c
+++ b/arch/loongarch/kvm/intc/eiointc.c
@@ -781,6 +781,129 @@ static const struct kvm_io_device_ops kvm_eiointc_virt_ops = {
.write = kvm_eiointc_virt_write,
};
+
+static int kvm_misc_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
+ gpa_t addr, int len, void *val)
+{
+ unsigned long flags, data, status;
+ unsigned int shift;
+ struct loongarch_eiointc *eiointc = vcpu->kvm->arch.eiointc;
+
+ if (!eiointc) {
+ kvm_err("%s: eiointc irqchip not valid!\n", __func__);
+ return -EINVAL;
+ }
+
+ addr -= MISC_BASE;
+ if (addr & (len - 1)) {
+ kvm_err("%s: eiointc not aligned addr %llx len %d\n", __func__, addr, len);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&eiointc->lock, flags);
+ status = eiointc->status;
+ spin_unlock_irqrestore(&eiointc->lock, flags);
+
+ data = 0;
+ if (status & BIT(EIOINTC_ENABLE))
+ data |= IOCSR_MISC_FUNC_EXT_IOI_EN;
+ if (status & BIT(EIOINTC_ENABLE_INT_ENCODE))
+ data |= IOCSR_MISC_FUNC_INT_ENCODE;
+
+ shift = (addr & 7) * 8;
+ data = data >> shift;
+ switch (len) {
+ case 1:
+ *(unsigned char *)val = (unsigned char)data;
+ break;
+
+ case 2:
+ *(unsigned short *)val = (unsigned short)data;
+ break;
+
+ case 4:
+ *(unsigned int *)val = (unsigned int)data;
+ break;
+
+ default:
+ *(unsigned long *)val = data;
+ break;
+ }
+
+ return 0;
+}
+
+static int kvm_misc_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
+ gpa_t addr, int len, const void *val)
+{
+ unsigned long flags, data, mask, old;
+ unsigned int shift;
+ struct loongarch_eiointc *eiointc = vcpu->kvm->arch.eiointc;
+
+ if (!eiointc) {
+ kvm_err("%s: eiointc irqchip not valid!\n", __func__);
+ return -EINVAL;
+ }
+
+ addr -= MISC_BASE;
+ if (addr & (len - 1)) {
+ kvm_err("%s: eiointc not aligned addr %llx len %d\n", __func__, addr, len);
+ return -EINVAL;
+ }
+
+ shift = (addr & 7) * 8;
+ switch (len) {
+ case 1:
+ data = *(unsigned char *)val;
+ mask = 0xFF;
+ mask = mask << shift;
+ data = data << shift;
+ break;
+
+ case 2:
+ data = *(unsigned short *)val;
+ mask = 0xFFFF;
+ mask = mask << shift;
+ data = data << shift;
+ break;
+
+ case 4:
+ data = *(unsigned int *)val;
+ mask = UINT_MAX;
+ mask = mask << shift;
+ data = data << shift;
+ break;
+
+ default:
+ data = *(unsigned long *)val;
+ mask = ULONG_MAX;
+ mask = mask << shift;
+ data = data << shift;
+ break;
+ }
+
+ spin_lock_irqsave(&eiointc->lock, flags);
+ old = 0;
+ if (eiointc->status & BIT(EIOINTC_ENABLE))
+ old |= IOCSR_MISC_FUNC_EXT_IOI_EN;
+ if (eiointc->status & BIT(EIOINTC_ENABLE_INT_ENCODE))
+ old |= IOCSR_MISC_FUNC_INT_ENCODE;
+
+ data = (old & ~mask) | data;
+ eiointc->status &= ~(BIT(EIOINTC_ENABLE_INT_ENCODE) | BIT(EIOINTC_ENABLE));
+ if (data & IOCSR_MISC_FUNC_INT_ENCODE)
+ eiointc->status |= BIT(EIOINTC_ENABLE_INT_ENCODE);
+ if (data & IOCSR_MISC_FUNC_EXT_IOI_EN)
+ eiointc->status |= BIT(EIOINTC_ENABLE);
+ spin_unlock_irqrestore(&eiointc->lock, flags);
+ return 0;
+}
+
+static const struct kvm_io_device_ops kvm_misc_ops = {
+ .read = kvm_misc_read,
+ .write = kvm_misc_write,
+};
+
static int kvm_eiointc_ctrl_access(struct kvm_device *dev,
struct kvm_device_attr *attr)
{
@@ -993,8 +1116,18 @@ static int kvm_eiointc_create(struct kvm_device *dev, u32 type)
kfree(s);
return ret;
}
- kvm->arch.eiointc = s;
+ device = &s->misc;
+ kvm_iodevice_init(device, &kvm_misc_ops);
+ ret = kvm_io_bus_register_dev(kvm, KVM_IOCSR_BUS, MISC_BASE, MISC_SIZE, device);
+ if (ret < 0) {
+ kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &s->device);
+ kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &s->device_vext);
+ kfree(s);
+ return ret;
+ }
+
+ kvm->arch.eiointc = s;
return 0;
}
@@ -1010,6 +1143,7 @@ static void kvm_eiointc_destroy(struct kvm_device *dev)
eiointc = kvm->arch.eiointc;
kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &eiointc->device);
kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &eiointc->device_vext);
+ kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &eiointc->misc);
kfree(eiointc);
}
--
2.39.3
© 2016 - 2026 Red Hat, Inc.