18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/errno.h>
78c2ecf20Sopenharmony_ci#include <linux/err.h>
88c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
98c2ecf20Sopenharmony_ci#include <linux/fs.h>
108c2ecf20Sopenharmony_ci#include <asm/page.h>
118c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
128c2ecf20Sopenharmony_ci#include "kvmcpu.h"
138c2ecf20Sopenharmony_ci#include <linux/kvm_host.h>
148c2ecf20Sopenharmony_ci#include "kvm_compat.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic u32 int_to_coreint[LOONGARCH_EXC_MAX] = {
178c2ecf20Sopenharmony_ci	[LARCH_INT_TIMER]	= CPU_TIMER,
188c2ecf20Sopenharmony_ci	[LARCH_INT_IPI]		= CPU_IPI,
198c2ecf20Sopenharmony_ci	[LARCH_INT_SIP0]	= CPU_SIP0,
208c2ecf20Sopenharmony_ci	[LARCH_INT_SIP1]	= CPU_SIP1,
218c2ecf20Sopenharmony_ci	[LARCH_INT_IP0]		= CPU_IP0,
228c2ecf20Sopenharmony_ci	[LARCH_INT_IP1]		= CPU_IP1,
238c2ecf20Sopenharmony_ci	[LARCH_INT_IP2]		= CPU_IP2,
248c2ecf20Sopenharmony_ci	[LARCH_INT_IP3]		= CPU_IP3,
258c2ecf20Sopenharmony_ci	[LARCH_INT_IP4]		= CPU_IP4,
268c2ecf20Sopenharmony_ci	[LARCH_INT_IP5]		= CPU_IP5,
278c2ecf20Sopenharmony_ci	[LARCH_INT_IP6]		= CPU_IP6,
288c2ecf20Sopenharmony_ci	[LARCH_INT_IP7]		= CPU_IP7,
298c2ecf20Sopenharmony_ci};
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic int _kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	unsigned int irq = 0;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	clear_bit(priority, &vcpu->arch.irq_pending);
368c2ecf20Sopenharmony_ci	if (priority < LOONGARCH_EXC_MAX)
378c2ecf20Sopenharmony_ci		irq = int_to_coreint[priority];
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	switch (priority) {
408c2ecf20Sopenharmony_ci	case LARCH_INT_TIMER:
418c2ecf20Sopenharmony_ci	case LARCH_INT_IPI:
428c2ecf20Sopenharmony_ci	case LARCH_INT_SIP0:
438c2ecf20Sopenharmony_ci	case LARCH_INT_SIP1:
448c2ecf20Sopenharmony_ci		kvm_set_gcsr_estat(irq);
458c2ecf20Sopenharmony_ci		break;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	case LARCH_INT_IP0:
488c2ecf20Sopenharmony_ci	case LARCH_INT_IP1:
498c2ecf20Sopenharmony_ci	case LARCH_INT_IP2:
508c2ecf20Sopenharmony_ci	case LARCH_INT_IP3:
518c2ecf20Sopenharmony_ci	case LARCH_INT_IP4:
528c2ecf20Sopenharmony_ci	case LARCH_INT_IP5:
538c2ecf20Sopenharmony_ci	case LARCH_INT_IP6:
548c2ecf20Sopenharmony_ci	case LARCH_INT_IP7:
558c2ecf20Sopenharmony_ci		kvm_set_csr_gintc(irq);
568c2ecf20Sopenharmony_ci		break;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	default:
598c2ecf20Sopenharmony_ci		break;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return 1;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int _kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	unsigned int irq = 0;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	clear_bit(priority, &vcpu->arch.irq_clear);
708c2ecf20Sopenharmony_ci	if (priority < LOONGARCH_EXC_MAX)
718c2ecf20Sopenharmony_ci		irq = int_to_coreint[priority];
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	switch (priority) {
748c2ecf20Sopenharmony_ci	case LARCH_INT_TIMER:
758c2ecf20Sopenharmony_ci	case LARCH_INT_IPI:
768c2ecf20Sopenharmony_ci	case LARCH_INT_SIP0:
778c2ecf20Sopenharmony_ci	case LARCH_INT_SIP1:
788c2ecf20Sopenharmony_ci		kvm_clear_gcsr_estat(irq);
798c2ecf20Sopenharmony_ci		break;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	case LARCH_INT_IP0:
828c2ecf20Sopenharmony_ci	case LARCH_INT_IP1:
838c2ecf20Sopenharmony_ci	case LARCH_INT_IP2:
848c2ecf20Sopenharmony_ci	case LARCH_INT_IP3:
858c2ecf20Sopenharmony_ci	case LARCH_INT_IP4:
868c2ecf20Sopenharmony_ci	case LARCH_INT_IP5:
878c2ecf20Sopenharmony_ci	case LARCH_INT_IP6:
888c2ecf20Sopenharmony_ci	case LARCH_INT_IP7:
898c2ecf20Sopenharmony_ci		kvm_clear_csr_gintc(irq);
908c2ecf20Sopenharmony_ci		break;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	default:
938c2ecf20Sopenharmony_ci		break;
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return 1;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_civoid _kvm_deliver_intr(struct kvm_vcpu *vcpu)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	unsigned long *pending = &vcpu->arch.irq_pending;
1028c2ecf20Sopenharmony_ci	unsigned long *pending_clr = &vcpu->arch.irq_clear;
1038c2ecf20Sopenharmony_ci	unsigned int priority;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (!(*pending) && !(*pending_clr))
1068c2ecf20Sopenharmony_ci		return;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (*pending_clr) {
1098c2ecf20Sopenharmony_ci		priority = __ffs(*pending_clr);
1108c2ecf20Sopenharmony_ci		while (priority <= LOONGARCH_EXC_IPNUM) {
1118c2ecf20Sopenharmony_ci			_kvm_irq_clear(vcpu, priority);
1128c2ecf20Sopenharmony_ci			priority = find_next_bit(pending_clr,
1138c2ecf20Sopenharmony_ci					BITS_PER_BYTE * sizeof(*pending_clr),
1148c2ecf20Sopenharmony_ci					priority + 1);
1158c2ecf20Sopenharmony_ci		}
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (*pending) {
1198c2ecf20Sopenharmony_ci		priority = __ffs(*pending);
1208c2ecf20Sopenharmony_ci		while (priority <= LOONGARCH_EXC_IPNUM) {
1218c2ecf20Sopenharmony_ci			_kvm_irq_deliver(vcpu, priority);
1228c2ecf20Sopenharmony_ci			priority = find_next_bit(pending,
1238c2ecf20Sopenharmony_ci					BITS_PER_BYTE * sizeof(*pending),
1248c2ecf20Sopenharmony_ci					priority + 1);
1258c2ecf20Sopenharmony_ci		}
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ciint _kvm_pending_timer(struct kvm_vcpu *vcpu)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	return test_bit(LARCH_INT_TIMER, &vcpu->arch.irq_pending);
1338c2ecf20Sopenharmony_ci}
134