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/atomic.h>
1162306a36Sopenharmony_ci#include <linux/bitmap.h>
1262306a36Sopenharmony_ci#include <linux/kvm_host.h>
1362306a36Sopenharmony_ci#include <linux/math.h>
1462306a36Sopenharmony_ci#include <linux/spinlock.h>
1562306a36Sopenharmony_ci#include <linux/swab.h>
1662306a36Sopenharmony_ci#include <kvm/iodev.h>
1762306a36Sopenharmony_ci#include <asm/csr.h>
1862306a36Sopenharmony_ci#include <asm/kvm_aia_imsic.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define IMSIC_MAX_EIX	(IMSIC_MAX_ID / BITS_PER_TYPE(u64))
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistruct imsic_mrif_eix {
2362306a36Sopenharmony_ci	unsigned long eip[BITS_PER_TYPE(u64) / BITS_PER_LONG];
2462306a36Sopenharmony_ci	unsigned long eie[BITS_PER_TYPE(u64) / BITS_PER_LONG];
2562306a36Sopenharmony_ci};
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct imsic_mrif {
2862306a36Sopenharmony_ci	struct imsic_mrif_eix eix[IMSIC_MAX_EIX];
2962306a36Sopenharmony_ci	unsigned long eithreshold;
3062306a36Sopenharmony_ci	unsigned long eidelivery;
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistruct imsic {
3462306a36Sopenharmony_ci	struct kvm_io_device iodev;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	u32 nr_msis;
3762306a36Sopenharmony_ci	u32 nr_eix;
3862306a36Sopenharmony_ci	u32 nr_hw_eix;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	/*
4162306a36Sopenharmony_ci	 * At any point in time, the register state is in
4262306a36Sopenharmony_ci	 * one of the following places:
4362306a36Sopenharmony_ci	 *
4462306a36Sopenharmony_ci	 * 1) Hardware: IMSIC VS-file (vsfile_cpu >= 0)
4562306a36Sopenharmony_ci	 * 2) Software: IMSIC SW-file (vsfile_cpu < 0)
4662306a36Sopenharmony_ci	 */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	/* IMSIC VS-file */
4962306a36Sopenharmony_ci	rwlock_t vsfile_lock;
5062306a36Sopenharmony_ci	int vsfile_cpu;
5162306a36Sopenharmony_ci	int vsfile_hgei;
5262306a36Sopenharmony_ci	void __iomem *vsfile_va;
5362306a36Sopenharmony_ci	phys_addr_t vsfile_pa;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	/* IMSIC SW-file */
5662306a36Sopenharmony_ci	struct imsic_mrif *swfile;
5762306a36Sopenharmony_ci	phys_addr_t swfile_pa;
5862306a36Sopenharmony_ci	spinlock_t swfile_extirq_lock;
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define imsic_vs_csr_read(__c)			\
6262306a36Sopenharmony_ci({						\
6362306a36Sopenharmony_ci	unsigned long __r;			\
6462306a36Sopenharmony_ci	csr_write(CSR_VSISELECT, __c);		\
6562306a36Sopenharmony_ci	__r = csr_read(CSR_VSIREG);		\
6662306a36Sopenharmony_ci	__r;					\
6762306a36Sopenharmony_ci})
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#define imsic_read_switchcase(__ireg)			\
7062306a36Sopenharmony_ci	case __ireg:					\
7162306a36Sopenharmony_ci		return imsic_vs_csr_read(__ireg);
7262306a36Sopenharmony_ci#define imsic_read_switchcase_2(__ireg)			\
7362306a36Sopenharmony_ci	imsic_read_switchcase(__ireg + 0)		\
7462306a36Sopenharmony_ci	imsic_read_switchcase(__ireg + 1)
7562306a36Sopenharmony_ci#define imsic_read_switchcase_4(__ireg)			\
7662306a36Sopenharmony_ci	imsic_read_switchcase_2(__ireg + 0)		\
7762306a36Sopenharmony_ci	imsic_read_switchcase_2(__ireg + 2)
7862306a36Sopenharmony_ci#define imsic_read_switchcase_8(__ireg)			\
7962306a36Sopenharmony_ci	imsic_read_switchcase_4(__ireg + 0)		\
8062306a36Sopenharmony_ci	imsic_read_switchcase_4(__ireg + 4)
8162306a36Sopenharmony_ci#define imsic_read_switchcase_16(__ireg)		\
8262306a36Sopenharmony_ci	imsic_read_switchcase_8(__ireg + 0)		\
8362306a36Sopenharmony_ci	imsic_read_switchcase_8(__ireg + 8)
8462306a36Sopenharmony_ci#define imsic_read_switchcase_32(__ireg)		\
8562306a36Sopenharmony_ci	imsic_read_switchcase_16(__ireg + 0)		\
8662306a36Sopenharmony_ci	imsic_read_switchcase_16(__ireg + 16)
8762306a36Sopenharmony_ci#define imsic_read_switchcase_64(__ireg)		\
8862306a36Sopenharmony_ci	imsic_read_switchcase_32(__ireg + 0)		\
8962306a36Sopenharmony_ci	imsic_read_switchcase_32(__ireg + 32)
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic unsigned long imsic_eix_read(int ireg)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	switch (ireg) {
9462306a36Sopenharmony_ci	imsic_read_switchcase_64(IMSIC_EIP0)
9562306a36Sopenharmony_ci	imsic_read_switchcase_64(IMSIC_EIE0)
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return 0;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci#define imsic_vs_csr_swap(__c, __v)		\
10262306a36Sopenharmony_ci({						\
10362306a36Sopenharmony_ci	unsigned long __r;			\
10462306a36Sopenharmony_ci	csr_write(CSR_VSISELECT, __c);		\
10562306a36Sopenharmony_ci	__r = csr_swap(CSR_VSIREG, __v);	\
10662306a36Sopenharmony_ci	__r;					\
10762306a36Sopenharmony_ci})
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci#define imsic_swap_switchcase(__ireg, __v)		\
11062306a36Sopenharmony_ci	case __ireg:					\
11162306a36Sopenharmony_ci		return imsic_vs_csr_swap(__ireg, __v);
11262306a36Sopenharmony_ci#define imsic_swap_switchcase_2(__ireg, __v)		\
11362306a36Sopenharmony_ci	imsic_swap_switchcase(__ireg + 0, __v)		\
11462306a36Sopenharmony_ci	imsic_swap_switchcase(__ireg + 1, __v)
11562306a36Sopenharmony_ci#define imsic_swap_switchcase_4(__ireg, __v)		\
11662306a36Sopenharmony_ci	imsic_swap_switchcase_2(__ireg + 0, __v)	\
11762306a36Sopenharmony_ci	imsic_swap_switchcase_2(__ireg + 2, __v)
11862306a36Sopenharmony_ci#define imsic_swap_switchcase_8(__ireg, __v)		\
11962306a36Sopenharmony_ci	imsic_swap_switchcase_4(__ireg + 0, __v)	\
12062306a36Sopenharmony_ci	imsic_swap_switchcase_4(__ireg + 4, __v)
12162306a36Sopenharmony_ci#define imsic_swap_switchcase_16(__ireg, __v)		\
12262306a36Sopenharmony_ci	imsic_swap_switchcase_8(__ireg + 0, __v)	\
12362306a36Sopenharmony_ci	imsic_swap_switchcase_8(__ireg + 8, __v)
12462306a36Sopenharmony_ci#define imsic_swap_switchcase_32(__ireg, __v)		\
12562306a36Sopenharmony_ci	imsic_swap_switchcase_16(__ireg + 0, __v)	\
12662306a36Sopenharmony_ci	imsic_swap_switchcase_16(__ireg + 16, __v)
12762306a36Sopenharmony_ci#define imsic_swap_switchcase_64(__ireg, __v)		\
12862306a36Sopenharmony_ci	imsic_swap_switchcase_32(__ireg + 0, __v)	\
12962306a36Sopenharmony_ci	imsic_swap_switchcase_32(__ireg + 32, __v)
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic unsigned long imsic_eix_swap(int ireg, unsigned long val)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	switch (ireg) {
13462306a36Sopenharmony_ci	imsic_swap_switchcase_64(IMSIC_EIP0, val)
13562306a36Sopenharmony_ci	imsic_swap_switchcase_64(IMSIC_EIE0, val)
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci#define imsic_vs_csr_write(__c, __v)		\
14262306a36Sopenharmony_cido {						\
14362306a36Sopenharmony_ci	csr_write(CSR_VSISELECT, __c);		\
14462306a36Sopenharmony_ci	csr_write(CSR_VSIREG, __v);		\
14562306a36Sopenharmony_ci} while (0)
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci#define imsic_write_switchcase(__ireg, __v)		\
14862306a36Sopenharmony_ci	case __ireg:					\
14962306a36Sopenharmony_ci		imsic_vs_csr_write(__ireg, __v);	\
15062306a36Sopenharmony_ci		break;
15162306a36Sopenharmony_ci#define imsic_write_switchcase_2(__ireg, __v)		\
15262306a36Sopenharmony_ci	imsic_write_switchcase(__ireg + 0, __v)		\
15362306a36Sopenharmony_ci	imsic_write_switchcase(__ireg + 1, __v)
15462306a36Sopenharmony_ci#define imsic_write_switchcase_4(__ireg, __v)		\
15562306a36Sopenharmony_ci	imsic_write_switchcase_2(__ireg + 0, __v)	\
15662306a36Sopenharmony_ci	imsic_write_switchcase_2(__ireg + 2, __v)
15762306a36Sopenharmony_ci#define imsic_write_switchcase_8(__ireg, __v)		\
15862306a36Sopenharmony_ci	imsic_write_switchcase_4(__ireg + 0, __v)	\
15962306a36Sopenharmony_ci	imsic_write_switchcase_4(__ireg + 4, __v)
16062306a36Sopenharmony_ci#define imsic_write_switchcase_16(__ireg, __v)		\
16162306a36Sopenharmony_ci	imsic_write_switchcase_8(__ireg + 0, __v)	\
16262306a36Sopenharmony_ci	imsic_write_switchcase_8(__ireg + 8, __v)
16362306a36Sopenharmony_ci#define imsic_write_switchcase_32(__ireg, __v)		\
16462306a36Sopenharmony_ci	imsic_write_switchcase_16(__ireg + 0, __v)	\
16562306a36Sopenharmony_ci	imsic_write_switchcase_16(__ireg + 16, __v)
16662306a36Sopenharmony_ci#define imsic_write_switchcase_64(__ireg, __v)		\
16762306a36Sopenharmony_ci	imsic_write_switchcase_32(__ireg + 0, __v)	\
16862306a36Sopenharmony_ci	imsic_write_switchcase_32(__ireg + 32, __v)
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic void imsic_eix_write(int ireg, unsigned long val)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	switch (ireg) {
17362306a36Sopenharmony_ci	imsic_write_switchcase_64(IMSIC_EIP0, val)
17462306a36Sopenharmony_ci	imsic_write_switchcase_64(IMSIC_EIE0, val)
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci#define imsic_vs_csr_set(__c, __v)		\
17962306a36Sopenharmony_cido {						\
18062306a36Sopenharmony_ci	csr_write(CSR_VSISELECT, __c);		\
18162306a36Sopenharmony_ci	csr_set(CSR_VSIREG, __v);		\
18262306a36Sopenharmony_ci} while (0)
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci#define imsic_set_switchcase(__ireg, __v)		\
18562306a36Sopenharmony_ci	case __ireg:					\
18662306a36Sopenharmony_ci		imsic_vs_csr_set(__ireg, __v);		\
18762306a36Sopenharmony_ci		break;
18862306a36Sopenharmony_ci#define imsic_set_switchcase_2(__ireg, __v)		\
18962306a36Sopenharmony_ci	imsic_set_switchcase(__ireg + 0, __v)		\
19062306a36Sopenharmony_ci	imsic_set_switchcase(__ireg + 1, __v)
19162306a36Sopenharmony_ci#define imsic_set_switchcase_4(__ireg, __v)		\
19262306a36Sopenharmony_ci	imsic_set_switchcase_2(__ireg + 0, __v)		\
19362306a36Sopenharmony_ci	imsic_set_switchcase_2(__ireg + 2, __v)
19462306a36Sopenharmony_ci#define imsic_set_switchcase_8(__ireg, __v)		\
19562306a36Sopenharmony_ci	imsic_set_switchcase_4(__ireg + 0, __v)		\
19662306a36Sopenharmony_ci	imsic_set_switchcase_4(__ireg + 4, __v)
19762306a36Sopenharmony_ci#define imsic_set_switchcase_16(__ireg, __v)		\
19862306a36Sopenharmony_ci	imsic_set_switchcase_8(__ireg + 0, __v)		\
19962306a36Sopenharmony_ci	imsic_set_switchcase_8(__ireg + 8, __v)
20062306a36Sopenharmony_ci#define imsic_set_switchcase_32(__ireg, __v)		\
20162306a36Sopenharmony_ci	imsic_set_switchcase_16(__ireg + 0, __v)	\
20262306a36Sopenharmony_ci	imsic_set_switchcase_16(__ireg + 16, __v)
20362306a36Sopenharmony_ci#define imsic_set_switchcase_64(__ireg, __v)		\
20462306a36Sopenharmony_ci	imsic_set_switchcase_32(__ireg + 0, __v)	\
20562306a36Sopenharmony_ci	imsic_set_switchcase_32(__ireg + 32, __v)
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic void imsic_eix_set(int ireg, unsigned long val)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	switch (ireg) {
21062306a36Sopenharmony_ci	imsic_set_switchcase_64(IMSIC_EIP0, val)
21162306a36Sopenharmony_ci	imsic_set_switchcase_64(IMSIC_EIE0, val)
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic unsigned long imsic_mrif_atomic_rmw(struct imsic_mrif *mrif,
21662306a36Sopenharmony_ci					   unsigned long *ptr,
21762306a36Sopenharmony_ci					   unsigned long new_val,
21862306a36Sopenharmony_ci					   unsigned long wr_mask)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	unsigned long old_val = 0, tmp = 0;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	__asm__ __volatile__ (
22362306a36Sopenharmony_ci		"0:	lr.w.aq   %1, %0\n"
22462306a36Sopenharmony_ci		"	and       %2, %1, %3\n"
22562306a36Sopenharmony_ci		"	or        %2, %2, %4\n"
22662306a36Sopenharmony_ci		"	sc.w.rl   %2, %2, %0\n"
22762306a36Sopenharmony_ci		"	bnez      %2, 0b"
22862306a36Sopenharmony_ci		: "+A" (*ptr), "+r" (old_val), "+r" (tmp)
22962306a36Sopenharmony_ci		: "r" (~wr_mask), "r" (new_val & wr_mask)
23062306a36Sopenharmony_ci		: "memory");
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	return old_val;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic unsigned long imsic_mrif_atomic_or(struct imsic_mrif *mrif,
23662306a36Sopenharmony_ci					  unsigned long *ptr,
23762306a36Sopenharmony_ci					  unsigned long val)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	return atomic_long_fetch_or(val, (atomic_long_t *)ptr);
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci#define imsic_mrif_atomic_write(__mrif, __ptr, __new_val)	\
24362306a36Sopenharmony_ci		imsic_mrif_atomic_rmw(__mrif, __ptr, __new_val, -1UL)
24462306a36Sopenharmony_ci#define imsic_mrif_atomic_read(__mrif, __ptr)			\
24562306a36Sopenharmony_ci		imsic_mrif_atomic_or(__mrif, __ptr, 0)
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic u32 imsic_mrif_topei(struct imsic_mrif *mrif, u32 nr_eix, u32 nr_msis)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	struct imsic_mrif_eix *eix;
25062306a36Sopenharmony_ci	u32 i, imin, imax, ei, max_msi;
25162306a36Sopenharmony_ci	unsigned long eipend[BITS_PER_TYPE(u64) / BITS_PER_LONG];
25262306a36Sopenharmony_ci	unsigned long eithreshold = imsic_mrif_atomic_read(mrif,
25362306a36Sopenharmony_ci							&mrif->eithreshold);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	max_msi = (eithreshold && (eithreshold <= nr_msis)) ?
25662306a36Sopenharmony_ci		   eithreshold : nr_msis;
25762306a36Sopenharmony_ci	for (ei = 0; ei < nr_eix; ei++) {
25862306a36Sopenharmony_ci		eix = &mrif->eix[ei];
25962306a36Sopenharmony_ci		eipend[0] = imsic_mrif_atomic_read(mrif, &eix->eie[0]) &
26062306a36Sopenharmony_ci			    imsic_mrif_atomic_read(mrif, &eix->eip[0]);
26162306a36Sopenharmony_ci#ifdef CONFIG_32BIT
26262306a36Sopenharmony_ci		eipend[1] = imsic_mrif_atomic_read(mrif, &eix->eie[1]) &
26362306a36Sopenharmony_ci			    imsic_mrif_atomic_read(mrif, &eix->eip[1]);
26462306a36Sopenharmony_ci		if (!eipend[0] && !eipend[1])
26562306a36Sopenharmony_ci#else
26662306a36Sopenharmony_ci		if (!eipend[0])
26762306a36Sopenharmony_ci#endif
26862306a36Sopenharmony_ci			continue;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		imin = ei * BITS_PER_TYPE(u64);
27162306a36Sopenharmony_ci		imax = ((imin + BITS_PER_TYPE(u64)) < max_msi) ?
27262306a36Sopenharmony_ci			imin + BITS_PER_TYPE(u64) : max_msi;
27362306a36Sopenharmony_ci		for (i = (!imin) ? 1 : imin; i < imax; i++) {
27462306a36Sopenharmony_ci			if (test_bit(i - imin, eipend))
27562306a36Sopenharmony_ci				return (i << TOPEI_ID_SHIFT) | i;
27662306a36Sopenharmony_ci		}
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	return 0;
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic int imsic_mrif_isel_check(u32 nr_eix, unsigned long isel)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	u32 num = 0;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	switch (isel) {
28762306a36Sopenharmony_ci	case IMSIC_EIDELIVERY:
28862306a36Sopenharmony_ci	case IMSIC_EITHRESHOLD:
28962306a36Sopenharmony_ci		break;
29062306a36Sopenharmony_ci	case IMSIC_EIP0 ... IMSIC_EIP63:
29162306a36Sopenharmony_ci		num = isel - IMSIC_EIP0;
29262306a36Sopenharmony_ci		break;
29362306a36Sopenharmony_ci	case IMSIC_EIE0 ... IMSIC_EIE63:
29462306a36Sopenharmony_ci		num = isel - IMSIC_EIE0;
29562306a36Sopenharmony_ci		break;
29662306a36Sopenharmony_ci	default:
29762306a36Sopenharmony_ci		return -ENOENT;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci#ifndef CONFIG_32BIT
30062306a36Sopenharmony_ci	if (num & 0x1)
30162306a36Sopenharmony_ci		return -EINVAL;
30262306a36Sopenharmony_ci#endif
30362306a36Sopenharmony_ci	if ((num / 2) >= nr_eix)
30462306a36Sopenharmony_ci		return -EINVAL;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	return 0;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic int imsic_mrif_rmw(struct imsic_mrif *mrif, u32 nr_eix,
31062306a36Sopenharmony_ci			  unsigned long isel, unsigned long *val,
31162306a36Sopenharmony_ci			  unsigned long new_val, unsigned long wr_mask)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	bool pend;
31462306a36Sopenharmony_ci	struct imsic_mrif_eix *eix;
31562306a36Sopenharmony_ci	unsigned long *ei, num, old_val = 0;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	switch (isel) {
31862306a36Sopenharmony_ci	case IMSIC_EIDELIVERY:
31962306a36Sopenharmony_ci		old_val = imsic_mrif_atomic_rmw(mrif, &mrif->eidelivery,
32062306a36Sopenharmony_ci						new_val, wr_mask & 0x1);
32162306a36Sopenharmony_ci		break;
32262306a36Sopenharmony_ci	case IMSIC_EITHRESHOLD:
32362306a36Sopenharmony_ci		old_val = imsic_mrif_atomic_rmw(mrif, &mrif->eithreshold,
32462306a36Sopenharmony_ci				new_val, wr_mask & (IMSIC_MAX_ID - 1));
32562306a36Sopenharmony_ci		break;
32662306a36Sopenharmony_ci	case IMSIC_EIP0 ... IMSIC_EIP63:
32762306a36Sopenharmony_ci	case IMSIC_EIE0 ... IMSIC_EIE63:
32862306a36Sopenharmony_ci		if (isel >= IMSIC_EIP0 && isel <= IMSIC_EIP63) {
32962306a36Sopenharmony_ci			pend = true;
33062306a36Sopenharmony_ci			num = isel - IMSIC_EIP0;
33162306a36Sopenharmony_ci		} else {
33262306a36Sopenharmony_ci			pend = false;
33362306a36Sopenharmony_ci			num = isel - IMSIC_EIE0;
33462306a36Sopenharmony_ci		}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		if ((num / 2) >= nr_eix)
33762306a36Sopenharmony_ci			return -EINVAL;
33862306a36Sopenharmony_ci		eix = &mrif->eix[num / 2];
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci#ifndef CONFIG_32BIT
34162306a36Sopenharmony_ci		if (num & 0x1)
34262306a36Sopenharmony_ci			return -EINVAL;
34362306a36Sopenharmony_ci		ei = (pend) ? &eix->eip[0] : &eix->eie[0];
34462306a36Sopenharmony_ci#else
34562306a36Sopenharmony_ci		ei = (pend) ? &eix->eip[num & 0x1] : &eix->eie[num & 0x1];
34662306a36Sopenharmony_ci#endif
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci		/* Bit0 of EIP0 or EIE0 is read-only */
34962306a36Sopenharmony_ci		if (!num)
35062306a36Sopenharmony_ci			wr_mask &= ~BIT(0);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci		old_val = imsic_mrif_atomic_rmw(mrif, ei, new_val, wr_mask);
35362306a36Sopenharmony_ci		break;
35462306a36Sopenharmony_ci	default:
35562306a36Sopenharmony_ci		return -ENOENT;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	if (val)
35962306a36Sopenharmony_ci		*val = old_val;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	return 0;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistruct imsic_vsfile_read_data {
36562306a36Sopenharmony_ci	int hgei;
36662306a36Sopenharmony_ci	u32 nr_eix;
36762306a36Sopenharmony_ci	bool clear;
36862306a36Sopenharmony_ci	struct imsic_mrif *mrif;
36962306a36Sopenharmony_ci};
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic void imsic_vsfile_local_read(void *data)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	u32 i;
37462306a36Sopenharmony_ci	struct imsic_mrif_eix *eix;
37562306a36Sopenharmony_ci	struct imsic_vsfile_read_data *idata = data;
37662306a36Sopenharmony_ci	struct imsic_mrif *mrif = idata->mrif;
37762306a36Sopenharmony_ci	unsigned long new_hstatus, old_hstatus, old_vsiselect;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	old_vsiselect = csr_read(CSR_VSISELECT);
38062306a36Sopenharmony_ci	old_hstatus = csr_read(CSR_HSTATUS);
38162306a36Sopenharmony_ci	new_hstatus = old_hstatus & ~HSTATUS_VGEIN;
38262306a36Sopenharmony_ci	new_hstatus |= ((unsigned long)idata->hgei) << HSTATUS_VGEIN_SHIFT;
38362306a36Sopenharmony_ci	csr_write(CSR_HSTATUS, new_hstatus);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	/*
38662306a36Sopenharmony_ci	 * We don't use imsic_mrif_atomic_xyz() functions to store
38762306a36Sopenharmony_ci	 * values in MRIF because imsic_vsfile_read() is always called
38862306a36Sopenharmony_ci	 * with pointer to temporary MRIF on stack.
38962306a36Sopenharmony_ci	 */
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	if (idata->clear) {
39262306a36Sopenharmony_ci		mrif->eidelivery = imsic_vs_csr_swap(IMSIC_EIDELIVERY, 0);
39362306a36Sopenharmony_ci		mrif->eithreshold = imsic_vs_csr_swap(IMSIC_EITHRESHOLD, 0);
39462306a36Sopenharmony_ci		for (i = 0; i < idata->nr_eix; i++) {
39562306a36Sopenharmony_ci			eix = &mrif->eix[i];
39662306a36Sopenharmony_ci			eix->eip[0] = imsic_eix_swap(IMSIC_EIP0 + i * 2, 0);
39762306a36Sopenharmony_ci			eix->eie[0] = imsic_eix_swap(IMSIC_EIE0 + i * 2, 0);
39862306a36Sopenharmony_ci#ifdef CONFIG_32BIT
39962306a36Sopenharmony_ci			eix->eip[1] = imsic_eix_swap(IMSIC_EIP0 + i * 2 + 1, 0);
40062306a36Sopenharmony_ci			eix->eie[1] = imsic_eix_swap(IMSIC_EIE0 + i * 2 + 1, 0);
40162306a36Sopenharmony_ci#endif
40262306a36Sopenharmony_ci		}
40362306a36Sopenharmony_ci	} else {
40462306a36Sopenharmony_ci		mrif->eidelivery = imsic_vs_csr_read(IMSIC_EIDELIVERY);
40562306a36Sopenharmony_ci		mrif->eithreshold = imsic_vs_csr_read(IMSIC_EITHRESHOLD);
40662306a36Sopenharmony_ci		for (i = 0; i < idata->nr_eix; i++) {
40762306a36Sopenharmony_ci			eix = &mrif->eix[i];
40862306a36Sopenharmony_ci			eix->eip[0] = imsic_eix_read(IMSIC_EIP0 + i * 2);
40962306a36Sopenharmony_ci			eix->eie[0] = imsic_eix_read(IMSIC_EIE0 + i * 2);
41062306a36Sopenharmony_ci#ifdef CONFIG_32BIT
41162306a36Sopenharmony_ci			eix->eip[1] = imsic_eix_read(IMSIC_EIP0 + i * 2 + 1);
41262306a36Sopenharmony_ci			eix->eie[1] = imsic_eix_read(IMSIC_EIE0 + i * 2 + 1);
41362306a36Sopenharmony_ci#endif
41462306a36Sopenharmony_ci		}
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	csr_write(CSR_HSTATUS, old_hstatus);
41862306a36Sopenharmony_ci	csr_write(CSR_VSISELECT, old_vsiselect);
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic void imsic_vsfile_read(int vsfile_hgei, int vsfile_cpu, u32 nr_eix,
42262306a36Sopenharmony_ci			      bool clear, struct imsic_mrif *mrif)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct imsic_vsfile_read_data idata;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	/* We can only read clear if we have a IMSIC VS-file */
42762306a36Sopenharmony_ci	if (vsfile_cpu < 0 || vsfile_hgei <= 0)
42862306a36Sopenharmony_ci		return;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	/* We can only read clear on local CPU */
43162306a36Sopenharmony_ci	idata.hgei = vsfile_hgei;
43262306a36Sopenharmony_ci	idata.nr_eix = nr_eix;
43362306a36Sopenharmony_ci	idata.clear = clear;
43462306a36Sopenharmony_ci	idata.mrif = mrif;
43562306a36Sopenharmony_ci	on_each_cpu_mask(cpumask_of(vsfile_cpu),
43662306a36Sopenharmony_ci			 imsic_vsfile_local_read, &idata, 1);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistruct imsic_vsfile_rw_data {
44062306a36Sopenharmony_ci	int hgei;
44162306a36Sopenharmony_ci	int isel;
44262306a36Sopenharmony_ci	bool write;
44362306a36Sopenharmony_ci	unsigned long val;
44462306a36Sopenharmony_ci};
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic void imsic_vsfile_local_rw(void *data)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	struct imsic_vsfile_rw_data *idata = data;
44962306a36Sopenharmony_ci	unsigned long new_hstatus, old_hstatus, old_vsiselect;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	old_vsiselect = csr_read(CSR_VSISELECT);
45262306a36Sopenharmony_ci	old_hstatus = csr_read(CSR_HSTATUS);
45362306a36Sopenharmony_ci	new_hstatus = old_hstatus & ~HSTATUS_VGEIN;
45462306a36Sopenharmony_ci	new_hstatus |= ((unsigned long)idata->hgei) << HSTATUS_VGEIN_SHIFT;
45562306a36Sopenharmony_ci	csr_write(CSR_HSTATUS, new_hstatus);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	switch (idata->isel) {
45862306a36Sopenharmony_ci	case IMSIC_EIDELIVERY:
45962306a36Sopenharmony_ci		if (idata->write)
46062306a36Sopenharmony_ci			imsic_vs_csr_write(IMSIC_EIDELIVERY, idata->val);
46162306a36Sopenharmony_ci		else
46262306a36Sopenharmony_ci			idata->val = imsic_vs_csr_read(IMSIC_EIDELIVERY);
46362306a36Sopenharmony_ci		break;
46462306a36Sopenharmony_ci	case IMSIC_EITHRESHOLD:
46562306a36Sopenharmony_ci		if (idata->write)
46662306a36Sopenharmony_ci			imsic_vs_csr_write(IMSIC_EITHRESHOLD, idata->val);
46762306a36Sopenharmony_ci		else
46862306a36Sopenharmony_ci			idata->val = imsic_vs_csr_read(IMSIC_EITHRESHOLD);
46962306a36Sopenharmony_ci		break;
47062306a36Sopenharmony_ci	case IMSIC_EIP0 ... IMSIC_EIP63:
47162306a36Sopenharmony_ci	case IMSIC_EIE0 ... IMSIC_EIE63:
47262306a36Sopenharmony_ci#ifndef CONFIG_32BIT
47362306a36Sopenharmony_ci		if (idata->isel & 0x1)
47462306a36Sopenharmony_ci			break;
47562306a36Sopenharmony_ci#endif
47662306a36Sopenharmony_ci		if (idata->write)
47762306a36Sopenharmony_ci			imsic_eix_write(idata->isel, idata->val);
47862306a36Sopenharmony_ci		else
47962306a36Sopenharmony_ci			idata->val = imsic_eix_read(idata->isel);
48062306a36Sopenharmony_ci		break;
48162306a36Sopenharmony_ci	default:
48262306a36Sopenharmony_ci		break;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	csr_write(CSR_HSTATUS, old_hstatus);
48662306a36Sopenharmony_ci	csr_write(CSR_VSISELECT, old_vsiselect);
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic int imsic_vsfile_rw(int vsfile_hgei, int vsfile_cpu, u32 nr_eix,
49062306a36Sopenharmony_ci			   unsigned long isel, bool write,
49162306a36Sopenharmony_ci			   unsigned long *val)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	int rc;
49462306a36Sopenharmony_ci	struct imsic_vsfile_rw_data rdata;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/* We can only access register if we have a IMSIC VS-file */
49762306a36Sopenharmony_ci	if (vsfile_cpu < 0 || vsfile_hgei <= 0)
49862306a36Sopenharmony_ci		return -EINVAL;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	/* Check IMSIC register iselect */
50162306a36Sopenharmony_ci	rc = imsic_mrif_isel_check(nr_eix, isel);
50262306a36Sopenharmony_ci	if (rc)
50362306a36Sopenharmony_ci		return rc;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	/* We can only access register on local CPU */
50662306a36Sopenharmony_ci	rdata.hgei = vsfile_hgei;
50762306a36Sopenharmony_ci	rdata.isel = isel;
50862306a36Sopenharmony_ci	rdata.write = write;
50962306a36Sopenharmony_ci	rdata.val = (write) ? *val : 0;
51062306a36Sopenharmony_ci	on_each_cpu_mask(cpumask_of(vsfile_cpu),
51162306a36Sopenharmony_ci			 imsic_vsfile_local_rw, &rdata, 1);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	if (!write)
51462306a36Sopenharmony_ci		*val = rdata.val;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	return 0;
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cistatic void imsic_vsfile_local_clear(int vsfile_hgei, u32 nr_eix)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	u32 i;
52262306a36Sopenharmony_ci	unsigned long new_hstatus, old_hstatus, old_vsiselect;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	/* We can only zero-out if we have a IMSIC VS-file */
52562306a36Sopenharmony_ci	if (vsfile_hgei <= 0)
52662306a36Sopenharmony_ci		return;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	old_vsiselect = csr_read(CSR_VSISELECT);
52962306a36Sopenharmony_ci	old_hstatus = csr_read(CSR_HSTATUS);
53062306a36Sopenharmony_ci	new_hstatus = old_hstatus & ~HSTATUS_VGEIN;
53162306a36Sopenharmony_ci	new_hstatus |= ((unsigned long)vsfile_hgei) << HSTATUS_VGEIN_SHIFT;
53262306a36Sopenharmony_ci	csr_write(CSR_HSTATUS, new_hstatus);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	imsic_vs_csr_write(IMSIC_EIDELIVERY, 0);
53562306a36Sopenharmony_ci	imsic_vs_csr_write(IMSIC_EITHRESHOLD, 0);
53662306a36Sopenharmony_ci	for (i = 0; i < nr_eix; i++) {
53762306a36Sopenharmony_ci		imsic_eix_write(IMSIC_EIP0 + i * 2, 0);
53862306a36Sopenharmony_ci		imsic_eix_write(IMSIC_EIE0 + i * 2, 0);
53962306a36Sopenharmony_ci#ifdef CONFIG_32BIT
54062306a36Sopenharmony_ci		imsic_eix_write(IMSIC_EIP0 + i * 2 + 1, 0);
54162306a36Sopenharmony_ci		imsic_eix_write(IMSIC_EIE0 + i * 2 + 1, 0);
54262306a36Sopenharmony_ci#endif
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	csr_write(CSR_HSTATUS, old_hstatus);
54662306a36Sopenharmony_ci	csr_write(CSR_VSISELECT, old_vsiselect);
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic void imsic_vsfile_local_update(int vsfile_hgei, u32 nr_eix,
55062306a36Sopenharmony_ci				      struct imsic_mrif *mrif)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	u32 i;
55362306a36Sopenharmony_ci	struct imsic_mrif_eix *eix;
55462306a36Sopenharmony_ci	unsigned long new_hstatus, old_hstatus, old_vsiselect;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	/* We can only update if we have a HW IMSIC context */
55762306a36Sopenharmony_ci	if (vsfile_hgei <= 0)
55862306a36Sopenharmony_ci		return;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	/*
56162306a36Sopenharmony_ci	 * We don't use imsic_mrif_atomic_xyz() functions to read values
56262306a36Sopenharmony_ci	 * from MRIF in this function because it is always called with
56362306a36Sopenharmony_ci	 * pointer to temporary MRIF on stack.
56462306a36Sopenharmony_ci	 */
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	old_vsiselect = csr_read(CSR_VSISELECT);
56762306a36Sopenharmony_ci	old_hstatus = csr_read(CSR_HSTATUS);
56862306a36Sopenharmony_ci	new_hstatus = old_hstatus & ~HSTATUS_VGEIN;
56962306a36Sopenharmony_ci	new_hstatus |= ((unsigned long)vsfile_hgei) << HSTATUS_VGEIN_SHIFT;
57062306a36Sopenharmony_ci	csr_write(CSR_HSTATUS, new_hstatus);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	for (i = 0; i < nr_eix; i++) {
57362306a36Sopenharmony_ci		eix = &mrif->eix[i];
57462306a36Sopenharmony_ci		imsic_eix_set(IMSIC_EIP0 + i * 2, eix->eip[0]);
57562306a36Sopenharmony_ci		imsic_eix_set(IMSIC_EIE0 + i * 2, eix->eie[0]);
57662306a36Sopenharmony_ci#ifdef CONFIG_32BIT
57762306a36Sopenharmony_ci		imsic_eix_set(IMSIC_EIP0 + i * 2 + 1, eix->eip[1]);
57862306a36Sopenharmony_ci		imsic_eix_set(IMSIC_EIE0 + i * 2 + 1, eix->eie[1]);
57962306a36Sopenharmony_ci#endif
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci	imsic_vs_csr_write(IMSIC_EITHRESHOLD, mrif->eithreshold);
58262306a36Sopenharmony_ci	imsic_vs_csr_write(IMSIC_EIDELIVERY, mrif->eidelivery);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	csr_write(CSR_HSTATUS, old_hstatus);
58562306a36Sopenharmony_ci	csr_write(CSR_VSISELECT, old_vsiselect);
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic void imsic_vsfile_cleanup(struct imsic *imsic)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	int old_vsfile_hgei, old_vsfile_cpu;
59162306a36Sopenharmony_ci	unsigned long flags;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	/*
59462306a36Sopenharmony_ci	 * We don't use imsic_mrif_atomic_xyz() functions to clear the
59562306a36Sopenharmony_ci	 * SW-file in this function because it is always called when the
59662306a36Sopenharmony_ci	 * VCPU is being destroyed.
59762306a36Sopenharmony_ci	 */
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	write_lock_irqsave(&imsic->vsfile_lock, flags);
60062306a36Sopenharmony_ci	old_vsfile_hgei = imsic->vsfile_hgei;
60162306a36Sopenharmony_ci	old_vsfile_cpu = imsic->vsfile_cpu;
60262306a36Sopenharmony_ci	imsic->vsfile_cpu = imsic->vsfile_hgei = -1;
60362306a36Sopenharmony_ci	imsic->vsfile_va = NULL;
60462306a36Sopenharmony_ci	imsic->vsfile_pa = 0;
60562306a36Sopenharmony_ci	write_unlock_irqrestore(&imsic->vsfile_lock, flags);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	memset(imsic->swfile, 0, sizeof(*imsic->swfile));
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	if (old_vsfile_cpu >= 0)
61062306a36Sopenharmony_ci		kvm_riscv_aia_free_hgei(old_vsfile_cpu, old_vsfile_hgei);
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic void imsic_swfile_extirq_update(struct kvm_vcpu *vcpu)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
61662306a36Sopenharmony_ci	struct imsic_mrif *mrif = imsic->swfile;
61762306a36Sopenharmony_ci	unsigned long flags;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	/*
62062306a36Sopenharmony_ci	 * The critical section is necessary during external interrupt
62162306a36Sopenharmony_ci	 * updates to avoid the risk of losing interrupts due to potential
62262306a36Sopenharmony_ci	 * interruptions between reading topei and updating pending status.
62362306a36Sopenharmony_ci	 */
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	spin_lock_irqsave(&imsic->swfile_extirq_lock, flags);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	if (imsic_mrif_atomic_read(mrif, &mrif->eidelivery) &&
62862306a36Sopenharmony_ci	    imsic_mrif_topei(mrif, imsic->nr_eix, imsic->nr_msis))
62962306a36Sopenharmony_ci		kvm_riscv_vcpu_set_interrupt(vcpu, IRQ_VS_EXT);
63062306a36Sopenharmony_ci	else
63162306a36Sopenharmony_ci		kvm_riscv_vcpu_unset_interrupt(vcpu, IRQ_VS_EXT);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	spin_unlock_irqrestore(&imsic->swfile_extirq_lock, flags);
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_cistatic void imsic_swfile_read(struct kvm_vcpu *vcpu, bool clear,
63762306a36Sopenharmony_ci			      struct imsic_mrif *mrif)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	/*
64262306a36Sopenharmony_ci	 * We don't use imsic_mrif_atomic_xyz() functions to read and
64362306a36Sopenharmony_ci	 * write SW-file and MRIF in this function because it is always
64462306a36Sopenharmony_ci	 * called when VCPU is not using SW-file and the MRIF points to
64562306a36Sopenharmony_ci	 * a temporary MRIF on stack.
64662306a36Sopenharmony_ci	 */
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	memcpy(mrif, imsic->swfile, sizeof(*mrif));
64962306a36Sopenharmony_ci	if (clear) {
65062306a36Sopenharmony_ci		memset(imsic->swfile, 0, sizeof(*imsic->swfile));
65162306a36Sopenharmony_ci		kvm_riscv_vcpu_unset_interrupt(vcpu, IRQ_VS_EXT);
65262306a36Sopenharmony_ci	}
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic void imsic_swfile_update(struct kvm_vcpu *vcpu,
65662306a36Sopenharmony_ci				struct imsic_mrif *mrif)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	u32 i;
65962306a36Sopenharmony_ci	struct imsic_mrif_eix *seix, *eix;
66062306a36Sopenharmony_ci	struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
66162306a36Sopenharmony_ci	struct imsic_mrif *smrif = imsic->swfile;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	imsic_mrif_atomic_write(smrif, &smrif->eidelivery, mrif->eidelivery);
66462306a36Sopenharmony_ci	imsic_mrif_atomic_write(smrif, &smrif->eithreshold, mrif->eithreshold);
66562306a36Sopenharmony_ci	for (i = 0; i < imsic->nr_eix; i++) {
66662306a36Sopenharmony_ci		seix = &smrif->eix[i];
66762306a36Sopenharmony_ci		eix = &mrif->eix[i];
66862306a36Sopenharmony_ci		imsic_mrif_atomic_or(smrif, &seix->eip[0], eix->eip[0]);
66962306a36Sopenharmony_ci		imsic_mrif_atomic_or(smrif, &seix->eie[0], eix->eie[0]);
67062306a36Sopenharmony_ci#ifdef CONFIG_32BIT
67162306a36Sopenharmony_ci		imsic_mrif_atomic_or(smrif, &seix->eip[1], eix->eip[1]);
67262306a36Sopenharmony_ci		imsic_mrif_atomic_or(smrif, &seix->eie[1], eix->eie[1]);
67362306a36Sopenharmony_ci#endif
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	imsic_swfile_extirq_update(vcpu);
67762306a36Sopenharmony_ci}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_civoid kvm_riscv_vcpu_aia_imsic_release(struct kvm_vcpu *vcpu)
68062306a36Sopenharmony_ci{
68162306a36Sopenharmony_ci	unsigned long flags;
68262306a36Sopenharmony_ci	struct imsic_mrif tmrif;
68362306a36Sopenharmony_ci	int old_vsfile_hgei, old_vsfile_cpu;
68462306a36Sopenharmony_ci	struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	/* Read and clear IMSIC VS-file details */
68762306a36Sopenharmony_ci	write_lock_irqsave(&imsic->vsfile_lock, flags);
68862306a36Sopenharmony_ci	old_vsfile_hgei = imsic->vsfile_hgei;
68962306a36Sopenharmony_ci	old_vsfile_cpu = imsic->vsfile_cpu;
69062306a36Sopenharmony_ci	imsic->vsfile_cpu = imsic->vsfile_hgei = -1;
69162306a36Sopenharmony_ci	imsic->vsfile_va = NULL;
69262306a36Sopenharmony_ci	imsic->vsfile_pa = 0;
69362306a36Sopenharmony_ci	write_unlock_irqrestore(&imsic->vsfile_lock, flags);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	/* Do nothing, if no IMSIC VS-file to release */
69662306a36Sopenharmony_ci	if (old_vsfile_cpu < 0)
69762306a36Sopenharmony_ci		return;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	/*
70062306a36Sopenharmony_ci	 * At this point, all interrupt producers are still using
70162306a36Sopenharmony_ci	 * the old IMSIC VS-file so we first re-direct all interrupt
70262306a36Sopenharmony_ci	 * producers.
70362306a36Sopenharmony_ci	 */
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	/* Purge the G-stage mapping */
70662306a36Sopenharmony_ci	kvm_riscv_gstage_iounmap(vcpu->kvm,
70762306a36Sopenharmony_ci				 vcpu->arch.aia_context.imsic_addr,
70862306a36Sopenharmony_ci				 IMSIC_MMIO_PAGE_SZ);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	/* TODO: Purge the IOMMU mapping ??? */
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	/*
71362306a36Sopenharmony_ci	 * At this point, all interrupt producers have been re-directed
71462306a36Sopenharmony_ci	 * to somewhere else so we move register state from the old IMSIC
71562306a36Sopenharmony_ci	 * VS-file to the IMSIC SW-file.
71662306a36Sopenharmony_ci	 */
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	/* Read and clear register state from old IMSIC VS-file */
71962306a36Sopenharmony_ci	memset(&tmrif, 0, sizeof(tmrif));
72062306a36Sopenharmony_ci	imsic_vsfile_read(old_vsfile_hgei, old_vsfile_cpu, imsic->nr_hw_eix,
72162306a36Sopenharmony_ci			  true, &tmrif);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	/* Update register state in IMSIC SW-file */
72462306a36Sopenharmony_ci	imsic_swfile_update(vcpu, &tmrif);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	/* Free-up old IMSIC VS-file */
72762306a36Sopenharmony_ci	kvm_riscv_aia_free_hgei(old_vsfile_cpu, old_vsfile_hgei);
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ciint kvm_riscv_vcpu_aia_imsic_update(struct kvm_vcpu *vcpu)
73162306a36Sopenharmony_ci{
73262306a36Sopenharmony_ci	unsigned long flags;
73362306a36Sopenharmony_ci	phys_addr_t new_vsfile_pa;
73462306a36Sopenharmony_ci	struct imsic_mrif tmrif;
73562306a36Sopenharmony_ci	void __iomem *new_vsfile_va;
73662306a36Sopenharmony_ci	struct kvm *kvm = vcpu->kvm;
73762306a36Sopenharmony_ci	struct kvm_run *run = vcpu->run;
73862306a36Sopenharmony_ci	struct kvm_vcpu_aia *vaia = &vcpu->arch.aia_context;
73962306a36Sopenharmony_ci	struct imsic *imsic = vaia->imsic_state;
74062306a36Sopenharmony_ci	int ret = 0, new_vsfile_hgei = -1, old_vsfile_hgei, old_vsfile_cpu;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	/* Do nothing for emulation mode */
74362306a36Sopenharmony_ci	if (kvm->arch.aia.mode == KVM_DEV_RISCV_AIA_MODE_EMUL)
74462306a36Sopenharmony_ci		return 1;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	/* Read old IMSIC VS-file details */
74762306a36Sopenharmony_ci	read_lock_irqsave(&imsic->vsfile_lock, flags);
74862306a36Sopenharmony_ci	old_vsfile_hgei = imsic->vsfile_hgei;
74962306a36Sopenharmony_ci	old_vsfile_cpu = imsic->vsfile_cpu;
75062306a36Sopenharmony_ci	read_unlock_irqrestore(&imsic->vsfile_lock, flags);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	/* Do nothing if we are continuing on same CPU */
75362306a36Sopenharmony_ci	if (old_vsfile_cpu == vcpu->cpu)
75462306a36Sopenharmony_ci		return 1;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	/* Allocate new IMSIC VS-file */
75762306a36Sopenharmony_ci	ret = kvm_riscv_aia_alloc_hgei(vcpu->cpu, vcpu,
75862306a36Sopenharmony_ci				       &new_vsfile_va, &new_vsfile_pa);
75962306a36Sopenharmony_ci	if (ret <= 0) {
76062306a36Sopenharmony_ci		/* For HW acceleration mode, we can't continue */
76162306a36Sopenharmony_ci		if (kvm->arch.aia.mode == KVM_DEV_RISCV_AIA_MODE_HWACCEL) {
76262306a36Sopenharmony_ci			run->fail_entry.hardware_entry_failure_reason =
76362306a36Sopenharmony_ci								CSR_HSTATUS;
76462306a36Sopenharmony_ci			run->fail_entry.cpu = vcpu->cpu;
76562306a36Sopenharmony_ci			run->exit_reason = KVM_EXIT_FAIL_ENTRY;
76662306a36Sopenharmony_ci			return 0;
76762306a36Sopenharmony_ci		}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci		/* Release old IMSIC VS-file */
77062306a36Sopenharmony_ci		if (old_vsfile_cpu >= 0)
77162306a36Sopenharmony_ci			kvm_riscv_vcpu_aia_imsic_release(vcpu);
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci		/* For automatic mode, we continue */
77462306a36Sopenharmony_ci		goto done;
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci	new_vsfile_hgei = ret;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	/*
77962306a36Sopenharmony_ci	 * At this point, all interrupt producers are still using
78062306a36Sopenharmony_ci	 * to the old IMSIC VS-file so we first move all interrupt
78162306a36Sopenharmony_ci	 * producers to the new IMSIC VS-file.
78262306a36Sopenharmony_ci	 */
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	/* Zero-out new IMSIC VS-file */
78562306a36Sopenharmony_ci	imsic_vsfile_local_clear(new_vsfile_hgei, imsic->nr_hw_eix);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	/* Update G-stage mapping for the new IMSIC VS-file */
78862306a36Sopenharmony_ci	ret = kvm_riscv_gstage_ioremap(kvm, vcpu->arch.aia_context.imsic_addr,
78962306a36Sopenharmony_ci				       new_vsfile_pa, IMSIC_MMIO_PAGE_SZ,
79062306a36Sopenharmony_ci				       true, true);
79162306a36Sopenharmony_ci	if (ret)
79262306a36Sopenharmony_ci		goto fail_free_vsfile_hgei;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	/* TODO: Update the IOMMU mapping ??? */
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	/* Update new IMSIC VS-file details in IMSIC context */
79762306a36Sopenharmony_ci	write_lock_irqsave(&imsic->vsfile_lock, flags);
79862306a36Sopenharmony_ci	imsic->vsfile_hgei = new_vsfile_hgei;
79962306a36Sopenharmony_ci	imsic->vsfile_cpu = vcpu->cpu;
80062306a36Sopenharmony_ci	imsic->vsfile_va = new_vsfile_va;
80162306a36Sopenharmony_ci	imsic->vsfile_pa = new_vsfile_pa;
80262306a36Sopenharmony_ci	write_unlock_irqrestore(&imsic->vsfile_lock, flags);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	/*
80562306a36Sopenharmony_ci	 * At this point, all interrupt producers have been moved
80662306a36Sopenharmony_ci	 * to the new IMSIC VS-file so we move register state from
80762306a36Sopenharmony_ci	 * the old IMSIC VS/SW-file to the new IMSIC VS-file.
80862306a36Sopenharmony_ci	 */
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	memset(&tmrif, 0, sizeof(tmrif));
81162306a36Sopenharmony_ci	if (old_vsfile_cpu >= 0) {
81262306a36Sopenharmony_ci		/* Read and clear register state from old IMSIC VS-file */
81362306a36Sopenharmony_ci		imsic_vsfile_read(old_vsfile_hgei, old_vsfile_cpu,
81462306a36Sopenharmony_ci				  imsic->nr_hw_eix, true, &tmrif);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci		/* Free-up old IMSIC VS-file */
81762306a36Sopenharmony_ci		kvm_riscv_aia_free_hgei(old_vsfile_cpu, old_vsfile_hgei);
81862306a36Sopenharmony_ci	} else {
81962306a36Sopenharmony_ci		/* Read and clear register state from IMSIC SW-file */
82062306a36Sopenharmony_ci		imsic_swfile_read(vcpu, true, &tmrif);
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	/* Restore register state in the new IMSIC VS-file */
82462306a36Sopenharmony_ci	imsic_vsfile_local_update(new_vsfile_hgei, imsic->nr_hw_eix, &tmrif);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_cidone:
82762306a36Sopenharmony_ci	/* Set VCPU HSTATUS.VGEIN to new IMSIC VS-file */
82862306a36Sopenharmony_ci	vcpu->arch.guest_context.hstatus &= ~HSTATUS_VGEIN;
82962306a36Sopenharmony_ci	if (new_vsfile_hgei > 0)
83062306a36Sopenharmony_ci		vcpu->arch.guest_context.hstatus |=
83162306a36Sopenharmony_ci			((unsigned long)new_vsfile_hgei) << HSTATUS_VGEIN_SHIFT;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	/* Continue run-loop */
83462306a36Sopenharmony_ci	return 1;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cifail_free_vsfile_hgei:
83762306a36Sopenharmony_ci	kvm_riscv_aia_free_hgei(vcpu->cpu, new_vsfile_hgei);
83862306a36Sopenharmony_ci	return ret;
83962306a36Sopenharmony_ci}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ciint kvm_riscv_vcpu_aia_imsic_rmw(struct kvm_vcpu *vcpu, unsigned long isel,
84262306a36Sopenharmony_ci				 unsigned long *val, unsigned long new_val,
84362306a36Sopenharmony_ci				 unsigned long wr_mask)
84462306a36Sopenharmony_ci{
84562306a36Sopenharmony_ci	u32 topei;
84662306a36Sopenharmony_ci	struct imsic_mrif_eix *eix;
84762306a36Sopenharmony_ci	int r, rc = KVM_INSN_CONTINUE_NEXT_SEPC;
84862306a36Sopenharmony_ci	struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	if (isel == KVM_RISCV_AIA_IMSIC_TOPEI) {
85162306a36Sopenharmony_ci		/* Read pending and enabled interrupt with highest priority */
85262306a36Sopenharmony_ci		topei = imsic_mrif_topei(imsic->swfile, imsic->nr_eix,
85362306a36Sopenharmony_ci					 imsic->nr_msis);
85462306a36Sopenharmony_ci		if (val)
85562306a36Sopenharmony_ci			*val = topei;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci		/* Writes ignore value and clear top pending interrupt */
85862306a36Sopenharmony_ci		if (topei && wr_mask) {
85962306a36Sopenharmony_ci			topei >>= TOPEI_ID_SHIFT;
86062306a36Sopenharmony_ci			if (topei) {
86162306a36Sopenharmony_ci				eix = &imsic->swfile->eix[topei /
86262306a36Sopenharmony_ci							  BITS_PER_TYPE(u64)];
86362306a36Sopenharmony_ci				clear_bit(topei & (BITS_PER_TYPE(u64) - 1),
86462306a36Sopenharmony_ci					  eix->eip);
86562306a36Sopenharmony_ci			}
86662306a36Sopenharmony_ci		}
86762306a36Sopenharmony_ci	} else {
86862306a36Sopenharmony_ci		r = imsic_mrif_rmw(imsic->swfile, imsic->nr_eix, isel,
86962306a36Sopenharmony_ci				   val, new_val, wr_mask);
87062306a36Sopenharmony_ci		/* Forward unknown IMSIC register to user-space */
87162306a36Sopenharmony_ci		if (r)
87262306a36Sopenharmony_ci			rc = (r == -ENOENT) ? 0 : KVM_INSN_ILLEGAL_TRAP;
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	if (wr_mask)
87662306a36Sopenharmony_ci		imsic_swfile_extirq_update(vcpu);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	return rc;
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ciint kvm_riscv_aia_imsic_rw_attr(struct kvm *kvm, unsigned long type,
88262306a36Sopenharmony_ci				bool write, unsigned long *val)
88362306a36Sopenharmony_ci{
88462306a36Sopenharmony_ci	u32 isel, vcpu_id;
88562306a36Sopenharmony_ci	unsigned long flags;
88662306a36Sopenharmony_ci	struct imsic *imsic;
88762306a36Sopenharmony_ci	struct kvm_vcpu *vcpu;
88862306a36Sopenharmony_ci	int rc, vsfile_hgei, vsfile_cpu;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	if (!kvm_riscv_aia_initialized(kvm))
89162306a36Sopenharmony_ci		return -ENODEV;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	vcpu_id = KVM_DEV_RISCV_AIA_IMSIC_GET_VCPU(type);
89462306a36Sopenharmony_ci	vcpu = kvm_get_vcpu_by_id(kvm, vcpu_id);
89562306a36Sopenharmony_ci	if (!vcpu)
89662306a36Sopenharmony_ci		return -ENODEV;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	isel = KVM_DEV_RISCV_AIA_IMSIC_GET_ISEL(type);
89962306a36Sopenharmony_ci	imsic = vcpu->arch.aia_context.imsic_state;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	read_lock_irqsave(&imsic->vsfile_lock, flags);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	rc = 0;
90462306a36Sopenharmony_ci	vsfile_hgei = imsic->vsfile_hgei;
90562306a36Sopenharmony_ci	vsfile_cpu = imsic->vsfile_cpu;
90662306a36Sopenharmony_ci	if (vsfile_cpu < 0) {
90762306a36Sopenharmony_ci		if (write) {
90862306a36Sopenharmony_ci			rc = imsic_mrif_rmw(imsic->swfile, imsic->nr_eix,
90962306a36Sopenharmony_ci					    isel, NULL, *val, -1UL);
91062306a36Sopenharmony_ci			imsic_swfile_extirq_update(vcpu);
91162306a36Sopenharmony_ci		} else
91262306a36Sopenharmony_ci			rc = imsic_mrif_rmw(imsic->swfile, imsic->nr_eix,
91362306a36Sopenharmony_ci					    isel, val, 0, 0);
91462306a36Sopenharmony_ci	}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	read_unlock_irqrestore(&imsic->vsfile_lock, flags);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	if (!rc && vsfile_cpu >= 0)
91962306a36Sopenharmony_ci		rc = imsic_vsfile_rw(vsfile_hgei, vsfile_cpu, imsic->nr_eix,
92062306a36Sopenharmony_ci				     isel, write, val);
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	return rc;
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ciint kvm_riscv_aia_imsic_has_attr(struct kvm *kvm, unsigned long type)
92662306a36Sopenharmony_ci{
92762306a36Sopenharmony_ci	u32 isel, vcpu_id;
92862306a36Sopenharmony_ci	struct imsic *imsic;
92962306a36Sopenharmony_ci	struct kvm_vcpu *vcpu;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	if (!kvm_riscv_aia_initialized(kvm))
93262306a36Sopenharmony_ci		return -ENODEV;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	vcpu_id = KVM_DEV_RISCV_AIA_IMSIC_GET_VCPU(type);
93562306a36Sopenharmony_ci	vcpu = kvm_get_vcpu_by_id(kvm, vcpu_id);
93662306a36Sopenharmony_ci	if (!vcpu)
93762306a36Sopenharmony_ci		return -ENODEV;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	isel = KVM_DEV_RISCV_AIA_IMSIC_GET_ISEL(type);
94062306a36Sopenharmony_ci	imsic = vcpu->arch.aia_context.imsic_state;
94162306a36Sopenharmony_ci	return imsic_mrif_isel_check(imsic->nr_eix, isel);
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_civoid kvm_riscv_vcpu_aia_imsic_reset(struct kvm_vcpu *vcpu)
94562306a36Sopenharmony_ci{
94662306a36Sopenharmony_ci	struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	if (!imsic)
94962306a36Sopenharmony_ci		return;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	kvm_riscv_vcpu_aia_imsic_release(vcpu);
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	memset(imsic->swfile, 0, sizeof(*imsic->swfile));
95462306a36Sopenharmony_ci}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ciint kvm_riscv_vcpu_aia_imsic_inject(struct kvm_vcpu *vcpu,
95762306a36Sopenharmony_ci				    u32 guest_index, u32 offset, u32 iid)
95862306a36Sopenharmony_ci{
95962306a36Sopenharmony_ci	unsigned long flags;
96062306a36Sopenharmony_ci	struct imsic_mrif_eix *eix;
96162306a36Sopenharmony_ci	struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	/* We only emulate one IMSIC MMIO page for each Guest VCPU */
96462306a36Sopenharmony_ci	if (!imsic || !iid || guest_index ||
96562306a36Sopenharmony_ci	    (offset != IMSIC_MMIO_SETIPNUM_LE &&
96662306a36Sopenharmony_ci	     offset != IMSIC_MMIO_SETIPNUM_BE))
96762306a36Sopenharmony_ci		return -ENODEV;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	iid = (offset == IMSIC_MMIO_SETIPNUM_BE) ? __swab32(iid) : iid;
97062306a36Sopenharmony_ci	if (imsic->nr_msis <= iid)
97162306a36Sopenharmony_ci		return -EINVAL;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	read_lock_irqsave(&imsic->vsfile_lock, flags);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	if (imsic->vsfile_cpu >= 0) {
97662306a36Sopenharmony_ci		writel(iid, imsic->vsfile_va + IMSIC_MMIO_SETIPNUM_LE);
97762306a36Sopenharmony_ci		kvm_vcpu_kick(vcpu);
97862306a36Sopenharmony_ci	} else {
97962306a36Sopenharmony_ci		eix = &imsic->swfile->eix[iid / BITS_PER_TYPE(u64)];
98062306a36Sopenharmony_ci		set_bit(iid & (BITS_PER_TYPE(u64) - 1), eix->eip);
98162306a36Sopenharmony_ci		imsic_swfile_extirq_update(vcpu);
98262306a36Sopenharmony_ci	}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	read_unlock_irqrestore(&imsic->vsfile_lock, flags);
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	return 0;
98762306a36Sopenharmony_ci}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_cistatic int imsic_mmio_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
99062306a36Sopenharmony_ci			   gpa_t addr, int len, void *val)
99162306a36Sopenharmony_ci{
99262306a36Sopenharmony_ci	if (len != 4 || (addr & 0x3) != 0)
99362306a36Sopenharmony_ci		return -EOPNOTSUPP;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	*((u32 *)val) = 0;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	return 0;
99862306a36Sopenharmony_ci}
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_cistatic int imsic_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
100162306a36Sopenharmony_ci			    gpa_t addr, int len, const void *val)
100262306a36Sopenharmony_ci{
100362306a36Sopenharmony_ci	struct kvm_msi msi = { 0 };
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	if (len != 4 || (addr & 0x3) != 0)
100662306a36Sopenharmony_ci		return -EOPNOTSUPP;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	msi.address_hi = addr >> 32;
100962306a36Sopenharmony_ci	msi.address_lo = (u32)addr;
101062306a36Sopenharmony_ci	msi.data = *((const u32 *)val);
101162306a36Sopenharmony_ci	kvm_riscv_aia_inject_msi(vcpu->kvm, &msi);
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	return 0;
101462306a36Sopenharmony_ci};
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_cistatic struct kvm_io_device_ops imsic_iodoev_ops = {
101762306a36Sopenharmony_ci	.read = imsic_mmio_read,
101862306a36Sopenharmony_ci	.write = imsic_mmio_write,
101962306a36Sopenharmony_ci};
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ciint kvm_riscv_vcpu_aia_imsic_init(struct kvm_vcpu *vcpu)
102262306a36Sopenharmony_ci{
102362306a36Sopenharmony_ci	int ret = 0;
102462306a36Sopenharmony_ci	struct imsic *imsic;
102562306a36Sopenharmony_ci	struct page *swfile_page;
102662306a36Sopenharmony_ci	struct kvm *kvm = vcpu->kvm;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	/* Fail if we have zero IDs */
102962306a36Sopenharmony_ci	if (!kvm->arch.aia.nr_ids)
103062306a36Sopenharmony_ci		return -EINVAL;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	/* Allocate IMSIC context */
103362306a36Sopenharmony_ci	imsic = kzalloc(sizeof(*imsic), GFP_KERNEL);
103462306a36Sopenharmony_ci	if (!imsic)
103562306a36Sopenharmony_ci		return -ENOMEM;
103662306a36Sopenharmony_ci	vcpu->arch.aia_context.imsic_state = imsic;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	/* Setup IMSIC context  */
103962306a36Sopenharmony_ci	imsic->nr_msis = kvm->arch.aia.nr_ids + 1;
104062306a36Sopenharmony_ci	rwlock_init(&imsic->vsfile_lock);
104162306a36Sopenharmony_ci	imsic->nr_eix = BITS_TO_U64(imsic->nr_msis);
104262306a36Sopenharmony_ci	imsic->nr_hw_eix = BITS_TO_U64(kvm_riscv_aia_max_ids);
104362306a36Sopenharmony_ci	imsic->vsfile_hgei = imsic->vsfile_cpu = -1;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	/* Setup IMSIC SW-file */
104662306a36Sopenharmony_ci	swfile_page = alloc_pages(GFP_KERNEL | __GFP_ZERO,
104762306a36Sopenharmony_ci				  get_order(sizeof(*imsic->swfile)));
104862306a36Sopenharmony_ci	if (!swfile_page) {
104962306a36Sopenharmony_ci		ret = -ENOMEM;
105062306a36Sopenharmony_ci		goto fail_free_imsic;
105162306a36Sopenharmony_ci	}
105262306a36Sopenharmony_ci	imsic->swfile = page_to_virt(swfile_page);
105362306a36Sopenharmony_ci	imsic->swfile_pa = page_to_phys(swfile_page);
105462306a36Sopenharmony_ci	spin_lock_init(&imsic->swfile_extirq_lock);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	/* Setup IO device */
105762306a36Sopenharmony_ci	kvm_iodevice_init(&imsic->iodev, &imsic_iodoev_ops);
105862306a36Sopenharmony_ci	mutex_lock(&kvm->slots_lock);
105962306a36Sopenharmony_ci	ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS,
106062306a36Sopenharmony_ci				      vcpu->arch.aia_context.imsic_addr,
106162306a36Sopenharmony_ci				      KVM_DEV_RISCV_IMSIC_SIZE,
106262306a36Sopenharmony_ci				      &imsic->iodev);
106362306a36Sopenharmony_ci	mutex_unlock(&kvm->slots_lock);
106462306a36Sopenharmony_ci	if (ret)
106562306a36Sopenharmony_ci		goto fail_free_swfile;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	return 0;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_cifail_free_swfile:
107062306a36Sopenharmony_ci	free_pages((unsigned long)imsic->swfile,
107162306a36Sopenharmony_ci		   get_order(sizeof(*imsic->swfile)));
107262306a36Sopenharmony_cifail_free_imsic:
107362306a36Sopenharmony_ci	vcpu->arch.aia_context.imsic_state = NULL;
107462306a36Sopenharmony_ci	kfree(imsic);
107562306a36Sopenharmony_ci	return ret;
107662306a36Sopenharmony_ci}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_civoid kvm_riscv_vcpu_aia_imsic_cleanup(struct kvm_vcpu *vcpu)
107962306a36Sopenharmony_ci{
108062306a36Sopenharmony_ci	struct kvm *kvm = vcpu->kvm;
108162306a36Sopenharmony_ci	struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	if (!imsic)
108462306a36Sopenharmony_ci		return;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	imsic_vsfile_cleanup(imsic);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	mutex_lock(&kvm->slots_lock);
108962306a36Sopenharmony_ci	kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &imsic->iodev);
109062306a36Sopenharmony_ci	mutex_unlock(&kvm->slots_lock);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	free_pages((unsigned long)imsic->swfile,
109362306a36Sopenharmony_ci		   get_order(sizeof(*imsic->swfile)));
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	vcpu->arch.aia_context.imsic_state = NULL;
109662306a36Sopenharmony_ci	kfree(imsic);
109762306a36Sopenharmony_ci}
1098