162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2021 Western Digital Corporation or its affiliates. 462306a36Sopenharmony_ci * Copyright (C) 2022 Ventana Micro Systems Inc. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Authors: 762306a36Sopenharmony_ci * Anup Patel <apatel@ventanamicro.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/bitops.h> 1262306a36Sopenharmony_ci#include <linux/irq.h> 1362306a36Sopenharmony_ci#include <linux/irqdomain.h> 1462306a36Sopenharmony_ci#include <linux/kvm_host.h> 1562306a36Sopenharmony_ci#include <linux/percpu.h> 1662306a36Sopenharmony_ci#include <linux/spinlock.h> 1762306a36Sopenharmony_ci#include <asm/hwcap.h> 1862306a36Sopenharmony_ci#include <asm/kvm_aia_imsic.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct aia_hgei_control { 2162306a36Sopenharmony_ci raw_spinlock_t lock; 2262306a36Sopenharmony_ci unsigned long free_bitmap; 2362306a36Sopenharmony_ci struct kvm_vcpu *owners[BITS_PER_LONG]; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct aia_hgei_control, aia_hgei); 2662306a36Sopenharmony_cistatic int hgei_parent_irq; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ciunsigned int kvm_riscv_aia_nr_hgei; 2962306a36Sopenharmony_ciunsigned int kvm_riscv_aia_max_ids; 3062306a36Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(kvm_riscv_aia_available); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic int aia_find_hgei(struct kvm_vcpu *owner) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci int i, hgei; 3562306a36Sopenharmony_ci unsigned long flags; 3662306a36Sopenharmony_ci struct aia_hgei_control *hgctrl = get_cpu_ptr(&aia_hgei); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci raw_spin_lock_irqsave(&hgctrl->lock, flags); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci hgei = -1; 4162306a36Sopenharmony_ci for (i = 1; i <= kvm_riscv_aia_nr_hgei; i++) { 4262306a36Sopenharmony_ci if (hgctrl->owners[i] == owner) { 4362306a36Sopenharmony_ci hgei = i; 4462306a36Sopenharmony_ci break; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&hgctrl->lock, flags); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci put_cpu_ptr(&aia_hgei); 5162306a36Sopenharmony_ci return hgei; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void aia_set_hvictl(bool ext_irq_pending) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci unsigned long hvictl; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* 5962306a36Sopenharmony_ci * HVICTL.IID == 9 and HVICTL.IPRIO == 0 represents 6062306a36Sopenharmony_ci * no interrupt in HVICTL. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci hvictl = (IRQ_S_EXT << HVICTL_IID_SHIFT) & HVICTL_IID; 6462306a36Sopenharmony_ci hvictl |= ext_irq_pending; 6562306a36Sopenharmony_ci csr_write(CSR_HVICTL, hvictl); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#ifdef CONFIG_32BIT 6962306a36Sopenharmony_civoid kvm_riscv_vcpu_aia_flush_interrupts(struct kvm_vcpu *vcpu) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; 7262306a36Sopenharmony_ci unsigned long mask, val; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (!kvm_riscv_aia_available()) 7562306a36Sopenharmony_ci return; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (READ_ONCE(vcpu->arch.irqs_pending_mask[1])) { 7862306a36Sopenharmony_ci mask = xchg_acquire(&vcpu->arch.irqs_pending_mask[1], 0); 7962306a36Sopenharmony_ci val = READ_ONCE(vcpu->arch.irqs_pending[1]) & mask; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci csr->hviph &= ~mask; 8262306a36Sopenharmony_ci csr->hviph |= val; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_civoid kvm_riscv_vcpu_aia_sync_interrupts(struct kvm_vcpu *vcpu) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (kvm_riscv_aia_available()) 9162306a36Sopenharmony_ci csr->vsieh = csr_read(CSR_VSIEH); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci#endif 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cibool kvm_riscv_vcpu_aia_has_interrupts(struct kvm_vcpu *vcpu, u64 mask) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci int hgei; 9862306a36Sopenharmony_ci unsigned long seip; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (!kvm_riscv_aia_available()) 10162306a36Sopenharmony_ci return false; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#ifdef CONFIG_32BIT 10462306a36Sopenharmony_ci if (READ_ONCE(vcpu->arch.irqs_pending[1]) & 10562306a36Sopenharmony_ci (vcpu->arch.aia_context.guest_csr.vsieh & upper_32_bits(mask))) 10662306a36Sopenharmony_ci return true; 10762306a36Sopenharmony_ci#endif 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci seip = vcpu->arch.guest_csr.vsie; 11062306a36Sopenharmony_ci seip &= (unsigned long)mask; 11162306a36Sopenharmony_ci seip &= BIT(IRQ_S_EXT); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (!kvm_riscv_aia_initialized(vcpu->kvm) || !seip) 11462306a36Sopenharmony_ci return false; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci hgei = aia_find_hgei(vcpu); 11762306a36Sopenharmony_ci if (hgei > 0) 11862306a36Sopenharmony_ci return !!(csr_read(CSR_HGEIP) & BIT(hgei)); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return false; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_civoid kvm_riscv_vcpu_aia_update_hvip(struct kvm_vcpu *vcpu) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (!kvm_riscv_aia_available()) 12862306a36Sopenharmony_ci return; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci#ifdef CONFIG_32BIT 13162306a36Sopenharmony_ci csr_write(CSR_HVIPH, vcpu->arch.aia_context.guest_csr.hviph); 13262306a36Sopenharmony_ci#endif 13362306a36Sopenharmony_ci aia_set_hvictl(!!(csr->hvip & BIT(IRQ_VS_EXT))); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_civoid kvm_riscv_vcpu_aia_load(struct kvm_vcpu *vcpu, int cpu) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (!kvm_riscv_aia_available()) 14162306a36Sopenharmony_ci return; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci csr_write(CSR_VSISELECT, csr->vsiselect); 14462306a36Sopenharmony_ci csr_write(CSR_HVIPRIO1, csr->hviprio1); 14562306a36Sopenharmony_ci csr_write(CSR_HVIPRIO2, csr->hviprio2); 14662306a36Sopenharmony_ci#ifdef CONFIG_32BIT 14762306a36Sopenharmony_ci csr_write(CSR_VSIEH, csr->vsieh); 14862306a36Sopenharmony_ci csr_write(CSR_HVIPH, csr->hviph); 14962306a36Sopenharmony_ci csr_write(CSR_HVIPRIO1H, csr->hviprio1h); 15062306a36Sopenharmony_ci csr_write(CSR_HVIPRIO2H, csr->hviprio2h); 15162306a36Sopenharmony_ci#endif 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_civoid kvm_riscv_vcpu_aia_put(struct kvm_vcpu *vcpu) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (!kvm_riscv_aia_available()) 15962306a36Sopenharmony_ci return; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci csr->vsiselect = csr_read(CSR_VSISELECT); 16262306a36Sopenharmony_ci csr->hviprio1 = csr_read(CSR_HVIPRIO1); 16362306a36Sopenharmony_ci csr->hviprio2 = csr_read(CSR_HVIPRIO2); 16462306a36Sopenharmony_ci#ifdef CONFIG_32BIT 16562306a36Sopenharmony_ci csr->vsieh = csr_read(CSR_VSIEH); 16662306a36Sopenharmony_ci csr->hviph = csr_read(CSR_HVIPH); 16762306a36Sopenharmony_ci csr->hviprio1h = csr_read(CSR_HVIPRIO1H); 16862306a36Sopenharmony_ci csr->hviprio2h = csr_read(CSR_HVIPRIO2H); 16962306a36Sopenharmony_ci#endif 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ciint kvm_riscv_vcpu_aia_get_csr(struct kvm_vcpu *vcpu, 17362306a36Sopenharmony_ci unsigned long reg_num, 17462306a36Sopenharmony_ci unsigned long *out_val) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (reg_num >= sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long)) 17962306a36Sopenharmony_ci return -ENOENT; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci *out_val = 0; 18262306a36Sopenharmony_ci if (kvm_riscv_aia_available()) 18362306a36Sopenharmony_ci *out_val = ((unsigned long *)csr)[reg_num]; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ciint kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu, 18962306a36Sopenharmony_ci unsigned long reg_num, 19062306a36Sopenharmony_ci unsigned long val) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (reg_num >= sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long)) 19562306a36Sopenharmony_ci return -ENOENT; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (kvm_riscv_aia_available()) { 19862306a36Sopenharmony_ci ((unsigned long *)csr)[reg_num] = val; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci#ifdef CONFIG_32BIT 20162306a36Sopenharmony_ci if (reg_num == KVM_REG_RISCV_CSR_AIA_REG(siph)) 20262306a36Sopenharmony_ci WRITE_ONCE(vcpu->arch.irqs_pending_mask[1], 0); 20362306a36Sopenharmony_ci#endif 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ciint kvm_riscv_vcpu_aia_rmw_topei(struct kvm_vcpu *vcpu, 21062306a36Sopenharmony_ci unsigned int csr_num, 21162306a36Sopenharmony_ci unsigned long *val, 21262306a36Sopenharmony_ci unsigned long new_val, 21362306a36Sopenharmony_ci unsigned long wr_mask) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci /* If AIA not available then redirect trap */ 21662306a36Sopenharmony_ci if (!kvm_riscv_aia_available()) 21762306a36Sopenharmony_ci return KVM_INSN_ILLEGAL_TRAP; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* If AIA not initialized then forward to user space */ 22062306a36Sopenharmony_ci if (!kvm_riscv_aia_initialized(vcpu->kvm)) 22162306a36Sopenharmony_ci return KVM_INSN_EXIT_TO_USER_SPACE; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci return kvm_riscv_vcpu_aia_imsic_rmw(vcpu, KVM_RISCV_AIA_IMSIC_TOPEI, 22462306a36Sopenharmony_ci val, new_val, wr_mask); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci/* 22862306a36Sopenharmony_ci * External IRQ priority always read-only zero. This means default 22962306a36Sopenharmony_ci * priority order is always preferred for external IRQs unless 23062306a36Sopenharmony_ci * HVICTL.IID == 9 and HVICTL.IPRIO != 0 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_cistatic int aia_irq2bitpos[] = { 23362306a36Sopenharmony_ci0, 8, -1, -1, 16, 24, -1, -1, /* 0 - 7 */ 23462306a36Sopenharmony_ci32, -1, -1, -1, -1, 40, 48, 56, /* 8 - 15 */ 23562306a36Sopenharmony_ci64, 72, 80, 88, 96, 104, 112, 120, /* 16 - 23 */ 23662306a36Sopenharmony_ci-1, -1, -1, -1, -1, -1, -1, -1, /* 24 - 31 */ 23762306a36Sopenharmony_ci-1, -1, -1, -1, -1, -1, -1, -1, /* 32 - 39 */ 23862306a36Sopenharmony_ci-1, -1, -1, -1, -1, -1, -1, -1, /* 40 - 47 */ 23962306a36Sopenharmony_ci-1, -1, -1, -1, -1, -1, -1, -1, /* 48 - 55 */ 24062306a36Sopenharmony_ci-1, -1, -1, -1, -1, -1, -1, -1, /* 56 - 63 */ 24162306a36Sopenharmony_ci}; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic u8 aia_get_iprio8(struct kvm_vcpu *vcpu, unsigned int irq) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci unsigned long hviprio; 24662306a36Sopenharmony_ci int bitpos = aia_irq2bitpos[irq]; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (bitpos < 0) 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci switch (bitpos / BITS_PER_LONG) { 25262306a36Sopenharmony_ci case 0: 25362306a36Sopenharmony_ci hviprio = csr_read(CSR_HVIPRIO1); 25462306a36Sopenharmony_ci break; 25562306a36Sopenharmony_ci case 1: 25662306a36Sopenharmony_ci#ifndef CONFIG_32BIT 25762306a36Sopenharmony_ci hviprio = csr_read(CSR_HVIPRIO2); 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci#else 26062306a36Sopenharmony_ci hviprio = csr_read(CSR_HVIPRIO1H); 26162306a36Sopenharmony_ci break; 26262306a36Sopenharmony_ci case 2: 26362306a36Sopenharmony_ci hviprio = csr_read(CSR_HVIPRIO2); 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci case 3: 26662306a36Sopenharmony_ci hviprio = csr_read(CSR_HVIPRIO2H); 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci#endif 26962306a36Sopenharmony_ci default: 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return (hviprio >> (bitpos % BITS_PER_LONG)) & TOPI_IPRIO_MASK; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic void aia_set_iprio8(struct kvm_vcpu *vcpu, unsigned int irq, u8 prio) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci unsigned long hviprio; 27962306a36Sopenharmony_ci int bitpos = aia_irq2bitpos[irq]; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (bitpos < 0) 28262306a36Sopenharmony_ci return; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci switch (bitpos / BITS_PER_LONG) { 28562306a36Sopenharmony_ci case 0: 28662306a36Sopenharmony_ci hviprio = csr_read(CSR_HVIPRIO1); 28762306a36Sopenharmony_ci break; 28862306a36Sopenharmony_ci case 1: 28962306a36Sopenharmony_ci#ifndef CONFIG_32BIT 29062306a36Sopenharmony_ci hviprio = csr_read(CSR_HVIPRIO2); 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci#else 29362306a36Sopenharmony_ci hviprio = csr_read(CSR_HVIPRIO1H); 29462306a36Sopenharmony_ci break; 29562306a36Sopenharmony_ci case 2: 29662306a36Sopenharmony_ci hviprio = csr_read(CSR_HVIPRIO2); 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci case 3: 29962306a36Sopenharmony_ci hviprio = csr_read(CSR_HVIPRIO2H); 30062306a36Sopenharmony_ci break; 30162306a36Sopenharmony_ci#endif 30262306a36Sopenharmony_ci default: 30362306a36Sopenharmony_ci return; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci hviprio &= ~(TOPI_IPRIO_MASK << (bitpos % BITS_PER_LONG)); 30762306a36Sopenharmony_ci hviprio |= (unsigned long)prio << (bitpos % BITS_PER_LONG); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci switch (bitpos / BITS_PER_LONG) { 31062306a36Sopenharmony_ci case 0: 31162306a36Sopenharmony_ci csr_write(CSR_HVIPRIO1, hviprio); 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci case 1: 31462306a36Sopenharmony_ci#ifndef CONFIG_32BIT 31562306a36Sopenharmony_ci csr_write(CSR_HVIPRIO2, hviprio); 31662306a36Sopenharmony_ci break; 31762306a36Sopenharmony_ci#else 31862306a36Sopenharmony_ci csr_write(CSR_HVIPRIO1H, hviprio); 31962306a36Sopenharmony_ci break; 32062306a36Sopenharmony_ci case 2: 32162306a36Sopenharmony_ci csr_write(CSR_HVIPRIO2, hviprio); 32262306a36Sopenharmony_ci break; 32362306a36Sopenharmony_ci case 3: 32462306a36Sopenharmony_ci csr_write(CSR_HVIPRIO2H, hviprio); 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci#endif 32762306a36Sopenharmony_ci default: 32862306a36Sopenharmony_ci return; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int aia_rmw_iprio(struct kvm_vcpu *vcpu, unsigned int isel, 33362306a36Sopenharmony_ci unsigned long *val, unsigned long new_val, 33462306a36Sopenharmony_ci unsigned long wr_mask) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci int i, first_irq, nirqs; 33762306a36Sopenharmony_ci unsigned long old_val; 33862306a36Sopenharmony_ci u8 prio; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci#ifndef CONFIG_32BIT 34162306a36Sopenharmony_ci if (isel & 0x1) 34262306a36Sopenharmony_ci return KVM_INSN_ILLEGAL_TRAP; 34362306a36Sopenharmony_ci#endif 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci nirqs = 4 * (BITS_PER_LONG / 32); 34662306a36Sopenharmony_ci first_irq = (isel - ISELECT_IPRIO0) * 4; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci old_val = 0; 34962306a36Sopenharmony_ci for (i = 0; i < nirqs; i++) { 35062306a36Sopenharmony_ci prio = aia_get_iprio8(vcpu, first_irq + i); 35162306a36Sopenharmony_ci old_val |= (unsigned long)prio << (TOPI_IPRIO_BITS * i); 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (val) 35562306a36Sopenharmony_ci *val = old_val; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (wr_mask) { 35862306a36Sopenharmony_ci new_val = (old_val & ~wr_mask) | (new_val & wr_mask); 35962306a36Sopenharmony_ci for (i = 0; i < nirqs; i++) { 36062306a36Sopenharmony_ci prio = (new_val >> (TOPI_IPRIO_BITS * i)) & 36162306a36Sopenharmony_ci TOPI_IPRIO_MASK; 36262306a36Sopenharmony_ci aia_set_iprio8(vcpu, first_irq + i, prio); 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return KVM_INSN_CONTINUE_NEXT_SEPC; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ciint kvm_riscv_vcpu_aia_rmw_ireg(struct kvm_vcpu *vcpu, unsigned int csr_num, 37062306a36Sopenharmony_ci unsigned long *val, unsigned long new_val, 37162306a36Sopenharmony_ci unsigned long wr_mask) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci unsigned int isel; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* If AIA not available then redirect trap */ 37662306a36Sopenharmony_ci if (!kvm_riscv_aia_available()) 37762306a36Sopenharmony_ci return KVM_INSN_ILLEGAL_TRAP; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* First try to emulate in kernel space */ 38062306a36Sopenharmony_ci isel = csr_read(CSR_VSISELECT) & ISELECT_MASK; 38162306a36Sopenharmony_ci if (isel >= ISELECT_IPRIO0 && isel <= ISELECT_IPRIO15) 38262306a36Sopenharmony_ci return aia_rmw_iprio(vcpu, isel, val, new_val, wr_mask); 38362306a36Sopenharmony_ci else if (isel >= IMSIC_FIRST && isel <= IMSIC_LAST && 38462306a36Sopenharmony_ci kvm_riscv_aia_initialized(vcpu->kvm)) 38562306a36Sopenharmony_ci return kvm_riscv_vcpu_aia_imsic_rmw(vcpu, isel, val, new_val, 38662306a36Sopenharmony_ci wr_mask); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* We can't handle it here so redirect to user space */ 38962306a36Sopenharmony_ci return KVM_INSN_EXIT_TO_USER_SPACE; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ciint kvm_riscv_aia_alloc_hgei(int cpu, struct kvm_vcpu *owner, 39362306a36Sopenharmony_ci void __iomem **hgei_va, phys_addr_t *hgei_pa) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci int ret = -ENOENT; 39662306a36Sopenharmony_ci unsigned long flags; 39762306a36Sopenharmony_ci struct aia_hgei_control *hgctrl = per_cpu_ptr(&aia_hgei, cpu); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (!kvm_riscv_aia_available() || !hgctrl) 40062306a36Sopenharmony_ci return -ENODEV; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci raw_spin_lock_irqsave(&hgctrl->lock, flags); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (hgctrl->free_bitmap) { 40562306a36Sopenharmony_ci ret = __ffs(hgctrl->free_bitmap); 40662306a36Sopenharmony_ci hgctrl->free_bitmap &= ~BIT(ret); 40762306a36Sopenharmony_ci hgctrl->owners[ret] = owner; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&hgctrl->lock, flags); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* TODO: To be updated later by AIA IMSIC HW guest file support */ 41362306a36Sopenharmony_ci if (hgei_va) 41462306a36Sopenharmony_ci *hgei_va = NULL; 41562306a36Sopenharmony_ci if (hgei_pa) 41662306a36Sopenharmony_ci *hgei_pa = 0; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci return ret; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_civoid kvm_riscv_aia_free_hgei(int cpu, int hgei) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci unsigned long flags; 42462306a36Sopenharmony_ci struct aia_hgei_control *hgctrl = per_cpu_ptr(&aia_hgei, cpu); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (!kvm_riscv_aia_available() || !hgctrl) 42762306a36Sopenharmony_ci return; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci raw_spin_lock_irqsave(&hgctrl->lock, flags); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (hgei > 0 && hgei <= kvm_riscv_aia_nr_hgei) { 43262306a36Sopenharmony_ci if (!(hgctrl->free_bitmap & BIT(hgei))) { 43362306a36Sopenharmony_ci hgctrl->free_bitmap |= BIT(hgei); 43462306a36Sopenharmony_ci hgctrl->owners[hgei] = NULL; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&hgctrl->lock, flags); 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_civoid kvm_riscv_aia_wakeon_hgei(struct kvm_vcpu *owner, bool enable) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci int hgei; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (!kvm_riscv_aia_available()) 44662306a36Sopenharmony_ci return; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci hgei = aia_find_hgei(owner); 44962306a36Sopenharmony_ci if (hgei > 0) { 45062306a36Sopenharmony_ci if (enable) 45162306a36Sopenharmony_ci csr_set(CSR_HGEIE, BIT(hgei)); 45262306a36Sopenharmony_ci else 45362306a36Sopenharmony_ci csr_clear(CSR_HGEIE, BIT(hgei)); 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic irqreturn_t hgei_interrupt(int irq, void *dev_id) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci int i; 46062306a36Sopenharmony_ci unsigned long hgei_mask, flags; 46162306a36Sopenharmony_ci struct aia_hgei_control *hgctrl = get_cpu_ptr(&aia_hgei); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci hgei_mask = csr_read(CSR_HGEIP) & csr_read(CSR_HGEIE); 46462306a36Sopenharmony_ci csr_clear(CSR_HGEIE, hgei_mask); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci raw_spin_lock_irqsave(&hgctrl->lock, flags); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci for_each_set_bit(i, &hgei_mask, BITS_PER_LONG) { 46962306a36Sopenharmony_ci if (hgctrl->owners[i]) 47062306a36Sopenharmony_ci kvm_vcpu_kick(hgctrl->owners[i]); 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&hgctrl->lock, flags); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci put_cpu_ptr(&aia_hgei); 47662306a36Sopenharmony_ci return IRQ_HANDLED; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic int aia_hgei_init(void) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci int cpu, rc; 48262306a36Sopenharmony_ci struct irq_domain *domain; 48362306a36Sopenharmony_ci struct aia_hgei_control *hgctrl; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* Initialize per-CPU guest external interrupt line management */ 48662306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 48762306a36Sopenharmony_ci hgctrl = per_cpu_ptr(&aia_hgei, cpu); 48862306a36Sopenharmony_ci raw_spin_lock_init(&hgctrl->lock); 48962306a36Sopenharmony_ci if (kvm_riscv_aia_nr_hgei) { 49062306a36Sopenharmony_ci hgctrl->free_bitmap = 49162306a36Sopenharmony_ci BIT(kvm_riscv_aia_nr_hgei + 1) - 1; 49262306a36Sopenharmony_ci hgctrl->free_bitmap &= ~BIT(0); 49362306a36Sopenharmony_ci } else 49462306a36Sopenharmony_ci hgctrl->free_bitmap = 0; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* Find INTC irq domain */ 49862306a36Sopenharmony_ci domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), 49962306a36Sopenharmony_ci DOMAIN_BUS_ANY); 50062306a36Sopenharmony_ci if (!domain) { 50162306a36Sopenharmony_ci kvm_err("unable to find INTC domain\n"); 50262306a36Sopenharmony_ci return -ENOENT; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* Map per-CPU SGEI interrupt from INTC domain */ 50662306a36Sopenharmony_ci hgei_parent_irq = irq_create_mapping(domain, IRQ_S_GEXT); 50762306a36Sopenharmony_ci if (!hgei_parent_irq) { 50862306a36Sopenharmony_ci kvm_err("unable to map SGEI IRQ\n"); 50962306a36Sopenharmony_ci return -ENOMEM; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* Request per-CPU SGEI interrupt */ 51362306a36Sopenharmony_ci rc = request_percpu_irq(hgei_parent_irq, hgei_interrupt, 51462306a36Sopenharmony_ci "riscv-kvm", &aia_hgei); 51562306a36Sopenharmony_ci if (rc) { 51662306a36Sopenharmony_ci kvm_err("failed to request SGEI IRQ\n"); 51762306a36Sopenharmony_ci return rc; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci return 0; 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic void aia_hgei_exit(void) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci /* Free per-CPU SGEI interrupt */ 52662306a36Sopenharmony_ci free_percpu_irq(hgei_parent_irq, &aia_hgei); 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_civoid kvm_riscv_aia_enable(void) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci if (!kvm_riscv_aia_available()) 53262306a36Sopenharmony_ci return; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci aia_set_hvictl(false); 53562306a36Sopenharmony_ci csr_write(CSR_HVIPRIO1, 0x0); 53662306a36Sopenharmony_ci csr_write(CSR_HVIPRIO2, 0x0); 53762306a36Sopenharmony_ci#ifdef CONFIG_32BIT 53862306a36Sopenharmony_ci csr_write(CSR_HVIPH, 0x0); 53962306a36Sopenharmony_ci csr_write(CSR_HIDELEGH, 0x0); 54062306a36Sopenharmony_ci csr_write(CSR_HVIPRIO1H, 0x0); 54162306a36Sopenharmony_ci csr_write(CSR_HVIPRIO2H, 0x0); 54262306a36Sopenharmony_ci#endif 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* Enable per-CPU SGEI interrupt */ 54562306a36Sopenharmony_ci enable_percpu_irq(hgei_parent_irq, 54662306a36Sopenharmony_ci irq_get_trigger_type(hgei_parent_irq)); 54762306a36Sopenharmony_ci csr_set(CSR_HIE, BIT(IRQ_S_GEXT)); 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_civoid kvm_riscv_aia_disable(void) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci int i; 55362306a36Sopenharmony_ci unsigned long flags; 55462306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 55562306a36Sopenharmony_ci struct aia_hgei_control *hgctrl; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci if (!kvm_riscv_aia_available()) 55862306a36Sopenharmony_ci return; 55962306a36Sopenharmony_ci hgctrl = get_cpu_ptr(&aia_hgei); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* Disable per-CPU SGEI interrupt */ 56262306a36Sopenharmony_ci csr_clear(CSR_HIE, BIT(IRQ_S_GEXT)); 56362306a36Sopenharmony_ci disable_percpu_irq(hgei_parent_irq); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci aia_set_hvictl(false); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci raw_spin_lock_irqsave(&hgctrl->lock, flags); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci for (i = 0; i <= kvm_riscv_aia_nr_hgei; i++) { 57062306a36Sopenharmony_ci vcpu = hgctrl->owners[i]; 57162306a36Sopenharmony_ci if (!vcpu) 57262306a36Sopenharmony_ci continue; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* 57562306a36Sopenharmony_ci * We release hgctrl->lock before notifying IMSIC 57662306a36Sopenharmony_ci * so that we don't have lock ordering issues. 57762306a36Sopenharmony_ci */ 57862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&hgctrl->lock, flags); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* Notify IMSIC */ 58162306a36Sopenharmony_ci kvm_riscv_vcpu_aia_imsic_release(vcpu); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* 58462306a36Sopenharmony_ci * Wakeup VCPU if it was blocked so that it can 58562306a36Sopenharmony_ci * run on other HARTs 58662306a36Sopenharmony_ci */ 58762306a36Sopenharmony_ci if (csr_read(CSR_HGEIE) & BIT(i)) { 58862306a36Sopenharmony_ci csr_clear(CSR_HGEIE, BIT(i)); 58962306a36Sopenharmony_ci kvm_vcpu_kick(vcpu); 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci raw_spin_lock_irqsave(&hgctrl->lock, flags); 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&hgctrl->lock, flags); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci put_cpu_ptr(&aia_hgei); 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ciint kvm_riscv_aia_init(void) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci int rc; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (!riscv_isa_extension_available(NULL, SxAIA)) 60562306a36Sopenharmony_ci return -ENODEV; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* Figure-out number of bits in HGEIE */ 60862306a36Sopenharmony_ci csr_write(CSR_HGEIE, -1UL); 60962306a36Sopenharmony_ci kvm_riscv_aia_nr_hgei = fls_long(csr_read(CSR_HGEIE)); 61062306a36Sopenharmony_ci csr_write(CSR_HGEIE, 0); 61162306a36Sopenharmony_ci if (kvm_riscv_aia_nr_hgei) 61262306a36Sopenharmony_ci kvm_riscv_aia_nr_hgei--; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* 61562306a36Sopenharmony_ci * Number of usable HGEI lines should be minimum of per-HART 61662306a36Sopenharmony_ci * IMSIC guest files and number of bits in HGEIE 61762306a36Sopenharmony_ci * 61862306a36Sopenharmony_ci * TODO: To be updated later by AIA IMSIC HW guest file support 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_ci kvm_riscv_aia_nr_hgei = 0; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci /* 62362306a36Sopenharmony_ci * Find number of guest MSI IDs 62462306a36Sopenharmony_ci * 62562306a36Sopenharmony_ci * TODO: To be updated later by AIA IMSIC HW guest file support 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_ci kvm_riscv_aia_max_ids = IMSIC_MAX_ID; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci /* Initialize guest external interrupt line management */ 63062306a36Sopenharmony_ci rc = aia_hgei_init(); 63162306a36Sopenharmony_ci if (rc) 63262306a36Sopenharmony_ci return rc; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci /* Register device operations */ 63562306a36Sopenharmony_ci rc = kvm_register_device_ops(&kvm_riscv_aia_device_ops, 63662306a36Sopenharmony_ci KVM_DEV_TYPE_RISCV_AIA); 63762306a36Sopenharmony_ci if (rc) { 63862306a36Sopenharmony_ci aia_hgei_exit(); 63962306a36Sopenharmony_ci return rc; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci /* Enable KVM AIA support */ 64362306a36Sopenharmony_ci static_branch_enable(&kvm_riscv_aia_available); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci return 0; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_civoid kvm_riscv_aia_exit(void) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci if (!kvm_riscv_aia_available()) 65162306a36Sopenharmony_ci return; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* Unregister device operations */ 65462306a36Sopenharmony_ci kvm_unregister_device_ops(KVM_DEV_TYPE_RISCV_AIA); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* Cleanup the HGEI state */ 65762306a36Sopenharmony_ci aia_hgei_exit(); 65862306a36Sopenharmony_ci} 659