162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ARM Generic Interrupt Controller (GIC) support
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <errno.h>
762306a36Sopenharmony_ci#include <linux/bits.h>
862306a36Sopenharmony_ci#include <linux/sizes.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "kvm_util.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <gic.h>
1362306a36Sopenharmony_ci#include "gic_private.h"
1462306a36Sopenharmony_ci#include "processor.h"
1562306a36Sopenharmony_ci#include "spinlock.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic const struct gic_common_ops *gic_common_ops;
1862306a36Sopenharmony_cistatic struct spinlock gic_lock;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic void gic_cpu_init(unsigned int cpu, void *redist_base)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	gic_common_ops->gic_cpu_init(cpu, redist_base);
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic void
2662306a36Sopenharmony_cigic_dist_init(enum gic_type type, unsigned int nr_cpus, void *dist_base)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	const struct gic_common_ops *gic_ops = NULL;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	spin_lock(&gic_lock);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	/* Distributor initialization is needed only once per VM */
3362306a36Sopenharmony_ci	if (gic_common_ops) {
3462306a36Sopenharmony_ci		spin_unlock(&gic_lock);
3562306a36Sopenharmony_ci		return;
3662306a36Sopenharmony_ci	}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (type == GIC_V3)
3962306a36Sopenharmony_ci		gic_ops = &gicv3_ops;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	GUEST_ASSERT(gic_ops);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	gic_ops->gic_init(nr_cpus, dist_base);
4462306a36Sopenharmony_ci	gic_common_ops = gic_ops;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	/* Make sure that the initialized data is visible to all the vCPUs */
4762306a36Sopenharmony_ci	dsb(sy);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	spin_unlock(&gic_lock);
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_civoid gic_init(enum gic_type type, unsigned int nr_cpus,
5362306a36Sopenharmony_ci		void *dist_base, void *redist_base)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	uint32_t cpu = guest_get_vcpuid();
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	GUEST_ASSERT(type < GIC_TYPE_MAX);
5862306a36Sopenharmony_ci	GUEST_ASSERT(dist_base);
5962306a36Sopenharmony_ci	GUEST_ASSERT(redist_base);
6062306a36Sopenharmony_ci	GUEST_ASSERT(nr_cpus);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	gic_dist_init(type, nr_cpus, dist_base);
6362306a36Sopenharmony_ci	gic_cpu_init(cpu, redist_base);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_civoid gic_irq_enable(unsigned int intid)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	GUEST_ASSERT(gic_common_ops);
6962306a36Sopenharmony_ci	gic_common_ops->gic_irq_enable(intid);
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_civoid gic_irq_disable(unsigned int intid)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	GUEST_ASSERT(gic_common_ops);
7562306a36Sopenharmony_ci	gic_common_ops->gic_irq_disable(intid);
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ciunsigned int gic_get_and_ack_irq(void)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	uint64_t irqstat;
8162306a36Sopenharmony_ci	unsigned int intid;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	GUEST_ASSERT(gic_common_ops);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	irqstat = gic_common_ops->gic_read_iar();
8662306a36Sopenharmony_ci	intid = irqstat & GENMASK(23, 0);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	return intid;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_civoid gic_set_eoi(unsigned int intid)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	GUEST_ASSERT(gic_common_ops);
9462306a36Sopenharmony_ci	gic_common_ops->gic_write_eoir(intid);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_civoid gic_set_dir(unsigned int intid)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	GUEST_ASSERT(gic_common_ops);
10062306a36Sopenharmony_ci	gic_common_ops->gic_write_dir(intid);
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_civoid gic_set_eoi_split(bool split)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	GUEST_ASSERT(gic_common_ops);
10662306a36Sopenharmony_ci	gic_common_ops->gic_set_eoi_split(split);
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_civoid gic_set_priority_mask(uint64_t pmr)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	GUEST_ASSERT(gic_common_ops);
11262306a36Sopenharmony_ci	gic_common_ops->gic_set_priority_mask(pmr);
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_civoid gic_set_priority(unsigned int intid, unsigned int prio)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	GUEST_ASSERT(gic_common_ops);
11862306a36Sopenharmony_ci	gic_common_ops->gic_set_priority(intid, prio);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_civoid gic_irq_set_active(unsigned int intid)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	GUEST_ASSERT(gic_common_ops);
12462306a36Sopenharmony_ci	gic_common_ops->gic_irq_set_active(intid);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_civoid gic_irq_clear_active(unsigned int intid)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	GUEST_ASSERT(gic_common_ops);
13062306a36Sopenharmony_ci	gic_common_ops->gic_irq_clear_active(intid);
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cibool gic_irq_get_active(unsigned int intid)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	GUEST_ASSERT(gic_common_ops);
13662306a36Sopenharmony_ci	return gic_common_ops->gic_irq_get_active(intid);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_civoid gic_irq_set_pending(unsigned int intid)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	GUEST_ASSERT(gic_common_ops);
14262306a36Sopenharmony_ci	gic_common_ops->gic_irq_set_pending(intid);
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_civoid gic_irq_clear_pending(unsigned int intid)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	GUEST_ASSERT(gic_common_ops);
14862306a36Sopenharmony_ci	gic_common_ops->gic_irq_clear_pending(intid);
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cibool gic_irq_get_pending(unsigned int intid)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	GUEST_ASSERT(gic_common_ops);
15462306a36Sopenharmony_ci	return gic_common_ops->gic_irq_get_pending(intid);
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_civoid gic_irq_set_config(unsigned int intid, bool is_edge)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	GUEST_ASSERT(gic_common_ops);
16062306a36Sopenharmony_ci	gic_common_ops->gic_irq_set_config(intid, is_edge);
16162306a36Sopenharmony_ci}
162