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/kvm_host.h> 1162306a36Sopenharmony_ci#include <linux/math.h> 1262306a36Sopenharmony_ci#include <linux/spinlock.h> 1362306a36Sopenharmony_ci#include <linux/swab.h> 1462306a36Sopenharmony_ci#include <kvm/iodev.h> 1562306a36Sopenharmony_ci#include <asm/kvm_aia_aplic.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct aplic_irq { 1862306a36Sopenharmony_ci raw_spinlock_t lock; 1962306a36Sopenharmony_ci u32 sourcecfg; 2062306a36Sopenharmony_ci u32 state; 2162306a36Sopenharmony_ci#define APLIC_IRQ_STATE_PENDING BIT(0) 2262306a36Sopenharmony_ci#define APLIC_IRQ_STATE_ENABLED BIT(1) 2362306a36Sopenharmony_ci#define APLIC_IRQ_STATE_ENPEND (APLIC_IRQ_STATE_PENDING | \ 2462306a36Sopenharmony_ci APLIC_IRQ_STATE_ENABLED) 2562306a36Sopenharmony_ci#define APLIC_IRQ_STATE_INPUT BIT(8) 2662306a36Sopenharmony_ci u32 target; 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistruct aplic { 3062306a36Sopenharmony_ci struct kvm_io_device iodev; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci u32 domaincfg; 3362306a36Sopenharmony_ci u32 genmsi; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci u32 nr_irqs; 3662306a36Sopenharmony_ci u32 nr_words; 3762306a36Sopenharmony_ci struct aplic_irq *irqs; 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic u32 aplic_read_sourcecfg(struct aplic *aplic, u32 irq) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci u32 ret; 4362306a36Sopenharmony_ci unsigned long flags; 4462306a36Sopenharmony_ci struct aplic_irq *irqd; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (!irq || aplic->nr_irqs <= irq) 4762306a36Sopenharmony_ci return 0; 4862306a36Sopenharmony_ci irqd = &aplic->irqs[irq]; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci raw_spin_lock_irqsave(&irqd->lock, flags); 5162306a36Sopenharmony_ci ret = irqd->sourcecfg; 5262306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irqd->lock, flags); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return ret; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic void aplic_write_sourcecfg(struct aplic *aplic, u32 irq, u32 val) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci unsigned long flags; 6062306a36Sopenharmony_ci struct aplic_irq *irqd; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (!irq || aplic->nr_irqs <= irq) 6362306a36Sopenharmony_ci return; 6462306a36Sopenharmony_ci irqd = &aplic->irqs[irq]; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (val & APLIC_SOURCECFG_D) 6762306a36Sopenharmony_ci val = 0; 6862306a36Sopenharmony_ci else 6962306a36Sopenharmony_ci val &= APLIC_SOURCECFG_SM_MASK; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci raw_spin_lock_irqsave(&irqd->lock, flags); 7262306a36Sopenharmony_ci irqd->sourcecfg = val; 7362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irqd->lock, flags); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic u32 aplic_read_target(struct aplic *aplic, u32 irq) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci u32 ret; 7962306a36Sopenharmony_ci unsigned long flags; 8062306a36Sopenharmony_ci struct aplic_irq *irqd; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (!irq || aplic->nr_irqs <= irq) 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci irqd = &aplic->irqs[irq]; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci raw_spin_lock_irqsave(&irqd->lock, flags); 8762306a36Sopenharmony_ci ret = irqd->target; 8862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irqd->lock, flags); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return ret; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void aplic_write_target(struct aplic *aplic, u32 irq, u32 val) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci unsigned long flags; 9662306a36Sopenharmony_ci struct aplic_irq *irqd; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (!irq || aplic->nr_irqs <= irq) 9962306a36Sopenharmony_ci return; 10062306a36Sopenharmony_ci irqd = &aplic->irqs[irq]; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci val &= APLIC_TARGET_EIID_MASK | 10362306a36Sopenharmony_ci (APLIC_TARGET_HART_IDX_MASK << APLIC_TARGET_HART_IDX_SHIFT) | 10462306a36Sopenharmony_ci (APLIC_TARGET_GUEST_IDX_MASK << APLIC_TARGET_GUEST_IDX_SHIFT); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci raw_spin_lock_irqsave(&irqd->lock, flags); 10762306a36Sopenharmony_ci irqd->target = val; 10862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irqd->lock, flags); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic bool aplic_read_pending(struct aplic *aplic, u32 irq) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci bool ret; 11462306a36Sopenharmony_ci unsigned long flags; 11562306a36Sopenharmony_ci struct aplic_irq *irqd; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (!irq || aplic->nr_irqs <= irq) 11862306a36Sopenharmony_ci return false; 11962306a36Sopenharmony_ci irqd = &aplic->irqs[irq]; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci raw_spin_lock_irqsave(&irqd->lock, flags); 12262306a36Sopenharmony_ci ret = (irqd->state & APLIC_IRQ_STATE_PENDING) ? true : false; 12362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irqd->lock, flags); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return ret; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void aplic_write_pending(struct aplic *aplic, u32 irq, bool pending) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci unsigned long flags, sm; 13162306a36Sopenharmony_ci struct aplic_irq *irqd; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (!irq || aplic->nr_irqs <= irq) 13462306a36Sopenharmony_ci return; 13562306a36Sopenharmony_ci irqd = &aplic->irqs[irq]; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci raw_spin_lock_irqsave(&irqd->lock, flags); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci sm = irqd->sourcecfg & APLIC_SOURCECFG_SM_MASK; 14062306a36Sopenharmony_ci if (!pending && 14162306a36Sopenharmony_ci ((sm == APLIC_SOURCECFG_SM_LEVEL_HIGH) || 14262306a36Sopenharmony_ci (sm == APLIC_SOURCECFG_SM_LEVEL_LOW))) 14362306a36Sopenharmony_ci goto skip_write_pending; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (pending) 14662306a36Sopenharmony_ci irqd->state |= APLIC_IRQ_STATE_PENDING; 14762306a36Sopenharmony_ci else 14862306a36Sopenharmony_ci irqd->state &= ~APLIC_IRQ_STATE_PENDING; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ciskip_write_pending: 15162306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irqd->lock, flags); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic bool aplic_read_enabled(struct aplic *aplic, u32 irq) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci bool ret; 15762306a36Sopenharmony_ci unsigned long flags; 15862306a36Sopenharmony_ci struct aplic_irq *irqd; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (!irq || aplic->nr_irqs <= irq) 16162306a36Sopenharmony_ci return false; 16262306a36Sopenharmony_ci irqd = &aplic->irqs[irq]; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci raw_spin_lock_irqsave(&irqd->lock, flags); 16562306a36Sopenharmony_ci ret = (irqd->state & APLIC_IRQ_STATE_ENABLED) ? true : false; 16662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irqd->lock, flags); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return ret; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void aplic_write_enabled(struct aplic *aplic, u32 irq, bool enabled) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci unsigned long flags; 17462306a36Sopenharmony_ci struct aplic_irq *irqd; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (!irq || aplic->nr_irqs <= irq) 17762306a36Sopenharmony_ci return; 17862306a36Sopenharmony_ci irqd = &aplic->irqs[irq]; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci raw_spin_lock_irqsave(&irqd->lock, flags); 18162306a36Sopenharmony_ci if (enabled) 18262306a36Sopenharmony_ci irqd->state |= APLIC_IRQ_STATE_ENABLED; 18362306a36Sopenharmony_ci else 18462306a36Sopenharmony_ci irqd->state &= ~APLIC_IRQ_STATE_ENABLED; 18562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irqd->lock, flags); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic bool aplic_read_input(struct aplic *aplic, u32 irq) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci bool ret; 19162306a36Sopenharmony_ci unsigned long flags; 19262306a36Sopenharmony_ci struct aplic_irq *irqd; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (!irq || aplic->nr_irqs <= irq) 19562306a36Sopenharmony_ci return false; 19662306a36Sopenharmony_ci irqd = &aplic->irqs[irq]; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci raw_spin_lock_irqsave(&irqd->lock, flags); 19962306a36Sopenharmony_ci ret = (irqd->state & APLIC_IRQ_STATE_INPUT) ? true : false; 20062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irqd->lock, flags); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return ret; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void aplic_inject_msi(struct kvm *kvm, u32 irq, u32 target) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci u32 hart_idx, guest_idx, eiid; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci hart_idx = target >> APLIC_TARGET_HART_IDX_SHIFT; 21062306a36Sopenharmony_ci hart_idx &= APLIC_TARGET_HART_IDX_MASK; 21162306a36Sopenharmony_ci guest_idx = target >> APLIC_TARGET_GUEST_IDX_SHIFT; 21262306a36Sopenharmony_ci guest_idx &= APLIC_TARGET_GUEST_IDX_MASK; 21362306a36Sopenharmony_ci eiid = target & APLIC_TARGET_EIID_MASK; 21462306a36Sopenharmony_ci kvm_riscv_aia_inject_msi_by_id(kvm, hart_idx, guest_idx, eiid); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic void aplic_update_irq_range(struct kvm *kvm, u32 first, u32 last) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci bool inject; 22062306a36Sopenharmony_ci u32 irq, target; 22162306a36Sopenharmony_ci unsigned long flags; 22262306a36Sopenharmony_ci struct aplic_irq *irqd; 22362306a36Sopenharmony_ci struct aplic *aplic = kvm->arch.aia.aplic_state; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (!(aplic->domaincfg & APLIC_DOMAINCFG_IE)) 22662306a36Sopenharmony_ci return; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci for (irq = first; irq <= last; irq++) { 22962306a36Sopenharmony_ci if (!irq || aplic->nr_irqs <= irq) 23062306a36Sopenharmony_ci continue; 23162306a36Sopenharmony_ci irqd = &aplic->irqs[irq]; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci raw_spin_lock_irqsave(&irqd->lock, flags); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci inject = false; 23662306a36Sopenharmony_ci target = irqd->target; 23762306a36Sopenharmony_ci if ((irqd->state & APLIC_IRQ_STATE_ENPEND) == 23862306a36Sopenharmony_ci APLIC_IRQ_STATE_ENPEND) { 23962306a36Sopenharmony_ci irqd->state &= ~APLIC_IRQ_STATE_PENDING; 24062306a36Sopenharmony_ci inject = true; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irqd->lock, flags); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (inject) 24662306a36Sopenharmony_ci aplic_inject_msi(kvm, irq, target); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ciint kvm_riscv_aia_aplic_inject(struct kvm *kvm, u32 source, bool level) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci u32 target; 25362306a36Sopenharmony_ci bool inject = false, ie; 25462306a36Sopenharmony_ci unsigned long flags; 25562306a36Sopenharmony_ci struct aplic_irq *irqd; 25662306a36Sopenharmony_ci struct aplic *aplic = kvm->arch.aia.aplic_state; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (!aplic || !source || (aplic->nr_irqs <= source)) 25962306a36Sopenharmony_ci return -ENODEV; 26062306a36Sopenharmony_ci irqd = &aplic->irqs[source]; 26162306a36Sopenharmony_ci ie = (aplic->domaincfg & APLIC_DOMAINCFG_IE) ? true : false; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci raw_spin_lock_irqsave(&irqd->lock, flags); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (irqd->sourcecfg & APLIC_SOURCECFG_D) 26662306a36Sopenharmony_ci goto skip_unlock; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci switch (irqd->sourcecfg & APLIC_SOURCECFG_SM_MASK) { 26962306a36Sopenharmony_ci case APLIC_SOURCECFG_SM_EDGE_RISE: 27062306a36Sopenharmony_ci if (level && !(irqd->state & APLIC_IRQ_STATE_INPUT) && 27162306a36Sopenharmony_ci !(irqd->state & APLIC_IRQ_STATE_PENDING)) 27262306a36Sopenharmony_ci irqd->state |= APLIC_IRQ_STATE_PENDING; 27362306a36Sopenharmony_ci break; 27462306a36Sopenharmony_ci case APLIC_SOURCECFG_SM_EDGE_FALL: 27562306a36Sopenharmony_ci if (!level && (irqd->state & APLIC_IRQ_STATE_INPUT) && 27662306a36Sopenharmony_ci !(irqd->state & APLIC_IRQ_STATE_PENDING)) 27762306a36Sopenharmony_ci irqd->state |= APLIC_IRQ_STATE_PENDING; 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci case APLIC_SOURCECFG_SM_LEVEL_HIGH: 28062306a36Sopenharmony_ci if (level && !(irqd->state & APLIC_IRQ_STATE_PENDING)) 28162306a36Sopenharmony_ci irqd->state |= APLIC_IRQ_STATE_PENDING; 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci case APLIC_SOURCECFG_SM_LEVEL_LOW: 28462306a36Sopenharmony_ci if (!level && !(irqd->state & APLIC_IRQ_STATE_PENDING)) 28562306a36Sopenharmony_ci irqd->state |= APLIC_IRQ_STATE_PENDING; 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (level) 29062306a36Sopenharmony_ci irqd->state |= APLIC_IRQ_STATE_INPUT; 29162306a36Sopenharmony_ci else 29262306a36Sopenharmony_ci irqd->state &= ~APLIC_IRQ_STATE_INPUT; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci target = irqd->target; 29562306a36Sopenharmony_ci if (ie && ((irqd->state & APLIC_IRQ_STATE_ENPEND) == 29662306a36Sopenharmony_ci APLIC_IRQ_STATE_ENPEND)) { 29762306a36Sopenharmony_ci irqd->state &= ~APLIC_IRQ_STATE_PENDING; 29862306a36Sopenharmony_ci inject = true; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ciskip_unlock: 30262306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irqd->lock, flags); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (inject) 30562306a36Sopenharmony_ci aplic_inject_msi(kvm, source, target); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic u32 aplic_read_input_word(struct aplic *aplic, u32 word) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci u32 i, ret = 0; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci for (i = 0; i < 32; i++) 31562306a36Sopenharmony_ci ret |= aplic_read_input(aplic, word * 32 + i) ? BIT(i) : 0; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return ret; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic u32 aplic_read_pending_word(struct aplic *aplic, u32 word) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci u32 i, ret = 0; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci for (i = 0; i < 32; i++) 32562306a36Sopenharmony_ci ret |= aplic_read_pending(aplic, word * 32 + i) ? BIT(i) : 0; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci return ret; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic void aplic_write_pending_word(struct aplic *aplic, u32 word, 33162306a36Sopenharmony_ci u32 val, bool pending) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci u32 i; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci for (i = 0; i < 32; i++) { 33662306a36Sopenharmony_ci if (val & BIT(i)) 33762306a36Sopenharmony_ci aplic_write_pending(aplic, word * 32 + i, pending); 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic u32 aplic_read_enabled_word(struct aplic *aplic, u32 word) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci u32 i, ret = 0; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci for (i = 0; i < 32; i++) 34662306a36Sopenharmony_ci ret |= aplic_read_enabled(aplic, word * 32 + i) ? BIT(i) : 0; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return ret; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic void aplic_write_enabled_word(struct aplic *aplic, u32 word, 35262306a36Sopenharmony_ci u32 val, bool enabled) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci u32 i; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci for (i = 0; i < 32; i++) { 35762306a36Sopenharmony_ci if (val & BIT(i)) 35862306a36Sopenharmony_ci aplic_write_enabled(aplic, word * 32 + i, enabled); 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic int aplic_mmio_read_offset(struct kvm *kvm, gpa_t off, u32 *val32) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci u32 i; 36562306a36Sopenharmony_ci struct aplic *aplic = kvm->arch.aia.aplic_state; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if ((off & 0x3) != 0) 36862306a36Sopenharmony_ci return -EOPNOTSUPP; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (off == APLIC_DOMAINCFG) { 37162306a36Sopenharmony_ci *val32 = APLIC_DOMAINCFG_RDONLY | 37262306a36Sopenharmony_ci aplic->domaincfg | APLIC_DOMAINCFG_DM; 37362306a36Sopenharmony_ci } else if ((off >= APLIC_SOURCECFG_BASE) && 37462306a36Sopenharmony_ci (off < (APLIC_SOURCECFG_BASE + (aplic->nr_irqs - 1) * 4))) { 37562306a36Sopenharmony_ci i = ((off - APLIC_SOURCECFG_BASE) >> 2) + 1; 37662306a36Sopenharmony_ci *val32 = aplic_read_sourcecfg(aplic, i); 37762306a36Sopenharmony_ci } else if ((off >= APLIC_SETIP_BASE) && 37862306a36Sopenharmony_ci (off < (APLIC_SETIP_BASE + aplic->nr_words * 4))) { 37962306a36Sopenharmony_ci i = (off - APLIC_SETIP_BASE) >> 2; 38062306a36Sopenharmony_ci *val32 = aplic_read_pending_word(aplic, i); 38162306a36Sopenharmony_ci } else if (off == APLIC_SETIPNUM) { 38262306a36Sopenharmony_ci *val32 = 0; 38362306a36Sopenharmony_ci } else if ((off >= APLIC_CLRIP_BASE) && 38462306a36Sopenharmony_ci (off < (APLIC_CLRIP_BASE + aplic->nr_words * 4))) { 38562306a36Sopenharmony_ci i = (off - APLIC_CLRIP_BASE) >> 2; 38662306a36Sopenharmony_ci *val32 = aplic_read_input_word(aplic, i); 38762306a36Sopenharmony_ci } else if (off == APLIC_CLRIPNUM) { 38862306a36Sopenharmony_ci *val32 = 0; 38962306a36Sopenharmony_ci } else if ((off >= APLIC_SETIE_BASE) && 39062306a36Sopenharmony_ci (off < (APLIC_SETIE_BASE + aplic->nr_words * 4))) { 39162306a36Sopenharmony_ci i = (off - APLIC_SETIE_BASE) >> 2; 39262306a36Sopenharmony_ci *val32 = aplic_read_enabled_word(aplic, i); 39362306a36Sopenharmony_ci } else if (off == APLIC_SETIENUM) { 39462306a36Sopenharmony_ci *val32 = 0; 39562306a36Sopenharmony_ci } else if ((off >= APLIC_CLRIE_BASE) && 39662306a36Sopenharmony_ci (off < (APLIC_CLRIE_BASE + aplic->nr_words * 4))) { 39762306a36Sopenharmony_ci *val32 = 0; 39862306a36Sopenharmony_ci } else if (off == APLIC_CLRIENUM) { 39962306a36Sopenharmony_ci *val32 = 0; 40062306a36Sopenharmony_ci } else if (off == APLIC_SETIPNUM_LE) { 40162306a36Sopenharmony_ci *val32 = 0; 40262306a36Sopenharmony_ci } else if (off == APLIC_SETIPNUM_BE) { 40362306a36Sopenharmony_ci *val32 = 0; 40462306a36Sopenharmony_ci } else if (off == APLIC_GENMSI) { 40562306a36Sopenharmony_ci *val32 = aplic->genmsi; 40662306a36Sopenharmony_ci } else if ((off >= APLIC_TARGET_BASE) && 40762306a36Sopenharmony_ci (off < (APLIC_TARGET_BASE + (aplic->nr_irqs - 1) * 4))) { 40862306a36Sopenharmony_ci i = ((off - APLIC_TARGET_BASE) >> 2) + 1; 40962306a36Sopenharmony_ci *val32 = aplic_read_target(aplic, i); 41062306a36Sopenharmony_ci } else 41162306a36Sopenharmony_ci return -ENODEV; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci return 0; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic int aplic_mmio_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev, 41762306a36Sopenharmony_ci gpa_t addr, int len, void *val) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci if (len != 4) 42062306a36Sopenharmony_ci return -EOPNOTSUPP; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci return aplic_mmio_read_offset(vcpu->kvm, 42362306a36Sopenharmony_ci addr - vcpu->kvm->arch.aia.aplic_addr, 42462306a36Sopenharmony_ci val); 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic int aplic_mmio_write_offset(struct kvm *kvm, gpa_t off, u32 val32) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci u32 i; 43062306a36Sopenharmony_ci struct aplic *aplic = kvm->arch.aia.aplic_state; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if ((off & 0x3) != 0) 43362306a36Sopenharmony_ci return -EOPNOTSUPP; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (off == APLIC_DOMAINCFG) { 43662306a36Sopenharmony_ci /* Only IE bit writeable */ 43762306a36Sopenharmony_ci aplic->domaincfg = val32 & APLIC_DOMAINCFG_IE; 43862306a36Sopenharmony_ci } else if ((off >= APLIC_SOURCECFG_BASE) && 43962306a36Sopenharmony_ci (off < (APLIC_SOURCECFG_BASE + (aplic->nr_irqs - 1) * 4))) { 44062306a36Sopenharmony_ci i = ((off - APLIC_SOURCECFG_BASE) >> 2) + 1; 44162306a36Sopenharmony_ci aplic_write_sourcecfg(aplic, i, val32); 44262306a36Sopenharmony_ci } else if ((off >= APLIC_SETIP_BASE) && 44362306a36Sopenharmony_ci (off < (APLIC_SETIP_BASE + aplic->nr_words * 4))) { 44462306a36Sopenharmony_ci i = (off - APLIC_SETIP_BASE) >> 2; 44562306a36Sopenharmony_ci aplic_write_pending_word(aplic, i, val32, true); 44662306a36Sopenharmony_ci } else if (off == APLIC_SETIPNUM) { 44762306a36Sopenharmony_ci aplic_write_pending(aplic, val32, true); 44862306a36Sopenharmony_ci } else if ((off >= APLIC_CLRIP_BASE) && 44962306a36Sopenharmony_ci (off < (APLIC_CLRIP_BASE + aplic->nr_words * 4))) { 45062306a36Sopenharmony_ci i = (off - APLIC_CLRIP_BASE) >> 2; 45162306a36Sopenharmony_ci aplic_write_pending_word(aplic, i, val32, false); 45262306a36Sopenharmony_ci } else if (off == APLIC_CLRIPNUM) { 45362306a36Sopenharmony_ci aplic_write_pending(aplic, val32, false); 45462306a36Sopenharmony_ci } else if ((off >= APLIC_SETIE_BASE) && 45562306a36Sopenharmony_ci (off < (APLIC_SETIE_BASE + aplic->nr_words * 4))) { 45662306a36Sopenharmony_ci i = (off - APLIC_SETIE_BASE) >> 2; 45762306a36Sopenharmony_ci aplic_write_enabled_word(aplic, i, val32, true); 45862306a36Sopenharmony_ci } else if (off == APLIC_SETIENUM) { 45962306a36Sopenharmony_ci aplic_write_enabled(aplic, val32, true); 46062306a36Sopenharmony_ci } else if ((off >= APLIC_CLRIE_BASE) && 46162306a36Sopenharmony_ci (off < (APLIC_CLRIE_BASE + aplic->nr_words * 4))) { 46262306a36Sopenharmony_ci i = (off - APLIC_CLRIE_BASE) >> 2; 46362306a36Sopenharmony_ci aplic_write_enabled_word(aplic, i, val32, false); 46462306a36Sopenharmony_ci } else if (off == APLIC_CLRIENUM) { 46562306a36Sopenharmony_ci aplic_write_enabled(aplic, val32, false); 46662306a36Sopenharmony_ci } else if (off == APLIC_SETIPNUM_LE) { 46762306a36Sopenharmony_ci aplic_write_pending(aplic, val32, true); 46862306a36Sopenharmony_ci } else if (off == APLIC_SETIPNUM_BE) { 46962306a36Sopenharmony_ci aplic_write_pending(aplic, __swab32(val32), true); 47062306a36Sopenharmony_ci } else if (off == APLIC_GENMSI) { 47162306a36Sopenharmony_ci aplic->genmsi = val32 & ~(APLIC_TARGET_GUEST_IDX_MASK << 47262306a36Sopenharmony_ci APLIC_TARGET_GUEST_IDX_SHIFT); 47362306a36Sopenharmony_ci kvm_riscv_aia_inject_msi_by_id(kvm, 47462306a36Sopenharmony_ci val32 >> APLIC_TARGET_HART_IDX_SHIFT, 0, 47562306a36Sopenharmony_ci val32 & APLIC_TARGET_EIID_MASK); 47662306a36Sopenharmony_ci } else if ((off >= APLIC_TARGET_BASE) && 47762306a36Sopenharmony_ci (off < (APLIC_TARGET_BASE + (aplic->nr_irqs - 1) * 4))) { 47862306a36Sopenharmony_ci i = ((off - APLIC_TARGET_BASE) >> 2) + 1; 47962306a36Sopenharmony_ci aplic_write_target(aplic, i, val32); 48062306a36Sopenharmony_ci } else 48162306a36Sopenharmony_ci return -ENODEV; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci aplic_update_irq_range(kvm, 1, aplic->nr_irqs - 1); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci return 0; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic int aplic_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev, 48962306a36Sopenharmony_ci gpa_t addr, int len, const void *val) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci if (len != 4) 49262306a36Sopenharmony_ci return -EOPNOTSUPP; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci return aplic_mmio_write_offset(vcpu->kvm, 49562306a36Sopenharmony_ci addr - vcpu->kvm->arch.aia.aplic_addr, 49662306a36Sopenharmony_ci *((const u32 *)val)); 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic struct kvm_io_device_ops aplic_iodoev_ops = { 50062306a36Sopenharmony_ci .read = aplic_mmio_read, 50162306a36Sopenharmony_ci .write = aplic_mmio_write, 50262306a36Sopenharmony_ci}; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ciint kvm_riscv_aia_aplic_set_attr(struct kvm *kvm, unsigned long type, u32 v) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci int rc; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (!kvm->arch.aia.aplic_state) 50962306a36Sopenharmony_ci return -ENODEV; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci rc = aplic_mmio_write_offset(kvm, type, v); 51262306a36Sopenharmony_ci if (rc) 51362306a36Sopenharmony_ci return rc; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return 0; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ciint kvm_riscv_aia_aplic_get_attr(struct kvm *kvm, unsigned long type, u32 *v) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci int rc; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (!kvm->arch.aia.aplic_state) 52362306a36Sopenharmony_ci return -ENODEV; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci rc = aplic_mmio_read_offset(kvm, type, v); 52662306a36Sopenharmony_ci if (rc) 52762306a36Sopenharmony_ci return rc; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci return 0; 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ciint kvm_riscv_aia_aplic_has_attr(struct kvm *kvm, unsigned long type) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci int rc; 53562306a36Sopenharmony_ci u32 val; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (!kvm->arch.aia.aplic_state) 53862306a36Sopenharmony_ci return -ENODEV; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci rc = aplic_mmio_read_offset(kvm, type, &val); 54162306a36Sopenharmony_ci if (rc) 54262306a36Sopenharmony_ci return rc; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ciint kvm_riscv_aia_aplic_init(struct kvm *kvm) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci int i, ret = 0; 55062306a36Sopenharmony_ci struct aplic *aplic; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* Do nothing if we have zero sources */ 55362306a36Sopenharmony_ci if (!kvm->arch.aia.nr_sources) 55462306a36Sopenharmony_ci return 0; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* Allocate APLIC global state */ 55762306a36Sopenharmony_ci aplic = kzalloc(sizeof(*aplic), GFP_KERNEL); 55862306a36Sopenharmony_ci if (!aplic) 55962306a36Sopenharmony_ci return -ENOMEM; 56062306a36Sopenharmony_ci kvm->arch.aia.aplic_state = aplic; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* Setup APLIC IRQs */ 56362306a36Sopenharmony_ci aplic->nr_irqs = kvm->arch.aia.nr_sources + 1; 56462306a36Sopenharmony_ci aplic->nr_words = DIV_ROUND_UP(aplic->nr_irqs, 32); 56562306a36Sopenharmony_ci aplic->irqs = kcalloc(aplic->nr_irqs, 56662306a36Sopenharmony_ci sizeof(*aplic->irqs), GFP_KERNEL); 56762306a36Sopenharmony_ci if (!aplic->irqs) { 56862306a36Sopenharmony_ci ret = -ENOMEM; 56962306a36Sopenharmony_ci goto fail_free_aplic; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci for (i = 0; i < aplic->nr_irqs; i++) 57262306a36Sopenharmony_ci raw_spin_lock_init(&aplic->irqs[i].lock); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* Setup IO device */ 57562306a36Sopenharmony_ci kvm_iodevice_init(&aplic->iodev, &aplic_iodoev_ops); 57662306a36Sopenharmony_ci mutex_lock(&kvm->slots_lock); 57762306a36Sopenharmony_ci ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, 57862306a36Sopenharmony_ci kvm->arch.aia.aplic_addr, 57962306a36Sopenharmony_ci KVM_DEV_RISCV_APLIC_SIZE, 58062306a36Sopenharmony_ci &aplic->iodev); 58162306a36Sopenharmony_ci mutex_unlock(&kvm->slots_lock); 58262306a36Sopenharmony_ci if (ret) 58362306a36Sopenharmony_ci goto fail_free_aplic_irqs; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* Setup default IRQ routing */ 58662306a36Sopenharmony_ci ret = kvm_riscv_setup_default_irq_routing(kvm, aplic->nr_irqs); 58762306a36Sopenharmony_ci if (ret) 58862306a36Sopenharmony_ci goto fail_unreg_iodev; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci return 0; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cifail_unreg_iodev: 59362306a36Sopenharmony_ci mutex_lock(&kvm->slots_lock); 59462306a36Sopenharmony_ci kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &aplic->iodev); 59562306a36Sopenharmony_ci mutex_unlock(&kvm->slots_lock); 59662306a36Sopenharmony_cifail_free_aplic_irqs: 59762306a36Sopenharmony_ci kfree(aplic->irqs); 59862306a36Sopenharmony_cifail_free_aplic: 59962306a36Sopenharmony_ci kvm->arch.aia.aplic_state = NULL; 60062306a36Sopenharmony_ci kfree(aplic); 60162306a36Sopenharmony_ci return ret; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_civoid kvm_riscv_aia_aplic_cleanup(struct kvm *kvm) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci struct aplic *aplic = kvm->arch.aia.aplic_state; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (!aplic) 60962306a36Sopenharmony_ci return; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci mutex_lock(&kvm->slots_lock); 61262306a36Sopenharmony_ci kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &aplic->iodev); 61362306a36Sopenharmony_ci mutex_unlock(&kvm->slots_lock); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci kfree(aplic->irqs); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci kvm->arch.aia.aplic_state = NULL; 61862306a36Sopenharmony_ci kfree(aplic); 61962306a36Sopenharmony_ci} 620