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 "kvmcpu.h"
78c2ecf20Sopenharmony_ci#include "ls3a_ipi.h"
88c2ecf20Sopenharmony_ci#include "ls7a_irq.h"
98c2ecf20Sopenharmony_ci#include "ls3a_ext_irq.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define ls3a_gipi_lock(s, flags)	spin_lock_irqsave(&s->lock, flags)
128c2ecf20Sopenharmony_ci#define ls3a_gipi_unlock(s, flags)	spin_unlock_irqrestore(&s->lock, flags)
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ciextern int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu,
158c2ecf20Sopenharmony_ci			     struct kvm_loongarch_interrupt *irq);
168c2ecf20Sopenharmony_ciint kvm_helper_send_ipi(struct kvm_vcpu *vcpu, unsigned int cpu, unsigned int action)
178c2ecf20Sopenharmony_ci{
188c2ecf20Sopenharmony_ci	struct kvm *kvm = vcpu->kvm;
198c2ecf20Sopenharmony_ci	struct ls3a_kvm_ipi *ipi = ls3a_ipi_irqchip(kvm);
208c2ecf20Sopenharmony_ci	gipiState *s = &(ipi->ls3a_gipistate);
218c2ecf20Sopenharmony_ci	unsigned long flags;
228c2ecf20Sopenharmony_ci	struct kvm_loongarch_interrupt irq;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	kvm->stat.pip_write_exits++;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	ls3a_gipi_lock(ipi, flags);
278c2ecf20Sopenharmony_ci	if (s->core[cpu].status == 0) {
288c2ecf20Sopenharmony_ci		irq.cpu = cpu;
298c2ecf20Sopenharmony_ci		irq.irq = LARCH_INT_IPI;
308c2ecf20Sopenharmony_ci		kvm_vcpu_ioctl_interrupt(kvm->vcpus[cpu], &irq);
318c2ecf20Sopenharmony_ci	}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	s->core[cpu].status |= action;
348c2ecf20Sopenharmony_ci	ls3a_gipi_unlock(ipi, flags);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	return 0;
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic int ls3a_gipi_writel(struct ls3a_kvm_ipi *ipi, gpa_t addr,
408c2ecf20Sopenharmony_ci						int len, const void *val)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	uint64_t data, offset;
438c2ecf20Sopenharmony_ci	struct kvm_loongarch_interrupt irq;
448c2ecf20Sopenharmony_ci	gipiState *s = &(ipi->ls3a_gipistate);
458c2ecf20Sopenharmony_ci	uint32_t cpu, action_data;
468c2ecf20Sopenharmony_ci	struct kvm *kvm;
478c2ecf20Sopenharmony_ci	void *pbuf;
488c2ecf20Sopenharmony_ci	int mailbox, action;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	kvm = ipi->kvm;
518c2ecf20Sopenharmony_ci	cpu = (addr >> 8) & 0xff;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	data = *(uint64_t *)val;
548c2ecf20Sopenharmony_ci	offset = addr & 0xFF;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	BUG_ON(offset & (len - 1));
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	switch (offset) {
598c2ecf20Sopenharmony_ci	case CORE0_STATUS_OFF:
608c2ecf20Sopenharmony_ci		printk("CORE0_SET_OFF Can't be write\n");
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci		break;
638c2ecf20Sopenharmony_ci	case CORE0_EN_OFF:
648c2ecf20Sopenharmony_ci		s->core[cpu].en = data;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci		break;
678c2ecf20Sopenharmony_ci	case CORE0_IPI_SEND:
688c2ecf20Sopenharmony_ci		cpu = ((data & 0xffffffff) >> 16) & 0x3ff;
698c2ecf20Sopenharmony_ci		action = (data & 0x1f);
708c2ecf20Sopenharmony_ci		action_data = (1 << action);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci		if (s->core[cpu].status == 0) {
738c2ecf20Sopenharmony_ci			irq.cpu = cpu;
748c2ecf20Sopenharmony_ci			irq.irq = LARCH_INT_IPI;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci			if (likely(kvm->vcpus[cpu])) {
778c2ecf20Sopenharmony_ci				kvm_vcpu_ioctl_interrupt(kvm->vcpus[cpu], &irq);
788c2ecf20Sopenharmony_ci			}
798c2ecf20Sopenharmony_ci		}
808c2ecf20Sopenharmony_ci		s->core[cpu].status |= action_data;
818c2ecf20Sopenharmony_ci		break;
828c2ecf20Sopenharmony_ci	case CORE0_SET_OFF:
838c2ecf20Sopenharmony_ci		pr_info("CORE0_SET_OFF simulation is required\n");
848c2ecf20Sopenharmony_ci		break;
858c2ecf20Sopenharmony_ci	case CORE0_CLEAR_OFF:
868c2ecf20Sopenharmony_ci		s->core[cpu].status &= ~data;
878c2ecf20Sopenharmony_ci		if (!s->core[cpu].status) {
888c2ecf20Sopenharmony_ci			irq.cpu = cpu;
898c2ecf20Sopenharmony_ci			irq.irq = -LARCH_INT_IPI;
908c2ecf20Sopenharmony_ci			if (likely(kvm->vcpus[cpu]))
918c2ecf20Sopenharmony_ci				kvm_vcpu_ioctl_interrupt(kvm->vcpus[cpu], &irq);
928c2ecf20Sopenharmony_ci			else
938c2ecf20Sopenharmony_ci				kvm_err("Failed lower ipi irq target cpu:%d\n", cpu);
948c2ecf20Sopenharmony_ci		}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci		break;
978c2ecf20Sopenharmony_ci	case CORE0_MAIL_SEND:
988c2ecf20Sopenharmony_ci		cpu = ((data & 0xffffffff) >> 16) & 0x3ff;
998c2ecf20Sopenharmony_ci		mailbox = ((data & 0xffffffff) >> 2) & 0x7;
1008c2ecf20Sopenharmony_ci		pbuf =  (void *)s->core[cpu].buf + mailbox * 4;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci		*(unsigned int *)pbuf = (unsigned int)(data >> 32);
1038c2ecf20Sopenharmony_ci		break;
1048c2ecf20Sopenharmony_ci	case 0x20 ... 0x3c:
1058c2ecf20Sopenharmony_ci		pbuf =  (void *)s->core[cpu].buf + (offset - 0x20);
1068c2ecf20Sopenharmony_ci		if (len == 1)
1078c2ecf20Sopenharmony_ci			*(unsigned char *)pbuf = (unsigned char)data;
1088c2ecf20Sopenharmony_ci		else if (len == 2)
1098c2ecf20Sopenharmony_ci			*(unsigned short *)pbuf = (unsigned short)data;
1108c2ecf20Sopenharmony_ci		else if (len == 4)
1118c2ecf20Sopenharmony_ci			*(unsigned int *)pbuf = (unsigned int)data;
1128c2ecf20Sopenharmony_ci		else if (len == 8)
1138c2ecf20Sopenharmony_ci			*(unsigned long *)pbuf = (unsigned long)data;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci		break;
1168c2ecf20Sopenharmony_ci	default:
1178c2ecf20Sopenharmony_ci		printk("ls3a_gipi_writel with unknown addr %llx \n", addr);
1188c2ecf20Sopenharmony_ci		break;
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci	return 0;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic uint64_t ls3a_gipi_readl(struct ls3a_kvm_ipi *ipi,
1248c2ecf20Sopenharmony_ci				gpa_t addr, int len, void *val)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	uint64_t offset;
1278c2ecf20Sopenharmony_ci	uint64_t ret = 0;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	gipiState *s = &(ipi->ls3a_gipistate);
1308c2ecf20Sopenharmony_ci	uint32_t cpu;
1318c2ecf20Sopenharmony_ci	void *pbuf;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	cpu = (addr >> 8) & 0xff;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	offset = addr & 0xFF;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	BUG_ON(offset & (len - 1));
1388c2ecf20Sopenharmony_ci	switch (offset) {
1398c2ecf20Sopenharmony_ci	case CORE0_STATUS_OFF:
1408c2ecf20Sopenharmony_ci		ret = s->core[cpu].status;
1418c2ecf20Sopenharmony_ci		break;
1428c2ecf20Sopenharmony_ci	case CORE0_EN_OFF:
1438c2ecf20Sopenharmony_ci		ret = s->core[cpu].en;
1448c2ecf20Sopenharmony_ci		break;
1458c2ecf20Sopenharmony_ci	case CORE0_SET_OFF:
1468c2ecf20Sopenharmony_ci		ret = 0;
1478c2ecf20Sopenharmony_ci		break;
1488c2ecf20Sopenharmony_ci	case CORE0_CLEAR_OFF:
1498c2ecf20Sopenharmony_ci		ret = 0;
1508c2ecf20Sopenharmony_ci		break;
1518c2ecf20Sopenharmony_ci	case 0x20 ... 0x3c:
1528c2ecf20Sopenharmony_ci		pbuf =  (void *)s->core[cpu].buf + (offset - 0x20);
1538c2ecf20Sopenharmony_ci		if (len == 1)
1548c2ecf20Sopenharmony_ci			ret  = *(unsigned char *)pbuf;
1558c2ecf20Sopenharmony_ci		else if (len == 2)
1568c2ecf20Sopenharmony_ci			ret = *(unsigned short *)pbuf;
1578c2ecf20Sopenharmony_ci		else if (len == 4)
1588c2ecf20Sopenharmony_ci			ret = *(unsigned int *)pbuf;
1598c2ecf20Sopenharmony_ci		else if (len == 8)
1608c2ecf20Sopenharmony_ci			ret = *(unsigned long *)pbuf;
1618c2ecf20Sopenharmony_ci		break;
1628c2ecf20Sopenharmony_ci	default:
1638c2ecf20Sopenharmony_ci		printk("ls3a_gipi_readl with unknown addr %llx \n", addr);
1648c2ecf20Sopenharmony_ci		break;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	*(uint64_t *)val = ret;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return ret;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic int kvm_ls3a_ipi_write(struct kvm_vcpu *vcpu,
1738c2ecf20Sopenharmony_ci			      struct kvm_io_device *dev,
1748c2ecf20Sopenharmony_ci			      gpa_t addr, int len, const void *val)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	struct ls3a_kvm_ipi *ipi;
1778c2ecf20Sopenharmony_ci	ipi_io_device *ipi_device;
1788c2ecf20Sopenharmony_ci	unsigned long flags;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	ipi_device = container_of(dev, ipi_io_device, device);
1818c2ecf20Sopenharmony_ci	ipi = ipi_device->ipi;
1828c2ecf20Sopenharmony_ci	ipi->kvm->stat.pip_write_exits++;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	ls3a_gipi_lock(ipi, flags);
1858c2ecf20Sopenharmony_ci	ls3a_gipi_writel(ipi, addr, len, val);
1868c2ecf20Sopenharmony_ci	ls3a_gipi_unlock(ipi, flags);
1878c2ecf20Sopenharmony_ci	return 0;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic int kvm_ls3a_ipi_read(struct kvm_vcpu *vcpu,
1928c2ecf20Sopenharmony_ci			     struct kvm_io_device *dev,
1938c2ecf20Sopenharmony_ci			     gpa_t addr, int len, void *val)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	struct ls3a_kvm_ipi *ipi;
1968c2ecf20Sopenharmony_ci	ipi_io_device *ipi_device;
1978c2ecf20Sopenharmony_ci	unsigned long flags;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	ipi_device = container_of(dev, ipi_io_device, device);
2008c2ecf20Sopenharmony_ci	ipi = ipi_device->ipi;
2018c2ecf20Sopenharmony_ci	ipi->kvm->stat.pip_read_exits++;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	ls3a_gipi_lock(ipi, flags);
2048c2ecf20Sopenharmony_ci	ls3a_gipi_readl(ipi, addr, len, val);
2058c2ecf20Sopenharmony_ci	ls3a_gipi_unlock(ipi, flags);
2068c2ecf20Sopenharmony_ci	return 0;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic const struct kvm_io_device_ops kvm_ls3a_ipi_ops = {
2118c2ecf20Sopenharmony_ci	.read	= kvm_ls3a_ipi_read,
2128c2ecf20Sopenharmony_ci	.write	= kvm_ls3a_ipi_write,
2138c2ecf20Sopenharmony_ci};
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_civoid kvm_destroy_ls3a_ipi(struct kvm *kvm)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	struct kvm_io_device *device;
2188c2ecf20Sopenharmony_ci	struct ls3a_kvm_ipi *vipi = kvm->arch.v_gipi;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	if (!vipi)
2218c2ecf20Sopenharmony_ci		return;
2228c2ecf20Sopenharmony_ci	device = &vipi->dev_ls3a_ipi.device;
2238c2ecf20Sopenharmony_ci	mutex_lock(&kvm->slots_lock);
2248c2ecf20Sopenharmony_ci	kvm_io_bus_unregister_dev(vipi->kvm, KVM_MMIO_BUS, device);
2258c2ecf20Sopenharmony_ci	mutex_unlock(&kvm->slots_lock);
2268c2ecf20Sopenharmony_ci	kfree(vipi);
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ciint kvm_create_ls3a_ipi(struct kvm *kvm)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	struct ls3a_kvm_ipi *s;
2328c2ecf20Sopenharmony_ci	unsigned long addr;
2338c2ecf20Sopenharmony_ci	struct kvm_io_device *device;
2348c2ecf20Sopenharmony_ci	int ret;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	s = kzalloc(sizeof(struct ls3a_kvm_ipi), GFP_KERNEL);
2378c2ecf20Sopenharmony_ci	if (!s)
2388c2ecf20Sopenharmony_ci		return -ENOMEM;
2398c2ecf20Sopenharmony_ci	spin_lock_init(&s->lock);
2408c2ecf20Sopenharmony_ci	s->kvm = kvm;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	/*
2438c2ecf20Sopenharmony_ci	 * Initialize MMIO device
2448c2ecf20Sopenharmony_ci	 */
2458c2ecf20Sopenharmony_ci	device = &s->dev_ls3a_ipi.device;
2468c2ecf20Sopenharmony_ci	kvm_iodevice_init(device, &kvm_ls3a_ipi_ops);
2478c2ecf20Sopenharmony_ci	addr = SMP_MAILBOX;
2488c2ecf20Sopenharmony_ci	mutex_lock(&kvm->slots_lock);
2498c2ecf20Sopenharmony_ci	ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS,
2508c2ecf20Sopenharmony_ci			addr, KVM_IOCSR_IPI_ADDR_SIZE, device);
2518c2ecf20Sopenharmony_ci	mutex_unlock(&kvm->slots_lock);
2528c2ecf20Sopenharmony_ci	if (ret < 0) {
2538c2ecf20Sopenharmony_ci		kvm_err("%s Initialize MMIO dev err ret:%d\n", __func__, ret);
2548c2ecf20Sopenharmony_ci		goto err;
2558c2ecf20Sopenharmony_ci	} else {
2568c2ecf20Sopenharmony_ci		s->dev_ls3a_ipi.ipi = s;
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	kvm->arch.v_gipi = s;
2608c2ecf20Sopenharmony_ci	return 0;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cierr:
2638c2ecf20Sopenharmony_ci	kfree(s);
2648c2ecf20Sopenharmony_ci	return -EFAULT;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ciint kvm_get_ls3a_ipi(struct kvm *kvm, struct loongarch_gipiState *state)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	struct ls3a_kvm_ipi *ipi = ls3a_ipi_irqchip(kvm);
2708c2ecf20Sopenharmony_ci	gipiState *ipi_state =  &(ipi->ls3a_gipistate);
2718c2ecf20Sopenharmony_ci	unsigned long flags;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	ls3a_gipi_lock(ipi, flags);
2748c2ecf20Sopenharmony_ci	memcpy(state, ipi_state, sizeof(gipiState));
2758c2ecf20Sopenharmony_ci	ls3a_gipi_unlock(ipi, flags);
2768c2ecf20Sopenharmony_ci	return 0;
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ciint kvm_set_ls3a_ipi(struct kvm *kvm, struct loongarch_gipiState *state)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct ls3a_kvm_ipi *ipi = ls3a_ipi_irqchip(kvm);
2828c2ecf20Sopenharmony_ci	gipiState *ipi_state =  &(ipi->ls3a_gipistate);
2838c2ecf20Sopenharmony_ci	unsigned long flags;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	if (!ipi)
2868c2ecf20Sopenharmony_ci		return -EINVAL;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	ls3a_gipi_lock(ipi, flags);
2898c2ecf20Sopenharmony_ci	memcpy(ipi_state, state, sizeof(gipiState));
2908c2ecf20Sopenharmony_ci	ls3a_gipi_unlock(ipi, flags);
2918c2ecf20Sopenharmony_ci	return 0;
2928c2ecf20Sopenharmony_ci}
293