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