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